Skip to content

CONSOLE-4447: Migrate core modals to PatternFly v6 modal components (part 1)#16100

Open
rhamilto wants to merge 1 commit intoopenshift:mainfrom
rhamilto:CONSOLE-4447-core
Open

CONSOLE-4447: Migrate core modals to PatternFly v6 modal components (part 1)#16100
rhamilto wants to merge 1 commit intoopenshift:mainfrom
rhamilto:CONSOLE-4447-core

Conversation

@rhamilto
Copy link
Member

@rhamilto rhamilto commented Mar 3, 2026

Summary

This PR migrates 9 core modal components from deprecated factory modal components to PatternFly v6 Modal components.

Note: This PR includes changes from #16015, so it should merge first.

Changes

Modal Files Migrated:

  1. ✅ add-secret-to-workload.tsx
  2. ✅ alert-routing-modal.tsx
  3. ✅ configure-cluster-upstream-modal.tsx
  4. ✅ configure-ns-pull-secret-modal.tsx
  5. ✅ configure-update-strategy-modal.tsx
  6. ✅ expand-pvc-modal.tsx
  7. ✅ labels-modal.tsx
  8. ✅ managed-resource-save-modal.tsx
  9. ✅ remove-volume-modal.tsx

Hook Updated:

  • useLabelsModal.tsx - Eliminated duplication by using LabelsModalOverlay directly

Pattern Applied:

  • ❌ Removed: ModalTitle, ModalSubmitFooter, ModalWrapper (deprecated factory components)
  • ✅ Added: PatternFly v6 Modal, ModalHeader, ModalBody, ModalVariant
  • ✅ Added: ModalFooterWithAlerts from @console/shared
  • ✅ Replaced HTML <form> with React Fragment <>
  • ✅ Added Form component with id for form submission
  • ✅ Associated submit buttons using form attribute
  • ✅ Replaced <p> tags with <Content component={ContentVariants.p}>
  • ✅ Used titleIconVariant for warning/danger icons instead of custom icon components
  • ✅ Modern overlay pattern with useState for isOpen management

Testing

No existing tests were found for these modals. Manual testing instructions are available for each modal.

Statistics

  • 11 files changed
  • 785 insertions(+)
  • 605 deletions(-)
  • Net: +180 lines (cleaner, more modern code)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Style

    • Modal dialogs redesigned with improved visual consistency, spacing, and better organization of information and actions throughout the application.
    • Enhanced form layouts in configuration and management modals for clearer presentation and improved user interactions.
  • Refactor

    • Modernized modal interaction patterns for streamlined user workflows and more consistent experience across features.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Mar 3, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Mar 3, 2026

@rhamilto: This pull request references CONSOLE-4447 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary

This PR migrates 9 core modal components from deprecated factory modal components to PatternFly v6 Modal components.

Changes

Modal Files Migrated:

  1. ✅ add-secret-to-workload.tsx
  2. ✅ alert-routing-modal.tsx
  3. ✅ configure-cluster-upstream-modal.tsx
  4. ✅ configure-ns-pull-secret-modal.tsx
  5. ✅ configure-update-strategy-modal.tsx
  6. ✅ expand-pvc-modal.tsx
  7. ✅ labels-modal.tsx
  8. ✅ managed-resource-save-modal.tsx
  9. ✅ remove-volume-modal.tsx

Hook Updated:

  • useLabelsModal.tsx - Eliminated duplication by using LabelsModalOverlay directly

Pattern Applied:

  • ❌ Removed: ModalTitle, ModalSubmitFooter, ModalWrapper (deprecated factory components)
  • ✅ Added: PatternFly v6 Modal, ModalHeader, ModalBody, ModalVariant
  • ✅ Added: ModalFooterWithAlerts from @console/shared
  • ✅ Replaced HTML <form> with React Fragment <>
  • ✅ Added Form component with id for form submission
  • ✅ Associated submit buttons using form attribute
  • ✅ Replaced <p> tags with <Content component={ContentVariants.p}>
  • ✅ Used titleIconVariant for warning/danger icons instead of custom icon components
  • ✅ Modern overlay pattern with useState for isOpen management

Testing

No existing tests were found for these modals. Manual testing instructions are available for each modal.

Statistics

  • 11 files changed
  • 785 insertions(+)
  • 605 deletions(-)
  • Net: +180 lines (cleaner, more modern code)

🤖 Generated with Claude Code

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci openshift-ci bot requested review from Leo6Leo and rawagner March 3, 2026 21:51
@openshift-ci openshift-ci bot added the component/core Related to console core functionality label Mar 3, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Mar 3, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: rhamilto

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added component/knative Related to knative-plugin approved Indicates a PR has been approved by an approver from all required OWNERS files. component/metal3 Related to metal3-plugin component/olm Related to OLM component/shared Related to console-shared labels Mar 3, 2026
@rhamilto rhamilto changed the title Open [WIP] CONSOLE-4447: Migrate core modals to PatternFly v6 Open [WIP] CONSOLE-4447: Migrate core modals to PatternFly v6 (part 1) Mar 3, 2026
@rhamilto rhamilto changed the title Open [WIP] CONSOLE-4447: Migrate core modals to PatternFly v6 (part 1) [WIP] CONSOLE-4447: Migrate core modals to PatternFly v6 (part 1) Mar 3, 2026
@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 3, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Mar 3, 2026

@rhamilto: This pull request references CONSOLE-4447 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary

This PR migrates 9 core modal components from deprecated factory modal components to PatternFly v6 Modal components.

Note: This PR includes changes from #16015, so it should merge first.

Changes

Modal Files Migrated:

  1. ✅ add-secret-to-workload.tsx
  2. ✅ alert-routing-modal.tsx
  3. ✅ configure-cluster-upstream-modal.tsx
  4. ✅ configure-ns-pull-secret-modal.tsx
  5. ✅ configure-update-strategy-modal.tsx
  6. ✅ expand-pvc-modal.tsx
  7. ✅ labels-modal.tsx
  8. ✅ managed-resource-save-modal.tsx
  9. ✅ remove-volume-modal.tsx

Hook Updated:

  • useLabelsModal.tsx - Eliminated duplication by using LabelsModalOverlay directly

Pattern Applied:

  • ❌ Removed: ModalTitle, ModalSubmitFooter, ModalWrapper (deprecated factory components)
  • ✅ Added: PatternFly v6 Modal, ModalHeader, ModalBody, ModalVariant
  • ✅ Added: ModalFooterWithAlerts from @console/shared
  • ✅ Replaced HTML <form> with React Fragment <>
  • ✅ Added Form component with id for form submission
  • ✅ Associated submit buttons using form attribute
  • ✅ Replaced <p> tags with <Content component={ContentVariants.p}>
  • ✅ Used titleIconVariant for warning/danger icons instead of custom icon components
  • ✅ Modern overlay pattern with useState for isOpen management

Testing

No existing tests were found for these modals. Manual testing instructions are available for each modal.

Statistics

  • 11 files changed
  • 785 insertions(+)
  • 605 deletions(-)
  • Net: +180 lines (cleaner, more modern code)

🤖 Generated with Claude Code

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@rhamilto
Copy link
Member Author

rhamilto commented Mar 3, 2026

/retest

1 similar comment
@rhamilto
Copy link
Member Author

rhamilto commented Mar 4, 2026

/retest

@rhamilto rhamilto force-pushed the CONSOLE-4447-core branch 3 times, most recently from 4ba1ef0 to ae6f268 Compare March 4, 2026 18:24
@rhamilto
Copy link
Member Author

rhamilto commented Mar 5, 2026

/retest

1 similar comment
@rhamilto
Copy link
Member Author

rhamilto commented Mar 5, 2026

/retest

@rhamilto rhamilto force-pushed the CONSOLE-4447-core branch from ae6f268 to ee674f7 Compare March 6, 2026 16:28
@openshift-ci openshift-ci bot added the component/topology Related to topology label Mar 6, 2026
@rhamilto rhamilto changed the title [WIP] CONSOLE-4447: Migrate core modals to PatternFly v6 (part 1) CONSOLE-4447: Migrate core modals to PatternFly v6 (part 1) Mar 8, 2026
@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 8, 2026
@rhamilto
Copy link
Member Author

rhamilto commented Mar 8, 2026

Tech debt
/label docs-approved
/label px-approved
/assign @sg00dwin
/assign @yapei

@openshift-ci openshift-ci bot added the docs-approved Signifies that Docs has signed off on this PR label Mar 8, 2026
@openshift-ci openshift-ci bot added the px-approved Signifies that Product Support has signed off on this PR label Mar 8, 2026
@rhamilto rhamilto force-pushed the CONSOLE-4447-core branch from ee674f7 to 1120313 Compare March 9, 2026 20:49
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Mar 9, 2026

@rhamilto: This pull request references CONSOLE-4447 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary

This PR migrates 9 core modal components from deprecated factory modal components to PatternFly v6 Modal components.

Note: This PR includes changes from #16015, so it should merge first.

Changes

Modal Files Migrated:

  1. ✅ add-secret-to-workload.tsx
  2. ✅ alert-routing-modal.tsx
  3. ✅ configure-cluster-upstream-modal.tsx
  4. ✅ configure-ns-pull-secret-modal.tsx
  5. ✅ configure-update-strategy-modal.tsx
  6. ✅ expand-pvc-modal.tsx
  7. ✅ labels-modal.tsx
  8. ✅ managed-resource-save-modal.tsx
  9. ✅ remove-volume-modal.tsx

Hook Updated:

  • useLabelsModal.tsx - Eliminated duplication by using LabelsModalOverlay directly

Pattern Applied:

  • ❌ Removed: ModalTitle, ModalSubmitFooter, ModalWrapper (deprecated factory components)
  • ✅ Added: PatternFly v6 Modal, ModalHeader, ModalBody, ModalVariant
  • ✅ Added: ModalFooterWithAlerts from @console/shared
  • ✅ Replaced HTML <form> with React Fragment <>
  • ✅ Added Form component with id for form submission
  • ✅ Associated submit buttons using form attribute
  • ✅ Replaced <p> tags with <Content component={ContentVariants.p}>
  • ✅ Used titleIconVariant for warning/danger icons instead of custom icon components
  • ✅ Modern overlay pattern with useState for isOpen management

Testing

No existing tests were found for these modals. Manual testing instructions are available for each modal.

Statistics

  • 11 files changed
  • 785 insertions(+)
  • 605 deletions(-)
  • Net: +180 lines (cleaner, more modern code)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Style

  • Modal dialogs redesigned with improved visual consistency, spacing, and better organization of information and actions throughout the application.

  • Enhanced form layouts in configuration and management modals for clearer presentation and improved user interactions.

  • Refactor

  • Modernized modal interaction patterns for streamlined user workflows and more consistent experience across features.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 9, 2026

📝 Walkthrough

Walkthrough

This pull request systematically refactors multiple modal components across the OpenShift Console frontend to adopt a consistent PatternFly-based modal architecture. The changes migrate away from a custom modal wrapper pattern (ModalWrapper, ModalTitle, ModalSubmitFooter) toward PatternFly's native Modal components (ModalHeader, ModalBody, ModalVariant). Each modal now uses ModalFooterWithAlerts for consistent error and loading state presentation, and implements local isOpen state management within the overlay component. Forms are explicitly wired to submit buttons via form attributes. One public API is renamed (AddSecretToWorkloadModalProvider to AddSecretToWorkloadModalOverlay), and its props signature is extended with ModalComponentProps. The majority of modal logic remains functionally equivalent; the changes are primarily architectural and compositional. A minor CSS rule (.co-m-radio-desc margin) is removed, likely to align with the refactored form layouts.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The PR title accurately describes the main change: migrating nine core modal components to PatternFly v6, which is precisely what the changeset delivers.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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

Copy link
Contributor

@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: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/public/components/modals/configure-ns-pull-secret-modal.tsx (1)

78-99: ⚠️ Potential issue | 🟠 Major

Relax the MIME check and trust JSON.parse instead.

The browser File.type property is unreliable for JSON uploads. Per the W3C File API specification, File.type returns either a parsable MIME type or an empty string if the browser cannot determine one. In practice, browsers determine type based on file extension rather than content, so users uploading .json files may see different MIME types across browsers and operating systems. The strict check file.type !== 'application/json' will reject valid JSON files before content validation runs.

Remove the MIME type gate and let JSON.parse serve as the source of truth. You can still guide users toward JSON files with the accept attribute on the input element.

Possible fix
 const file = event.target.files?.[0];
- if (!file || file.type !== 'application/json') {
+ if (!file) {
     setInvalidJson(true);
     return;
   }

   const reader = new FileReader();
   reader.onload = (e) => {
<input
  type="file"
  accept=".json,application/json"
  id="namespace-pull-secret-file"
  onChange={onFileChange}
  aria-describedby="namespace-pull-secret-file-help"
/>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/public/components/modals/configure-ns-pull-secret-modal.tsx` around
lines 78 - 99, The file upload handler onFileChange currently rejects files
based on file.type which is unreliable; remove the MIME gate that checks
file.type !== 'application/json' and instead rely on JSON.parse inside the
FileReader.onload to validate content (keep calling setInvalidJson(true) on
parse errors and setFileData(input) on success), and update the corresponding
file input to use accept=".json,application/json" so users are guided to select
JSON files without enforcing MIME-based rejection.
🧹 Nitpick comments (3)
frontend/public/components/modals/managed-resource-save-modal.tsx (2)

21-25: Consider defensive error handling if onSubmit can throw or is async.

The submit handler calls props.close() unconditionally after props.onSubmit(). If onSubmit is asynchronous or can throw, the modal will close regardless of success or failure. Since this appears to be a confirmation modal (user acknowledges the managed resource warning), the risk is likely low, but wrapping in try/catch or awaiting would be more defensive.

🛡️ Optional: Add defensive handling
-  const submit = (event) => {
+  const submit = async (event) => {
     event.preventDefault();
-    props.onSubmit();
-    props.close();
+    try {
+      await props.onSubmit();
+      props.close();
+    } catch (e) {
+      // If onSubmit is sync and cannot fail, this is a no-op
+      console.error('Failed to submit:', e);
+    }
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/public/components/modals/managed-resource-save-modal.tsx` around
lines 21 - 25, The submit handler in submit currently calls props.onSubmit() and
then props.close() unconditionally; make submit async, await props.onSubmit(),
and wrap the await in a try/catch so the modal only closes on success (call
props.close() after a successful await), and handle or rethrow errors in the
catch (e.g., surface an error state or call an onError callback) to avoid
closing the modal if onSubmit throws or rejects.

74-89: Conditional rendering pattern works but bypasses PatternFly Modal animations.

The current implementation uses isOpen ? <Modal isOpen ...> : null, which unmounts the Modal immediately on close. Notice that the isOpen prop is hardcoded to true—it doesn't control visibility since the conditional rendering already does. This works functionally but loses PatternFly's exit animations and may not properly manage focus trapping on the way out.

PatternFly's Modal is designed to remain mounted and use the isOpen prop to orchestrate enter/exit transitions. Consider refactoring to leverage this:

♻️ Keep Modal mounted and use isOpen for state management
 export const ManagedResourceSaveModalOverlay: OverlayComponent<ManagedResourceSaveModalProps> = (
   props,
 ) => {
   const [isOpen, setIsOpen] = useState(true);
   const handleClose = () => {
     setIsOpen(false);
   };
 
+  const handleAfterClose = () => {
+    props.closeOverlay();
+  };
+
-  return isOpen ? (
+  return (
     <Modal
       variant={ModalVariant.small}
-      isOpen
+      isOpen={isOpen}
       onClose={handleClose}
+      afterClose={handleAfterClose}
       aria-labelledby="managed-resource-save-modal-title"
     >
       <ManagedResourceSaveModal {...props} close={handleClose} />
     </Modal>
-  ) : null;
+  );
 };

Alternatively, if PatternFly's Modal lifecycle callbacks don't align with your cleanup timing, the current conditional pattern is acceptable—just document why animation loss is acceptable for this overlay.

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

In `@frontend/public/components/modals/managed-resource-save-modal.tsx` around
lines 74 - 89, The Modal is being unmounted by the conditional render which
bypasses PatternFly exit animations; instead keep the Modal mounted and control
visibility with the isOpen state: remove the ternary that returns null and
render <Modal ...> unconditionally, set its isOpen prop to the component's
isOpen state (the useState variable named isOpen), keep onClose wired to
handleClose, and continue passing close={handleClose} into
ManagedResourceSaveModal so focus/cleanup runs when Modal finishes its exit
transition.
frontend/public/components/modals/labels-modal.tsx (1)

117-135: The FormGroup label is correctly associated with SelectorInput—both hardcode the same id value.

Verification confirms SelectorInput hardcodes id: 'tags-input' on its underlying input (selector-input.jsx, line 149), which matches the fieldId="tags-input" in FormGroup. The label-to-control wiring works correctly and will announce properly to assistive technologies.

However, improve maintainability by wiring the id explicitly: pass fieldId as a prop to SelectorInput instead of both hardcoding 'tags-input'. This eliminates the fragile coupling if someone changes one without the other. Consider updating SelectorInput to accept an optional id or inputId prop, defaulting to 'tags-input' for backward compatibility.

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

In `@frontend/public/components/modals/labels-modal.tsx` around lines 117 - 135,
FormGroup currently uses fieldId="tags-input" while SelectorInput hardcodes
id:'tags-input' internally; make the association explicit by adding a prop
(e.g., id or inputId) to SelectorInput and pass the FormGroup's fieldId into
SelectorInput from labels-modal (replace hardcoded value usage in SelectorInput
with the new prop while defaulting to 'tags-input' for backward compatibility),
updating the SelectorInput implementation (selector-input.jsx) to consume that
prop and apply it to its underlying input so label-to-control wiring remains
correct and maintainable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/public/components/modals/add-secret-to-workload.tsx`:
- Around line 278-287: The Button component with id "confirm-action" currently
has an onClick={submit} which creates a secondary submission path that bypasses
native form validation; remove the onClick prop from the Button in
add-secret-to-workload.tsx so the button relies on type="submit" and
form="co-add-secret-to-workload" to trigger the form's onSubmit handler (submit)
and allow the browser/FormGroup (mountPath isRequired) validation to run
normally.
- Around line 307-315: The modal currently spreads {...props} after explicitly
passing close={handleClose} and cancel={handleClose}, allowing incoming
props.close/props.cancel to override handleClose and break cleanup; update the
JSX so that {...props} is spread first on the Modal/AddSecretToWorkloadModal and
then explicitly pass close={handleClose} and cancel={handleClose} (referencing
AddSecretToWorkloadModal, Modal, handleClose) so handleClose (which calls
setIsOpen(false) and closeOverlay()) cannot be overwritten by incoming props.
- Around line 196-219: The FormGroup that renders the radio group (label "Add
secret as") is reusing the form's id via fieldId="co-add-secret-to-workload",
breaking label-to-field association; update that FormGroup's fieldId to a unique
identifier such as "co-add-secret-to-workload__add-as" (same file's
add-secret-to-workload modal, FormGroup for the radio group) so the label
correctly targets the radio controls.

In `@frontend/public/components/modals/configure-cluster-upstream-modal.tsx`:
- Around line 96-142: The FormGroup used as the radio group currently has an
unrelated fieldId ("co-add-secret-to-workload") and each Radio uses a different
name ("config-default" vs "config-custom"), breaking native radio grouping;
update the FormGroup.fieldId to a meaningful id like "cluster-version-config"
and make both Radio.name values identical (e.g., "cluster-version-config") so
the radios form a proper native group while keeping the existing
isChecked/onChange handlers in the Radio components.

In `@frontend/public/components/modals/configure-ns-pull-secret-modal.tsx`:
- Around line 312-325: The cancel button and the modal overlay/header close
should be disabled while the submit chain is running: when inProgress is true,
make the Cancel Button (the one calling cancel with
data-test-id="modal-cancel-action") disabled or hidden the same as the primary
Button, and ensure the modal's close handler (the header X / overlay onClose) is
no-op or prevented while inProgress is true (so the header close cannot dismiss
mid-submit). Update both the Footer block containing
ModalFooterWithAlerts/Button and the other occurrences referenced (lines
~341-347) to guard the modal close path and the cancel action using the
inProgress flag so no dismissal can occur until the create/patch completes.
- Around line 300-304: The modal currently renders uploaded pull-secret contents
into the DOM via fileData inside CodeBlock/CodeBlockCode (in
ConfigureNsPullSecretModal), which exposes sensitive tokens; instead stop
rendering raw fileData and replace it with a non-sensitive UI: show the uploaded
filename or a generic "file loaded" message and a validation/status badge, keep
any validation logic that inspects fileData but do not inject the raw JSON into
JSX or aria text, and ensure any existing references to fileData for display are
removed or replaced with a safeDisplayName variable used by the UI components.

In `@frontend/public/components/modals/configure-update-strategy-modal.tsx`:
- Around line 221-235: Disable all dismiss paths while the patch is pending: add
inProgress guards to the Cancel button and the modal close handler so users
cannot dismiss while k8sPatch is running. Specifically, set the Cancel <Button>
(the one using cancel and data-test-id="modal-cancel-action") to
isDisabled={inProgress} and modify the modal's onClose/closeOverlay handler to
early-return when inProgress is true (or wrap cancel to check inProgress before
calling closeOverlay). Ensure ModalFooterWithAlerts/Modal onClose behavior and
the cancel function both respect the inProgress flag so neither Cancel nor
clicking outside/close icon will close the modal while the request is in flight.

In `@frontend/public/components/modals/expand-pvc-modal.tsx`:
- Around line 80-88: The RequestSizeInput is receiving a translated string for
its name prop (name={t('public~requestSize')}), which causes generated form
field names (e.g., requestSizeValue, requestSizeUnit) to vary by locale; change
the prop to a static identifier (name="requestSize") in the ExpandPVCModal where
RequestSizeInput is rendered so the form field names remain consistent across
locales and match how create-pvc.tsx uses it.

In `@frontend/public/components/modals/labels-modal.tsx`:
- Around line 120-122: The interpolated "for" fragment is missing the public
namespace; update the i18n call used where descriptionKey is checked (the t(...)
expression that produces "{{description}} for") to use the public namespace
(e.g., change the t invocation for the interpolated string so it uses "public~"
or move the whole interpolation into a single public~ keyed string) so the "for"
fragment is localized consistently; locate the t call alongside descriptionKey
in the labels-modal component and apply the namespace there.
- Line 23: The LabelsModalOverlay is incorrectly coupled to ModalComponentProps
and silently overrides provided cancel/close handlers; remove the import of
ModalComponentProps and stop inheriting it in LabelsModalProps, instead declare
a local prop type that includes only the needed fields (e.g., labels, onSave,
isOpen) and excludes cancel/close so callers cannot pass them; update
LabelsModalOverlay to accept the new LabelsModalProps and keep using its
internal handleClose for closing logic; search for any consumers passing
cancel/close and update them to use the modal's public API (onSave or open
state) if needed.

In `@frontend/public/components/modals/remove-volume-modal.tsx`:
- Line 147: The JSX spreads props after passing controlled handlers, so any
close or cancel in props can override handleClose; change the order to spread
props first in the RemoveVolumeModal element (so {...props} comes before
close={handleClose} cancel={handleClose}) to ensure the controlled handlers
(handleClose / closeOverlay via RemoveVolumeModalProps) always take precedence
and cannot be overridden by incoming props.

---

Outside diff comments:
In `@frontend/public/components/modals/configure-ns-pull-secret-modal.tsx`:
- Around line 78-99: The file upload handler onFileChange currently rejects
files based on file.type which is unreliable; remove the MIME gate that checks
file.type !== 'application/json' and instead rely on JSON.parse inside the
FileReader.onload to validate content (keep calling setInvalidJson(true) on
parse errors and setFileData(input) on success), and update the corresponding
file input to use accept=".json,application/json" so users are guided to select
JSON files without enforcing MIME-based rejection.

---

Nitpick comments:
In `@frontend/public/components/modals/labels-modal.tsx`:
- Around line 117-135: FormGroup currently uses fieldId="tags-input" while
SelectorInput hardcodes id:'tags-input' internally; make the association
explicit by adding a prop (e.g., id or inputId) to SelectorInput and pass the
FormGroup's fieldId into SelectorInput from labels-modal (replace hardcoded
value usage in SelectorInput with the new prop while defaulting to 'tags-input'
for backward compatibility), updating the SelectorInput implementation
(selector-input.jsx) to consume that prop and apply it to its underlying input
so label-to-control wiring remains correct and maintainable.

In `@frontend/public/components/modals/managed-resource-save-modal.tsx`:
- Around line 21-25: The submit handler in submit currently calls
props.onSubmit() and then props.close() unconditionally; make submit async,
await props.onSubmit(), and wrap the await in a try/catch so the modal only
closes on success (call props.close() after a successful await), and handle or
rethrow errors in the catch (e.g., surface an error state or call an onError
callback) to avoid closing the modal if onSubmit throws or rejects.
- Around line 74-89: The Modal is being unmounted by the conditional render
which bypasses PatternFly exit animations; instead keep the Modal mounted and
control visibility with the isOpen state: remove the ternary that returns null
and render <Modal ...> unconditionally, set its isOpen prop to the component's
isOpen state (the useState variable named isOpen), keep onClose wired to
handleClose, and continue passing close={handleClose} into
ManagedResourceSaveModal so focus/cleanup runs when Modal finishes its exit
transition.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: da7a2443-cc05-4147-bae8-f2b3d51f226b

📥 Commits

Reviewing files that changed from the base of the PR and between 874c37e and 1120313.

📒 Files selected for processing (11)
  • frontend/packages/console-shared/src/hooks/useLabelsModal.tsx
  • frontend/public/components/modals/add-secret-to-workload.tsx
  • frontend/public/components/modals/alert-routing-modal.tsx
  • frontend/public/components/modals/configure-cluster-upstream-modal.tsx
  • frontend/public/components/modals/configure-ns-pull-secret-modal.tsx
  • frontend/public/components/modals/configure-update-strategy-modal.tsx
  • frontend/public/components/modals/expand-pvc-modal.tsx
  • frontend/public/components/modals/labels-modal.tsx
  • frontend/public/components/modals/managed-resource-save-modal.tsx
  • frontend/public/components/modals/remove-volume-modal.tsx
  • frontend/public/style/_common.scss
💤 Files with no reviewable changes (1)
  • frontend/public/style/_common.scss

Comment on lines +196 to +219
<Form id="co-add-secret-to-workload" onSubmit={submit}>
<FormGroup
label={t('public~Add this secret to workload')}
isRequired
fieldId="co-add-secret-to-workload__workload"
>
<ConsoleSelect
items={workloadOptions}
selectedKey={selectedWorkloadUID}
title={selectWorkloadPlaceholder}
onChange={onWorkloadChange}
autocompleteFilter={autocompleteFilter}
autocompletePlaceholder={selectWorkloadPlaceholder}
id="co-add-secret-to-workload__workload"
data-test="add-secret-to-workload-button"
/>
</FormGroup>
<FormGroup
label={t('public~Add secret as')}
isRequired
role="radiogroup"
fieldId="co-add-secret-to-workload"
isStack
>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n frontend/public/components/modals/add-secret-to-workload.tsx | sed -n '190,320p'

Repository: openshift/console

Length of output: 5565


🏁 Script executed:

find frontend/public/components/modals -name "*.tsx" -type f | head -10

Repository: openshift/console

Length of output: 756


🏁 Script executed:

rg -A 8 'role="radiogroup"' frontend/public/components/modals/ -t tsx

Repository: openshift/console

Length of output: 88


🏁 Script executed:

rg -A 8 'role="radiogroup"' frontend/public/components/modals/ --type ts

Repository: openshift/console

Length of output: 2962


🏁 Script executed:

grep -n 'id="co-add-secret-to-workload"' frontend/public/components/modals/configure-cluster-upstream-modal.tsx | head -5

Repository: openshift/console

Length of output: 43


🏁 Script executed:

grep -n '<Form' frontend/public/components/modals/configure-cluster-upstream-modal.tsx | head -5

Repository: openshift/console

Length of output: 293


🏁 Script executed:

sed -n '95,180p' frontend/public/components/modals/configure-cluster-upstream-modal.tsx | cat -n

Repository: openshift/console

Length of output: 3899


🏁 Script executed:

rg 'aria-labelledby' frontend/public/components/modals/add-secret-to-workload.tsx

Repository: openshift/console

Length of output: 119


🏁 Script executed:

rg -B 2 -A 2 'fieldId=' frontend/public/components/modals/configure-ns-pull-secret-modal.tsx | head -20

Repository: openshift/console

Length of output: 315


🏁 Script executed:

rg 'fieldId="co-add-secret-to-workload' frontend/public/components/modals/ --type ts

Repository: openshift/console

Length of output: 697


Use a unique fieldId for the radio group.

The FormGroup at line 217 reuses the form's id (co-add-secret-to-workload) instead of using a unique identifier. This breaks the label-to-field association; the "Add secret as" label will reference the form element rather than the radio group. Change fieldId="co-add-secret-to-workload" to fieldId="co-add-secret-to-workload__add-as" to match the namespace pattern used by adjacent form fields.

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

In `@frontend/public/components/modals/add-secret-to-workload.tsx` around lines
196 - 219, The FormGroup that renders the radio group (label "Add secret as") is
reusing the form's id via fieldId="co-add-secret-to-workload", breaking
label-to-field association; update that FormGroup's fieldId to a unique
identifier such as "co-add-secret-to-workload__add-as" (same file's
add-secret-to-workload modal, FormGroup for the radio group) so the label
correctly targets the radio controls.

Comment on lines +278 to +287
<Button
type="submit"
variant="primary"
onClick={submit}
isLoading={inProgress}
isDisabled={inProgress}
data-test="confirm-action"
id="confirm-action"
form="co-add-secret-to-workload"
>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd frontend/public/components/modals && wc -l add-secret-to-workload.tsx

Repository: openshift/console

Length of output: 91


🏁 Script executed:

cd frontend/public/components/modals && sed -n '190,200p' add-secret-to-workload.tsx

Repository: openshift/console

Length of output: 552


🏁 Script executed:

cd frontend/public/components/modals && sed -n '275,290p' add-secret-to-workload.tsx

Repository: openshift/console

Length of output: 572


🏁 Script executed:

cd frontend/public/components/modals && sed -n '1,50p' add-secret-to-workload.tsx | head -100

Repository: openshift/console

Length of output: 2215


🏁 Script executed:

cd frontend/public/components/modals && grep -n "const submit" add-secret-to-workload.tsx

Repository: openshift/console

Length of output: 118


🏁 Script executed:

cd frontend/public/components/modals && grep -n "mountPath" add-secret-to-workload.tsx | head -20

Repository: openshift/console

Length of output: 246


🏁 Script executed:

cd frontend/public/components/modals && sed -n '100,180p' add-secret-to-workload.tsx

Repository: openshift/console

Length of output: 2816


🏁 Script executed:

cd frontend/public/components/modals && sed -n '220,275p' add-secret-to-workload.tsx

Repository: openshift/console

Length of output: 2254


🏁 Script executed:

cd frontend/public/components/modals && sed -n '125,145p' alert-routing-modal.tsx

Repository: openshift/console

Length of output: 621


Remove the onClick handler to let the form's validation run.

The button's onClick={submit} creates a secondary submission path that bypasses native form validation. Since the button already targets the form via form="co-add-secret-to-workload" and carries type="submit", the form's onSubmit handler fires automatically on click. The current setup allows submission without validating mountPath even though it's marked isRequired in the FormGroup. This diverges from the cleaner pattern in alert-routing-modal.tsx Lines 129–136, which relies entirely on the button's type and form attribute without a direct onClick handler.

♻️ Suggested fix
         <Button
           type="submit"
           variant="primary"
-          onClick={submit}
           isLoading={inProgress}
           isDisabled={inProgress}
           data-test="confirm-action"
           id="confirm-action"
           form="co-add-secret-to-workload"
📝 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
<Button
type="submit"
variant="primary"
onClick={submit}
isLoading={inProgress}
isDisabled={inProgress}
data-test="confirm-action"
id="confirm-action"
form="co-add-secret-to-workload"
>
<Button
type="submit"
variant="primary"
isLoading={inProgress}
isDisabled={inProgress}
data-test="confirm-action"
id="confirm-action"
form="co-add-secret-to-workload"
>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/public/components/modals/add-secret-to-workload.tsx` around lines
278 - 287, The Button component with id "confirm-action" currently has an
onClick={submit} which creates a secondary submission path that bypasses native
form validation; remove the onClick prop from the Button in
add-secret-to-workload.tsx so the button relies on type="submit" and
form="co-add-secret-to-workload" to trigger the form's onSubmit handler (submit)
and allow the browser/FormGroup (mountPath isRequired) validation to run
normally.

Comment on lines +307 to +315
return isOpen ? (
<Modal
variant={ModalVariant.small}
isOpen
onClose={handleClose}
aria-labelledby="add-secret-to-workload-modal-title"
>
<AddSecretToWorkloadModal close={handleClose} cancel={handleClose} {...props} />
</Modal>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find and examine the AddSecretToWorkloadModal type definitions
fd -t f "add-secret-to-workload" --exec cat {} \;

Repository: openshift/console

Length of output: 12095


🏁 Script executed:

# Search for AddSecretToWorkloadModalProps definition
rg "AddSecretToWorkloadModalProps" -t ts -t tsx -A 5 -B 2

Repository: openshift/console

Length of output: 88


🏁 Script executed:

# Check the type definition for ModalComponentProps
rg "ModalComponentProps" -t ts -t tsx -A 5 -B 2

Repository: openshift/console

Length of output: 88


🏁 Script executed:

# Examine the referenced alert-routing-modal.tsx at line 161
fd "alert-routing-modal.tsx" --exec cat {} \;

Repository: openshift/console

Length of output: 5910


🏁 Script executed:

# Examine the referenced configure-cluster-upstream-modal.tsx at line 210
fd "configure-cluster-upstream-modal.tsx" --exec cat {} \;

Repository: openshift/console

Length of output: 7555


🏁 Script executed:

# Search for ModalComponentProps definition in factory files
find . -name "factory*" -o -name "modal.ts" -o -name "modal.tsx" | head -20

Repository: openshift/console

Length of output: 795


🏁 Script executed:

# Search for ModalComponentProps type definition
rg "type ModalComponentProps" -A 5

Repository: openshift/console

Length of output: 447


🏁 Script executed:

# Also check for interface ModalComponentProps
rg "interface ModalComponentProps" -A 5

Repository: openshift/console

Length of output: 43


Reorder props to ensure modal cleanup executes properly.

AddSecretToWorkloadModalProps extends ModalComponentProps, which defines optional close and cancel properties. Since {...props} spreads after the explicit prop assignments on the current line, any close or cancel from props will overwrite handleClose, breaking the guarantee that both setIsOpen(false) and closeOverlay() execute together. This differs from the patterns in alert-routing-modal.tsx (Line 161) and configure-cluster-upstream-modal.tsx (Line 210), which correctly spread props first.

Suggested fix
-      <AddSecretToWorkloadModal close={handleClose} cancel={handleClose} {...props} />
+      <AddSecretToWorkloadModal {...props} close={handleClose} cancel={handleClose} />
📝 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
return isOpen ? (
<Modal
variant={ModalVariant.small}
isOpen
onClose={handleClose}
aria-labelledby="add-secret-to-workload-modal-title"
>
<AddSecretToWorkloadModal close={handleClose} cancel={handleClose} {...props} />
</Modal>
return isOpen ? (
<Modal
variant={ModalVariant.small}
isOpen
onClose={handleClose}
aria-labelledby="add-secret-to-workload-modal-title"
>
<AddSecretToWorkloadModal {...props} close={handleClose} cancel={handleClose} />
</Modal>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/public/components/modals/add-secret-to-workload.tsx` around lines
307 - 315, The modal currently spreads {...props} after explicitly passing
close={handleClose} and cancel={handleClose}, allowing incoming
props.close/props.cancel to override handleClose and break cleanup; update the
JSX so that {...props} is spread first on the Modal/AddSecretToWorkloadModal and
then explicitly pass close={handleClose} and cancel={handleClose} (referencing
AddSecretToWorkloadModal, Modal, handleClose) so handleClose (which calls
setIsOpen(false) and closeOverlay()) cannot be overwritten by incoming props.

Comment on lines +96 to +142
<FormGroup
label={t('public~Configuration')}
role="radiogroup"
fieldId="co-add-secret-to-workload"
isStack
>
<Radio
name="config-default"
id="config-default"
onChange={() => {
setCustomSelected(false);
setInvalidCustomURL(false);
}}
label={t('public~Default')}
isChecked={!customSelected}
body={
!customSelected && (
<>
<TextInput
id={'cluster-version-default-upstream-server-url'}
type="url"
readOnly
value={CLUSTER_VERSION_DEFAULT_UPSTREAM_SERVER_URL_PLACEHOLDER}
readOnlyVariant="default"
/>
<FormHelperText className="pf-v6-u-mt-sm">
<HelperText>
<HelperTextItem>
{t('public~Receive update information from Red Hat.')}
</HelperTextItem>
</HelperText>
</FormHelperText>
</>
)
}
/>
<Radio
name="config-custom"
id="config-custom"
onChange={() => {
setCustomSelected(true);
setTimeout(() => {
customURLInputRef.current?.focus();
}, 0);
}}
label={t('public~Custom update service')}
isChecked={customSelected}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

wc -l frontend/public/components/modals/configure-cluster-upstream-modal.tsx

Repository: openshift/console

Length of output: 135


🏁 Script executed:

sed -n '90,150p' frontend/public/components/modals/configure-cluster-upstream-modal.tsx

Repository: openshift/console

Length of output: 2308


🏁 Script executed:

# Check if "co-add-secret-to-workload" appears elsewhere in this file or is reused elsewhere
rg "co-add-secret-to-workload" frontend/public/components/modals/configure-cluster-upstream-modal.tsx

Repository: openshift/console

Length of output: 108


🏁 Script executed:

# Check if this pattern (different name attributes for grouped radios) appears elsewhere in the file
grep -n "name=" frontend/public/components/modals/configure-cluster-upstream-modal.tsx

Repository: openshift/console

Length of output: 139


🏁 Script executed:

# Look at similar FormGroup + Radio patterns in the codebase to understand best practices
rg -A 8 'role="radiogroup"' frontend/public/components/ -t tsx | head -60

Repository: openshift/console

Length of output: 88


🏁 Script executed:

# Search for radio group patterns in TypeScript files
rg -A 8 'role="radiogroup"' frontend/public/components/ -t ts | head -80

Repository: openshift/console

Length of output: 7090


🏁 Script executed:

# Also check in JSX files within the components directory
find frontend/public/components -name "*.tsx" -o -name "*.jsx" | xargs grep -l "role=\"radiogroup\"" 2>/dev/null | head -5

Repository: openshift/console

Length of output: 387


🏁 Script executed:

# Look at one example of proper radio group usage
fd "\.tsx$" frontend/public/components | head -1 | xargs grep -B 3 -A 15 'role="radiogroup"' | head -40

Repository: openshift/console

Length of output: 43


🏁 Script executed:

# Let me check the add-secret-to-workload.tsx to confirm the fieldId is copied from there
sed -n '40,80p' frontend/public/components/modals/add-secret-to-workload.tsx | grep -A 15 "FormGroup"

Repository: openshift/console

Length of output: 43


🏁 Script executed:

# Check the correct pattern: what should the name attributes be for radios in the same group?
sed -n '40,80p' frontend/public/components/modals/configure-ns-pull-secret-modal.tsx | grep -B 2 -A 10 "radiogroup"

Repository: openshift/console

Length of output: 43


🏁 Script executed:

# Check add-secret-to-workload.tsx structure
grep -n "FormGroup\|name=" frontend/public/components/modals/add-secret-to-workload.tsx | head -20

Repository: openshift/console

Length of output: 667


🏁 Script executed:

# Check configure-ns-pull-secret-modal.tsx structure
grep -n "FormGroup\|name=" frontend/public/components/modals/configure-ns-pull-secret-modal.tsx | head -20

Repository: openshift/console

Length of output: 326


🏁 Script executed:

# Get the full FormGroup and Radio section from configure-cluster-upstream-modal.tsx to understand context
sed -n '96,145p' frontend/public/components/modals/configure-cluster-upstream-modal.tsx

Repository: openshift/console

Length of output: 1820


🏁 Script executed:

# Check the form ID to understand naming convention
grep -n "form\|Form" frontend/public/components/modals/configure-cluster-upstream-modal.tsx | head -10

Repository: openshift/console

Length of output: 629


🏁 Script executed:

# Verify PatternFly FormGroup and Radio component expectations
web_search "PatternFly radio group name attribute same group best practice"

Repository: openshift/console

Length of output: 111


🌐 Web query:

PatternFly Radio component same name attribute radio group

💡 Result:

In PatternFly Radio (React), the name prop is the HTML name attribute used to group radios—so:

  • All radios in the same group must share the same name (so only one can be selected). (patternfly.org)
  • Different radio groups on the same page must use different name values, or they’ll behave like one big group (selecting in one group will unselect the other). (This is standard HTML radio behavior; PatternFly’s name maps to that.) (patternfly.org)

Example:

<Radio id="opt-a" name="billingMode" label="Monthly" />
<Radio id="opt-b" name="billingMode" label="Annual" />

<Radio id="ship-a" name="shippingSpeed" label="Standard" />
<Radio id="ship-b" name="shippingSpeed" label="Express" />

If you’re labeling the group with PatternFly FormGroup, pass role="radiogroup" for accessibility. (patternfly.org)

Citations:


Fix the radio-group name attributes and fieldId.

Lines 103 and 133 assign different name values ("config-default" and "config-custom"), which breaks native HTML radio grouping semantics. Both radios must share the same name to enable keyboard navigation and proper screen-reader labeling. Additionally, line 99's fieldId="co-add-secret-to-workload" is copied from an unrelated modal and should match this form's purpose (e.g., "cluster-version-config"). The controlled state keeps them visually exclusive, but the DOM no longer represents a single, properly labeled radio group.

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

In `@frontend/public/components/modals/configure-cluster-upstream-modal.tsx`
around lines 96 - 142, The FormGroup used as the radio group currently has an
unrelated fieldId ("co-add-secret-to-workload") and each Radio uses a different
name ("config-default" vs "config-custom"), breaking native radio grouping;
update the FormGroup.fieldId to a meaningful id like "cluster-version-config"
and make both Radio.name values identical (e.g., "cluster-version-config") so
the radios form a proper native group while keeping the existing
isChecked/onChange handlers in the Radio components.

Comment on lines +300 to +304
{fileData && (
<GridItem span={9} smOffset={3}>
<CodeBlock>
<CodeBlockCode>{fileData}</CodeBlockCode>
</CodeBlock>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t echo the uploaded pull-secret contents back into the DOM.

This file can contain reusable auth data or tokens. Rendering the raw JSON in a CodeBlock unnecessarily exposes secret material in the UI and accessibility tree; a filename, validation badge, or generic “file loaded” message is safer.

Safer pattern
- {fileData && (
-   <GridItem span={9} smOffset={3}>
-     <CodeBlock>
-       <CodeBlockCode>{fileData}</CodeBlockCode>
-     </CodeBlock>
-   </GridItem>
- )}
+ {fileData && (
+   <GridItem span={9} smOffset={3}>
+     <Alert
+       isInline
+       className="co-alert"
+       variant="success"
+       title={t('public~File loaded')}
+     >
+       {t('public~The Docker config file was validated and is ready to save.')}
+     </Alert>
+   </GridItem>
+ )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/public/components/modals/configure-ns-pull-secret-modal.tsx` around
lines 300 - 304, The modal currently renders uploaded pull-secret contents into
the DOM via fileData inside CodeBlock/CodeBlockCode (in
ConfigureNsPullSecretModal), which exposes sensitive tokens; instead stop
rendering raw fileData and replace it with a non-sensitive UI: show the uploaded
filename or a generic "file loaded" message and a validation/status badge, keep
any validation logic that inspects fileData but do not inject the raw JSON into
JSX or aria text, and ensure any existing references to fileData for display are
removed or replaced with a safeDisplayName variable used by the UI components.

Comment on lines +221 to +235
<ModalFooterWithAlerts errorMessage={errorMessage}>
<Button
type="submit"
variant="primary"
isLoading={inProgress}
isDisabled={inProgress}
data-test="confirm-action"
form="configure-update-strategy-form"
>
{t('public~Save')}
</Button>
<Button variant="link" onClick={cancel} data-test-id="modal-cancel-action">
{t('public~Cancel')}
</Button>
</ModalFooterWithAlerts>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the overlay close contract.
fd 'OverlayProvider\.(ts|tsx)$' frontend -t f -x sed -n '1,240p' {}

# Inspect the legacy modal footer behavior for in-progress submits.
sed -n '1,320p' frontend/public/components/factory/modal.tsx
rg -n -C3 'ModalSubmitFooter|inProgress|cancel' frontend/public/components/factory/modal.tsx

# Re-open the current flow with surrounding context.
rg -n -C4 'handlePromise|handleClose|closeOverlay|inProgress' \
  frontend/public/components/modals/configure-update-strategy-modal.tsx

Repository: openshift/console

Length of output: 14713


Disable every dismiss path while the patch is pending.

Only Save is gated by inProgress (lines 225–226). Cancel and the modal's onClose handler (lines 232, 253) remain active during the patch, allowing users to dismiss while k8sPatch runs in the background. This sacrifices error feedback and completion visibility. While closeOverlay is idempotent and double-calling it after the overlay is already gone is safe, the poor UX should be corrected: block Cancel/onClose while the request is in flight, or guard the close path with an inProgress check.

The legacy footer (ModalSubmitFooter) had the same issue—cancel was never gated on inProgress—so this is not a regression, but a pattern that should be improved here.

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

In `@frontend/public/components/modals/configure-update-strategy-modal.tsx` around
lines 221 - 235, Disable all dismiss paths while the patch is pending: add
inProgress guards to the Cancel button and the modal close handler so users
cannot dismiss while k8sPatch is running. Specifically, set the Cancel <Button>
(the one using cancel and data-test-id="modal-cancel-action") to
isDisabled={inProgress} and modify the modal's onClose/closeOverlay handler to
early-return when inProgress is true (or wrap cancel to check inProgress before
calling closeOverlay). Ensure ModalFooterWithAlerts/Modal onClose behavior and
the cancel function both respect the inProgress flag so neither Cancel nor
clicking outside/close icon will close the modal while the request is in flight.

Comment on lines +80 to +88
<RequestSizeInput
name={t('public~requestSize')}
required
onChange={handleRequestSizeInputChange}
defaultRequestSizeUnit={requestSizeUnit}
defaultRequestSizeValue={requestSizeValue}
dropdownUnits={dropdownUnits}
testID="pvc-expand-size-input"
minValue={defaultSize[0]}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check how RequestSizeInput uses the `name` prop to determine if translation is appropriate.

# Find the RequestSizeInput component definition
ast-grep --pattern $'const RequestSizeInput = ($$$) => {
  $$$
}'

# Also search for class-based or FC definition
rg -n -A 20 'RequestSizeInput.*=' frontend/public/components/utils/request-size-input

Repository: openshift/console

Length of output: 152


🏁 Script executed:

#!/bin/bash
# Find where RequestSizeInput is defined
rg -n "RequestSizeInput" frontend/public/components --type tsx --type ts -l

# Also search for where it's imported/exported
rg -n "export.*RequestSizeInput" frontend/public --type tsx --type ts

Repository: openshift/console

Length of output: 116


🏁 Script executed:

#!/bin/bash
# Search for RequestSizeInput definition without file type constraint
rg -n "RequestSizeInput" frontend/public/components --max-count=10

# Search for where it's exported
rg -n "export.*RequestSizeInput"

Repository: openshift/console

Length of output: 1325


🏁 Script executed:

#!/bin/bash
# Read the RequestSizeInput component definition
cat -n frontend/public/components/utils/request-size-input.tsx

Repository: openshift/console

Length of output: 3863


🏁 Script executed:

#!/bin/bash
# Check how RequestSizeInput is called in other places to see the pattern
rg -B 2 -A 8 "<RequestSizeInput" frontend/public/components/storage/create-pvc.tsx

Repository: openshift/console

Length of output: 433


🏁 Script executed:

#!/bin/bash
# Verify the exact code in expand-pvc-modal.tsx around line 80
sed -n '75,92p' frontend/public/components/modals/expand-pvc-modal.tsx

Repository: openshift/console

Length of output: 744


Use a static technical identifier for the name prop instead of a translated string.

Line 81 passes name={t('public~requestSize')}, but RequestSizeInput constructs HTML form field names from this prop (e.g., requestSizeValue, requestSizeUnit). Using a translated string means form field names vary by locale, breaking form serialization consistency and violating form best practices. Use a static identifier like create-pvc.tsx does: name="requestSize".

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

In `@frontend/public/components/modals/expand-pvc-modal.tsx` around lines 80 - 88,
The RequestSizeInput is receiving a translated string for its name prop
(name={t('public~requestSize')}), which causes generated form field names (e.g.,
requestSizeValue, requestSizeUnit) to vary by locale; change the prop to a
static identifier (name="requestSize") in the ExpandPVCModal where
RequestSizeInput is rendered so the form field names remain consistent across
locales and match how create-pvc.tsx uses it.

ModalTitle,
ModalWrapper,
} from '../factory/modal';
import { ModalComponentProps } from '../factory/modal';
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== ModalComponentProps definition =="
fd 'modal\.tsx?$' frontend/public/components/factory | while read -r file; do
  echo "== $file =="
  rg -n -C3 'export (type|interface) ModalComponentProps' "$file"
done

echo
echo "== Labels modal usages =="
rg -n -C2 '\bLabelsModalOverlay\b|\bLabelsModalProps\b' frontend -g '!**/*.test.*' -g '!**/*.spec.*'

echo
echo "== useLabelsModal hook =="
fd 'useLabelsModal\.tsx?$' frontend | while read -r file; do
  echo "== $file =="
  sed -n '1,220p' "$file"
done

Repository: openshift/console

Length of output: 4210


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Get the full labels-modal.tsx file with line numbers
echo "== Full labels-modal.tsx content =="
wc -l frontend/public/components/modals/labels-modal.tsx

echo
echo "== Lines 150-210 (context around line 182 and 164) =="
sed -n '150,210p' frontend/public/components/modals/labels-modal.tsx | cat -n --v

Repository: openshift/console

Length of output: 536


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Get the full labels-modal.tsx file with line numbers
echo "== Full labels-modal.tsx (all 197 lines) =="
sed -n '1,197p' frontend/public/components/modals/labels-modal.tsx | nl -ba

Repository: openshift/console

Length of output: 164


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Get the full labels-modal.tsx file with line numbers using cat
echo "== Full labels-modal.tsx (all 197 lines) =="
cat -n frontend/public/components/modals/labels-modal.tsx

Repository: openshift/console

Length of output: 7878


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== All references to LabelsModal(Overlay|Props) in codebase =="
rg -n 'LabelsModal(Overlay|Props)' frontend --type ts --type tsx

echo
echo "== Check OverlayComponent type definition =="
rg -n -C5 'type OverlayComponent' frontend --type ts --type tsx | head -50

echo
echo "== Check if any code passes cancel or close to any modal overlay =="
rg -n 'cancel\s*=' frontend/public/components/modals --type ts --type tsx | head -20

Repository: openshift/console

Length of output: 168


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== All references to LabelsModal(Overlay|Props) in codebase =="
rg -n 'LabelsModal(Overlay|Props)' frontend

echo
echo "== Check OverlayComponent type definition =="
rg -n -C5 'type OverlayComponent' frontend | head -50

echo
echo "== Check if any code passes cancel or close to LabelsModalOverlay =="
rg -n -B3 -A3 'LabelsModalOverlay' frontend | grep -E 'cancel|close'

Repository: openshift/console

Length of output: 2989


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== useCommonActions context around LabelsModalOverlay call (line 104) =="
sed -n '95,115p' frontend/packages/console-app/src/actions/hooks/useCommonActions.ts | cat -n

echo
echo "== Full useCommonActions file to understand launchModal usage =="
wc -l frontend/packages/console-app/src/actions/hooks/useCommonActions.ts
head -120 frontend/packages/console-app/src/actions/hooks/useCommonActions.ts | tail -40 | cat -n

Repository: openshift/console

Length of output: 2912


Decouple LabelsModalOverlay from ModalComponentProps contract.

The overlay owns the cancel and close callbacks (line 182 explicitly overrides them with handleClose), yet the public LabelsModalProps type inherits optional cancel/close from ModalComponentProps. This creates a silent contract violation: the type suggests callers can provide these, but the overlay always discards them. The unnecessary import of ModalComponentProps on line 23 couples the modal to the factory layer without benefit.

🧹 One way to decouple the overlay from `../factory/modal`
-import { ModalComponentProps } from '../factory/modal';
...
-type BaseLabelsModalProps = {
+type LabelsModalOwnProps = {
   descriptionKey?: string;
   isPodSelector?: boolean;
   kind: K8sModel;
   labelClassName?: string;
   messageKey?: string;
   messageVariables?: { [key: string]: string };
-  path: string;
   resource: K8sResourceKind;
-} & ModalComponentProps;
-export type LabelsModalProps = Omit<BaseLabelsModalProps, 'path'>;
+};
+
+type LabelsModalInjectedProps = {
+  cancel: () => void;
+  close: () => void;
+};
+
+type BaseLabelsModalProps = LabelsModalOwnProps &
+  LabelsModalInjectedProps & {
+    path: string;
+  };
+
+export type LabelsModalProps = LabelsModalOwnProps;
+type LabelsModalInternalProps = LabelsModalProps & LabelsModalInjectedProps;
...
-const LabelsModal: FC<LabelsModalProps> = (props) => (
+const LabelsModal: FC<LabelsModalInternalProps> = (props) => (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/public/components/modals/labels-modal.tsx` at line 23, The
LabelsModalOverlay is incorrectly coupled to ModalComponentProps and silently
overrides provided cancel/close handlers; remove the import of
ModalComponentProps and stop inheriting it in LabelsModalProps, instead declare
a local prop type that includes only the needed fields (e.g., labels, onSave,
isOpen) and excludes cancel/close so callers cannot pass them; update
LabelsModalOverlay to accept the new LabelsModalProps and keep using its
internal handleClose for closing logic; search for any consumers passing
cancel/close and update them to use the modal's public API (onSave or open
state) if needed.

Comment on lines +120 to +122
{descriptionKey
? t('{{description}} for', { description: t(descriptionKey) })
: t('public~Labels for')}{' '}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Keep this interpolated label in the public namespace.

Line 121 is the only user-facing string here without public~, so the "for" fragment will stay English in localized builds.

💬 Suggested fix
-                  ? t('{{description}} for', { description: t(descriptionKey) })
+                  ? t('public~{{description}} for', { description: t(descriptionKey) })
📝 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
{descriptionKey
? t('{{description}} for', { description: t(descriptionKey) })
: t('public~Labels for')}{' '}
{descriptionKey
? t('public~{{description}} for', { description: t(descriptionKey) })
: t('public~Labels for')}{' '}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/public/components/modals/labels-modal.tsx` around lines 120 - 122,
The interpolated "for" fragment is missing the public namespace; update the i18n
call used where descriptionKey is checked (the t(...) expression that produces
"{{description}} for") to use the public namespace (e.g., change the t
invocation for the interpolated string so it uses "public~" or move the whole
interpolation into a single public~ keyed string) so the "for" fragment is
localized consistently; locate the t call alongside descriptionKey in the
labels-modal component and apply the namespace there.

onClose={handleClose}
aria-labelledby="remove-volume-modal-title"
>
<RemoveVolumeModal close={handleClose} cancel={handleClose} {...props} />
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Props spread order may override controlled close handlers.

The close and cancel props are set before {...props} is spread, meaning if props contains close or cancel (which RemoveVolumeModalProps allows as optional), they would override handleClose. This could bypass closeOverlay() and leave the overlay in an inconsistent state.

Spread props first so the controlled handlers always take precedence.

🛠️ Proposed fix
-      <RemoveVolumeModal close={handleClose} cancel={handleClose} {...props} />
+      <RemoveVolumeModal {...props} close={handleClose} cancel={handleClose} />
📝 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
<RemoveVolumeModal close={handleClose} cancel={handleClose} {...props} />
<RemoveVolumeModal {...props} close={handleClose} cancel={handleClose} />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/public/components/modals/remove-volume-modal.tsx` at line 147, The
JSX spreads props after passing controlled handlers, so any close or cancel in
props can override handleClose; change the order to spread props first in the
RemoveVolumeModal element (so {...props} comes before close={handleClose}
cancel={handleClose}) to ensure the controlled handlers (handleClose /
closeOverlay via RemoveVolumeModalProps) always take precedence and cannot be
overridden by incoming props.

@rhamilto
Copy link
Member Author

rhamilto commented Mar 9, 2026

/retest

1 similar comment
@rhamilto
Copy link
Member Author

/retest

@rhamilto
Copy link
Member Author

/retest

@rhamilto
Copy link
Member Author

/unassign @sg00dwin
/assign @vojtechszocs

@openshift-ci openshift-ci bot assigned vojtechszocs and unassigned sg00dwin Mar 10, 2026
@rhamilto rhamilto changed the title CONSOLE-4447: Migrate core modals to PatternFly v6 (part 1) CONSOLE-4447: Migrate core modals to PatternFly v6 modal components (part 1) Mar 10, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Mar 10, 2026

@rhamilto: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-gcp-console 1120313 link true /test e2e-gcp-console

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@rhamilto
Copy link
Member Author

/retest

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. component/core Related to console core functionality component/knative Related to knative-plugin component/metal3 Related to metal3-plugin component/olm Related to OLM component/shared Related to console-shared component/topology Related to topology docs-approved Signifies that Docs has signed off on this PR jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. px-approved Signifies that Product Support has signed off on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants