Skip to content

chore(ui): improve BreadcrumbItem#1756

Open
guoda-puidokaite wants to merge 4 commits into
mainfrom
guoda-breadcrumb-item
Open

chore(ui): improve BreadcrumbItem#1756
guoda-puidokaite wants to merge 4 commits into
mainfrom
guoda-breadcrumb-item

Conversation

@guoda-puidokaite

@guoda-puidokaite guoda-puidokaite commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Updated BreadcrumbItem with BREAKING CHANGES:

  • Removed Default href="#": This eliminated default navigation behavior, changing to without explicit href.
  • Structural Updates: Modified DOM structure by eliminating unnecessary wrappers.
  • Rendering Logic Enhanced: Introduced for onClick without href. Ensured usage for neither href nor onClick.
  • Accessibility Improvements: Added aria-current="page" for active and aria-disabled for disabled items.
  • Expanded onClick Type: Now supports both and elements for broader interaction handling.

Changes Made

See ACs to verify changes made.

Related Issues

Closes #1717

Checklist

  • I have performed a self-review of my code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have made corresponding changes to the documentation (if applicable).
  • My changes generate no new warnings or errors.
  • I have created a changeset for my changes.

PR Manifesto

Review the PR Manifesto for best practises.

Signed-off-by: I531348 <guoda.puidokaite@sap.com>
@guoda-puidokaite guoda-puidokaite self-assigned this Jun 15, 2026
@guoda-puidokaite guoda-puidokaite added the package All tasks related to package under packages/ label Jun 15, 2026
Copilot AI review requested due to automatic review settings June 15, 2026 07:13
@guoda-puidokaite guoda-puidokaite added the ui-components All tasks related to juno-ui-components library label Jun 15, 2026
@guoda-puidokaite guoda-puidokaite requested a review from a team as a code owner June 15, 2026 07:13
@changeset-bot

changeset-bot Bot commented Jun 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9ae51ac

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@cloudoperators/juno-ui-components Major
@cloudoperators/juno-app-carbon Patch
@cloudoperators/juno-app-doop Patch
@cloudoperators/juno-app-example Patch
@cloudoperators/juno-app-greenhouse Patch
@cloudoperators/juno-app-heureka Patch
@cloudoperators/juno-app-supernova Patch
@cloudoperators/juno-app-template Patch
@cloudoperators/juno-messages-provider Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@guoda-puidokaite guoda-puidokaite changed the title improve breadcrumb item chore(ui-components): improve breadcrumb item Jun 15, 2026
@guoda-puidokaite guoda-puidokaite changed the title chore(ui-components): improve breadcrumb item chore(ui-components): improve BreadcrumbItem Jun 15, 2026
@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

🚀 View preview at
https://cloudoperators.github.io/juno/pr-preview/pr-1756/

Built to branch gh-pages at 2026-06-15 08:07 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@guoda-puidokaite guoda-puidokaite changed the title chore(ui-components): improve BreadcrumbItem chore(ui): improve BreadcrumbItem Jun 15, 2026

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

This PR refactors BreadcrumbItem in @cloudoperators/juno-ui-components to render semantically correct elements (<a>, <button>, <span>) based on href, onClick, active, and disabled, and documents the breaking change via a major changeset.

Changes:

  • Update BreadcrumbItem rendering logic (remove default href="#", add button path for onClick-only, add aria-current/aria-disabled).
  • Update unit tests to reflect the new rendering semantics and accessibility attributes.
  • Update Storybook stories and add a major changeset entry for the breaking change.

Reviewed changes

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

File Description
packages/ui-components/src/components/BreadcrumbItem/BreadcrumbItem.component.tsx Implements the new semantic rendering matrix and ARIA attributes for BreadcrumbItem.
packages/ui-components/src/components/BreadcrumbItem/BreadcrumbItem.test.tsx Updates/extends tests to match new render paths and accessibility expectations.
packages/ui-components/src/components/BreadcrumbItem/BreadcrumbItem.stories.tsx Adjusts stories and adds explicit Link/Button examples for the new behaviors.
.changeset/chilly-trams-jam.md Declares a major bump and documents the breaking behavior/DOM changes.

Comment thread packages/ui-components/src/components/BreadcrumbItem/BreadcrumbItem.component.tsx Outdated
Comment thread packages/ui-components/src/components/BreadcrumbItem/BreadcrumbItem.component.tsx Outdated
Comment on lines 88 to +92
const onClickSpy = vi.fn()
render(<BreadcrumbItem href="#" disabled data-testid="breadcrumbitem" onClick={onClickSpy} />)
expect(screen.getByTestId("breadcrumbitem")).toBeInTheDocument()
expect(screen.getByTestId("breadcrumbitem")).toHaveClass("juno-breadcrumb-item-disabled")
const element = screen.getByTestId("breadcrumbitem")
expect(element).toBeInTheDocument()
expect(element).toHaveAttribute("aria-disabled", "true")

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.

I actually agree with this comment. We should assert that disabled items don't render as links to catch potential regressions, since the acceptance criteria states that in the disabled case we don't render a link at all.

Comment thread packages/ui-components/src/components/BreadcrumbItem/BreadcrumbItem.stories.tsx Outdated
Comment thread .changeset/chilly-trams-jam.md Outdated
Signed-off-by: I531348 <guoda.puidokaite@sap.com>
Signed-off-by: I531348 <guoda.puidokaite@sap.com>
/**
* The URL that the breadcrumb item points to for navigation.
* @default "#"
* @default undefined

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nice 👍

@edda edda 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.

Blocker comment to give me time to review

<a href={href} className={breadcrumbLinkBaseStyles} aria-label={ariaLabel || label} onClick={handleLinkClick}>
if (href) {
// Render the breadcrumb item as a link if href is provided and apply breadcrumbLinkBaseStyles
const linkClassName = `${combinedClassName} ${breadcrumbLinkBaseStyles}`

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.

For the button type we render only combinedClassName. We should do the same here. Looking at the contents of breadcrumbLinkBaseStyles they are essentially duplicates of styles already present in combineClassName. In order to ensure the link and button styles don't drift apart let's remove the breadcrumbLinkBaseStyles constant and the application of them here and use only combinedClassName

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Agree. Also, cobinedClassName sets jn:flex, which is then overwritten with jn:inline-flex when breadcrumbLinkBaseStyles is applied, so merging these two seems to clarify things here as well.

const imgElement = screen.getByRole("img")
expect(imgElement).toBeInTheDocument()
expect(imgElement).toHaveAttribute("alt", "help")
})

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.

Why did you remove this assertion? It's good to verify that the icon is rendered if we specifically use the icon prop for this test

Comment on lines 88 to +92
const onClickSpy = vi.fn()
render(<BreadcrumbItem href="#" disabled data-testid="breadcrumbitem" onClick={onClickSpy} />)
expect(screen.getByTestId("breadcrumbitem")).toBeInTheDocument()
expect(screen.getByTestId("breadcrumbitem")).toHaveClass("juno-breadcrumb-item-disabled")
const element = screen.getByTestId("breadcrumbitem")
expect(element).toBeInTheDocument()
expect(element).toHaveAttribute("aria-disabled", "true")

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.

I actually agree with this comment. We should assert that disabled items don't render as links to catch potential regressions, since the acceptance criteria states that in the disabled case we don't render a link at all.

@edda edda 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.

Additonally, if you look at storybook the Breadcrumb page makes no mention of the different options for breadcrumb items, so if you only look at the main story and never at the BreadcrumbItem stories, you 'll miss this completely. As we did for other components that have children, let's add at least one story to the Breadcrumb page which contains a bunch of different types of breadcrumb items and in its description refers to the BreadcrumbItems stories.

parameters: {
docs: {
description: {
story: "A breadcrumb item rendered as a <a> with provided href",

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.

Html tags in the story are actually rendered as html. So the text of the story when you look at it in storybook actually reads: "A breadcrumb item rendered as a using onClick for interaction." Replace the tag with html entities to make it render as intended.

parameters: {
docs: {
description: {
story: "A breadcrumb item rendered as a <button> using onClick for interaction.",

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.

Same as link story above, replace tag with html entities to make it render correctly.

<a href={href} className={breadcrumbLinkBaseStyles} aria-label={ariaLabel || label} onClick={handleLinkClick}>
if (href) {
// Render the breadcrumb item as a link if href is provided and apply breadcrumbLinkBaseStyles
const linkClassName = `${combinedClassName} ${breadcrumbLinkBaseStyles}`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Agree. Also, cobinedClassName sets jn:flex, which is then overwritten with jn:inline-flex when breadcrumbLinkBaseStyles is applied, so merging these two seems to clarify things here as well.

expect(screen.getByTestId("breadcrumbitem")).toBeInTheDocument()
expect(screen.getByTestId("breadcrumbitem")).toHaveClass("juno-breadcrumb-item-disabled")
const element = screen.getByTestId("breadcrumbitem")
expect(element).toBeInTheDocument()

@franzheidl franzheidl Jun 16, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

When assigning the element to a constas in the previous line, a matching element either does exist in the document, or the test will fail right away when attempting const assignment, so checking expect(element).toBeInTheDocument() is obsolete – line 92 can safely be removed.

expect(screen.queryByRole("link")).not.toBeInTheDocument()
expect(screen.getByTestId("breadcrumbitem")).toBeInTheDocument()
})

@franzheidl franzheidl Jun 16, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

According to the logic, the href branch fires first in case both href and onClick are passed (which is correct/as designed). We should explicitly test this behaviour by adding a new test ("renders a link if both href and onClick are passed") in order to catch any regressions, and as it is not totally obvious.
Also the onClick handler should be tested for this case explicitly.

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

Labels

package All tasks related to package under packages/ ui-components All tasks related to juno-ui-components library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Task](ui): refactor BreadcrumbItem to render semantically correct elements based on props

5 participants