Skip to content

feat(ui): Glossary Terms table on TableV2 — adaptable search, column resize, drag reorder, sticky header#29728

Open
siddhant1 wants to merge 25 commits into
mainfrom
glossary-table-v2
Open

feat(ui): Glossary Terms table on TableV2 — adaptable search, column resize, drag reorder, sticky header#29728
siddhant1 wants to merge 25 commits into
mainfrom
glossary-table-v2

Conversation

@siddhant1

@siddhant1 siddhant1 commented Jul 3, 2026

Copy link
Copy Markdown
Member

What

Completes the migration of the Glossary Terms table to the React Aria TableV2/TableCard stack and restores the behaviors that were lost during the migration.

Changes

  • Adaptable search bar — the toolbar search input now grows/shrinks to fill available width, while Status / Bulk Edit / Expand-All stay fixed-size and always visible (no more clipped buttons on narrow viewports).
  • Column resize indicator — the ColumnResizer handle was invisible under Tailwind v4 (deprecated bg-[--var] arbitrary-value syntax silently dropped). Switched to the valid bg-border-secondary / bg-border-brand utilities: a subtle divider at idle, brand-blue while dragging.
  • Row drag-and-drop reorder / re-parent — React Aria's Row strips native HTML5 drag props (filterDOMProps), so the old onRow drag handlers never reached the DOM. Reimplemented with useDragAndDrop (dragAndDropHooks), reusing the existing move-confirmation modal + PATCH flow. Load-more placeholder rows are excluded as drag sources/targets.
  • Sticky header — the table now owns its own vertical scroll (scroll.y) so the sticky <thead> engages; the outer container no longer scrolls; and infinite scroll is re-pointed at the internal scroll region.

Files

  • GlossaryTermTab.component.tsx — search layout, RAC drag-and-drop, internal-scroll + sticky-header wiring, infinite-scroll rework.
  • common/Table/TableV2.tsx — column resize indicator token fix.

Testing

Manual verification in the Glossary → Terms tab:

  • Resize the viewport → search adapts, buttons stay visible.
  • Drag a column edge → visible resize handle; drag a row onto another → re-parent modal → nests; drop on empty area → moves to top level.
  • Scroll the term list → header stays pinned; infinite scroll still loads more (regular + search); short lists auto-fill.

Note: the scroll.y offset (calc(100vh - 350px)) may need minor tuning per layout.

🤖 Generated with Claude Code


Summary by Gitar

  • Drag-and-drop mechanics:
    • Added useDragAndDrop from react-aria-components to handle row re-parenting and promotion, replacing deprecated HTML5 handlers.
    • Added getDropOperation logic to manage valid drop targets and prevent interaction with load-more rows.
  • Component architecture:
    • Moved table layout to TableCard.Root and updated GlossaryTermTab to use internal scroll.y for consistent sticky header behavior.
  • Refactoring:
    • Removed useInView infinite scroll observer in favor of the table's internal scroll management.
    • Added glossaryTermByFqn lookup memo for efficient drag-and-drop target identification.
  • Bug fixes:
    • Fixed missing permissions.EditAll dependency in extraTableFilters memo to ensure accurate bulk-edit button visibility.

This will update automatically on new commits.

Greptile Summary

This PR completes the Glossary Terms table migration to the React Aria TableV2/TableCard stack, restoring four behaviors lost during the migration: an adaptable search bar, a visible column-resize indicator, row drag-and-drop reorder/re-parent via useDragAndDrop, and a sticky header backed by an internal scroll region. Playwright tests are updated to match the new DOM structure — data-testid selectors replace Ant Design class-name selectors, and scroll helpers walk the DOM to find the real scrollable container instead of the now-static outer div.

  • GlossaryTermTab.component.tsx — switches from useInView-based infinite scroll to a MutationObserver + scroll-event hybrid, wires up React Aria drag-and-drop with useDragAndDrop, and routes internal scroll through the TableV2 scroll.y prop so the sticky header engages.
  • TableV2.tsx — fixes the ColumnResizer color token from the invalid bg-[--var] arbitrary-value syntax to the valid bg-border-secondary / bg-border-brand Tailwind v4 utilities.
  • Test filesLargeGlossaryPerformance.spec.ts and GlossaryStatusFilterLargeDataset.spec.ts now use the DOM-walking helper to locate the real scroll container, resolving the silent-discard scroll tests flagged in earlier review rounds.

Confidence Score: 5/5

Safe to merge; all changes are additive UI behavior or test selector updates with no data-model or API contract changes.

The core drag-and-drop and infinite-scroll rewrites are mechanically sound and the previously flagged scroll-container issues in the test suite have been addressed. The two newly found concerns are narrow edge cases that do not affect primary user flows and are mitigated by surrounding guards.

GlossaryTermTab.component.tsx carries the most complex logic (dual MutationObserver + scroll handler, drag-and-drop wiring, glossaryTermByFqn map) and warrants the closest reading; DraggableMenuItemV2.component.tsx should be verified for keyboard accessibility before the next accessibility audit.

Important Files Changed

Filename Overview
openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx Major refactor: adds React Aria drag-and-drop, internal table scroll, infinite-scroll rework, and adaptable search toolbar; several subtle issues in scroll/drag logic carry over from prior review cycles.
openmetadata-ui/src/main/resources/ui/src/components/common/Table/TableV2.tsx Fixes ColumnResizer color token from invalid arbitrary-value syntax to valid Tailwind v4 utilities; adds data-testid to toolbar and column dropdown title; straightforward change.
openmetadata-ui/src/main/resources/ui/src/components/common/Table/DraggableMenu/DraggableMenuItemV2.component.tsx Restructures drag and toggle responsibilities: full row becomes drag source, nested button handles visibility toggle; removes onPress from Dropdown.Item, creating a keyboard accessibility gap.
openmetadata-ui/src/main/resources/ui/src/components/Glossary/glossaryV1.less Replaces old Ant Design-targeted sticky-header CSS with RAC-compatible drag row styles and top-level drop-target highlight; clean change.
openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts Updates test utilities to use data-testid selectors, scopes the Move modal button to the confirmation modal, and adds helper ensureColumnsVisible; changes look correct.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Glossary/LargeGlossaryPerformance.spec.ts Extracts shared helpers to walk the DOM for the real scroll container; updates row selectors from Ant Design class names to data-row-key; comprehensive fix for the scroll-to-load path.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Glossary/GlossaryStatusFilterLargeDataset.spec.ts Adopts the same DOM-walking scroll helper and data-testid selectors for the status-filter infinite scroll tests; fixes the silent-discard problem flagged in a previous review.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Glossary/GlossaryHierarchy.spec.ts Adds new H-M03b test: drag a nested term to root level; uses existing dragAndDropTerm/confirmationDragAndDropGlossary helpers correctly.

Reviews (22): Last reviewed commit: "Merge branch 'main' into glossary-table-..." | Re-trigger Greptile

@siddhant1 siddhant1 requested a review from a team as a code owner July 3, 2026 09:35
Copilot AI review requested due to automatic review settings July 3, 2026 09:35
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@github-actions

github-actions Bot commented Jul 3, 2026

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!

@siddhant1 siddhant1 added UI UI specific issues safe to test Add this label to run secure Github workflows on PRs labels Jul 3, 2026
Comment on lines +1478 to +1492
const { dragAndDropHooks } = useDragAndDrop({
getItems: (keys) => {
const key = Array.from(keys)[0];
const record = key ? glossaryTermByFqn.get(String(key)) : undefined;

if (!record || record.isLoadMoreButton) {
return [];
}

return [{ [GLOSSARY_TERM_DRAG_TYPE]: record.fullyQualifiedName ?? '' }];
},
acceptedDragTypes: [GLOSSARY_TERM_DRAG_TYPE],
onDragStart: (event) => {
const key = Array.from(event.keys)[0];
draggedGlossaryTermRef.current = key

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Edge Case: Row drag reorder is not gated by edit permission

The new useDragAndDrop wiring is always enabled and passed to both the populated and empty tables regardless of permissions.EditAll/permissions.Create. The toolbar Bulk Edit button is permission-gated (getBulkEditButton(permissions.EditAll, ...)), but any user who can view the terms can now drag a row to re-parent/reorder it. This triggers the confirmation modal and a patchGlossaryTerm PATCH which will fail server-side for unauthorized users, surfacing an error toast instead of preventing the action. Consider only providing dragAndDropHooks when the user has the required permission (e.g. permissions.EditAll/permissions.Create), or making getDropOperation/getItems return cancel/[] when unauthorized so the drag affordance is disabled entirely.

Disable drag source when the user lacks edit permission (also guard getDropOperation).:

getItems: (keys) => {
  if (!permissions.EditAll) {
    return [];
  }
  const key = Array.from(keys)[0];
  const record = key ? glossaryTermByFqn.get(String(key)) : undefined;
  if (!record || record.isLoadMoreButton) {
    return [];
  }
  return [{ [GLOSSARY_TERM_DRAG_TYPE]: record.fullyQualifiedName ?? '' }];
},
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎


const GLOSSARY_TERM_DRAG_TYPE = 'application/x-om-glossary-term';

const GLOSSARY_TABLE_SCROLL = { y: 'calc(100vh - 350px)' };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Quality: Sticky-header scroll offset is a hardcoded magic value

GLOSSARY_TABLE_SCROLL = { y: 'calc(100vh - 350px)' } hardcodes a viewport-relative offset that assumes a fixed amount of chrome (350px) above the table. In embedded/modal layouts or when the header height changes, the table's internal scroll region will be mis-sized (too tall, causing double scrollbars, or too short). The PR description itself flags this as needing per-layout tuning. Consider deriving the height from the actual container via a ref/ResizeObserver (a containerWidth observer already exists in this component) rather than a constant, or documenting the assumption clearly.

Document/derive the scroll height dynamically to avoid layout-specific breakage.:

// Prefer computing from the measured container instead of a fixed 350px:
// const scrollY = containerHeight ? containerHeight - toolbarHeight : 'calc(100vh - 350px)';
const GLOSSARY_TABLE_SCROLL = { y: 'calc(100vh - 350px)' };
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Migrates the Glossary Terms table UI to the React Aria TableV2/TableCard stack and restores key UX behaviors (adaptive search layout, column resizing affordance, drag-and-drop re-parenting, sticky header + internal scroll for infinite loading).

Changes:

  • Updates GlossaryTermTab to render TableV2 inside TableCard, add internal scroll.y, and implement RAC drag-and-drop via useDragAndDrop.
  • Adjusts glossary table styling for nested-row shading and RAC sticky-header scrolling behavior.
  • Fixes ColumnResizer handle styling by replacing deprecated Tailwind arbitrary CSS var syntax with theme utility classes.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx Switch to TableV2/TableCard, implement RAC drag-and-drop hooks, and rework table scroll/infinite-load wiring.
openmetadata-ui/src/main/resources/ui/src/components/Glossary/glossaryV1.less Update nested-row background styling for the new table DOM and adjust overflow behavior for sticky header scroll container.
openmetadata-ui/src/main/resources/ui/src/components/common/Table/TableV2.tsx Fix resizer indicator styling using valid Tailwind theme tokens.

Comment on lines +1479 to +1488
getItems: (keys) => {
const key = Array.from(keys)[0];
const record = key ? glossaryTermByFqn.get(String(key)) : undefined;

if (!record || record.isLoadMoreButton) {
return [];
}

return [{ [GLOSSARY_TERM_DRAG_TYPE]: record.fullyQualifiedName ?? '' }];
},
Comment on lines 497 to 499
// Monitor for DOM changes to detect when the table becomes scrollable
useEffect(() => {
const observer = new MutationObserver(() => {

const GLOSSARY_TERM_DRAG_TYPE = 'application/x-om-glossary-term';

const GLOSSARY_TABLE_SCROLL = { y: 'calc(100vh - 350px)' };

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.

P2 Hardcoded viewport-offset magic number

calc(100vh - 350px) assumes exactly 350 px of chrome above the table (nav bars, breadcrumbs, tab headers). The PR description acknowledges this "may need minor tuning per layout," but storing it as a module-level constant makes it invisible and easy to miss when layout changes. Consider deriving the offset from tableContainerRef at runtime (similar to the existing containerWidth measurement) so the table height stays correct regardless of the page's header height.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +1461 to +1476
const glossaryTermByFqn = useMemo(() => {
const termByFqn = new Map<string, ModifiedGlossaryTerm>();
const walk = (terms: ModifiedGlossaryTerm[]) => {
terms.forEach((term) => {
if (term.fullyQualifiedName) {
termByFqn.set(term.fullyQualifiedName, term);
}
if (term.children?.length) {
walk(term.children as ModifiedGlossaryTerm[]);
}
});
};
walk(filteredGlossaryTerms);

return termByFqn;
}, [filteredGlossaryTerms]);

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.

P1 glossaryTermByFqn corrupted by load-more placeholders, breaking drag-and-drop on paginated parents

processTermsWithLoadMore appends a load-more placeholder to a parent term's children via spread ({ ...term, isLoadMoreButton: true, key: ... }). The placeholder inherits the parent's fullyQualifiedName. When walk descends into that parent's children, it hits the placeholder and overwrites the parent's entry in the map:

termByFqn.set("Glossary.TermA", loadMoreButton) ← clobbers the real term

From that point, for any parent with hasMoreChildren: true and childrenPagingAfter set:

  • getItems returns [] (placeholder's isLoadMoreButton check), so the row cannot be dragged
  • getDropOperation returns 'cancel', so nothing can be dropped onto it

Fix: skip load-more placeholders in the walk by adding an !term.isLoadMoreButton guard before inserting into the map.

…resize, drag reorder, sticky header

Finish migrating the Glossary Terms table to the React Aria TableV2/TableCard stack and restore the behaviors lost in the migration:

- Adaptable search bar: the toolbar search grows/shrinks to fill space while Status/Bulk Edit/Expand-All stay fixed and visible.
- Column resize indicator: fix the ColumnResizer handle, which was invisible under Tailwind v4 (deprecated bg-[--var] arbitrary syntax -> valid bg-border-* utilities).
- Row drag-and-drop reorder/re-parent: replace the stripped native HTML5 onRow drag props with React Aria useDragAndDrop (dragAndDropHooks), reusing the existing move-confirmation flow; load-more rows excluded as sources/targets.
- Sticky header: give the table its own vertical scroll (scroll.y) so the sticky <thead> engages, make the outer container non-scrolling, and re-point infinite scroll at the internal scroll region.
- glossaryV1.less: RAC-compatible nested-row shading and the sticky-header enabling rule (neutralize the table's overflow-x).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 3, 2026 10:57
@siddhant1 siddhant1 force-pushed the glossary-table-v2 branch from 900c293 to c86e6be Compare July 3, 2026 10:57

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

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

Comment on lines +188 to 192

table {
overflow-x: visible;
}
}
Copilot AI review requested due to automatic review settings July 3, 2026 11:03

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1256

  • extraTableFilters is memoized with useMemo, but its dependency array is missing several values used inside the memo (e.g., t, permissions.EditAll, handleEditGlossary, handleSearchChange). This can cause stale UI (e.g., bulk edit button visibility not updating with permissions, search input handler changes not reflected, and i18n text not updating on language change) and may violate the repo’s hook-deps lint rules.
  }, [
    isAllExpanded,
    isExpandingAll,
    isStatusDropdownVisible,
    statusDropdownMenu,

The suite still mocked the old react-dnd stack and rendered the real
react-aria resizable + drag-and-drop TableV2, which does not mount in jsdom
(all 43 tests crashed on render). Mock common/Table/TableV2 with a lightweight
table that invokes the component's column render functions, expand icon, and
toolbar filters — matching the pattern used by other TableV2-based suites —
and drop the dead react-dnd mocks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 64%
64.46% (75503/117117) 48.42% (45055/93038) 49.09% (13603/27705)

onDragStart stored whatever row key was dragged into draggedGlossaryTermRef
without the isLoadMoreButton guard its sibling getItems has. Since react-aria
spreads drag props on the whole row, a load-more placeholder could be dragged
and reach the custom root-drop handler, opening the move modal with an invalid
"<parentFqn>-load-more" id. Align onDragStart with getItems so placeholder
drags are inert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

siddhant1 and others added 3 commits July 7, 2026 11:04
The Glossary Terms table moved from AntD Table to react-aria TableV2, changing
the rendered markup. Update the shared glossary util and specs that still
queried AntD-specific selectors so the glossary E2E suite matches the new DOM.

- validateGlossaryTerm: getByRole('columnheader',{name}) -> th hasText — the
  react-aria <th> isn't matched by the columnheader role query. This helper is
  called by createGlossaryTerm, so its failure cascaded across nearly the whole
  glossary suite.
- filterStatus / status-filter specs: tbody.ant-table-tbody > tr -> tbody > tr
- row counts: tbody .ant-table-row -> tbody tr[data-row-key]
- nested level rows: .ant-table-row-level-1 -> tr[data-level="1"]
- expand controls: .ant-table-row-expand-icon / .vertical-baseline
  -> [data-testid="expand-icon"]

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the glossary selector migration, validated against a real
TableV2 build (dev server) which surfaced interaction/role differences the
AntD DOM masked:

- selectActiveGlossaryTerm: use a plain click; a forced click on the term
  link is swallowed by the draggable react-aria row and never navigates.
- confirmationDragAndDropGlossary: scope the "Move" button to the
  confirmation modal — TableV2 drag handles expose aria-label "Move the
  term", which also matched getByRole('button', { name: 'Move' }).
- openColumnDropdown: drop the AntD '.ant-dropdown [role=menu]' scope; the
  customize dropdown is a react-aria popover.
- Term name cells are react-aria rowheaders, not cells:
  getByRole('cell', { name }) -> getByRole('rowheader', { name }); the
  exact-name variant (drag-handle label pollutes the accessible name) ->
  getByTestId.
- GlossaryTermTab: emit a stable glossary-term-level-{n} row class
  (react-aria clobbers data-level), replacing .ant-table-row-level-N.
- Fix a glossary-term-table testid typo (-> glossary-terms-table).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace brittle CSS-class selectors with data-testid in the glossary terms
table code (specs + the component/DOM queries they depend on):

- GlossaryTermTab: add data-testid to the scroll container, status-filter
  options and Save/Cancel buttons; switch findScrollContainer + the drag
  effect off class/Tailwind querySelectors (.p-x-md.p-y-md,
  .glossary-terms-scroll-container .glossary-terms-table table) to testids;
  drop the now-unused glossary-term-level row class.
- TableV2: add data-testid="table-toolbar" (backs the drag effect's toolbar
  query). DraggableMenuItem: add data-testid="column-menu-item-{key}".
- Specs: .glossary-terms-scroll-container, .glossary-dropdown-label,
  .glossary-term-level-1 (now counted via child term testids +
  load-more-children-button), .draggable-menu-item*, .glossary-details,
  status .ant-btn-primary/.ant-btn-default, .status-selection-dropdown, and
  .ant-skeleton all switch to getByTestId. Column/status helpers now take
  stable keys/values.

Validated against a real TableV2 build (dev server).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

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

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

The column-customize helpers now select by data-testid keyed on the column
key (column-menu-item-{key}); this caller still passed the display label
('Reviewer'), so getByTestId('column-menu-item-Reviewer') never matched and
the test timed out. Pass the key ('reviewers') and keep the label for the
header visibility check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Discard glossary term fetch responses whose search term or status filter no
longer matches the active context, so an in-flight listing request can no
longer repopulate the table after a search has cleared it. Deduplicate terms
by fullyQualifiedName (memo + load-more merge) to avoid duplicate row keys,
which made the table collection throw "Invalid array length" while rendering.

Fix the large-glossary pagination E2E to scroll the table's inner scroll
region (which owns the vertical overflow) instead of the outer container, so
infinite scroll is actually triggered.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

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

Copilot reviewed 13 out of 13 changed files in this pull request and generated 6 comments.

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gitar-bot

gitar-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown
Code Review 👍 Approved with suggestions 1 resolved / 3 findings

Migrates the Glossary Terms table to the TableV2 stack with improved drag-and-drop, sticky headers, and responsive search layout. Please address the missing drag-and-drop permission gating and consider replacing the magic scroll offset with a more robust layout-relative value.

💡 Edge Case: Row drag reorder is not gated by edit permission

📄 openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1478-1492

The new useDragAndDrop wiring is always enabled and passed to both the populated and empty tables regardless of permissions.EditAll/permissions.Create. The toolbar Bulk Edit button is permission-gated (getBulkEditButton(permissions.EditAll, ...)), but any user who can view the terms can now drag a row to re-parent/reorder it. This triggers the confirmation modal and a patchGlossaryTerm PATCH which will fail server-side for unauthorized users, surfacing an error toast instead of preventing the action. Consider only providing dragAndDropHooks when the user has the required permission (e.g. permissions.EditAll/permissions.Create), or making getDropOperation/getItems return cancel/[] when unauthorized so the drag affordance is disabled entirely.

Disable drag source when the user lacks edit permission (also guard getDropOperation).
getItems: (keys) => {
  if (!permissions.EditAll) {
    return [];
  }
  const key = Array.from(keys)[0];
  const record = key ? glossaryTermByFqn.get(String(key)) : undefined;
  if (!record || record.isLoadMoreButton) {
    return [];
  }
  return [{ [GLOSSARY_TERM_DRAG_TYPE]: record.fullyQualifiedName ?? '' }];
},
💡 Quality: Sticky-header scroll offset is a hardcoded magic value

📄 openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:127 📄 openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1618 📄 openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1656

GLOSSARY_TABLE_SCROLL = { y: 'calc(100vh - 350px)' } hardcodes a viewport-relative offset that assumes a fixed amount of chrome (350px) above the table. In embedded/modal layouts or when the header height changes, the table's internal scroll region will be mis-sized (too tall, causing double scrollbars, or too short). The PR description itself flags this as needing per-layout tuning. Consider deriving the height from the actual container via a ref/ResizeObserver (a containerWidth observer already exists in this component) rather than a constant, or documenting the assumption clearly.

Document/derive the scroll height dynamically to avoid layout-specific breakage.
// Prefer computing from the measured container instead of a fixed 350px:
// const scrollY = containerHeight ? containerHeight - toolbarHeight : 'calc(100vh - 350px)';
const GLOSSARY_TABLE_SCROLL = { y: 'calc(100vh - 350px)' };
✅ 1 resolved
Bug: Infinite-scroll relies on fragile nested DOM selector

📄 openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:466-480 📄 openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:521-523
findScrollContainer now resolves the scroll region via document.querySelector('.glossary-terms-scroll-container .glossary-terms-table table')?.parentElement. This couples infinite scroll and sticky-header detection to the exact internal DOM structure of TableV2/React-Aria (the <table>'s parent being the scroll.y element). If the core Table component wraps the <table> in an additional element (or renders it inside ResizableTableContainer), parentElement will be the wrong node and the scroll listener will attach to a non-scrolling element, silently breaking infinite scroll. The MutationObserver effect (line 521) also observes .glossary-terms-scroll-container but reads searchTerm from a stale closure since searchTerm is not in its dependency array. Prefer keying off a stable, component-owned attribute (e.g. a data attribute the table sets on its scroll region) rather than parentElement, and add searchTerm to the observer effect deps.

🤖 Prompt for agents
Code Review: Migrates the Glossary Terms table to the TableV2 stack with improved drag-and-drop, sticky headers, and responsive search layout. Please address the missing drag-and-drop permission gating and consider replacing the magic scroll offset with a more robust layout-relative value.

1. 💡 Edge Case: Row drag reorder is not gated by edit permission
   Files: openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1478-1492

   The new `useDragAndDrop` wiring is always enabled and passed to both the populated and empty tables regardless of `permissions.EditAll`/`permissions.Create`. The toolbar Bulk Edit button is permission-gated (`getBulkEditButton(permissions.EditAll, ...)`), but any user who can view the terms can now drag a row to re-parent/reorder it. This triggers the confirmation modal and a `patchGlossaryTerm` PATCH which will fail server-side for unauthorized users, surfacing an error toast instead of preventing the action. Consider only providing `dragAndDropHooks` when the user has the required permission (e.g. `permissions.EditAll`/`permissions.Create`), or making `getDropOperation`/`getItems` return `cancel`/`[]` when unauthorized so the drag affordance is disabled entirely.

   Fix (Disable drag source when the user lacks edit permission (also guard getDropOperation).):
   getItems: (keys) => {
     if (!permissions.EditAll) {
       return [];
     }
     const key = Array.from(keys)[0];
     const record = key ? glossaryTermByFqn.get(String(key)) : undefined;
     if (!record || record.isLoadMoreButton) {
       return [];
     }
     return [{ [GLOSSARY_TERM_DRAG_TYPE]: record.fullyQualifiedName ?? '' }];
   },

2. 💡 Quality: Sticky-header scroll offset is a hardcoded magic value
   Files: openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:127, openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1618, openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1656

   `GLOSSARY_TABLE_SCROLL = { y: 'calc(100vh - 350px)' }` hardcodes a viewport-relative offset that assumes a fixed amount of chrome (350px) above the table. In embedded/modal layouts or when the header height changes, the table's internal scroll region will be mis-sized (too tall, causing double scrollbars, or too short). The PR description itself flags this as needing per-layout tuning. Consider deriving the height from the actual container via a ref/ResizeObserver (a `containerWidth` observer already exists in this component) rather than a constant, or documenting the assumption clearly.

   Fix (Document/derive the scroll height dynamically to avoid layout-specific breakage.):
   // Prefer computing from the measured container instead of a fixed 350px:
   // const scrollY = containerHeight ? containerHeight - toolbarHeight : 'calc(100vh - 350px)';
   const GLOSSARY_TABLE_SCROLL = { y: 'calc(100vh - 350px)' };

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

@sonarqubecloud

Copy link
Copy Markdown

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

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants