Skip to content

Migrate settings and admin forms from antd Form to core HookForm - #35285

Closed
shah-harshit wants to merge 32 commits into
shah-harshit/antd-select-input-standalonefrom
shah-harshit/antd-forms-settings
Closed

shah-harshit wants to merge 32 commits into
shah-harshit/antd-select-input-standalonefrom
shah-harshit/antd-forms-settings

Conversation

@shah-harshit

@shah-harshit shah-harshit commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Describe your changes:

Part of open-metadata/openmetadata-collate#6893 (epic open-metadata/openmetadata-collate#6884).

PR 2 of 3 for the Form / Select / Input migration: settings and admin forms move from antd Form to react-hook-form + @openmetadata/ui-core-components (HookForm, FormField, FormFields, Input, Select, Autocomplete, TimePicker).

Stacked on #35280. Base is shah-harshit/antd-select-input-standalone; it will be retargeted to main once #35280 merges.

Type of change:

  • Improvement

High-level design:

Where AI mode already had a core version of a form, the classic page now reuses it instead of keeping a second antd copy:

  • AccessControlRuleForm for add/edit rule (classic RuleForm deleted)
  • AI email-settings mappers in EmailConfigForm
  • FieldValueBoostDialog in classic search settings (classic FieldValueBoostModal deleted)
  • AccessControlDebuggerPanel in the classic permission debugger page
  • Shared CustomPropertyFormFields + payload utils (extracted from the AI add page) for the classic add-custom-property drawer/page
  • Shared useLearningResourceFields for the classic learning-resource drawer

Migrated in place:

  • Modals: ProfileEditModal, LineageConfigModal, TestEmail, ChangePasswordForm, ModalWithQueryEditor, ModalWithFunctionEditor
  • Settings pages: login/URL configuration, policies, roles, theme (AppearanceConfigSettingsPage), data asset rules
  • Users/teams/bots: CreateUser, AddTeamForm, AuthMechanismForm, team email + subscription, AddEditPersonaForm (its unreachable edit branch dropped)
  • AddQueryPage, AddKPIPage / EditKPIPage (shared KPIFormFields), LineageConfigPage, TableConstraintsModal (foreign-key field array; parsing in TableConstraintsModal.utils), TaskFormSettingsPage + TaskFormBuilderSection

Core:

  • inputDataTestId added to FieldPropsMap; PasswordInput forwards it.
  • HookForm defaults to react-aria validationBehavior="aria", so a native required no longer blocks submit before react-hook-form can show inline errors (covered by hook-form.test.tsx).

Playwright helpers move from antd class selectors to role-based locators for the touched forms; antd/positional suppression baselines shrink accordingly.

Behaviour changes:

  • Inline validation: errors show under each field on submit (react-hook-form) instead of antd's form-level validation.
  • KPI target: the slider loses its 0/100% marks; a percentage target above 100 is rejected inline (the antd InputNumber max used to clamp it).
  • Multi-selects (roles, policies, categories, contexts, …) have no clear-all button; chips are removed one at a time.
  • Task form settings: options are added one per Enter.
  • Table constraints: the related column is picked from a plain FQN list.
  • Learning resource drawer no longer closes on a mask click, so picking a select option cannot dismiss it.

Screenshots (light, classic mode — before = main, after = this branch):

Form Before (main) After
Create user create-user-before create-user-after
Create bot create-bot-before create-bot-after
Add role add-role-before add-role-after
Add policy add-policy-before add-policy-after
Add custom property add-custom-property-before add-custom-property-after
Add KPI add-kpi-before add-kpi-after
Learning resource drawer learning-resource-before learning-resource-after

Testing:

  • yarn ui-checkstyle:changed, yarn lint:playwright, yarn test:eslint-rules, rule-table check: pass
  • jest --findRelatedTests on changed files: 883 suites / 11,393 tests pass
  • core vitest run: 77 files / 797 tests pass
  • Local Playwright (chromium, against :8585): Users, Teams, Roles/Policies, Bots, Persona, KPI/DataInsight, TableConstraint, QueryEntity, TaskFormSettings, CustomThemeConfig, LineageSettings, LearningResources pass (Teams "Export team" CSV job did not complete locally — under investigation; CustomProperties/DataInsightSettings rerun pending)
  • tsc on changed files: clean (one pre-existing error in EntitySearchSettings.tsx untouched)

Checklist:

  • I have read the CONTRIBUTING document.
  • I have commented on my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

✅ SSO Login Nightly — PASSED

View workflow run · Commit f3a174d

Provider Status Pass Fail Flaky Skip Duration
basic ✅ pass 28 0 0 0 6m 34s
ldap ✅ pass 7 0 0 0 1m 07s
keycloak-oidc-confidential ✅ pass 12 0 0 0 1m 53s
keycloak-oidc-public ✅ pass 11 0 0 0 1m 35s
msal-mock ✅ pass 7 0 0 0 0m 54s
auth0-mock ✅ pass 6 0 0 0 0m 59s
okta ⏩ skipped - - - - -
keycloak-azure-saml ✅ pass 23 0 0 0 6m 22s
keycloak-azure-saml-crosssite ⏩ skipped - - - - -

Totals: 94 passed · 0 failed · 0 flaky · 0 skipped · duration 19m 28s

shah-harshit and others added 21 commits October 13, 2026 16:15
…arch

The navbar search-index select is now the core Select, which renders a
hidden native <select> whose options (Table, Topic, Data Product, ...)
match the Customize UI card titles, so page-wide getByText(type, { exact })
lookups resolve to two elements. Give the CustomizeUI grid a test id and
scope the card lookups to it.

LineageControls still filled the antd .ant-select-selection-search-input;
fill the core combobox inside search-entity-select instead.
getByTitle('All').nth(1) relied on the antd navbar search-index select
contributing the first title="All"; with the core Select there is only
one, so nth(1) never resolved and the TestCase filters test timed out.
Scope the click to the open antd dropdown instead, and prune the now
unused positional-locator suppression.
ProfileEditModal, LineageConfigModal, TestEmail, ChangePasswordForm,
ModalWithQueryEditor and ModalWithFunctionEditor now use react-hook-form
with core FormFields/FormField and core Input/PasswordInput.

Core: FieldPropsMap gains inputDataTestId so Input-based fields keep test
ids on the inner input, and PasswordInput forwards inputDataTestId.
- Login/URL configuration pages use core FormFields/Input; numbers are
  converted on submit, and doc-panel focus tracking moves to a wrapper.
- Add policy, add rule and edit rule reuse the core AccessControlRuleForm;
  the classic antd RuleForm is deleted.
- Add role uses a core Input and an Autocomplete for policies.
- Form.constants gains react-hook-form name/display-name rules.
- Policies and Roles specs drive the core controls.
…okForm

EmailConfigForm reuses the AI email-settings mappers; AddTeamForm, AuthMechanismForm
and CreateUser move to react-hook-form with core Input/Select/Autocomplete/Toggle.
getJWTTokenExpiryOptions now returns core select items. E2E helpers switch to role
locators, and the antd/positional suppression baselines shrink accordingly.
…ost dialog

AddEditPersonaForm moves to react-hook-form; its edit branch was unreachable (the
page only opens it empty) and is dropped. The classic search settings pages now
render the AI-mode FieldValueBoostDialog, so the antd FieldValueBoostModal is removed.
… with classic settings

The add-custom-property fields move out of the AI CustomPropertiesAddPage into a shared
CustomPropertyFormFields component plus payload utils, and the classic AddCustomProperty
drawer/page now renders them on react-hook-form. The classic permission debugger page
renders the AI AccessControlDebuggerPanel instead of its antd copy.
AppearanceConfigSettingsPage drives its fields through react-hook-form; a primary colour
change regenerates the hover and selected shades via a watch subscription. The profile
sample field loses its layout-only antd Form.Item.
…awer

The AI learning-resource field list moves into useLearningResourceFields; the classic
LearningResourceForm drawer now renders it on react-hook-form and reuses the AI
payload mappers.
…Form validation

HookForm now defaults to react-aria's aria validation, so native required attributes
no longer block submit before react-hook-form can report inline errors. AddQueryPage
and both KPI pages move to react-hook-form; the KPI pages share KPIFormFields. The
semantics tab validates its editor by explicit field paths.
…Form

TableConstraintsModal becomes react-hook-form with a foreign-key field array, core
Autocomplete key pickers and an async ComboBox for the related column; its
saved-constraints parsing moves to TableConstraintsModal.utils. Playwright helpers
switch to role locators.
Only the general fields are form-managed (the designer keeps the schema state),
so loading a template no longer risks spreading stale schema values. Builder,
transition and stage editors use core Input/Select/TextArea/Autocomplete.
The rule condition combobox now allows custom values, so react-aria no longer
resets the typed expression on blur and drops it from the saved rule. CreateUser's
bot fields take control as a prop: HookForm's provider belongs to the core package's
react-hook-form copy, so useFormContext() in the app returned null and crashed the
add-bot page. AddRoleAndAssignToUser picks policies by role.
Core Toggle caps its width at the container so a long hint wraps instead of
overflowing (the bot form's impersonation hint ran off the card). The table
constraint modal's add button keeps its content width and the related-column
combobox drops the shortcut hint. The task form grid span is escaped so LESS
stops computing 1 / -1 into -1, which put Description in a third column.
Percentage KPI targets are validated to 0-100 again, as the antd InputNumber max
did. Two decision records capture rules the PR description stated only in prose:
classic forms reuse the AI-mode core form when one exists, and inside HookForm
react-hook-form rules are the only validation that blocks submit.
@shah-harshit
shah-harshit marked this pull request as ready for review October 13, 2026 11:08
@github-actions

Copy link
Copy Markdown
Contributor

🔄 Playwright impact map auto-refreshed

This PR touched specs or UI source that changed the source→spec routing map. I regenerated .github/playwright/impact-map.generated.json and pushed the diff to this branch.

- source entries: 922 → 915
- 1 added, 8 removed, 6 changed spec-list

New source→spec entries:
  openmetadata-ui/src/main/resources/ui/src/components/discovery/personal-space/Profile/tabs/platform-settings/search/FieldValueBoostDialog.tsx

Removed source→spec entries:
  openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/LineageConfigModal.tsx
  openmetadata-ui/src/main/resources/ui/src/components/Modals/ModalWithFunctionEditor/ModalWithFunctionEditor.tsx
  openmetadata-ui/src/main/resources/ui/src/components/SearchSettings/FieldValueBoostModal/FieldValueBoostModal.tsx
  openmetadata-ui/src/main/resources/ui/src/pages/Configuration/EditUrlConfiguration/EditUrlConfigurationPage.tsx
  openmetadata-ui/src/main/resources/ui/src/pages/KPIPage/AddKPIPage.tsx
  openmetadata-ui/src/main/resources/ui/src/pages/KPIPage/EditKPIPage.tsx
  openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/RuleForm/RuleForm.tsx
  openmetadata-ui/src/main/resources/ui/src/pages/TeamsPage/AddTeamForm.tsx

Entries whose spec list changed:
  openmetadata-ui/src/main/resources/ui/src/components/Settings/Email/EmailConfigForm/EmailConfigForm.component.tsx
  openmetadata-ui/src/main/resources/ui/src/components/Settings/Users/CreateUser/CreateUser.component.tsx
  openmetadata-ui/src/main/resources/ui/src/pages/LearningResourcesPage/LearningResourceForm.component.tsx
  openmetadata-ui/src/main/resources/ui/src/pages/LineageConfigPage/LineageConfigPage.tsx
  openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/AddPolicyPage/AddPolicyPage.tsx
  openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/AddRolePage/AddRolePage.tsx

What is this file? It is the auto-generated half of Playwright's PR planner. It routes "if source X changes, run specs Y" by walking spec imports and cross-referencing getByTestId strings. Hand-authored routing in impact-map.json always wins on conflict.

What if I want to regenerate locally instead? Run this before pushing your next change to skip the bot commit:

python3 .github/scripts/generate_playwright_impact_map.py
git add .github/playwright/impact-map.generated.json
git commit --amend --no-edit  # or a separate commit

@gitar-bot

gitar-bot Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Code Review ✅ Approved 3 closed / 3 findings

🔴 High risk · Migrating role and policy forms changes authorization configuration, so value-mapping or validation differences could grant unintended access.

Migrates settings and admin forms from antd Form to core HookForm and reuses the AI core forms in classic pages. Resolved this session: the percentage KPI target cap at 100 is removed, and the classic-reuses-AI-core-form rule is now recorded in docs. No open issues found.

✅ 3 closed
✅ Quality: Classic-reuses-AI-core-form rule exists only in the PR description

📄 openmetadata-ui/src/main/resources/ui/src/components/Settings/Users/AdminPermissionDebugger/AdminPermissionDebugger.component.tsx:18-32 📄 openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/AddCustomProperty/AddCustomProperty.tsx:50-52 📄 openmetadata-ui/src/main/resources/ui/src/components/discovery/personal-space/Profile/panels/CustomPropertiesPanel/CustomPropertyFormFields.tsx:92 📄 openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/RuleForm/RuleForm.tsx 📄 openmetadata-ui/src/main/resources/ui/src/components/SearchSettings/FieldValueBoostModal/FieldValueBoostModal.tsx
The description sets a rule for the rest of the migration: "Where AI mode already had a core version of a form, the classic page now reuses it instead of keeping a second antd copy". This PR acts on it by deleting RuleForm, FieldValueBoostModal and the classic permission-debugger body, and by pulling CustomPropertyFormFields and useLearningResourceFields out of the AI pages. No record in docs/decisions/ says this. The two migration records (2026-10-11-antd-imports-are-frozen-by-eslint-suppressions, 2026-10-11-migrated-antd-components-adopt-core-styling) cover import freezing and styling, not which copy wins. The repo squash-merges, so the rule disappears once this merges, and PR 3 of 3 (KPI, query, lineage config, table constraints, task form) needs it.

The record would say: "When migrating a classic settings form off antd, if AI mode already has a @openmetadata/ui-core-components version of that form, the classic page renders the shared AI component (with its fields/payload utils extracted) and the antd copy is deleted; a second core copy is not written."

✅ Edge Case: Percentage KPI target no longer capped at 100

📄 openmetadata-ui/src/main/resources/ui/src/pages/KPIPage/KPIFormFields.tsx:130-144
The antd version paired the slider with InputNumber max={100} min={0}, which clamped the percentage target. In the new version the number Input has no min/max, and the only rule is value >= 0. Typing 150 in percentage mode now saves a 150% KPI target, and the slider gets a value above its maxValue. Add an upper bound to the validator when metricType === KpiTargetType.Percentage, or clamp the value in onChange.

✅ Quality: HookForm now makes isRequired visual-only, with no record of the rule

📄 openmetadata-ui-core-components/src/main/resources/ui/src/components/base/form/hook-form.tsx:121-122 📄 openmetadata-ui-core-components/src/main/resources/ui/src/components/base/form/hook-form.tsx:151
HookForm now sets validationBehavior="aria" by default for every consumer. As a result, isRequired on a core field no longer blocks submit: only react-hook-form rules do. Existing consumers that relied on native validation lose the check silently. For example, OntologyAxiomPanel renders propertyIri, targetIri and literalValue with isRequired and no RHF rule, so empty values now go straight to validate/createOntologyAxiom. The commit states the rule ("let RHF own HookForm validation"), and every HookForm consumer has to follow it, but no record in docs/decisions/ says so. Add RHF required rules to the affected fields. Then add a record stating: "Inside HookForm, react-hook-form rules are the only thing that blocks submit; isRequired on a core field is presentational, so every required field must carry an RHF required rule."

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

Auto-approval Not enabled

Set up

Options

Display: compact → Counting what did not apply, without listing it.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@github-actions

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 77%
77.44% (124243/160425) 63.71% (77572/121752) 65.11% (26011/39948)

@sonarqubecloud

Copy link
Copy Markdown

@shah-harshit

Copy link
Copy Markdown
Contributor Author

Combined into #35280 to go through the merge queue once. All commits, review fixes, screenshots and test results from this PR are carried over there.

This branch was successfully deployed

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

Labels

safe to test Add this label to run secure Github workflows on PRs skip-pr-checks Bypass PR metadata validation check UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants