Skip to content

fix(ui-core): stop Typography ellipsis tooltip from swallowing ancestor clicks - #30804

Merged
chirag-madlani merged 1 commit into
mainfrom
antd-migration/core-tooltip-click-fix
Aug 2, 2026
Merged

fix(ui-core): stop Typography ellipsis tooltip from swallowing ancestor clicks#30804
chirag-madlani merged 1 commit into
mainfrom
antd-migration/core-tooltip-click-fix

Conversation

@chirag-madlani

@chirag-madlani chirag-madlani commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

The bug

Typography with ellipsis={{ tooltip: … }} wraps its content in TooltipTrigger, which renders a react-aria Button. react-aria's usePress defaults shouldStopPropagation to true, so a click on truncated text never reaches an ancestor's onClick.

That wrapper exists purely to host a hover/focus tooltip over non-interactive text. Swallowing clicks is not intended behavior there.

Real breakage this caused

Surfaced during the antd → ui-core-components Typography migration (#30780):

  • Persona switcher — clicking a persona in the profile dropdown did nothing; the handler is on an ancestor row, so persona/navigation settings silently never applied.
  • Test-case selection cards (AddTestCaseList) — cards couldn't be selected at all; three Playwright specs hung waiting on API calls that never fired.

There are ~57 ellipsis={{ tooltip: true }} call sites in openmetadata-ui. Only a few sit on e2e-covered paths, so an unknown number of click targets are broken today with nothing watching — which is why this is fixed in core rather than at call sites.

The fix

Pass onPress={(e) => e.continuePropagation()} to the single TooltipTrigger inside Typography's ellipsis branch. This is react-aria's own documented escape hatch (@react-types/shared: "the default … is not to propagate. This can be overridden by calling continuePropagation()"), verified against the installed @react-aria/interactions source rather than docs alone.

Deliberately scoped to Typography, not the shared TooltipTrigger. Auditing consumers (form-item-label, input/label, input, table, avatar-add-button) found that table.tsx's sortable-column help icon relies on the swallowing default — clicking it must not also trigger column sort. A global default change would have regressed that.

No visual or className changes; truncation, line-clamp, width behavior and tooltip appearance are untouched.

Tests

4 new cases in typography.test.tsx: click propagates to an ancestor handler (the regression guard), tooltip still opens on hover, tooltip still opens on keyboard focus (a11y guard), and non-ellipsis Typography unaffected. Verified failing-before by stashing only the typography.tsx change. After: 11/11 in typography, 15/15 package-wide. Lint, prettier, tsc --noEmit clean.

For reviewers

Any call site that accidentally relied on the old swallow as a click guard will now see its ancestor onClick fire. I audited the core package; a sweep of openmetadata-ui call sites is worth a look during review.

Fixes #30803

🤖 Generated with Claude Code

Greptile Summary

The PR restores ancestor click propagation specifically for Typography’s ellipsis tooltip wrapper while preserving the shared TooltipTrigger’s default behavior.

  • Adds a scoped onPress handler that calls continuePropagation().
  • Adds regression coverage for ancestor clicks, hover and keyboard tooltip activation, and unaffected non-ellipsis Typography.

Confidence Score: 5/5

The PR appears safe to merge, with the propagation change narrowly scoped and covered by behavior-focused tests.

The implementation restores the intended ancestor click behavior only for ellipsis-wrapped Typography while leaving other TooltipTrigger consumers unchanged, and no concrete regression remains established.

Important Files Changed

Filename Overview
openmetadata-ui-core-components/src/main/resources/ui/src/components/foundations/typography.tsx Correctly scopes press propagation to the non-interactive ellipsis tooltip wrapper without changing shared TooltipTrigger behavior.
openmetadata-ui-core-components/src/main/resources/ui/src/components/foundations/typography.test.tsx Adds focused regression and accessibility coverage for click propagation and tooltip activation.

Reviews (1): Last reviewed commit: "fix(ui-core): stop Typography ellipsis t..." | Re-trigger Greptile

…or clicks

Typography's ellipsis-tooltip path wraps truncated content in
`TooltipTrigger`, which renders a react-aria `Button`. react-aria's
`usePress` hook stops a completed press from propagating to ancestor DOM
listeners by default - this is documented, intentional behavior
("the default for React Spectrum components is not to propagate. This can
be overridden by calling continuePropagation() on the event", see
@react-types/shared/src/events.d.ts) - not a bug in react-aria itself.
Verified at the source level in node_modules/@react-aria/interactions'
usePress: the returned onClick handler unconditionally calls
`e.stopPropagation()` unless `continuePropagation()` was called on the
PressEvent during onPress/onPressStart/onPressEnd.

For Typography's ellipsis tooltip specifically, that default is wrong: the
wrapper only exists to host a hover/focus tooltip over otherwise
non-interactive text, so any ancestor `onClick` (a selectable card, a
persona-switcher row, ...) should still receive the click. Real breakage
from this in the antd->core Typography migration (PR #30780): the persona
switcher's row onClick never fired, and AddTestCaseList's selectable cards
could not be selected at all, hanging three e2e specs. With ~57
`ellipsis={{ tooltip: ... }}` call sites in openmetadata-ui and only a
handful covered by e2e, an unknown number of click targets were silently
broken.

Fix: pass an `onPress` handler to the ellipsis path's `TooltipTrigger` that
calls `event.continuePropagation()`, restoring default click propagation
for all 57 call sites with no per-site changes needed.

Scoped to Typography's own usage rather than changing `TooltipTrigger`'s
default: grepping other `TooltipTrigger` consumers in this package
(form-item-label, input, table column header, avatar-add-button) found a
case - the sortable table column header's help-icon tooltip - that
actively relies on the click-swallowing default so the help icon doesn't
also trigger the header's sort-on-click. Changing the shared component
would have fixed Typography at the cost of regressing that case.

Added regression tests in typography.test.tsx: click propagates to an
ancestor onClick (fails without the fix), tooltip still shows on hover and
on keyboard focus, and non-ellipsis Typography is unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 1, 2026 15:14
@github-actions

github-actions Bot commented Aug 1, 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 safe to test Add this label to run secure Github workflows on PRs UI UI specific issues labels Aug 1, 2026
@gitar-bot

gitar-bot Bot commented Aug 1, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

Fixes antd Typography ellipsis tooltip interaction by configuring event propagation so clicks reach ancestor handlers. No issues found.

Options

Display: compact → Showing less information.

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

Compact
gitar display:verbose         

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

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

Fixes a UI-core interaction bug where Typography with ellipsis={{ tooltip: ... }} prevented ancestor onClick handlers from firing because the underlying react-aria press handling stops propagation by default.

Changes:

  • Allows ellipsis-tooltip presses to propagate by calling PressEvent.continuePropagation() on the TooltipTrigger used by Typography’s ellipsis-tooltip branch.
  • Adds regression tests to ensure ancestor clicks work while hover/focus tooltip behavior remains intact.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
openmetadata-ui-core-components/src/main/resources/ui/src/components/foundations/typography.tsx Opts Typography’s ellipsis-tooltip TooltipTrigger into press propagation to restore ancestor click handlers.
openmetadata-ui-core-components/src/main/resources/ui/src/components/foundations/typography.test.tsx Adds test coverage for click propagation and tooltip behavior on hover and keyboard focus.

Comment on lines +19 to +36
// `TooltipTrigger` renders a react-aria `Button`, whose `usePress` hook stops
// a completed press from propagating to ancestor DOM listeners by default
// (react-aria's documented behavior: "the default for React Spectrum
// components is not to propagate. This can be overridden by calling
// continuePropagation() on the event" - see
// node_modules/@react-types/shared/src/events.d.ts). For most `TooltipTrigger`
// call sites that is desirable (e.g. a help-icon tooltip nested inside a
// sortable table header should not also trigger the header's sort-on-click).
// But Typography's ellipsis tooltip wraps *arbitrary, non-interactive* text
// content: the wrapper is only there to host the hover/focus tooltip, so a
// click on the truncated text should reach whatever ancestor `onClick` the
// consumer attached (e.g. a selectable card, a persona-switcher row). Calling
// `continuePropagation()` here restores that click, scoped to this call site
// only - it does not change `TooltipTrigger`'s default for its other
// consumers (form-item-label, input, table column header, avatar add button).
const allowEllipsisTooltipPressToPropagate = (e: PressEvent) => {
e.continuePropagation();
};
@chirag-madlani
chirag-madlani added this pull request to the merge queue Aug 1, 2026
chirag-madlani added a commit that referenced this pull request Aug 1, 2026
Follow-ups from the code review on the antd Typography sweep:

- SignInPage: `md:tw:gap-10` is not a valid Tailwind v4 prefix-mode class and
  generated no CSS, so the breakpoint spacing the deleted `.less` provided was
  silently lost. The prefix must lead the variant chain (`tw:md:gap-10`).

- SignInPage: `.forgot-password-link`'s `flex: 1` only applies when the anchor is
  a direct flex child of antd's `inline-flex` label. Typography wraps `as="a"` in
  a block `div.prose`, breaking that. Own the row layout locally instead.

- PageHeader: `{...titleProps}` / `{...subHeaderProps}` were spread *before* a
  hardcoded `className`, so a caller-supplied className was silently dropped.
  Merge them instead.

- AddTestCaseList: drop the `onClick` handlers that were re-wired onto the
  Typography elements. They sit inside the react-aria TooltipTrigger button, so
  today they never fire; and once that button stops swallowing propagation
  (#30804) they would fire *in addition to* the row-level handler on the
  <Space>, and since `handleCardClick` toggles, the two would cancel out.
  Selection is left to the single row-level handler.

- DataAssetsHeaderExtraInfo / MarketPlaceAppDetails: both used a default
  (inline) Typography purely as a layout container around block content. Mark
  them `as="div"`, which is what they were structurally, and which clears the
  last remaining invalid nesting — a scan of all 591 Typography files now
  reports none.
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 2, 2026
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

🚦 Removed from the merge queue — failed_checks (2026-08-02T03:09:59Z)

These checks failed on merge-queue commit fcba408:

@chirag-madlani
chirag-madlani added this pull request to the merge queue Aug 2, 2026
Merged via the queue into main with commit 1b11da6 Aug 2, 2026
69 of 71 checks passed
@chirag-madlani
chirag-madlani deleted the antd-migration/core-tooltip-click-fix branch August 2, 2026 10:05
chirag-madlani added a commit that referenced this pull request Aug 2, 2026
#30780)

* fix(tooling): reuse existing core Typography import in partial conversions

The antd-typography-to-core codemod only checked for a plain, unaliased
`Typography` specifier when deciding whether core already imports
Typography. It missed an existing `Typography as CoreTypography` alias,
so on a fully-converted file it pushed a second `Typography` specifier
onto the same import (utils/IngestionUtils.tsx repro), instead of
reusing the alias already in scope. Fixed by resolving the existing
core-import local name (plain or aliased) once, up front, and reusing
it for both the fully-converted and partial/alias code paths; a new
specifier is only introduced when no core import exists at all.

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

* refactor(ui): migrate utils/* Typography to ui-core-components (sweep 1/13)

Chunk 1 of the antd Typography -> ui-core-components migration
(docs/antd-migration/typography.md): 32 leaf files under
openmetadata-ui/src/main/resources/ui/src/utils regenerated with the
fixed antd-typography-to-core codemod. All 32 convert cleanly with no
hand-finish skips.

Also includes two fixes surfaced by the verification gates rather than
the codemod itself:
- utils/NavbarUtils.test.tsx and utils/CSV/CSV.utils.test.tsx asserted
  against / mocked the old antd Typography shape; updated to the core
  component so their tests keep passing.
- Widened ui-core-components' TypographyProps (href/target/rel) so the
  as="a" shape produced by Typography.Link conversions type-checks;
  these props already reached the DOM at runtime via prop spreading,
  this only corrects the type surface to match, additive-only.

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

* test(ui): fix core-components mocks broken by typography sweep

ClassificationUtils.tsx and IngestionListTableUtils.tsx now render the core
Typography component instead of antd's Typography.Text. Two test suites
mock '@openmetadata/ui-core-components' wholesale but didn't stub
Typography, so it resolved to undefined at render time and broke
ClassificationDetails.test.tsx and TestSuitePipelineTab.test.tsx in CI's
full-suite jest run (our local `jest src/utils` run never touched these
files). Add the same Typography stub already used in CSV.utils.test.tsx.

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

* test(ui): drop non-deterministic incident-manager visual baseline

The incident-manager page renders seeded test-case rows whose names, table
names, and "Last Updated" timestamps differ per CI run; the table's
auto-width columns then shift the whole layout to fit that per-run content,
so no committed baseline can be stable. Same class of non-determinism as
'roles', already dropped.

Verified this is pre-existing and unrelated to this sweep: none of the 32
utils files touched by the sweep render anywhere in the IncidentManager
component tree (checked the full import graph from IncidentManagerPage
down), and the incident-manager baseline also fails intermittently on
unrelated, concurrent PRs that never touch those files (e.g. runs
91219477124 "fix async-polling bugs in EntityExportModalProvider" and
91193942898 "Fix the flakiness in SearchRBAC test").

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

* refactor(ui): migrate components/common (part A) Typography to ui-core-components (sweep 2/13)

Mechanical codemod conversion of antd Typography usages to
@openmetadata/ui-core-components in components/common (AsyncSelectList
through ManageButtonContentItem, 26 leaf dirs / 40 files).

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

* refactor(ui): migrate components/common (part B) Typography to ui-core-components (sweep 3/13)

Mechanical codemod conversion of antd Typography usages to
@openmetadata/ui-core-components in components/common (NoOwner through
UserTeamSelectableList, 21 leaf dirs / 22 files).

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

* refactor(ui): migrate small isolated components Typography to ui-core-components (sweep 4/13)

Mechanical codemod conversion of antd Typography usages to
@openmetadata/ui-core-components across a grab-bag of small, isolated
feature components: APIEndpoint, AuditLog, Certification,
Classifications, DataAssetRules, Domain, Learning, NavBar, PageHeader,
ProfileCard, SearchDropdown, Tag, Topic, UploadFile,
WorkflowDefinitions, AppBar, Container, DataAssets, DataProducts,
ExploreV1, NotificationBox, Pipeline, Suggestions, TestLibrary,
Visualisations, Announcement (26 dirs / 38 files).

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

* refactor(ui): migrate Dashboard/Metric/ServiceInsights/MlModel/KnowledgeCenter/SettingsSso/SearchSettings/DataInsight Typography to ui-core-components (sweep 5/13)

Mechanical codemod conversion of antd Typography usages to
@openmetadata/ui-core-components in components/{Dashboard,Metric,
ServiceInsights,MlModel,KnowledgeCenter,SettingsSso,SearchSettings,
DataInsight} (36 files).

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

* refactor(ui): migrate DriveService/Glossary/Alerts/DataQuality Typography to ui-core-components (sweep 6/13)

Mechanical codemod conversion of antd Typography usages to
@openmetadata/ui-core-components in components/{DriveService,Glossary,
Alerts,DataQuality} (31 files). DataQuality and Glossary are
entity-detail-page building blocks.

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

* refactor(ui): migrate Entity/Modals/Explore Typography to ui-core-components (sweep 7/13)

Mechanical codemod conversion of antd Typography usages to
@openmetadata/ui-core-components in components/{Entity,Modals,Explore}
(37 files). Entity is shared across many entity-detail pages.

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

* refactor(ui): migrate DataContract/Database Typography to ui-core-components (sweep 8/13)

Mechanical codemod conversion of antd Typography usages to
@openmetadata/ui-core-components in components/{DataContract,Database}
(28 files).

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

* refactor(ui): migrate components/ActivityFeed Typography to ui-core-components (sweep 9/13)

Mechanical codemod conversion of antd Typography usages to
@openmetadata/ui-core-components in components/ActivityFeed (18 files).

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

* refactor(ui): migrate components/MyData Typography to ui-core-components (sweep 10/13)

Mechanical codemod conversion of antd Typography usages to
@openmetadata/ui-core-components in components/MyData (30 files),
including Widgets/ and CustomizableComponents/ (landing-page surface).

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

* refactor(ui): migrate components/Settings Typography to ui-core-components (sweep 11/13)

Mechanical codemod conversion of antd Typography usages to
@openmetadata/ui-core-components in components/Settings (36 files).
Concentrates 4 of the hardest hand-finish sites (copyable/code props),
handled separately per the migration guide.

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

* refactor(ui): migrate pages (part A) Typography to ui-core-components (sweep 12/13)

Mechanical codemod conversion of antd Typography usages to
@openmetadata/ui-core-components across 33 single-file page dirs plus
Configuration, DataInsightPage, KPIPage, LoginPage, RolesPage (39
files). Page shells carry the highest visual-regression exposure.

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

* refactor(ui): migrate TaskFormSettingsPage/SignUp/TableDetailsPageV1/PoliciesPage/TasksPage Typography to ui-core-components (sweep 13/13)

Mechanical codemod conversion of antd Typography usages to
@openmetadata/ui-core-components in pages/{TaskFormSettingsPage,
SignUp,TableDetailsPageV1,PoliciesPage,TasksPage} (23 files). TasksPage
is the single largest page directory, kept whole.

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

* refactor(ui): hand-finish typography conversions

Resolve the sweep's remaining hand-finish worklist per
docs/antd-migration/typography.md:

- BlockEditor/* (BubbleMenu, AttachmentPlaceholder, HashList, MentionList,
  SlashCommandList): bare antd `<Typography>` wrappers with no automated
  coverage, converted directly to core Typography.
- CustomStatistic.tsx: `Text type={expr}` (unsupported-type skip) resolved
  to `color={expr}` since both ternary branches are allowed values.
- TestCaseIncidentManagerStatus.component.tsx, SuccessScreen.tsx,
  SummaryTagsDescription.component.tsx, DataInsightHeader.component.tsx:
  bare `<Typography>` usage mixed with converted sub-components; dropped
  the antd import and renamed the `CoreTypography` alias back to
  `Typography`.
- ProfilerObjectFieldTemplate.tsx, MetricExpression.tsx, TourEndModal.tsx,
  Sso{RolesSelectField,ConfigurationFormArrayFieldTemplate}.tsx,
  ErrorPlaceHolderIngestion.tsx, WorkflowArrayFieldTemplate.tsx: bare
  `<Typography>` (zero-conversion, not counted in the codemod's 410)
  converted by hand.
- UserProfileIcon.component.tsx, PersonaDetailsCard.tsx: core
  `ellipsis={{ tooltip: true }}` renders a real `<button>` trigger
  (TooltipTrigger), which is invalid nested inside another interactive
  element (a dropdown-trigger Button / whole-card onClick). Switched to
  plain `ellipsis` + a native `title` attribute at the two sites where
  this nesting broke click handling.
- TierCard.tsx: fixed a pre-existing `react/jsx-sort-props` prop-order
  violation surfaced by running eslint over the full changed-file set.

AlertDetails.component.tsx, ReindexFailures.component.tsx,
AdminPermissionDebugger.component.tsx, and UserPermissions.component.tsx
keep their partial `CoreTypography` alias — `code`/`copyable`/
`ellipsis.expandable` have no core equivalent yet.

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

* test(ui): fix core-components mocks for typography sweep

Fix the test-mock breakage class the Typography sweep repeatedly
triggers: a jest.mock('@openmetadata/ui-core-components', ...) factory
that omits Typography now returns undefined for a component that starts
importing it, crashing with "Element type is invalid" ("Check the
render method of ..."). Fixed in TableQueries.test.tsx,
ColumnDetailPanel.test.tsx, LineageTabContent.test.tsx,
ExploreSearchCard.test.tsx, TestSuitesTable.test.tsx, TestSuites.test.tsx
by adding the established Typography stub:
({ as: Component = 'span', children, ...props }) => <Component
{...props}>{children}</Component>.

Also fixes a second, narrower breakage class: stale antd
`jest.mock('antd', ...)` stubs for `Typography.Text`/`.Paragraph` that
inject a `data-testid` no longer used once the underlying component
switched to core Typography (DomainsSection, DataQualitySection,
DataProductsSection, OwnersSection, TagsSection, LineageTabContent).
Removed the dead mocks and updated assertions to query by the real
rendered class/text instead of the removed synthetic testid.

IncidentManagerPage.test.tsx and WidgetHeader.test.tsx had an existing
Typography stub that dropped all props except `children` (or dropped
`as`/`data-testid`); widened both to the same forwarding pattern so
`data-testid="heading"`/`"sub-heading"` and the `tw:truncate` ellipsis
class survive the mock.

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

* fix(ui): type PageHeader title/sub-header props against core Typography

PageHeader.interface.ts still typed titleProps/subHeaderProps as antd's
Typography/Paragraph props after PageHeader.component.tsx was converted
to core Typography (sweep 4/13). antd's ParagraphProps inherits a
generic HTML `color?: string` from HTMLAttributes, which isn't
assignable to core Typography's narrower `color?: TypographyColor`
union, so spreading `{...subHeaderProps}` onto the core component no
longer type-checked. Point both props at core's TypographyProps
instead — this was the one genuine new tsc error introduced by the
sweep (isolated by diffing against the pre-sweep baseline).

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

* refactor(ui): migrate final Typography holdouts off antd

Finishes the sweep's hand-finish worklist: AlertDetails, ReindexFailures,
AdminPermissionDebugger, and UserPermissions kept a partial CoreTypography
alias because their `code`/`copyable`/`ellipsis.expandable` usages had no
core equivalent. Resolves the gaps directly:

- `Typography.Text code` -> `CoreTypography as="code"` with the
  rounded/mono/bg-secondary className idiom already used by
  ContextRuleCard.component.tsx.
- `Typography.Text/Paragraph copyable` -> `CoreTypography` composed with
  the existing `CopyToClipboardButton` in a flex row; the `expandable`
  ellipsis option is dropped in favor of `rows: 2` since the cell already
  sits inside a Tooltip showing the full text.
- Drop the now-unused `Typography` antd import/destructure from all four
  files.

Also fixes a regression the hand-edit introduced in AlertDetails: core's
Typography always wraps its content in an outer `<div className="prose">`
regardless of `as`, so nesting the new `as="code"` Typography inside the
pre-existing `as='p'` Typography produced an invalid `<div>`-in-`<p>`
(validateDOMNesting warning, not caught by any existing assertion). Switched
the outer wrapper to `as="div"`.

openmetadata-ui's Typography ledger row now reads 0.

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

* fix(ui): keep ExploreTree title testid's parent stable for e2e selectors

Core Typography always wraps its children in an extra div.prose, so the
element carrying `data-testid="explore-tree-title-*"` is no longer a direct
child of the `d-flex justify-between` row — it now sits one level deeper,
inside that wrapper div. ExploreBrowse.spec.ts and ExploreTree.spec.ts both
walk `getByTestId(...).locator('..')` to reach the sibling
`.explore-node-count` badge, and were landing on the prose div instead of
the row, so the count badge was never found.

Move the testid off Typography onto a plain <span> wrapper at the row's
original DOM position, with Typography nested inside it purely for text
styling. Restores the DOM shape the e2e specs (and the CSS in
explore-tree.less targeting `[data-testid^='explore-tree-title-']`) expect,
without reverting the Typography migration.

See #30779 for the general div.prose blast-radius
writeup.

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

* fix(ui): use a native button for nested-column links to restore e2e/a11y

Core Typography always wraps its children in an extra div.prose. The
nested-column link (`.nested-column-name`) used to be an antd
Typography.Link, rendered directly as <a>, a direct child of the padded
<p style="padding-left: {depth*8}px"> row. After the sweep converted it to
`<Typography as="a">`, the div.prose wrapper interposed between the <a> and
that <p>, so `.nested-column-name.locator('..')` (used by
Entity.spec.ts's "Complex nested column structures" e2e test to read
`getComputedStyle(el).paddingLeft`) landed on the prose div instead, which
has no padding — the indentation assertion always saw `0px`.

Typography wasn't buying anything here beyond a plain className, so drop it
for a native element at the original DOM position. Used <button> rather
than plain <a> (which has no href) — a bare interactive <a> without href
also trips 3 jsx-a11y warnings (anchor-is-valid, no-static-element-
interactions, click-events-have-key-events) that a real <button> avoids for
free. Updated the one test that asserted `closest('a')` to match.

See #30779 for the general div.prose blast-radius
writeup.

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

* test(ui): regenerate applications baseline, drop non-deterministic pages

playwright-visual-regression failed on 6 static pages in PR #30780's CI run
(30667147887). Investigated each by downloading the actual/expected/diff
PNGs:

- applications (marketplace): ~3% diff, static marketplace-app content
  (no seeded data). Genuine, minor Typography-driven vertical shift
  (ApplicationCard's `Typography as="h5" size="text-md"` doesn't reproduce
  antd Typography.Title level=5's exact metrics). Regenerated the baseline
  from this run's correct render.

- teams, data-quality: baked-in aggregate/seeded content (team names with
  Playwright-generated random suffixes, Total Users/Teams counts, Data
  Assets Coverage numbers) that drifts every CI run as other specs create
  data in the shared environment — same non-determinism class as the
  already-dropped 'roles'/'incident-manager' entries. No file this sweep
  touches renders these values. Dropped from PAGES with the same
  documentation style as the existing precedents.

- landing-page (+ its collapsed-sidebar variant): already masked/tolerance-
  bumped for known async-widget variance, but still saw 9-21% diffs this
  run — the committed baseline itself was captured mid-load (skeleton rows,
  empty KPI state), so no single screenshot can be stable against it.
  Confirmed unrelated to this sweep (LeftSidebar.component.tsx is the only
  touched file on this page, and only its logout-modal text changed).
  Dropped both entries.

- explore: small (~2%) remaining diff after the existing masks, from an
  unmasked pagination total and an environment-toggled AI-search icon.
  Bumped maxDiffPixelRatio to 0.03, matching the tolerance already used for
  landing-page's known-volatile-widget case.

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

* style(ui): run organize-imports + eslint --fix + prettier over the sweep's changed files

ui-checkstyle failed on PR #30780 with a large "ESLint + Prettier + Organise
Imports (src)" file list — the gate re-runs the auto-fixers (organize-
imports-cli, `eslint --fix`, `prettier --write`) against every file changed
vs origin/main and fails if that leaves a diff. The whole Typography sweep
(this branch's full stack on top of antd-migration/wave-1) consistently used
single-quoted JSX attributes (`as='h5'`, `size='text-md'`) where the
project's .prettierrc.yaml (singleQuote: true, but JSX attributes always use
double quotes as part of the JSX spec Prettier follows) expects double
quotes, plus a handful of import/attribute wrapping differences — none of it
semantic, all of it mechanical.

Ran `make ui-checkstyle-changed` (organize-imports:cli, then `yarn lint:base
--fix`, then `yarn pretty:base --write`, in that order so Prettier has final
say) against the full file list this PR's stack changed vs origin/main.
organize-imports and i18n/app-docs generation found nothing to change;
Prettier and ESLint's autofix corrected quoting/wrapping only.

Verified no semantic changes: `tsc --noEmit` reports the same 524
pre-existing errors before and after (same file set, byte-for-byte), and
`eslint` reports the same 1402 warnings / 0 errors as this PR's original CI
run. `prettier --check` and `eslint` (no --fix) both now pass clean over the
same file list.

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

* refactor(ui): modernize SignInPage heading spacing and color

Replaces the sibling margin-top rules in login.style.less with a flex gap
on the .login-box container (already display:flex), and drops the
.header-text/.login-form margin rules those replaced.

The heading keeps as="h3" so the page's main heading retains heading
semantics (core Typography defaults to span), uses the semantic
tw:text-primary token rather than a raw palette value so dark mode is
carried, and uses a responsive tw:gap-6 md:tw:gap-10 to preserve the
breakpoint reductions the deleted .less rules had at 670px and 1200px.

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

* fix(ui): restore inline layout for search suggestion fqn text

Typography always wraps its children in a block-level div.prose,
regardless of the `as` prop. Rendering the `(fqn)` hint as Typography
inside the suggestion Link/Button broke the single-line layout,
pushing the fqn onto a second line and shifting the Button's click
point off the anchor's actual line boxes. GlobalSearchSuggestions.spec.ts
"Navigate to column from column suggestion" timed out waiting for the
resulting navigation because the click never reached the Link.

Use a plain span (this Typography added nothing but a className) to
keep the text inline and preserve the original click target geometry.

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

* fix(ui): stop persona-switch and profile-link clicks from being swallowed

core Typography's `ellipsis={{ tooltip: true }}` renders its content
inside a react-aria TooltipTrigger `<button>`. react-aria's usePress
stops native click propagation by default (see
@react-aria/interactions usePress.js), so a click landing on that
button never bubbles to an ancestor's onClick.

Two call sites in this file relied on exactly that bubbling:
- the persona-name Typography inside `data-testid="persona-label"`,
  whose *ancestor* div.onClick performs the actual persona switch;
- the "View Profile" Typography inside a react-router `<Link>`.

Clicking either silently no-op'd instead of switching persona or
navigating. This is why SettingsNavigationPage.spec.ts "should handle
multiple items being hidden at once" saw app-bar-item-explore still
visible: `getByRole('menuitem', { name: persona.displayName }).click()`
never actually invoked `handleSelectedPersonaChange`, so the persona
whose nav config was edited was never selected.

Apply the same fix already used for `default-persona` in this file:
plain `ellipsis` truncation plus a native `title` attribute, which
preserves the hover-tooltip text without adding a click-swallowing
interactive element.

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

* fix(ui): re-wire test case card selection after Typography ellipsis change

Same react-aria click-swallowing hazard as UserProfileIcon: the test
name and test-definition name in each test-case card use
`ellipsis={{ tooltip: true }}`, which wraps them in a react-aria
TooltipTrigger `<button>` that stops click propagation by default.
The card's selection handler lives on the outer `<Space
onClick={() => handleCardClick(test)}>`, so clicking the (often
truncated) test name inside the button no longer selected the card.

This broke test-case selection in the "Add Test Case" list shared by
TestSuite.spec.ts "Logical TestSuite", TestSuiteMultiPipeline.spec.ts
"TestSuite multi pipeline support", and TestSuiteDetailsPage.spec.ts
"Add test case modal on Test Suite details page - filters and select"
- all three clicked `getByTestId(testCaseName)` inside
`test-case-selection-card` and then hung waiting for the resulting
test-suite create/deploy API calls that never fired because no test
case was ever actually selected.

Attach the same onClick directly to both Typography elements so
selection no longer depends on the click bubbling past the
click-swallowing wrapper.

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

* fix(ui-core): drop Typography's block wrapper for span/div

Typography always rendered its content inside a `<div className="prose">`.
antd's `Typography.Text` rendered an inline `<span>`, so every mechanically
converted call site became block-level: text embedded mid-sentence wrapped onto
its own line, and a nested Typography produced a `<div>` inside a `<span>` —
invalid DOM that trips React's validateDOMNesting.

`styles/typography.css` applies its real rules through a *descendant* selector
(`.prose :not(...)`), and every rule in it is gated on an element type — p,
h1-h6, ol, ul, li, blockquote, a, code, pre, img, figure, table. For those the
wrapper is load-bearing; moving `prose` onto the element would stop the rule
matching (a `p` would silently lose its margins).

`span` and `div` are targeted by no such rule. There the wrapper contributes
only the element-level `.prose` layer — the `--tw-prose-*` vars plus `color`,
`font-size` and `line-height`, all inherited properties — so setting `prose`
directly on the element yields an identical computed style on the text while
dropping the spurious block box.

Render the element directly in that case. Ellipsis keeps the wrapper (it carries
the truncation classes) and so does a non-default quote variant (also styled via
a descendant selector). Deliberately a small allowlist: anything unlisted is
unchanged.

Verified with the full consumer suite: 1155 jest suites / 13637 tests, no
failures.

* fix(ui): address Typography sweep review findings

Follow-ups from the code review on the antd Typography sweep:

- SignInPage: `md:tw:gap-10` is not a valid Tailwind v4 prefix-mode class and
  generated no CSS, so the breakpoint spacing the deleted `.less` provided was
  silently lost. The prefix must lead the variant chain (`tw:md:gap-10`).

- SignInPage: `.forgot-password-link`'s `flex: 1` only applies when the anchor is
  a direct flex child of antd's `inline-flex` label. Typography wraps `as="a"` in
  a block `div.prose`, breaking that. Own the row layout locally instead.

- PageHeader: `{...titleProps}` / `{...subHeaderProps}` were spread *before* a
  hardcoded `className`, so a caller-supplied className was silently dropped.
  Merge them instead.

- AddTestCaseList: drop the `onClick` handlers that were re-wired onto the
  Typography elements. They sit inside the react-aria TooltipTrigger button, so
  today they never fire; and once that button stops swallowing propagation
  (#30804) they would fire *in addition to* the row-level handler on the
  <Space>, and since `handleCardClick` toggles, the two would cancel out.
  Selection is left to the single row-level handler.

- DataAssetsHeaderExtraInfo / MarketPlaceAppDetails: both used a default
  (inline) Typography purely as a layout container around block content. Mark
  them `as="div"`, which is what they were structurally, and which clears the
  last remaining invalid nesting — a scan of all 591 Typography files now
  reports none.

* test(ui): refresh three visual baselines for restored inline flow

Dropping Typography's block `div.prose` wrapper for span/div lets former
`Typography.Text` call sites participate in inline flow again, which legitimately
changes rendering on three of the twenty harness pages.

Each diff was compared against its previous baseline before adoption rather than
adopted wholesale:

- applications: "Cache Warmup" no longer force-wraps onto two lines, so the
  `Disabled` badge sits inline beside the title instead of being pushed down;
  cards tighten by ~12px. The bold "Data Insights" descriptions are present in
  the old baseline too - pre-existing markdown, not a change here.
- landing-page / landing-page-sidebar-collapsed: the hero tightens, shifting the
  widget grid up ~10px. The large diff regions are the harness's own mask fills
  moving with it, not content changes.

These baselines had encoded the block-wrapper layout as expected, which is how
the regression escaped review in the first place. Captured from CI artifacts per
docs/antd-migration/README.md - never from a local run.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.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.

Typography ellipsis tooltip swallows clicks intended for ancestor elements

3 participants