The effect was calling the default getData = () => [] even when the
consumer used the data prop, immediately clearing the loading state.
Now detects whether getData was explicitly provided and only runs the
async loading logic for that case. Also clears isLoading when data
transitions from undefined to defined.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Jonathan Roebuck <jroebuck@spotify.com>
Move the cell wrapper requirement documentation from the package
README to the docs-ui table component page where it belongs.
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
Added TSDoc comments to ColumnConfig.cell, RowRenderFn, CellProps,
CellTextProps, and CellProfileProps making it explicit that cell render
functions must return a cell component (Cell, CellText, or CellProfile)
as the top-level element. Also added a Table Cell Requirement section
to the package README with correct and incorrect usage examples.
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
* feat(ui): centralize routing in BUIProvider
BUIProvider now auto-detects React Router context and provides
client-side navigation for all BUI components. Retired
InternalLinkProvider and added BUIRouterProvider as a public
export for integration use.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
* feat(plugin-app): move BUIProvider inside app router
Moved BUIProvider from wrapping AppRouter to being a child inside
it, so it detects the React Router context and provides client-side
routing for all BUI components.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
* feat(core-app-api): add BUIRouterProvider to legacy app router
Added BUIRouterProvider inside the legacy AppRouter to provide
React Aria routing for all BUI components.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
* docs(ui): update BUIProvider documentation for routing
Updated installation docs to cover BUIProvider's routing role
and the requirement to render it inside a React Router context.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
* refactor(ui): move BUIProvider from analytics to provider directory
BUIProvider now handles both analytics and routing, so it no longer
belongs in the analytics directory.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
* fix(ui): add BUIProvider to storybook stories with MemoryRouter
Added BUIProvider inside MemoryRouter in all stories that use
routing, so client-side navigation works in Storybook.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
* fix(plugin-app): move BUIProvider inside RouterComponent
Moved BUIProvider to wrap all content inside RouterComponent
so that extraElements (like dialogs) also get BUI context.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
* refactor: replace BUIRouterProvider with BUIProvider in legacy app
Use BUIProvider directly inside the legacy AppRouter instead of a
separate BUIRouterProvider export. Removes BUIRouterProvider from
the public API of @backstage/ui.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
* refactor(ui): inline routing logic into BUIProvider
Removed the routing/ directory and inlined the RouterProvider
setup directly into BUIProvider since it's the only consumer.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
---------
Signed-off-by: Johan Persson <johanopersson@gmail.com>
Add SearchAutocomplete and SearchAutocompleteItem components for
building accessible search-with-results patterns. Built on React
Aria's Autocomplete with virtual focus for keyboard navigation
and a non-modal popover for results.
Features:
- Controlled input via inputValue/onInputChange
- Configurable popover width and placement
- Rich content support per result item
- Item selection via onAction
- defaultOpen prop for visual testing
- Close on interact outside and input clear
Includes Storybook stories, docs-ui documentation, changeset,
and API report.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
Root cause: The --bui-bg-tint-* CSS tokens were removed from the design system
in favour of --bui-bg-neutral-* tokens, but Table.module.css was not updated
during the migration, leaving row hover, selected, pressed, and disabled states
with no visual effect.
Fix: Replace --bui-bg-tint-hover/pressed/disabled with the equivalent
--bui-bg-neutral-1-hover/pressed/disabled tokens, matching the migration
mapping documented in the CHANGELOG.
Signed-off-by: shivamtiwari3 <33183708+shivamtiwari3@users.noreply.github.com>
Adds a `customActions` prop that renders a flex row of React nodes on
the right side of a list item. Click and keyboard events on the actions
area are stopped from propagating to the item's selection handler.
Adds two new stories:
- WithActionsMenu: ButtonIcon (ellipsis) triggering a dropdown Menu
- WithActionsTags: inline TagGroup showing metadata tags per item
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
- Check icon is now only rendered when the item is selected, so it
takes no space when unselected and pushes content in when selected
- Icon slot is now a fixed 32x32px box to consistently frame any
React icon component (e.g. from Remix icons)
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
Adds standalone `ListBox` and `ListBoxItem` components to `@backstage/ui`,
built on top of React Aria's ListBox primitives. Items support icons,
descriptions, and single or multiple selection modes.
Includes Storybook stories and docs-ui documentation page.
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
Automatically apply `target="_blank"` and `rel="noopener noreferrer"` on
`Row` when `href` is an external link, so external links in default BUI
Table rows open in a new tab instead of navigating the current page.
`rel` tokens are merged rather than replaced, so consumer-provided tokens
(e.g. `rel="nofollow"`) are preserved while `noopener noreferrer` is
always enforced whenever `target="_blank"` is in effect.
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
- Changed Container default bottom spacing from padding-bottom to margin-bottom
- Prevented the Container margin-bottom from applying when used as the Header root element via mb="0"
- Renamed Header CSS classes from bui-HeaderPage* to bui-Header* to match the component name
Fixes https://linear.app/spotify/issue/BACUI-249
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
Replaces the invalid `min(auto, calc(100vh - 3rem))` CSS pattern with a
flex-based layout. The dialog now grows with its content by default and
scrolls when the content exceeds the viewport height. A fixed height can
still be applied via the `height` prop.
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
Use spread for conditional selection props. Add a dedicated loading
prop and data-loading data attribute to TableRoot, distinct from
stale. Both set aria-busy and default to the same opacity, but
consumers can now style them independently.
Signed-off-by: Jonathan Roebuck <jroebuck@spotify.com>
Removed per reviewer feedback — BUI Table relies on Storybook visual
tests rather than unit tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Jonathan Roebuck <jroebuck@spotify.com>
- Add @testing-library/jest-dom and @testing-library/react as explicit
devDependencies for @backstage/ui
- Disable selection on TableRoot during initial loading to prevent
interaction with skeleton rows
- Clarify test comment about aria-busy vs data-stale behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Jonathan Roebuck <jroebuck@spotify.com>
Add a skeleton loading state to the BUI Table component that shows the
table header with animated skeleton rows instead of plain "Loading..."
text. Includes accessibility support via aria-busy and live region
announcements.
BUCKS-2919
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Jonathan Roebuck <jroebuck@spotify.com>
Move the disabled rule after the pointer cursor rules so that
disabled rows always show cursor: not-allowed, even when they
have data-href or data-selection-mode.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
Wrap column header text in a label span with text-overflow: ellipsis
so that long headers truncate instead of wrapping to multiple lines.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
Row always passed onAction to React Aria even when no handler or
href was set, causing all rows to appear interactive. Now onAction
is only passed when there is an actual interaction. CSS explicitly
sets cursor: default on rows and scopes cursor: pointer to rows
with href, selection mode, or pressable state.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
* chore(deps): update dependency react-aria-components to v1.16.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* fix(ui): fix selectedKey type mismatch in Tabs component
Change `selectedId` initialization from `null` to `undefined` to
match React Aria's `selectedKey` prop type (`Key | undefined`).
Signed-off-by: Johan Persson <johanopersson@gmail.com>
---------
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Signed-off-by: Johan Persson <johanopersson@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Johan Persson <johanopersson@gmail.com>
* Deprecate HeaderPage in favor of Header in @backstage/ui
Rename the HeaderPage component to Header, keeping HeaderPage as a
deprecated alias for backwards compatibility. Also deprecate
HeaderPageProps, HeaderPageOwnProps, HeaderPageBreadcrumb, and
HeaderPageDefinition with new Header* equivalents. Update all internal
usages, stories, and docs-ui documentation to use the new names.
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
* Rename HeaderPage files and directories to Header
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
* Add changeset for mui-to-bui Header rename
Document the plugin release impact of switching the MUI to BUI theme converter page to the renamed Header component.
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
---------
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>