Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<!--
SPDX-FileCopyrightText: 2025 hexaTune LLC
SPDX-License-Identifier: MIT
-->

<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import AdminBreadcrumbs from './AdminBreadcrumbs.svelte';
import { fn } from 'storybook/test';
import type { BreadcrumbItem } from '../../core/overlay-navigation/Breadcrumbs.svelte';

const { Story } = defineMeta({
title: 'Admin/Layout/AdminBreadcrumbs',
component: AdminBreadcrumbs,
tags: ['autodocs'],
argTypes: {
separator: { control: 'text' },
size: {
control: 'select',
options: ['xs', 'sm', 'md', 'lg']
},
variant: {
control: 'select',
options: [
'primary',
'secondary',
'accent',
'neutral',
'info',
'success',
'warning',
'error'
]
},
disabled: { control: 'boolean' },
loading: { control: 'boolean' },
ariaLabel: { control: 'text' }
}
});

const sampleItems: BreadcrumbItem[] = [
{ id: 1, label: 'Admin', href: '/admin' },
{ id: 2, label: 'Dashboard', href: '/admin/dashboard' },
{ id: 3, label: 'Settings', current: true }
];

const itemsWithIcons: BreadcrumbItem[] = [
{ id: 1, label: 'Home', href: '/admin', icon: '🏠' },
{ id: 2, label: 'Users', href: '/admin/users', icon: 'πŸ‘₯' },
{ id: 3, label: 'Profile', current: true, icon: 'πŸ‘€' }
];

const longItems: BreadcrumbItem[] = [
{ id: 1, label: 'Admin', href: '/admin' },
{ id: 2, label: 'System', href: '/admin/system' },
{ id: 3, label: 'Configuration', href: '/admin/system/config' },
{ id: 4, label: 'Network', href: '/admin/system/config/network' },
{ id: 5, label: 'Security', current: true }
];

const itemsWithButtons: BreadcrumbItem[] = [
{ id: 1, label: 'Admin', onclick: fn() },
{ id: 2, label: 'System', onclick: fn() },
{ id: 3, label: 'Tools', current: true }
];
</script>

<!-- 1. Default Story -->
<Story
name="Default"
args={{
items: sampleItems
}}
/>

<!-- 2. Size Variants -->
<Story
name="Extra Small Size"
args={{
items: sampleItems,
size: 'xs'
}}
/>

<Story
name="Large Size"
args={{
items: sampleItems,
size: 'lg'
}}
/>

<!-- 3. Variant Colors -->
<Story
name="Primary Variant"
args={{
items: sampleItems,
variant: 'primary'
}}
/>

<Story
name="Success Variant"
args={{
items: sampleItems,
variant: 'success'
}}
/>

<!-- 5. With Icons -->
<Story
name="With Icons"
args={{
items: itemsWithIcons
}}
/>

<!-- 6. Items with Buttons -->
<Story
name="Items with Buttons"
args={{
items: itemsWithButtons
}}
/>

<!-- 7. Custom Separator -->
<Story
name="Custom Separator"
args={{
items: sampleItems,
separator: 'β†’'
}}
/>

<!-- 8. Long Path -->
<Story
name="Long Path"
args={{
items: longItems
}}
/>

<!-- 7. States -->
<Story
name="Loading State"
args={{
items: sampleItems,
loading: true
}}
/>

<Story
name="Disabled State"
args={{
items: sampleItems,
disabled: true
}}
/>

<!-- βœ… REQUIRED: Interactive Playground Story (must be last) -->
<Story
name="Playground"
args={{
items: sampleItems,
separator: '/',
size: 'sm',
variant: 'neutral',
disabled: false,
loading: false,
ariaLabel: 'Admin navigation'
}}
/>
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,90 @@
SPDX-FileCopyrightText: 2025 hexaTune LLC
SPDX-License-Identifier: MIT
-->

<script lang="ts">
import Breadcrumbs from '../../core/overlay-navigation/Breadcrumbs.svelte';
import type { BreadcrumbItem } from '../../core/overlay-navigation/Breadcrumbs.svelte';

/**
* Props interface for the AdminBreadcrumbs component
*/
interface Props {
/**
* Array of breadcrumb items to display
*/
items: BreadcrumbItem[];
/**
* Custom separator between breadcrumb items
* @default '/'
*/
separator?: string;
/**
* Size variant of the breadcrumbs
* @default 'sm'
*/
size?: 'xs' | 'sm' | 'md' | 'lg';
/**
* Color variant of the breadcrumbs
* @default 'neutral'
*/
variant?:
| 'primary'
| 'secondary'
| 'accent'
| 'neutral'
| 'info'
| 'success'
| 'warning'
| 'error';
/**
* Whether the breadcrumbs are disabled
* @default false
*/
disabled?: boolean;
/**
* Whether the breadcrumbs are in loading state
* @default false
*/
loading?: boolean;
/**
* Accessible label for the breadcrumbs navigation
* @default 'Admin navigation'
*/
ariaLabel?: string;
/**
* ID of the element that labels this breadcrumbs
*/
ariaLabelledby?: string;
/**
* Additional CSS classes
*/
class?: string;
}

const {
items,
separator = '/',
size = 'sm',
variant = 'neutral',
disabled = false,
loading = false,
ariaLabel = 'Admin navigation',
ariaLabelledby,
class: className = '',
...props
}: Props = $props();
</script>

<Breadcrumbs
{items}
{separator}
{size}
{variant}
{disabled}
{loading}
{ariaLabel}
{ariaLabelledby}
class={className}
{...props}
/>
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ SPDX-License-Identifier: MIT
// Computed container classes
let containerClasses = $derived(
[
'breadcrumbs',
'hb-breadcrumbs',
size === 'xs' && 'text-xs',
size === 'sm' && 'text-sm',
size === 'md' && 'text-base',
Expand Down Expand Up @@ -201,7 +201,7 @@ SPDX-License-Identifier: MIT
aria-labelledby={ariaLabelledby}
{...props}
>
<ul class="breadcrumbs" role="list">
<ul class="hb-breadcrumbs-list" role="list">
{#each items as item, index (item.id ?? index)}
{@const isLast = isLastItem(index)}
{@const itemClasses = getItemClasses(item, index, isLast)}
Expand Down Expand Up @@ -288,7 +288,7 @@ SPDX-License-Identifier: MIT
</nav>

<style>
.breadcrumbs {
.hb-breadcrumbs-list {
display: flex;
flex-wrap: wrap;
align-items: center;
Expand Down Expand Up @@ -424,8 +424,9 @@ SPDX-License-Identifier: MIT
}

@media (max-width: 640px) {
.breadcrumbs {
.hb-breadcrumbs-list {
font-size: 0.875rem;
gap: 0.25rem;
}

.breadcrumb-separator {
Expand Down
Loading