Skip to content

Fixes #6884: migrate selection and numeric forms to core - #35262

Merged
shah-harshit merged 7 commits into
shah-harshit/7300-antd-display-componentsfrom
shah-harshit/7302-core-controls-and-forms
Oct 13, 2026
Merged

shah-harshit merged 7 commits into
shah-harshit/7300-antd-display-componentsfrom
shah-harshit/7302-core-controls-and-forms

Conversation

@shah-harshit

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

Copy link
Copy Markdown
Contributor

Describe your changes:

Fixes open-metadata/openmetadata-collate#7302
Fixes open-metadata/openmetadata-collate#7303
Part of open-metadata/openmetadata-collate#6884.
Depends on #35253; this draft is based on its branch to keep the overlapping form, profiler, and search changes in sequence.

Migrate the remaining selection, upload, numeric, autocomplete, and time controls to core components. Move the parent profiler, KPI, policy, lineage, login, user-creation, and SLA forms to React Hook Form where they contain these controls. Remove the unused legacy ParameterForm and its test.

Type of change:

  • Improvement

High-level design:

Use core components directly. Add canonical NumberInput behavior for nullable numeric values, decimal edits, relative arrow stepping, and bounds on commit. TreeSelect adds visible-chip limits, parent display condensation, loading, and focus callbacks. Tree utilities preserve schema values and expand synthetic All selections; SLA serialization retains wall-clock HH:mm and distinguishes zero from a cleared duration.

Nested forms receive their form instance explicitly. Profiler field arrays retain stable row IDs and unwrap partition rows to the API's string arrays. KPI date normalization clears an incompatible loaded end date instead of restoring it. The controls adopt core styling, including file drop zones and view-switch buttons.

All eight migrated components reach zero root, subpath, and type imports and are frozen in ESLint. The suppression count falls from 615 to 536. ADR:2026-10-12-numeric-fields-preserve-edits-and-api-values records the numeric and serialization contract.

The independent Modal/Button migrations remain separate. Provider/package removal in open-metadata/openmetadata-collate#7304 must wait for the remaining Modal, Button, Input, Select, and Form consumers.

Visual follow-ups also restore the name-column row header in learning resources, constrain KPI numeric fields so their sliders remain visible, and keep async permission-search options open while loading.

Tests:

Use cases covered

  • Decimal stepping, bounds, clearing, and duplicate numeric notifications.
  • Tree selection payloads, synthetic All, visible-chip limits, selectable team branches, and clearing.
  • SLA wall-clock round trips, zero durations, and omitted cleared/invalid values.
  • Profiler sampling and partitioning round trips, SQL-only edits, minimum values, and legacy stored zero samples.
  • KPI date normalization and invalid date-range clearing.
  • Existing user creation, policy, upload, lineage, search settings, and data-contract behavior.

Unit tests

  • Full UI Jest suite on the stacked branch (this PR + Fixes #6884: migrate selection and numeric forms to core #35262), rebased on main cc33564c31b: 1,924 suites / 22,212 tests; the one failing suite (EntityImport, upload input moved to core) is fixed and passes 45/45.
  • Changed-file ESLint/Prettier/import organization, Playwright lint, test:eslint-rules (pinned suppression counts), and the production vite build pass.
  • Core library build passes. Full-app tsc reports only pre-existing diagnostics (stale generated types and the existing onPress-on-ButtonUtility pattern), none new in the changed files' migration code.
  • Migrated-component tests rewritten to assert core behavior: ContractViewSwitchTab (ButtonGroup selection), CustomRangeWidget / ProfilerSettings (NumberInput suffix, native range min), EntityImport (core file input).
  • Core tree/numeric tests: 5 suites, 51 passed, 0 failed.
  • Scoped application coverage: 70.54% lines (2,053/2,910), 69.32% statements (tracked changed production modules, not whole-app).

Backend integration tests

Not applicable: no backend or API changes.

Ingestion integration tests

Not applicable: no ingestion changes.

Playwright (UI) tests

Not run for this revision: local Playwright was deferred at the author's request. The earlier partial chromium run (before the sidebar fix) had 100 passed / 16 failed; the sidebar, notification, and pipeline tree-view failures were fixed and their focused reruns pass. CI Playwright covers the remaining specs.

Manual testing performed

Compared main 0f7fdbf8704 against the complete stacked branch (display/navigation + controls/forms) on an isolated local backend. Attached 52 scene comparisons / 208 PNGs, covering main and branch in both light and dark. The archives include an HTML index for full-size browsing. Gallery navigation uses two repository images supplied as fixture metadata; isolated error, progress, and image-fallback screenshots mount the real migrated components with fixture values.

Visual verification found and fixed keyboard navigation/labels in TreeSelect, a missing learning-resource table row header, KPI slider width, and permission-search popup dismissal while async results arrive. Local browser reruns confirm those states.

Display/navigation comparisons — 30 scenes · Controls/forms comparisons — 22 scenes

Before/after, light/dark preview

Sidebar refresh (latest fix): collapsed group triggers and links now share core icon size and row height; hover menus stay interactive. Rows: expanded · collapsed · collapsed + hover menu; columns: main/branch × light/dark.

Sidebar before/after, light/dark

UI screen recording:

Not applicable: component/form refactor. Before/after screenshots are attached above; local Playwright validation follows.

Checklist:

  • PR title and description reference the linked issues.
  • Non-obvious numeric, tree, field-array, and serialization constraints are documented.
  • Tests cover the migrated behavior and API payloads.
  • Durable numeric/API decisions are recorded in the tree.
  • JSON Schema migrations are not applicable.
  • Feature recording is not applicable to this refactor.
  • Before/after light/dark screenshots and full-size archives attached for the migrated component groups.
  • Affected Playwright specs pass locally; results and tested commit recorded.

Comment on lines +27 to +33
options.map((option) => ({
id: String(option.value),
value: String(option.value),
// Core uses string values; preserve numeric enums for the schema payload.
data: option.value,
label: option.label ?? option.title ?? String(option.value),
parentId,

@gitar-bot gitar-bot Bot Oct 12, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Bug: getTreeSelectValues returns stringified ids, so numeric enum values become strings

toTreeSelectNodes stores the original option value in data and says this is to "preserve numeric enums for the schema payload". getTreeSelectValue reads data when it rebuilds the selection. But getTreeSelectValues, which builds the outgoing payload, returns node.value ?? node.id, and both were set with String(option.value). So when the user picks a numeric enum option in TreeSelectWidget, RJSF's onChange receives a string like '1' instead of the number 1. That value fails a numeric items.enum and differs from the stored value it was loaded from. The widget's non-expandAllValue path, which persists values as-is, is where this shows up. Return node.data when it is set, so the round-trip keeps the original type.

Prefer the preserved original value when serializing the selection.:

return nodes
  .filter((node) => !node.parentId || !selectedIds.has(node.parentId))
  .map(
    (node) =>
      (node.data as string | number | undefined) ?? node.value ?? node.id
  );

Was this helpful? React with 👍 / 👎

@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

❌ SSO Login Nightly — FAILED

View workflow run · Commit c53e8d3

Provider Status Pass Fail Flaky Skip Duration
basic ✅ pass 28 0 0 0 7m 27s
ldap ✅ pass 7 0 0 0 1m 06s
keycloak-oidc-confidential ✅ pass 12 0 0 0 1m 35s
keycloak-oidc-public ✅ pass 11 0 0 0 1m 40s
msal-mock ❌ FAIL 6 1 0 0 0m 51s
auth0-mock ✅ pass 6 0 0 0 0m 38s
okta ⏩ skipped - - - - -
keycloak-azure-saml ✅ pass 23 0 0 0 6m 20s
keycloak-azure-saml-crosssite ⏩ skipped - - - - -

Totals: 93 passed · 1 failed · 0 flaky · 0 skipped · duration 19m 42s

@shah-harshit
shah-harshit force-pushed the shah-harshit/7300-antd-display-components branch from 11dec04 to 7220b68 Compare October 12, 2026 13:14
@shah-harshit
shah-harshit force-pushed the shah-harshit/7302-core-controls-and-forms branch from 22aaf7c to 3eb490a Compare October 12, 2026 13:15
@shah-harshit
shah-harshit force-pushed the shah-harshit/7300-antd-display-components branch from 11cb9a1 to c302948 Compare October 12, 2026 15:57
@shah-harshit
shah-harshit force-pushed the shah-harshit/7302-core-controls-and-forms branch from c648675 to 2defee0 Compare October 12, 2026 15:57
@gitar-bot

gitar-bot Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Code Review 👍 Approved with suggestions 0 closed / 2 findings

🔴 High risk · Migrating policy-rule resource and operation selectors changes inputs used for access-control rules, potentially saving unintended permissions or denials.

Migrates selection, upload, numeric, autocomplete, and time controls to core components, and moves several forms to React Hook Form. Consider two follow-ups: getTreeSelectValues returns stringified ids so numeric enum values become strings (return node.data to keep the original type), and a pending tree focus can outlive empty results and steal focus while typing (clear it when loading finishes without a row or when the input value changes).

💡 Bug: getTreeSelectValues returns stringified ids, so numeric enum values become strings

📄 openmetadata-ui/src/main/resources/ui/src/utils/TreeSelect.utils.ts:27-33 📄 openmetadata-ui/src/main/resources/ui/src/utils/TreeSelect.utils.ts:66-75 📄 openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/TreeSelectWidget.tsx:65-76

toTreeSelectNodes stores the original option value in data and says this is to "preserve numeric enums for the schema payload". getTreeSelectValue reads data when it rebuilds the selection. But getTreeSelectValues, which builds the outgoing payload, returns node.value ?? node.id, and both were set with String(option.value). So when the user picks a numeric enum option in TreeSelectWidget, RJSF's onChange receives a string like '1' instead of the number 1. That value fails a numeric items.enum and differs from the stored value it was loaded from. The widget's non-expandAllValue path, which persists values as-is, is where this shows up. Return node.data when it is set, so the round-trip keeps the original type.

Prefer the preserved original value when serializing the selection.
return nodes
  .filter((node) => !node.parentId || !selectedIds.has(node.parentId))
  .map(
    (node) =>
      (node.data as string | number | undefined) ?? node.value ?? node.id
  );
💡 Bug: Pending tree focus outlives empty results and steals focus while typing

📄 openmetadata-ui-core-components/src/main/resources/ui/src/components/application/tree-select/tree-select.tsx:977-991 📄 openmetadata-ui-core-components/src/main/resources/ui/src/components/application/tree-select/tree-select.tsx:1035-1038 📄 openmetadata-ui-core-components/src/main/resources/ui/src/components/application/tree-select/tree-select.tsx:1281-1294

If ArrowDown is pressed while the dropdown shows the no-data branch (lines 1035-1038 render no role="row"), the effect finds no row, so pendingTreeFocus stays set. Because the effect depends on filteredTreeData, the user can then type a search term in the searchable input, and as soon as results appear row.focus() moves focus off the input in the middle of typing. The same thing happens when ArrowDown is pressed and the user keeps typing while a search fetch is loading. Fix: clear the pending focus once loading has finished without finding a row, and also clear it whenever the input value changes.

Drop the pending request when no row exists after loading, and on any search edit
  const row =
    pendingTreeFocus === 'first' ? rows?.[0] : rows?.[rows.length - 1];
  row?.focus();
  setPendingTreeFocus(null);
}, [isOpen, pendingTreeFocus, loading, filteredTreeData]);

// and in the input onChange:
// onChange={(event) => { setPendingTreeFocus(null); searchable && setInputValue(event.target.value); }}
🤖 Prompt for agents
Code Review: Migrates selection, upload, numeric, autocomplete, and time controls to core components, and moves several forms to React Hook Form. Consider two follow-ups: `getTreeSelectValues` returns stringified ids so numeric enum values become strings (return `node.data` to keep the original type), and a pending tree focus can outlive empty results and steal focus while typing (clear it when loading finishes without a row or when the input value changes).

1. 💡 Bug: getTreeSelectValues returns stringified ids, so numeric enum values become strings
   Files: openmetadata-ui/src/main/resources/ui/src/utils/TreeSelect.utils.ts:27-33, openmetadata-ui/src/main/resources/ui/src/utils/TreeSelect.utils.ts:66-75, openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/TreeSelectWidget.tsx:65-76

   `toTreeSelectNodes` stores the original option value in `data` and says this is to "preserve numeric enums for the schema payload". `getTreeSelectValue` reads `data` when it rebuilds the selection. But `getTreeSelectValues`, which builds the outgoing payload, returns `node.value ?? node.id`, and both were set with `String(option.value)`. So when the user picks a numeric enum option in `TreeSelectWidget`, RJSF's `onChange` receives a string like `'1'` instead of the number `1`. That value fails a numeric `items.enum` and differs from the stored value it was loaded from. The widget's non-`expandAllValue` path, which persists `values` as-is, is where this shows up. Return `node.data` when it is set, so the round-trip keeps the original type.

   Fix (Prefer the preserved original value when serializing the selection.):
   return nodes
     .filter((node) => !node.parentId || !selectedIds.has(node.parentId))
     .map(
       (node) =>
         (node.data as string | number | undefined) ?? node.value ?? node.id
     );

2. 💡 Bug: Pending tree focus outlives empty results and steals focus while typing
   Files: openmetadata-ui-core-components/src/main/resources/ui/src/components/application/tree-select/tree-select.tsx:977-991, openmetadata-ui-core-components/src/main/resources/ui/src/components/application/tree-select/tree-select.tsx:1035-1038, openmetadata-ui-core-components/src/main/resources/ui/src/components/application/tree-select/tree-select.tsx:1281-1294

   If ArrowDown is pressed while the dropdown shows the no-data branch (lines 1035-1038 render no `role="row"`), the effect finds no row, so `pendingTreeFocus` stays set. Because the effect depends on `filteredTreeData`, the user can then type a search term in the searchable input, and as soon as results appear `row.focus()` moves focus off the input in the middle of typing. The same thing happens when ArrowDown is pressed and the user keeps typing while a search fetch is loading. Fix: clear the pending focus once loading has finished without finding a row, and also clear it whenever the input value changes.

   Fix (Drop the pending request when no row exists after loading, and on any search edit):
     const row =
       pendingTreeFocus === 'first' ? rows?.[0] : rows?.[rows.length - 1];
     row?.focus();
     setPendingTreeFocus(null);
   }, [isOpen, pendingTreeFocus, loading, filteredTreeData]);
   
   // and in the input onChange:
   // onChange={(event) => { setPendingTreeFocus(null); searchable && setInputValue(event.target.value); }}

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

@shah-harshit

Copy link
Copy Markdown
Contributor Author

Folded into #35253, which now carries both the display/navigation and the selection/numeric form migrations (same commits plus a change leaving Affix, Empty, Image, Result and Spin to #35339). Closing in favour of #35253.

This branch had an error being deployed

1 failed 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.

1 participant