Skip to content

Fixes #35519: Stabilise flaky glossary Playwright specs - #35520

Merged
anuj-kumary merged 9 commits into
mainfrom
anuj-kumary/glossarytermrelatedterms-flaky-fix
Oct 16, 2026
Merged

anuj-kumary merged 9 commits into
mainfrom
anuj-kumary/glossarytermrelatedterms-flaky-fix

Conversation

@anuj-kumary

@anuj-kumary anuj-kumary commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Describe your changes:

Fixes #35519

I pulled the last 24h of merge-queue Playwright results (playwright_flaky_report.py, 2026-10-14 10:00 → 2026-10-15 09:00 UTC, 208 runs). For every glossary test that flaked or failed, I pulled the failed attempt's log, screenshot and aria snapshot from the shard blobs.

Already fixed by #35416 (merged 07:28 today, no change here). Every occurrence predates the merge and shows that race's signature: the button reads Collapse All, only first-level rows render, and the child row never appears.
GlossaryTermRelationsGraph:387 (26×), LargeGlossaryPerformance:176 (13×), GlossaryApprovalAfterMove:311/:199, GlossaryHierarchy:39/:108/:210/:341, Glossary.spec Expand All :1539 / row-action :2394, GlossaryWorkflow:560.

Fixed in this PR

Test Root cause Fix
GlossaryTermRelatedTerms :292, :220 The 3rd row's relation-type Select sits at the viewport's bottom edge (y≈702/720). Its popover is react-aria non-modal, so useCloseOnScroll closes it on any ancestor scroll. While it repositioned, the click went to a detached node (60s timeout), or a stale click picked the neighbour. The CI screenshot shows Antonym saved instead of Has Part / See Also. addRelatedTermsByRelationType (and the inline pick in the edit test) uses selectOptionWithRetry (centre trigger, settle, reopen) and re-picks until the trigger shows the requested label.
GlossaryAssets:46 (hard fail) addAssetToGlossaryTerm returned without waiting for PUT …/assets/add. Teardown hard-deleted the term while the add was in flight, leaving an orphan tag_usage row. TagLabelRowMapperWithTargetFqnHash then threw an NPE on GlossaryTerm.getName(), so the topic returned 500 on read and on delete. The helper waits for the add response and asserts 200 (all 5 callers are success paths). The mapper returns the label unenriched when the joined tag/term is gone, the same as applyTagCommonFieldsGracefully. New unit test TagLabelRowMapperWithTargetFqnHashTest.
Glossary.spec "Request tags for Glossary" When the new glossary sorts first, /glossary auto-selects it and redirects in an effect. selectActiveGlossary skipped its wait, and the redirect replaced the request-tags navigation. Open /glossary/<fqn> directly.
Glossary.spec "Create term with related terms, tags and owners" Clicking Tags scrolls the drawer, and the late scroll event closes the just-opened non-modal dropdown. selectTagInGlossaryForm calls scrollIntoViewAndSettle before opening.
GlossaryImportExport "Circular Reference" The success toast lives 3s. The 15s wait started only after the Update click (~10s) and the loader wait. Use the existing saveBulkImport (wait hoisted before the click), in all 3 spots in the file.
BulkEditEntity Glossary The cleanup DELETE went out on a keep-alive socket idle for ~66s, past the server's 60s timeout ("socket hang up"). DELETE is not retried. Clean up on a fresh API context.

Not changed:

No positional locators in the related-terms flow. The new helpers in utils/glossary.ts are getRelationRows, getOnlyRelationRow, addRelationRow and selectRelationType.

  • Finding a new row: rows carry Date.now() ids, so the row an add-row click creates is found by the id it introduces.
  • The relation-type trigger: found as button[aria-haspopup="listbox"].
  • The row to remove: found by its trigger's selected value, not by plain row text. Each row's hidden native <select> lists every type, so matching row text hits every row.
  • Suppressions: GlossaryTermRelatedTerms.spec.ts has no positional locators left, so its eslint-suppressions.json entry is pruned, and utils/glossary.ts drops from 11 to 8.
  • Re-run: GlossaryTermRelatedTerms.spec.ts (6 tests) and GlossaryTermDetails.spec.ts:205 each ran ×2 locally with --retries=0, and all 17 runs passed (including setup).

Type of change:

  • Bug fix

Tests:

Use cases covered

  • Adding Related To / Narrower / Has Part (and Synonym / See Also) relations on multiple rows persists the chosen relation types.
  • Adding an asset to a glossary term, then tearing it down, no longer leaves the asset unreadable or undeletable.

Unit tests

  • TagLabelRowMapperWithTargetFqnHashTest: an orphan glossary/classification tag_usage row maps to an unenriched label instead of throwing. I couldn't run it locally (the local openmetadata-spec jar is stale), so it relies on CI.

Playwright

Local runs against a running server, --retries=0, every test passed:

Spec Runs
GlossaryTermRelatedTerms.spec.ts (all 6 tests) ×2, plus ×2 on the earlier revision
GlossaryAssets:46 ×2
GlossaryImportExport Circular Reference ×2
Glossary.spec Request tags / Create term with related terms, tags and owners ×2 each

I didn't run the BulkEditEntity Glossary test locally (import-export lane). yarn lint:playwright: 0 errors. tsc: clean for the touched files.

Related terms, assets and import-export specs passing
Glossary.spec request-tags and create-term specs passing

🤖 Generated with Claude Code

- Related terms: pick the relation type through selectOptionWithRetry and
  re-pick until the trigger shows it; the bottom-row non-modal Select closed
  on scroll or took a stale click on the neighbouring option.
- addAssetToGlossaryTerm: wait for PUT /assets/add before returning so
  teardown cannot delete the term mid-add.
- Tag label row mapper: tolerate a tag_usage row whose tag/term is gone
  instead of throwing an NPE that made the asset unreadable and undeletable.
- Request tags: open the glossary by FQN so the /glossary auto-select
  redirect cannot replace the request-tags navigation.
- Glossary form tags: centre the trigger before opening so the drawer's
  late scroll does not close the dropdown.
- Glossary import: use saveBulkImport so the toast wait starts before Update.
- Bulk edit glossary: clean up on a fresh API context (idle keep-alive
  socket hang up).
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR checks passed

The linked issue has a description and all required Shipping project fields set. Thanks!

@github-actions github-actions Bot added the UI UI specific issues label Oct 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@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: 921 → 922
- 1 added, 0 removed, 13 changed spec-list

New source→spec entries:
  openmetadata-ui/src/main/resources/ui/src/components/observability/data-quality/ThresholdPreview/ThresholdPreview.tsx

Entries whose spec list changed:
  openmetadata-ui/src/main/resources/ui/playwright/constant/config.ts
  openmetadata-ui/src/main/resources/ui/playwright/e2e/Utils/appMode.ts
  openmetadata-ui/src/main/resources/ui/playwright/e2e/fixtures/pages.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/codeEditor.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/dataQuality.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/testCases.ts
  openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormBody.tsx
  openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/RunDetailsCard/RunDetailsCard.tsx
  … and 3 more

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

@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@anuj-kumary anuj-kumary self-assigned this Oct 15, 2026
@anuj-kumary anuj-kumary added the safe to test Add this label to run secure Github workflows on PRs label Oct 15, 2026
anuj-kumary and others added 3 commits October 15, 2026 15:13
Rows are found by the id the add-row click introduces (ids are Date.now()),
the relation-type trigger by button[aria-haspopup=listbox], and the row to
remove by its trigger's selected value. Prune the now-stale suppressions.
@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: 933 → 932
- 0 added, 1 removed, 0 changed spec-list

Removed source→spec entries:
  openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/TermsRowEditor.component.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

@anuj-kumary
anuj-kumary enabled auto-merge October 15, 2026 10:49
@anuj-kumary
anuj-kumary added this pull request to the merge queue Oct 15, 2026
Rohit0301 added a commit that referenced this pull request Oct 15, 2026
The `settings matches baseline` visual-regression test was failing on this
branch and only on this branch — PR #35520 carries main's sidebar rewrite and
passes, so the stale-baseline theory I had been working from was wrong.

Measuring the card top-borders in CI's own PNGs shows the cards drift: the
first is identical at y=157, then 218->219, 240->241, 276->278, 297->299,
growing down the page. That is this branch's SettingItemCard change.

antd's <Icon> wraps the svg in a `.anticon` span, and antd injects
`.anticon { display: inline-flex; line-height: 0; vertical-align: -0.125em }`
at runtime from @ant-design/icons/lib/utils.js. Replacing the wrapper with a
bare <svg> dropped that offset: a baseline-aligned inline svg reserves room
for the descender, making the icon box 2px taller.

Measured in Chrome against the real antd CSS: legacy 90x93, bare svg 90x95
(the 2px), and 90x93 again with `vertical-align: -0.125em` restored.
`display: block` overshoots at 90x90, which is why the earlier attempts at
this missed.

`tw:align-[-0.125em]` is already used in EntitySummaryDetails on this branch,
so the utility compiles.

Verified: eslint 0 errors; jest 4/4; the ui-checkstyle pipeline leaves the
file clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@anuj-kumary
anuj-kumary removed this pull request from the merge queue due to a manual request Oct 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🚦 Removed from the merge queue — manual (2026-10-15T19:34:34Z)

The entry left the queue before it was built, so no checks ran against it.

…ermrelatedterms-flaky-fix

# Conflicts:
#	openmetadata-ui/src/main/resources/ui/playwright/eslint-rules/tests/corpus.test.mjs
@anuj-kumary
anuj-kumary enabled auto-merge October 15, 2026 19:36
@anuj-kumary
anuj-kumary added this pull request to the merge queue Oct 15, 2026
@anuj-kumary
anuj-kumary removed this pull request from the merge queue due to a manual request Oct 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🚦 Removed from the merge queue — manual (2026-10-16T05:22:36Z)

The entry left the queue before it was built, so no checks ran against it.

# Conflicts:
#	openmetadata-ui/src/main/resources/ui/playwright/eslint-rules/tests/corpus.test.mjs
@anuj-kumary
anuj-kumary enabled auto-merge October 16, 2026 05:24
@gitar-bot

gitar-bot Bot commented Oct 16, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

🟡 Medium risk · Null-safe tag-label mapping prevents orphan-row read crashes but could expose incomplete labels when joined glossary terms or tags are missing.

Stabilises flaky glossary Playwright specs by switching related-terms relation-type selection to a retrying helper, waiting for asset-add responses before teardown, and making the tag-label mapper tolerate orphaned tag_usage rows. No issues found.

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

Auto-approval Not enabled

Set up

For more verification details, comment gitar display:verbose.

Options

Display: compact → Showing compact verification results.

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

@anuj-kumary
anuj-kumary added this pull request to the merge queue Oct 16, 2026
timothybrush pushed a commit to timothybrush/OpenMetadata that referenced this pull request Oct 16, 2026
…er (open-metadata#35205)

* feat(ui): Implemented New Persona setting ui

* fixedall the UI issues and added unit and playwright test

* feat(ui): finish persona settings migration and drop antd from home customize

- Render persona fullscreen customize views in normal flow (no z-index overrides)
- Guard persona customize exits with the unsaved-changes prompt
- Wait for the customize store page type before mounting entity customize pages
- Migrate Curated Assets modal, header theme, search bar and carousel off antd
- Save persona home header colour; restore Members/Bots profile nav ids
- Move CustomizeStore and page-type utils out of pages/
- Add unit tests for persona settings and Playwright save coverage

* lint fix

* chore(playwright): auto-refresh impact-map.generated.json

* removed antd refs

* fix(playwright): replace antd modal waits with role-based dialog waits in CustomizeDetailPage spec

The Add Tab modal was migrated from antd Modal to SimpleModal, so
.ant-modal never renders. Drop the dead .ant-modal.waitFor and
waitForAntdModalToSettle calls; the existing getByRole('dialog') check
already gates the dialog, leaving only expect(addButton).toBeEnabled()
before the click.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* lint fix

* Persona settings review fixes: filled eye and save icons, header action alignment, tailwind tokens for home widgets, revert import-only churn, stabilize curated assets e2e

* Remove accidentally committed local playwright report

* Update NavigationBlocker and CustomizeLandingPage specs for the core UnsavedChangesModal

* Sort imports

* Restore Members nav item and ProfileNavId re-export lost in merge

* Fix Sonar warnings in persona settings: void floating promises, Select value/onChange, useState naming

* lint fix

* Address review: persist home reset, sync doc-store cache, guard dirty in-modal editors, scroll-only dialog dividers, move usePersonaUsersField to hooks

* Keep the unsaved-changes prompt open when save-before-exit fails

* Label test-only buttons for jsx-a11y

* lint fix

* Fix hook dependency and async lint warnings without disabling rules

* Fix remaining lint warnings: hook deps in AddWidgetModal/CustomiseSearchBar, isolated admin fixture in persona specs

* chore(playwright): auto-refresh impact-map.generated.json

* Fix hook deps in CustomiseHomeModal/EmptyWidgetPlaceholder and move CustomizeLandingPage to the isolated admin fixture

* chore(playwright): auto-refresh impact-map.generated.json

* feat(ui): migrate antd icons to core UI icon library

Add 11 new icons to @openmetadata/ui-core-components and replace every
named @ant-design/icons import across OMD with a core-UI equivalent.

Icons added (SVG + generated TSX):
CheckCircleFilled, CloseCircleFilled, ExclamationCircleFilled,
EyeInvisibleFilled, Drag, Mail, Paperclip, Send, SortAscending,
SortDescending. EyeFilled already existed.

Call-site migration: all 29 distinct named antd icons are gone
(CloseOutlined->XClose, PlusOutlined->Plus, CheckOutlined->Check,
InfoCircleOutlined->InfoCircle, and 25 more). The package itself stays
for now: 104 files still use the default <Icon component={Svg} />, which
core-UI's Icon (iconValue: string) cannot accept.

Naming: drop the "Outlined" suffix (MailOutlined->Mail,
SaveOutlined->Save); "Filled" is kept as a real variant marker.

Deduplicate X/XClose: same glyph, differing only in inset. XClose has 68
imports on main vs 11 for X, so X is removed and its users converted.

LoadingOutlined (an animated antd Spin indicator) becomes the in-house
spinner idiom used by tree/tree-select/button: RefreshCw01 +
tw:animate-spin. CheckCircleTwoTone keeps its green via color=.

Fix generate-icons.mjs: SVGO's convertColors rewrites `white` to `#fff`
before replaceHardcodedColors runs, so the guard matching only the
literal "white" let the knockout on filled icons be themed into
currentColor - making the tick/cross/bang invisible against the disc.
Match the hex forms too. This also corrects SecurityService's clipPath.

Tests: 5 suites asserted antd implementation details (incomplete icon
mocks, getByRole('img'), and an accessible name of "left
label.browse-app-plural" from LeftOutlined's aria-label). Core-UI icons
are aria-hidden, so assertions move to data-testid.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(ui): render SVG assets directly instead of antd's Icon wrapper

Replace every `<Icon component={Svg} />` with the SVG component itself, and
drop the direct @ant-design/icons dependency. No named antd icon imports and
no uses of its default Icon component remain; antd keeps the package as its
own transitive dependency, so the yarn.lock entry stays until antd goes.

antd's Icon rendered `<span role="img" class="anticon">` around an `<svg>` it
sized at `1em`. Rendering the asset directly removes that wrapper, which four
kinds of styling quietly depended on:

- `.anticon { font-size }` sized the icon. Each call site now carries the size
  the CSS asked for (the left sidebar was the worst case at 28px, and had been
  rendering at 14px), and the dead font-size declarations are removed.
- `.anticon svg { fill }` and `.class svg { width/height }` targeted a
  descendant. The icon *is* the svg now, so those rules match the element
  itself; the sidebar's `fill: transparent` and the onboarding icon's 24px
  were both being dropped.
- `.ant-btn > .anticon + span { margin-left: 8px }` gave antd buttons their
  icon/label gap. Icons inside an antd `<Button icon=…>` with a label get
  `tw:mr-2`.
- `.anticon` was `inline-flex` and would not shrink. A bare `<svg>` in a flex
  row can be squashed by a long sibling, so converted icons get `tw:shrink-0`.

Sizes are preserved rather than standardised: `1em` resolved against antd's
`body { font-size: 14px }`, so sites with no explicit size become `tw:size-3.5`
and sites with `style={{ fontSize }}` keep their own value.

`alt` is carried to `aria-label` + `role="img"`. antd put it on the wrapper
span, where it meant nothing.

`getColumnDataTypeIcon` returned null for an unknown dataType, which antd's
`component={null}` tolerated; it now guards instead of crashing the lineage
node.

Tests that asserted antd internals (`.anticon`, `role="img"`, an accessible
name of "left …" from LeftOutlined's aria-label, and svg mocks that dropped
className) assert the rendered element instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ui): size the settings card icon at 90px, not the inherited 14px

`.setting-card-item .setting-card-icon svg { font-size: var(--om-font-size-90) }`
sized this icon while it was wrapped in antd's `.anticon`. font-size cannot size
an `<svg>`, so once the asset rendered directly the rule stopped applying and
the icon fell back to the 14px default every other converted icon takes —
a 6x shrink on every card in Settings.

The size moves onto the icon's class and the dead declaration is removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* minor fix

* fix(ui): size the converted icons and restore the antd button icon gap

Two things the icon conversion left behind, both found in review.

Icons from `@openmetadata/ui-core-components/icons` default to `size = 24` and
write that to the svg's width/height, where the antd icons they replaced were
`1em` — about 14px against antd's `body { font-size: 14px }`. 67 call sites
rendered the icon bare, so they all grew to 24px: overflowing 18px and 24px
icon-only buttons, and dwarfing the `text-xs` labels they sit beside. Each one
now carries the size it used to render at, keeping any explicit `fontSize` the
old call site set (so KPILatestResultsV1 stays 20px and 14px).

`.ant-btn > .anticon + span` and `span + .anticon` gave antd buttons their 8px
icon/label gap, and neither matches a bare svg. 11 buttons with the icon before
the label get `tw:mr-2`; 2 with it after get `tw:ml-2`.

Also: WhatsNewAlert's close icon was invisible. `padding: 16px` used to apply to
antd's wrapper span; on the svg itself, under the global border-box reset, 32px
of padding against a 14px box leaves a 0x0 content area — measured in Chrome as
`box 32x32, content 0x0`. It is the only element carrying the dismiss handler.
`box-sizing: content-box` restores it to `box 46x46, content 14x14`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ui): size the icons written as bare tags, and drop the dead fontSize

The previous pass keyed off an existing `className` on the icon, so the call
sites written as a bare `<XClose />` or `<Check />` were skipped and kept
rendering at the core-ui default of 24px. 13 of them, in buttons sized for a
14px glyph: BotDetails and RolesCard's `size="small"` save/cancel pair,
ObservabilityFormFiltersItem and TriggerItem, Execution, TermBoost's Select
suffix, CustomizeAppModeSidebarPage and ProfilerConfigurationPage.

Two take their size from the button's own CSS rather than the 14px default:
`.include-columns-add-button` is 18x18px with an 8px glyph, so both of
ProfilerSettingsModal's Plus icons are `tw:size-2`.

Also removed 7 `style={{ fontSize }}` declarations left on converted icons.
font-size cannot size an `<svg>`, and each site's size class already matched
the value it was asking for.

IncidentList's ShieldTick is left unsized: it belongs to main, not this branch,
and is deliberately large in an empty-state placeholder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ui): merge the duplicate className on SearchInputIcon

The icon-sizing pass appended a second `className` instead of merging into the
one already there. JSX keeps the last, so the `className` the caller passes in
was silently dropped and only the size class survived. Merge both through
`classNames`, which the file already imports.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ui): restore the button icon gap on 11 more sites, prune stale suppressions

The gap sweep matched `icon={<X ... />}` literally, so it missed icons wrapped
in an expression — EmailConfigSettingsPage renders its Edit icon behind an
`!isUndefined(...) && ...` guard, and its label sat flush against the icon.
Widening the scan to the whole `icon=` prop value found 11 labelled antd
buttons still missing the 8px gap `.ant-btn > .anticon + span` used to supply.

`yarn lint:src:suppressions` drops 36 `no-antd-import` suppressions for files
this branch moved off antd. Verified none of the 36 still imports antd, so no
real violation is being hidden. Without this, `make ui-checkstyle-changed`
fails with "There are suppressions left that do not occur anymore".

The five `LeftOutlined` prev-buttons in the DataContract tabs keep their current
spacing: they are pre-existing SVGR assets with explicit width/height that this
branch never converted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test(ui): lower the antd suppression ratchet to 559

`openmetadata-antd.test.mjs` pins the exact number of suppressed antd symbols
so re-running `--suppress-rule` cannot quietly absorb new usage, and its comment
says to lower the figure when a PR removes antd imports and prunes the baseline.
This branch removes 164, so the count is 723 -> 559.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(ui): size core-ui icons with the size prop instead of a class

The icons from `@openmetadata/ui-core-components/icons` take a `size` prop, so
73 call sites pass it directly rather than carrying a `tw:size-*` class. Each
keeps the size it already rendered at, including the odd ones: `size={8}` for
the 18px include-columns button, `size={18}`, `size={20}`.

The 61 SVGR assets imported from `assets/` keep their class. Those components
spread props straight onto the `<svg>` and have no `size` handling, so `size`
would land as a meaningless `size="14"` attribute and leave the icon unsized.

One consequence worth knowing: `size` writes width/height attributes, which CSS
outranks, where a class does not. Checked every class still attached to a
converted icon for a CSS width/height rule — the only matches are generic
utilities (`d-flex`, `cursor-pointer`) and a `drawer-close` prefix match on
`drawer-close-icon`, which belongs to a ButtonUtility this branch never touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(ui): move the last 12 core-ui icons onto the size prop

A re-audit of all 160 changed .tsx files, with a brace- and quote-aware scanner
that also reads multi-line tags and dynamic classNames, found 12 core-ui icons
still sized by a `tw:size-*` class. They were added after the earlier
conversion, in the pass that gave bare tags an explicit size.

The 76 SVGR assets keep their class: they have no `size` prop, so `size` would
land as a meaningless `size="14"` attribute. Six were traced back to their
`assets/*.svg` import to confirm the classification, including `Calendar`,
whose name also exists in the core-ui icon set.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(ui): drop the redundant white style and the vestigial anticon class

The seven `style={{ color: 'white' }}` on Plus icons sit inside antd
`<Button type="primary">`, which already sets `color: #fff`, so the icon
inherits white through `currentColor` without them.

The four `anticon` classNames were antd's wrapper hook. Nothing styles them now
that the icon is the svg itself — the one rule that could have matched,
`.alert-icon .anticon`, is scoped to a container none of these sit in.

The other 17 style props stay. They are not redundant: core-ui icons hardcode
`stroke="currentColor"` on the path, which overrides the root's `stroke={color}`,
so the glyph follows the inherited CSS color. Verified in Chrome — dropping
`style={{ color }}` renders the path black rather than the intended colour.
Those set greys (GRAYED_OUT_COLOR, #37352F4D) or spacing and all came across
from the antd icons they replaced.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(ui): move icon classes onto Tailwind tokens, dark-mode aware

63 icon sites carried legacy utility classes. Each is mapped to its Tailwind
equivalent after resolving the actual CSS value, so nothing shifts: m-r-xss is
4px so `tw:mr-1`, m-l-xs is 8px so `tw:ml-2`, m-l-sm is 12px so `tw:ml-3`.

The colours matter most. `text-grey-muted` is a hardcoded #757575 with no dark
variant; `tw:text-quaternary` resolves through `--color-gray-500`, which
`.dark-mode` redefines. Measured in Chrome:

  legacy  light rgb(117,117,117)  dark rgb(117,117,117)   unchanged
  token   light rgb(117,117,117)  dark rgb(123,127,134)   adapts

Also `text-grey-600` -> `tw:text-tertiary`, `text-base-color` ->
`tw:text-secondary`, `text-primary` -> `tw:text-brand-secondary` (what
DataAssetsHeader.utils and ServiceFlowStepper already use for brand text).

26 classes stay: `sla-icon`, `status-icon`, `inherit-icon`, `configure-arrow`,
`primary-key-section-line` and the like are component classes carrying their own
colours and transforms, not utilities — replacing them means deleting their
stylesheets.

`TagsUtils` had `className="d-block'"`, a stray apostrophe that made the class
match nothing. Now `tw:block`.

NavbarUtils asserts the literal className, so its expectation moves with the
component. No other test asserts on a converted class.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(ui): drop the sizing classes that do nothing on an icon

An `<svg>` with an explicit size ignores font-size, so `tw:text-md`,
`tw:text-xs` and `tw:font-medium` on 15 already-sized icons were dead. Measured
in Chrome: the same icon renders 14x14 with and without them. Only sites that
already carry a size prop, a size class or width/height attributes are touched —
an unsized icon still inherits font-size through `1em`, and none of those exist
here.

ArrayFieldTemplate's delete icon carried `tw:w-7 tw:h-0` alongside
`tw:size-3.5`, which is a real conflict: whichever Tailwind emits last wins and
`h-0` would collapse it. The original had `w-7 h-0` on antd's wrapper span,
where it was harmless and, as it turns out, already dead — neither class is
defined in the LESS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(ui): drop the icon styles and classes that carry nothing

Style props on icons go from 17 to 5. The six `marginRight` become `tw:mr-2` /
`tw:mr-2.5`, the six `color: GRAYED_OUT_COLOR` become `tw:text-quaternary`
(#959595 hardcoded, so it never adapted to dark mode), and one
`transform: rotate(90deg)` becomes `tw:rotate-90`. Four GRAYED_OUT_COLOR
imports are no longer used.

ProfileCard carried `className="tw:mr-1"` and `style={{ marginRight: '10px' }}`
on the same icon. The inline style won, so the class was dead and the markup
said 4px while rendering 10px. One `tw:mr-2.5` now.

`close-btn` and `ant-icon` are defined in no stylesheet, antd's included. Both
came across from the antd icons they replaced, where they did nothing either.

Five style props stay because nothing else expresses them: a dynamic
`theme.primaryColor`, an intentional `currentColor`, and three specific values
(TEXT_BODY_COLOR, #546E7A, #37352F4D) with no exact token — #546E7A is close to
`tw:text-tertiary` (#535862) but not equal, and swapping it would change the
colour rather than tidy it.

The 23 remaining classes were each checked against the stylesheets and do carry
CSS, `ant-form-item-tooltip` included: antd defines it with a colour and
`cursor: help`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* style(ui): convert remaining legacy icon classes and fix dead sidebar CSS

Follow-up review pass on the antd icon migration.

- Convert the legacy utility classes still sitting on migrated icons to
  their Tailwind equivalents: m-r-xss/m-t-xss (4px) -> tw:mr-1/tw:mt-1,
  m-r-xs (8px) -> tw:mr-2, m-x-sm (12px) -> tw:mx-3, align-middle and
  self-center -> tw:align-middle / tw:self-center.
- GlobalSearchBar: text-primary (!important ant var) -> tw:text-brand-secondary,
  and drop 'text-color', which no stylesheet defines.
- Replace two hardcoded colours with dark-mode-aware tokens:
  LimitBanner's #546E7A -> tw:text-quaternary, and AddAttributeModal's
  #37352F4D -> tw:text-placeholder. The latter was a real dark-mode bug:
  at 30% alpha the glyph flattens to #c3c2c0 on light but #191c22 on dark,
  i.e. it got darker against a dark background.
- left-sidebar.less: antd's Menu clones the `icon` node and appends
  `ant-menu-item-icon` to its className -- it never wraps it in `.anticon`.
  Verified against the rendered DOM (0 `.anticon`, 3 `.ant-menu-item-icon`).
  Every `.anticon` rule in the file was therefore dead once the icons became
  bare <svg>, including the hover colour at `.ant-menu-item-active`. Point
  them at `.ant-menu-item-icon`, collapse the four-selector `fill: transparent`
  block into one rule, and drop the `font-size` rules (a sized <svg> ignores
  font-size) along with the now-unused @left-sidebar-icon-size, whose 28px
  the `tw:size-7` class already carries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* lint fix

* style(ui): drop the inline styles this branch introduced on icons

Addresses the review comments on open-metadata#35205.

- TaskFeedCardFromTask: the footer Box already supports `gap`, so use
  gap={2} (tw:gap-2, 8px — what tw:mr-2 was) instead of margins on the
  icon and the replies-count button.
- The seven `<Plus size={14} style={{ color: 'white', fontSize: '12px' }} />`
  icons inside `<Button color="primary">` become `iconLeading={Plus}`, the
  component form used at 235 other call sites. All three properties were
  dead: Button's primary root carries tw:text-white (inherited through
  currentColor) and sizes its icon with `tw:*:data-icon:size-*`, a class
  that outranks the size attribute; fontSize never sized a sized <svg>.
- TreeSelectWidget: TEXT_BODY_COLOR (#37352F, no dark variant) -> the
  tw:text-secondary token, and drop the now-unused import.
- executionUtils: color: 'transparent' -> tw:text-transparent, twice.
- AddAttributeModal: a second #37352F4D Search prefix the main merge brought
  in -> tw:text-placeholder, matching the one already converted.
- ExploreV1: drop `fontSize: '14px'` from the two sort icons — font-size
  cannot size an <svg>, and the enclosing core Button sizes them anyway.

Left inline deliberately: `theme.primaryColor` (EntityLineageSidebar) and
`currentColor` (AppearanceConfigSettingsPage) are user-configurable brand
colours from Appearance settings, so no static Tailwind class can track them.

Verified: tsc 573 vs origin/main's 626 baseline; jest 872/872 across the 64
affected suites; eslint 0 errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* style(ui): replace the last inline styles and hardcoded status colours

- EntityLineageSidebar: both inline styles go. `cursor: 'grab'` becomes a
  conditional `tw:cursor-grab`, and `theme.primaryColor` becomes
  `tw:text-fg-brand-primary`. I previously argued this one had to stay inline
  because the colour is user-configurable; that was wrong. The theme provider
  publishes primaryColor into --tw-color-fg-brand-primary at runtime
  (theme-provider.utils.ts), so the class tracks the configured colour and the
  component no longer needs useApplicationStore at all.
- SSOConfigurationForm: the status disc was a hardcoded @green-7/@Red-7 with
  no dark variant and a raw tw:text-white glyph. Both sides move to semantic
  tokens — tw:bg-success-solid / tw:bg-error-solid, which globals.css
  redefines under the dark block, with tw:text-fg-white on the glyph. The two
  now-unused LESS rules are deleted.
- app.less: drop `.pagination-prev-icon`, whose `margin-top: calc(-1 *
  var(--om-space-3))` and `transform: rotate(180deg)` become
  `tw:-mt-[3px] tw:rotate-180` at the call site (--om-space-3 is 3px, off
  Tailwind's 4px scale, hence the arbitrary value). `.pagination-next-icon`
  was referenced but never defined, so that class is dropped too.

Its three tests asserted the old implementation — inline `cursor: grab` and
`useApplicationStore` being called — so they now assert the class and the
rendered handle. The global svg.mock drops props, so the brand tint is not
assertable in jsdom; the test says so rather than pretending to check it.

Verified: tsc 573 vs origin/main's 626 baseline; jest 450/450 across the 29
affected suites; eslint 0 errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* lint fix

* chore(playwright): auto-refresh impact-map.generated.json

* fix(ui): restore the icon sizes and line box the visual baselines expect

The visual-regression suite failed on applications, settings and glossary.
Both causes are this branch's, and both come from replacing an antd <Icon>
with a bare <svg>.

1. Badge icons were sized 14 when the antd icon they replaced rendered at
   1em of the badge's own font-size:
     ApplicationCard     .text-xss -> 10px (was 14)
     EntityHeaderTitle   .text-xs  -> 12px (was 14)
     TeamsHeadingLabel   .text-xs  -> 12px (was 14)
     AppDetails          .text-xs  -> 12px (was 14)
   On the marketplace the 4px of extra width pushed "Cache Warmup" onto a
   second line, which cascaded ~12px down the grid and reordered the cards.
   My earlier size audits only looked for icons with no size at all, so a
   size that was present but wrong went unnoticed.

2. setting-item-card: dropping `font-size: 90px` from the icon's <svg> also
   dropped the inline line box the `.anticon` wrapper used to contribute, and
   that box is part of the card's height. Measured in Chrome: the antd markup
   gives a 109px row, the bare svg 93px inline / 90px block, and putting the
   font-size back on `.setting-card-icon` returns it to 109px exactly. The
   svg keeps its tw:size-[90px]; the font-size is only there for the line box.

Verified: tsc 573 vs origin/main's 626 baseline; jest 63/63 across the 10
affected suites. The screenshots themselves can only be re-checked by CI,
which runs them in the pinned Playwright container.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(ui): express the settings-card line box as a Tailwind class

Replaces the `font-size: var(--om-font-size-90)` I had put back into
setting-item-card.style.less with `tw:text-[90px]` on the container, so the
LESS carries no om token and its only change remains the removal of the old
rule.

Measured in Chrome, both give the card the same 109px row the antd `.anticon`
wrapper produced; the bare svg alone gives 93px inline / 90px block.

Verified: tsc 574 vs origin/main's 626 baseline; jest 4/4; eslint 0 errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ui): size the settings-card icon the way the legacy markup did

My previous two attempts here were based on a wrong reconstruction of the old
DOM. The real markup is `<svg width="1em" height="1em">` inside a plain
`.anticon` span that carries no font-size of its own — the 90px came from the
LESS rule `.setting-card-icon svg { font-size: 90px }` applying to the svg,
where font-size resolves `1em`. So it sized the glyph; it was never a line box.

Measured against that real markup in Chrome: legacy gives a 90x93 icon box,
a 93px row and the beta badge at (9, 82) from the box corner. A bare inline
<svg> sized by `tw:size-[90px]`, with no height pin and no font-size, gives
exactly the same three numbers. The `tw:h-[109px]` I added before was 16px too
tall and made every settings card taller than the baseline instead of matching.

The diff is now just: drop the antd wrapper, move the 90px from the LESS onto
the icon as a Tailwind class.

Verified: jest 4/4; eslint 0 errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* lint fix

* fix(core-ui): restore the 16 icons the last merge regenerated

Valid review comment on Edit01. Resolving the icon conflicts in the previous
merge, I re-ran `yarn icons:generate` to rebuild Paperclip/X — but that
rewrites every icon, and main's checked-in output does not match what main's
own generator produces. 450 of main's icons carry `stroke={color}` on the
path, while the generator emits `stroke="currentColor"`, which overrides the
root's `stroke={color}` and makes the `color` prop inert.

Edit01 was the clearest casualty: main keeps a hand-maintained three-path
pencil-with-baseline whose edit01.svg source is the stock single-path
UntitledUI pencil, so regenerating collapsed 3 paths into 1 and changed the
glyph at every call site (IntakeFormsTable, RelationshipTypeTable,
DomainDetails, BotDetailPanel, the access-control pages, EntityBulkEditUtils).

Restore all 16 pre-existing icons to main's version. The 11 this PR genuinely
adds (CheckCircleFilled, CloseCircleFilled, Drag, ExclamationCircleFilled,
EyeInvisibleFilled, Mail, Paperclip, Save, Send, SortAscending,
SortDescending) are untouched, and index.ts still exports them plus main's X.

Not fixing the source/output drift here: it predates this branch, spans ~450
icons, and belongs in its own PR rather than one whose goal is no visual
change.

Verified: Edit01.tsx byte-identical to main (3 paths, stroke={color});
tsc 564 vs origin/main's 568 baseline; core-components rebuilds clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* style(ui): organise the icon imports the merge left out of order

ui-checkstyle failed its "ESLint + Prettier + Organise Imports (src)" gate on
seven files. That step runs organize-imports-cli, eslint --fix and prettier
over the changed files and fails if any of them rewrites something.

All seven are files where the last merge needed a core-icon import added back
(main's side of the conflict had dropped it). I appended each import at the
end of the block instead of in sorted position, so organize-imports-cli moved
it and the gate tripped.

No code change — only the import lines move.

Verified by running the three CI commands in order over the same file list:
the tree is clean afterwards, so the gate's `git status --porcelain` check
now passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* lint fix

* chore(playwright): auto-refresh impact-map.generated.json

* lint fix

* fix(ui): keep the settings card icon's baseline offset

The `settings matches baseline` visual-regression test was failing on this
branch and only on this branch — PR open-metadata#35520 carries main's sidebar rewrite and
passes, so the stale-baseline theory I had been working from was wrong.

Measuring the card top-borders in CI's own PNGs shows the cards drift: the
first is identical at y=157, then 218->219, 240->241, 276->278, 297->299,
growing down the page. That is this branch's SettingItemCard change.

antd's <Icon> wraps the svg in a `.anticon` span, and antd injects
`.anticon { display: inline-flex; line-height: 0; vertical-align: -0.125em }`
at runtime from @ant-design/icons/lib/utils.js. Replacing the wrapper with a
bare <svg> dropped that offset: a baseline-aligned inline svg reserves room
for the descender, making the icon box 2px taller.

Measured in Chrome against the real antd CSS: legacy 90x93, bare svg 90x95
(the 2px), and 90x93 again with `vertical-align: -0.125em` restored.
`display: block` overshoots at 90x90, which is why the earlier attempts at
this missed.

`tw:align-[-0.125em]` is already used in EntitySummaryDetails on this branch,
so the utility compiles.

Verified: eslint 0 errors; jest 4/4; the ui-checkstyle pipeline leaves the
file clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test(playwright): locate the selected-policy check by testid, not .anticon-check

Roles.spec asserted the selected policy with `.locator('.anticon-check')`.
That class came from antd's <CheckOutlined>; this branch renders core <Check>,
which emits no `.anticon-*` class, so the locator could never match.

Give the icon `data-testid="selected-check-icon"` and locate by that, which is
how the rest of that assertion already finds its elements and does not depend
on a library's internal class names. Verified in jsdom that the core icon
spreads `data-testid` through to the <svg>.

Also swaps the icon's legacy `text-primary` (an antd-variable class with
`!important` and no dark-mode variant) for `tw:text-brand-secondary`, matching
the conversion done elsewhere on this branch.

Left alone: the two `.anticon` locators in playwright/utils/user.ts. They sit
in `editTeams` and `editDescription`, which have no callers, and the first is
already stale on main — TeamsSelectableNew renders core TreeSelect, not antd
Select. Neither is a regression from this PR.

Verified: eslint 0 errors; jest 16/16 across src/pages/RolesPage; the
ui-checkstyle pipeline leaves both files clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* lint fix

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Merged via the queue into main with commit b17ce82 Oct 16, 2026
77 of 78 checks passed
@anuj-kumary
anuj-kumary deleted the anuj-kumary/glossarytermrelatedterms-flaky-fix branch October 16, 2026 09:39
shah-harshit pushed a commit that referenced this pull request Oct 16, 2026
* Fixes #35519: Stabilise flaky glossary Playwright specs

- Related terms: pick the relation type through selectOptionWithRetry and
  re-pick until the trigger shows it; the bottom-row non-modal Select closed
  on scroll or took a stale click on the neighbouring option.
- addAssetToGlossaryTerm: wait for PUT /assets/add before returning so
  teardown cannot delete the term mid-add.
- Tag label row mapper: tolerate a tag_usage row whose tag/term is gone
  instead of throwing an NPE that made the asset unreadable and undeletable.
- Request tags: open the glossary by FQN so the /glossary auto-select
  redirect cannot replace the request-tags navigation.
- Glossary form tags: centre the trigger before opening so the drawer's
  late scroll does not close the dropdown.
- Glossary import: use saveBulkImport so the toast wait starts before Update.
- Bulk edit glossary: clean up on a fresh API context (idle keep-alive
  socket hang up).

* chore(playwright): auto-refresh impact-map.generated.json

* Drop positional locators from glossary related-terms helpers and spec

Rows are found by the id the add-row click introduces (ids are Date.now()),
the relation-type trigger by button[aria-haspopup=listbox], and the row to
remove by its trigger's selected value. Prune the now-stale suppressions.

* chore(playwright): auto-refresh impact-map.generated.json

* Trim review comments to one-line summaries

* Lower recorded no-positional-locator suppression count to 552

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
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 UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Glossary Playwright specs flaky in merge queue (related terms, assets, import, request tags)

3 participants