@douglasneuroinformatics/libui 2.5.3 → 2.6.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/README.md +1 -1
- package/dist/components/Form/NumberField/NumberFieldRadio.d.ts +1 -1
- package/dist/components/Form/NumberField/NumberFieldRadio.d.ts.map +1 -1
- package/dist/components/Form/NumberField/NumberFieldRadio.js +7 -4
- package/dist/components/Form/NumberField/NumberFieldSelect.d.ts +1 -1
- package/dist/components/Form/NumberField/NumberFieldSelect.d.ts.map +1 -1
- package/dist/components/Form/NumberField/NumberFieldSelect.js +6 -2
- package/dist/douglasneuroinformatics-libui-2.6.0.tgz +0 -0
- package/dist/i18n.js +1 -1
- package/package.json +16 -14
- package/src/components/Accordion/Accordion.spec.tsx +37 -0
- package/src/components/Accordion/Accordion.stories.tsx +35 -0
- package/src/components/ActionDropdown/ActionDropdown.stories.tsx +17 -0
- package/src/components/AlertDialog/AlertDialog.stories.tsx +35 -0
- package/src/components/ArrowToggle/ArrowToggle.spec.tsx +49 -0
- package/src/components/ArrowToggle/ArrowToggle.stories.tsx +27 -0
- package/src/components/Avatar/Avatar.spec.tsx +26 -0
- package/src/components/Avatar/Avatar.stories.tsx +20 -0
- package/src/components/Badge/Badge.spec.tsx +19 -0
- package/src/components/Badge/Badge.stories.tsx +13 -0
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +44 -0
- package/src/components/Button/Button.spec.tsx +27 -0
- package/src/components/Button/Button.stories.tsx +63 -0
- package/src/components/Card/Card.spec.tsx +19 -0
- package/src/components/Card/Card.stories.tsx +56 -0
- package/src/components/Checkbox/Checkbox.spec.tsx +28 -0
- package/src/components/Checkbox/Checkbox.stories.tsx +34 -0
- package/src/components/ClientTable/ClientTable.stories.tsx +126 -0
- package/src/components/Collapsible/Collapsible.stories.tsx +45 -0
- package/src/components/Command/Command.stories.tsx +55 -0
- package/src/components/ContextMenu/ContextMenu.stories.tsx +61 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +15 -0
- package/src/components/Dialog/Dialog.stories.tsx +44 -0
- package/src/components/Drawer/Drawer.stories.tsx +37 -0
- package/src/components/DropdownButton/DropdownButton.stories.tsx +13 -0
- package/src/components/DropdownMenu/DropdownMenu.stories.tsx +78 -0
- package/src/components/ErrorFallback/ErrorFallback.stories.tsx +9 -0
- package/src/components/Form/BooleanField/BooleanField.spec.tsx +35 -0
- package/src/components/Form/BooleanField/BooleanField.stories.tsx +49 -0
- package/src/components/Form/DateField/DateField.spec.tsx +99 -0
- package/src/components/Form/DateField/DateField.stories.tsx +28 -0
- package/src/components/Form/Form.stories.tsx +360 -0
- package/src/components/Form/Form.test.tsx +119 -0
- package/src/components/Form/NumberField/NumberField.stories.tsx +103 -0
- package/src/components/Form/NumberField/NumberFieldRadio.tsx +16 -8
- package/src/components/Form/NumberField/NumberFieldSelect.tsx +10 -6
- package/src/components/Form/SetField/SetField.stories.tsx +63 -0
- package/src/components/Form/StringField/StringField.stories.tsx +94 -0
- package/src/components/Heading/Heading.stories.tsx +37 -0
- package/src/components/HoverCard/HoverCard.stories.tsx +40 -0
- package/src/components/Input/Input.spec.tsx +23 -0
- package/src/components/Input/Input.stories.tsx +9 -0
- package/src/components/Label/Label.spec.tsx +17 -0
- package/src/components/Label/Label.stories.tsx +13 -0
- package/src/components/LanguageToggle/LanguageToggle.stories.tsx +17 -0
- package/src/components/LineGraph/LineGraph.stories.tsx +81 -0
- package/src/components/ListboxDropdown/ListboxDropdown.stories.tsx +44 -0
- package/src/components/MenuBar/MenuBar.stories.tsx +101 -0
- package/src/components/NotificationHub/NotificationHub.stories.tsx +41 -0
- package/src/components/Pagination/Pagination.stories.tsx +41 -0
- package/src/components/Popover/Popover.spec.tsx +24 -0
- package/src/components/Popover/Popover.stories.tsx +72 -0
- package/src/components/Progress/Progress.stories.tsx +24 -0
- package/src/components/RadioGroup/RadioGroup.spec.tsx +42 -0
- package/src/components/RadioGroup/RadioGroup.stories.tsx +35 -0
- package/src/components/Resizable/Resizable.stories.tsx +39 -0
- package/src/components/ScrollArea/ScrollArea.spec.tsx +19 -0
- package/src/components/ScrollArea/ScrollArea.stories.tsx +23 -0
- package/src/components/SearchBar/SearchBar.stories.tsx +11 -0
- package/src/components/Select/Select.stories.tsx +31 -0
- package/src/components/Separator/Separator.spec.tsx +19 -0
- package/src/components/Separator/Separator.stories.tsx +30 -0
- package/src/components/Sheet/Sheet.stories.tsx +49 -0
- package/src/components/Slider/Slider.stories.tsx +9 -0
- package/src/components/Spinner/Spinner.stories.tsx +14 -0
- package/src/components/SpinnerIcon/SpinnerIcon.stories.tsx +9 -0
- package/src/components/Switch/Switch.stories.tsx +21 -0
- package/src/components/Table/Table.stories.tsx +88 -0
- package/src/components/Tabs/Tabs.stories.tsx +70 -0
- package/src/components/TextArea/TextArea.spec.tsx +23 -0
- package/src/components/TextArea/TextArea.stories.tsx +13 -0
- package/src/components/ThemeToggle/ThemeToggle.stories.tsx +9 -0
- package/src/components/Tooltip/Tooltip.stories.tsx +44 -0
- package/src/hooks/useDownload.test.ts +66 -0
- package/src/hooks/useEventCallback.test.tsx +22 -0
- package/src/hooks/useEventListener.test.tsx +120 -0
- package/src/hooks/useInterval.test.ts +58 -0
- package/src/hooks/useIsomorphicLayoutEffect.test.ts +27 -0
- package/src/hooks/useMediaQuery.test.ts +33 -0
- package/src/hooks/useNotificationsStore.test.ts +30 -0
- package/src/hooks/useOnClickOutside.test.ts +59 -0
- package/src/hooks/useSessionStorage.test.ts +186 -0
- package/src/hooks/useTheme.test.ts +74 -0
- package/src/hooks/useWindowSize.test.ts +57 -0
- package/src/i18n.ts +1 -1
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
|
|
5
|
+
import { MenuBar } from './MenuBar.js';
|
|
6
|
+
|
|
7
|
+
type Story = StoryObj<typeof MenuBar>;
|
|
8
|
+
|
|
9
|
+
export default { component: MenuBar } as Meta<typeof MenuBar>;
|
|
10
|
+
|
|
11
|
+
export const Default: Story = {
|
|
12
|
+
args: {
|
|
13
|
+
children: (
|
|
14
|
+
<>
|
|
15
|
+
<MenuBar.Menu>
|
|
16
|
+
<MenuBar.Trigger>File</MenuBar.Trigger>
|
|
17
|
+
<MenuBar.Content>
|
|
18
|
+
<MenuBar.Item>
|
|
19
|
+
New Tab <MenuBar.Shortcut>⌘T</MenuBar.Shortcut>
|
|
20
|
+
</MenuBar.Item>
|
|
21
|
+
<MenuBar.Item>
|
|
22
|
+
New Window <MenuBar.Shortcut>⌘N</MenuBar.Shortcut>
|
|
23
|
+
</MenuBar.Item>
|
|
24
|
+
<MenuBar.Item disabled>New Incognito Window</MenuBar.Item>
|
|
25
|
+
<MenuBar.Separator />
|
|
26
|
+
<MenuBar.Sub>
|
|
27
|
+
<MenuBar.SubTrigger>Share</MenuBar.SubTrigger>
|
|
28
|
+
<MenuBar.SubContent>
|
|
29
|
+
<MenuBar.Item>Email link</MenuBar.Item>
|
|
30
|
+
<MenuBar.Item>Messages</MenuBar.Item>
|
|
31
|
+
<MenuBar.Item>Notes</MenuBar.Item>
|
|
32
|
+
</MenuBar.SubContent>
|
|
33
|
+
</MenuBar.Sub>
|
|
34
|
+
<MenuBar.Separator />
|
|
35
|
+
<MenuBar.Item>
|
|
36
|
+
Print... <MenuBar.Shortcut>⌘P</MenuBar.Shortcut>
|
|
37
|
+
</MenuBar.Item>
|
|
38
|
+
</MenuBar.Content>
|
|
39
|
+
</MenuBar.Menu>
|
|
40
|
+
<MenuBar.Menu>
|
|
41
|
+
<MenuBar.Trigger>Edit</MenuBar.Trigger>
|
|
42
|
+
<MenuBar.Content>
|
|
43
|
+
<MenuBar.Item>
|
|
44
|
+
Undo <MenuBar.Shortcut>⌘Z</MenuBar.Shortcut>
|
|
45
|
+
</MenuBar.Item>
|
|
46
|
+
<MenuBar.Item>
|
|
47
|
+
Redo <MenuBar.Shortcut>⇧⌘Z</MenuBar.Shortcut>
|
|
48
|
+
</MenuBar.Item>
|
|
49
|
+
<MenuBar.Separator />
|
|
50
|
+
<MenuBar.Sub>
|
|
51
|
+
<MenuBar.SubTrigger>Find</MenuBar.SubTrigger>
|
|
52
|
+
<MenuBar.SubContent>
|
|
53
|
+
<MenuBar.Item>Search the web</MenuBar.Item>
|
|
54
|
+
<MenuBar.Separator />
|
|
55
|
+
<MenuBar.Item>Find...</MenuBar.Item>
|
|
56
|
+
<MenuBar.Item>Find Next</MenuBar.Item>
|
|
57
|
+
<MenuBar.Item>Find Previous</MenuBar.Item>
|
|
58
|
+
</MenuBar.SubContent>
|
|
59
|
+
</MenuBar.Sub>
|
|
60
|
+
<MenuBar.Separator />
|
|
61
|
+
<MenuBar.Item>Cut</MenuBar.Item>
|
|
62
|
+
<MenuBar.Item>Copy</MenuBar.Item>
|
|
63
|
+
<MenuBar.Item>Paste</MenuBar.Item>
|
|
64
|
+
</MenuBar.Content>
|
|
65
|
+
</MenuBar.Menu>
|
|
66
|
+
<MenuBar.Menu>
|
|
67
|
+
<MenuBar.Trigger>View</MenuBar.Trigger>
|
|
68
|
+
<MenuBar.Content>
|
|
69
|
+
<MenuBar.CheckboxItem>Always Show Bookmarks Bar</MenuBar.CheckboxItem>
|
|
70
|
+
<MenuBar.CheckboxItem checked>Always Show Full URLs</MenuBar.CheckboxItem>
|
|
71
|
+
<MenuBar.Separator />
|
|
72
|
+
<MenuBar.Item inset>
|
|
73
|
+
Reload <MenuBar.Shortcut>⌘R</MenuBar.Shortcut>
|
|
74
|
+
</MenuBar.Item>
|
|
75
|
+
<MenuBar.Item disabled inset>
|
|
76
|
+
Force Reload <MenuBar.Shortcut>⇧⌘R</MenuBar.Shortcut>
|
|
77
|
+
</MenuBar.Item>
|
|
78
|
+
<MenuBar.Separator />
|
|
79
|
+
<MenuBar.Item inset>Toggle Fullscreen</MenuBar.Item>
|
|
80
|
+
<MenuBar.Separator />
|
|
81
|
+
<MenuBar.Item inset>Hide Sidebar</MenuBar.Item>
|
|
82
|
+
</MenuBar.Content>
|
|
83
|
+
</MenuBar.Menu>
|
|
84
|
+
<MenuBar.Menu>
|
|
85
|
+
<MenuBar.Trigger>Profiles</MenuBar.Trigger>
|
|
86
|
+
<MenuBar.Content>
|
|
87
|
+
<MenuBar.RadioGroup value="benoit">
|
|
88
|
+
<MenuBar.RadioItem value="andy">Andy</MenuBar.RadioItem>
|
|
89
|
+
<MenuBar.RadioItem value="benoit">Benoit</MenuBar.RadioItem>
|
|
90
|
+
<MenuBar.RadioItem value="Luis">Luis</MenuBar.RadioItem>
|
|
91
|
+
</MenuBar.RadioGroup>
|
|
92
|
+
<MenuBar.Separator />
|
|
93
|
+
<MenuBar.Item inset>Edit...</MenuBar.Item>
|
|
94
|
+
<MenuBar.Separator />
|
|
95
|
+
<MenuBar.Item inset>Add Profile...</MenuBar.Item>
|
|
96
|
+
</MenuBar.Content>
|
|
97
|
+
</MenuBar.Menu>
|
|
98
|
+
</>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
|
|
5
|
+
import { useNotificationsStore } from '../../hooks/useNotificationsStore.js';
|
|
6
|
+
import { Button } from '../Button/Button.js';
|
|
7
|
+
import { NotificationHub } from './NotificationHub.js';
|
|
8
|
+
|
|
9
|
+
type Story = StoryObj<typeof NotificationHub>;
|
|
10
|
+
|
|
11
|
+
const meta: Meta<typeof NotificationHub> = {
|
|
12
|
+
component: NotificationHub,
|
|
13
|
+
decorators: [
|
|
14
|
+
(Story) => {
|
|
15
|
+
const notifications = useNotificationsStore();
|
|
16
|
+
return (
|
|
17
|
+
<div className="border">
|
|
18
|
+
<Story />
|
|
19
|
+
<Button
|
|
20
|
+
label="Add Notification"
|
|
21
|
+
type="button"
|
|
22
|
+
onClick={() => {
|
|
23
|
+
notifications.addNotification({
|
|
24
|
+
message: `Notification ${notifications.notifications.length}`,
|
|
25
|
+
type: 'info'
|
|
26
|
+
});
|
|
27
|
+
}}
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default meta;
|
|
36
|
+
|
|
37
|
+
export const Default: Story = {
|
|
38
|
+
args: {
|
|
39
|
+
timeout: 100000
|
|
40
|
+
}
|
|
41
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
|
|
5
|
+
import { Pagination } from './Pagination.js';
|
|
6
|
+
|
|
7
|
+
type Story = StoryObj<typeof Pagination>;
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
args: {
|
|
11
|
+
children: (
|
|
12
|
+
<>
|
|
13
|
+
<Pagination.Content>
|
|
14
|
+
<Pagination.Item>
|
|
15
|
+
<Pagination.Previous href="#" />
|
|
16
|
+
</Pagination.Item>
|
|
17
|
+
<Pagination.Item>
|
|
18
|
+
<Pagination.Link href="#">1</Pagination.Link>
|
|
19
|
+
</Pagination.Item>
|
|
20
|
+
<Pagination.Item>
|
|
21
|
+
<Pagination.Link isActive href="#">
|
|
22
|
+
2
|
|
23
|
+
</Pagination.Link>
|
|
24
|
+
</Pagination.Item>
|
|
25
|
+
<Pagination.Item>
|
|
26
|
+
<Pagination.Link href="#">3</Pagination.Link>
|
|
27
|
+
</Pagination.Item>
|
|
28
|
+
<Pagination.Item>
|
|
29
|
+
<Pagination.Ellipsis />
|
|
30
|
+
</Pagination.Item>
|
|
31
|
+
<Pagination.Item>
|
|
32
|
+
<Pagination.Next href="#" />
|
|
33
|
+
</Pagination.Item>
|
|
34
|
+
</Pagination.Content>
|
|
35
|
+
</>
|
|
36
|
+
)
|
|
37
|
+
},
|
|
38
|
+
component: Pagination
|
|
39
|
+
} as Meta<typeof Pagination>;
|
|
40
|
+
|
|
41
|
+
export const Default: Story = {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import { userEvent } from '@testing-library/user-event';
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { Popover } from './Popover.js';
|
|
8
|
+
|
|
9
|
+
const TestPopover = () => (
|
|
10
|
+
<Popover>
|
|
11
|
+
<Popover.Trigger>Open</Popover.Trigger>
|
|
12
|
+
<Popover.Content>Place content for the popover here.</Popover.Content>
|
|
13
|
+
</Popover>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
describe('Popover', () => {
|
|
17
|
+
it('should function correctly', async () => {
|
|
18
|
+
render(<TestPopover />);
|
|
19
|
+
expect(screen.getByText('Open')).toBeInTheDocument();
|
|
20
|
+
expect(() => screen.getByText('Place content for the popover here.')).toThrow();
|
|
21
|
+
await userEvent.click(screen.getByText('Open'));
|
|
22
|
+
expect(screen.getByText('Place content for the popover here.')).toBeInTheDocument();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
import { CircleHelpIcon } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
import { Button } from '../Button/Button.js';
|
|
7
|
+
import { Input } from '../Input/Input.js';
|
|
8
|
+
import { Label } from '../Label/Label.js';
|
|
9
|
+
import { Popover } from './Popover.js';
|
|
10
|
+
|
|
11
|
+
type Story = StoryObj<typeof Popover>;
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
component: Popover,
|
|
15
|
+
parameters: {
|
|
16
|
+
layout: 'centered'
|
|
17
|
+
},
|
|
18
|
+
tags: ['autodocs']
|
|
19
|
+
} as Meta<typeof Popover>;
|
|
20
|
+
|
|
21
|
+
export const Form: Story = {
|
|
22
|
+
args: {
|
|
23
|
+
children: (
|
|
24
|
+
<React.Fragment>
|
|
25
|
+
<Popover.Trigger asChild>
|
|
26
|
+
<Button variant="outline">Open popover</Button>
|
|
27
|
+
</Popover.Trigger>
|
|
28
|
+
<Popover.Content className="w-80 p-4">
|
|
29
|
+
<div className="grid gap-4">
|
|
30
|
+
<div className="space-y-2">
|
|
31
|
+
<h4 className="font-medium leading-none">Dimensions</h4>
|
|
32
|
+
<p className="text-sm text-muted-foreground">Set the dimensions for the layer.</p>
|
|
33
|
+
</div>
|
|
34
|
+
<div className="grid gap-2">
|
|
35
|
+
<div className="grid grid-cols-3 items-center gap-4">
|
|
36
|
+
<Label htmlFor="width">Width</Label>
|
|
37
|
+
<Input className="col-span-2 h-8" defaultValue="100%" id="width" />
|
|
38
|
+
</div>
|
|
39
|
+
<div className="grid grid-cols-3 items-center gap-4">
|
|
40
|
+
<Label htmlFor="maxWidth">Max. width</Label>
|
|
41
|
+
<Input className="col-span-2 h-8" defaultValue="300px" id="maxWidth" />
|
|
42
|
+
</div>
|
|
43
|
+
<div className="grid grid-cols-3 items-center gap-4">
|
|
44
|
+
<Label htmlFor="height">Height</Label>
|
|
45
|
+
<Input className="col-span-2 h-8" defaultValue="25px" id="height" />
|
|
46
|
+
</div>
|
|
47
|
+
<div className="grid grid-cols-3 items-center gap-4">
|
|
48
|
+
<Label htmlFor="maxHeight">Max. height</Label>
|
|
49
|
+
<Input className="col-span-2 h-8" defaultValue="none" id="maxHeight" />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</Popover.Content>
|
|
54
|
+
</React.Fragment>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const Icon: Story = {
|
|
60
|
+
args: {
|
|
61
|
+
children: (
|
|
62
|
+
<React.Fragment>
|
|
63
|
+
<Popover.Trigger asChild>
|
|
64
|
+
<Button size="icon" variant="ghost">
|
|
65
|
+
<CircleHelpIcon />
|
|
66
|
+
</Button>
|
|
67
|
+
</Popover.Trigger>
|
|
68
|
+
<Popover.Content className="w-min whitespace-nowrap text-sm">Hello World</Popover.Content>
|
|
69
|
+
</React.Fragment>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
|
|
5
|
+
import { Progress } from './Progress.js';
|
|
6
|
+
|
|
7
|
+
type Story = StoryObj<typeof Progress>;
|
|
8
|
+
|
|
9
|
+
export default { component: Progress } as Meta<typeof Progress>;
|
|
10
|
+
|
|
11
|
+
export const Default: Story = {
|
|
12
|
+
decorators: [
|
|
13
|
+
(Story) => {
|
|
14
|
+
const [progress, setProgress] = useState(13);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const timer = setTimeout(() => setProgress(66), 500);
|
|
18
|
+
return () => clearTimeout(timer);
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
return <Story args={{ className: 'w-[60%]', value: progress }} />;
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import { userEvent } from '@testing-library/user-event';
|
|
5
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { Label } from '../Label/Label.js';
|
|
8
|
+
import { RadioGroup, type RadioGroupProps } from './RadioGroup.js';
|
|
9
|
+
|
|
10
|
+
const TEST_ID = 'radio-group';
|
|
11
|
+
|
|
12
|
+
const TestRadioGroup = (props: RadioGroupProps) => (
|
|
13
|
+
<RadioGroup {...props}>
|
|
14
|
+
<div className="flex items-center space-x-2">
|
|
15
|
+
<RadioGroup.Item id="option-one" value="option-one" />
|
|
16
|
+
<Label htmlFor="option-one">Option One</Label>
|
|
17
|
+
</div>
|
|
18
|
+
<div className="flex items-center space-x-2">
|
|
19
|
+
<RadioGroup.Item id="option-two" value="option-two" />
|
|
20
|
+
<Label htmlFor="option-two">Option Two</Label>
|
|
21
|
+
</div>
|
|
22
|
+
</RadioGroup>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
describe('RadioGroup', () => {
|
|
26
|
+
it('should render', () => {
|
|
27
|
+
render(<TestRadioGroup />);
|
|
28
|
+
expect(screen.getByTestId(TEST_ID)).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
it('should contain a custom class name', () => {
|
|
31
|
+
render(<TestRadioGroup className="foo" />);
|
|
32
|
+
expect(screen.getByTestId(TEST_ID)).toHaveClass('foo');
|
|
33
|
+
});
|
|
34
|
+
it('should function correctly', async () => {
|
|
35
|
+
const handleValueChange = vi.fn();
|
|
36
|
+
render(<TestRadioGroup onValueChange={handleValueChange} />);
|
|
37
|
+
await userEvent.click(screen.getByText('Option One'));
|
|
38
|
+
expect(handleValueChange).toHaveBeenLastCalledWith('option-one');
|
|
39
|
+
await userEvent.click(screen.getByText('Option Two'));
|
|
40
|
+
expect(handleValueChange).toHaveBeenLastCalledWith('option-two');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
|
|
5
|
+
import { Label } from '../Label/Label.js';
|
|
6
|
+
import { RadioGroup } from './RadioGroup.js';
|
|
7
|
+
|
|
8
|
+
type Story = StoryObj<typeof RadioGroup>;
|
|
9
|
+
|
|
10
|
+
export default { component: RadioGroup, tags: ['autodocs'] } as Meta<typeof RadioGroup>;
|
|
11
|
+
|
|
12
|
+
export const Default: Story = {
|
|
13
|
+
args: {
|
|
14
|
+
children: (
|
|
15
|
+
<>
|
|
16
|
+
<div className="flex items-center space-x-2">
|
|
17
|
+
<RadioGroup.Item id="r1" value="default" />
|
|
18
|
+
<Label htmlFor="r1">Default</Label>
|
|
19
|
+
</div>
|
|
20
|
+
<div className="flex items-center space-x-2">
|
|
21
|
+
<RadioGroup.Item id="r2" value="comfortable" />
|
|
22
|
+
<Label htmlFor="r2">Comfortable</Label>
|
|
23
|
+
</div>
|
|
24
|
+
<div className="flex items-center space-x-2">
|
|
25
|
+
<RadioGroup.Item id="r3" value="compact" />
|
|
26
|
+
<Label htmlFor="r3">Compact</Label>
|
|
27
|
+
</div>
|
|
28
|
+
</>
|
|
29
|
+
),
|
|
30
|
+
defaultValue: 'default',
|
|
31
|
+
onValueChange(value) {
|
|
32
|
+
alert(value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
|
|
5
|
+
import { Resizable } from './Resizable.js';
|
|
6
|
+
|
|
7
|
+
type Story = StoryObj<typeof Resizable>;
|
|
8
|
+
|
|
9
|
+
export default { component: Resizable } as Meta<typeof Resizable>;
|
|
10
|
+
|
|
11
|
+
export const Default: Story = {
|
|
12
|
+
args: {
|
|
13
|
+
children: (
|
|
14
|
+
<Resizable.PanelGroup className="max-w-md rounded-lg border" direction="horizontal">
|
|
15
|
+
<Resizable.Panel defaultSize={50}>
|
|
16
|
+
<div className="flex h-[200px] items-center justify-center p-6">
|
|
17
|
+
<span className="font-semibold">One</span>
|
|
18
|
+
</div>
|
|
19
|
+
</Resizable.Panel>
|
|
20
|
+
<Resizable.Handle />
|
|
21
|
+
<Resizable.Panel defaultSize={50}>
|
|
22
|
+
<Resizable.PanelGroup direction="vertical">
|
|
23
|
+
<Resizable.Panel defaultSize={25}>
|
|
24
|
+
<div className="flex h-full items-center justify-center p-6">
|
|
25
|
+
<span className="font-semibold">Two</span>
|
|
26
|
+
</div>
|
|
27
|
+
</Resizable.Panel>
|
|
28
|
+
<Resizable.Handle />
|
|
29
|
+
<Resizable.Panel defaultSize={75}>
|
|
30
|
+
<div className="flex h-full items-center justify-center p-6">
|
|
31
|
+
<span className="font-semibold">Three</span>
|
|
32
|
+
</div>
|
|
33
|
+
</Resizable.Panel>
|
|
34
|
+
</Resizable.PanelGroup>
|
|
35
|
+
</Resizable.Panel>
|
|
36
|
+
</Resizable.PanelGroup>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
|
|
6
|
+
import { ScrollArea } from './ScrollArea.js';
|
|
7
|
+
|
|
8
|
+
const TEST_ID = 'scroll-area';
|
|
9
|
+
|
|
10
|
+
describe('ScrollArea', () => {
|
|
11
|
+
it('should render', () => {
|
|
12
|
+
render(<ScrollArea />);
|
|
13
|
+
expect(screen.getByTestId(TEST_ID)).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
it('should contain a custom class name', () => {
|
|
16
|
+
render(<ScrollArea className="foo" />);
|
|
17
|
+
expect(screen.getByTestId(TEST_ID)).toHaveClass('foo');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
|
|
5
|
+
import { ScrollArea } from './ScrollArea.js';
|
|
6
|
+
|
|
7
|
+
type Story = StoryObj<typeof ScrollArea>;
|
|
8
|
+
|
|
9
|
+
export default { component: ScrollArea, tags: ['autodocs'] } as Meta<typeof ScrollArea>;
|
|
10
|
+
|
|
11
|
+
export const Default: Story = {
|
|
12
|
+
args: {
|
|
13
|
+
children: (
|
|
14
|
+
<p>
|
|
15
|
+
Jokester began sneaking into the castle in the middle of the night and leaving jokes all over the place: under
|
|
16
|
+
the king's pillow, in his soup, even in the royal toilet. The king was furious, but he could not seem to
|
|
17
|
+
stop Jokester. And then, one day, the people of the kingdom discovered that the jokes left by Jokester were so
|
|
18
|
+
funny that they could not help but laugh. And once they started laughing, they could not stop.
|
|
19
|
+
</p>
|
|
20
|
+
),
|
|
21
|
+
className: 'h-[200px] w-[350px] rounded-md border p-4'
|
|
22
|
+
}
|
|
23
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import { SearchBar } from './SearchBar.js';
|
|
4
|
+
|
|
5
|
+
type Story = StoryObj<typeof SearchBar>;
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof SearchBar> = { component: SearchBar };
|
|
8
|
+
|
|
9
|
+
export default meta;
|
|
10
|
+
|
|
11
|
+
export const Default: Story = {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
|
|
5
|
+
import { Select } from './Select.js';
|
|
6
|
+
|
|
7
|
+
type Story = StoryObj<typeof Select>;
|
|
8
|
+
|
|
9
|
+
export default { component: Select, tags: ['autodocs'] } as Meta<typeof Select>;
|
|
10
|
+
|
|
11
|
+
export const Default: Story = {
|
|
12
|
+
args: {
|
|
13
|
+
children: (
|
|
14
|
+
<>
|
|
15
|
+
<Select.Trigger className="w-[180px]">
|
|
16
|
+
<Select.Value placeholder="Select a fruit" />
|
|
17
|
+
</Select.Trigger>
|
|
18
|
+
<Select.Content>
|
|
19
|
+
<Select.Group>
|
|
20
|
+
<Select.Label>Fruits</Select.Label>
|
|
21
|
+
<Select.Item value="apple">Apple</Select.Item>
|
|
22
|
+
<Select.Item value="banana">Banana</Select.Item>
|
|
23
|
+
<Select.Item value="blueberry">Blueberry</Select.Item>
|
|
24
|
+
<Select.Item value="grapes">Grapes</Select.Item>
|
|
25
|
+
<Select.Item value="pineapple">Pineapple</Select.Item>
|
|
26
|
+
</Select.Group>
|
|
27
|
+
</Select.Content>
|
|
28
|
+
</>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
|
|
6
|
+
import { Separator } from './Separator.js';
|
|
7
|
+
|
|
8
|
+
const TEST_ID = 'separator';
|
|
9
|
+
|
|
10
|
+
describe('Separator', () => {
|
|
11
|
+
it('should render', () => {
|
|
12
|
+
render(<Separator />);
|
|
13
|
+
expect(screen.getByTestId(TEST_ID)).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
it('should contain a custom class name', () => {
|
|
16
|
+
render(<Separator className="foo" />);
|
|
17
|
+
expect(screen.getByTestId(TEST_ID)).toHaveClass('foo');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
|
|
5
|
+
import { Separator } from './Separator.js';
|
|
6
|
+
|
|
7
|
+
type Story = StoryObj<typeof Separator>;
|
|
8
|
+
|
|
9
|
+
export default { component: Separator, tags: ['autodocs'] } as Meta<typeof Separator>;
|
|
10
|
+
|
|
11
|
+
export const Default: Story = {
|
|
12
|
+
decorators: [
|
|
13
|
+
(Story) => (
|
|
14
|
+
<div>
|
|
15
|
+
<div className="space-y-1">
|
|
16
|
+
<h4 className="text-sm font-medium leading-none">Radix Primitives</h4>
|
|
17
|
+
<p className="text-sm text-muted-foreground">An open-source UI component library.</p>
|
|
18
|
+
</div>
|
|
19
|
+
<Story args={{ className: 'my-4' }} />
|
|
20
|
+
<div className="flex h-5 items-center space-x-4 text-sm">
|
|
21
|
+
<div>Blog</div>
|
|
22
|
+
<Separator orientation="vertical" />
|
|
23
|
+
<div>Docs</div>
|
|
24
|
+
<Separator orientation="vertical" />
|
|
25
|
+
<div>Source</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
]
|
|
30
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
|
|
5
|
+
import { Button } from '../Button/Button.js';
|
|
6
|
+
import { Input } from '../Input/Input.js';
|
|
7
|
+
import { Label } from '../Label/Label.js';
|
|
8
|
+
import { Sheet } from './Sheet.js';
|
|
9
|
+
|
|
10
|
+
type Story = StoryObj<typeof Sheet>;
|
|
11
|
+
|
|
12
|
+
export default { component: Sheet } as Meta<typeof Sheet>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
args: {
|
|
16
|
+
children: (
|
|
17
|
+
<React.Fragment>
|
|
18
|
+
<Sheet.Trigger asChild>
|
|
19
|
+
<Button variant="outline">Open</Button>
|
|
20
|
+
</Sheet.Trigger>
|
|
21
|
+
<Sheet.Content>
|
|
22
|
+
<Sheet.Header>
|
|
23
|
+
<Sheet.Title>Edit profile</Sheet.Title>
|
|
24
|
+
<Sheet.Description>Make changes to your profile here. Click save when you are done.</Sheet.Description>
|
|
25
|
+
</Sheet.Header>
|
|
26
|
+
<Sheet.Body className="grid gap-4">
|
|
27
|
+
<div className="grid grid-cols-4 items-center gap-4">
|
|
28
|
+
<Label className="text-right" htmlFor="name">
|
|
29
|
+
Name
|
|
30
|
+
</Label>
|
|
31
|
+
<Input className="col-span-3" id="name" value="Pedro Duarte" />
|
|
32
|
+
</div>
|
|
33
|
+
<div className="grid grid-cols-4 items-center gap-4">
|
|
34
|
+
<Label className="text-right" htmlFor="username">
|
|
35
|
+
Username
|
|
36
|
+
</Label>
|
|
37
|
+
<Input className="col-span-3" id="username" value="@peduarte" />
|
|
38
|
+
</div>
|
|
39
|
+
</Sheet.Body>
|
|
40
|
+
<Sheet.Footer>
|
|
41
|
+
<Sheet.Close asChild>
|
|
42
|
+
<Button type="submit">Save changes</Button>
|
|
43
|
+
</Sheet.Close>
|
|
44
|
+
</Sheet.Footer>
|
|
45
|
+
</Sheet.Content>
|
|
46
|
+
</React.Fragment>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import { Spinner } from './Spinner.js';
|
|
4
|
+
|
|
5
|
+
type Story = StoryObj<typeof Spinner>;
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
component: Spinner,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'centered'
|
|
11
|
+
}
|
|
12
|
+
} as Meta<typeof Spinner>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import { SpinnerIcon } from './SpinnerIcon.js';
|
|
4
|
+
|
|
5
|
+
type Story = StoryObj<typeof SpinnerIcon>;
|
|
6
|
+
|
|
7
|
+
export default { component: SpinnerIcon } as Meta<typeof SpinnerIcon>;
|
|
8
|
+
|
|
9
|
+
export const Default: Story = {};
|