Skip to content

[8424] Field information dialog, help icon in inheritance Alert updates#4800

Open
jvega190 wants to merge 12 commits into
craftercms:developfrom
jvega190:bugfix/8424
Open

[8424] Field information dialog, help icon in inheritance Alert updates#4800
jvega190 wants to merge 12 commits into
craftercms:developfrom
jvega190:bugfix/8424

Conversation

@jvega190

@jvega190 jvega190 commented Feb 13, 2026

Copy link
Copy Markdown
Member

craftercms/craftercms#8424

Summary by CodeRabbit

  • New Features

    • Added a dedicated Field Information dialog to display comprehensive field metadata and properties.
    • Enhanced field header with improved dropdown interactions.
  • Improvements

    • Updated content-inheritance affordance with clearer icons and direct edit functionality.
  • Bug Fixes

    • Corrected spelling in inherited-value message text.

…le to edit source, and move 'learn more' link to alert icon
@coderabbitai

coderabbitai Bot commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a new FieldInformationDialog component that renders field metadata (basic properties and descriptor-driven sections) inside an EnhancedDialog. The dialog is registered lazily in the studio UI component map and is dispatched via Redux from the FormsEngineField header dropdown. The content-inheritance alert is also updated: the icon changes and the read-only "View" action is replaced with a direct "Edit" action.

Changes

FieldInformationDialog and FormsEngineField Integration

Layer / File(s) Summary
FieldInformationDialog component and registration
ui/app/src/components/FieldInformationDialog/FieldInformationDialog.tsx, ui/app/src/components/FieldInformationDialog/index.ts, ui/app/src/env/studioUI.ts
New FieldInformationDialog renders a "Basic Properties" card from commonControlFieldsDescriptors and additional descriptor-driven section cards (with boolean Chip display) inside EnhancedDialog. Exported via index.ts and lazily registered in the studioUI components map.
FormsEngineField header and inheritance alert updates
ui/app/src/components/FormsEngine/components/FormsEngineField.tsx
Removes Button import; adds EditOutlined, InfoOutlinedIcon, useDispatch, pushDialog, and createComponentId. The "Field Information" menu item now dispatches pushDialog to open FieldInformationDialog. The inheritance alert replaces HelpOutlineRounded and the read-only pushForm call with InfoOutlinedIcon and an EditOutlined icon button that pushes the form for editing. Fixes "overriden" → "overridden".

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • rart
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description only contains a GitHub issue link without explaining the changes, missing required context beyond the ticket reference. Expand the description to explain what changes were made, why they were made, and any important implementation details beyond just linking to the issue.
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 (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main changes: creation of a field information dialog and updates to help icon/inheritance alert UI.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx`:
- Around line 76-77: The help text is being injected via dangerouslySetInnerHTML
(in FieldInformationDialog.tsx where renderHtml renders <Typography
dangerouslySetInnerHTML={{ __html: value ?? '-' }} />) which is an XSS risk;
sanitize the HTML before injecting—import a sanitizer (e.g., DOMPurify) and
replace the raw assignment with something like const safeHtml =
DOMPurify.sanitize(value ?? '-'); then pass safeHtml to dangerouslySetInnerHTML.
Apply the same fix for the identical pattern in FormsEngineField.tsx (around the
render that uses dangerouslySetInnerHTML at the noted location) so all
helpText/html rendering is sanitized centrally before rendering.
- Around line 88-116: The Card rendered inside descriptor.sections?.map(...) is
missing a key prop; add a stable unique key to the Card (e.g., use section.id if
available, otherwise a safe fallback like section.title) so React can properly
reconcile; update the map callback that renders <Card> (inside
FieldInformationDialog, referencing descriptor.sections, section and fieldName)
to pass that key prop to the Card element.

In `@ui/app/src/components/FormsEngine/components/FormsEngineField.tsx`:
- Around line 230-243: The IconButton currently uses a placeholder href="/" with
component="a" and target="_blank" which will open the site root; update the
IconButton in FormsEngineField.tsx (the IconButton / href / component="a" /
target="_blank" usage) to be inert until a real docs URL is available — either
remove href and component="a" (render a plain IconButton with title/tooltip) or
change to href="#" and add an onClick handler that calls event.preventDefault()
(and remove target="_blank"); keep the title/InfoOutlineIcon/formatMessage as-is
so the button remains accessible.
- Around line 276-281: In the FormattedMessage inside FormsEngineField (the
defaultMessage text for the inherited value case), correct the typo by changing
"overriden" to "overridden" so the user-facing string reads "Inherited value
from {label} is overridden"; update the defaultMessage in the FormattedMessage
component used around itemsByPath[sourceMap[fieldId]]?.label.
🧹 Nitpick comments (3)
ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx (2)

50-51: Add a type annotation for Body props.

The Body component's props parameter is implicitly any. Consider typing it for consistency with the rest of the codebase.

Proposed fix
-function Body(props) {
-	const { field } = props;
+function Body(props: { field: ContentTypeField }) {
+	const { field } = props;

94-102: Reassigning value from a primitive to a JSX element may cause type confusion.

value starts as a property/validation value (string, boolean, number, etc.), then is conditionally reassigned to a <Chip> ReactNode. This works at runtime but muddies the type. Also, on line 109, value ? value : '-' will treat 0, false (after the boolean branch), and "" as missing — though false is already handled, 0 and "" would incorrectly show '-'.

Suggested approach
 {section.fields?.map((fieldName) => {
-	let value = field.properties?.[fieldName]?.value ?? field.validations?.[fieldName]?.value;
-	if (typeof value === 'boolean') {
-		value = value ? (
-			<Chip label="true" color="success" size="small" />
-		) : (
-			<Chip label="false" color="default" size="small" />
-		);
-	}
+	const rawValue = field.properties?.[fieldName]?.value ?? field.validations?.[fieldName]?.value;
+	let display: ReactNode;
+	if (typeof rawValue === 'boolean') {
+		display = rawValue ? (
+			<Chip label="true" color="success" size="small" />
+		) : (
+			<Chip label="false" color="default" size="small" />
+		);
+	} else {
+		display = rawValue != null && rawValue !== '' ? String(rawValue) : '-';
+	}
 	return (
 		<Grid container spacing={2} key={fieldName} sx={{ mt: 0.5 }}>
 			<Grid size={{ xs: 4 }}>
 				<Typography color="text.secondary">{getPossibleTranslation(fieldName, formatMessage)}</Typography>
 			</Grid>
 			<Grid size={{ xs: 8 }}>
-				<Typography>{value ? value : '-'}</Typography>
+				<Typography>{display}</Typography>
 			</Grid>
 		</Grid>
 	);
 })}
ui/app/src/components/FormsEngine/components/FormsEngineField.tsx (1)

190-197: Inline array pattern for conditional menu items.

Returning [<Divider>, <MenuItem>] from a conditional expression works but is slightly unusual. A <Fragment> wrapper would be more idiomatic and avoids the need for key props on each element:

Suggested alternative
-								{hasChanges && [
-									<Divider key="rollback-divider" />,
-									<MenuItem key="rollback-action" onClick={handleRollback}>
-										<ListItemText>
-											<FormattedMessage defaultMessage="Rollback changes" />
-										</ListItemText>
-									</MenuItem>
-								]}
+								{hasChanges && (
+									<>
+										<Divider />
+										<MenuItem onClick={handleRollback}>
+											<ListItemText>
+												<FormattedMessage defaultMessage="Rollback changes" />
+											</ListItemText>
+										</MenuItem>
+									</>
+								)}

Comment thread ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx Outdated
Comment thread ui/app/src/components/FormsEngine/components/FormsEngineField.tsx Outdated
Comment thread ui/app/src/components/FormsEngine/components/FormsEngineField.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx`:
- Line 79: The JSX rendering uses a falsy check that treats legitimate values
like 0, "" or false as missing (e.g., in FieldInformationDialog's Typography
where it currently uses value ? value : '-'); change this to a nullish check so
only null or undefined are treated as absent (use the nullish coalescing
approach for the same expression and update the identical occurrence around the
other Typography instance referenced on line 109) so valid falsy values are
preserved.
- Around line 104-107: The displayed field label is using the raw key variable
fieldName (e.g., "maxlength") so getPossibleTranslation(formatMessage) returns
it verbatim; update the render in FieldInformationDialog to pass the actual
MessageDescriptor (descriptor.fields[fieldName].name) — or fallback safely if
descriptor or descriptor.fields[fieldName] is missing — into
getPossibleTranslation/formatMessage so the UI shows the translated field name
instead of the raw id.

In `@ui/app/src/components/FormsEngine/components/FormsEngineField.tsx`:
- Line 52: Import and component name are wrong: replace the import of
InfoOutlineIcon from '@mui/icons-material/InfoOutline' with InfoOutlinedIcon
from '@mui/icons-material/InfoOutlined' and update the component usage in
FormsEngineField (change any <InfoOutlineIcon /> instances to <InfoOutlinedIcon
/>) so the symbol names (InfoOutlinedIcon) match MUI v7's outlined icon exports.
- Around line 216-222: The dangerouslySetInnerHTML usage in FormsEngineField
(rendering field.helpText) is an XSS risk; install and import a sanitizer like
DOMPurify and replace the raw usage with a sanitized value (e.g., const
safeHelpText = DOMPurify.sanitize(field.helpText || '') and pass that to
dangerouslySetInnerHTML). Update the FormsEngineField component to import
DOMPurify, sanitize field.helpText before rendering, and consider adding/using a
shared sanitizeHtml helper for reuse across similar components (SiteTools,
GlobalApp, FieldInformationDialog) to keep behavior consistent.
🧹 Nitpick comments (5)
ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx (3)

50-51: Add a type annotation for Body props.

The props parameter is untyped, which loses type safety. A simple inline type or a pick from the dialog props would suffice.

Proposed fix
-function Body(props) {
-	const { field } = props;
+function Body(props: { field: ContentTypeField }) {
+	const { field } = props;

65-66: Variable descriptor shadows the outer scope.

The .map callback parameter descriptor shadows the descriptor declared on line 55. Consider renaming it (e.g., fieldDescriptor or commonField) to avoid confusion.


88-89: Prefer a stable key over array index.

Descriptor sections have an id property (e.g., 'properties', 'constraints'). Using section.id as the key is more robust than the array index.

Proposed fix
-			{descriptor.sections?.map((section, index) => (
-				<Card key={index}>
+			{descriptor.sections?.map((section) => (
+				<Card key={section.id}>
ui/app/src/components/FormsEngine/components/FormsEngineField.tsx (2)

244-259: Nit: Unnecessary Fragment wrapper around single element.

The action contains a single <IconButton> wrapped in <>...</>. The Fragment can be removed for cleaner JSX.

♻️ Proposed simplification
 action={
-  <>
-    <IconButton
-      color="inherit"
-      size="small"
-      sx={{ px: 0.5, minWidth: 0 }}
-      onClick={() => {
-        globalApi.pushForm({
-          update: { path: sourceMap[fieldId] }
-        });
-      }}
-      title={formatMessage({ defaultMessage: 'Edit' })}
-    >
-      <EditOutlined fontSize="small" />
-    </IconButton>
-  </>
+  <IconButton
+    color="inherit"
+    size="small"
+    sx={{ px: 0.5, minWidth: 0 }}
+    onClick={() => {
+      globalApi.pushForm({
+        update: { path: sourceMap[fieldId] }
+      });
+    }}
+    title={formatMessage({ defaultMessage: 'Edit' })}
+  >
+    <EditOutlined fontSize="small" />
+  </IconButton>
 }

204-204: Use JSX children for consistency (Biome lint: noChildrenProp).

Line 204 passes children as a prop while the other ListItemText usages (lines 186–188, 193–195) use JSX children. Aligning with the JSX form is more idiomatic and silences the Biome lint error.

♻️ Proposed fix
-<MenuItem key={option.id} onClick={(e) => onMenuOptionClick?.(e, option.id, handleCloseMenu)}>
-  <ListItemText children={option.text} />
-</MenuItem>
+<MenuItem key={option.id} onClick={(e) => onMenuOptionClick?.(e, option.id, handleCloseMenu)}>
+  <ListItemText>{option.text}</ListItemText>
+</MenuItem>

Comment thread ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx Outdated
Comment thread ui/app/src/components/FormsEngine/components/FormsEngineField.tsx Outdated
Comment thread ui/app/src/components/FormsEngine/components/FormsEngineField.tsx Outdated
@jvega190 jvega190 marked this pull request as ready for review February 19, 2026 17:02
@jvega190 jvega190 requested a review from rart as a code owner February 19, 2026 17:02
@rart

rart commented Mar 12, 2026

Copy link
Copy Markdown
Member

Please share a screenshot of how this is looking

@jvega190

Copy link
Copy Markdown
Member Author

Please share a screenshot of how this is looking

Screenshot from 2026-03-17 12-13-54 Screenshot from 2026-03-17 12-12-26 Screenshot from 2026-03-17 12-12-08

Comment thread ui/app/src/components/FormsEngine/components/FormsEngineField.tsx Outdated
Comment thread ui/app/src/env/studioUI.ts Outdated
FolderMoveAlertDialog: lazy(() => import('../components/FolderMoveAlertDialog')),
FormEngineControls: lazy(() => import('../components/FormEngineControls')),
FormsEngine: lazy(() => import('../components/FormsEngine/FormsEngine')),
FieldInformationDialog: lazy(() => import('../components/FormsEngine/components/FieldInformationDialog')),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@rart by adding this we can simply use the dialog's system. Would it make sense to include components inside other components folder (in the script) so we can do this?

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx (1)

79-80: ⚠️ Potential issue | 🟠 Major

XSS: help HTML is still rendered unsanitized.

Per the prior discussion where @rart agreed to add DOMPurify, field.help (rendered here via dangerouslySetInnerHTML) is still injected without sanitization. Please sanitize before injecting, e.g.:

-								<Typography dangerouslySetInnerHTML={{ __html: value ?? '-' }} />
+								<Typography dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(value ?? '-') }} />

The same sanitization should be applied to FormsEngineField.tsx line 229 and any other dangerouslySetInnerHTML sites. If you'd like, I can open a tracking issue for introducing a shared sanitizeHtml helper.

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

In `@ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx`
around lines 79 - 80, FieldInformationDialog.tsx is still injecting unsanitized
HTML via the renderHtml branch (Typography using dangerouslySetInnerHTML) which
allows XSS; update the render path to sanitize the HTML (e.g., import and use a
shared sanitizeHtml helper that wraps DOMPurify) before passing it to
dangerouslySetInnerHTML, and apply the same change to the other site mentioned
(FormsEngineField component where dangerouslySetInnerHTML is used) so all uses
of dangerouslySetInnerHTML call sanitizeHtml(value or field.help) and not raw
content.
🧹 Nitpick comments (3)
ui/app/src/components/FormsEngine/components/FormsEngineField.tsx (1)

253-269: Unnecessary fragment around a single child.

The action prop only wraps one IconButton; the surrounding <>...</> is redundant and can be removed.

♻️ Proposed simplification
-				action={
-					<>
-						<IconButton
+				action={
+					<IconButton
 							color="inherit"
 							size="small"
 							sx={{ px: 0.5, minWidth: 0 }}
 							onClick={() => {
 								globalApi.pushForm({
 									update: { path: sourceMap[fieldId] }
 								});
 							}}
 							title={formatMessage({ defaultMessage: 'Edit' })}
-						>
-							<EditOutlined fontSize="small" />
-						</IconButton>
-					</>
+					>
+						<EditOutlined fontSize="small" />
+					</IconButton>
 				}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ui/app/src/components/FormsEngine/components/FormsEngineField.tsx` around
lines 253 - 269, Remove the redundant fragment wrapping the single IconButton in
the action prop of the FormsEngineField component: locate the action={ <>
<IconButton ... /> </> } JSX (the IconButton that calls globalApi.pushForm with
update: { path: sourceMap[fieldId] }) and replace it with the IconButton
directly (action={ <IconButton ... /> }) so there is no unnecessary <>...</>
wrapper.
ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx (2)

68-87: Variable name shadowing reduces readability.

The inner descriptor inside Object.values(commonControlFieldsDescriptors).map((descriptor) => ...) shadows the outer descriptor (the control type descriptor computed on line 58). Later on line 91 the outer descriptor.sections is used, and on line 110 descriptor.fields?.[fieldName]?.name is used — so both are live in the file concurrently. Rename the inner one for clarity (e.g., fieldDescriptor) to make it obvious which descriptor is being referenced.

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

In `@ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx`
around lines 68 - 87, The inner map callback in FieldInformationDialog.tsx
shadows the outer control-type `descriptor`; rename the inner parameter (e.g.,
from `descriptor` to `fieldDescriptor`) in the
Object.values(commonControlFieldsDescriptors).map(...) and update all uses
inside that block (`descriptor.id`, `descriptor.name`, etc.) to
`fieldDescriptor.id`, `fieldDescriptor.name` so the outer `descriptor` (used
later for `descriptor.sections` and `descriptor.fields?.[fieldName]?.name`)
remains unshadowed and the intent is clear.

28-28: Inconsistent MUI import style.

Every other MUI component in this file is imported via its subpath (@mui/material/Box, @mui/material/Card, etc.), but Chip uses the barrel import. This defeats the tree‑shaking benefit of subpath imports and is inconsistent with the rest of the file and the surrounding codebase.

-import { Chip } from '@mui/material';
+import Chip from '@mui/material/Chip';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx` at
line 28, The import for Chip uses the barrel import "@mui/material" which is
inconsistent with other subpath imports in FieldInformationDialog.tsx; replace
the barrel import line importing Chip with a subpath import from
"@mui/material/Chip" (keeping the existing named symbol Chip) so tree-shaking
and import style match other components in this file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx`:
- Around line 53-58: The Body component has an untyped props parameter causing
implicit any for field; define an explicit props interface (e.g., BodyProps with
a typed field property matching your form field descriptor used elsewhere) and
annotate the component signature (change function Body(props) to function
Body(props: BodyProps) or function Body({ field }: BodyProps)). Ensure the Field
type includes the properties used (type, properties, validations) or import the
existing Field/FormField type, so usages of field.type and
controlDescriptors[field.type] are fully typed.

In `@ui/app/src/components/FormsEngine/components/FormsEngineField.tsx`:
- Around line 185-198: The FieldInformation menu currently always dispatches
pushDialog(createComponentId('FieldInformationDialog'), props: { field }) which
allows duplicate dialogs; change the onClick handler in FormsEngineField (the
MenuItem callback that calls dispatch(pushDialog(...))) to first check the
dialogs state (e.g., via the existing selector that returns the dialogs stack or
getDialogs) for an entry whose component equals
createComponentId('FieldInformationDialog'), and if found dispatch an
updateDialog-style action to replace its props with { field } instead of
pushing, otherwise dispatch pushDialog as before; alternatively, add logic in
the reducer handling pushDialog to dedupe by component id for
'FieldInformationDialog' like CodeEditorDialog/FormsEngineDialog do so the same
instance is reused.

---

Duplicate comments:
In `@ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx`:
- Around line 79-80: FieldInformationDialog.tsx is still injecting unsanitized
HTML via the renderHtml branch (Typography using dangerouslySetInnerHTML) which
allows XSS; update the render path to sanitize the HTML (e.g., import and use a
shared sanitizeHtml helper that wraps DOMPurify) before passing it to
dangerouslySetInnerHTML, and apply the same change to the other site mentioned
(FormsEngineField component where dangerouslySetInnerHTML is used) so all uses
of dangerouslySetInnerHTML call sanitizeHtml(value or field.help) and not raw
content.

---

Nitpick comments:
In `@ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx`:
- Around line 68-87: The inner map callback in FieldInformationDialog.tsx
shadows the outer control-type `descriptor`; rename the inner parameter (e.g.,
from `descriptor` to `fieldDescriptor`) in the
Object.values(commonControlFieldsDescriptors).map(...) and update all uses
inside that block (`descriptor.id`, `descriptor.name`, etc.) to
`fieldDescriptor.id`, `fieldDescriptor.name` so the outer `descriptor` (used
later for `descriptor.sections` and `descriptor.fields?.[fieldName]?.name`)
remains unshadowed and the intent is clear.
- Line 28: The import for Chip uses the barrel import "@mui/material" which is
inconsistent with other subpath imports in FieldInformationDialog.tsx; replace
the barrel import line importing Chip with a subpath import from
"@mui/material/Chip" (keeping the existing named symbol Chip) so tree-shaking
and import style match other components in this file.

In `@ui/app/src/components/FormsEngine/components/FormsEngineField.tsx`:
- Around line 253-269: Remove the redundant fragment wrapping the single
IconButton in the action prop of the FormsEngineField component: locate the
action={ <> <IconButton ... /> </> } JSX (the IconButton that calls
globalApi.pushForm with update: { path: sourceMap[fieldId] }) and replace it
with the IconButton directly (action={ <IconButton ... /> }) so there is no
unnecessary <>...</> wrapper.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 386b7066-5450-4f3d-ab47-487eed186f75

📥 Commits

Reviewing files that changed from the base of the PR and between 460bba2 and ca5ac9b.

📒 Files selected for processing (3)
  • ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx
  • ui/app/src/components/FormsEngine/components/FormsEngineField.tsx
  • ui/app/src/env/studioUI.ts

Comment thread ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx Outdated
Comment thread ui/app/src/components/FormsEngine/components/FormsEngineField.tsx Outdated
@jvega190 jvega190 marked this pull request as draft April 22, 2026 18:26

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx (1)

93-95: ⚡ Quick win

Use a stable section key instead of array index.

key={index} can cause reconciliation issues if section ordering changes. Prefer a stable identifier such as section.id (with a safe fallback only if needed).

Proposed change
-			{descriptor.sections?.map((section, index) => (
-				<Card key={index}>
+			{descriptor.sections?.map((section, index) => (
+				<Card key={section.id ?? index}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx`
around lines 93 - 95, The Card elements in FieldInformationDialog.tsx use
key={index} when rendering descriptor.sections in the map, which can break
reconciliation; update the map to use a stable identifier such as section.id for
the React key (e.g., key={section.id}) and only fall back to a deterministic
alternative if id is absent (for example a concatenation of another stable field
like section.name or a generated stable id), ensuring the key is unique and
stable across renders.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx`:
- Around line 93-95: The Card elements in FieldInformationDialog.tsx use
key={index} when rendering descriptor.sections in the map, which can break
reconciliation; update the map to use a stable identifier such as section.id for
the React key (e.g., key={section.id}) and only fall back to a deterministic
alternative if id is absent (for example a concatenation of another stable field
like section.name or a generated stable id), ensuring the key is unique and
stable across renders.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bf7b5225-a44b-4290-b8d3-e26e1115fd17

📥 Commits

Reviewing files that changed from the base of the PR and between ca5ac9b and 7632252.

📒 Files selected for processing (1)
  • ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx

@jvega190

Copy link
Copy Markdown
Member Author

@coderabbitai can you check if everything looks good? Please give it a final review before I hand over for human review

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the new FieldInformationDialog component
cat -n ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx

echo "---SEPARATOR---"
# Read the modified FormsEngineField
cat -n ui/app/src/components/FormsEngine/components/FormsEngineField.tsx

echo "---SEPARATOR---"
# Check studioUI.ts around the new entry
rg -n "FieldInformationDialog" ui/app/src/env/studioUI.ts -C 3

echo "---SEPARATOR---"
# Check how pushDialog / createComponentId are used elsewhere to confirm pattern correctness
rg -n "createComponentId" ui/app/src/components/FormsEngine/components/FormsEngineField.tsx

echo "---SEPARATOR---"
# Confirm FieldInformationDialog is registered as a component id
rg -rn "FieldInformationDialog" ui/app/src/

echo "---SEPARATOR---"
# Check if there are any TypeScript issues with the props passed to FieldInformationDialog
rg -n "FieldInformationDialogProps\|FieldInformationDialog" ui/app/src/components/FormsEngine/components/FieldInformationDialog.tsx

Length of output: 16155


🏁 Script executed:

#!/bin/bash
# Read the actual FieldInformationDialog component file
cat -n ui/app/src/components/FieldInformationDialog/FieldInformationDialog.tsx

echo "---SEPARATOR---"
cat -n ui/app/src/components/FieldInformationDialog/index.ts

echo "---SEPARATOR---"
# Exact lines around FieldInformationDialog in studioUI.ts
sed -n '135,150p' ui/app/src/env/studioUI.ts

echo "---SEPARATOR---"
# Check the exact line in FormsEngineField.tsx around createComponentId
grep -n "createComponentId" ui/app/src/components/FormsEngine/components/FormsEngineField.tsx

Length of output: 8380



@jvega190 The implementation looks correct and well-wired end-to-end. Here's the full review before handoff:


✅ What looks good

  • FieldInformationDialog component — properly exported, typed with FieldInformationDialogProps, and lazy-loaded in studioUI.ts.
  • createComponentId('FieldInformationDialog') in FormsEngineField.tsx matches the key registered in studioUI.ts — the dialog dispatch will resolve correctly.
  • Typo fix "overriden""overridden"
  • Icon swap for the inheritance alert: HelpOutlineRoundedInfoOutlinedIcon is semantically more accurate (info vs help). HelpOutlineRounded is still used for the field help-text toggle button, so neither import is dead.
  • dangerouslySetInnerHTML for the help field is consistent with the existing usage in FormsEngineField (line 224), so no regression there.
  • Inline Edit button (EditOutlined) in the inheritance alert replacing the old "View" button is a cleaner UX.

🔶 Minor issues to address before merging

  1. studioUI.ts — out-of-alphabetical-order entry
    FieldInformationDialog is inserted between FormsEngine and FormsEngineDialog (lines 140–142), but alphabetically it should appear before FolderBrowserTreeView. Consider moving it to maintain the sorted order:

    - FormsEngine: lazy(() => import('../components/FormsEngine/FormsEngine')),
    - FieldInformationDialog: lazy(() => import('../components/FieldInformationDialog/FieldInformationDialog')),
    - FormsEngineDialog: lazy(() => import('../components/FormsEngine/FormsEngineDialog')),
    + FieldInformationDialog: lazy(() => import('../components/FieldInformationDialog/FieldInformationDialog')),
      ...
      FormsEngine: lazy(() => import('../components/FormsEngine/FormsEngine')),
      FormsEngineDialog: lazy(() => import('../components/FormsEngine/FormsEngineDialog')),
  2. href="/" placeholder in the inheritance alert icon button (FormsEngineField.tsx lines ~235–245)
    The "Learn more" IconButton links to "/", which will navigate to the root if clicked. The TODO comment acknowledges this, but it's worth confirming this won't be shipped as-is — at minimum it should be href="#" with e.preventDefault(), or the button should be disabled/hidden until the real link is available.

  3. key={section.title} in FieldInformationDialog (line 95)
    Using section.title as a React key works as long as titles are unique within a descriptor's sections. If there's any chance of duplicate titles, using the array index as a fallback (e.g., key={${section.title}_${index}}) would be safer.


🔵 Nit (no action required)

  • The <>...</> fragment wrapping <FormControl> in FormsEngineField.tsx's return is harmless but unnecessary since there's only one root element. Can be cleaned up if desired.

Overall this is ready for human review once the alphabetical ordering is fixed and the href="/" placeholder situation is confirmed/intentional. Good work! 🐇

…refactor FormsEngineField component structure for improved readability and functionality.
@jvega190

Copy link
Copy Markdown
Member Author

@coderabbitai can you check if everything looks good? Please give it a final review before I hand over for human review

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

@jvega190 My previous review (just above in this thread) covers everything — here's a quick summary of the status:

✅ Ready for human review, pending these items:

# Issue Severity
1 FieldInformationDialog entry in studioUI.ts is out of alphabetical order (sits between FormsEngine and FormsEngineDialog; should appear before FolderBrowserTreeView) Minor
2 href="/" on the "Learn more" IconButton in FormsEngineField.tsx — confirm this is intentional/temporary or replace with href="#" + e.preventDefault() Minor
3 key={section.title} in FieldInformationDialog — safe only if section titles are guaranteed unique per descriptor Nit

Everything else looks solid:

  • Component registration, lazy-loading, and createComponentId dispatch are correctly wired ✅
  • Typo "overriden""overridden"
  • Icon swap (HelpOutlineRoundedInfoOutlinedIcon) is semantically correct ✅
  • dangerouslySetInnerHTML for the help field is consistent with existing usage ✅

Once you address (or consciously accept) items 1 & 2, this is good to hand off. 🐇

✅ Action performed

Full review finished.

@jvega190

Copy link
Copy Markdown
Member Author

@CodeRabbit it seems like you checked against a previous version. Also, the href="/" will be dealed later, not on this PR. Please give it a final review before I hand over for human review considering the details mentioned above

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== FieldInformationDialog.tsx ==="
fd "FieldInformationDialog.tsx" ui/app/src --exec cat -n {}

echo "=== FieldInformationDialog index ==="
fd "index.ts" ui/app/src/components/FieldInformationDialog --exec cat -n {}

echo "=== FormsEngineField.tsx ==="
cat -n ui/app/src/components/FormsEngine/components/FormsEngineField.tsx

echo "=== studioUI.ts around FieldInformationDialog ==="
grep -n "FieldInformationDialog\|FolderBrowser\|FormsEngine" ui/app/src/env/studioUI.ts

echo "=== studioUI.ts full entry list (just component lines) ==="
grep -n "lazy(" ui/app/src/env/studioUI.ts | head -80

Length of output: 26672



@jvega190 Apologies for reviewing against the old version. Here's the up-to-date final review:


✅ Everything looks good

All three previously raised concerns are already resolved in this version:

Previous concern Status
FieldInformationDialog out-of-alphabetical-order in studioUI.ts ✅ Fixed — correctly sits between ExpiringDashlet and FolderBrowserTreeView (line 136)
key={section.title} potentially non-unique ✅ Fixed — now uses `${section.title}_${index}` (line 95)
Unnecessary <>...</> fragment wrapping <FormControl> ✅ Removed — clean direct return
href="/" placeholder ✅ Acknowledged — deferred to a later PR per your note

A few things specifically verified in the current code:

  • Component registration wiringcreateComponentId('FieldInformationDialog') in FormsEngineField.tsx exactly matches the key in studioUI.ts. Dialog dispatch will resolve correctly. ✅
  • Chip inside Typography — when a boolean property value is transformed into a <Chip> node, nnou(value) && value !== '' correctly evaluates to true for a React element, so the chip renders as expected. ✅
  • dangerouslySetInnerHTML for the help field falls back to '-' when empty — consistent with the rest of the component. ✅
  • Typo "overriden""overridden" is in place (line 281). ✅
  • InfoOutlinedIcon for the inheritance alert and EditOutlined for the inline edit action are both correctly imported and used. ✅

This is ready to hand off for human review. 🐇

@jvega190 jvega190 marked this pull request as ready for review June 23, 2026 21:28

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ui/app/src/components/FieldInformationDialog/FieldInformationDialog.tsx`:
- Around line 82-83: The Typography component that renders HTML via
dangerouslySetInnerHTML is missing the component="section" prop, which can cause
invalid DOM nesting when the help text contains block-level elements like
paragraphs or lists. Add component="section" to the Typography component on line
83 that renders the value with dangerouslySetInnerHTML to properly contain the
rich HTML content, following the same pattern used in FormsEngineField.tsx for
rendering field.helpText.

In `@ui/app/src/components/FormsEngine/components/FormsEngineField.tsx`:
- Around line 234-259: The two icon-only IconButton components in the
inheritance alert (the one with InfoOutlinedIcon at line 234 and the one with
EditOutlined at line 248) currently rely only on the title prop for
accessibility. Add an explicit aria-label prop to both IconButton components
using the same formatMessage calls as their respective title props to ensure
reliable naming for assistive technology. The first IconButton should have
aria-label={formatMessage({defaultMessage: 'Learn more about content
inheritance'})} and the second should have
aria-label={formatMessage({defaultMessage: 'Edit'})}.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 46338ff9-ea27-419b-b434-91f9866fa5b8

📥 Commits

Reviewing files that changed from the base of the PR and between 9990ce9 and c03a756.

📒 Files selected for processing (4)
  • ui/app/src/components/FieldInformationDialog/FieldInformationDialog.tsx
  • ui/app/src/components/FieldInformationDialog/index.ts
  • ui/app/src/components/FormsEngine/components/FormsEngineField.tsx
  • ui/app/src/env/studioUI.ts

Comment on lines +82 to +83
{renderHtml ? (
<Typography dangerouslySetInnerHTML={{ __html: value ?? '-' }} />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n ui/app/src/components/FieldInformationDialog/FieldInformationDialog.tsx | sed -n '75,95p'

Repository: craftercms/studio-ui

Length of output: 976


🏁 Script executed:

ast-grep outline ui/app/src/components/FieldInformationDialog/FieldInformationDialog.tsx --view expanded

Repository: craftercms/studio-ui

Length of output: 589


🏁 Script executed:

cat -n ui/app/src/components/FieldInformationDialog/FieldInformationDialog.tsx | sed -n '1,100p'

Repository: craftercms/studio-ui

Length of output: 4522


🏁 Script executed:

# Check for Typography component import and usage
rg "Typography" ui/app/src/components/FieldInformationDialog/FieldInformationDialog.tsx -A 1 -B 1

Repository: craftercms/studio-ui

Length of output: 1441


🏁 Script executed:

# Check where renderHtml and value are defined
rg "renderHtml|value" ui/app/src/components/FieldInformationDialog/FieldInformationDialog.tsx -B 3 -A 1 | head -60

Repository: craftercms/studio-ui

Length of output: 1261


🏁 Script executed:

# Check what basicPropsMap maps to, especially for help
rg "basicPropsMap|helpText" ui/app/src/components/FieldInformationDialog/FieldInformationDialog.tsx -B 2 -A 2

Repository: craftercms/studio-ui

Length of output: 440


🏁 Script executed:

# Check the ContentTypeField model to understand what field.helpText contains
find ui/app/src -name "*.ts" -o -name "*.tsx" | xargs grep -l "ContentTypeField" | head -5

Repository: craftercms/studio-ui

Length of output: 487


🏁 Script executed:

# Look for helpText field definition in models
rg "helpText" ui/app/src/models --type ts --type tsx -B 2 -A 2

Repository: craftercms/studio-ui

Length of output: 186


🏁 Script executed:

# Check if there are any examples or tests showing what help text content looks like
rg "dangerouslySetInnerHTML|helpText.*html|help.*<" ui/app/src --type ts --type tsx -B 2 -A 2 | head -40

Repository: craftercms/studio-ui

Length of output: 186


🏁 Script executed:

# Find ContentTypeField definition
rg "interface ContentTypeField|type ContentTypeField" ui/app/src --type ts -A 15

Repository: craftercms/studio-ui

Length of output: 8964


🏁 Script executed:

# Look for where helpText is used or defined
rg "helpText" ui/app/src -B 2 -A 2 | head -50

Repository: craftercms/studio-ui

Length of output: 4319


🏁 Script executed:

# Check if help text can contain HTML by looking at RTE or rich text usage
rg "richText|rte|RTE|html.*help|help.*html|dangerouslySetInnerHTML" ui/app/src --type ts -B 2 -A 2 | head -60

Repository: craftercms/studio-ui

Length of output: 5149


🏁 Script executed:

# Check Typography default component mapping in MUI
web_search "Material-UI Typography default component variant rendering"

Repository: craftercms/studio-ui

Length of output: 206


🏁 Script executed:

# Check the exact implementation in FormsEngineField to compare
cat -n ui/app/src/components/FormsEngine/components/FormsEngineField.tsx | sed -n '70,140p'

Repository: craftercms/studio-ui

Length of output: 2756


🏁 Script executed:

# Verify if helpText in the model can contain HTML
cat -n ui/app/src/models/ContentType.ts | sed -n '1,150p'

Repository: craftercms/studio-ui

Length of output: 4784


🏁 Script executed:

# Get more context around the dangerouslySetInnerHTML usage in FormsEngineField
cat -n ui/app/src/components/FormsEngine/components/FormsEngineField.tsx | sed -n '155,185p'

Repository: craftercms/studio-ui

Length of output: 1409


🏁 Script executed:

# Search for all instances of dangerouslySetInnerHTML to see the pattern used elsewhere
rg "dangerouslySetInnerHTML" ui/app/src -B 2 -A 2

Repository: craftercms/studio-ui

Length of output: 3354


Add component="section" to Typography to properly contain rich HTML help text.

Line 83 renders help text via dangerouslySetInnerHTML into Typography without specifying a component. Since help text is a string field that can contain HTML, this may inject block-level elements (paragraphs, lists, etc.) into an inline-by-default component, causing invalid DOM nesting. The codebase already follows this pattern elsewhere—FormsEngineField.tsx renders the same field.helpText with component="section" for the same purpose.

Proposed fix
-										<Typography dangerouslySetInnerHTML={{ __html: value ?? '-' }} />
+										<Typography component="section" dangerouslySetInnerHTML={{ __html: value ?? '-' }} />
📝 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
{renderHtml ? (
<Typography dangerouslySetInnerHTML={{ __html: value ?? '-' }} />
{renderHtml ? (
<Typography component="section" dangerouslySetInnerHTML={{ __html: value ?? '-' }} />
🧰 Tools
🪛 ast-grep (0.44.0)

[warning] 82-82: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation

(react-unsafe-html-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/app/src/components/FieldInformationDialog/FieldInformationDialog.tsx`
around lines 82 - 83, The Typography component that renders HTML via
dangerouslySetInnerHTML is missing the component="section" prop, which can cause
invalid DOM nesting when the help text contains block-level elements like
paragraphs or lists. Add component="section" to the Typography component on line
83 that renders the value with dangerouslySetInnerHTML to properly contain the
rich HTML content, following the same pattern used in FormsEngineField.tsx for
rendering field.helpText.

Comment on lines +234 to +259
<IconButton
href="/"
size="small"
color="inherit"
target="_blank"
component="a"
title={formatMessage({ defaultMessage: 'Learn more about content inheritance' })}
sx={{ p: 0 }}
>
<InfoOutlinedIcon />
</IconButton>
}
action={
<>
<Button
<IconButton
color="inherit"
size="small"
sx={{ px: 0.5, minWidth: 0 }}
onClick={() => {
globalApi.pushForm({
readonly: true,
update: { path: sourceMap[fieldId] }
});
}}
title={formatMessage({ defaultMessage: 'Edit' })}
>
View
</Button>
{/* TODO: Create or link to content inheritance article */}
<IconButton
href="/"
size="small"
color="inherit"
target="_blank"
component="a"
title={formatMessage({ defaultMessage: 'Learn more about content inheritance' })}
>
<HelpOutlineRounded fontSize="small" />
<EditOutlined fontSize="small" />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add aria-label to icon-only actions in the inheritance alert.

Line 234 and Line 248 use icon-only IconButtons with title, but explicit aria-label is needed for reliable assistive-tech naming.

Proposed fix
 						<IconButton
 							href="/"
 							size="small"
 							color="inherit"
 							target="_blank"
 							component="a"
+							aria-label={formatMessage({ defaultMessage: 'Learn more about content inheritance' })}
 							title={formatMessage({ defaultMessage: 'Learn more about content inheritance' })}
 							sx={{ p: 0 }}
 						>
 							<InfoOutlinedIcon />
 						</IconButton>
@@
 							<IconButton
 								color="inherit"
 								size="small"
 								sx={{ px: 0.5, minWidth: 0 }}
+								aria-label={formatMessage({ defaultMessage: 'Edit inherited source content' })}
 								onClick={() => {
 									globalApi.pushForm({
 										update: { path: sourceMap[fieldId] }
 									});
 								}}
 								title={formatMessage({ defaultMessage: 'Edit' })}
 							>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/app/src/components/FormsEngine/components/FormsEngineField.tsx` around
lines 234 - 259, The two icon-only IconButton components in the inheritance
alert (the one with InfoOutlinedIcon at line 234 and the one with EditOutlined
at line 248) currently rely only on the title prop for accessibility. Add an
explicit aria-label prop to both IconButton components using the same
formatMessage calls as their respective title props to ensure reliable naming
for assistive technology. The first IconButton should have
aria-label={formatMessage({defaultMessage: 'Learn more about content
inheritance'})} and the second should have
aria-label={formatMessage({defaultMessage: 'Edit'})}.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants