Skip to content

fix: only show Edit ... Properties menu items on items that actually have the DefaultUserOperationsTypes.UPDATE_PROPS defined#1685

Draft
jstarpl wants to merge 1 commit intoSofie-Automation:mainfrom
nrkno:contrib/fix/sofie-4263/props-menu-items
Draft

fix: only show Edit ... Properties menu items on items that actually have the DefaultUserOperationsTypes.UPDATE_PROPS defined#1685
jstarpl wants to merge 1 commit intoSofie-Automation:mainfrom
nrkno:contrib/fix/sofie-4263/props-menu-items

Conversation

@jstarpl
Copy link
Contributor

@jstarpl jstarpl commented Mar 11, 2026

About the Contributor

This pull request is posted on behalf of the NRK.

Type of Contribution

This is a:

Bug fix

Current Behavior

The Edit ... Properties menu items are shown on all Items (Segments, Parts, Pieces) when user editing is enabled, even if those items do not have the DefaultUserOperationsTypes.UPDATE_PROPS operation declared in userEditOperations.

New Behavior

Only show the items on relevant items.

Testing

  • I have added one or more unit tests for this PR
  • I have updated the relevant unit tests
  • No unit test changes are needed for this PR

Affected areas

Time Frame

Other Information

Status

  • PR is ready to be reviewed.
  • The functionality has been tested by the author.
  • Relevant unit tests has been added / updated.
  • Relevant documentation (code comments, system documentation) has been added / updated.

…have the DefaultUserOperationsTypes.UPDATE_PROPS defined
@jstarpl jstarpl added the Contribution from NRK Contributions sponsored by NRK (nrk.no) label Mar 11, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

Walkthrough

Modified SegmentContextMenu component to conditionally render edit menu items for Segment, Part, and Piece properties. Added a helper function doesItemSupportUserEditUpdateProps to detect UPDATE_PROPS support in user edit operations, and applied guarded rendering based on enableUserEdits flag, entity existence, and capability checks.

Changes

Cohort / File(s) Summary
Menu Item Guard Logic
packages/webui/src/client/ui/SegmentTimeline/SegmentContextMenu.tsx
Added doesItemSupportUserEditUpdateProps helper function to check for UPDATE_PROPS action support. Applied conditional rendering to Edit Segment Properties, Edit Part Properties, and Edit Piece Properties menu items based on enableUserEdits flag, entity existence, userEditProperties presence, and capability detection. Expanded imports to include CoreUserEditingDefinition variants, types, ReadonlyObjectDeep, and UserEditingType.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: conditionally rendering Edit Properties menu items only when DEFAULT_USER_OPERATIONS_TYPES.UPDATE_PROPS is defined.
Description check ✅ Passed The description is related to the changeset, explaining the bug (menu items showing unconditionally) and the fix (showing them only on relevant items with UPDATE_PROPS defined).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can enable review details to help with troubleshooting, context usage and more.

Enable the reviews.review_details setting to include review details such as the model used, the time taken for each step and more in the review comments.

@codecov
Copy link

codecov bot commented Mar 11, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@jstarpl
Copy link
Contributor Author

jstarpl commented Mar 17, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/webui/src/client/ui/SegmentTimeline/SegmentContextMenu.tsx (1)

94-106: Consider using .some() instead of .find() for semantic clarity.

Since the result is only used in a truthy context, .some() more clearly expresses the intent of checking existence rather than retrieving the operation. Additionally, extracting this function outside the component would avoid recreation on each render, though the impact is negligible for a context menu.

♻️ Proposed refactor

Extract above the component and use .some():

+const doesItemSupportUserEditUpdateProps = (
+	userEditOperations:
+		| readonly (
+				| ReadonlyObjectDeep<CoreUserEditingDefinitionAction>
+				| ReadonlyObjectDeep<CoreUserEditingDefinitionForm>
+				| ReadonlyObjectDeep<CoreUserEditingDefinitionSofie>
+		  )[]
+		| undefined
+): boolean => {
+	return (
+		userEditOperations?.some(
+			(op) => op.type === UserEditingType.SOFIE && op.id === DefaultUserOperationsTypes.UPDATE_PROPS
+		) ?? false
+	)
+}
+
 export function SegmentContextMenu({

Then remove lines 94-106 from inside the component.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/webui/src/client/ui/SegmentTimeline/SegmentContextMenu.tsx` around
lines 94 - 106, The helper doesItemSupportUserEditUpdateProps currently uses
.find() inside the component to check existence; extract this function out of
the component and change its implementation to use .some() to express boolean
intent (i.e., check userEditOperations.some(op => op.type ===
UserEditingType.SOFIE && op.id === DefaultUserOperationsTypes.UPDATE_PROPS));
keep the same name and signature so callers are unchanged and ensure
imports/references to UserEditingType.SOFIE and
DefaultUserOperationsTypes.UPDATE_PROPS remain available after moving the
function.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/webui/src/client/ui/SegmentTimeline/SegmentContextMenu.tsx`:
- Around line 313-337: The <hr /> can render alone when enableUserEdits is true
but none of the edit menu items are eligible; update the SegmentContextMenu
render so the divider is only output when at least one edit item will render by
either moving the <hr /> inside the same conditional checks that render the
MenuItem(s) or by computing a boolean like hasAnyEditPropsItem using segment,
part, piece and doesItemSupportUserEditUpdateProps (checking the same properties
used for the MenuItem rendering) and then rendering {enableUserEdits &&
hasAnyEditPropsItem && <hr />} before the MenuItem blocks; keep existing
onEditProps calls and the checks on segment.userEditProperties,
part.instance.part.userEditProperties, piece.instance.piece.userEditProperties
and their corresponding doesItemSupportUserEditUpdateProps calls.

---

Nitpick comments:
In `@packages/webui/src/client/ui/SegmentTimeline/SegmentContextMenu.tsx`:
- Around line 94-106: The helper doesItemSupportUserEditUpdateProps currently
uses .find() inside the component to check existence; extract this function out
of the component and change its implementation to use .some() to express boolean
intent (i.e., check userEditOperations.some(op => op.type ===
UserEditingType.SOFIE && op.id === DefaultUserOperationsTypes.UPDATE_PROPS));
keep the same name and signature so callers are unchanged and ensure
imports/references to UserEditingType.SOFIE and
DefaultUserOperationsTypes.UPDATE_PROPS remain available after moving the
function.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4bd0990b-08c0-4bf2-b013-200a8f427bfa

📥 Commits

Reviewing files that changed from the base of the PR and between 0c60e8a and 00c4387.

📒 Files selected for processing (1)
  • packages/webui/src/client/ui/SegmentTimeline/SegmentContextMenu.tsx

Comment on lines 313 to 337
{enableUserEdits && (
<>
<hr />
<MenuItem onClick={() => onEditProps({ type: 'segment', elementId: part.instance.segmentId })}>
<span>{t('Edit Segment Properties')}</span>
</MenuItem>
<MenuItem onClick={() => onEditProps({ type: 'part', elementId: part.instance.part._id })}>
<span>{t('Edit Part Properties')}</span>
</MenuItem>
{piece && piece.instance.piece.userEditProperties && (
<MenuItem onClick={() => onEditProps({ type: 'piece', elementId: piece.instance.piece._id })}>
<span>{t('Edit Piece Properties')}</span>
</MenuItem>
)}
{segment &&
segment.userEditProperties &&
doesItemSupportUserEditUpdateProps(segment.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'segment', elementId: part.instance.segmentId })}>
<span>{t('Edit Segment Properties')}</span>
</MenuItem>
)}
{part &&
part.instance.part.userEditProperties &&
doesItemSupportUserEditUpdateProps(part.instance.part.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'part', elementId: part.instance.part._id })}>
<span>{t('Edit Part Properties')}</span>
</MenuItem>
)}
{piece &&
piece.instance.piece.userEditProperties &&
doesItemSupportUserEditUpdateProps(piece.instance.piece.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'piece', elementId: piece.instance.piece._id })}>
<span>{t('Edit Piece Properties')}</span>
</MenuItem>
)}
</>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential orphan <hr /> when no edit menu items render.

If enableUserEdits is true but none of segment, part, or piece satisfy their conditions (i.e., none have both userEditProperties and UPDATE_PROPS support), the block will render only the <hr /> on line 315 with no menu items following it, leaving a visual artifact at the end of the context menu.

🛠️ Proposed fix

Move the <hr /> inside a condition that checks if at least one item will render:

 {enableUserEdits && (
 	<>
-		<hr />
+		{(segment?.userEditProperties && doesItemSupportUserEditUpdateProps(segment.userEditOperations)) ||
+		 (part?.instance.part.userEditProperties && doesItemSupportUserEditUpdateProps(part.instance.part.userEditOperations)) ||
+		 (piece?.instance.piece.userEditProperties && doesItemSupportUserEditUpdateProps(piece.instance.piece.userEditOperations)) ? (
+			<hr />
+		) : null}
 		{segment &&
 			segment.userEditProperties &&
 			doesItemSupportUserEditUpdateProps(segment.userEditOperations) && (

Alternatively, compute a boolean flag earlier:

const hasAnyEditPropsItem =
	(segment?.userEditProperties && doesItemSupportUserEditUpdateProps(segment.userEditOperations)) ||
	(part?.instance.part.userEditProperties && doesItemSupportUserEditUpdateProps(part.instance.part.userEditOperations)) ||
	(piece?.instance.piece.userEditProperties && doesItemSupportUserEditUpdateProps(piece.instance.piece.userEditOperations))

Then use {enableUserEdits && hasAnyEditPropsItem && <hr />} before the menu items.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{enableUserEdits && (
<>
<hr />
<MenuItem onClick={() => onEditProps({ type: 'segment', elementId: part.instance.segmentId })}>
<span>{t('Edit Segment Properties')}</span>
</MenuItem>
<MenuItem onClick={() => onEditProps({ type: 'part', elementId: part.instance.part._id })}>
<span>{t('Edit Part Properties')}</span>
</MenuItem>
{piece && piece.instance.piece.userEditProperties && (
<MenuItem onClick={() => onEditProps({ type: 'piece', elementId: piece.instance.piece._id })}>
<span>{t('Edit Piece Properties')}</span>
</MenuItem>
)}
{segment &&
segment.userEditProperties &&
doesItemSupportUserEditUpdateProps(segment.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'segment', elementId: part.instance.segmentId })}>
<span>{t('Edit Segment Properties')}</span>
</MenuItem>
)}
{part &&
part.instance.part.userEditProperties &&
doesItemSupportUserEditUpdateProps(part.instance.part.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'part', elementId: part.instance.part._id })}>
<span>{t('Edit Part Properties')}</span>
</MenuItem>
)}
{piece &&
piece.instance.piece.userEditProperties &&
doesItemSupportUserEditUpdateProps(piece.instance.piece.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'piece', elementId: piece.instance.piece._id })}>
<span>{t('Edit Piece Properties')}</span>
</MenuItem>
)}
</>
{enableUserEdits && (
<>
{(segment?.userEditProperties && doesItemSupportUserEditUpdateProps(segment.userEditOperations)) ||
(part?.instance.part.userEditProperties && doesItemSupportUserEditUpdateProps(part.instance.part.userEditOperations)) ||
(piece?.instance.piece.userEditProperties && doesItemSupportUserEditUpdateProps(piece.instance.piece.userEditOperations)) ? (
<hr />
) : null}
{segment &&
segment.userEditProperties &&
doesItemSupportUserEditUpdateProps(segment.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'segment', elementId: part.instance.segmentId })}>
<span>{t('Edit Segment Properties')}</span>
</MenuItem>
)}
{part &&
part.instance.part.userEditProperties &&
doesItemSupportUserEditUpdateProps(part.instance.part.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'part', elementId: part.instance.part._id })}>
<span>{t('Edit Part Properties')}</span>
</MenuItem>
)}
{piece &&
piece.instance.piece.userEditProperties &&
doesItemSupportUserEditUpdateProps(piece.instance.piece.userEditOperations) && (
<MenuItem onClick={() => onEditProps({ type: 'piece', elementId: piece.instance.piece._id })}>
<span>{t('Edit Piece Properties')}</span>
</MenuItem>
)}
</>
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/webui/src/client/ui/SegmentTimeline/SegmentContextMenu.tsx` around
lines 313 - 337, The <hr /> can render alone when enableUserEdits is true but
none of the edit menu items are eligible; update the SegmentContextMenu render
so the divider is only output when at least one edit item will render by either
moving the <hr /> inside the same conditional checks that render the MenuItem(s)
or by computing a boolean like hasAnyEditPropsItem using segment, part, piece
and doesItemSupportUserEditUpdateProps (checking the same properties used for
the MenuItem rendering) and then rendering {enableUserEdits &&
hasAnyEditPropsItem && <hr />} before the MenuItem blocks; keep existing
onEditProps calls and the checks on segment.userEditProperties,
part.instance.part.userEditProperties, piece.instance.piece.userEditProperties
and their corresponding doesItemSupportUserEditUpdateProps calls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Contribution from NRK Contributions sponsored by NRK (nrk.no)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant