Enhancement: Set up Storybook for component documentation and development - #25
Conversation
…ment Configure Storybook 10 with React + Vite, Tailwind CSS v4 + daisyUI integration, and 27 story files covering all component categories (form, layout, navigation, data display, feedback, utility). Includes DaisyUI theme switcher in toolbar, addon-docs for auto-generated prop tables, and addon-a11y for accessibility checks. Closes #11
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds Storybook 10 (React + Vite) configuration with Tailwind/DaisyUI integration, ESLint Storybook rules, devDependencies, a Vite alias for Changes
Sequence Diagram(s)sequenceDiagram
rect rgba(13,110,253,0.5)
participant Dev as Developer
end
rect rgba(16,185,129,0.5)
participant SB as Storybook
end
rect rgba(234,88,12,0.5)
participant Vite as Vite
end
rect rgba(120,53,138,0.5)
participant Tokens as LocalTokens
end
Dev->>SB: run storybook
SB->>Vite: initialize with framework `@storybook/react-vite`
Vite->>Vite: apply plugins (existing + `@tailwindcss/vite`)
Vite->>Tokens: resolve alias `@artisanpack-ui/tokens` -> ../packages/tokens/src
Vite->>SB: serve built stories (CSS + decorators)
SB->>Dev: render UI with DaisyUI theme toolbar
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
package.json (1)
20-38:⚠️ Potential issue | 🟠 MajorUpdate Node
enginesconstraint to match Storybook and Tailwind toolchain requirements.Lines 20–38 introduce Storybook 10.3.x and
@tailwindcss/vite, which require Node.js 20.19+ or 22.12+ (Storybook's minimum is 20.19.0, not 20.0.0). Add anengines.nodeconstraint to prevent incompatible Node versions across developer and CI environments.Proposed diff
{ "name": "@artisanpack-ui/monorepo", "private": true, "type": "module", + "engines": { + "node": ">=20.19.0" + }, "workspaces": [🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@package.json` around lines 20 - 38, Add a top-level "engines" field to package.json to require a compatible Node version for Storybook 10.3.x and `@tailwindcss/vite`; specifically add "engines": { "node": ">=20.19.0" } (so CI and dev environments running older Node 20.0.0 are blocked), placing it at the root of package.json alongside "devDependencies".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/react/src/components/data/Avatar/Avatar.stories.tsx`:
- Line 32: The story currently uses a remote image URL
('https://placehold.co/96x96') which makes tests flaky; replace that remote
dependency by importing a local asset or using an inline data URI and assign it
to the story's image arg (the property image in Avatar.stories.tsx). Update the
story to import the local file (or embed the base64 data URI) and use that
imported identifier as the image value so visual tests/docs are deterministic;
if using a local file, ensure the asset is added to Storybook/static or the
bundler asset pipeline.
In `@packages/react/src/components/feedback/Alert/Alert.stories.tsx`:
- Line 68: In the WithIcon story (Alert.stories.tsx) the icon is decorative and
should be hidden from assistive tech; update the JSX for the icon prop
(currently "icon: <span>ℹ️</span>") to include aria-hidden="true" on the span so
screen readers skip it (e.g., change the span in the WithIcon story to <span
aria-hidden="true">ℹ️</span>).
In `@packages/react/src/components/form/Input/Input.stories.tsx`:
- Around line 29-34: The WithError Story uses a controlled prop (value) without
an onChange handler, causing React to treat the input as read-only and emit
warnings; update the WithError story's args to use defaultValue instead of value
(or alternatively add an onChange noop) so the Input component is uncontrolled
and editable in the error-state example—locate the WithError export in
Input.stories.tsx and replace args.value with args.defaultValue (or add a simple
onChange handler) accordingly.
In `@packages/react/src/components/form/Textarea/Textarea.stories.tsx`:
- Around line 28-33: The WithError Story object currently sets a controlled prop
"value: ''" which triggers React's controlled-component warning; update the
WithError story to replace the "value" prop with "defaultValue" (e.g., remove
value and add defaultValue: '') so the Textarea story remains uncontrolled for
this static example, and do not add an onChange handler.
In `@packages/react/src/components/layout/Card/Card.stories.tsx`:
- Around line 35-39: Replace the external src on the <img> elements in
Card.stories.tsx with a self-contained placeholder: either import a local static
asset (e.g., import placeholder from './placeholder.png' and set
src={placeholder}) or inline a data URI/SVG string as the src, so the story's
<img src=...> no longer depends on network-hosted images and won't be flaky in
Storybook/Chromatic.
- Around line 97-101: The menu button in the WithMenu Story (args.menu) is
icon-only and lacks an accessible name; update the menu element used in the
WithMenu story (the menu arg) to include an accessible label such as aria-label
or aria-labelledby (e.g., aria-label="Open menu" or aria-label="More options")
so screen readers announce a meaningful control name; ensure the change is made
on the menu JSX provided in the WithMenu args so the story renders an accessible
button.
In `@packages/react/src/components/layout/Drawer/Drawer.stories.tsx`:
- Around line 23-27: In the Drawer.stories.tsx menu example (the <ul
className="menu bg-base-200 min-h-full w-60 p-4"> block), replace the href="#"
anchor usages with non-navigating demo actions: either use realistic demo routes
like "/home", "/about", "/contact" or convert the anchors to buttons (or add an
onClick handler that calls e.preventDefault()) so clicking items doesn't trigger
in-canvas navigation; update the three menu items (Home, About, Contact)
accordingly.
- Around line 15-62: Add an args-driven Storybook story (e.g., export const
Playground) that exposes the Drawer props open, end, and persistent for
controls; implement it similarly to Default/EndSide but use Storybook's useArgs
to read/update args so the Drawer is controlled by args (initial args: { open:
false, end: false, persistent: false }), wire the Drawer prop values to
args.open/args.end/args.persistent, and handle onClose/onClick to call setArgs
(from useArgs) to toggle open so controls reflect interactions.
In `@packages/react/src/components/layout/Modal/Modal.stories.tsx`:
- Around line 15-94: Add a new args-driven story named Playground that exposes
open, title, subtitle, persistent, and bottom as configurable args; define
default args (e.g., open: false, title: "Modal Title", subtitle: "", persistent:
false, bottom: false) and implement render as a function receiving (args, {
updateArgs }) that uses local state for open (const [open, setOpen] =
useState(args.open)), syncs local open to controls via updateArgs({ open }) when
it changes, and passes the rest of args into the Modal component (Modal
open={open} onClose={() => setOpen(false)} title={args.title}
subtitle={args.subtitle} persistent={args.persistent} bottom={args.bottom}) so
Storybook Controls can drive the modal; add a Button in the render to open the
modal by calling setOpen(true).
In
`@packages/react/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsx`:
- Around line 15-40: Replace href="#" placeholders inside the story args (the
items arrays used by the Breadcrumbs stories such as WithIcons and Truncated)
with non-navigating handlers or realistic routes: remove href="#" and either
provide a real route (e.g., "/home") or add an onClick handler like onClick: (e)
=> e.preventDefault() so clicks do not trigger hash navigation in the Storybook
iframe; update each item object in the items arrays for WithIcons and Truncated
(and any other story items arrays) accordingly.
In `@packages/react/src/components/navigation/Menu/Menu.stories.tsx`:
- Line 35: Create a single shared decorator constant (e.g., WIDTH_DECORATOR)
that returns the wrapper div with className "w-56" and replace the duplicated
inline decorators arrays in Menu.stories.tsx (the decorators: [(Story) => <div
className="w-56"><Story /></div>] occurrences) with that constant so all stories
reuse the same decorator; update each story's decorators reference to use the
shared constant to avoid repetition and make future width changes a single edit.
In `@packages/react/src/components/utility/Tooltip/Tooltip.stories.tsx`:
- Around line 14-17: The Storybook control for the Tooltip color prop is missing
the 'neutral' DaisyColor option; update the control options array for the
`color` control in Tooltip.stories.tsx (the `color: { control: 'select',
options: [...] }` block) to include 'neutral' so Storybook exposes all valid
DaisyColor values for `Tooltip.color`.
---
Outside diff comments:
In `@package.json`:
- Around line 20-38: Add a top-level "engines" field to package.json to require
a compatible Node version for Storybook 10.3.x and `@tailwindcss/vite`;
specifically add "engines": { "node": ">=20.19.0" } (so CI and dev environments
running older Node 20.0.0 are blocked), placing it at the root of package.json
alongside "devDependencies".
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 17a788d4-e45e-42c0-b39b-8de4759ea86d
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (32)
.storybook/main.ts.storybook/preview.ts.storybook/storybook.csseslint.config.jspackage.jsonpackages/react/src/components/data/Avatar/Avatar.stories.tsxpackages/react/src/components/data/Badge/Badge.stories.tsxpackages/react/src/components/data/Progress/Progress.stories.tsxpackages/react/src/components/data/Stat/Stat.stories.tsxpackages/react/src/components/data/Table/Table.stories.tsxpackages/react/src/components/feedback/Alert/Alert.stories.tsxpackages/react/src/components/feedback/EmptyState/EmptyState.stories.tsxpackages/react/src/components/feedback/Loading/Loading.stories.tsxpackages/react/src/components/feedback/Skeleton/Skeleton.stories.tsxpackages/react/src/components/form/Button/Button.stories.tsxpackages/react/src/components/form/Checkbox/Checkbox.stories.tsxpackages/react/src/components/form/Input/Input.stories.tsxpackages/react/src/components/form/Select/Select.stories.tsxpackages/react/src/components/form/Textarea/Textarea.stories.tsxpackages/react/src/components/form/Toggle/Toggle.stories.tsxpackages/react/src/components/layout/Accordion/Accordion.stories.tsxpackages/react/src/components/layout/Card/Card.stories.tsxpackages/react/src/components/layout/Drawer/Drawer.stories.tsxpackages/react/src/components/layout/Dropdown/Dropdown.stories.tsxpackages/react/src/components/layout/Modal/Modal.stories.tsxpackages/react/src/components/layout/Tabs/Tabs.stories.tsxpackages/react/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsxpackages/react/src/components/navigation/Menu/Menu.stories.tsxpackages/react/src/components/navigation/Pagination/Pagination.stories.tsxpackages/react/src/components/navigation/Steps/Steps.stories.tsxpackages/react/src/components/utility/Clipboard/Clipboard.stories.tsxpackages/react/src/components/utility/Tooltip/Tooltip.stories.tsx
| export const Default: Story = { | ||
| render: () => { | ||
| const [open, setOpen] = useState(false); | ||
| return ( | ||
| <> | ||
| <Button label="Open Modal" color="primary" onClick={() => setOpen(true)} /> | ||
| <Modal open={open} onClose={() => setOpen(false)} title="Modal Title"> | ||
| <p>This is the modal body content.</p> | ||
| </Modal> | ||
| </> | ||
| ); | ||
| }, | ||
| }; | ||
|
|
||
| export const WithActions: Story = { | ||
| render: () => { | ||
| const [open, setOpen] = useState(false); | ||
| return ( | ||
| <> | ||
| <Button label="Open Modal" color="primary" onClick={() => setOpen(true)} /> | ||
| <Modal | ||
| open={open} | ||
| onClose={() => setOpen(false)} | ||
| title="Confirm Delete" | ||
| subtitle="This action cannot be undone." | ||
| actions={ | ||
| <> | ||
| <Button label="Cancel" color="ghost" onClick={() => setOpen(false)} /> | ||
| <Button label="Delete" color="error" onClick={() => setOpen(false)} /> | ||
| </> | ||
| } | ||
| > | ||
| <p>Are you sure you want to delete this item?</p> | ||
| </Modal> | ||
| </> | ||
| ); | ||
| }, | ||
| }; | ||
|
|
||
| export const Persistent: Story = { | ||
| render: () => { | ||
| const [open, setOpen] = useState(false); | ||
| return ( | ||
| <> | ||
| <Button label="Open Persistent Modal" color="warning" onClick={() => setOpen(true)} /> | ||
| <Modal | ||
| open={open} | ||
| onClose={() => setOpen(false)} | ||
| title="Required Action" | ||
| persistent | ||
| actions={ | ||
| <Button label="I Understand" color="primary" onClick={() => setOpen(false)} /> | ||
| } | ||
| > | ||
| <p>You must acknowledge this before continuing. Clicking outside will not close it.</p> | ||
| </Modal> | ||
| </> | ||
| ); | ||
| }, | ||
| }; | ||
|
|
||
| export const Bottom: Story = { | ||
| render: () => { | ||
| const [open, setOpen] = useState(false); | ||
| return ( | ||
| <> | ||
| <Button label="Open Bottom Sheet" color="accent" onClick={() => setOpen(true)} /> | ||
| <Modal | ||
| open={open} | ||
| onClose={() => setOpen(false)} | ||
| title="Bottom Sheet" | ||
| bottom | ||
| actions={<Button label="Close" color="ghost" onClick={() => setOpen(false)} />} | ||
| > | ||
| <p>This modal slides up from the bottom on mobile.</p> | ||
| </Modal> | ||
| </> | ||
| ); | ||
| }, | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
❓ Verification inconclusive
Script executed:
find . -name "Modal.stories.tsx" -type fRepository: ArtisanPack-UI/react
Repository: ArtisanPack-UI/react
Exit code: 0
stdout:
./packages/react/src/components/layout/Modal/Modal.stories.tsx
Script executed:
cat -n ./packages/react/src/components/layout/Modal/Modal.stories.tsxRepository: ArtisanPack-UI/react
Repository: ArtisanPack-UI/react
Exit code: 0
stdout:
1 import { useState } from 'react';
2 import type { Meta, StoryObj } from '@storybook/react-vite';
3 import { Modal } from './Modal';
4 import { Button } from '../../form/Button/Button';
5
6 const meta: Meta<typeof Modal> = {
7 title: 'Layout/Modal',
8 component: Modal,
9 tags: ['autodocs'],
10 };
11
12 export default meta;
13 type Story = StoryObj<typeof Modal>;
14
15 export const Default: Story = {
16 render: () => {
17 const [open, setOpen] = useState(false);
18 return (
19 <>
20 <Button label="Open Modal" color="primary" onClick={() => setOpen(true)} />
21 <Modal open={open} onClose={() => setOpen(false)} title="Modal Title">
22 <p>This is the modal body content.</p>
23 </Modal>
24 </>
25 );
26 },
27 };
28
29 export const WithActions: Story = {
30 render: () => {
31 const [open, setOpen] = useState(false);
32 return (
33 <>
34 <Button label="Open Modal" color="primary" onClick={() => setOpen(true)} />
35 <Modal
36 open={open}
37 onClose={() => setOpen(false)}
38 title="Confirm Delete"
39 subtitle="This action cannot be undone."
40 actions={
41 <>
42 <Button label="Cancel" color="ghost" onClick={() => setOpen(false)} />
43 <Button label="Delete" color="error" onClick={() => setOpen(false)} />
44 </>
45 }
46 >
47 <p>Are you sure you want to delete this item?</p>
48 </Modal>
49 </>
50 );
51 },
52 };
53
54 export const Persistent: Story = {
55 render: () => {
56 const [open, setOpen] = useState(false);
57 return (
58 <>
59 <Button label="Open Persistent Modal" color="warning" onClick={() => setOpen(true)} />
60 <Modal
61 open={open}
62 onClose={() => setOpen(false)}
63 title="Required Action"
64 persistent
65 actions={
66 <Button label="I Understand" color="primary" onClick={() => setOpen(false)} />
67 }
68 >
69 <p>You must acknowledge this before continuing. Clicking outside will not close it.</p>
70 </Modal>
71 </>
72 );
73 },
74 };
75
76 export const Bottom: Story = {
77 render: () => {
78 const [open, setOpen] = useState(false);
79 return (
80 <>
81 <Button label="Open Bottom Sheet" color="accent" onClick={() => setOpen(true)} />
82 <Modal
83 open={open}
84 onClose={() => setOpen(false)}
85 title="Bottom Sheet"
86 bottom
87 actions={<Button label="Close" color="ghost" onClick={() => setOpen(false)} />}
88 >
89 <p>This modal slides up from the bottom on mobile.</p>
90 </Modal>
91 </>
92 );
93 },
94 };
Script executed:
find . -name "Modal.tsx" -type f | grep -E "components/layout/Modal/Modal\.tsx$"Repository: ArtisanPack-UI/react
Repository: ArtisanPack-UI/react
Exit code: 0
stdout:
./packages/react/src/components/layout/Modal/Modal.tsx
Script executed:
cat -n ./packages/react/src/components/layout/Modal/Modal.tsxRepository: ArtisanPack-UI/react
Repository: ArtisanPack-UI/react
Exit code: 0
stdout:
1 import {
2 forwardRef,
3 useEffect,
4 useRef,
5 useCallback,
6 useId,
7 type HTMLAttributes,
8 type ReactNode,
9 type MouseEvent,
10 } from 'react';
11 import { cn } from '@artisanpack-ui/tokens';
12
13 export interface ModalProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
14 /** Whether the modal is open */
15 open: boolean;
16 /** Callback to close the modal */
17 onClose: () => void;
18 /** Modal title */
19 title?: ReactNode;
20 /** Modal subtitle */
21 subtitle?: string;
22 /** Actions slot (buttons in footer) */
23 actions?: ReactNode;
24 /** Prevent closing via escape key or backdrop click */
25 persistent?: boolean;
26 /** Glass morphism effect */
27 glass?: boolean;
28 /** Position modal at bottom (mobile sheet style) */
29 bottom?: boolean;
30 }
31
32 /**
33 * Dialog overlay with focus trapping, keyboard dismiss, and backdrop click.
34 */
35 export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
36 (
37 {
38 open,
39 onClose,
40 title,
41 subtitle,
42 actions,
43 persistent = false,
44 glass = false,
45 bottom = false,
46 className,
47 children,
48 ...rest
49 },
50 ref,
51 ) => {
52 const dialogRef = useRef<HTMLDialogElement | null>(null);
53 const previousActiveElement = useRef<Element | null>(null);
54 const titleId = useId();
55
56 const setRefs = useCallback(
57 (node: HTMLDialogElement | null) => {
58 dialogRef.current = node;
59 if (typeof ref === 'function') {
60 ref(node);
61 } else if (ref) {
62 (ref as React.MutableRefObject<HTMLDialogElement | null>).current = node;
63 }
64 },
65 [ref],
66 );
67
68 useEffect(() => {
69 const dialog = dialogRef.current;
70 if (!dialog) return;
71
72 if (open) {
73 previousActiveElement.current = document.activeElement;
74 if (!dialog.open) {
75 dialog.showModal();
76 }
77 } else {
78 if (dialog.open) {
79 dialog.close();
80 }
81 if (previousActiveElement.current instanceof HTMLElement) {
82 previousActiveElement.current.focus();
83 }
84 }
85 }, [open]);
86
87 useEffect(() => {
88 const dialog = dialogRef.current;
89 if (!dialog) return;
90
91 const handleCancel = (e: Event) => {
92 e.preventDefault();
93 if (!persistent) {
94 onClose();
95 }
96 };
97
98 dialog.addEventListener('cancel', handleCancel);
99 return () => dialog.removeEventListener('cancel', handleCancel);
100 }, [persistent, onClose]);
101
102 const handleBackdropClick = (e: MouseEvent<HTMLDialogElement>) => {
103 if (persistent) return;
104 if (e.target === dialogRef.current) {
105 onClose();
106 }
107 };
108
109 return (
110 <dialog
111 ref={setRefs}
112 className={cn(
113 'modal',
114 bottom && 'modal-bottom',
115 className,
116 )}
117 onClick={handleBackdropClick}
118 aria-modal="true"
119 aria-labelledby={title ? titleId : undefined}
120 {...rest}
121 >
122 <div className={cn('modal-box', glass && 'glass')}>
123 {(title || subtitle) && (
124 <div className="mb-4">
125 {title && (
126 typeof title === 'string' ? (
127 <h3 id={titleId} className="text-lg font-bold">{title}</h3>
128 ) : (
129 <div id={titleId}>{title}</div>
130 )
131 )}
132 {subtitle && (
133 <p className="text-base-content/60 text-sm">{subtitle}</p>
134 )}
135 </div>
136 )}
137 {!persistent && (
138 <button
139 type="button"
140 className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
141 onClick={onClose}
142 aria-label="Close"
143 >
144 ✕
145 </button>
146 )}
147 <div>{children}</div>
148 {actions && (
149 <div className="modal-action">{actions}</div>
150 )}
151 </div>
152 </dialog>
153 );
154 },
155 );
156
157 Modal.displayName = 'Modal';
Add an args-driven Modal playground story to expose interactive controls.
Current stories demonstrate specific use cases but don't provide interactive prop controls. Add one args-based story with configurable props (open, title, subtitle, persistent, bottom) to enable controls-first exploration in Storybook's Controls panel.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/react/src/components/layout/Modal/Modal.stories.tsx` around lines 15
- 94, Add a new args-driven story named Playground that exposes open, title,
subtitle, persistent, and bottom as configurable args; define default args
(e.g., open: false, title: "Modal Title", subtitle: "", persistent: false,
bottom: false) and implement render as a function receiving (args, { updateArgs
}) that uses local state for open (const [open, setOpen] = useState(args.open)),
syncs local open to controls via updateArgs({ open }) when it changes, and
passes the rest of args into the Modal component (Modal open={open} onClose={()
=> setOpen(false)} title={args.title} subtitle={args.subtitle}
persistent={args.persistent} bottom={args.bottom}) so Storybook Controls can
drive the modal; add a Button in the render to open the modal by calling
setOpen(true).
- Replace external placeholder images with inline SVG data URIs - Fix controlled value warnings (use defaultValue instead of value) - Add aria-hidden to decorative emoji icons - Add aria-label to icon-only menu button - Use realistic demo routes instead of href="#" in Breadcrumbs/Drawer - Add preventDefault to Drawer demo links - Extract shared width decorator in Menu stories - Add neutral to Tooltip/Avatar color control options
There was a problem hiding this comment.
Actionable comments posted: 7
♻️ Duplicate comments (1)
packages/react/src/components/layout/Drawer/Drawer.stories.tsx (1)
17-64:⚠️ Potential issue | 🟠 MajorAdd an args-driven playground story for controls parity.
Both stories are fully local-state driven, so
open,end, andpersistentare not controllable from the Storybook Controls panel. This leaves the Drawer docs/demo incomplete for interactive prop testing.Proposed patch
import { useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react-vite'; +import { useArgs } from '@storybook/preview-api'; import { Drawer } from './Drawer'; import { Button } from '../../form/Button/Button'; @@ export default meta; type Story = StoryObj<typeof Drawer>; @@ const handleLinkClick = (e: React.MouseEvent) => e.preventDefault(); + +export const Playground: Story = { + args: { + open: false, + end: false, + persistent: false, + }, + render: (args) => { + const [, setArgs] = useArgs(); + return ( + <Drawer + {...args} + open={!!args.open} + end={!!args.end} + persistent={!!args.persistent} + onClose={() => setArgs({ open: false })} + side={ + <ul className="menu bg-base-200 min-h-full w-60 p-4"> + <li><a href="/home" onClick={handleLinkClick}>Home</a></li> + <li><a href="/about" onClick={handleLinkClick}>About</a></li> + <li><a href="/contact" onClick={handleLinkClick}>Contact</a></li> + </ul> + } + > + <div className="p-4"> + <Button + label="Toggle Drawer" + color="primary" + onClick={() => setArgs({ open: !args.open })} + /> + <p className="mt-4">Use controls to test open/end/persistent behavior.</p> + </div> + </Drawer> + ); + }, +};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/react/src/components/layout/Drawer/Drawer.stories.tsx` around lines 17 - 64, Add an args-driven "Playground" story so Storybook Controls can manipulate open, end, and persistent: create a new export Playground: Story that defines args { open: false, end: false, persistent: false } and in its render use the Storybook useArgs hook to read/update args (e.g., const [{ open, end, persistent }, updateArgs] = useArgs()); pass those values into <Drawer open={open} end={end} persistent={persistent} /> and make onClose call updateArgs({ open: false }); for the open toggle buttons call updateArgs({ open: true }); and reuse the same side markup (or replace handleLinkClick with a local noop) so the story is interactive via Controls and mirrors the existing Default/EndSide props.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/react/src/components/data/Avatar/Avatar.stories.tsx`:
- Around line 78-88: The Colors story for the Avatar component omits the
"neutral" variant even though argTypes.color includes it; update the Colors
Story (export const Colors) to render an additional <Avatar> with placeholder
"N" and color="neutral" alongside the existing variants so the showcased color
matrix matches argTypes.color and the Avatar component's available options.
In `@packages/react/src/components/form/Input/Input.stories.tsx`:
- Around line 37-43: The decorative search emoji in the Story object WithIcons
(args.icon) should be marked aria-hidden so screen readers ignore it; update the
icon value in the WithIcons story to render the emoji with aria-hidden="true"
(e.g., change the span used for the icon in WithIcons to include aria-hidden) so
the visual emoji is skipped by assistive tech while the label "Search" remains
accessible.
In `@packages/react/src/components/layout/Card/Card.stories.tsx`:
- Around line 37-40: The SVG data-URI construction is duplicated in
Card.stories.tsx (used at the image placeholder in multiple stories); extract it
into a single helper (e.g., a constant SVG_PLACEHOLDER_DATA_URI or a small
function buildSvgPlaceholder(width, height, text)) and replace the inline
concatenation in the stories with that helper; update every use (the instances
around the current encodeURIComponent blocks) so the stories import/consume the
new helper, keeping the same output format and parameters for width/height/text
if you choose a function.
- Line 118: The story currently sets the Card component's link prop to '#' which
causes hash-jump behavior; update the story args for the Card story (the object
that includes link: '#') to use a realistic demo route like '/pricing/plan-a' or
remove the link and provide a non-navigating onClick handler that calls
event.preventDefault() for interactive previews; locate the "link" prop in
Card.stories.tsx (the story args object for the Card story) and replace '#' with
the chosen stable path or switch to a prevent-default click handler to avoid
navigation during Storybook interactions.
In `@packages/react/src/components/navigation/Menu/Menu.stories.tsx`:
- Around line 56-58: The emoji spans in the menu items array in Menu.stories.tsx
(the objects with keys 'home', 'settings', 'profile') are decorative and need
aria-hidden set; update the <span> elements used as icons (the spans containing
"🏠", "⚙️", "👤") to include aria-hidden="true" so screen readers ignore the
decorative emoji while keeping the visible label text intact.
In `@packages/react/src/components/utility/Tooltip/Tooltip.stories.tsx`:
- Around line 51-67: The Colors story for Tooltip only shows a subset of the
available colors; update the exported Colors story (symbol: Colors) to render
Tooltip/Button examples for the remaining exposed color options (neutral,
warning, error, info) so docs match controls. Modify the render function inside
Colors to include additional <Tooltip tip="..." color="..."><Button label="..."
color="..." /></Tooltip> entries for neutral, warning, error, and info (or
iterate over a shared color list constant if one exists) ensuring each color
string matches the component prop names.
- Around line 29-30: Duplicate inline decorator used in the stories (the
decorators: [(Story) => <div className="p-16 flex justify-center"><Story
/></div>] usage) should be extracted into a shared constant (e.g.,
centeredDecorator) and reused by both story definitions; replace the inline
lambda passed to the decorators array with the shared centeredDecorator constant
in each story so layout changes are centralized and duplication removed.
---
Duplicate comments:
In `@packages/react/src/components/layout/Drawer/Drawer.stories.tsx`:
- Around line 17-64: Add an args-driven "Playground" story so Storybook Controls
can manipulate open, end, and persistent: create a new export Playground: Story
that defines args { open: false, end: false, persistent: false } and in its
render use the Storybook useArgs hook to read/update args (e.g., const [{ open,
end, persistent }, updateArgs] = useArgs()); pass those values into <Drawer
open={open} end={end} persistent={persistent} /> and make onClose call
updateArgs({ open: false }); for the open toggle buttons call updateArgs({ open:
true }); and reuse the same side markup (or replace handleLinkClick with a local
noop) so the story is interactive via Controls and mirrors the existing
Default/EndSide props.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 9e0771b8-1a51-45dd-a000-d22d01f160ba
📒 Files selected for processing (9)
packages/react/src/components/data/Avatar/Avatar.stories.tsxpackages/react/src/components/feedback/Alert/Alert.stories.tsxpackages/react/src/components/form/Input/Input.stories.tsxpackages/react/src/components/form/Textarea/Textarea.stories.tsxpackages/react/src/components/layout/Card/Card.stories.tsxpackages/react/src/components/layout/Drawer/Drawer.stories.tsxpackages/react/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsxpackages/react/src/components/navigation/Menu/Menu.stories.tsxpackages/react/src/components/utility/Tooltip/Tooltip.stories.tsx
- Add neutral Avatar to Colors story to match argTypes - Add aria-hidden to decorative emoji icons in Input/Menu stories - Extract svgPlaceholder helper to deduplicate Card data URIs - Replace href="#" with realistic route in Card AsLink story - Add all color variants to Tooltip Colors story - Extract shared centeredDecorator in Tooltip stories
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/react/src/components/layout/Card/Card.stories.tsx`:
- Around line 138-143: The Card story uses the glass prop on the Card component
(Card title="Plan C" ... glass) but lacks a colored backdrop so the glass
morphism won't be visible; either wrap this Card in a gradient/colored container
(e.g., a decorator or a surrounding div with the same gradient used by the Glass
story) to make the effect visible, or remove the glass prop from this
composition example if the glass look is not intended. Locate the Card instance
in Card.stories.tsx and apply the chosen fix (add gradient wrapper/decorator or
remove glass).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 8c787a92-1e09-400e-9ecd-21ebf6255fe0
📒 Files selected for processing (5)
packages/react/src/components/data/Avatar/Avatar.stories.tsxpackages/react/src/components/form/Input/Input.stories.tsxpackages/react/src/components/layout/Card/Card.stories.tsxpackages/react/src/components/navigation/Menu/Menu.stories.tsxpackages/react/src/components/utility/Tooltip/Tooltip.stories.tsx
The glass morphism effect is invisible without a colored backdrop. Replaced with bordered to keep the three cards visually distinct. The dedicated Glass story already demonstrates the effect properly.
Description
Set up Storybook 10 with React + Vite for component development and documentation, covering all 6 component categories with 27 story files.
Closes: #11
Type of Change
Related Issue
Issue: #11
Motivation and Context
Developers need a visual development environment and interactive documentation for the 80+ React components. Storybook provides isolated component development, auto-generated docs from TypeScript types, DaisyUI theme switching, and accessibility checks in one tool.
Changes Made
@tailwindcss/vitepluginautodocs, variant stories (colors, sizes, states), and composition examples@artisanpack-ui/tokensalias for monorepo resolutioneslint-plugin-storybookfor story lintingpackage.jsonwith Storybook dependenciesHow Has This Been Tested?
Testing Environment:
Tests Performed:
npm run build-storybookcompletes successfullynpm run storybookstarts dev server, all stories render correctlyAccessibility Tests Run
Details: Storybook addon-a11y runs axe-core accessibility checks on all stories. Components inherit accessibility from the main component implementations.
Tests Added
Test details: Storybook build serves as a compilation/rendering test for all 27 story files. No unit tests added as stories are documentation/development tooling. Note: existing vitest tests have a pre-existing
@vitejs/plugin-react@6/vite@7version conflict unrelated to this change.Documentation
Documentation details: All stories use
tags: ['autodocs']for automatic documentation generation from TypeScript prop interfaces.Pre-Submission Checklist
Summary by CodeRabbit
New Features
Documentation
Chores