CONSOLE-5015: Refactor file-input to use PF#15952
CONSOLE-5015: Refactor file-input to use PF#15952openshift-merge-bot[bot] merged 1 commit intoopenshift:mainfrom
Conversation
|
@logonoff: This pull request references CONSOLE-5015 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. DetailsIn response to this:
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. |
b242ead to
4f2d07b
Compare
7cfc3b6 to
812b830
Compare
|
@logonoff: This pull request references CONSOLE-5015 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. DetailsIn response to this:
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. |
|
/assign @rhamilto |
|
/label px-approved |
|
/assign @yapei |
|
@logonoff: This pull request references CONSOLE-5015 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. DetailsIn response to this:
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. |
|
@logonoff: This pull request references CONSOLE-5015 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. DetailsIn response to this:
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. |
| onChange={onChange} | ||
| inputFileData={value} | ||
| id={id} | ||
| label={t('public~CA file')} |
|
performed manual regression about IDP configuration, secret creation, no regression issues found |
| label={t('public~Key')} | ||
| hideContents | ||
| inputFieldHelpText={t( | ||
| filenamePlaceholder={t( |
812b830 to
4721454
Compare
|
@logonoff: This pull request references CONSOLE-5015 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. DetailsIn response to this:
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. |
📝 WalkthroughWalkthroughThis pull request refactors form field components and file input handling across the console codebase. The cypress-file-upload dependency is removed and replaced with native Cypress selectFile APIs. The DroppableFileInput component is rewritten from a class-based implementation to a functional component using PatternFly FileUpload with enhanced error handling and binary file detection. The FieldProps interface is simplified by moving label and helpText properties to individual field-type interfaces (BaseInputFieldProps, DropdownFieldProps, NumberSpinnerField, and others). Test suites for identity provider configurations are converted from synchronous to asynchronous patterns. Additional localization strings and stylesheet updates support the new file upload UI and form styling adjustments. 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/packages/console-shared/src/components/formik-fields/DroppableFileInputField.tsx (1)
22-28:⚠️ Potential issue | 🟡 MinoronChange signature mismatch with underlying DroppableFileInput.
The
DroppableFileInputcomponent now expectsonChange: (inputFileData: string, inputFileIsBinary: boolean) => void, but this wrapper only passesfileData: stringto the callback. The binary flag is silently discarded.If consumers of
DroppableFileInputFieldneed to know whether uploaded content is binary (e.g., for proper encoding when submitting to the API), this information is lost.🔧 Proposed fix to propagate binary flag
<DroppableFileInput id={fieldId} label={label} - onChange={(fileData: string) => { + onChange={(fileData: string, isBinary: boolean) => { setFieldValue(name, fileData); - onChange && onChange(fileData); + onChange && onChange(fileData, isBinary); }} inputFileData={field.value} filenamePlaceholder={helpText} aria-describedby={helpText ? `${fieldId}-helper` : undefined} />You'll also need to update
DroppableFileInputFieldPropsinfield-types.tsto reflect the new signature:onChange?: (fileData: string, isBinary: boolean) => void;
🤖 Fix all issues with AI agents
In
`@frontend/packages/console-shared/src/components/formik-fields/field-types.ts`:
- Around line 18-22: Change the DroppableFileInputFieldProps interface so its
label prop accepts React.ReactNode instead of string to match
BaseInputFieldProps/TextAreaProps and PatternFly FormGroup; update the label
declaration in DroppableFileInputFieldProps and add any necessary React import
(React or type-only) so consumers can pass rich content (icons/JSX) to the label
while keeping helpText as string.
In `@frontend/public/components/utils/file-input.tsx`:
- Around line 54-58: The initial call to isBinary using filename (which starts
as '') can mis-detect when inputFileData is pre-populated; change initialization
of inputFileIsBinary to a safe default (e.g., false) and move the actual binary
detection into a useEffect that runs when filename or inputFileData changes and
only computes isBinary(filename, Buffer.from(inputFileData)) when a file has
been selected (e.g., filename is non-empty or inputFileData is non-empty),
updating state via setInputFileIsBinary; reference inputFileIsBinary,
setInputFileIsBinary, filename, inputFileData, and isBinary to locate where to
make this change.
🧹 Nitpick comments (6)
frontend/public/components/secrets/create-secret/PullSecretUploadForm.tsx (1)
45-48: Consider preserving original error context in catch block.While the error state is correctly set, the original exception
eis silently discarded. For debugging purposes, consider logging or inspecting the error to differentiate between JSON syntax errors and other unexpected failures.💡 Optional: Log the parse error for debugging
} catch (e) { + // eslint-disable-next-line no-console + console.warn('Failed to parse pull secret JSON:', e); setParseError(true); onFormDisable(true); }frontend/public/components/cluster-settings/__tests__/test-utils.ts (1)
137-148: Consider adding a more descriptive assertion message for the container check.The
.closest()traversal is acceptable, but if the PF class name changes in future versions, this could silently fail. The assertion at line 138 would benefit from a clearer failure message.💡 Optional: Add descriptive assertion message
// Find the file upload container by traversing up from the filename input const fileUploadContainer = filenameInput.closest('.pf-v6-c-file-upload'); - expect(fileUploadContainer).toBeInTheDocument(); + expect(fileUploadContainer).toBeInTheDocument(); // PatternFly v6 FileUpload container // Verify browse button is visible within the container const browseButton = within(fileUploadContainer as HTMLElement).getByRole('button', { name: 'Browse...', }); expect(browseButton).toBeVisible(); // Find the hidden file input element within the container const fileInput = fileUploadContainer.querySelector('input[type="file"]') as HTMLInputElement; - expect(fileInput).toBeTruthy(); + expect(fileInput).toBeTruthy(); // Hidden file input must exist within FileUploadfrontend/public/components/utils/file-input.tsx (1)
181-203: Helper text structure could be simplified.The nested
FileUploadHelperText > HelperText > HelperTextItemhierarchy is verbose. Consider consolidating error messages into a singleHelperTextblock to reduce DOM nesting.💡 Optional: Consolidate helper text rendering
- {textareaFieldHelpText || hasError ? ( - <FileUploadHelperText id={`${id}-textarea-help`}> - {textareaFieldHelpText ? ( - <HelperText id={`${id}-help`}> - <HelperTextItem>{textareaFieldHelpText}</HelperTextItem> - </HelperText> - ) : null} - {errorMessage ? ( - <FormHelperText> - <HelperText> - <HelperTextItem variant="error">{errorMessage}</HelperTextItem> - </HelperText> - </FormHelperText> - ) : null} - {uploadErrorMessage ? ( - <FormHelperText> - <HelperText> - <HelperTextItem variant="error">{uploadErrorMessage}</HelperTextItem> - </HelperText> - </FormHelperText> - ) : null} - </FileUploadHelperText> - ) : null} + {(textareaFieldHelpText || hasError) && ( + <FileUploadHelperText id={`${id}-textarea-help`}> + <HelperText id={`${id}-help`}> + {textareaFieldHelpText && ( + <HelperTextItem>{textareaFieldHelpText}</HelperTextItem> + )} + {errorMessage && ( + <HelperTextItem variant="error">{errorMessage}</HelperTextItem> + )} + {uploadErrorMessage && ( + <HelperTextItem variant="error">{uploadErrorMessage}</HelperTextItem> + )} + </HelperText> + </FileUploadHelperText> + )}frontend/packages/console-shared/src/components/formik-fields/SelectorInputField.tsx (1)
46-50: FormHelperText renders empty DOM when helpText is undefined.The
HelperTextItemwill render an empty element whenhelpTextis undefined, adding unnecessary DOM nodes. Consider conditionally rendering the helper text block.♻️ Proposed fix to conditionally render helper text
- <FormHelperText> - <HelperText> - <HelperTextItem>{helpText}</HelperTextItem> - </HelperText> - </FormHelperText> + {helpText && ( + <FormHelperText> + <HelperText> + <HelperTextItem>{helpText}</HelperTextItem> + </HelperText> + </FormHelperText> + )}frontend/public/components/cluster-settings/htpasswd-idp-form.tsx (1)
16-21: Extract the AsyncComponent wrapper to a shared utility module.This AsyncComponent wrapper pattern is duplicated across four cluster-settings files:
htpasswd-idp-form.tsx,basicauth-idp-form.tsx,keystone-idp-form.tsx, andidp-cafile-input.tsx. A TODO comment infrontend/public/components/secrets/create-secret/DropableFileInput.tsxacknowledges this same duplication concern. Consider extracting to a shared location in the cluster-settings directory or reusing the existingDroppableFileInputFieldfromconsole-sharedif the formik integration is compatible.frontend/packages/console-shared/src/components/formik-fields/NumberSpinnerField.tsx (1)
57-68: Consider conditional rendering for empty helpText.The
aria-describedbyis conditionally set (line 57), but theFormHelperTextblock always renders when there's no error. IfhelpTextisundefinedor empty, you'll render an emptyHelperTextItemnode.This is minor since PatternFly handles empty children gracefully, but for cleaner DOM output, consider:
♻️ Suggested refinement
<FormHelperText> <HelperText> {!isValid ? ( <HelperTextItem variant="error">{errorMessage}</HelperTextItem> - ) : ( + ) : helpText ? ( <HelperTextItem>{helpText}</HelperTextItem> - )} + ) : null} </HelperText> </FormHelperText>
| export interface DroppableFileInputFieldProps extends FieldProps { | ||
| onChange?: (fileData: string) => void; | ||
| helpText?: string; | ||
| label?: string; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, let's find the file and examine the interface
cat -n frontend/packages/console-shared/src/components/formik-fields/field-types.ts | head -50Repository: openshift/console
Length of output: 1913
🏁 Script executed:
#!/bin/bash
# Search for DroppableFileInputField usage across the codebase
rg -n "DroppableFileInputField" --type=ts --type=tsx -A 2 -B 1 2>/dev/null | head -80Repository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Compare with other FieldProps interfaces to see how they type helpText and label
rg -n "helpText\?.*:" frontend/packages/console-shared/src/components/formik-fields/ --type=ts --type=tsx -B 2 2>/dev/nullRepository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Check for React.ReactNode usage in similar props
rg -n "label\?.*React\.ReactNode\|helpText\?.*React\.ReactNode" frontend/packages/console-shared/src/components/formik-fields/ --type=ts --type=tsx 2>/dev/nullRepository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Find the DroppableFileInputField component file
fd -t f "DroppableFileInputField\|droppable.*file" frontend/packages/console-shared/src/components/formik-fields/ 2>/dev/nullRepository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Search in the entire formik-fields directory for DroppableFileInputField
rg -l "DroppableFileInputField" frontend/packages/console-shared/src/components/formik-fields/ 2>/dev/nullRepository: openshift/console
Length of output: 414
🏁 Script executed:
#!/bin/bash
# Search more broadly across the entire codebase for usage
rg "DroppableFileInputField" --type=ts --type=tsx 2>/dev/null | head -30Repository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Check the entire formik-fields directory structure
ls -la frontend/packages/console-shared/src/components/formik-fields/Repository: openshift/console
Length of output: 2736
🏁 Script executed:
#!/bin/bash
# Examine the DroppableFileInputField component implementation
cat -n frontend/packages/console-shared/src/components/formik-fields/DroppableFileInputField.tsxRepository: openshift/console
Length of output: 1380
🏁 Script executed:
#!/bin/bash
# Search for actual usage of DroppableFileInputField to see what props are passed
rg "DroppableFileInputField" frontend/packages/console --type=ts --type=tsx -A 5 2>/dev/null | grep -A 5 "helpText\|label"Repository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Find and examine the DroppableFileInput component it depends on
fd -t f "DroppableFileInput\|file-input" frontend/packages/internal/src/components/utils/ 2>/dev/nullRepository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Search for DroppableFileInput definition
rg "interface DroppableFileInputProps\|type DroppableFileInputProps\|const DroppableFileInput.*:.*FC" frontend/packages/internal/src/components/utils/ --type=ts --type=tsx -A 10 2>/dev/nullRepository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Look for any usage examples in tests or documentation
rg "DroppableFileInputField" frontend/packages/console --type=ts --type=tsx -B 2 -A 10 2>/dev/null | head -80Repository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Find the file-input file in internal components
find frontend/packages/internal -name "*file-input*" -type f 2>/dev/nullRepository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Search for DroppableFileInput definition more broadly
rg "export.*DroppableFileInput" frontend/packages/internal --type=ts --type=tsx -A 15 2>/dev/null | head -100Repository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Check what PatternFly FormGroup expects for label
rg "FormGroup" frontend/packages/console-shared/src/components/formik-fields/ --type=ts --type=tsx -B 2 -A 2 2>/dev/null | grep -A 5 "label\|helpText"Repository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Check how other FormGroup usages handle label in the codebase
rg "FormGroup.*label" frontend/packages/console-shared/src/components/formik-fields/ --type=tsx -B 1 -A 1 2>/dev/null | head -40Repository: openshift/console
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Look at BaseInputField to see how it handles the React.ReactNode label/helpText
cat -n frontend/packages/console-shared/src/components/formik-fields/BaseInputField.tsx | head -60Repository: openshift/console
Length of output: 2074
🏁 Script executed:
#!/bin/bash
# Check if helpText is rendered in BaseInputField's FormHelperText to see PatternFly support
cat -n frontend/packages/console-shared/src/components/formik-fields/BaseInputField.tsx | tail -20Repository: openshift/console
Length of output: 713
🏁 Script executed:
#!/bin/bash
# Search for usage of DroppableFileInputField in actual forms
rg -B 5 -A 10 "DroppableFileInputField\|droppable-file\|file.*input.*field" frontend/packages/console --type=tsx 2>/dev/null | head -100Repository: openshift/console
Length of output: 43
Consider aligning label with standard field component types.
The helpText string constraint is appropriate here—it's used as filenamePlaceholder, which requires a string value. However, label should follow the React.ReactNode pattern established by BaseInputFieldProps and TextAreaProps (similar field components). This would enable consumers to pass rich content (icons, formatted text) to the label without creating friction. Since FormGroup (PatternFly) accepts label?: React.ReactNode, there's no implementation barrier to aligning this interface.
🤖 Prompt for AI Agents
In `@frontend/packages/console-shared/src/components/formik-fields/field-types.ts`
around lines 18 - 22, Change the DroppableFileInputFieldProps interface so its
label prop accepts React.ReactNode instead of string to match
BaseInputFieldProps/TextAreaProps and PatternFly FormGroup; update the label
declaration in DroppableFileInputFieldProps and add any necessary React import
(React or type-only) so consumers can pass rich content (icons/JSX) to the label
while keeping helpText as string.
| const [filename, setFilename] = useState<string>(''); | ||
| const [uploadErrorMessage, setUploadErrorMessage] = useState<string>(''); | ||
| const [inputFileIsBinary, setInputFileIsBinary] = useState<boolean>( | ||
| isBinary(filename, Buffer.from(inputFileData)), | ||
| ); |
There was a problem hiding this comment.
Potential issue with initial binary detection when inputFileData is pre-populated.
The initial state for inputFileIsBinary calls isBinary(filename, Buffer.from(inputFileData)) where filename is initialized to empty string. If inputFileData is pre-populated (e.g., from saved form state), the binary detection may be inaccurate since isBinary uses filename extension hints.
Consider deferring binary detection until a file is actually selected, or ensure callers don't pre-populate inputFileData without also managing the binary state externally.
🤖 Prompt for AI Agents
In `@frontend/public/components/utils/file-input.tsx` around lines 54 - 58, The
initial call to isBinary using filename (which starts as '') can mis-detect when
inputFileData is pre-populated; change initialization of inputFileIsBinary to a
safe default (e.g., false) and move the actual binary detection into a useEffect
that runs when filename or inputFileData changes and only computes
isBinary(filename, Buffer.from(inputFileData)) when a file has been selected
(e.g., filename is non-empty or inputFileData is non-empty), updating state via
setInputFileIsBinary; reference inputFileIsBinary, setInputFileIsBinary,
filename, inputFileData, and isBinary to locate where to make this change.
| value={inputFileData} | ||
| filename={filename} | ||
| filenamePlaceholder={filenamePlaceholder} | ||
| filenameAriaLabel={t('{{label}} filename', { label })} // Make the 'aria-label' unique since 'input' and 'textarea' fields share the same 'id'. |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, locate and examine the file-input.tsx file
find . -name "file-input.tsx" -type fRepository: openshift/console
Length of output: 110
🏁 Script executed:
# Read the file-input.tsx file to see the exact code
cat -n "frontend/public/components/utils/file-input.tsx" | head -200Repository: openshift/console
Length of output: 8338
🏁 Script executed:
# Search for DroppableFileInput usage with label prop across the codebase
rg "DroppableFileInput" -t tsx -A 5 -B 2 | head -100Repository: openshift/console
Length of output: 88
🏁 Script executed:
# Search for DroppableFileInput usage with label prop
rg "DroppableFileInput" -t ts -t tsx -A 5 -B 2Repository: openshift/console
Length of output: 88
🏁 Script executed:
# Also search for any FormGroup label usage patterns (since that's also at line 121)
rg "label={label}" --type tsx -B 3 -A 3 | head -150Repository: openshift/console
Length of output: 163
🏁 Script executed:
# Search for DroppableFileInput without file type issues
rg "DroppableFileInput" -A 5 -B 2 | head -150Repository: openshift/console
Length of output: 13956
🏁 Script executed:
# Search for label prop passed to DroppableFileInput
rg "label=" frontend/public/components/utils/file-input.tsxRepository: openshift/console
Length of output: 180
🏁 Script executed:
# Search more broadly for how this component is used
rg "DroppableFileInput" --type-list | grep -i tsxRepository: openshift/console
Length of output: 450
🏁 Script executed:
# List TypeScript/TSX files to understand the structure
find frontend -name "*.tsx" -o -name "*.ts" | head -20Repository: openshift/console
Length of output: 1073
🏁 Script executed:
# Search for imports of DroppableFileInput
rg "from.*file-input"Repository: openshift/console
Length of output: 1259
🏁 Script executed:
# Search for DroppableFileInput component usage
rg "DroppableFileInput" | head -100Repository: openshift/console
Length of output: 5139
🏁 Script executed:
# Check the DroppableFileInputField wrapper
cat -n "frontend/packages/console-shared/src/components/formik-fields/DroppableFileInputField.tsx"Repository: openshift/console
Length of output: 1380
🏁 Script executed:
# Search for any non-i18n label usages or ReactNode-like values
rg "label=\{(?!t\()" frontend/public/components --type ts --type tsx -A 2 | grep -i droppableRepository: openshift/console
Length of output: 287
🏁 Script executed:
# Check if there are any cases where label is computed or not from i18n
rg "DroppableFileInput" -B 5 -A 10 | grep -E "(label=|const.*label)" | head -50Repository: openshift/console
Length of output: 2023
Address type safety for a11y: label should be constrained to string for aria-label interpolation.
The label prop is typed as ReactNode (line 31), but interpolated into aria-label strings via t('{{label}} filename', { label }) (lines 129 and 168). This creates a type safety gap—while all current usages pass translated strings from t(), the permissive type signature allows JSX elements to be passed, which would render as "[object Object] filename" in the accessibility tree.
Either narrow the type to string, document that label must be a string for a11y, or introduce a separate ariaLabelPrefix prop for explicit control.
- Remove `label`, `helpText`, `helpTextInvalid` from `FieldProps` because it's not common to all fields - Remove `hideContents` as a bug previously made this prop not work (we would only hideContents based on if the input file is binary). Now I consider having contents a feature - Rename inputFieldHelpText prop to filenamePlaceholder for PF alignment - Align `co-required` style with PF
4721454 to
d6ff285
Compare
|
thanks for the updates, now everything looks good
|
|
@yapei: This PR has been marked as verified by DetailsIn response to this:
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. |
|
|
||
| // removes extra spacing between file input form groups when hideDefaultPreview is true | ||
| .co-file-input { | ||
| .pf-v6-c-file-upload__file-details:empty { |
There was a problem hiding this comment.
Nit: no need to nest.
| .pf-v6-c-file-upload__file-details:empty { | |
| .co-file-input .pf-v6-c-file-upload__file-details:empty { |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: logonoff, rhamilto The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
/retest |
|
@logonoff: all tests passed! Full PR test history. Your PR dashboard. DetailsInstructions 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. |




label,helpText,helpTextInvalidfromFieldPropsbecause it's not common to all fieldshideContentsas a bug previously made this prop not work (we would only hideContents based on if the input file is binary). Now I consider having contents a featureco-requiredstyle with PFDroppableFileInputis now under the PF form ecosystem instead of the legacy one. That means that to useDroppableFileInputin a legacy form, you'll have to wrap it in apf-v6-c-formfor proper styling of the form group labelSummary by CodeRabbit
Release Notes
New Features
Bug Fixes
Chores