Skip to content

Commit 2fd07f6

Browse files
committed
chore: add cursor skills and notepads [AR-47851]
1 parent 63acda1 commit 2fd07f6

9 files changed

Lines changed: 593 additions & 79 deletions

File tree

.cursor/notepads/add-play-tests.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
## Add Play Tests to Stories
2+
3+
Look at the current file (or the file I specify). Find all exported stories that are missing a `play` function.
4+
5+
For each story without `play`, generate an interaction test following these rules:
6+
7+
1. Import `{ expect, fn, userEvent, within }` from `'storybook/test'`
8+
2. Use `fn()` for all callback args in the story's `args`
9+
3. Use a11y queries only: `getByRole`, `getByLabelText`, `getByText` -- never `getByTestId`
10+
4. Use semantic assertions: `toBeChecked()`, `toBeDisabled()`, `toBeVisible()`, `toBeInTheDocument()`
11+
5. Await all interactions: `await userEvent.click(...)`, `await expect(...)`
12+
6. Don't test implementation details (no data attributes, no CSS selectors), unless it's a required part of the story
13+
7. Test user-visible behavior: render check, click interactions, callback assertions
14+
15+
For stories with callback args, verify the callback was called:
16+
17+
```tsx
18+
await userEvent.click(button);
19+
await expect(args.onClick).toHaveBeenCalledOnce();
20+
```
21+
22+
For stories showing disabled state, verify interactions are blocked:
23+
24+
```tsx
25+
await userEvent.click(element, { pointerEventsCheck: 0 });
26+
await expect(args.onClick).not.toHaveBeenCalled();
27+
```

.cursor/notepads/check-ark-ui.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## Check Ark UI for Component
2+
3+
I'm about to build a component. Before writing custom code, check if Ark UI already provides a primitive for it.
4+
5+
Steps:
6+
7+
1. Call the Ark UI MCP `list_components` tool with `framework: "react"` to get all available components.
8+
2. If a matching component exists:
9+
- Call `get_component_props` with `framework: "react"` and the component name to show me the full API.
10+
- Call `get_example` with `framework: "react"`, the component name, and `exampleId: "basic"` to show a usage example.
11+
- Call `styling_guide` with the component name to show data attributes I can use in SCSS.
12+
3. If no match exists, tell me, so I know to build custom.
13+
14+
When showing the Ark UI API, highlight:
15+
16+
- Which props I should expose in my own props layer (don't expose all Ark props)
17+
- Which state Ark manages internally (so I don't duplicate with `useState`)
18+
- Which callbacks I should wrap to forward to my own props
19+
- Which data attributes I can use in SCSS for styling states

.cursor/rules/code-review.mdc

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
11
---
2-
description: Use this rule when doing code reviews
2+
description: Apply when reviewing code changes, running git diff, or preparing a PR
33
alwaysApply: false
44
---
55

66
# Code Review Rules
77

88
It is a local code review process done before submitting a PR.
99

10-
Objectives:
10+
## Process
1111

12-
1. Review the current PR diff and flag only clear, high-severity issues
13-
2. Verify requirements and applicable code standards/cursor rules
14-
3. Add short JS comments with a structure:
12+
1. Get diff: `git diff origin/main`
13+
2. Review every changed file against project cursor rules
14+
3. Flag only clear, high-severity issues (max 10 inline comments)
15+
16+
## Inline comment format
1517

1618
```typescript
1719
/**
1820
* REVIEW-[SEVERITY]: [ISSUE DESCRIPTION]
1921
*/
2022
```
2123

22-
- Get diff: git diff upstream/main
23-
- Max 10 inline comments total; prioritize the most critical issues
2424
- One issue per comment; place on the exact changed line
25-
- Natural tone, specific and actionable; do not mention automated or a high-confidence
26-
- Use emojis: 🚨 Critical 🔒 Security ⚡ Performance ⚠️ Logic ✅ Resolved ✨ Improvement
25+
- Natural tone, specific and actionable; do not mention automated or high-confidence
26+
- Severity emojis: 🚨 Critical 🔒 Security ⚡ Performance ⚠️ Logic ✨ Improvement
27+
28+
## Priorities to check
29+
30+
- No `!important` in SCSS
31+
- No hardcoded colors or spacing (use `--color-*`, `--spacing-*` tokens)
32+
- No `:global` in CSS modules
33+
- No unnecessary `useMemo`/`useCallback`
34+
- No `forwardRef` (deprecated)
35+
- No cross-component internal imports
36+
- No `useLayoutEffect` without DOM reads
37+
- Stories have `play` functions with `fn()` and a11y queries
38+
- No inline styles in stories
39+
- Props layer doesn't expose library internals
40+
- Logic errors, security issues, race conditions, missing edge cases
2741

.cursor/rules/design-system.mdc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,9 @@ export type { Ds{Name}Props } from './ds-{name}.types';
6969
2. **Radix UI** (`@radix-ui/react-*`) - Deprecated, modify existing only
7070
3. **TanStack** - Data-heavy components (table, virtual)
7171
4. **Custom** - Only when no primitive exists
72+
73+
## Skills
74+
75+
- To scaffold a new component, use the **component-scaffold** skill.
76+
- To create a component from a Figma URL, use the **figma-to-component** skill.
77+
- To check if Ark UI has a primitive, use the **check-ark-ui** notepad.

.cursor/rules/react-patterns.mdc

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ function useControllableState<T>(
6262

6363
| Requirement | Details |
6464
| -------------------------------------------- | -------------------------------------------------------------------------------- |
65-
| **`useLayoutEffect` only for DOM reads** | If not reading DOM layout (measurements, positions), use `useEffect` instead |
65+
| **`useLayoutEffect` only for DOM reads** | If you need to mutate the DOM and/or do need to perform measurements |
6666
| **`useId` for generated IDs** | Use React `useId()` for SVG `clipPath`, gradient, and filter IDs to avoid dupes |
6767
| **Extract complex hook logic** | Break large hooks into small, named pure functions that receive parameters |
6868
| **`{ passive: true }` for scroll listeners** | Add `{ passive: true }` to `scroll`, `wheel`, and `touchmove` event listeners |
@@ -87,6 +87,22 @@ const id = useId();
8787
container.addEventListener('scroll', checkOverflow, { passive: true });
8888
```
8989

90+
One other situation you might want to use useLayoutEffect instead of useEffect is if you're updating a value (like a ref)
91+
and you want to make sure it's up-to-date before any other code runs. For example:
92+
93+
```tsx
94+
// Good
95+
const ref = React.useRef()
96+
React.useEffect(() => {
97+
ref.current = 'some value'
98+
})
99+
100+
// then, later in another hook or something
101+
React.useLayoutEffect(() => {
102+
console.log(ref.current) // <-- this logs an old value because this runs first!
103+
})
104+
```
105+
90106
---
91107

92108
## React 19
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
---
2+
name: component-scaffold
3+
description: Scaffold a new design system component with all required files, Ark UI integration, stories with play tests, and barrel export wiring. Use when the user asks to create, scaffold, or add a new component.
4+
---
5+
6+
# Component Scaffold Skill
7+
8+
Scaffold a new design system component following all project conventions.
9+
10+
## Input
11+
12+
The user provides a component name (e.g., "tooltip", "progress-bar"). Normalize it to kebab-case for file names and PascalCase for code identifiers.
13+
14+
- File prefix: `ds-{name}` (e.g., `ds-tooltip`)
15+
- Component name: `Ds{Name}` (e.g., `DsTooltip`)
16+
- Directory: `packages/design-system/src/components/ds-{name}/`
17+
18+
## Steps
19+
20+
### Step 1: Check Ark UI for an existing primitive
21+
22+
Before writing any code, check if Ark UI already provides this component:
23+
24+
1. Call the Ark UI MCP `list_components` tool with `framework: "react"`.
25+
2. If a matching component exists:
26+
- Call `get_component_props` with `framework: "react"` and the component name to get the full API.
27+
- Call `get_example` with `framework: "react"`, the component name, and `exampleId: "basic"` to get reference code.
28+
- Call `styling_guide` with the component name to get data attributes for SCSS.
29+
- Use the Ark primitive as the base. Do NOT expose all Ark props -- create an own props layer.
30+
- Do NOT duplicate Ark internal state with `useState`.
31+
3. If no matching component exists, build a custom implementation.
32+
33+
### Step 2: Create the 5 component files
34+
35+
All files go in `packages/design-system/src/components/ds-{name}/`.
36+
37+
#### `ds-{name}.types.ts`
38+
39+
```typescript
40+
import type { CSSProperties, ReactNode, Ref } from 'react';
41+
42+
export const ds{Name}Variants = ['...'] as const;
43+
export type Ds{Name}Variant = (typeof ds{Name}Variants)[number];
44+
45+
export interface Ds{Name}Props {
46+
// Value props first
47+
ref?: Ref<HTMLElement>;
48+
className?: string;
49+
style?: CSSProperties;
50+
children?: ReactNode;
51+
52+
// Slot props
53+
locale?: { /* any hardcoded strings */ };
54+
55+
// Callbacks last
56+
onChange?: (value: unknown) => void;
57+
}
58+
```
59+
60+
Follow these rules:
61+
62+
- Value/config props first, then slot/render props, then callbacks last.
63+
- Export variant arrays as `as const` for storybook argTypes.
64+
- Use `Ref<HTMLElement>` for the ref type.
65+
- Add `locale` prop if the component has any hardcoded user-facing text.
66+
67+
#### `ds-{name}.tsx`
68+
69+
```tsx
70+
import classNames from 'classnames';
71+
import styles from './ds-{name}.module.scss';
72+
import type { Ds{Name}Props } from './ds-{name}.types';
73+
74+
const Ds{Name} = ({
75+
ref,
76+
className,
77+
style,
78+
children,
79+
...rest
80+
}: Ds{Name}Props) => {
81+
return (
82+
<div
83+
ref={ref}
84+
className={classNames(styles.root, className)}
85+
style={style}
86+
{...rest}
87+
>
88+
{children}
89+
</div>
90+
);
91+
};
92+
93+
export default Ds{Name};
94+
```
95+
96+
Follow these rules:
97+
98+
- No `forwardRef` -- pass `ref` as a regular prop.
99+
- Use `classNames` for conditional classes, not template literals.
100+
- No unnecessary `useMemo` or `useCallback`.
101+
- If wrapping an Ark UI primitive, hook into its callbacks to forward to own props. Do not mirror its internal state.
102+
103+
#### `ds-{name}.module.scss`
104+
105+
```scss
106+
.root {
107+
display: flex;
108+
align-items: center;
109+
}
110+
```
111+
112+
Follow these rules:
113+
114+
- Use design tokens: `--color-*`, `--spacing-*`, `--font-size-*`.
115+
- No hardcoded colors or spacing.
116+
- No `!important`.
117+
- No comments (unless genuinely complex).
118+
- Use `0.2s` for transitions.
119+
- Use `[data-focus-visible]` for focus states, `[data-disabled]` or `&:disabled` for disabled.
120+
- Keep component-specific CSS variables in this file, not in `_root.scss`.
121+
- Use `@mixin` for repeated patterns.
122+
123+
#### `ds-{name}.stories.tsx`
124+
125+
```tsx
126+
import type { Meta, StoryObj } from '@storybook/react-vite';
127+
import { expect, fn, userEvent, within } from 'storybook/test';
128+
import Ds{Name} from './ds-{name}';
129+
import { ds{Name}Variants } from './ds-{name}.types';
130+
131+
const meta: Meta<typeof Ds{Name}> = {
132+
title: 'Design System/{Name}',
133+
component: Ds{Name},
134+
parameters: {
135+
layout: 'centered',
136+
},
137+
tags: ['autodocs'],
138+
argTypes: {
139+
variant: { control: 'select', options: ds{Name}Variants },
140+
className: { table: { disable: true } },
141+
style: { table: { disable: true } },
142+
ref: { table: { disable: true } },
143+
},
144+
};
145+
146+
export default meta;
147+
148+
type Story = StoryObj<typeof Ds{Name}>;
149+
150+
export const Default: Story = {
151+
args: {
152+
onChange: fn(),
153+
},
154+
play: async ({ canvasElement, args }) => {
155+
const canvas = within(canvasElement);
156+
// Add assertions using getByRole, getByText, getByLabelText
157+
// Use semantic assertions: toBeChecked, toBeDisabled, toBeVisible
158+
// Await all interactions
159+
},
160+
};
161+
```
162+
163+
Follow these rules:
164+
165+
- Every story MUST have a `play` function.
166+
- Use `fn()` for all callback args.
167+
- Use a11y queries: `getByRole`, `getByLabelText`, `getByText`. Never `getByTestId`.
168+
- Import variant arrays from types for `argTypes.options`.
169+
- Hide internal args (`className`, `style`, `ref`) with `table: { disable: true }`.
170+
- No inline styles -- use `*.stories.module.scss` if needed.
171+
- Add stories for: Default, each variant, Disabled, Controlled (if applicable), Localized (if has `locale` prop).
172+
173+
#### `index.ts`
174+
175+
```typescript
176+
export { default as Ds{Name} } from './ds-{name}';
177+
export type { Ds{Name}Props } from './ds-{name}.types';
178+
```
179+
180+
Note: the barrel file uses `.ts` extension, not `.tsx`.
181+
182+
### Step 3: Wire the barrel export
183+
184+
Add the new component to `packages/design-system/src/index.ts` in **alphabetical order**:
185+
186+
```typescript
187+
export * from './components/ds-{name}';
188+
```
189+
190+
### Step 4: Validate
191+
192+
Run the following commands from the workspace root:
193+
194+
```bash
195+
pnpm eslint packages/design-system/src/components/ds-{name}/
196+
pnpm --filter @drivenets/design-system typecheck
197+
```
198+
199+
Fix any errors before finishing.

0 commit comments

Comments
 (0)