tharaday 0.8.2 → 0.8.3
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/dist/{src/components → components}/Accordion/Accordion.d.ts +0 -1
- package/dist/{src/components → components}/Avatar/Avatar.d.ts +0 -1
- package/dist/{src/components → components}/Badge/Badge.d.ts +0 -1
- package/dist/{src/components → components}/Box/Box.d.ts +0 -1
- package/dist/{src/components → components}/Box/Box.types.d.ts +11 -11
- package/dist/{src/components → components}/Box/helpers/getSpacingStyles.d.ts +2 -2
- package/dist/{src/components → components}/Breadcrumbs/Breadcrumbs.d.ts +0 -1
- package/dist/{src/components → components}/Button/Button.d.ts +0 -1
- package/dist/{src/components → components}/Card/Card.d.ts +0 -1
- package/dist/{src/components → components}/Checkbox/Checkbox.d.ts +0 -1
- package/dist/components/DatePicker/DatePicker.d.ts +1 -0
- package/dist/{src/components → components}/Divider/Divider.d.ts +0 -1
- package/dist/components/Drawer/Drawer.d.ts +1 -0
- package/dist/{src/components → components}/Dropdown/Dropdown.d.ts +0 -1
- package/dist/components/EmptyState/EmptyState.d.ts +1 -0
- package/dist/{src/components → components}/Header/Header.d.ts +0 -1
- package/dist/{src/components → components}/Input/Input.d.ts +0 -1
- package/dist/{src/components → components}/List/List.d.ts +2 -2
- package/dist/{src/components → components}/List/List.types.d.ts +3 -3
- package/dist/{src/components → components}/List/ListItem.d.ts +1 -1
- package/dist/{src/components → components}/List/ListItem.types.d.ts +1 -1
- package/dist/{src/components → components}/Loader/Loader.d.ts +0 -1
- package/dist/{src/components → components}/Modal/Modal.d.ts +1 -2
- package/dist/{src/components → components}/NavBar/NavBar.d.ts +0 -1
- package/dist/{src/components → components}/Notification/Notification.d.ts +0 -1
- package/dist/{src/components → components}/Pagination/Pagination.d.ts +0 -1
- package/dist/components/Popover/Popover.d.ts +1 -0
- package/dist/{src/components → components}/ProgressBar/ProgressBar.d.ts +0 -1
- package/dist/{src/components → components}/RadioButton/RadioButton.d.ts +0 -1
- package/dist/{src/components → components}/Select/Select.d.ts +0 -1
- package/dist/{src/components → components}/Skeleton/Skeleton.d.ts +0 -1
- package/dist/{src/components → components}/Slider/Slider.d.ts +0 -1
- package/dist/{src/components → components}/Stepper/Step.d.ts +0 -1
- package/dist/{src/components → components}/Stepper/Stepper.d.ts +0 -1
- package/dist/{src/components → components}/Stepper/stepper.utils.d.ts +2 -2
- package/dist/{src/components → components}/Switch/Switch.d.ts +0 -1
- package/dist/{src/components → components}/Table/Table.d.ts +0 -1
- package/dist/{src/components → components}/Tabs/Tabs.d.ts +0 -1
- package/dist/components/Tag/Tag.d.ts +1 -0
- package/dist/{src/components → components}/Text/Text.d.ts +0 -1
- package/dist/{src/components → components}/Textarea/Textarea.d.ts +0 -1
- package/dist/{src/components → components}/Tooltip/Tooltip.d.ts +0 -1
- package/dist/{src/components → components}/Tree/Tree.d.ts +2 -2
- package/dist/{src/components → components}/Tree/Tree.types.d.ts +1 -1
- package/dist/{src/components → components}/Tree/TreeItem.d.ts +1 -1
- package/dist/{src/components → components}/Tree/TreeItem.types.d.ts +1 -1
- package/dist/ds.css +1 -1
- package/dist/ds.js +1294 -1149
- package/dist/ds.umd.cjs +1 -1
- package/dist/hooks/useClickOutside.d.ts +6 -0
- package/dist/{src/hooks → hooks}/useComponentId.d.ts +1 -1
- package/dist/hooks/useFocusTrap.d.ts +17 -0
- package/dist/{src/index.d.ts → index.d.ts} +10 -0
- package/dist/{src/layouts → layouts}/AppLayout/AppLayout.d.ts +0 -1
- package/dist/{src/layouts → layouts}/AuthLayout/AuthLayout.d.ts +0 -1
- package/dist/{src/layouts → layouts}/DashboardLayout/DashboardLayout.d.ts +0 -1
- package/dist/{src/layouts → layouts}/SettingsLayout/SettingsLayout.d.ts +0 -1
- package/package.json +11 -10
- package/src/components/Box/Box.module.css +0 -557
- package/src/components/Box/Box.test.tsx +4 -4
- package/src/components/Box/Box.tsx +8 -16
- package/src/components/Box/helpers/getSpacingStyles.ts +23 -17
- package/src/components/DatePicker/DatePicker.module.css +212 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +53 -0
- package/src/components/DatePicker/DatePicker.test.tsx +61 -0
- package/src/components/DatePicker/DatePicker.tsx +269 -0
- package/src/components/DatePicker/DatePicker.types.ts +11 -0
- package/src/components/Drawer/Drawer.module.css +126 -0
- package/src/components/Drawer/Drawer.stories.tsx +70 -0
- package/src/components/Drawer/Drawer.test.tsx +49 -0
- package/src/components/Drawer/Drawer.tsx +77 -0
- package/src/components/Drawer/Drawer.types.ts +17 -0
- package/src/components/EmptyState/EmptyState.module.css +73 -0
- package/src/components/EmptyState/EmptyState.stories.tsx +65 -0
- package/src/components/EmptyState/EmptyState.test.tsx +30 -0
- package/src/components/EmptyState/EmptyState.tsx +29 -0
- package/src/components/EmptyState/EmptyState.types.ts +12 -0
- package/src/components/Header/Header.test.tsx +5 -5
- package/src/components/Modal/Modal.tsx +2 -62
- package/src/components/Popover/Popover.module.css +52 -0
- package/src/components/Popover/Popover.stories.tsx +67 -0
- package/src/components/Popover/Popover.test.tsx +40 -0
- package/src/components/Popover/Popover.tsx +78 -0
- package/src/components/Popover/Popover.types.ts +13 -0
- package/src/components/Tag/Tag.module.css +115 -0
- package/src/components/Tag/Tag.stories.tsx +61 -0
- package/src/components/Tag/Tag.test.tsx +42 -0
- package/src/components/Tag/Tag.tsx +74 -0
- package/src/components/Tag/Tag.types.ts +15 -0
- package/src/components/Text/Text.module.css +0 -521
- package/src/components/Text/Text.test.tsx +4 -4
- package/src/components/Text/Text.tsx +0 -14
- package/src/components/Tooltip/Tooltip.module.css +1 -1
- package/src/components/Tooltip/Tooltip.test.tsx +5 -5
- package/src/components/Tooltip/Tooltip.tsx +2 -6
- package/src/hooks/useClickOutside.test.tsx +68 -0
- package/src/hooks/useClickOutside.ts +35 -0
- package/src/hooks/useFocusTrap.test.tsx +95 -0
- package/src/hooks/useFocusTrap.ts +88 -0
- package/src/index.ts +10 -0
- package/src/styles/themes.browser.test.ts +75 -0
- package/vite.config.ts +1 -1
- package/dist/src/components/Accordion/Accordion.stories.d.ts +0 -14
- package/dist/src/components/Accordion/Accordion.types.d.ts +0 -18
- package/dist/src/components/Avatar/Avatar.stories.d.ts +0 -14
- package/dist/src/components/Avatar/Avatar.types.d.ts +0 -10
- package/dist/src/components/Badge/Badge.stories.d.ts +0 -33
- package/dist/src/components/Badge/Badge.types.d.ts +0 -10
- package/dist/src/components/Box/Box.stories.d.ts +0 -38
- package/dist/src/components/Breadcrumbs/Breadcrumbs.stories.d.ts +0 -13
- package/dist/src/components/Breadcrumbs/Breadcrumbs.types.d.ts +0 -11
- package/dist/src/components/Button/Button.stories.d.ts +0 -22
- package/dist/src/components/Button/Button.types.d.ts +0 -12
- package/dist/src/components/Card/Card.stories.d.ts +0 -27
- package/dist/src/components/Card/Card.types.d.ts +0 -16
- package/dist/src/components/Checkbox/Checkbox.stories.d.ts +0 -17
- package/dist/src/components/Checkbox/Checkbox.types.d.ts +0 -7
- package/dist/src/components/Divider/Divider.stories.d.ts +0 -15
- package/dist/src/components/Divider/Divider.types.d.ts +0 -10
- package/dist/src/components/Dropdown/Dropdown.stories.d.ts +0 -12
- package/dist/src/components/Dropdown/Dropdown.types.d.ts +0 -24
- package/dist/src/components/Header/Header.stories.d.ts +0 -20
- package/dist/src/components/Header/Header.types.d.ts +0 -16
- package/dist/src/components/Input/Input.stories.d.ts +0 -32
- package/dist/src/components/Input/Input.types.d.ts +0 -10
- package/dist/src/components/List/List.stories.d.ts +0 -25
- package/dist/src/components/Loader/Loader.stories.d.ts +0 -25
- package/dist/src/components/Loader/Loader.types.d.ts +0 -8
- package/dist/src/components/Modal/Modal.stories.d.ts +0 -28
- package/dist/src/components/Modal/Modal.types.d.ts +0 -12
- package/dist/src/components/NavBar/NavBar.stories.d.ts +0 -8
- package/dist/src/components/NavBar/NavBar.types.d.ts +0 -38
- package/dist/src/components/Notification/Notification.stories.d.ts +0 -26
- package/dist/src/components/Notification/Notification.types.d.ts +0 -9
- package/dist/src/components/Pagination/Pagination.stories.d.ts +0 -21
- package/dist/src/components/Pagination/Pagination.types.d.ts +0 -34
- package/dist/src/components/ProgressBar/ProgressBar.stories.d.ts +0 -32
- package/dist/src/components/ProgressBar/ProgressBar.types.d.ts +0 -12
- package/dist/src/components/RadioButton/RadioButton.stories.d.ts +0 -30
- package/dist/src/components/RadioButton/RadioButton.types.d.ts +0 -9
- package/dist/src/components/Select/Select.stories.d.ts +0 -32
- package/dist/src/components/Select/Select.types.d.ts +0 -23
- package/dist/src/components/Skeleton/Skeleton.stories.d.ts +0 -15
- package/dist/src/components/Skeleton/Skeleton.types.d.ts +0 -9
- package/dist/src/components/Slider/Slider.stories.d.ts +0 -23
- package/dist/src/components/Slider/Slider.types.d.ts +0 -15
- package/dist/src/components/Stepper/Step.types.d.ts +0 -18
- package/dist/src/components/Stepper/Stepper.stories.d.ts +0 -15
- package/dist/src/components/Stepper/Stepper.types.d.ts +0 -14
- package/dist/src/components/Switch/Switch.stories.d.ts +0 -16
- package/dist/src/components/Switch/Switch.types.d.ts +0 -6
- package/dist/src/components/Table/Table.stories.d.ts +0 -27
- package/dist/src/components/Table/Table.types.d.ts +0 -19
- package/dist/src/components/Tabs/Tabs.stories.d.ts +0 -19
- package/dist/src/components/Tabs/Tabs.types.d.ts +0 -16
- package/dist/src/components/Text/Text.stories.d.ts +0 -78
- package/dist/src/components/Text/Text.types.d.ts +0 -52
- package/dist/src/components/Textarea/Textarea.stories.d.ts +0 -32
- package/dist/src/components/Textarea/Textarea.types.d.ts +0 -11
- package/dist/src/components/Tooltip/Tooltip.stories.d.ts +0 -10
- package/dist/src/components/Tooltip/Tooltip.types.d.ts +0 -12
- package/dist/src/components/Tree/Tree.stories.d.ts +0 -27
- package/dist/src/layouts/AppLayout/AppLayout.stories.d.ts +0 -13
- package/dist/src/layouts/AppLayout/AppLayout.types.d.ts +0 -13
- package/dist/src/layouts/AuthLayout/AuthLayout.stories.d.ts +0 -12
- package/dist/src/layouts/AuthLayout/AuthLayout.types.d.ts +0 -8
- package/dist/src/layouts/DashboardLayout/DashboardLayout.stories.d.ts +0 -11
- package/dist/src/layouts/DashboardLayout/DashboardLayout.types.d.ts +0 -10
- package/dist/src/layouts/SettingsLayout/SettingsLayout.stories.d.ts +0 -11
- package/dist/src/layouts/SettingsLayout/SettingsLayout.types.d.ts +0 -9
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useEffect, type RefObject } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Calls onClose when the user clicks outside the referenced element
|
|
5
|
+
* or presses Escape. Only active when enabled is true.
|
|
6
|
+
*/
|
|
7
|
+
export const useClickOutside = <T extends HTMLElement>(
|
|
8
|
+
ref: RefObject<T | null>,
|
|
9
|
+
onClose: () => void,
|
|
10
|
+
enabled: boolean
|
|
11
|
+
): void => {
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!enabled) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const handleClickOutside = (e: MouseEvent) => {
|
|
18
|
+
if (ref.current && !ref.current.contains(e.target as Node)) {
|
|
19
|
+
onClose();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
23
|
+
if (e.key === 'Escape') {
|
|
24
|
+
onClose();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
29
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
30
|
+
return () => {
|
|
31
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
32
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
33
|
+
};
|
|
34
|
+
}, [ref, onClose, enabled]);
|
|
35
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
|
|
4
|
+
import { useFocusTrap } from './useFocusTrap.ts';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
isLoading?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const TestComponent = ({ isOpen, onClose, isLoading }: Props) => {
|
|
13
|
+
const ref = useFocusTrap<HTMLDivElement>({ isOpen, onClose, isLoading });
|
|
14
|
+
if (!isOpen) {
|
|
15
|
+
return <button data-testid="trigger">Open</button>;
|
|
16
|
+
}
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<button data-testid="trigger">Open</button>
|
|
20
|
+
<div ref={ref} data-testid="trap">
|
|
21
|
+
<button data-testid="first">First</button>
|
|
22
|
+
<button data-testid="second">Second</button>
|
|
23
|
+
<button data-testid="last">Last</button>
|
|
24
|
+
</div>
|
|
25
|
+
</>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
describe('useFocusTrap', () => {
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
vi.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => {
|
|
32
|
+
cb(0);
|
|
33
|
+
return 0;
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
vi.restoreAllMocks();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('locks body scroll when open', () => {
|
|
42
|
+
render(<TestComponent isOpen onClose={vi.fn()} />);
|
|
43
|
+
expect(document.body.style.overflow).toBe('hidden');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('unlocks body scroll when closed', () => {
|
|
47
|
+
const { rerender } = render(<TestComponent isOpen onClose={vi.fn()} />);
|
|
48
|
+
rerender(<TestComponent isOpen={false} onClose={vi.fn()} />);
|
|
49
|
+
expect(document.body.style.overflow).toBe('unset');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('moves focus to the first focusable element when opened', () => {
|
|
53
|
+
render(<TestComponent isOpen onClose={vi.fn()} />);
|
|
54
|
+
expect(document.activeElement).toBe(screen.getByTestId('first'));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('calls onClose when Escape is pressed', async () => {
|
|
58
|
+
const onClose = vi.fn();
|
|
59
|
+
render(<TestComponent isOpen onClose={onClose} />);
|
|
60
|
+
await userEvent.keyboard('{Escape}');
|
|
61
|
+
expect(onClose).toHaveBeenCalledOnce();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('does not call onClose on Escape when isLoading', async () => {
|
|
65
|
+
const onClose = vi.fn();
|
|
66
|
+
render(<TestComponent isOpen isLoading onClose={onClose} />);
|
|
67
|
+
await userEvent.keyboard('{Escape}');
|
|
68
|
+
expect(onClose).not.toHaveBeenCalled();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('cycles Tab from last to first focusable element', async () => {
|
|
72
|
+
render(<TestComponent isOpen onClose={vi.fn()} />);
|
|
73
|
+
screen.getByTestId('last').focus();
|
|
74
|
+
await userEvent.keyboard('{Tab}');
|
|
75
|
+
expect(document.activeElement).toBe(screen.getByTestId('first'));
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('cycles Shift+Tab from first to last focusable element', async () => {
|
|
79
|
+
render(<TestComponent isOpen onClose={vi.fn()} />);
|
|
80
|
+
screen.getByTestId('first').focus();
|
|
81
|
+
await userEvent.keyboard('{Shift>}{Tab}{/Shift}');
|
|
82
|
+
expect(document.activeElement).toBe(screen.getByTestId('last'));
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('restores focus to the previously focused element on close', () => {
|
|
86
|
+
const { rerender } = render(<TestComponent isOpen={false} onClose={vi.fn()} />);
|
|
87
|
+
screen.getByTestId('trigger').focus();
|
|
88
|
+
expect(document.activeElement).toBe(screen.getByTestId('trigger'));
|
|
89
|
+
|
|
90
|
+
rerender(<TestComponent isOpen onClose={vi.fn()} />);
|
|
91
|
+
rerender(<TestComponent isOpen={false} onClose={vi.fn()} />);
|
|
92
|
+
|
|
93
|
+
expect(document.activeElement).toBe(screen.getByTestId('trigger'));
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
const FOCUSABLE_SELECTORS =
|
|
4
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
|
|
5
|
+
|
|
6
|
+
interface UseFocusTrapOptions {
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
isLoading?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Traps keyboard focus inside a container while it is open.
|
|
14
|
+
*
|
|
15
|
+
* - Locks body scroll and moves focus to the first focusable element on open
|
|
16
|
+
* - Cycles Tab / Shift+Tab within the container
|
|
17
|
+
* - Closes on Escape (unless isLoading)
|
|
18
|
+
* - Restores focus to the previously focused element on close
|
|
19
|
+
*
|
|
20
|
+
* Returns a ref that must be attached to the container element.
|
|
21
|
+
*/
|
|
22
|
+
export const useFocusTrap = <T extends HTMLElement>({
|
|
23
|
+
isOpen,
|
|
24
|
+
onClose,
|
|
25
|
+
isLoading = false,
|
|
26
|
+
}: UseFocusTrapOptions) => {
|
|
27
|
+
const containerRef = useRef<T>(null);
|
|
28
|
+
const previousFocus = useRef<HTMLElement | null>(null);
|
|
29
|
+
const onCloseRef = useRef(onClose);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
onCloseRef.current = onClose;
|
|
33
|
+
}, [onClose]);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
37
|
+
if (event.key === 'Escape' && !isLoading) {
|
|
38
|
+
onCloseRef.current();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (event.key === 'Tab' && containerRef.current) {
|
|
43
|
+
const focusableElements =
|
|
44
|
+
containerRef.current.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS);
|
|
45
|
+
const firstElement = focusableElements[0];
|
|
46
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
47
|
+
|
|
48
|
+
if (!firstElement || !lastElement) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (event.shiftKey) {
|
|
53
|
+
if (document.activeElement === firstElement) {
|
|
54
|
+
lastElement.focus();
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
if (document.activeElement === lastElement) {
|
|
59
|
+
firstElement.focus();
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
if (isOpen) {
|
|
67
|
+
previousFocus.current = document.activeElement as HTMLElement;
|
|
68
|
+
document.body.style.overflow = 'hidden';
|
|
69
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
70
|
+
|
|
71
|
+
requestAnimationFrame(() => {
|
|
72
|
+
const focusableElements =
|
|
73
|
+
containerRef.current?.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS);
|
|
74
|
+
if (focusableElements && focusableElements.length > 0) {
|
|
75
|
+
focusableElements[0].focus();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return () => {
|
|
81
|
+
document.body.style.overflow = 'unset';
|
|
82
|
+
window.removeEventListener('keydown', handleKeyDown);
|
|
83
|
+
previousFocus.current?.focus();
|
|
84
|
+
};
|
|
85
|
+
}, [isOpen, isLoading]);
|
|
86
|
+
|
|
87
|
+
return containerRef;
|
|
88
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -44,6 +44,8 @@ export type {
|
|
|
44
44
|
} from './components/Card/Card.types.ts';
|
|
45
45
|
export { Checkbox } from './components/Checkbox/Checkbox.tsx';
|
|
46
46
|
export type { CheckboxProps } from './components/Checkbox/Checkbox.types.ts';
|
|
47
|
+
export { DatePicker } from './components/DatePicker/DatePicker.tsx';
|
|
48
|
+
export type { DatePickerProps } from './components/DatePicker/DatePicker.types.ts';
|
|
47
49
|
export { Dropdown } from './components/Dropdown/Dropdown.tsx';
|
|
48
50
|
export type {
|
|
49
51
|
DropdownProps,
|
|
@@ -52,6 +54,10 @@ export type {
|
|
|
52
54
|
} from './components/Dropdown/Dropdown.types.ts';
|
|
53
55
|
export { Divider } from './components/Divider/Divider.tsx';
|
|
54
56
|
export type { DividerProps, DividerOrientation } from './components/Divider/Divider.types.ts';
|
|
57
|
+
export { Drawer } from './components/Drawer/Drawer.tsx';
|
|
58
|
+
export type { DrawerProps, DrawerPlacement, DrawerSize } from './components/Drawer/Drawer.types.ts';
|
|
59
|
+
export { EmptyState } from './components/EmptyState/EmptyState.tsx';
|
|
60
|
+
export type { EmptyStateProps, EmptyStateSize } from './components/EmptyState/EmptyState.types.ts';
|
|
55
61
|
export { Header } from './components/Header/Header.tsx';
|
|
56
62
|
export type { HeaderProps } from './components/Header/Header.types.ts';
|
|
57
63
|
export { NavBar } from './components/NavBar/NavBar.tsx';
|
|
@@ -73,6 +79,8 @@ export type {
|
|
|
73
79
|
PaginationProps,
|
|
74
80
|
PaginationLabels,
|
|
75
81
|
} from './components/Pagination/Pagination.types.ts';
|
|
82
|
+
export { Popover } from './components/Popover/Popover.tsx';
|
|
83
|
+
export type { PopoverProps, PopoverPlacement } from './components/Popover/Popover.types.ts';
|
|
76
84
|
export { ProgressBar } from './components/ProgressBar/ProgressBar.tsx';
|
|
77
85
|
export type {
|
|
78
86
|
ProgressBarProps,
|
|
@@ -96,6 +104,8 @@ export { Select } from './components/Select/Select.tsx';
|
|
|
96
104
|
export type { SelectProps, SelectOption, SelectSize } from './components/Select/Select.types.ts';
|
|
97
105
|
export { Switch } from './components/Switch/Switch.tsx';
|
|
98
106
|
export type { SwitchProps } from './components/Switch/Switch.types.ts';
|
|
107
|
+
export { Tag } from './components/Tag/Tag.tsx';
|
|
108
|
+
export type { TagProps, TagIntent, TagSize } from './components/Tag/Tag.types.ts';
|
|
99
109
|
export {
|
|
100
110
|
Table,
|
|
101
111
|
TableBody,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import './ds.css';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolves a CSS custom property to its final computed color by applying it
|
|
5
|
+
* as a background-color on a temporary element in the current theme context.
|
|
6
|
+
*/
|
|
7
|
+
function resolveColorToken(token: string): string {
|
|
8
|
+
const el = document.createElement('div');
|
|
9
|
+
el.style.backgroundColor = `var(${token})`;
|
|
10
|
+
document.body.appendChild(el);
|
|
11
|
+
const color = getComputedStyle(el).backgroundColor;
|
|
12
|
+
el.remove();
|
|
13
|
+
return color;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
document.documentElement.removeAttribute('data-theme');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('light theme (default)', () => {
|
|
21
|
+
it('surface-0 is white', () => {
|
|
22
|
+
expect(resolveColorToken('--ds-surface-0')).toBe('rgb(255, 255, 255)');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('surface-1 is off-white', () => {
|
|
26
|
+
expect(resolveColorToken('--ds-surface-1')).toBe('rgb(250, 250, 250)');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('text-1 is near-black', () => {
|
|
30
|
+
expect(resolveColorToken('--ds-text-1')).toBe('rgb(36, 36, 41)');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('border-1 is light grey', () => {
|
|
34
|
+
expect(resolveColorToken('--ds-border-1')).toBe('rgb(238, 238, 238)');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('dark theme', () => {
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
document.documentElement.setAttribute('data-theme', 'dark');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('surface-0 is near-black', () => {
|
|
44
|
+
expect(resolveColorToken('--ds-surface-0')).toBe('rgb(9, 9, 11)');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('surface-1 is dark grey', () => {
|
|
48
|
+
expect(resolveColorToken('--ds-surface-1')).toBe('rgb(36, 36, 41)');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('text-1 is white', () => {
|
|
52
|
+
expect(resolveColorToken('--ds-text-1')).toBe('rgb(255, 255, 255)');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('border-1 is dark grey', () => {
|
|
56
|
+
expect(resolveColorToken('--ds-border-1')).toBe('rgb(50, 50, 55)');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('theme contrast — light vs dark', () => {
|
|
61
|
+
it('surface-0 differs between themes', () => {
|
|
62
|
+
const light = resolveColorToken('--ds-surface-0');
|
|
63
|
+
document.documentElement.setAttribute('data-theme', 'dark');
|
|
64
|
+
const dark = resolveColorToken('--ds-surface-0');
|
|
65
|
+
expect(light).not.toBe(dark);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('text-1 inverts between themes', () => {
|
|
69
|
+
const lightText = resolveColorToken('--ds-text-1');
|
|
70
|
+
document.documentElement.setAttribute('data-theme', 'dark');
|
|
71
|
+
const darkText = resolveColorToken('--ds-text-1');
|
|
72
|
+
// light text-1 (neutral-900) becomes dark surface-0 (neutral-950) equivalent
|
|
73
|
+
expect(lightText).not.toBe(darkText);
|
|
74
|
+
});
|
|
75
|
+
});
|
package/vite.config.ts
CHANGED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import { Accordion } from './Accordion.tsx';
|
|
3
|
-
declare const meta: Meta<typeof Accordion>;
|
|
4
|
-
export default meta;
|
|
5
|
-
type Story = StoryObj<typeof Accordion>;
|
|
6
|
-
export declare const Default: Story;
|
|
7
|
-
export declare const Ghost: Story;
|
|
8
|
-
export declare const Bordered: Story;
|
|
9
|
-
export declare const Separated: Story;
|
|
10
|
-
export declare const Small: Story;
|
|
11
|
-
export declare const Large: Story;
|
|
12
|
-
export declare const AllowMultiple: Story;
|
|
13
|
-
export declare const DefaultExpanded: Story;
|
|
14
|
-
export declare const ComplexContent: Story;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
|
-
export interface AccordionItem {
|
|
3
|
-
id: string;
|
|
4
|
-
title: ReactNode;
|
|
5
|
-
content: ReactNode;
|
|
6
|
-
isDisabled?: boolean;
|
|
7
|
-
}
|
|
8
|
-
export type AccordionVariant = 'default' | 'ghost' | 'separated' | 'bordered';
|
|
9
|
-
export type AccordionSize = 'sm' | 'md' | 'lg';
|
|
10
|
-
export interface AccordionProps {
|
|
11
|
-
items: AccordionItem[];
|
|
12
|
-
allowMultiple?: boolean;
|
|
13
|
-
defaultExpanded?: string[];
|
|
14
|
-
variant?: AccordionVariant;
|
|
15
|
-
size?: AccordionSize;
|
|
16
|
-
className?: string;
|
|
17
|
-
id?: string;
|
|
18
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
-
declare const meta: {
|
|
3
|
-
title: string;
|
|
4
|
-
component: ({ src, alt, name, size, fallback, className, ...props }: import('./Avatar.types.ts').AvatarProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
tags: string[];
|
|
6
|
-
parameters: {
|
|
7
|
-
layout: string;
|
|
8
|
-
};
|
|
9
|
-
};
|
|
10
|
-
export default meta;
|
|
11
|
-
type Story = StoryObj<typeof meta>;
|
|
12
|
-
export declare const Default: Story;
|
|
13
|
-
export declare const WithImage: Story;
|
|
14
|
-
export declare const Sizes: Story;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { HTMLAttributes } from 'react';
|
|
2
|
-
export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
3
|
-
export interface AvatarProps extends HTMLAttributes<HTMLDivElement> {
|
|
4
|
-
src?: string;
|
|
5
|
-
alt?: string;
|
|
6
|
-
name?: string;
|
|
7
|
-
size?: AvatarSize;
|
|
8
|
-
fallback?: string;
|
|
9
|
-
className?: string;
|
|
10
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
-
declare const meta: {
|
|
3
|
-
title: string;
|
|
4
|
-
component: ({ variant, size, intent, className, children, ...props }: import('./Badge.types.ts').BadgeProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
tags: string[];
|
|
6
|
-
parameters: {
|
|
7
|
-
layout: string;
|
|
8
|
-
};
|
|
9
|
-
argTypes: {
|
|
10
|
-
intent: {
|
|
11
|
-
control: "select";
|
|
12
|
-
options: string[];
|
|
13
|
-
};
|
|
14
|
-
variant: {
|
|
15
|
-
control: "select";
|
|
16
|
-
options: string[];
|
|
17
|
-
};
|
|
18
|
-
size: {
|
|
19
|
-
control: "select";
|
|
20
|
-
options: string[];
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
export default meta;
|
|
25
|
-
type Story = StoryObj<typeof meta>;
|
|
26
|
-
export declare const Solid: Story;
|
|
27
|
-
export declare const Subtle: Story;
|
|
28
|
-
export declare const Outline: Story;
|
|
29
|
-
export declare const Success: Story;
|
|
30
|
-
export declare const Warning: Story;
|
|
31
|
-
export declare const Danger: Story;
|
|
32
|
-
export declare const Info: Story;
|
|
33
|
-
export declare const All: Story;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { HTMLAttributes } from 'react';
|
|
2
|
-
export type BadgeVariant = 'solid' | 'subtle' | 'outline';
|
|
3
|
-
export type BadgeSize = 'sm' | 'md' | 'lg';
|
|
4
|
-
export type BadgeIntent = 'neutral' | 'success' | 'warning' | 'danger' | 'info';
|
|
5
|
-
export interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
|
|
6
|
-
className?: string;
|
|
7
|
-
variant?: BadgeVariant;
|
|
8
|
-
size?: BadgeSize;
|
|
9
|
-
intent?: BadgeIntent;
|
|
10
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
-
declare const meta: {
|
|
3
|
-
title: string;
|
|
4
|
-
component: ({ children, as: Component, display, padding, paddingX, paddingY, paddingTop, paddingBottom, paddingLeft, paddingRight, margin, marginX, marginY, marginTop, marginBottom, marginLeft, marginRight, gap, flexDirection, alignItems, justifyContent, flexWrap, flexGrow, flexShrink, flex, fullWidth, textAlign, width, height, maxWidth, gridTemplateColumns, backgroundColor, border, borderBottom, borderColor, borderRadius, className, style, ...props }: import('./Box.types.ts').BoxProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
tags: string[];
|
|
6
|
-
argTypes: {
|
|
7
|
-
display: {
|
|
8
|
-
control: "select";
|
|
9
|
-
options: string[];
|
|
10
|
-
};
|
|
11
|
-
padding: {
|
|
12
|
-
control: "select";
|
|
13
|
-
options: number[];
|
|
14
|
-
};
|
|
15
|
-
gap: {
|
|
16
|
-
control: "select";
|
|
17
|
-
options: number[];
|
|
18
|
-
};
|
|
19
|
-
flexDirection: {
|
|
20
|
-
control: "select";
|
|
21
|
-
options: string[];
|
|
22
|
-
};
|
|
23
|
-
alignItems: {
|
|
24
|
-
control: "select";
|
|
25
|
-
options: string[];
|
|
26
|
-
};
|
|
27
|
-
justifyContent: {
|
|
28
|
-
control: "select";
|
|
29
|
-
options: string[];
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
};
|
|
33
|
-
export default meta;
|
|
34
|
-
type Story = StoryObj<typeof meta>;
|
|
35
|
-
export declare const Default: Story;
|
|
36
|
-
export declare const FlexRow: Story;
|
|
37
|
-
export declare const FlexColumn: Story;
|
|
38
|
-
export declare const CustomElement: Story;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
-
declare const meta: {
|
|
3
|
-
title: string;
|
|
4
|
-
component: ({ children, separator, className, ...props }: import('./Breadcrumbs.types.ts').BreadcrumbsProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
tags: string[];
|
|
6
|
-
parameters: {
|
|
7
|
-
layout: string;
|
|
8
|
-
};
|
|
9
|
-
};
|
|
10
|
-
export default meta;
|
|
11
|
-
type Story = StoryObj<typeof meta>;
|
|
12
|
-
export declare const Default: Story;
|
|
13
|
-
export declare const CustomSeparator: Story;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { ReactNode, AnchorHTMLAttributes, HTMLAttributes } from 'react';
|
|
2
|
-
export interface BreadcrumbsProps extends HTMLAttributes<HTMLElement> {
|
|
3
|
-
children?: ReactNode;
|
|
4
|
-
separator?: ReactNode;
|
|
5
|
-
className?: string;
|
|
6
|
-
}
|
|
7
|
-
export interface BreadcrumbItemProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
8
|
-
href?: string;
|
|
9
|
-
isCurrent?: boolean;
|
|
10
|
-
children: ReactNode;
|
|
11
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
-
declare const meta: {
|
|
3
|
-
title: string;
|
|
4
|
-
component: ({ type, size, variant, intent, fullWidth, isLoading, className, children, disabled, ...props }: import('./Button.types.ts').ButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
parameters: {
|
|
6
|
-
layout: string;
|
|
7
|
-
};
|
|
8
|
-
tags: string[];
|
|
9
|
-
args: {
|
|
10
|
-
onClick: import('@vitest/spy').Mock<(...args: any[]) => any>;
|
|
11
|
-
};
|
|
12
|
-
};
|
|
13
|
-
export default meta;
|
|
14
|
-
type Story = StoryObj<typeof meta>;
|
|
15
|
-
export declare const Solid: Story;
|
|
16
|
-
export declare const Subtle: Story;
|
|
17
|
-
export declare const Outline: Story;
|
|
18
|
-
export declare const ExtraSmall: Story;
|
|
19
|
-
export declare const Small: Story;
|
|
20
|
-
export declare const Large: Story;
|
|
21
|
-
export declare const ExtraLarge: Story;
|
|
22
|
-
export declare const All: Story;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { ButtonHTMLAttributes } from 'react';
|
|
2
|
-
export type ButtonVariant = 'solid' | 'subtle' | 'outline';
|
|
3
|
-
export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
4
|
-
export type ButtonIntent = 'neutral' | 'success' | 'warning' | 'danger' | 'info';
|
|
5
|
-
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
6
|
-
variant?: ButtonVariant;
|
|
7
|
-
size?: ButtonSize;
|
|
8
|
-
intent?: ButtonIntent;
|
|
9
|
-
isLoading?: boolean;
|
|
10
|
-
fullWidth?: boolean;
|
|
11
|
-
className?: string;
|
|
12
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
-
declare const meta: {
|
|
3
|
-
title: string;
|
|
4
|
-
component: ({ padding, shadow, bordered, borderColor, backgroundColor, className, children, ...props }: import('./Card.types.ts').CardProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
tags: string[];
|
|
6
|
-
parameters: {
|
|
7
|
-
layout: string;
|
|
8
|
-
};
|
|
9
|
-
argTypes: {
|
|
10
|
-
padding: {
|
|
11
|
-
control: "select";
|
|
12
|
-
options: string[];
|
|
13
|
-
};
|
|
14
|
-
shadow: {
|
|
15
|
-
control: "select";
|
|
16
|
-
options: string[];
|
|
17
|
-
};
|
|
18
|
-
bordered: {
|
|
19
|
-
control: "boolean";
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
export default meta;
|
|
24
|
-
type Story = StoryObj<typeof meta>;
|
|
25
|
-
export declare const Default: Story;
|
|
26
|
-
export declare const Simple: Story;
|
|
27
|
-
export declare const BackgroundColor: Story;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
-
import { BoxBackgroundColor, BoxBorderColor } from '../Box/Box.types.ts';
|
|
3
|
-
export interface CardProps extends HTMLAttributes<HTMLDivElement> {
|
|
4
|
-
className?: string;
|
|
5
|
-
padding?: 'none' | 'sm' | 'md' | 'lg';
|
|
6
|
-
shadow?: 'none' | 'sm' | 'md';
|
|
7
|
-
bordered?: boolean;
|
|
8
|
-
borderColor?: BoxBorderColor;
|
|
9
|
-
backgroundColor?: BoxBackgroundColor;
|
|
10
|
-
}
|
|
11
|
-
export interface CardHeaderProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
12
|
-
title?: ReactNode;
|
|
13
|
-
subtitle?: ReactNode;
|
|
14
|
-
}
|
|
15
|
-
export type CardContentProps = HTMLAttributes<HTMLDivElement>;
|
|
16
|
-
export type CardFooterProps = HTMLAttributes<HTMLDivElement>;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
-
declare const meta: {
|
|
3
|
-
title: string;
|
|
4
|
-
component: ({ label, error, helperText, className, disabled, id, ...props }: import('./Checkbox.types.ts').CheckboxProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
tags: string[];
|
|
6
|
-
parameters: {
|
|
7
|
-
layout: string;
|
|
8
|
-
};
|
|
9
|
-
};
|
|
10
|
-
export default meta;
|
|
11
|
-
type Story = StoryObj<typeof meta>;
|
|
12
|
-
export declare const Default: Story;
|
|
13
|
-
export declare const Checked: Story;
|
|
14
|
-
export declare const WithHelperText: Story;
|
|
15
|
-
export declare const Error: Story;
|
|
16
|
-
export declare const Disabled: Story;
|
|
17
|
-
export declare const All: Story;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
-
declare const meta: {
|
|
3
|
-
title: string;
|
|
4
|
-
component: ({ orientation, thickness, length, spacing, inset, className, style, ...props }: import('./Divider.types.ts').DividerProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
tags: string[];
|
|
6
|
-
args: {
|
|
7
|
-
orientation: "horizontal";
|
|
8
|
-
};
|
|
9
|
-
};
|
|
10
|
-
export default meta;
|
|
11
|
-
type Story = StoryObj<typeof meta>;
|
|
12
|
-
export declare const Horizontal: Story;
|
|
13
|
-
export declare const Inset: Story;
|
|
14
|
-
export declare const Spacing: Story;
|
|
15
|
-
export declare const Vertical: Story;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { HTMLAttributes } from 'react';
|
|
2
|
-
export type DividerOrientation = 'horizontal' | 'vertical';
|
|
3
|
-
export interface DividerProps extends Omit<HTMLAttributes<HTMLHRElement>, 'color'> {
|
|
4
|
-
className?: string;
|
|
5
|
-
orientation?: DividerOrientation;
|
|
6
|
-
thickness?: number | string;
|
|
7
|
-
length?: number | string;
|
|
8
|
-
spacing?: number | string;
|
|
9
|
-
inset?: number | string;
|
|
10
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import { Dropdown } from './Dropdown.tsx';
|
|
3
|
-
declare const meta: Meta<typeof Dropdown>;
|
|
4
|
-
export default meta;
|
|
5
|
-
type Story = StoryObj<typeof Dropdown>;
|
|
6
|
-
export declare const Default: Story;
|
|
7
|
-
export declare const WithLabel: Story;
|
|
8
|
-
export declare const WithValue: Story;
|
|
9
|
-
export declare const Error: Story;
|
|
10
|
-
export declare const Disabled: Story;
|
|
11
|
-
export declare const Sizes: Story;
|
|
12
|
-
export declare const FullWidth: Story;
|