@sovereignfs/ui 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/NOTICE +24 -0
- package/dist/Avatar.module.css +38 -0
- package/dist/Badge.module.css +88 -0
- package/dist/Button.module.css +77 -0
- package/dist/Card.module.css +34 -0
- package/dist/Dialog.module.css +188 -0
- package/dist/Drawer.module.css +30 -0
- package/dist/EmptyState.module.css +27 -0
- package/dist/FormField.module.css +28 -0
- package/dist/Icon.module.css +22 -0
- package/dist/Input.module.css +37 -0
- package/dist/NavTabs.module.css +48 -0
- package/dist/PageHeader.module.css +32 -0
- package/dist/Popover.module.css +44 -0
- package/dist/SegmentedControl.module.css +53 -0
- package/dist/Select.module.css +69 -0
- package/dist/Spinner.module.css +39 -0
- package/dist/SystemBanner.module.css +67 -0
- package/dist/Tabs.module.css +49 -0
- package/dist/Toast.module.css +130 -0
- package/dist/Toggle.module.css +52 -0
- package/dist/Tooltip.module.css +51 -0
- package/dist/index.d.ts +327 -0
- package/dist/index.js +1370 -0
- package/dist/tokens/primitives.css +99 -0
- package/dist/tokens/semantic.css +111 -0
- package/dist/tokens.css +5 -0
- package/package.json +50 -0
- package/src/components/Avatar/Avatar.module.css +38 -0
- package/src/components/Avatar/Avatar.tsx +42 -0
- package/src/components/Badge/Badge.module.css +88 -0
- package/src/components/Badge/Badge.stories.tsx +118 -0
- package/src/components/Badge/Badge.tsx +59 -0
- package/src/components/Badge/__tests__/Badge.test.tsx +64 -0
- package/src/components/Button/Button.module.css +77 -0
- package/src/components/Button/Button.stories.tsx +76 -0
- package/src/components/Button/Button.tsx +36 -0
- package/src/components/Button/__tests__/Button.test.tsx +37 -0
- package/src/components/Card/Card.module.css +34 -0
- package/src/components/Card/Card.tsx +34 -0
- package/src/components/Dialog/Dialog.module.css +188 -0
- package/src/components/Dialog/Dialog.stories.tsx +97 -0
- package/src/components/Dialog/Dialog.tsx +137 -0
- package/src/components/Dialog/__tests__/Dialog.test.tsx +73 -0
- package/src/components/Drawer/Drawer.module.css +30 -0
- package/src/components/Drawer/Drawer.stories.tsx +88 -0
- package/src/components/Drawer/Drawer.tsx +103 -0
- package/src/components/Drawer/__tests__/Drawer.test.tsx +56 -0
- package/src/components/EmptyState/EmptyState.module.css +27 -0
- package/src/components/EmptyState/EmptyState.tsx +27 -0
- package/src/components/FormField/FormField.module.css +28 -0
- package/src/components/FormField/FormField.tsx +53 -0
- package/src/components/Icon/Icon.module.css +22 -0
- package/src/components/Icon/Icon.stories.tsx +94 -0
- package/src/components/Icon/Icon.tsx +43 -0
- package/src/components/Icon/__tests__/Icon.test.tsx +40 -0
- package/src/components/Icon/icons/activity.tsx +20 -0
- package/src/components/Icon/icons/alert-triangle.tsx +22 -0
- package/src/components/Icon/icons/bell.tsx +21 -0
- package/src/components/Icon/icons/check.tsx +20 -0
- package/src/components/Icon/icons/chevron-down.tsx +20 -0
- package/src/components/Icon/icons/chevron-left.tsx +20 -0
- package/src/components/Icon/icons/chevron-right.tsx +20 -0
- package/src/components/Icon/icons/chevron-up.tsx +20 -0
- package/src/components/Icon/icons/eye-off.tsx +23 -0
- package/src/components/Icon/icons/eye.tsx +21 -0
- package/src/components/Icon/icons/grid-2x2.tsx +22 -0
- package/src/components/Icon/icons/house.tsx +21 -0
- package/src/components/Icon/icons/index.ts +122 -0
- package/src/components/Icon/icons/info.tsx +22 -0
- package/src/components/Icon/icons/lock.tsx +21 -0
- package/src/components/Icon/icons/log-out.tsx +22 -0
- package/src/components/Icon/icons/mail.tsx +21 -0
- package/src/components/Icon/icons/package.tsx +23 -0
- package/src/components/Icon/icons/pencil.tsx +21 -0
- package/src/components/Icon/icons/plus.tsx +21 -0
- package/src/components/Icon/icons/rotate-ccw.tsx +21 -0
- package/src/components/Icon/icons/search.tsx +21 -0
- package/src/components/Icon/icons/settings.tsx +21 -0
- package/src/components/Icon/icons/shield.tsx +20 -0
- package/src/components/Icon/icons/sliders-horizontal.tsx +28 -0
- package/src/components/Icon/icons/terminal.tsx +19 -0
- package/src/components/Icon/icons/trash-2.tsx +24 -0
- package/src/components/Icon/icons/user.tsx +21 -0
- package/src/components/Icon/icons/x.tsx +21 -0
- package/src/components/Input/Input.module.css +37 -0
- package/src/components/Input/Input.stories.tsx +86 -0
- package/src/components/Input/Input.tsx +14 -0
- package/src/components/Input/__tests__/Input.test.tsx +22 -0
- package/src/components/NavTabs/NavTabs.module.css +48 -0
- package/src/components/NavTabs/NavTabs.tsx +33 -0
- package/src/components/PageHeader/PageHeader.module.css +32 -0
- package/src/components/PageHeader/PageHeader.tsx +21 -0
- package/src/components/Popover/Popover.module.css +44 -0
- package/src/components/Popover/Popover.stories.tsx +232 -0
- package/src/components/Popover/Popover.tsx +75 -0
- package/src/components/Popover/__tests__/Popover.test.tsx +98 -0
- package/src/components/SegmentedControl/SegmentedControl.module.css +53 -0
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +118 -0
- package/src/components/SegmentedControl/SegmentedControl.tsx +56 -0
- package/src/components/SegmentedControl/__tests__/SegmentedControl.test.tsx +63 -0
- package/src/components/Select/Select.module.css +69 -0
- package/src/components/Select/Select.stories.tsx +115 -0
- package/src/components/Select/Select.tsx +40 -0
- package/src/components/Select/__tests__/Select.test.tsx +59 -0
- package/src/components/Spinner/Spinner.module.css +39 -0
- package/src/components/Spinner/Spinner.tsx +23 -0
- package/src/components/SystemBanner/SystemBanner.module.css +67 -0
- package/src/components/SystemBanner/SystemBanner.stories.tsx +86 -0
- package/src/components/SystemBanner/SystemBanner.tsx +50 -0
- package/src/components/SystemBanner/__tests__/SystemBanner.test.tsx +50 -0
- package/src/components/Tabs/Tabs.module.css +49 -0
- package/src/components/Tabs/Tabs.stories.tsx +100 -0
- package/src/components/Tabs/Tabs.tsx +46 -0
- package/src/components/Tabs/__tests__/Tabs.test.tsx +45 -0
- package/src/components/Toast/Toast.module.css +130 -0
- package/src/components/Toast/Toast.stories.tsx +191 -0
- package/src/components/Toast/Toast.tsx +139 -0
- package/src/components/Toggle/Toggle.module.css +52 -0
- package/src/components/Toggle/Toggle.stories.tsx +95 -0
- package/src/components/Toggle/Toggle.tsx +39 -0
- package/src/components/Toggle/__tests__/Toggle.test.tsx +47 -0
- package/src/components/Tooltip/Tooltip.module.css +51 -0
- package/src/components/Tooltip/Tooltip.tsx +30 -0
- package/src/css-modules.d.ts +4 -0
- package/src/index.ts +45 -0
- package/src/stories/Avatar.stories.tsx +44 -0
- package/src/stories/Card.stories.tsx +35 -0
- package/src/stories/DesignSystemOverview.stories.tsx +1683 -0
- package/src/stories/EmptyState.stories.tsx +33 -0
- package/src/stories/FormField.stories.tsx +47 -0
- package/src/stories/MobilePatterns.stories.tsx +1285 -0
- package/src/stories/NavTabs.stories.tsx +35 -0
- package/src/stories/PageHeader.stories.tsx +31 -0
- package/src/stories/Spinner.stories.tsx +25 -0
- package/src/stories/TokenGallery.stories.tsx +355 -0
- package/src/stories/Tooltip.stories.tsx +59 -0
- package/src/tokens/primitives.css +99 -0
- package/src/tokens/semantic.css +111 -0
- package/src/tokens.css +5 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
+
import { Toggle } from './Toggle';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'Components/Toggle',
|
|
7
|
+
component: Toggle,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component:
|
|
13
|
+
'38×22px binary switch for settings rows. Renders as `<button role="switch">` for reliable VoiceOver support. `aria-label` is required.',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
argTypes: {
|
|
18
|
+
checked: { control: 'boolean' },
|
|
19
|
+
disabled: { control: 'boolean' },
|
|
20
|
+
},
|
|
21
|
+
args: {
|
|
22
|
+
checked: false,
|
|
23
|
+
onChange: () => {},
|
|
24
|
+
'aria-label': 'Toggle setting',
|
|
25
|
+
},
|
|
26
|
+
} satisfies Meta<typeof Toggle>;
|
|
27
|
+
|
|
28
|
+
export default meta;
|
|
29
|
+
type Story = StoryObj<typeof meta>;
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
export const Off: Story = { args: { checked: false } };
|
|
34
|
+
export const On: Story = { args: { checked: true } };
|
|
35
|
+
export const Disabled: Story = { args: { checked: false, disabled: true } };
|
|
36
|
+
export const DisabledOn: Story = { args: { checked: true, disabled: true } };
|
|
37
|
+
|
|
38
|
+
/** Interactive — click to toggle. */
|
|
39
|
+
export const Interactive: Story = {
|
|
40
|
+
render: (args) => {
|
|
41
|
+
const [checked, setChecked] = useState(args.checked);
|
|
42
|
+
return <Toggle {...args} checked={checked} onChange={setChecked} />;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** Settings rows — typical usage with adjacent label text. */
|
|
47
|
+
export const SettingsRows: Story = {
|
|
48
|
+
render: (_args) => {
|
|
49
|
+
const [twoFA, setTwoFA] = useState(true);
|
|
50
|
+
const [email, setEmail] = useState(false);
|
|
51
|
+
|
|
52
|
+
const rowStyle: React.CSSProperties = {
|
|
53
|
+
display: 'flex',
|
|
54
|
+
alignItems: 'center',
|
|
55
|
+
justifyContent: 'space-between',
|
|
56
|
+
gap: 32,
|
|
57
|
+
padding: '12px 0',
|
|
58
|
+
borderBottom: '1px solid var(--sv-color-border)',
|
|
59
|
+
fontFamily: 'var(--sv-font-family)',
|
|
60
|
+
};
|
|
61
|
+
const labelStyle: React.CSSProperties = {
|
|
62
|
+
display: 'flex',
|
|
63
|
+
flexDirection: 'column',
|
|
64
|
+
gap: 2,
|
|
65
|
+
};
|
|
66
|
+
const titleStyle: React.CSSProperties = {
|
|
67
|
+
fontSize: 'var(--sv-font-size-sm)',
|
|
68
|
+
fontWeight: 'var(--sv-font-weight-medium)',
|
|
69
|
+
color: 'var(--sv-color-text-primary)',
|
|
70
|
+
};
|
|
71
|
+
const descStyle: React.CSSProperties = {
|
|
72
|
+
fontSize: 'var(--sv-font-size-caption)',
|
|
73
|
+
color: 'var(--sv-color-text-muted)',
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<div style={{ width: 320 }}>
|
|
78
|
+
<div style={rowStyle}>
|
|
79
|
+
<div style={labelStyle}>
|
|
80
|
+
<span style={titleStyle}>Two-factor authentication</span>
|
|
81
|
+
<span style={descStyle}>Require a code when signing in.</span>
|
|
82
|
+
</div>
|
|
83
|
+
<Toggle checked={twoFA} onChange={setTwoFA} aria-label="Two-factor authentication" />
|
|
84
|
+
</div>
|
|
85
|
+
<div style={rowStyle}>
|
|
86
|
+
<div style={labelStyle}>
|
|
87
|
+
<span style={titleStyle}>Email notifications</span>
|
|
88
|
+
<span style={descStyle}>Receive digests and alerts.</span>
|
|
89
|
+
</div>
|
|
90
|
+
<Toggle checked={email} onChange={setEmail} aria-label="Email notifications" />
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
},
|
|
95
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ButtonHTMLAttributes } from 'react';
|
|
4
|
+
import styles from './Toggle.module.css';
|
|
5
|
+
|
|
6
|
+
export interface ToggleProps extends Omit<
|
|
7
|
+
ButtonHTMLAttributes<HTMLButtonElement>,
|
|
8
|
+
'onChange' | 'aria-label'
|
|
9
|
+
> {
|
|
10
|
+
checked: boolean;
|
|
11
|
+
onChange: (checked: boolean) => void;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
'aria-label': string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Toggle — 38×22px binary switch for settings rows.
|
|
18
|
+
*
|
|
19
|
+
* Renders as `<button role="switch">` rather than a hidden `<input type="checkbox">`
|
|
20
|
+
* to avoid iOS VoiceOver focus quirks on visually styled checkboxes. The `aria-label`
|
|
21
|
+
* prop is required because the control has no visible text label of its own.
|
|
22
|
+
*/
|
|
23
|
+
export function Toggle({ checked, onChange, disabled, className, ...rest }: ToggleProps) {
|
|
24
|
+
return (
|
|
25
|
+
<button
|
|
26
|
+
type="button"
|
|
27
|
+
role="switch"
|
|
28
|
+
aria-checked={checked}
|
|
29
|
+
disabled={disabled}
|
|
30
|
+
className={[styles.track, checked ? styles.on : styles.off, className]
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
.join(' ')}
|
|
33
|
+
onClick={() => onChange(!checked)}
|
|
34
|
+
{...rest}
|
|
35
|
+
>
|
|
36
|
+
<span className={styles.thumb} />
|
|
37
|
+
</button>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { cleanup, render, screen, fireEvent } from '@testing-library/react';
|
|
4
|
+
|
|
5
|
+
afterEach(cleanup);
|
|
6
|
+
import { Toggle } from '../Toggle';
|
|
7
|
+
|
|
8
|
+
describe('Toggle', () => {
|
|
9
|
+
it('renders as a switch button', () => {
|
|
10
|
+
render(<Toggle checked={false} onChange={() => {}} aria-label="Notifications" />);
|
|
11
|
+
expect(screen.getByRole('switch', { name: 'Notifications' })).toBeDefined();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('reflects checked state via aria-checked', () => {
|
|
15
|
+
const { rerender } = render(
|
|
16
|
+
<Toggle checked={false} onChange={() => {}} aria-label="Setting" />,
|
|
17
|
+
);
|
|
18
|
+
expect(screen.getByRole('switch').getAttribute('aria-checked')).toBe('false');
|
|
19
|
+
|
|
20
|
+
rerender(<Toggle checked={true} onChange={() => {}} aria-label="Setting" />);
|
|
21
|
+
expect(screen.getByRole('switch').getAttribute('aria-checked')).toBe('true');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('calls onChange with toggled value on click', () => {
|
|
25
|
+
const onChange = vi.fn();
|
|
26
|
+
render(<Toggle checked={false} onChange={onChange} aria-label="Setting" />);
|
|
27
|
+
fireEvent.click(screen.getByRole('switch'));
|
|
28
|
+
expect(onChange).toHaveBeenCalledWith(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('does not call onChange when disabled', () => {
|
|
32
|
+
const onChange = vi.fn();
|
|
33
|
+
render(<Toggle checked={false} onChange={onChange} disabled aria-label="Setting" />);
|
|
34
|
+
fireEvent.click(screen.getByRole('switch'));
|
|
35
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('applies on class when checked', () => {
|
|
39
|
+
render(<Toggle checked={true} onChange={() => {}} aria-label="Setting" />);
|
|
40
|
+
expect(screen.getByRole('switch').className).toContain('on');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('applies off class when unchecked', () => {
|
|
44
|
+
render(<Toggle checked={false} onChange={() => {}} aria-label="Setting" />);
|
|
45
|
+
expect(screen.getByRole('switch').className).toContain('off');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
.wrapper {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.tip {
|
|
7
|
+
position: absolute;
|
|
8
|
+
z-index: 50;
|
|
9
|
+
white-space: nowrap;
|
|
10
|
+
padding: var(--sv-space-1) var(--sv-space-2);
|
|
11
|
+
background: var(--sv-color-surface-raised);
|
|
12
|
+
border: 1px solid var(--sv-color-border);
|
|
13
|
+
border-radius: var(--sv-radius-md);
|
|
14
|
+
box-shadow: var(--sv-shadow-popover);
|
|
15
|
+
font-size: var(--sv-font-size-xs);
|
|
16
|
+
color: var(--sv-color-text-primary);
|
|
17
|
+
font-family: var(--sv-font-family);
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
opacity: 0;
|
|
20
|
+
transition: opacity 0.1s ease;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.wrapper:hover .tip,
|
|
24
|
+
.wrapper:focus-within .tip {
|
|
25
|
+
opacity: 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* Placement */
|
|
29
|
+
.top {
|
|
30
|
+
bottom: calc(100% + var(--sv-space-2));
|
|
31
|
+
left: 50%;
|
|
32
|
+
transform: translateX(-50%);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.bottom {
|
|
36
|
+
top: calc(100% + var(--sv-space-2));
|
|
37
|
+
left: 50%;
|
|
38
|
+
transform: translateX(-50%);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.left {
|
|
42
|
+
right: calc(100% + var(--sv-space-2));
|
|
43
|
+
top: 50%;
|
|
44
|
+
transform: translateY(-50%);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.right {
|
|
48
|
+
left: calc(100% + var(--sv-space-2));
|
|
49
|
+
top: 50%;
|
|
50
|
+
transform: translateY(-50%);
|
|
51
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import { useId } from 'react';
|
|
3
|
+
import styles from './Tooltip.module.css';
|
|
4
|
+
|
|
5
|
+
export interface TooltipProps {
|
|
6
|
+
content: string;
|
|
7
|
+
children: ReactElement;
|
|
8
|
+
side?: 'top' | 'bottom' | 'left' | 'right';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const sideClass: Record<NonNullable<TooltipProps['side']>, string> = {
|
|
12
|
+
top: styles.top as string,
|
|
13
|
+
bottom: styles.bottom as string,
|
|
14
|
+
left: styles.left as string,
|
|
15
|
+
right: styles.right as string,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function Tooltip({ content, children, side = 'top' }: TooltipProps) {
|
|
19
|
+
const tipId = useId();
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<span className={styles.wrapper}>
|
|
23
|
+
{/* aria-describedby wires the tooltip text to the trigger for screen readers */}
|
|
24
|
+
<span aria-describedby={tipId}>{children}</span>
|
|
25
|
+
<span id={tipId} role="tooltip" className={[styles.tip, sideClass[side]].join(' ')}>
|
|
26
|
+
{content}
|
|
27
|
+
</span>
|
|
28
|
+
</span>
|
|
29
|
+
);
|
|
30
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export { Badge } from './components/Badge/Badge';
|
|
2
|
+
export type { BadgeProps, BadgeVariant, BadgeStatus } from './components/Badge/Badge';
|
|
3
|
+
export { Icon } from './components/Icon/Icon';
|
|
4
|
+
export { Toggle } from './components/Toggle/Toggle';
|
|
5
|
+
export type { ToggleProps } from './components/Toggle/Toggle';
|
|
6
|
+
export { SegmentedControl } from './components/SegmentedControl/SegmentedControl';
|
|
7
|
+
export type {
|
|
8
|
+
SegmentedControlProps,
|
|
9
|
+
SegmentedOption,
|
|
10
|
+
} from './components/SegmentedControl/SegmentedControl';
|
|
11
|
+
export { Tabs } from './components/Tabs/Tabs';
|
|
12
|
+
export type { TabsProps, TabItem } from './components/Tabs/Tabs';
|
|
13
|
+
export { Select } from './components/Select/Select';
|
|
14
|
+
export type { SelectProps } from './components/Select/Select';
|
|
15
|
+
export { SystemBanner } from './components/SystemBanner/SystemBanner';
|
|
16
|
+
export type { SystemBannerProps, BannerVariant } from './components/SystemBanner/SystemBanner';
|
|
17
|
+
export { Popover } from './components/Popover/Popover';
|
|
18
|
+
export type { PopoverProps } from './components/Popover/Popover';
|
|
19
|
+
export type { IconProps, IconName } from './components/Icon/Icon';
|
|
20
|
+
export { Button } from './components/Button/Button';
|
|
21
|
+
export type { ButtonProps, ButtonVariant, ButtonSize } from './components/Button/Button';
|
|
22
|
+
export { Input } from './components/Input/Input';
|
|
23
|
+
export type { InputProps } from './components/Input/Input';
|
|
24
|
+
export { Dialog } from './components/Dialog/Dialog';
|
|
25
|
+
export type { DialogProps, DialogSize } from './components/Dialog/Dialog';
|
|
26
|
+
export { Drawer } from './components/Drawer/Drawer';
|
|
27
|
+
export type { DrawerProps } from './components/Drawer/Drawer';
|
|
28
|
+
export { ToastProvider, useToast } from './components/Toast/Toast';
|
|
29
|
+
export type { ToastItem, ToastContextValue } from './components/Toast/Toast';
|
|
30
|
+
export { Card } from './components/Card/Card';
|
|
31
|
+
export type { CardProps } from './components/Card/Card';
|
|
32
|
+
export { FormField } from './components/FormField/FormField';
|
|
33
|
+
export type { FormFieldProps } from './components/FormField/FormField';
|
|
34
|
+
export { PageHeader } from './components/PageHeader/PageHeader';
|
|
35
|
+
export type { PageHeaderProps } from './components/PageHeader/PageHeader';
|
|
36
|
+
export { EmptyState } from './components/EmptyState/EmptyState';
|
|
37
|
+
export type { EmptyStateProps } from './components/EmptyState/EmptyState';
|
|
38
|
+
export { Spinner } from './components/Spinner/Spinner';
|
|
39
|
+
export type { SpinnerProps } from './components/Spinner/Spinner';
|
|
40
|
+
export { Avatar } from './components/Avatar/Avatar';
|
|
41
|
+
export type { AvatarProps } from './components/Avatar/Avatar';
|
|
42
|
+
export { NavTabs } from './components/NavTabs/NavTabs';
|
|
43
|
+
export type { NavTabsProps, NavTabItem } from './components/NavTabs/NavTabs';
|
|
44
|
+
export { Tooltip } from './components/Tooltip/Tooltip';
|
|
45
|
+
export type { TooltipProps } from './components/Tooltip/Tooltip';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { Avatar } from '../components/Avatar/Avatar';
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Components/Avatar',
|
|
6
|
+
component: Avatar,
|
|
7
|
+
parameters: { layout: 'centered' },
|
|
8
|
+
} satisfies Meta<typeof Avatar>;
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof meta>;
|
|
12
|
+
|
|
13
|
+
export const Initials: Story = {
|
|
14
|
+
args: { name: 'Jane Smith' },
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const SingleName: Story = {
|
|
18
|
+
args: { name: 'Admin' },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const WithImage: Story = {
|
|
22
|
+
args: {
|
|
23
|
+
name: 'Jane Smith',
|
|
24
|
+
src: 'https://i.pravatar.cc/150?u=jane',
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const BrokenImage: Story = {
|
|
29
|
+
args: {
|
|
30
|
+
name: 'Jane Smith',
|
|
31
|
+
src: 'https://broken.invalid/avatar.png',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const AllSizes: Story = {
|
|
36
|
+
args: { name: 'Jane Smith' },
|
|
37
|
+
render: () => (
|
|
38
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
39
|
+
<Avatar name="Jane Smith" size="sm" />
|
|
40
|
+
<Avatar name="Jane Smith" size="md" />
|
|
41
|
+
<Avatar name="Jane Smith" size="lg" />
|
|
42
|
+
</div>
|
|
43
|
+
),
|
|
44
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { Card } from '../components/Card/Card';
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Components/Card',
|
|
6
|
+
component: Card,
|
|
7
|
+
parameters: { layout: 'padded' },
|
|
8
|
+
args: { children: 'Card content goes here.' },
|
|
9
|
+
} satisfies Meta<typeof Card>;
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof meta>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {};
|
|
15
|
+
|
|
16
|
+
export const SmallPadding: Story = { args: { padding: 'sm' } };
|
|
17
|
+
export const LargePadding: Story = { args: { padding: 'lg' } };
|
|
18
|
+
|
|
19
|
+
export const Interactive: Story = {
|
|
20
|
+
args: {
|
|
21
|
+
as: 'article',
|
|
22
|
+
interactive: true,
|
|
23
|
+
children: 'Click me — I have hover + focus styles.',
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const AsListItem: Story = {
|
|
28
|
+
render: () => (
|
|
29
|
+
<ul style={{ listStyle: 'none', padding: 0, display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
30
|
+
<Card as="li">First item</Card>
|
|
31
|
+
<Card as="li">Second item</Card>
|
|
32
|
+
<Card as="li">Third item</Card>
|
|
33
|
+
</ul>
|
|
34
|
+
),
|
|
35
|
+
};
|