Skip to content

[EuiFilePicker] Migrate from class to function component - #9879

Open
mehuljariwala wants to merge 5 commits into
elastic:mainfrom
mehuljariwala:migrate-file-picker-function-component
Open

[EuiFilePicker] Migrate from class to function component#9879
mehuljariwala wants to merge 5 commits into
elastic:mainfrom
mehuljariwala:migrate-file-picker-function-component

Conversation

@mehuljariwala

@mehuljariwala mehuljariwala commented Aug 8, 2026

Copy link
Copy Markdown

Summary

What: Migrates EuiFilePicker from a class component to a function component.

Why: Closes #9495.

How:

Before After
class EuiFilePickerClass extends Component FunctionComponent<EuiFilePickerProps>
withEuiStylesMemoizer HOC + stylesMemoizer prop useEuiMemoizedStyles(euiFilePickerStyles)
static contextType = FormContext useFormContext()
static defaultProps destructuring defaults
fileInput instance field + callback ref useRef<HTMLInputElement>
this.state / setState useState ×2
componentDidUpdate useEffect keyed on files
htmlIdGenerator()() useGeneratedHtmlId()
<EuiI18n> render-prop wrapper useEuiI18n()

Two decisions worth flagging for reviewers:

  1. The files effect keeps a previous-value ref. A bare useEffect(..., [files]) also runs on mount, which would trigger a redundant second render in the multi-file case, since getPromptTextFromFileList returns a fresh EuiI18n element that can never reference-compare equal. The ref guard makes the effect fire only on an actual prop change, matching componentDidUpdate exactly.

  2. promptText is deliberately not derived during render. The existing test clears file name when clear button is clicked clears the input while the files prop is still set, so re-deriving from props on every render would resurrect the filename and break it.

The EuiFilePickerClass export is removed along with the HOC. It was never re-exported from index.ts (only EuiFilePicker and EuiFilePickerProps are public), so this stays internal-only.

I also swapped htmlIdGenerator()() for useGeneratedHtmlId() while here, which clears one more function-component usage tracked in #7093.

One note on the issue description: it lists the class as 262 lines with Lifecycle: None, but it is currently 382 lines and does have a componentDidUpdate, handled as described above.

API Changes

None — this is an internal refactor.

component / parent prop / child change description
EuiFilePicker None Internal refactor only; no public API surface changed

Screenshots

No visual changes — rendered markup is identical, and the existing snapshot passes unmodified.

Impact Assessment

  • 🔴 Breaking changes
  • 💅 Visual changes
  • 🧪 Test impact
  • 🔧 Hard to integrate

Impact level: 🟢 None

Release Readiness

  • Documentation — no doc changes; behavior and API are unchanged
  • Figma — no design changes
  • Migration guide — no breaking or visual changes
  • Adoption plan — not a new feature

Changelog: this is an internal implementation change with no consumer-facing impact, so per the changelog guide I believe skip-changelog applies here — matching #9558, #9667 and #9831, which each migrated a component to a function component with no changelog entry. I'm happy to add one if you'd prefer; I can't apply the label myself.

QA instructions for reviewer

Open the EuiFilePicker Storybook stories:

  • Selecting a single file shows its filename in the prompt.
  • Selecting multiple files shows "N files selected".
  • The clear button appears once a file is selected, resets the prompt to initialPromptText, and calls onChange with null.
  • Dragging a file over the input applies the dropping state, and does not when disabled.
  • The files prop still controls displayed state, including when the prop reference changes.
  • fullWidth still inherits from a wrapping <EuiForm fullWidth>.
  • isLoading, isInvalid, compressed and display="default" render as before.

Checklist before marking Ready for Review

  • Filled out all sections above
  • QA: Tested light/dark modes, high contrast, mobile, Chrome/Safari/Edge/Firefox, keyboard-only, screen reader
  • QA: Tested in CodeSandbox and Kibana
  • QA: Tested docs changes — no docs changes
  • Tests: Existing Jest suite passes unchanged — 13 file_picker tests with the snapshot unmodified, and the full src/components/form suite (38 suites / 382 tests / 222 snapshots) green, plus tsc --noEmit and ESLint clean. No new tests added: the migration is behavior-preserving and the existing suite already covers the state and clear-button paths.
  • Changelog: see note above re skip-changelog
  • Breaking changes: label — none

@mehuljariwala
mehuljariwala requested a review from a team as a code owner August 8, 2026 11:02
@cla-checker-service

cla-checker-service Bot commented Aug 8, 2026

Copy link
Copy Markdown

💚 CLA has been signed

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

👋 Since this is a community submitted pull request, a Buildkite build has not been started automatically. Would an Elastic organization member please verify the contents of this pull request and kick off a build manually?

@github-actions github-actions Bot added the community contribution (Don't delete - used for automation) label Aug 8, 2026
@mehuljariwala mehuljariwala reopened this Aug 8, 2026
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

👋 Since this is a community submitted pull request, a Buildkite build has not been started automatically. Would an Elastic organization member please verify the contents of this pull request and kick off a build manually?

@weronikaolejniczak
weronikaolejniczak requested a lite review from Copilot August 10, 2026 07:40
@weronikaolejniczak

Copy link
Copy Markdown
Contributor

buildkite test this

@weronikaolejniczak weronikaolejniczak added the skip-changelog Use on PRs to skip changelog requirement (Don't delete - used for automation) label Aug 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Migrates EuiFilePicker from a class component to a function component while preserving behavior, markup, and the existing test suite expectations for controlled files display state, clearing, and drag-over styling.

Changes:

  • Converted class lifecycle/state/refs to hooks (useState, useRef, useEffect, useCallback) while keeping the files-driven prompt text update semantics aligned with the prior componentDidUpdate.
  • Replaced legacy patterns with hook equivalents (useFormContext, useEuiMemoizedStyles, useGeneratedHtmlId, useEuiI18n) and removed the withEuiStylesMemoizer HOC/class export.
  • Kept prompt text state intentionally independent from files during render to preserve the existing “clear while files is still set” behavior covered by tests.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/eui/src/components/form/file_picker/file_picker.tsx Outdated
Comment thread packages/eui/src/components/form/file_picker/file_picker.tsx Outdated

@weronikaolejniczak weronikaolejniczak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The refactor works great 💪🏻 I didn't notice any regression compared to prod and VRTs pass.

One improvement we could do: add a "MultipleFiles" story that passes the multiple prop or have the control to toggle this in "Playground" story.

Thanks for contributing, @mehuljariwala 🙌🏻

mehuljariwala and others added 2 commits August 10, 2026 17:48
Co-authored-by: Weronika Olejniczak <32842468+weronikaolejniczak@users.noreply.github.com>
Co-authored-by: Weronika Olejniczak <32842468+weronikaolejniczak@users.noreply.github.com>
@mehuljariwala

Copy link
Copy Markdown
Author

Hello @weronikaolejniczak,

Thank you for taking the time to review this. I have a well-defined plan to migrate all the components one by one, which will allow us to maintain a clean codebase and ensure everything remains well-structured and manageable throughout the migration.

@weronikaolejniczak

Copy link
Copy Markdown
Contributor

@mehuljariwala please be aware we receive contributions only for issues labeled as "help wanted".

@mehuljariwala

Copy link
Copy Markdown
Author

@mehuljariwala please be aware we receive contributions only for issues labeled as "help wanted".

Yes sounds good

@weronikaolejniczak

Copy link
Copy Markdown
Contributor

@mehuljariwala there's a syntax error. I would advise to test your changes properly before pushing. Spin up Storybook, documentation website! 😄

@mehuljariwala

Copy link
Copy Markdown
Author

@mehuljariwala there's a syntax error. I would advise to test your changes properly before pushing. Spin up Storybook, documentation website! 😄

Yes done thank you @weronikaolejniczak

styles.euiFilePicker__prompt,
disabled && styles.disabled,
...(normalFormControl
? [compressed ? styles.compressed : styles.uncompressed]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@mehuljariwala you've addressed the typo but you haven't actually went and updated the style 🤔

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Oh yeah correct give me one moment i am checking on that as well

@infra-vault-gh-plugin-prod

infra-vault-gh-plugin-prod Bot commented Aug 10, 2026

Copy link
Copy Markdown

💔 Build Failed

Failed CI Steps

History

… style

The showDrop suggestion was applied over the useCallback body, leaving a
stray closing brace and dependency array behind, which broke parsing and
failed every Buildkite build.

Simplify hideDrop to match, and finish the typo fix by renaming the
icon style key itself from 'compresssed' to 'compressed'.
@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

💔 Build Failed

Failed CI Steps

History

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

Labels

community contribution (Don't delete - used for automation) skip-changelog Use on PRs to skip changelog requirement (Don't delete - used for automation)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[EuiFilePicker] Migrate from class to function component

3 participants