diff --git a/__snapshots__/breadcrumb/component/chromium/DBBreadcrumb-should-match-screenshot.png b/__snapshots__/breadcrumb/component/chromium/DBBreadcrumb-should-match-screenshot.png new file mode 100644 index 000000000000..2c60b3ea190c Binary files /dev/null and b/__snapshots__/breadcrumb/component/chromium/DBBreadcrumb-should-match-screenshot.png differ diff --git a/__snapshots__/breadcrumb/component/firefox/DBBreadcrumb-should-match-screenshot.png b/__snapshots__/breadcrumb/component/firefox/DBBreadcrumb-should-match-screenshot.png new file mode 100644 index 000000000000..3937624bdef7 Binary files /dev/null and b/__snapshots__/breadcrumb/component/firefox/DBBreadcrumb-should-match-screenshot.png differ diff --git a/__snapshots__/breadcrumb/component/mobile-chrome/DBBreadcrumb-should-match-screenshot.png b/__snapshots__/breadcrumb/component/mobile-chrome/DBBreadcrumb-should-match-screenshot.png new file mode 100644 index 000000000000..93da699e6d39 Binary files /dev/null and b/__snapshots__/breadcrumb/component/mobile-chrome/DBBreadcrumb-should-match-screenshot.png differ diff --git a/__snapshots__/breadcrumb/showcase/chromium-highContrast/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png b/__snapshots__/breadcrumb/showcase/chromium-highContrast/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png new file mode 100644 index 000000000000..e187caac9287 Binary files /dev/null and b/__snapshots__/breadcrumb/showcase/chromium-highContrast/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png differ diff --git a/__snapshots__/breadcrumb/showcase/chromium-highContrast/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml b/__snapshots__/breadcrumb/showcase/chromium-highContrast/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml new file mode 100644 index 000000000000..a2d4bc2b5f8d --- /dev/null +++ b/__snapshots__/breadcrumb/showcase/chromium-highContrast/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml @@ -0,0 +1,36 @@ +- main: + - heading "DBBreadcrumb" [level=1] + - link "Size" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - link "Separator" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: / + - link "Category" + - listitem: / Current Page \ No newline at end of file diff --git a/__snapshots__/breadcrumb/showcase/chromium/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png b/__snapshots__/breadcrumb/showcase/chromium/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png new file mode 100644 index 000000000000..9ecdc28bda69 Binary files /dev/null and b/__snapshots__/breadcrumb/showcase/chromium/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png differ diff --git a/__snapshots__/breadcrumb/showcase/chromium/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml b/__snapshots__/breadcrumb/showcase/chromium/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml new file mode 100644 index 000000000000..a2d4bc2b5f8d --- /dev/null +++ b/__snapshots__/breadcrumb/showcase/chromium/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml @@ -0,0 +1,36 @@ +- main: + - heading "DBBreadcrumb" [level=1] + - link "Size" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - link "Separator" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: / + - link "Category" + - listitem: / Current Page \ No newline at end of file diff --git a/__snapshots__/breadcrumb/showcase/firefox/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png b/__snapshots__/breadcrumb/showcase/firefox/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png new file mode 100644 index 000000000000..2752028de60b Binary files /dev/null and b/__snapshots__/breadcrumb/showcase/firefox/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png differ diff --git a/__snapshots__/breadcrumb/showcase/firefox/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml b/__snapshots__/breadcrumb/showcase/firefox/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml new file mode 100644 index 000000000000..a2d4bc2b5f8d --- /dev/null +++ b/__snapshots__/breadcrumb/showcase/firefox/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml @@ -0,0 +1,36 @@ +- main: + - heading "DBBreadcrumb" [level=1] + - link "Size" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - link "Separator" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: / + - link "Category" + - listitem: / Current Page \ No newline at end of file diff --git a/__snapshots__/breadcrumb/showcase/mobile-chrome/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png b/__snapshots__/breadcrumb/showcase/mobile-chrome/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png new file mode 100644 index 000000000000..0bf760c85c38 Binary files /dev/null and b/__snapshots__/breadcrumb/showcase/mobile-chrome/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png differ diff --git a/__snapshots__/breadcrumb/showcase/mobile-chrome/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml b/__snapshots__/breadcrumb/showcase/mobile-chrome/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml new file mode 100644 index 000000000000..a2d4bc2b5f8d --- /dev/null +++ b/__snapshots__/breadcrumb/showcase/mobile-chrome/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml @@ -0,0 +1,36 @@ +- main: + - heading "DBBreadcrumb" [level=1] + - link "Size" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - link "Separator" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: / + - link "Category" + - listitem: / Current Page \ No newline at end of file diff --git a/__snapshots__/breadcrumb/showcase/mobile-safari/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png b/__snapshots__/breadcrumb/showcase/mobile-safari/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png new file mode 100644 index 000000000000..5b65205f0511 Binary files /dev/null and b/__snapshots__/breadcrumb/showcase/mobile-safari/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png differ diff --git a/__snapshots__/breadcrumb/showcase/mobile-safari/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml b/__snapshots__/breadcrumb/showcase/mobile-safari/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml new file mode 100644 index 000000000000..a2d4bc2b5f8d --- /dev/null +++ b/__snapshots__/breadcrumb/showcase/mobile-safari/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml @@ -0,0 +1,36 @@ +- main: + - heading "DBBreadcrumb" [level=1] + - link "Size" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - link "Separator" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: / + - link "Category" + - listitem: / Current Page \ No newline at end of file diff --git a/__snapshots__/breadcrumb/showcase/webkit/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png b/__snapshots__/breadcrumb/showcase/webkit/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png new file mode 100644 index 000000000000..58e95462e28a Binary files /dev/null and b/__snapshots__/breadcrumb/showcase/webkit/DBBreadcrumb-should-match-screenshot-1/DBBreadcrumb-should-match-screenshot.png differ diff --git a/__snapshots__/breadcrumb/showcase/webkit/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml b/__snapshots__/breadcrumb/showcase/webkit/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml new file mode 100644 index 000000000000..a2d4bc2b5f8d --- /dev/null +++ b/__snapshots__/breadcrumb/showcase/webkit/should-have-same-aria-snapshot/DBBreadcrumb-should-have-same-aria-snapshot.yaml @@ -0,0 +1,36 @@ +- main: + - heading "DBBreadcrumb" [level=1] + - link "Size" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - link "Separator" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: › + - link "Category" + - listitem: › Current Page + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" + - listitem: + - text: / + - link "Category" + - listitem: / Current Page \ No newline at end of file diff --git a/packages/components/scripts/post-build/components.ts b/packages/components/scripts/post-build/components.ts index ae1dea9fe8ec..077712d30033 100644 --- a/packages/components/scripts/post-build/components.ts +++ b/packages/components/scripts/post-build/components.ts @@ -355,6 +355,34 @@ export const getComponents = (): Component[] => [ { name: 'brand' }, + { + name: 'breadcrumb', + overwrites: { + stencil: [{ from: 'import { DBIcon } from "../icon";\n', to: '' }], + angular: [ + // Fix TypeScript strict null checks by adding non-null assertions + // Within the conditional block where items() is checked, subsequent accesses need ! + { from: /items\(\)\.length/g, to: 'items()!.length' }, + { from: /items\(\)\[0\]/g, to: 'items()![0]' }, + { from: /items\(\)\.slice/g, to: 'items()!.slice' }, + { from: /maxItems\(\) >/g, to: 'maxItems()! >' }, + { from: /> maxItems\(\)/g, to: '> maxItems()!' }, + { from: /maxItems\(\) - 1/g, to: 'maxItems()! - 1' } + ] + } + }, + { + name: 'breadcrumb-item', + overwrites: { + stencil: [ + { from: 'import { DBIcon } from "../icon";\n', to: '' }, + { + from: '{this.text || this.children}', + to: '{this.text ? this.text : }' + } + ] + } + }, { name: 'input', overwrites: { diff --git a/packages/components/src/components/breadcrumb-item/agent/breadcrumb-item.agent.lite.tsx b/packages/components/src/components/breadcrumb-item/agent/breadcrumb-item.agent.lite.tsx new file mode 100644 index 000000000000..3a5bd3cc96ae --- /dev/null +++ b/packages/components/src/components/breadcrumb-item/agent/breadcrumb-item.agent.lite.tsx @@ -0,0 +1,71 @@ +import { DBBreadcrumb } from '../../breadcrumb/index'; +import { DBBreadcrumbItem } from '../index'; + +export default function BreadcrumbItem() { + return ( + <> +

DBBreadcrumbItem Documentation Examples

+ +

1. Basic Breadcrumb with Items

+ + Home + Category + + Current Page + + + +

2. With Text Prop

+ + + + + + +

3. Disabled Item

+ + Home + Disabled Item + Current + + +

4. Different Sizes

+ + Home + Category + + Current (Small) + + + + + Home + Category + + Current (Medium) + + + +

5. Different Separators

+ + Home + Category + Chevron + + + + Home + Category + Slash + + +

6. Custom Class

+ + + Home with Custom Class + + Current + + + ); +} diff --git a/packages/components/src/components/breadcrumb-item/breadcrumb-item.lite.tsx b/packages/components/src/components/breadcrumb-item/breadcrumb-item.lite.tsx new file mode 100644 index 000000000000..c5bcc2857cdb --- /dev/null +++ b/packages/components/src/components/breadcrumb-item/breadcrumb-item.lite.tsx @@ -0,0 +1,28 @@ +import { useDefaultProps, useMetadata, useRef } from '@builder.io/mitosis'; +import { cls } from '../../utils'; +import { DBIcon } from '../icon'; +import type { DBBreadcrumbItemProps } from './model'; + +useMetadata({}); + +useDefaultProps({}); + +export default function DBBreadcrumbItem(props: DBBreadcrumbItemProps) { + const _ref = useRef(null); + + return ( +
  • + {props.href && !props.disabled ? ( + + {props.icon && } + {props.text ? props.text : props.children} + + ) : ( + + {props.icon && } + {props.text ? props.text : props.children} + + )} +
  • + ); +} diff --git a/packages/components/src/components/breadcrumb-item/breadcrumb-item.scss b/packages/components/src/components/breadcrumb-item/breadcrumb-item.scss new file mode 100644 index 000000000000..af7770743be3 --- /dev/null +++ b/packages/components/src/components/breadcrumb-item/breadcrumb-item.scss @@ -0,0 +1,4 @@ +@charset "utf-8"; + +// Breadcrumb item styling is handled by the parent breadcrumb component +// This file exists to maintain the component structure diff --git a/packages/components/src/components/breadcrumb-item/breadcrumb-item.spec.tsx b/packages/components/src/components/breadcrumb-item/breadcrumb-item.spec.tsx new file mode 100644 index 000000000000..0695583194ea --- /dev/null +++ b/packages/components/src/components/breadcrumb-item/breadcrumb-item.spec.tsx @@ -0,0 +1,45 @@ +import AxeBuilder from '@axe-core/playwright'; +import { expect, test } from '@playwright/experimental-ct-react'; + +import { DBBreadcrumb } from '../breadcrumb/index'; +import { DBBreadcrumbItem } from './index'; +// @ts-ignore - vue can only find it with .ts as file ending +import { DEFAULT_VIEWPORT } from '../../shared/constants.ts'; + +const comp: any = ( + + Home + Category + Current Page + +); + +const testComponent = () => { + test('should contain text', async ({ mount }) => { + const component = await mount(comp); + await expect(component).toContainText('Home'); + await expect(component).toContainText('Current Page'); + }); + + test('should match screenshot', async ({ mount }) => { + const component = await mount(comp); + await expect(component).toHaveScreenshot(); + }); + + test('should not have any accessibility issues', async ({ + mount, + page + }) => { + await mount(comp); + const accessibilityScanResults = await new AxeBuilder({ + page + }).analyze(); + + expect(accessibilityScanResults.violations).toEqual([]); + }); +}; + +test.describe('DBBreadcrumbItem', () => { + test.use({ viewport: DEFAULT_VIEWPORT }); + testComponent(); +}); diff --git a/packages/components/src/components/breadcrumb-item/docs/Angular.md b/packages/components/src/components/breadcrumb-item/docs/Angular.md new file mode 100644 index 000000000000..ff419488e1de --- /dev/null +++ b/packages/components/src/components/breadcrumb-item/docs/Angular.md @@ -0,0 +1,50 @@ +## Angular + +For general installation and configuration take a look at the [ngx-core-components](https://www.npmjs.com/package/@db-ux/ngx-core-components) package. + +### Use component + +```ts app.component.ts +// app.component.ts +import { Component } from "@angular/core"; +import { DBBreadcrumb, DBBreadcrumbItem } from "@db-ux/ngx-core-components"; + +@Component({ + selector: "app-root", + imports: [DBBreadcrumb, DBBreadcrumbItem], + template: ` + + Home + Category + Current Page + + `, + standalone: true +}) +export class App {} +``` + +### With text prop + +```html + + + + + +``` + +### Disabled item + +```html + + Home + Disabled Item + Current Page + +``` diff --git a/packages/components/src/components/breadcrumb-item/docs/HTML.md b/packages/components/src/components/breadcrumb-item/docs/HTML.md new file mode 100644 index 000000000000..cf404a6abe2d --- /dev/null +++ b/packages/components/src/components/breadcrumb-item/docs/HTML.md @@ -0,0 +1,51 @@ +## HTML + +For general installation and configuration take a look at the [wc-core-components](https://www.npmjs.com/package/@db-ux/wc-core-components) package. + +### Use component + +```html + + + + + DBBreadcrumbItem + + + + + Home + Category + Current Page + + + +``` + +### With text attribute + +```html + + + + + +``` + +### Disabled item + +```html + + Home + Disabled Item + Current Page + +``` diff --git a/packages/components/src/components/breadcrumb-item/docs/Migration.md b/packages/components/src/components/breadcrumb-item/docs/Migration.md new file mode 100644 index 000000000000..fc97c62beb3a --- /dev/null +++ b/packages/components/src/components/breadcrumb-item/docs/Migration.md @@ -0,0 +1,3 @@ +## Migration + +This is a new component in v3. There is no migration path from v2. diff --git a/packages/components/src/components/breadcrumb-item/docs/React.md b/packages/components/src/components/breadcrumb-item/docs/React.md new file mode 100644 index 000000000000..ede525d91408 --- /dev/null +++ b/packages/components/src/components/breadcrumb-item/docs/React.md @@ -0,0 +1,40 @@ +## React + +For general installation and configuration take a look at the [react-core-components](https://www.npmjs.com/package/@db-ux/react-core-components) package. + +### Use component + +```tsx App.tsx +// App.tsx +import { DBBreadcrumb, DBBreadcrumbItem } from "@db-ux/react-core-components"; + +const App = () => ( + + Home + Category + Current Page + +); + +export default App; +``` + +### With text prop + +```tsx + + + + + +``` + +### Disabled item + +```tsx + + Home + Disabled Item + Current Page + +``` diff --git a/packages/components/src/components/breadcrumb-item/docs/Vue.md b/packages/components/src/components/breadcrumb-item/docs/Vue.md new file mode 100644 index 000000000000..c202792ebee2 --- /dev/null +++ b/packages/components/src/components/breadcrumb-item/docs/Vue.md @@ -0,0 +1,43 @@ +## Vue + +For general installation and configuration take a look at the [v-core-components](https://www.npmjs.com/package/@db-ux/v-core-components) package. + +### Use component + +```vue App.vue + + + +``` + +### With text prop + +```vue + +``` + +### Disabled item + +```vue + +``` diff --git a/packages/components/src/components/breadcrumb-item/index.ts b/packages/components/src/components/breadcrumb-item/index.ts new file mode 100644 index 000000000000..df523908cb6b --- /dev/null +++ b/packages/components/src/components/breadcrumb-item/index.ts @@ -0,0 +1,2 @@ +export { default as DBBreadcrumbItem } from './breadcrumb-item'; +export * from './model'; diff --git a/packages/components/src/components/breadcrumb-item/model.ts b/packages/components/src/components/breadcrumb-item/model.ts new file mode 100644 index 000000000000..c8d45b9798c5 --- /dev/null +++ b/packages/components/src/components/breadcrumb-item/model.ts @@ -0,0 +1,39 @@ +import { GlobalProps } from '../../shared/model'; + +export type AriaCurrent = + | 'page' + | 'step' + | 'location' + | 'date' + | 'time' + | 'true' + | 'false'; + +export type DBBreadcrumbItemDefaultProps = { + /** + * The URL the breadcrumb item links to + */ + href?: string; + + /** + * The text content of the breadcrumb item + */ + text?: string; + + /** + * Icon name from DB UX icon library + */ + icon?: string; + + /** + * Indicates the current page in the breadcrumb + */ + ariaCurrent?: AriaCurrent; + + /** + * Whether this item is disabled (renders as span instead of link) + */ + disabled?: boolean; +}; + +export type DBBreadcrumbItemProps = DBBreadcrumbItemDefaultProps & GlobalProps; diff --git a/packages/components/src/components/breadcrumb/agent/breadcrumb.agent.lite.tsx b/packages/components/src/components/breadcrumb/agent/breadcrumb.agent.lite.tsx new file mode 100644 index 000000000000..5daac5173da9 --- /dev/null +++ b/packages/components/src/components/breadcrumb/agent/breadcrumb.agent.lite.tsx @@ -0,0 +1,42 @@ +import { DBBreadcrumb } from '../index'; + +export default function Breadcrumb() { + return ( + <> +

    DBBreadcrumb Documentation Examples

    + +

    1. Default Breadcrumb

    + +
  • + Home +
  • +
  • + Category +
  • +
  • Current Page
  • +
    + +

    2. Long Breadcrumb Path

    + +
  • + Home +
  • +
  • + Category +
  • +
  • + Subcategory +
  • +
  • + Product Group +
  • +
  • Current Product
  • +
    + +

    3. Single Item

    + +
  • Current Page
  • +
    + + ); +} diff --git a/packages/components/src/components/breadcrumb/breadcrumb.lite.tsx b/packages/components/src/components/breadcrumb/breadcrumb.lite.tsx new file mode 100644 index 000000000000..3591e84418df --- /dev/null +++ b/packages/components/src/components/breadcrumb/breadcrumb.lite.tsx @@ -0,0 +1,189 @@ +import { + useDefaultProps, + useMetadata, + useRef, + useStore +} from '@builder.io/mitosis'; +import { cls } from '../../utils'; +import { DBIcon } from '../icon'; +import type { DBBreadcrumbProps, DBBreadcrumbState } from './model'; +import DBTooltip from '../tooltip/tooltip.lite'; +import DBPopover from '../popover/popover.lite'; + +useMetadata({}); + +useDefaultProps({ + size: 'small', + separator: 'chevron', + maxItems: undefined, + collapsedMenu: false, + items: undefined, + ellipsisAriaLabel: 'Show all breadcrumb items' +}); + +export default function DBBreadcrumb(props: DBBreadcrumbProps) { + const _ref = useRef(null); + + const state = useStore({ + isExpanded: false, + toggleExpanded() { + state.isExpanded = !state.isExpanded; + } + }); + + return ( + + ); +} diff --git a/packages/components/src/components/breadcrumb/breadcrumb.scss b/packages/components/src/components/breadcrumb/breadcrumb.scss new file mode 100644 index 000000000000..1647ebcb3894 --- /dev/null +++ b/packages/components/src/components/breadcrumb/breadcrumb.scss @@ -0,0 +1,181 @@ +@charset "utf-8"; +@use "@db-ux/core-foundations/build/styles/fonts"; +@use "@db-ux/core-foundations/build/styles/variables"; +@use "@db-ux/core-foundations/build/styles/colors"; +@use "@db-ux/core-foundations/build/styles/helpers"; + +.db-breadcrumb { + // Default: Small size + .db-breadcrumb-list { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: variables.$db-spacing-fixed-3xs; // 2px gap for small + list-style: none; + margin: 0; + padding: 0; + } + + // Breadcrumb items (li elements) + li { + display: flex; + align-items: center; + gap: variables.$db-spacing-fixed-3xs; // 2px between separator and item (small) + } + + // Link and text styling (small) + a, + span { + display: flex; + align-items: center; + gap: variables.$db-spacing-fixed-2xs; // 4px gap between icon and text + padding: variables.$db-spacing-fixed-3xs variables.$db-spacing-fixed-2xs; // 2px 4px (small) + border-radius: variables.$db-border-radius-xs; // 4px + background-color: colors.$db-adaptive-bg-basic-transparent-full-default; + color: colors.$db-adaptive-on-bg-basic-emphasis-100-default; + font-size: variables.$db-sizing-sm; // 14px (body-sm) + line-height: variables.$db-sizing-md; // 20px + font-family: var(--db-font-family-sans); + font-weight: 400; + text-decoration: none; + white-space: nowrap; + transition: background-color 0.2s ease; + + // Current page (last item with aria-current="page") + &[aria-current="page"] { + font-weight: 700; // Bold for current item + cursor: default; + pointer-events: none; + + &:hover, + &:focus { + background-color: colors.$db-adaptive-bg-basic-transparent-full-default; + text-decoration: none; + } + } + + &:hover, + &:focus { + background-color: colors.$db-adaptive-bg-basic-transparent-semi-default; + } + + &:active { + background-color: colors.$db-adaptive-bg-basic-transparent-full-pressed; + } + + @media screen and (prefers-reduced-motion: reduce) { + transition: none; + } + } + + // Ellipsis button for collapsed breadcrumbs + .db-breadcrumb-ellipsis { + appearance: none; + background: none; + border-width: 0; + margin: 0; + cursor: pointer; + display: flex; + align-items: center; + padding: variables.$db-spacing-fixed-3xs variables.$db-spacing-fixed-2xs; // match link padding (small) + border-radius: variables.$db-border-radius-xs; + background-color: colors.$db-adaptive-bg-basic-transparent-full-default; + color: colors.$db-adaptive-on-bg-basic-emphasis-100-default; + font-size: variables.$db-sizing-sm; // 14px (body-sm) + line-height: variables.$db-sizing-md; // 20px + font-family: var(--db-font-family-sans); + font-weight: 400; + white-space: nowrap; + transition: background-color 0.2s ease; + + &:hover, + &:focus { + background-color: colors.$db-adaptive-bg-basic-transparent-semi-default; + } + + &:active { + background-color: colors.$db-adaptive-bg-basic-transparent-full-pressed; + } + + &:focus-visible { + outline: 2px solid colors.$db-adaptive-on-bg-basic-emphasis-100-default; + outline-offset: 2px; + } + + @media screen and (prefers-reduced-motion: reduce) { + transition: none; + } + } + + // Default chevron: apply when the breadcrumb does NOT request a slash + &:not([data-separator="slash"]) + .db-breadcrumb-list + > li:not(:first-child)::before { + content: "›"; // chevron right glyph (U+203A) + display: flex; + align-items: center; + justify-content: center; + inline-size: variables.$db-sizing-sm; // 24px (small) + block-size: variables.$db-sizing-sm; + font-size: variables.$db-sizing-md; // 20px + color: colors.$db-adaptive-on-bg-basic-emphasis-100-default; + font-weight: 400; + } + + // Slash separator override when component has data-separator="slash" + &[data-separator="slash"] .db-breadcrumb-list li:not(:first-child)::before { + content: "/"; // ASCII slash (0x2F) + font-weight: 400; + color: colors.$db-adaptive-on-bg-basic-emphasis-100-default; + + /* reduce the inline size so the slash doesn't create extra empty box */ + inline-size: auto; + block-size: auto; + font-size: variables.$db-sizing-sm; // match small text sizing by default + margin: 0 variables.$db-spacing-fixed-2xs; // small horizontal spacing + } + + // Medium size variant + &[data-size="medium"] { + .db-breadcrumb-list { + // Slightly more breathing room for medium size (matches Figma spacing) + gap: variables.$db-spacing-fixed-xs; // 8px gap for medium + + /* ensure list items are vertically centered in medium size */ + align-items: center; + } + + li { + // Increase gap between separator and item for better touch target + gap: variables.$db-spacing-fixed-xs; // 8px between separator and item (medium) + + &:not(:first-child)::before { + // Make the chevron larger to match visual weight in Figma + inline-size: variables.$db-sizing-lg; // ~24px + block-size: variables.$db-sizing-lg; + font-size: variables.$db-sizing-lg; // use large sizing token + font-weight: 600; + color: colors.$db-adaptive-on-bg-basic-emphasis-100-default; + } + + a, + span { + // Keep the medium padding but ensure comfortable tap/click area + padding: variables.$db-spacing-fixed-2xs + variables.$db-spacing-fixed-xs; // 4px 8px (medium) + + font-size: variables.$db-sizing-md; // 16px (body-md) + line-height: variables.$db-sizing-lg; // 24px + } + } + + // Ellipsis button styling for medium size + .db-breadcrumb-ellipsis { + padding: variables.$db-spacing-fixed-2xs + variables.$db-spacing-fixed-xs; // match medium padding + + font-size: variables.$db-sizing-md; // 16px (body-md) + line-height: variables.$db-sizing-lg; // 24px + } + } +} diff --git a/packages/components/src/components/breadcrumb/breadcrumb.spec.tsx b/packages/components/src/components/breadcrumb/breadcrumb.spec.tsx new file mode 100644 index 000000000000..1af69632434d --- /dev/null +++ b/packages/components/src/components/breadcrumb/breadcrumb.spec.tsx @@ -0,0 +1,46 @@ +import AxeBuilder from '@axe-core/playwright'; +import { expect, test } from '@playwright/experimental-ct-react'; + +import { DBBreadcrumb } from './index'; +// @ts-ignore - vue can only find it with .ts as file ending +import { DEFAULT_VIEWPORT } from '../../shared/constants.ts'; + +const defaultBreadcrumb: any = ( + +
  • + Home +
  • +
  • + Category +
  • +
  • Current Page
  • +
    +); + +test.describe('DBBreadcrumb', () => { + test('should render', async ({ mount }) => { + const component = await mount(defaultBreadcrumb); + await expect(component).toBeVisible(); + }); + + test('should have accessible role', async ({ mount }) => { + const component = await mount(defaultBreadcrumb); + await expect(component).toHaveAttribute('aria-label', 'breadcrumb'); + }); + + test('should not have basic accessibility issues', async ({ mount }) => { + const component = await mount(defaultBreadcrumb); + const accessibilityScanResults = await new AxeBuilder({ + page: component.page() + }) + .include('.db-breadcrumb') + .analyze(); + expect(accessibilityScanResults.violations).toEqual([]); + }); + + test('should match screenshot', async ({ mount }) => { + const component = await mount(defaultBreadcrumb); + await component.page().setViewportSize(DEFAULT_VIEWPORT); + await expect(component).toHaveScreenshot(); + }); +}); diff --git a/packages/components/src/components/breadcrumb/docs/Angular.md b/packages/components/src/components/breadcrumb/docs/Angular.md new file mode 100644 index 000000000000..9cee713c86f0 --- /dev/null +++ b/packages/components/src/components/breadcrumb/docs/Angular.md @@ -0,0 +1,22 @@ +## Angular + +For general installation and configuration take a look at the [ngx-core-components](https://www.npmjs.com/package/@db-ux/ngx-core-components) package. + +### Use component + +```ts app.component.ts +// app.component.ts +import { Component } from "@angular/core"; + +@Component({ + selector: "app-root", + template: ` + +
  • Home
  • +
  • Category
  • +
  • Current Page
  • +
    + ` +}) +export class AppComponent {} +``` diff --git a/packages/components/src/components/breadcrumb/docs/HTML.md b/packages/components/src/components/breadcrumb/docs/HTML.md new file mode 100644 index 000000000000..f32ee069f5be --- /dev/null +++ b/packages/components/src/components/breadcrumb/docs/HTML.md @@ -0,0 +1,19 @@ +## HTML + +### Use component + +```html + +``` + +### Import styles + +```scss app.scss +@forward "@db-ux/core-components/build/styles/relative"; +``` diff --git a/packages/components/src/components/breadcrumb/docs/Migration.md b/packages/components/src/components/breadcrumb/docs/Migration.md new file mode 100644 index 000000000000..c31cf696ec13 --- /dev/null +++ b/packages/components/src/components/breadcrumb/docs/Migration.md @@ -0,0 +1,15 @@ +## Migration Guide + +### From v2.x to v3.x + +Currently no migration needed as this is a new component in v3.x. + +### Breaking Changes + +- None (new component) + +### New Features + +- Added `DBBreadcrumb` component for navigation breadcrumbs +- Supports semantic HTML structure with proper ARIA labels +- Responsive design with flexbox layout diff --git a/packages/components/src/components/breadcrumb/docs/React.md b/packages/components/src/components/breadcrumb/docs/React.md new file mode 100644 index 000000000000..25db6cf87a59 --- /dev/null +++ b/packages/components/src/components/breadcrumb/docs/React.md @@ -0,0 +1,24 @@ +## React + +For general installation and configuration take a look at the [react-core-components](https://www.npmjs.com/package/@db-ux/react-core-components) package. + +### Use component + +```tsx App.tsx +// App.tsx +import { DBBreadcrumb } from "@db-ux/react-core-components"; + +const App = () => ( + +
  • + Home +
  • +
  • + Category +
  • +
  • Current Page
  • +
    +); + +export default App; +``` diff --git a/packages/components/src/components/breadcrumb/docs/Vue.md b/packages/components/src/components/breadcrumb/docs/Vue.md new file mode 100644 index 000000000000..66655ae86eac --- /dev/null +++ b/packages/components/src/components/breadcrumb/docs/Vue.md @@ -0,0 +1,19 @@ +## Vue + +For general installation and configuration take a look at the [v-core-components](https://www.npmjs.com/package/@db-ux/v-core-components) package. + +### Use component + +```vue App.vue + + + +``` diff --git a/packages/components/src/components/breadcrumb/index.html b/packages/components/src/components/breadcrumb/index.html new file mode 100644 index 000000000000..afbd1afedfe3 --- /dev/null +++ b/packages/components/src/components/breadcrumb/index.html @@ -0,0 +1,30 @@ + + + + + DBBreadcrumb + + + + + +

    + + + + + diff --git a/packages/components/src/components/breadcrumb/index.ts b/packages/components/src/components/breadcrumb/index.ts new file mode 100644 index 000000000000..dbaad9162cf9 --- /dev/null +++ b/packages/components/src/components/breadcrumb/index.ts @@ -0,0 +1 @@ +export { default as DBBreadcrumb } from './breadcrumb'; diff --git a/packages/components/src/components/breadcrumb/model.ts b/packages/components/src/components/breadcrumb/model.ts new file mode 100644 index 000000000000..5b23ccfa4871 --- /dev/null +++ b/packages/components/src/components/breadcrumb/model.ts @@ -0,0 +1,63 @@ +import { GlobalProps, GlobalState } from '../../shared/model'; +import type { DBBreadcrumbItemProps } from '../breadcrumb-item'; + +export const BreadcrumbSizeList = ['small', 'medium'] as const; +export type BreadcrumbSize = (typeof BreadcrumbSizeList)[number]; +export const BreadcrumbSeparatorList = ['chevron', 'slash'] as const; +export type BreadcrumbSeparator = (typeof BreadcrumbSeparatorList)[number]; + +export interface DBBreadcrumbDefaultProps { + /** + * The size of the breadcrumb items + */ + size?: BreadcrumbSize; + + /** + * The separator between breadcrumb items: 'chevron' or 'slash' + */ + separator?: BreadcrumbSeparator; + + /** + * Maximum number of items to display before collapsing + */ + maxItems?: number; + + /** + * Show collapsed items in a dropdown menu instead of expanding inline + */ + collapsedMenu?: boolean; + + /** + * Aria label for the ellipsis button in collapsed view + */ + ellipsisAriaLabel?: string; + + /** + * The breadcrumb items + */ + items?: DBBreadcrumbItemProps[]; + + /** + * Aria label for the breadcrumb navigation + */ + ariaLabel?: string; +} + +export interface DBBreadcrumbProps + extends DBBreadcrumbDefaultProps, + GlobalProps {} + +export interface DBBreadcrumbDefaultState { + /** + * Tracks whether the breadcrumb is expanded or collapsed + */ + isExpanded?: boolean; + /** + * Toggle function for expanding/collapsing + */ + toggleExpanded: () => void; +} + +export interface DBBreadcrumbState + extends DBBreadcrumbDefaultState, + GlobalState {} diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index b905eb44af91..4b8ba0993169 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -6,6 +6,10 @@ export * from './components/badge'; export * from './components/badge/model'; export * from './components/brand'; export * from './components/brand/model'; +export * from './components/breadcrumb'; +export * from './components/breadcrumb-item'; +export * from './components/breadcrumb-item/model'; +export * from './components/breadcrumb/model'; export * from './components/button'; export * from './components/button/model'; export * from './components/card'; diff --git a/packages/components/src/styles/index.scss b/packages/components/src/styles/index.scss index 0830302f8b04..47cf969698b2 100644 --- a/packages/components/src/styles/index.scss +++ b/packages/components/src/styles/index.scss @@ -5,6 +5,7 @@ @forward "../components/card/card"; @forward "../components/input/input"; @forward "../components/brand/brand"; +@forward "../components/breadcrumb/breadcrumb"; @forward "../components/header/header"; @forward "../components/page/page"; @forward "../components/link/link"; diff --git a/showcases/angular-showcase/src/app/components/breadcrumb/breadcrumb.component.html b/showcases/angular-showcase/src/app/components/breadcrumb/breadcrumb.component.html new file mode 100644 index 000000000000..8ed290f3fc3d --- /dev/null +++ b/showcases/angular-showcase/src/app/components/breadcrumb/breadcrumb.component.html @@ -0,0 +1,130 @@ + + + + + + diff --git a/showcases/angular-showcase/src/app/components/breadcrumb/breadcrumb.component.ts b/showcases/angular-showcase/src/app/components/breadcrumb/breadcrumb.component.ts new file mode 100644 index 000000000000..e252cae66ed2 --- /dev/null +++ b/showcases/angular-showcase/src/app/components/breadcrumb/breadcrumb.component.ts @@ -0,0 +1,21 @@ +import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +// TODO: Uncomment after build-outputs: import { DBBreadcrumb } from '../../../../../../output/angular/src'; +import { DBIcon, DBTooltip } from '@db-ux/ngx-core-components/src'; +import defaultComponentVariants from '../../../../../shared/breadcrumb.json'; +import { DefaultComponent } from '../default.component'; + +@Component({ + selector: 'app-breadcrumb', + templateUrl: './breadcrumb.component.html', + imports: [DefaultComponent, DBIcon, DBTooltip], + standalone: true, + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BreadcrumbComponent { + variants = defaultComponentVariants; + isExpanded: Record = {}; + + toggleExpanded(key: string) { + this.isExpanded[key] = !this.isExpanded[key]; + } +} diff --git a/showcases/angular-showcase/src/app/components/default.component.ts b/showcases/angular-showcase/src/app/components/default.component.ts index 95956a2f5e3d..cfb0c99cd66e 100644 --- a/showcases/angular-showcase/src/app/components/default.component.ts +++ b/showcases/angular-showcase/src/app/components/default.component.ts @@ -16,7 +16,7 @@ import { DBLink, DENSITY, DENSITY_CONST -} from '../../../../../output/angular/src'; +} from '@db-ux/ngx-core-components/src'; import type { DefaultComponentProps, DefaultComponentVariants diff --git a/showcases/angular-showcase/src/app/utils/navigation-item.ts b/showcases/angular-showcase/src/app/utils/navigation-item.ts index b7b06efdd447..718c4a70fd51 100644 --- a/showcases/angular-showcase/src/app/utils/navigation-item.ts +++ b/showcases/angular-showcase/src/app/utils/navigation-item.ts @@ -3,6 +3,7 @@ import { AccordionItemComponent } from '../components/accordion-item/accordion-i import { AccordionComponent } from '../components/accordion/accordion.component'; import { BadgeComponent } from '../components/badge/badge.component'; import { BrandComponent } from '../components/brand/brand.component'; +import { BreadcrumbComponent } from '../components/breadcrumb/breadcrumb.component'; import { ButtonComponent } from '../components/button/button.component'; import { CardComponent } from '../components/card/card.component'; import { CheckboxComponent } from '../components/checkbox/checkbox.component'; @@ -60,6 +61,11 @@ export const NAVIGATION_ITEMS: NavItem[] = [ path: '05', label: '05 Navigation', subNavigation: getSortedNavigationItems([ + { + path: '05/breadcrumb', + label: 'Breadcrumb', + component: BreadcrumbComponent + }, { path: '05/navigation-item', label: 'NavigationItem', diff --git a/showcases/e2e/breadcrumb/breadcrumb-a11y-checker.spec.ts b/showcases/e2e/breadcrumb/breadcrumb-a11y-checker.spec.ts new file mode 100644 index 000000000000..826ae6a20316 --- /dev/null +++ b/showcases/e2e/breadcrumb/breadcrumb-a11y-checker.spec.ts @@ -0,0 +1,6 @@ +import { test } from '@playwright/test'; +import { runA11yCheckerTest } from '../default.ts'; + +test.describe('DBBreadcrumb', () => { + runA11yCheckerTest({ path: '05/breadcrumb' }); +}); diff --git a/showcases/e2e/breadcrumb/breadcrumb-aria-snapshot.spec.ts b/showcases/e2e/breadcrumb/breadcrumb-aria-snapshot.spec.ts new file mode 100644 index 000000000000..dd90e6c03396 --- /dev/null +++ b/showcases/e2e/breadcrumb/breadcrumb-aria-snapshot.spec.ts @@ -0,0 +1,6 @@ +import { test } from '@playwright/test'; +import { runAriaSnapshotTest } from '../default.ts'; + +test.describe('DBBreadcrumb', () => { + runAriaSnapshotTest({ path: '05/breadcrumb' }); +}); diff --git a/showcases/e2e/breadcrumb/breadcrumb-axe-core.spec.ts b/showcases/e2e/breadcrumb/breadcrumb-axe-core.spec.ts new file mode 100644 index 000000000000..7d7da93e1b31 --- /dev/null +++ b/showcases/e2e/breadcrumb/breadcrumb-axe-core.spec.ts @@ -0,0 +1,9 @@ +import { test } from '@playwright/test'; +import { runAxeCoreTest } from '../default.ts'; +import { lvl3 } from '../fixtures/variants'; + +test.describe('DBBreadcrumb', () => { + runAxeCoreTest({ path: '05/breadcrumb' }); + runAxeCoreTest({ path: '05/breadcrumb', color: lvl3 }); + runAxeCoreTest({ path: '05/breadcrumb', density: 'functional' }); +}); diff --git a/showcases/e2e/breadcrumb/breadcrumb-visual-snapshot.spec.ts b/showcases/e2e/breadcrumb/breadcrumb-visual-snapshot.spec.ts new file mode 100644 index 000000000000..1324a7edc3dd --- /dev/null +++ b/showcases/e2e/breadcrumb/breadcrumb-visual-snapshot.spec.ts @@ -0,0 +1,7 @@ +import { test } from '@playwright/test'; +import { getDefaultScreenshotTest } from '../default.ts'; + +const path = '05/breadcrumb'; +test.describe('DBBreadcrumb', () => { + getDefaultScreenshotTest({ path }); +}); diff --git a/showcases/patternhub/data/components.json b/showcases/patternhub/data/components.json index 987d0eb9d487..fba7a83bbf31 100644 --- a/showcases/patternhub/data/components.json +++ b/showcases/patternhub/data/components.json @@ -167,6 +167,16 @@ "label": "DBNavigationItem", "name": "navigation-item", "isHiddenInMenu": true + }, + { + "label": "DBBreadcrumb", + "name": "breadcrumb", + "subNavigation": [ + { + "label": "DBBreadcrumb Properties", + "path": "/components/breadcrumb/properties" + } + ] } ] }, diff --git a/showcases/react-showcase/src/components/breadcrumb/index.tsx b/showcases/react-showcase/src/components/breadcrumb/index.tsx new file mode 100644 index 000000000000..94d57ea57450 --- /dev/null +++ b/showcases/react-showcase/src/components/breadcrumb/index.tsx @@ -0,0 +1,60 @@ +import { DBBreadcrumb } from '../../../../../output/react/src'; +import defaultComponentVariants from '../../../../shared/breadcrumb.json'; +import { getVariants } from '../data'; +import DefaultComponent from '../default-component'; + +type BreadcrumbItem = { + href?: string; + text: string; + icon?: string; + ariaCurrent?: 'page' | undefined; +}; + +type BreadcrumbExampleProps = { + children?: BreadcrumbItem[]; + size?: 'small' | 'medium'; + className?: string; + separator?: 'chevron' | 'slash'; + maxItems?: number; + collapsedMenu?: boolean; + ariaLabel?: string; + id?: string; +}; + +const getBreadcrumb = ({ + children, + size, + className, + separator, + maxItems, + collapsedMenu, + ariaLabel, + id +}: BreadcrumbExampleProps) => ( + +); + +type BreadcrumbComponentProps = { + slotCode?: Record; +}; + +const BreadcrumbComponent = (props: BreadcrumbComponentProps) => ( + +); + +export default BreadcrumbComponent; diff --git a/showcases/react-showcase/src/components/data.ts b/showcases/react-showcase/src/components/data.ts index cd1333254ce9..4e774471b786 100644 --- a/showcases/react-showcase/src/components/data.ts +++ b/showcases/react-showcase/src/components/data.ts @@ -14,6 +14,8 @@ export const getVariants = ( ], examples: variant.examples.map((example, exampleIndex) => ({ ...example, + // Ensure className from props is available on the example object + className: example.className ?? example.props?.className, example: getExample({ ...example.props, id: example.props?.id ?? example.name, diff --git a/showcases/react-showcase/src/utils/navigation-item.tsx b/showcases/react-showcase/src/utils/navigation-item.tsx index 1f5f0774bb6e..6797ac7b1b22 100644 --- a/showcases/react-showcase/src/utils/navigation-item.tsx +++ b/showcases/react-showcase/src/utils/navigation-item.tsx @@ -2,6 +2,7 @@ import AccordionComponent from '../components/accordion'; import AccordionItemComponent from '../components/accordion-item'; import BadgeComponent from '../components/badge'; import BrandComponent from '../components/brand'; +import BreadcrumbComponent from '../components/breadcrumb'; import ButtonComponent from '../components/button'; import CardComponent from '../components/card'; import CheckboxComponent from '../components/checkbox'; @@ -60,6 +61,11 @@ export const NAVIGATION_ITEMS: NavigationItem[] = [ path: '05', label: '05 Navigation', subNavigation: getSortedNavigationItems([ + { + path: 'breadcrumb', + label: 'Breadcrumb', + component: + }, { path: 'navigation-item', label: 'NavigationItem', diff --git a/showcases/shared/breadcrumb.json b/showcases/shared/breadcrumb.json new file mode 100644 index 000000000000..be7954993a33 --- /dev/null +++ b/showcases/shared/breadcrumb.json @@ -0,0 +1,123 @@ +[ + { + "name": "Size", + "examples": [ + { + "name": "(Default) Small", + "className": "w-full", + "props": { + "size": "small", + "children": [ + { "href": "#", "text": "Home" }, + { "href": "#", "text": "Category" }, + { "text": "Current Page", "ariaCurrent": "page", "href": "#" } + ] + } + }, + { + "name": "Medium", + "className": "w-full", + "props": { + "size": "medium", + "children": [ + { "href": "#", "text": "Home" }, + { "href": "#", "text": "Category" }, + { "text": "Current Page", "ariaCurrent": "page" } + ] + } + } + ] + }, + { + "name": "Separator", + "examples": [ + { + "name": "Chevron", + "className": "w-full", + "props": { + "separator": "chevron", + "children": [ + { "href": "#", "text": "Home" }, + { "href": "#", "text": "Category" }, + { "text": "Current Page", "ariaCurrent": "page" } + ] + } + }, + { + "name": "Slash", + "className": "w-full", + "props": { + "separator": "slash", + "children": [ + { "href": "#", "text": "Home" }, + { "href": "#", "text": "Category" }, + { "text": "Current Page", "ariaCurrent": "page" } + ] + } + } + ] + }, + { + "name": "Collapsed", + "examples": [ + { + "name": "Collapsed (maxItems=3)", + "className": "w-full", + "props": { + "size": "small", + "id": "123", + "maxItems": 3, + "ellipsisAriaLabel": "More breadcrumb items", + "children": [ + { "href": "#", "text": "Root" }, + { "href": "#", "text": "Path 1" }, + { "href": "#", "text": "Path 2" }, + { "href": "#", "text": "Path 3" }, + { "text": "Current Page", "ariaCurrent": "page", "href": "#" } + ] + } + }, + { + "name": "Collapsed with Menu", + "className": "w-full", + "props": { + "size": "small", + "maxItems": 3, + "id": "234", + "collapsedMenu": true, + "ariaLabel": "Breadcrumb Menu", + "ellipsisAriaLabel": "More breadcrumb items", + "children": [ + { "href": "#", "text": "Root" }, + { "href": "#1", "text": "Path 1" }, + { "href": "#2", "text": "Path 2" }, + { "href": "#3", "text": "Path 3" }, + { "text": "Current Page", "ariaCurrent": "page", "href": "#" } + ] + } + } + ] + }, + { + "name": "Icons", + "examples": [ + { + "name": "With Icons", + "className": "w-full", + "props": { + "size": "small", + "children": [ + { "href": "#", "text": "Root", "icon": "house" }, + { + "href": "#", + "text": "Settings", + "icon": "gear_wheel" + }, + { "href": "#", "text": "Profile", "icon": "person" }, + { "href": "#", "text": "Notifications", "icon": "bell" } + ] + } + } + ] + } +] diff --git a/showcases/showcase-styles.css b/showcases/showcase-styles.css index b244b174a8f8..dc3c89cacc5a 100644 --- a/showcases/showcase-styles.css +++ b/showcases/showcase-styles.css @@ -67,6 +67,18 @@ db-card:is(.variants-card) > .db-card { margin-block: 0 auto; } +/* Breadcrumb showcase: stack breadcrumb examples vertically */ +@supports selector(:has(*)) { + .variants-list > div:has(.db-breadcrumb) { + flex: 0 0 100%; + } +} + +/* Fallback: if :has unsupported, rely on utility class or force wrappers containing db-breadcrumb to behave as block via descendant width trick */ +.variants-list .db-breadcrumb { + inline-size: 100%; +} + .html-code-container { display: flex; flex-direction: column; diff --git a/showcases/vue-showcase/src/components/breadcrumb/breadcrumb.vue b/showcases/vue-showcase/src/components/breadcrumb/breadcrumb.vue new file mode 100644 index 000000000000..e00a629d2055 --- /dev/null +++ b/showcases/vue-showcase/src/components/breadcrumb/breadcrumb.vue @@ -0,0 +1,131 @@ + + + diff --git a/showcases/vue-showcase/src/utils/navigation-items.ts b/showcases/vue-showcase/src/utils/navigation-items.ts index beee8433a446..a4fb5ae25ef5 100644 --- a/showcases/vue-showcase/src/utils/navigation-items.ts +++ b/showcases/vue-showcase/src/utils/navigation-items.ts @@ -4,6 +4,7 @@ import AccordionItem from '../components/accordion-item/AccordionItem.vue'; import Accordion from '../components/accordion/Accordion.vue'; import Badge from '../components/badge/Badge.vue'; import Brand from '../components/brand/Brand.vue'; +import Breadcrumb from '../components/breadcrumb/breadcrumb.vue'; import Button from '../components/button/Button.vue'; import Card from '../components/card/Card.vue'; import Checkbox from '../components/checkbox/Checkbox.vue'; @@ -61,6 +62,11 @@ export const navigationItems: NavItem[] = [ path: '/05', label: '05 Navigation', subNavigation: getSortedNavigationItems([ + { + path: '/05/breadcrumb', + label: 'Breadcrumb', + component: markRaw(Breadcrumb) + }, { path: '/05/navigation-item', label: 'NavigationItem',