19202 Commits

Author SHA1 Message Date
Johan Persson 1a8de99d1d Add test suite for useDefinition hook in @backstage/ui (#34042)
* Add test infrastructure to @backstage/ui

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* Add resolveResponsiveValue tests

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* Add useDefinition prop resolution and classes tests

Fixed a bug in useDefinition where passing null for classNameTarget or
utilityTarget was incorrectly defaulted to 'root' due to the nullish
coalescing operator treating null as falsy.

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* Add useDefinition data attributes tests

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* Add resolveDefinitionProps tests

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* Add processUtilityProps tests

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* Add useDefinition bg system tests

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* Add useDefinition utility style and analytics tests

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* Add useDefinition href resolution tests

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* Add useDefinition options tests

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* Wrap all useDefinition tests with BUIProvider

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* Fix type errors in useDefinition tests

Use `as const satisfies ComponentConfig<any, any>` instead of
`as ComponentConfig<any, any>` to preserve literal types needed
by the conditional type machinery (DataAttributes, ResolveBgProps,
analytics intersection).

Signed-off-by: Johan Persson <johanopersson@gmail.com>

* Address review feedback in useDefinition tests

- Rename shadowed Wrapper to RouterWrapper in createRouterWrapper
- Fix inaccurate comment about splat vs non-splat routes
- Clarify misleading test name for provider data-bg behavior

Signed-off-by: Johan Persson <johanopersson@gmail.com>

---------

Signed-off-by: Johan Persson <johanopersson@gmail.com>
2026-04-23 16:00:09 +02:00
Patrik Oldsberg 564697a8dd Merge pull request #34031 from backstage/rugvip/isolate-invalid-feature-flag-registration
frontend-app-api: isolate invalid feature flag registrations
2026-04-23 15:57:32 +02:00
Patrik Oldsberg 482cc5900a Address review feedback for feature flag isolation
Deduplicate the plugin/module feature flag registration loops and
distinguish the error source (Plugin vs Module). Treat
FEATURE_FLAG_INVALID as a warning in frontend-defaults.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
2026-04-23 14:47:52 +02:00
Marat Dyatko ab1cdbb9db Fix circular self-imports and add no-self-package-imports lint rule
- Fixes the `Cannot access '_AppRootElementBlueprintesm' before
  initialization` crash in `@backstage/frontend-plugin-api` caused by a
  self-referential import in the packaged ESM.
- Cleans up similar self-imports in `@backstage/catalog-model`,
  `@backstage/core-plugin-api`, `@backstage/plugin-catalog-node`,
  `@backstage/plugin-kubernetes-common`, and
  `@backstage/plugin-kubernetes-node`. Value imports switch to relative
  paths; type-only imports use `import type` so they're erased at
  runtime.
- Adds a new `@backstage/no-self-package-imports` ESLint rule. It reads
  each package's `exports` map, traverses the relative import graph from
  every entry's source file, and only reports imports where the current
  file is in the same bundle as the target entry (same-entry). Files
  that aren't reachable from any entry (tests, scripts, orphans) are
  skipped. `import type`, `package.json` imports, and cross-entry
  self-imports are allowed by default; cross-entry can be opted into
  with `allowCrossEntry: false`.

Signed-off-by: Marat Dyatko <maratd@spotify.com>
Made-with: Cursor
2026-04-23 14:43:01 +02:00
Fredrik Adelöw 427d5219a6 Merge pull request #34035 from backstage/freben/remove-uuid-dependency
Remove uuid dependency in favor of crypto.randomUUID()
2026-04-23 14:19:33 +02:00
Patrik Oldsberg 3009b62ad5 Merge pull request #34025 from etienne-napoleone/patch-2
fix(ui): disable card content scroll shadow on unsuported browsers
2026-04-23 12:53:24 +02:00
Fredrik Adelöw e9b78e9698 Remove uuid dependency in favor of crypto.randomUUID()
The uuid package dropped its CommonJS entry point in v14, making it
incompatible with Backstage's CJS build output and Jest test runner.
Rather than working around the ESM-only issue, replace all usage with
the built-in crypto.randomUUID() which has been available in Node.js
since v16.7 and in all major browsers since March 2022.

Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Made-with: Cursor
2026-04-23 10:51:43 +02:00
Charles de Dreuille 5a7495d072 chore: merge master and resolve package.json conflict
Both @braintree/sanitize-url (this branch) and @internationalized/date
(master) were added to packages/ui dependencies; keeping both.

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-04-23 08:53:44 +01:00
Charles de Dreuille 251acf38d6 fix(ui): address further PR review comments
- Replace custom UNSAFE_HREF_RE with @braintree/sanitize-url for robust XSS prevention
- Shorten renderInlineMarkdown JSDoc
- Single-user with href: collapse two adjacent links into one wrapping avatar + name
- Multi-user list: use href ?? index:name as key to avoid collisions on duplicate names
- Status dot: replace role="img"/aria-label with aria-hidden (text label is sufficient)

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-04-23 08:52:08 +01:00
Deepthi Ajith 23ee7899b0 fix(ui): add invalid state styling to Checkbox component
Signed-off-by: Deepthi Ajith <deepthi.ajith@infosys.com>
2026-04-23 09:37:35 +02:00
Patrik Oldsberg b6ca666812 frontend-app-api: isolate invalid feature flag registrations
Wrap each feature flag registration in a try/catch so that a single
invalid flag name (e.g. containing a slash) is reported through the
error collector instead of crashing the entire app at bootstrap.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
2026-04-23 00:38:05 +02:00
Charles de Dreuille 6407493de8 Update report.api.md
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-04-22 15:25:41 +01:00
Charles de Dreuille a69c2e29a2 fix(ui): improve semantic markup and a11y across Header components
- Tags: replace div+Fragment with ul/li; move circle divider to CSS ::before pseudo-element, removing it from the DOM
- Metadata: replace div wrapper with dl/dt/dd for proper key-value semantics; reset dl and dd browser margins
- HeaderMetadataUsers: render multi-user stack as ul/li; simplify single-user branch into one ternary with a fragment
- HeaderMetadataStatus: add role="img" and aria-label to the status dot so screen readers announce its meaning
- Restore Fragment import from react

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-04-22 15:02:28 +01:00
Patrik Oldsberg 1b55701923 Merge pull request #34006 from backstage/rugvip/fix-lockfile-removal-detection
Fix lockfile dependency removal detection in PackageGraph
2026-04-22 15:36:14 +02:00
Fredrik Adelöw db3e59cc70 Merge pull request #34004 from backstage/bui-fix-external-links
fix(ui): preserve external hrefs in BUI link components
2026-04-22 14:28:36 +02:00
Etienne Napoleone a0ea7b3152 fix(ui): disable card content scroll shadow on unsuported browsers
bugged in firefox https://caniuse.com/mdn-css_properties_animation-timeline_scroll

Signed-off-by: Etienne Napoleone <etienne.napoleone@gmail.com>
2026-04-22 14:10:02 +02:00
Charles de Dreuille ec109ce7fb fix(ui): replace custom regex with marked Lexer for inline description parsing
Uses marked's Lexer.lexInline() instead of a hand-rolled regex to parse
inline links in the Header description. marked ships CommonJS, has zero
dependencies, and is already used in the monorepo. This gives us a proper
token model that handles edge cases the regex could not.

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-04-22 12:41:26 +01:00
1337 e907ba6fe1 Merge branch 'backstage:master' into feature/catalog-export 2026-04-22 09:17:13 +02:00
github-actions[bot] 1cc86bee1c Version Packages (next) 2026-04-21 15:07:43 +00:00
Charles de Dreuille 50a66a9183 Merge pull request #33909 from backstage/cursor/add-date-range-picker
feat(ui): add DateRangePicker component
2026-04-21 15:54:38 +01:00
Charles de Dreuille ea11646d8d fix(ui): address second round of PR review comments
- Replace hardcoded gap: 20px with var(--bui-space-5) in metadata row
- Trim leading whitespace from href before unsafe-scheme check to prevent bypass

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-04-21 09:34:34 +01:00
Charles de Dreuille 4dfd2f1284 revert(ui): restore Container wrapper on Header
Reverting the full-width change as it is too disruptive at this time
and will be handled in a separate PR.

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-04-21 08:55:53 +01:00
Charles de Dreuille a2e0636c1f fix(ui): address PR review comments on Header components
- Guard against unsafe URL schemes (javascript:/vbscript:/data:) in description links
- Use index-based keys for tags and metadata to avoid duplicate-key warnings
- Render ReactNode metadata values directly instead of wrapping in Text to avoid invalid span>div nesting; only wrap plain strings in Text
- Replace empty-string Avatar src fallback with data:, to prevent spurious page requests
- Fix JSDoc in HeaderMetadataUsers to accurately describe the row layout

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-04-20 21:22:20 +01:00
1337 7348cc16fb Merge branch 'backstage:master' into feature/catalog-export 2026-04-20 20:14:38 +02:00
Patrik Oldsberg 94e941867b Merge pull request #34011 from backstage/rugvip/remove-unused-integration-exports
integration: remove unnecessary exports from internal helpers
2026-04-20 19:59:42 +02:00
Charles de Dreuille c48da5c93d Merge branch 'master' into charlesdedreuille/act-355-header-improvements 2026-04-20 18:07:12 +01:00
Charles de Dreuille 2deaa49120 fix(ui): replace react-markdown with inline parser to fix ESM Jest failures
react-markdown v8+ is ESM-only and breaks Jest in Node-role packages that
transitively import @backstage/ui via core-app-api. Since the Header
description only needs inline link support, a small regex-based parser
is sufficient and avoids the ESM dependency entirely.

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-04-20 17:59:45 +01:00
Johan Persson e8a1a35714 feat(ui): add isPending prop and deprecate loading
Add `isPending` prop to Alert, Button, ButtonIcon, Table, and
TableRoot, aligning with React Aria naming conventions. The
`loading` prop is deprecated but remains functional as an alias.

CSS selectors now target `data-ispending` instead of `data-loading`
for pending state styling. The `data-loading` attribute is still
emitted for backward compatibility.

Internal Table hooks (`PaginationResult`, `UsePageCacheResult`)
renamed `loading` to `isPending`. The `useTable` hook returns both
`isPending` and `loading` on `tableProps` to preserve backward
compatibility.

Updated docs-ui documentation and stories accordingly.

Signed-off-by: Johan Persson <johanopersson@gmail.com>
2026-04-20 17:36:21 +02:00
Johan Persson df705bbdbf fix(ui): preserve external hrefs in BUI link components
When the app was served under a non-root base path, BUI link
components rewrote absolute `href` values as in-app paths — e.g.
`https://example.com` became `/basename/https:/example.com` —
because every href was passed through react-router's `useHref`,
which treats all strings as relative paths.

External URLs (`http://`, `https://`, `//`, `mailto:`, `tel:`)
now bypass href resolution. Internal hrefs are normalized to
their canonical pre-basename form in `useDefinition`, so
downstream resolution by react-router's `useHref` (for
rendering) and `navigate` (for click-navigation) adds the
basename exactly once.

Signed-off-by: Johan Persson <johanopersson@gmail.com>
2026-04-20 17:28:04 +02:00
Patrik Oldsberg 191b41a0b3 integration: remove unnecessary exports from internal helpers
Remove `export` from five internal helper functions that are not part of
the published API and are only used within their own files. The `sleep`
function in GitLabIntegration is kept exported for test access but marked
as `@internal`.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
2026-04-20 16:49:21 +02:00
Patrik Oldsberg 357d63949e Fix lockfile dependency removal detection in PackageGraph
The `otherGraph` variable in `listChangedPackages` was incorrectly
created from `thisLockfile` instead of `otherLockfile`, making the
merged dependency graph a duplicate of the current one. This meant
that dependencies only present in the old lockfile were never added
to the graph, so transitive removals could not be detected.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
2026-04-20 16:37:26 +02:00
MT Lewis 7192e84cad Merge pull request #33948 from backstage/flex-item-props
ui: add support for flex-item props to generic layout components
2026-04-20 09:54:14 +01:00
Charles de Dreuille 4e932ef781 chore(ui): regenerate API report
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
2026-04-19 19:51:53 +01:00
Charles de Dreuille 6310790556 feat(ui): add href support to HeaderMetadataUsers, remove bold from description, full-width header
- Add `href` to `HeaderMetadataUser`: avatar becomes a link and name renders as a primary `Link` with `standalone` (no underline at rest)
- Add `WithMetadataUsersNoLinks` story alongside `WithMetadataUsers` to cover both link and non-link variants
- Remove `strong`/`em` from ReactMarkdown `allowedElements` in Header description — only plain text and inline links are now supported
- Replace `<Container>` root with a plain `<div>` so the Header spans its full parent width
- Add `HeaderMetadataStatus` component and CSS (new files)
- Update docs: fixtures, snippets, props-definition, and page.mdx to reflect all changes

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
2026-04-19 11:41:55 +02:00
Charles de Dreuille 0e8edce069 feat(ui): add HeaderMetadataUsers component and polish Header metadata/tags
- Add HeaderMetadataUsers component: single user shows avatar + name,
  multiple users show avatar stack with tooltip on hover
- Use Pressable from react-aria for tooltip trigger compatibility
- Switch tags and metadata text to body-medium variant
- Fix metadata item styling: secondary color label, no bold, no colon,
  flex row with gap-2 between label and value
- Update Header gap to space-3
- Update docs with HeaderMetadataUsers example, correct prop types,
  and synced default snippet

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
2026-04-19 10:48:09 +02:00
Charles de Dreuille fcc8c4d328 fix(ui): set Header gap to space-3
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
2026-04-19 10:05:43 +02:00
Charles de Dreuille 6ba1167cb0 fix(ui): increase Header gap from space-1 to space-4
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
2026-04-19 10:05:31 +02:00
Charles de Dreuille c96e2b3445 feat(ui): add description, tags, and metadata props to Header
Add three new props to the Header component:
- `tags`: renders a row of text/link items above the title, separated by
  3×3px circle dividers
- `description`: renders a markdown string below the title with inline
  link support via react-markdown
- `metadata`: renders key-value pairs below the description with 20px gaps

Also deprecates the `breadcrumbs` prop for future removal.

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
2026-04-19 09:48:56 +02:00
Charles de Dreuille 3e9f20340a fix(ui): improve DateRangePicker accessibility and fix docs link
- Add aria-label="Open calendar" and aria-hidden on icon for the calendar trigger button
- Add aria-hidden on nav button icons (React Aria provides built-in labels for prev/next slots)
- Update React Aria docs link to react-aria.adobe.com format

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
2026-04-19 07:36:48 +02:00
Charles de Dreuille d676565abb fix(ui): refine DateRangePicker calendar button styling
- Fixed size: 1.5rem (small), 2rem (medium) in rem units
- No padding, flex-centered icon
- Inset focus ring using 1px box-shadow with --bui-ring token
- Border radius bumped to radius-2
- Medium size padding-inline-end reduced to space-1

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
2026-04-19 06:59:06 +02:00
Charles de Dreuille f221068ab9 fix(ui): use BUI Popover and add unavailable dates story in DateRangePicker
- Switch DateRangePicker to use the BUI Popover component (with hideArrow)
  instead of the raw react-aria-components Popover
- Add WithUnavailableDates story showing isDateUnavailable with weekends
  blocked and non-contiguous ranges disallowed (default behaviour)

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
2026-04-19 06:38:47 +02:00
Charles de Dreuille ff68a57b14 Update report.api.md
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-04-19 06:38:21 +02:00
Charles de Dreuille 20a5de0605 Fix styles
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-04-19 06:38:21 +02:00
Charles de Dreuille 401916d55b feat(ui): add DateRangePicker component
Adds a new DateRangePicker component to @backstage/ui built on React Aria,
featuring a custom field group with two DateInput fields and a calendar
trigger, a RangeCalendar popover with proper range selection visuals
(solid circles for start/end, transparent solid fill for in-range cells
with row-boundary rounding), and full BUI token usage including bg
consumer auto-increment. Includes Storybook stories and a docs-ui page.

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
2026-04-19 06:38:19 +02:00
Fredrik Adelöw 89d324840c fix(scheduler): handle setTimeout overflow for long sleep durations
Node.js setTimeout uses a 32-bit signed integer for the delay, so
values larger than 2^31-1 ms (~24.8 days) cause the callback to fire
immediately. Fix by chunking the wait into segments of at most 2^30 ms.

Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Made-with: Cursor
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Made-with: Cursor
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Made-with: Cursor
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Made-with: Cursor
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Made-with: Cursor
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Made-with: Cursor
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Made-with: Cursor
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Made-with: Cursor
2026-04-18 15:30:07 +02:00
the-serious-programmer 82cf16f63c feat: catalog export feature
Signed-off-by: the-serious-programmer <19777147+the-serious-programmer@users.noreply.github.com>
2026-04-18 14:29:30 +02:00
MT Lewis 0912de3a25 ui: transform number to px in basis utility prop
Signed-off-by: MT Lewis <mtlewis@users.noreply.github.com>
2026-04-17 17:52:23 +01:00
MT Lewis 68d1725113 ui: style overrides take higher precedence than utility styles in Box
Signed-off-by: MT Lewis <mtlewis@users.noreply.github.com>
2026-04-17 17:47:46 +01:00
MT Lewis ea220e9a9d ui: document FlexItemProps
Signed-off-by: MT Lewis <mtlewis@users.noreply.github.com>
2026-04-17 17:47:29 +01:00
Johan Persson c6fc76f532 fix(ui): prevent hidden Tabs tree from stomping active indicator opacity
React Aria's `CollectionBuilder` renders `TabList`'s children into both a
hidden collection-building tree and the real DOM. The hidden instance of
`TabsIndicators` sits outside the `TabListStateContext` provider, so its
`state` is `null` — causing its `updateCSSVariables` effect to hit the
`else` branch and write `--active-tab-opacity: 0` to the `tabsRef` DOM
element that the real instance also writes to. Under the right render
ordering, this hidden write lands after the real instance's `opacity: 1`
and makes the active indicator disappear on uncontrolled Tabs.

Guard `updateCSSVariables` with an early return when `state == null` so
the hidden instance never writes to the shared DOM element.

Signed-off-by: Johan Persson <johanopersson@gmail.com>
2026-04-17 14:18:22 +02:00