Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 6 additions & 143 deletions .cursor/rules/guidelines.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -27,152 +27,13 @@ Package name: `@cube-dev/ui-kit`

All tests: `$ pnpm test`
Specific test: `$ pnpm test -- {TestFileName}`
Update snaphosts: `$ pnpm test -u -- {TestFileName}`
Update snapshots: `$ pnpm test -u -- {TestFileName}`

## Build

`pnpm run build`

## Lint + Fix

`pnpm run fix`

# Stack

- `tasty` style helper.
- `src/tasty` - sources
- `src/stories/Tasty.docs.mdx` - documentation
- `src/stories/Styles.docs.mdx` - custom tasty styles documentation
- `src/stories/CreateComponent.docs.mdx` - create components using tasty helper.
- Storybook v8.6
- React and React DOM v18
- `react-aria` and `react-stately` with the latest versions.
- `tabler/icons-react` - icons.

# Recomendations

- Use `DOCUMENTATION_GUIDELINES.md` for writing documentation for components.
- Use icons from `/src/icons` if they have a required one. If not - use `tabler/icons-react`. If we need to customize the size or color of the icon, then wrap it with `<Icon/>` component and pass all required props there. Do not add any props to the tabler icons directly.

## Form System

- Form validation uses async rule-based system with built-in validators:
- `required` - field is required
- `type` - validates data type (email, url, number, etc.)
- `pattern` - regex pattern validation
- `min`/`max` - length/value constraints
- `enum` - allowed values
- `whitespace` - non-empty content
- `validator` - custom async function
- Form fields support direct integration without Field wrapper
- Use `useForm` hook for form instance management
- Form state includes validation, touched state, and error handling

## Testing

- Testing setup: Jest + React Testing Library + `@testing-library/react-hooks`
- Test configuration: `src/test/setup.ts` with custom configurations
- Testing utilities: `src/test/render.tsx` provides `renderWithRoot` wrapper
- QA attributes: Use `qa` prop for e2e testing selectors (`data-qa`)
- Test environment: Uses `jsdom` with React 18 act() environment
- Coverage: Run `pnpm test-cover` for coverage reports

## Accessibility

- All components use React Aria hooks for accessibility
- Keyboard navigation patterns are consistent across components
- ARIA attributes are automatically managed by React Aria
- Screen reader support is built-in with proper announcements
- Focus management is handled automatically
- Components support all standard ARIA labeling props

## TypeScript

- Interface naming: Use descriptive names with `Props` suffix for component props
- Base props: Extend from `BaseProps` or `AllBaseProps` for standard properties
- Form types: Use `FieldTypes` interface for form field type definitions
- Style props: Use specific style prop interfaces (e.g., `ContainerStyleProps`)
- Generic constraints: Use `extends` for type safety in form and field components

## Component Architecture

- Use `filterBaseProps` to separate design system props from DOM props
- Export pattern: Use barrel exports with compound components (e.g., `Button.Group`)
- Style system: Use `extractStyles` for separating style props from other props
- Modifiers: Use `mods` prop for state-based styling
- Sub-elements: Use `data-element` attribute for targeting specific parts in styles

## Style System (Tasty)

- Use `tasty` documentation
- Use `tasty` custom styles with tasty syntax when possible.
- Use `style` property only for dynamic styles and tokens (css custom properties).
- Style categories: BASE, POSITION, BLOCK, COLOR, TEXT, DIMENSION, FLOW, CONTAINER, OUTER
- Responsive values: Use arrays for breakpoint-based styling
- Modifiers: Use object syntax for conditional styles
- Sub-elements: Target inner elements using capitalized keys in styles
- Style props: Direct style application without `styles` prop
- CSS custom properties: Use `@token-name` syntax for design tokens
- To declare a CSS animation use `keyframes` method and then pass the animation name to the tasty styles.

## Export Patterns

- Compound components: Use `Object.assign` pattern for sub-components
- Barrel exports: Each category has index.ts for re-exports
- Main export: All components exported from `src/index.ts`
- Type exports: Export component prop types for external use

## Development Workflow

- Branch naming: `[type/(task-name | scope)]` (e.g., `feat/button-group`)
- Commit convention: `category: message` format
- Changesets: Use `pnpm changeset` for version management
- Code snippets: Use `jsx live=false` for documentation snippets
- Storybook: Two modes - `stories` and `docs` for different outputs

## Performance

- Icon optimization: Reuse icon components, wrap with `<Icon/>` for customization
- Style caching: Tasty system includes built-in style caching
- Bundle size: Monitor with `pnpm size` command
- Lazy loading: Use dynamic imports for large components

## Error Handling

- Form validation: Async error handling with Promise-based validation
- Console suppression: Test setup includes act() warning suppression
- Error boundaries: Use proper error boundaries in complex components
- Validation state: Use `validationState` prop for field error states

# Description

Package name: `@cube-dev/ui-kit`

# Project Structure

## Component file structure (preferable)

/src/components/{category}/{ComponentName}/
- {ComponentName}.tsx – implementation of the component
- {ComponentName}.docs.mdx - documentation
- {ComponentName}.stories.tsx - Storybook stories
- {ComponentName}.test.tsx - Unit tests
- index.tsx - re-export of all instances

## Icons

/src/icons/

# Commands

## Test

All tests: `$ pnpm test`
Specific test: `$ pnpm test -- {TestFileName}`

## Build

`pnpm run build`
Build tool: `tsdown` (config in `tsdown.config.ts`). Uses unbundled ESM output.

## Lint + Fix

Expand All @@ -192,6 +53,7 @@ Specific test: `$ pnpm test -- {TestFileName}`

# Recomendations

- Use `DOCUMENTATION_GUIDELINES.md` for writing documentation for components.
- Use icons from `/src/icons` if they have a required one. If not - use `tabler/icons-react`. If we need to customize the size or color of the icon, then wrap it with `<Icon/>` component and pass all required props there. Do not add any props to the tabler icons directly.

## Form System
Expand All @@ -210,11 +72,12 @@ Specific test: `$ pnpm test -- {TestFileName}`

## Testing

- Testing setup: Jest + React Testing Library + `@testing-library/react-hooks`
- Test configuration: `src/test/setup.ts` with custom configurations
- Testing setup: Vitest + React Testing Library
- Test configuration: `vitest.config.ts` + `src/test/setup.ts`
- Testing utilities: `src/test/render.tsx` provides `renderWithRoot` wrapper
- QA attributes: Use `qa` prop for e2e testing selectors (`data-qa`)
- Test environment: Uses `jsdom` with React 18 act() environment
- Vitest globals are enabled (`vi.mock`, `vi.fn`, `vi.spyOn`, `describe`, `it`, `expect`, etc.)
- Coverage: Run `pnpm test-cover` for coverage reports

## Accessibility
Expand Down
10 changes: 6 additions & 4 deletions .cursor/rules/tests.mdc
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
---
globs: *.test.tsx
globs: *.test.tsx,*.test.ts
alwaysApply: false
---

# Testing Rules for UI Kit

Test runner: **Vitest** (globals enabled — `vi`, `describe`, `it`, `expect` etc. are available without imports).

## Setup

- Place test files next to components with `.test.tsx` extension
- Mock internal warnings: `jest.mock('../../../_internal/hooks/use-warn')`
- Mock internal warnings: `vi.mock('../../../_internal/hooks/use-warn')`
- Define test data at the top of `describe` blocks

## Render Functions
Expand Down Expand Up @@ -55,7 +57,7 @@ Wait for removal: `await waitForElementToBeRemoved(() => queryByRole('dialog'))`
### Basic Rendering & User Interactions
```tsx
it('should handle button press', async () => {
const onPress = jest.fn();
const onPress = vi.fn();
const { getByRole } = render(<Button onPress={onPress}>Label</Button>);

await userEvent.click(getByRole('button'));
Expand Down Expand Up @@ -163,7 +165,7 @@ it.each([
['aria-label', { 'aria-label': 'test' }],
['label', { label: 'test' }],
])('should not warn if %s is provided', (_, props) => {
const spy = jest.spyOn(console, 'warn').mockImplementation(() => {});
const spy = vi.spyOn(console, 'warn').mockImplementation(() => {});
render(<Button {...props} />);
expect(spy).not.toHaveBeenCalled();
spy.mockRestore();
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ jobs:
run: npm config delete //registry.npmjs.org/:_authToken || true

- name: Publish canary to npm
working-directory: ./dist
run: npm publish --access public --tag pr_${{ github.event.number }} --provenance

- name: Comment PR
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:
run: pnpm lint

- name: Run the tests
run: pnpm test:no-cache
run: pnpm test

deploy-chromatic:
name: 'Prepare Storybook for review & tests'
Expand Down
6 changes: 3 additions & 3 deletions .size-limit.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const reportFolder = process.env.REPORT_FOLDER ?? './size-limit-report';
module.exports = [
{
name: 'All',
path: './dist/es/index.js',
path: './dist/index.js',
webpack: true,
import: '*',
modifyWebpackConfig: (webpackConfig) => {
Expand All @@ -24,14 +24,14 @@ module.exports = [
},
{
name: 'Tree shaking (just a Button)',
path: './dist/es/index.js',
path: './dist/index.js',
webpack: true,
import: '{ Button }',
limit: '62kB',
},
{
name: 'Tree shaking (just tasty)',
path: './dist/es/index.js',
path: './dist/index.js',
webpack: true,
import: '{ tasty }',
limit: '38kB',
Expand Down
26 changes: 26 additions & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// @ts-check
import { readFileSync } from 'node:fs';

import react from '@vitejs/plugin-react';
import remarkGfm from 'remark-gfm';

const pkg = JSON.parse(readFileSync('./package.json', 'utf-8'));

const config = {
staticDirs: ['../public'],

Expand Down Expand Up @@ -41,5 +46,26 @@ const config = {
},
},
],

viteFinal(config) {
const REACT_PLUGIN_NAMES = ['vite:react-babel', 'vite:react-refresh'];
const existingPlugins = (config.plugins ?? [])
.flat()
.filter(
(p) =>
p &&
typeof p === 'object' &&
!REACT_PLUGIN_NAMES.includes(/** @type {any} */ (p).name),
);

config.plugins = [...existingPlugins, react({ jsxRuntime: 'automatic' })];

config.define = {
...config.define,
__UIKIT_VERSION__: JSON.stringify(pkg.version),
};

return config;
},
};
export default config;
7 changes: 6 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,12 @@ export default [
// TypeScript files
{
files: ['**/*.{ts,tsx}'],
ignores: ['vite.config.ts', 'tasty.config.ts'],
ignores: [
'vite.config.ts',
'vitest.config.ts',
'tsdown.config.ts',
'tasty.config.ts',
],
languageOptions: {
parser,
parserOptions: {
Expand Down
27 changes: 0 additions & 27 deletions jest.config.cjs

This file was deleted.

Loading
Loading