@1money/component-ui 0.0.23 → 0.0.25
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/es/components/Table/interface.d.ts +2 -1
- package/es/components/Table/renderers/EmptyState.d.ts +2 -1
- package/es/components/Table/renderers/EmptyState.js +15 -8
- package/es/components/Table/style/Table.css +1 -1
- package/es/index.css +1 -1
- package/es/stories/docs/ComponentDocsPage.js +234 -0
- package/es/stories/docs/componentDocMeta.js +97 -0
- package/es/stories/docs/storybook-docs.css +79 -0
- package/lib/components/Table/interface.d.ts +2 -1
- package/lib/components/Table/renderers/EmptyState.d.ts +2 -1
- package/lib/components/Table/renderers/EmptyState.js +15 -7
- package/lib/components/Table/style/Table.css +1 -1
- package/lib/index.css +1 -1
- package/lib/stories/docs/ComponentDocsPage.js +244 -0
- package/lib/stories/docs/componentDocMeta.js +104 -0
- package/lib/stories/docs/storybook-docs.css +79 -0
- package/package.json +23 -8
- package/scripts/mcp-server/README.md +267 -0
- package/scripts/mcp-server/bin.mjs +2 -0
- package/scripts/mcp-server/drift.json +5 -0
- package/scripts/mcp-server/examples.generated.json +2651 -0
- package/scripts/mcp-server/index.generated.json +18098 -0
- package/scripts/mcp-server/index.mjs +308 -26
- package/scripts/mcp-server/tools/get-examples.mjs +125 -0
- package/scripts/mcp-server/tools/get-library-info.mjs +25 -0
- package/scripts/mcp-server/tools/get-symbol.mjs +232 -0
- package/scripts/mcp-server/tools/get-token.mjs +60 -0
- package/scripts/mcp-server/tools/list-icons.mjs +38 -0
- package/scripts/mcp-server/tools/list-symbols.mjs +46 -0
- package/scripts/mcp-server/tools/resolve-import.mjs +125 -0
- package/scripts/mcp-server/tools/search-symbols.mjs +79 -0
- package/.agents/skills/1money-component-dev/SKILL.md +0 -224
- package/.agents/skills/1money-component-dev/checklist.md +0 -159
- package/.agents/skills/1money-component-dev/references/ComponentPatterns.md +0 -478
- package/.agents/skills/1money-component-dev/references/FigmaExtractionChecklist.md +0 -144
- package/.agents/skills/1money-component-dev/references/HooksGuide.md +0 -360
- package/.agents/skills/1money-component-dev/references/SemanticColors.md +0 -215
- package/.agents/skills/1money-component-dev/references/StyleSystemAPI.md +0 -389
- package/.claude/settings.local.json +0 -120
- package/.claude/skills/1money-component-dev/SKILL.md +0 -229
- package/.claude/skills/1money-component-dev/checklist.md +0 -159
- package/.claude/skills/1money-component-dev/references/ComponentPatterns.md +0 -478
- package/.claude/skills/1money-component-dev/references/FigmaExtractionChecklist.md +0 -144
- package/.claude/skills/1money-component-dev/references/HooksGuide.md +0 -360
- package/.claude/skills/1money-component-dev/references/SemanticColors.md +0 -215
- package/.claude/skills/1money-component-dev/references/StyleSystemAPI.md +0 -389
- package/.claude/skills/1money-component-review/SKILL.md +0 -316
- package/.claude/skills/component-pipeline/SKILL.md +0 -116
- package/.claude/skills/component-pipeline/checklist.md +0 -125
- package/.hintrc +0 -13
- package/@types/global.d.ts +0 -28
- package/AGENTS.md +0 -546
- package/CLAUDE.md +0 -1
- package/jest.setup.d.ts +0 -1
- package/jest.setup.ts +0 -1
- package/patches/primereact.patch +0 -323
- package/patches/react-pro-sidebar.patch +0 -6421
- package/public/favicon.ico +0 -0
- package/public/fonts/Aeonik/Aeonik-Air.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-AirItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Black.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-BlackItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Bold.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-BoldItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Light.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-LightItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Medium.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-MediumItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Regular.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-RegularItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Thin.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-ThinItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Black.ttf +0 -0
- package/public/fonts/Inter/Inter-BlackItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Bold.ttf +0 -0
- package/public/fonts/Inter/Inter-BoldItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-ExtraBold.ttf +0 -0
- package/public/fonts/Inter/Inter-ExtraBoldItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-ExtraLight.ttf +0 -0
- package/public/fonts/Inter/Inter-ExtraLightItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Italic.ttf +0 -0
- package/public/fonts/Inter/Inter-Light.ttf +0 -0
- package/public/fonts/Inter/Inter-LightItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Medium.ttf +0 -0
- package/public/fonts/Inter/Inter-MediumItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Regular.ttf +0 -0
- package/public/fonts/Inter/Inter-SemiBold.ttf +0 -0
- package/public/fonts/Inter/Inter-SemiBoldItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Thin.ttf +0 -0
- package/public/fonts/Inter/Inter-ThinItalic.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Black.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Bold.ttf +0 -0
- package/public/fonts/Outfit/Outfit-ExtraBold.ttf +0 -0
- package/public/fonts/Outfit/Outfit-ExtraLight.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Light.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Medium.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Regular.ttf +0 -0
- package/public/fonts/Outfit/Outfit-SemiBold.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Thin.ttf +0 -0
- package/public/github-mark.svg +0 -3
- package/public/tokens/GYEN.svg +0 -9
- package/public/tokens/PYUSD.svg +0 -9
- package/public/tokens/USDT.svg +0 -6
- package/scripts/mcp-server/resources.d.mts +0 -1
- package/scripts/mcp-server/resources.mjs +0 -102
- package/test/jsdom-global-register.d.ts +0 -1
- package/test/jsdom-global-register.js +0 -1
|
@@ -1,478 +0,0 @@
|
|
|
1
|
-
# Component Patterns Reference
|
|
2
|
-
|
|
3
|
-
Annotated templates for all 8 files in a `@1money/components-ui` component directory. Based on the canonical `Button` component.
|
|
4
|
-
|
|
5
|
-
Replace `{Name}` with PascalCase name (e.g., `Chip`) and `{kebab-name}` with kebab-case (e.g., `chip`).
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1. `{Name}.tsx` — Main Component
|
|
10
|
-
|
|
11
|
-
```tsx
|
|
12
|
-
// ---- Imports ----
|
|
13
|
-
import { memo } from 'react';
|
|
14
|
-
// Import the PrimeReact base component directly from primereact/*
|
|
15
|
-
import { PrimeComponent as PrimeBase } from 'primereact/{component}';
|
|
16
|
-
// Import internal components if needed (e.g., Spinner for loading states)
|
|
17
|
-
import Spinner from '@/components/Spinner';
|
|
18
|
-
// Import classnames utility — default export is pre-configured with 'om-react-ui' prefix
|
|
19
|
-
import { default as classnames, joinCls } from '@/utils/classnames';
|
|
20
|
-
// Import types separately using `import type`
|
|
21
|
-
import type { FC, PropsWithChildren } from 'react';
|
|
22
|
-
import type { {Name}Props } from './interface';
|
|
23
|
-
|
|
24
|
-
// ---- Constants ----
|
|
25
|
-
// Extract magic strings/numbers as named constants
|
|
26
|
-
const SOME_CONSTANT = '5';
|
|
27
|
-
|
|
28
|
-
// ---- Component ----
|
|
29
|
-
// Named export with explicit FC typing
|
|
30
|
-
// Use PropsWithChildren<> if the component accepts children, otherwise just {Name}Props
|
|
31
|
-
export const {Name}: FC<PropsWithChildren<{Name}Props>> = props => {
|
|
32
|
-
// Destructure with defaults
|
|
33
|
-
// - prefixCls defaults to kebab-case component name
|
|
34
|
-
// - Variant props get sensible defaults
|
|
35
|
-
// - ref is destructured but aliased to _ref (forwarded via PrimeReact's own ref handling)
|
|
36
|
-
// - ...rest captures all remaining PrimeReact props
|
|
37
|
-
const {
|
|
38
|
-
children,
|
|
39
|
-
className = '',
|
|
40
|
-
prefixCls = '{kebab-name}',
|
|
41
|
-
color = 'primary',
|
|
42
|
-
size = 'medium',
|
|
43
|
-
ref: _ref,
|
|
44
|
-
...rest
|
|
45
|
-
} = props;
|
|
46
|
-
|
|
47
|
-
// Create class generator — returns a function: (suffix?, className?) => string
|
|
48
|
-
const classes = classnames(prefixCls);
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<PrimeBase
|
|
52
|
-
// Spread remaining props FIRST so our overrides take precedence
|
|
53
|
-
{...rest}
|
|
54
|
-
// Build root className:
|
|
55
|
-
// - classes(void 0, ...) generates the root class: `.om-react-ui-{kebab-name}`
|
|
56
|
-
// - joinCls combines variant classes and external className
|
|
57
|
-
className={classes(
|
|
58
|
-
void 0,
|
|
59
|
-
joinCls(classes(color), classes(size), className),
|
|
60
|
-
)}
|
|
61
|
-
>
|
|
62
|
-
{children}
|
|
63
|
-
</PrimeBase>
|
|
64
|
-
);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// Default export MUST be wrapped in memo()
|
|
68
|
-
export default memo({Name});
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### `classnames` Utility Explained
|
|
72
|
-
|
|
73
|
-
```ts
|
|
74
|
-
import { default as classnames, joinCls } from '@/utils/classnames';
|
|
75
|
-
|
|
76
|
-
// classnames is a curried function:
|
|
77
|
-
// classnames(prefix) returns a class generator
|
|
78
|
-
|
|
79
|
-
const classes = classnames('button');
|
|
80
|
-
// classes is now: (suffix?, className?) => string
|
|
81
|
-
|
|
82
|
-
classes(void 0) // → 'om-react-ui-button'
|
|
83
|
-
classes('primary') // → 'om-react-ui-button-primary'
|
|
84
|
-
classes('icon-start') // → 'om-react-ui-button-icon-start'
|
|
85
|
-
classes(void 0, 'extra') // → 'om-react-ui-button extra'
|
|
86
|
-
classes('sm', 'extra') // → 'om-react-ui-button-sm extra'
|
|
87
|
-
|
|
88
|
-
// joinCls concatenates non-falsy class strings
|
|
89
|
-
joinCls(classes('primary'), classes('large'), className)
|
|
90
|
-
// → 'om-react-ui-button-primary om-react-ui-button-large my-custom'
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## 2. `interface.ts` — TypeScript Interfaces
|
|
96
|
-
|
|
97
|
-
```ts
|
|
98
|
-
import type { ReactNode, RefObject } from 'react';
|
|
99
|
-
// Import PrimeReact component props
|
|
100
|
-
import type { PrimeComponentProps } from 'primereact/{component}';
|
|
101
|
-
|
|
102
|
-
// Use Omit<> to remove PrimeReact props that conflict with our custom API
|
|
103
|
-
// Common removals: 'label', 'severity', 'size', 'icon', 'className'
|
|
104
|
-
export interface {Name}Props extends Omit<PrimeComponentProps, 'label' | 'severity' | 'size'> {
|
|
105
|
-
// Ref prop with the correct HTML element type
|
|
106
|
-
ref?: RefObject<HTMLButtonElement | null>;
|
|
107
|
-
|
|
108
|
-
// Class name prefix — allows consumers to customize the BEM namespace
|
|
109
|
-
prefixCls?: string;
|
|
110
|
-
|
|
111
|
-
// Variant props as union types
|
|
112
|
-
color?: 'primary' | 'secondary' | 'grey' | 'black' | 'white' | 'danger' | 'warning';
|
|
113
|
-
size?: 'large' | 'medium' | 'small';
|
|
114
|
-
|
|
115
|
-
// Slot props for custom content
|
|
116
|
-
iconStart?: ReactNode;
|
|
117
|
-
iconEnd?: ReactNode;
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### Rules
|
|
122
|
-
|
|
123
|
-
- Always use `interface` (not `type`) for object structures
|
|
124
|
-
- Name: `{Name}Props`
|
|
125
|
-
- Extend PrimeReact props with `Omit<>` to avoid conflicts
|
|
126
|
-
- Use `ReactNode` for slot props, not `JSX.Element`
|
|
127
|
-
- Use `RefObject<HTMLElement | null>` for ref props
|
|
128
|
-
- Use literal union types for variant props (never `enum`)
|
|
129
|
-
- If variant values are also used in stories, define them as `as const` arrays in the stories file, not here
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
## 3. `index.ts` — Barrel Export
|
|
134
|
-
|
|
135
|
-
```ts
|
|
136
|
-
import {Name} from './{Name}';
|
|
137
|
-
|
|
138
|
-
export { {Name} } from './{Name}';
|
|
139
|
-
|
|
140
|
-
export default {Name};
|
|
141
|
-
|
|
142
|
-
export * from './interface';
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Rules
|
|
146
|
-
|
|
147
|
-
- Import default + re-export named + re-export default + re-export types
|
|
148
|
-
- This pattern ensures both `import {Name}` and `import { {Name} }` work
|
|
149
|
-
- `export *` from interface re-exports all type definitions
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
## 4. `{Name}.stories.tsx` — Storybook Stories
|
|
154
|
-
|
|
155
|
-
```tsx
|
|
156
|
-
import React from 'react';
|
|
157
|
-
import { fn } from '@storybook/test';
|
|
158
|
-
|
|
159
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
160
|
-
import { {Name} } from './index';
|
|
161
|
-
|
|
162
|
-
// Import styles for Storybook rendering
|
|
163
|
-
import './style';
|
|
164
|
-
|
|
165
|
-
// Define variant constants as `as const` for type safety and reuse
|
|
166
|
-
const COLORS = ['primary', 'secondary', 'grey', 'black', 'white', 'danger', 'warning'] as const;
|
|
167
|
-
const SIZES = ['large', 'medium', 'small'] as const;
|
|
168
|
-
|
|
169
|
-
const meta: Meta<typeof {Name}> = {
|
|
170
|
-
title: 'Components/{Name}',
|
|
171
|
-
component: {Name},
|
|
172
|
-
argTypes: {
|
|
173
|
-
// Boolean controls
|
|
174
|
-
disabled: { control: 'boolean' },
|
|
175
|
-
loading: { control: 'boolean' },
|
|
176
|
-
// Radio controls for variant props — spread the const array
|
|
177
|
-
size: { control: 'radio', options: [...SIZES] },
|
|
178
|
-
color: { control: 'radio', options: [...COLORS] },
|
|
179
|
-
},
|
|
180
|
-
args: {
|
|
181
|
-
// Sensible defaults for the controls panel
|
|
182
|
-
disabled: false,
|
|
183
|
-
loading: false,
|
|
184
|
-
size: 'medium',
|
|
185
|
-
color: 'primary',
|
|
186
|
-
onClick: fn(),
|
|
187
|
-
},
|
|
188
|
-
tags: ['autodocs'],
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
export default meta;
|
|
192
|
-
|
|
193
|
-
type Story = StoryObj<typeof {Name}>;
|
|
194
|
-
|
|
195
|
-
// ---- AllVariants ----
|
|
196
|
-
// Matrix of ALL size x color combinations for visual QA
|
|
197
|
-
// Tagged '!autodocs' to exclude from auto-generated docs
|
|
198
|
-
// Tagged 'dev' for developer-only visibility
|
|
199
|
-
export const AllVariants: Story = {
|
|
200
|
-
render: (args) => (
|
|
201
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 32 }}>
|
|
202
|
-
{SIZES.map(size => (
|
|
203
|
-
<div key={size}>
|
|
204
|
-
<h3 style={{ marginBottom: 12 }}>{size}</h3>
|
|
205
|
-
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center' }}>
|
|
206
|
-
{COLORS.map(color => (
|
|
207
|
-
<{Name} {...args} key={`${size}-${color}`} size={size} color={color}>
|
|
208
|
-
{color}
|
|
209
|
-
</{Name}>
|
|
210
|
-
))}
|
|
211
|
-
</div>
|
|
212
|
-
{/* Disabled row */}
|
|
213
|
-
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center', marginTop: 8 }}>
|
|
214
|
-
{COLORS.map(color => (
|
|
215
|
-
<{Name} {...args} key={`${size}-${color}-disabled`} size={size} color={color} disabled>
|
|
216
|
-
{color}
|
|
217
|
-
</{Name}>
|
|
218
|
-
))}
|
|
219
|
-
</div>
|
|
220
|
-
</div>
|
|
221
|
-
))}
|
|
222
|
-
</div>
|
|
223
|
-
),
|
|
224
|
-
args: {
|
|
225
|
-
children: undefined,
|
|
226
|
-
},
|
|
227
|
-
tags: ['!autodocs', 'dev'],
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
// ---- Feature-specific stories ----
|
|
231
|
-
export const Sizes: Story = {
|
|
232
|
-
render: (args) => (
|
|
233
|
-
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
|
|
234
|
-
{SIZES.map(size => (
|
|
235
|
-
<{Name} {...args} key={size} size={size}>
|
|
236
|
-
{size}
|
|
237
|
-
</{Name}>
|
|
238
|
-
))}
|
|
239
|
-
</div>
|
|
240
|
-
),
|
|
241
|
-
};
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
### Rules
|
|
245
|
-
|
|
246
|
-
- Import `fn` from `@storybook/test` for action callbacks (not `action` from `@storybook/addon-actions`)
|
|
247
|
-
- Meta title follows `Components/{Name}` convention
|
|
248
|
-
- Meta must include `tags: ['autodocs']`
|
|
249
|
-
- Variant constants are `as const` arrays spread into options
|
|
250
|
-
- `AllVariants` story always has `tags: ['!autodocs', 'dev']`
|
|
251
|
-
- At minimum: `AllVariants` + one feature story
|
|
252
|
-
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
## 5. `style/{Name}.scss` — Component Styles
|
|
256
|
-
|
|
257
|
-
```scss
|
|
258
|
-
@use '@/styles/api' as theme;
|
|
259
|
-
@use '@/styles/recipes/variants' as variants;
|
|
260
|
-
|
|
261
|
-
$component: '{kebab-name}';
|
|
262
|
-
|
|
263
|
-
// ── Variant DSL ──
|
|
264
|
-
// 1. Schema: auto-generate from component name + token key list
|
|
265
|
-
// Expands to: (--om-{kebab-name}-text: text, --om-{kebab-name}-bg: bg, ...)
|
|
266
|
-
$component-variant-schema: variants.om-variant-schema(
|
|
267
|
-
$component, text, bg, hover-bg, disabled-text, disabled-bg
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
// 2. Variants map: each variant defines all keys from the schema
|
|
271
|
-
$component-variants: (
|
|
272
|
-
'primary': (
|
|
273
|
-
text: theme.palette(text, 'on-neutral'),
|
|
274
|
-
bg: theme.palette(bg, 'brand'),
|
|
275
|
-
hover-bg: theme.palette(bg, 'brand-hover'),
|
|
276
|
-
disabled-text: theme.palette(text, 'disabled-white'),
|
|
277
|
-
disabled-bg: theme.palette(bg, 'disabled-brand'),
|
|
278
|
-
),
|
|
279
|
-
'secondary': (
|
|
280
|
-
text: theme.palette(text, 'brand'),
|
|
281
|
-
bg: theme.palette(bg, 'brand-secondary'),
|
|
282
|
-
hover-bg: theme.palette(bg, 'brand-secondary-hover'),
|
|
283
|
-
disabled-text: theme.palette(text, 'brand-secondary'),
|
|
284
|
-
disabled-bg: theme.palette(bg, 'brand-tertiary'),
|
|
285
|
-
),
|
|
286
|
-
// ... more variants
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
// Root selector — generates: .om-react-ui-{kebab-name}
|
|
290
|
-
.#{theme.$prefix}-#{$component} {
|
|
291
|
-
// 3. Apply default variant + validate all variants against schema
|
|
292
|
-
@include variants.om-variant-default($component-variants, 'primary', $component-variant-schema);
|
|
293
|
-
|
|
294
|
-
// ── Base styles — consume variant CSS custom properties ──
|
|
295
|
-
display: inline-flex;
|
|
296
|
-
gap: theme.spacing(gap, 200);
|
|
297
|
-
align-items: center;
|
|
298
|
-
justify-content: center;
|
|
299
|
-
padding: theme.spacing(component-padding, 400);
|
|
300
|
-
color: var(--om-{kebab-name}-text);
|
|
301
|
-
background-color: var(--om-{kebab-name}-bg);
|
|
302
|
-
border: none;
|
|
303
|
-
border-radius: theme.shape(300);
|
|
304
|
-
box-shadow: none;
|
|
305
|
-
cursor: pointer;
|
|
306
|
-
|
|
307
|
-
// Typography — emits font-family, font-size, line-height, etc.
|
|
308
|
-
@include theme.typography(label, lg);
|
|
309
|
-
|
|
310
|
-
// ── Disabled — reads variant custom properties ──
|
|
311
|
-
&.p-disabled {
|
|
312
|
-
color: var(--om-{kebab-name}-disabled-text);
|
|
313
|
-
background-color: var(--om-{kebab-name}-disabled-bg);
|
|
314
|
-
cursor: not-allowed;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// ── Hover — reads variant custom property ──
|
|
318
|
-
&:hover:not(.p-disabled) {
|
|
319
|
-
background-color: var(--om-{kebab-name}-hover-bg);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// ── Size variants ──
|
|
323
|
-
// Use &-{size} for variant classes
|
|
324
|
-
&-medium {
|
|
325
|
-
padding: theme.spacing(component-padding, 300);
|
|
326
|
-
border-radius: theme.shape(300);
|
|
327
|
-
@include theme.typography(label, md);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
&-small {
|
|
331
|
-
padding: theme.spacing(component-padding, 200);
|
|
332
|
-
border-radius: theme.shape(200);
|
|
333
|
-
@include theme.typography(label, sm);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// ── Slot styles ──
|
|
337
|
-
&-icon-start,
|
|
338
|
-
&-icon-end {
|
|
339
|
-
display: inline-flex;
|
|
340
|
-
align-items: center;
|
|
341
|
-
justify-content: center;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// ── Color variants (Variant DSL) ──
|
|
345
|
-
// Generate all modifier classes, skipping the default 'primary' variant
|
|
346
|
-
@include variants.om-variant-classes($component-variants, $component-variant-schema, $default: 'primary');
|
|
347
|
-
|
|
348
|
-
// ── Responsive ──
|
|
349
|
-
// @include theme.down(md) { ... }
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
### SCSS Rules
|
|
354
|
-
|
|
355
|
-
- First lines: `@use '@/styles/api' as theme;` and `@use '@/styles/recipes/variants' as variants;` (if using variants)
|
|
356
|
-
- `$component` is always kebab-case
|
|
357
|
-
- Root: `.#{theme.$prefix}-#{$component}` (do NOT hardcode `om-react-ui`)
|
|
358
|
-
- **Color variants**: Use the Variant DSL (`variants.om-variant-schema` + `variants.om-variant-default` + `variants.om-variant-classes`) with CSS custom properties instead of manual `&-{variant}` blocks for each color
|
|
359
|
-
- **Size variants**: Use `&-{size}` nesting (these remain manual since they change structural properties, not just colors)
|
|
360
|
-
- Disabled: target `.p-disabled` class (PrimeReact adds this)
|
|
361
|
-
- Hover: `&:hover:not(.p-disabled)` to skip disabled elements
|
|
362
|
-
- All values use token functions — no raw hex, px (for tokens), or font-family
|
|
363
|
-
- Exception: `!important` overrides on third-party (PrimeReact) styles are acceptable when necessary
|
|
364
|
-
- Variant names must be quoted strings (e.g., `'white'`, `'black'`) to avoid Sass color literal issues
|
|
365
|
-
|
|
366
|
-
---
|
|
367
|
-
|
|
368
|
-
## 6. `style/index.ts` — Style Entry Point
|
|
369
|
-
|
|
370
|
-
```ts
|
|
371
|
-
import './{Name}.scss';
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
Single line. This file is imported in stories and by the build system.
|
|
375
|
-
|
|
376
|
-
---
|
|
377
|
-
|
|
378
|
-
## 7. `__test__/index.test.tsx` — Snapshot Test
|
|
379
|
-
|
|
380
|
-
```tsx
|
|
381
|
-
import 'jsdom-global/register';
|
|
382
|
-
import * as React from 'react';
|
|
383
|
-
import { render, screen } from '@testing-library/react';
|
|
384
|
-
import '@testing-library/jest-dom';
|
|
385
|
-
import { {Name} } from '../index';
|
|
386
|
-
|
|
387
|
-
// Suppress known console errors from PrimeReact/JSDOM incompatibility
|
|
388
|
-
const originalConsoleError = console.error;
|
|
389
|
-
console.error = (message, ...optionalParams) => {
|
|
390
|
-
const errorMessage = typeof message === 'string' ? message : String(message);
|
|
391
|
-
if (
|
|
392
|
-
errorMessage.includes('Could not parse CSS stylesheet') ||
|
|
393
|
-
errorMessage.includes('findDOMNode is deprecated and will be removed')
|
|
394
|
-
) {
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
originalConsoleError(message, ...optionalParams);
|
|
398
|
-
};
|
|
399
|
-
|
|
400
|
-
// Mock lottie-web if any component in the dependency chain uses Loading/Spinner
|
|
401
|
-
jest.mock('lottie-web', () => ({
|
|
402
|
-
loadAnimation: jest.fn(() => ({
|
|
403
|
-
play: jest.fn(),
|
|
404
|
-
pause: jest.fn(),
|
|
405
|
-
destroy: jest.fn(),
|
|
406
|
-
})),
|
|
407
|
-
}));
|
|
408
|
-
|
|
409
|
-
describe('{Name}', () => {
|
|
410
|
-
it('renders correctly', () => {
|
|
411
|
-
const wrapper = render(
|
|
412
|
-
<{Name}>Test</{Name}>
|
|
413
|
-
);
|
|
414
|
-
expect(wrapper).toMatchSnapshot();
|
|
415
|
-
});
|
|
416
|
-
});
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
### Rules
|
|
420
|
-
|
|
421
|
-
- Always import `jsdom-global/register` first
|
|
422
|
-
- Always suppress the two known console errors
|
|
423
|
-
- Always mock `lottie-web` (safe even if not needed)
|
|
424
|
-
- At minimum: one `renders correctly` snapshot test
|
|
425
|
-
- Optionally add tests for variants, events, disabled state
|
|
426
|
-
|
|
427
|
-
---
|
|
428
|
-
|
|
429
|
-
## 8. `README.md` — Component Documentation
|
|
430
|
-
|
|
431
|
-
```md
|
|
432
|
-
# {Name}
|
|
433
|
-
|
|
434
|
-
Brief description of what the component does and when to use it.
|
|
435
|
-
|
|
436
|
-
## Import
|
|
437
|
-
|
|
438
|
-
\```tsx
|
|
439
|
-
import { {Name} } from '@1money/components-ui';
|
|
440
|
-
// or
|
|
441
|
-
import { {Name} } from '@1money/components-ui/{Name}';
|
|
442
|
-
\```
|
|
443
|
-
|
|
444
|
-
## Usage
|
|
445
|
-
|
|
446
|
-
\```tsx
|
|
447
|
-
<{Name} color="primary" size="medium">
|
|
448
|
-
Click me
|
|
449
|
-
</{Name}>
|
|
450
|
-
\```
|
|
451
|
-
|
|
452
|
-
## Props
|
|
453
|
-
|
|
454
|
-
| Prop | Type | Default | Description |
|
|
455
|
-
|------|------|---------|-------------|
|
|
456
|
-
| `color` | `'primary' \| 'secondary' \| ...` | `'primary'` | Visual color variant |
|
|
457
|
-
| `size` | `'large' \| 'medium' \| 'small'` | `'medium'` | Size variant |
|
|
458
|
-
| `disabled` | `boolean` | `false` | Disables the component |
|
|
459
|
-
| `loading` | `boolean` | `false` | Shows loading spinner |
|
|
460
|
-
| `prefixCls` | `string` | `'{kebab-name}'` | CSS class prefix |
|
|
461
|
-
| `className` | `string` | `''` | Additional CSS classes |
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
---
|
|
465
|
-
|
|
466
|
-
## Library Registration (`src/index.ts`)
|
|
467
|
-
|
|
468
|
-
After creating all component files, update the barrel export:
|
|
469
|
-
|
|
470
|
-
```ts
|
|
471
|
-
// Add re-export for the component (alphabetical order)
|
|
472
|
-
export { {Name} } from './components/{Name}';
|
|
473
|
-
|
|
474
|
-
// Add type re-export
|
|
475
|
-
export type { {Name}Props } from './components/{Name}';
|
|
476
|
-
```
|
|
477
|
-
|
|
478
|
-
Note: The current `src/index.ts` uses only re-export statements — there is no import block and no default export object.
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
# Figma Extraction Checklist
|
|
2
|
-
|
|
3
|
-
Checklist for extracting design data from Figma when building new `@1money/components-ui` library components. Adapted for library-internal development where Figma tokens must map to the internal style system (not consumer-facing Foundation tokens).
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Pre-flight
|
|
8
|
-
|
|
9
|
-
- [ ] Figma URL is a design or node link (not a prototype or FigJam board)
|
|
10
|
-
- [ ] Node ID is known, or top-level frames have been listed and the user has selected one
|
|
11
|
-
- [ ] Component name confirmed (PascalCase)
|
|
12
|
-
|
|
13
|
-
## Extraction Steps
|
|
14
|
-
|
|
15
|
-
### Step 1 — Get Design Context
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
mcp__figma__get_design_context(nodeId, fileKey)
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
- [ ] Call returned valid data (code + screenshot)
|
|
22
|
-
- [ ] If MCP returned empty or errors, retried once before reporting failure
|
|
23
|
-
- [ ] Noted the reference code structure (React+Tailwind hints) for mapping guidance
|
|
24
|
-
|
|
25
|
-
### Step 2 — Get Screenshot
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
mcp__figma__get_screenshot(nodeId, fileKey)
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
- [ ] Called for any frame where layout, spacing, or alignment is ambiguous
|
|
32
|
-
- [ ] Screenshot reviewed for: padding, gap, border-radius, shadow, alignment
|
|
33
|
-
- [ ] Visual hierarchy and nesting structure understood
|
|
34
|
-
|
|
35
|
-
### Step 3 — Get Variable Definitions
|
|
36
|
-
|
|
37
|
-
```
|
|
38
|
-
mcp__figma__get_variable_defs(nodeId, fileKey)
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
- [ ] Design tokens retrieved (colors, spacing, radii, typography)
|
|
42
|
-
- [ ] Figma variable names noted for mapping to internal style system
|
|
43
|
-
|
|
44
|
-
### Step 4 — Get Metadata (if no Node ID)
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
mcp__figma__get_metadata(nodeId, fileKey)
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
- [ ] Top-level frames listed
|
|
51
|
-
- [ ] User selected the target frame
|
|
52
|
-
- [ ] Re-run Steps 1-3 with the selected Node ID
|
|
53
|
-
|
|
54
|
-
## Token Mapping Phase
|
|
55
|
-
|
|
56
|
-
Map every Figma design value to the internal style system. Consult `StyleSystemAPI.md` and `SemanticColors.md`.
|
|
57
|
-
|
|
58
|
-
### Colors
|
|
59
|
-
|
|
60
|
-
- [ ] Background fills mapped to `theme.palette(bg, ...)` keys
|
|
61
|
-
- [ ] Text colors mapped to `theme.palette(text, ...)` keys
|
|
62
|
-
- [ ] Icon colors mapped to `theme.palette(icon, ...)` keys
|
|
63
|
-
- [ ] Border colors mapped to `theme.palette(border, ...)` keys
|
|
64
|
-
- [ ] No raw hex values remain unmapped
|
|
65
|
-
|
|
66
|
-
### Spacing
|
|
67
|
-
|
|
68
|
-
- [ ] Padding values mapped to `theme.spacing(component-padding, key)` or `theme.spacing(scale, key)`
|
|
69
|
-
- [ ] Gap values mapped to `theme.spacing(gap, key)`
|
|
70
|
-
- [ ] Margin values mapped to `theme.spacing(scale, key)`
|
|
71
|
-
- [ ] No raw pixel values for spacing remain unmapped
|
|
72
|
-
|
|
73
|
-
### Typography
|
|
74
|
-
|
|
75
|
-
- [ ] Font family, size, weight, line-height identified per text layer
|
|
76
|
-
- [ ] Mapped to `@include theme.typography(category, size)` — find the closest match from:
|
|
77
|
-
- `display`: xl/lg/md/sm/xs (Aeonik)
|
|
78
|
-
- `headline`: lg/md/sm/xs (Aeonik)
|
|
79
|
-
- `title`: lg/md/sm (Inter, strong=700)
|
|
80
|
-
- `body`: lg/md/sm (Inter, strong=500)
|
|
81
|
-
- `link`: md/sm (Inter)
|
|
82
|
-
- `label`: xl/lg/md/sm/xs (Inter, strong=600-700)
|
|
83
|
-
- [ ] Any `$strong: true` variants identified
|
|
84
|
-
|
|
85
|
-
### Visual Properties
|
|
86
|
-
|
|
87
|
-
- [ ] Border-radius mapped to `theme.shape(key)` keys (0/100/200/300/400/600/full)
|
|
88
|
-
- [ ] Box-shadow mapped to `theme.shadows(key)` keys (0/100/200)
|
|
89
|
-
- [ ] Opacity mapped to `theme.opacity(key)` keys
|
|
90
|
-
- [ ] Component heights mapped to `theme.sizing(component-height, key)` keys (xs/sm/md/lg/xl/2xl/3xl)
|
|
91
|
-
|
|
92
|
-
## Variant Identification
|
|
93
|
-
|
|
94
|
-
- [ ] All visual variants listed (e.g., color variants: primary, secondary, danger, etc.)
|
|
95
|
-
- [ ] All size variants listed (e.g., large, medium, small)
|
|
96
|
-
- [ ] Variant naming aligned with existing library conventions (check Button for reference)
|
|
97
|
-
- [ ] Each variant's token mapping documented
|
|
98
|
-
|
|
99
|
-
## Slot Identification
|
|
100
|
-
|
|
101
|
-
- [ ] Content slots identified (e.g., `children`, `label`, `description`)
|
|
102
|
-
- [ ] Icon slots identified (e.g., `iconStart`, `iconEnd`, `icon`)
|
|
103
|
-
- [ ] Custom render slots identified (e.g., `header`, `footer`, `prefix`, `suffix`)
|
|
104
|
-
- [ ] Each slot typed as `ReactNode` in the interface
|
|
105
|
-
|
|
106
|
-
## State Coverage Assessment
|
|
107
|
-
|
|
108
|
-
### Required States (implement even if not in Figma)
|
|
109
|
-
|
|
110
|
-
- [ ] **Default**: Normal resting state
|
|
111
|
-
- [ ] **Hover**: `:hover:not(.p-disabled)` interaction
|
|
112
|
-
- [ ] **Focus**: `:focus-visible` interaction (if applicable)
|
|
113
|
-
- [ ] **Active**: `:active` interaction (if applicable)
|
|
114
|
-
- [ ] **Disabled**: `.p-disabled` class with `cursor: not-allowed` and muted colors
|
|
115
|
-
- [ ] **Loading**: Spinner integration (if applicable)
|
|
116
|
-
|
|
117
|
-
### Conditional States (implement if component type requires)
|
|
118
|
-
|
|
119
|
-
- [ ] **Error/Danger**: Validation error state with danger colors
|
|
120
|
-
- [ ] **Warning**: Warning state with warning colors
|
|
121
|
-
- [ ] **Success/Positive**: Success state with positive colors
|
|
122
|
-
- [ ] **Selected/Active**: Toggle or selection state
|
|
123
|
-
- [ ] **Empty**: Empty/placeholder state
|
|
124
|
-
|
|
125
|
-
## Unmapped Items
|
|
126
|
-
|
|
127
|
-
- [ ] All Figma nodes that could not be mapped are listed explicitly
|
|
128
|
-
- [ ] Blocking ambiguities escalated to the user before implementation
|
|
129
|
-
- [ ] Asset placeholders documented for images/icons without a component match
|
|
130
|
-
|
|
131
|
-
## Summary Table Template
|
|
132
|
-
|
|
133
|
-
Use this table format to document the mapping:
|
|
134
|
-
|
|
135
|
-
```markdown
|
|
136
|
-
| Figma Element | Figma Value | Style System Function | Key |
|
|
137
|
-
|---------------|-------------|----------------------|-----|
|
|
138
|
-
| Background | #1D4ED8 | `theme.palette(bg, ...)` | `brand` |
|
|
139
|
-
| Text | #FFFFFF | `theme.palette(text, ...)` | `on-neutral` |
|
|
140
|
-
| Padding | 16px | `theme.spacing(component-padding, ...)` | `400` |
|
|
141
|
-
| Border radius | 12px | `theme.shape(...)` | `300` |
|
|
142
|
-
| Height | 52px | `theme.sizing(component-height, ...)` | `2xl` |
|
|
143
|
-
| Typography | Inter 14/500 | `theme.typography(...)` | `label, md` |
|
|
144
|
-
```
|