From 611133f5db055ad1f1e5c72ef67204fce13f724c Mon Sep 17 00:00:00 2001 From: John Toro Date: Mon, 26 Oct 2026 20:08:35 -0500 Subject: [PATCH 1/3] feat: standardize components, a11y, security hardening, upgrade deps & CI to v2.6.0 --- .agents/skills/create-component/SKILL.md | 177 +- .github/workflows/chromatic.yml | 16 +- .github/workflows/npm-build.yml | 28 +- .github/workflows/publish.yml | 19 +- .nvmrc | 2 +- .prettierrc | 2 +- ADR.md | 8 +- CONTEXT.md | 10 +- Changelog.md | 30 + README.md | 13 +- package-lock.json | 1775 +++++------------ package.json | 65 +- .../react-hook-form/controller.component.tsx | 19 +- src/theme/button.css | 26 + src/theme/main.css | 8 + src/types/helpers/controller.types.ts | 39 +- src/types/index.ts | 1 + .../display/accordion/accordion.component.tsx | 5 +- .../display/accordion/accordion.stories.tsx | 1 + .../display/accordion/accordion.types.ts | 6 +- .../avatar-group/avatar-group.component.tsx | 28 +- .../display/avatar/avatar.component.tsx | 5 +- .../display/avatar/avatar.stories.tsx | 1 + .../components/display/avatar/avatar.types.ts | 1 + .../display/badge/badge.component.tsx | 5 +- .../display/badge/badge.stories.tsx | 1 + .../components/display/badge/badge.types.ts | 1 + .../display/button/button.component.tsx | 3 +- .../display/button/button.stories.tsx | 1 + .../display/card/card.component.tsx | 7 +- .../display/carousel/carousel.component.tsx | 3 +- .../chat-bubble/chat-bubble.component.tsx | 3 +- .../hover-gallery/hover-gallery.component.tsx | 7 +- .../display/mask/mask.component.tsx | 6 +- .../display/rating/rating.component.tsx | 6 +- .../display/swap/swap.component.tsx | 2 + .../display/timeline/timeline.component.tsx | 2 +- .../feedback/alert/alert.component.tsx | 5 +- .../components/feedback/alert/alert.types.ts | 1 + .../feedback/modal/modal.component.tsx | 26 +- .../components/feedback/modal/modal.test.tsx | 11 + .../components/feedback/modal/modal.types.ts | 1 + .../feedback/progress/progress.component.tsx | 3 +- .../feedback/toast/toast.component.tsx | 11 +- .../components/feedback/toast/toast.types.ts | 1 + .../feedback/tooltip/tooltip.component.tsx | 5 +- .../feedback/tooltip/tooltip.types.ts | 1 + .../layout/collapse/collapse.component.tsx | 8 +- .../layout/footer/footer.component.tsx | 3 +- .../components/layout/hero/hero.component.tsx | 12 +- .../action-dropdown.component.tsx | 2 + .../breadcrumb/breadcrumb.component.tsx | 16 +- .../navigation/breadcrumb/breadcrumb.types.ts | 1 + .../navigation/drawer/drawer.component.tsx | 16 +- .../navigation/drawer/drawer.test.tsx | 11 + .../navigation/link/link.component.tsx | 12 +- .../mega-menu/mega-menu.component.tsx | 6 +- .../navigation/menu/menu.component.tsx | 3 +- .../pagination/pagination.component.tsx | 6 +- .../navigation/pagination/pagination.types.ts | 1 + .../navigation/steps/steps.component.tsx | 10 +- .../navigation/steps/steps.types.ts | 1 + .../navigation/tabs/tabs.component.tsx | 49 +- .../components/navigation/tabs/tabs.test.tsx | 19 + .../components/navigation/tabs/tabs.types.ts | 1 + src/ui/forms/calendar/calendar.component.tsx | 2 +- src/ui/forms/calendar/calendar.test.tsx | 11 + .../__snapshots__/checkbox.test.tsx.snap | 2 +- src/ui/forms/checkbox/checkbox.component.tsx | 5 +- src/ui/forms/checkbox/checkbox.stories.tsx | 1 + src/ui/forms/checkbox/checkbox.types.ts | 1 + .../__snapshots__/dropdown.test.tsx.snap | 2 +- src/ui/forms/dropdown/dropdown.component.tsx | 4 +- src/ui/forms/dropdown/dropdown.stories.tsx | 1 + src/ui/forms/dropdown/dropdown.types.ts | 2 +- .../forms/file-input/file-input.component.tsx | 83 +- src/ui/forms/file-input/file-input.types.ts | 9 +- src/ui/forms/otp/otp.component.tsx | 112 +- src/ui/forms/otp/otp.types.ts | 6 +- src/ui/forms/radio-list/index.ts | 2 + .../forms/radio-list/radio-list.component.tsx | 13 +- .../forms/radio-list/radio-list.stories.tsx | 1 + src/ui/forms/radio-list/radio-list.types.ts | 6 + .../radio/__snapshots__/radio.test.tsx.snap | 2 +- src/ui/forms/radio/radio.component.tsx | 5 +- src/ui/forms/radio/radio.stories.tsx | 1 + src/ui/forms/radio/radio.types.ts | 5 +- src/ui/forms/range/range.component.tsx | 100 +- src/ui/forms/range/range.types.ts | 6 +- src/ui/forms/select/select.component.tsx | 117 +- src/ui/forms/select/select.types.ts | 6 +- src/ui/forms/textarea/textarea.component.tsx | 85 +- src/ui/forms/textarea/textarea.types.ts | 6 +- .../__snapshots__/textbox.test.tsx.snap | 2 +- src/ui/forms/textbox/textbox.component.tsx | 6 +- src/ui/forms/textbox/textbox.stories.tsx | 1 + src/ui/forms/toggle/toggle.component.tsx | 102 +- src/ui/forms/toggle/toggle.types.ts | 6 +- src/ui/forms/validator/validator.test.tsx | 10 + .../creativecode/creativecode-ui.provider.tsx | 21 +- src/ui/provider/index.ts | 2 + src/utils/string.utils.test.ts | 44 +- src/utils/string.utils.ts | 50 +- 103 files changed, 1715 insertions(+), 1699 deletions(-) create mode 100644 src/ui/forms/radio-list/radio-list.types.ts diff --git a/.agents/skills/create-component/SKILL.md b/.agents/skills/create-component/SKILL.md index d44b465..0e143b2 100644 --- a/.agents/skills/create-component/SKILL.md +++ b/.agents/skills/create-component/SKILL.md @@ -7,7 +7,7 @@ description: >- # Create Component Workflow for @creativecodeco/ui -Follow this standardized 6-step workflow whenever creating a new component or form control in `@creativecodeco/ui`. +Follow this standardized workflow and best-practices checklist whenever creating or updating a UI component or Form control in `@creativecodeco/ui`. --- @@ -17,17 +17,40 @@ Create the props interface co-located in `src/ui/components//Type` (e.g. `CardType`, `StepsType`). -- Mark all optional props with `?`. -- Use specific types (`React.ReactNode`, `React.Key`, `ColorType`, `SizeType`, etc.). +- Name the interface `Type` (e.g. `CardType`, `StepsType`, `SelectType`). +- **Always include `className?: string`** on every component interface so consumers can compose layout utilities cleanly. +- **For Form controls (`src/ui/forms/*`)**: Always extend `ErrorType` (`import type { ErrorType } from '@/types'`) so the control supports both `isError?: boolean` and `error?: string`, plus `id?: string`, `name?: string`, `label?: string`, and `disabled?: boolean`. +- Mark all non-mandatory props as optional with `?` to prevent friction in unit tests, Storybook, and React Hook Form `Controller`. +- Use specific types (`React.ReactNode`, `React.Key`, `ColorType`, `SizeType`, etc.) — **zero `any` or implicit types**. - Re-export the types from the component's `index.ts` (`export type * from './.types';`), which automatically flows through `src/types/index.ts`. +### UI Component Interface Example + ```ts -import type { ColorType } from '@/types'; +import type { ColorType, SizeType } from '@/types'; export interface ComponentNameType { children?: React.ReactNode; - variant?: ColorType; + color?: ColorType; + size?: SizeType; + disabled?: boolean; + className?: string; +} +``` + +### Form Control Interface Example + +```ts +import type { ColorType, ErrorType, SizeType } from '@/types'; + +export interface FormControlNameType extends ErrorType { + id?: string; + name?: string; + label?: string; + value?: string; + onChange?: React.ChangeEventHandler; + color?: ColorType; + size?: SizeType; disabled?: boolean; className?: string; } @@ -35,46 +58,128 @@ export interface ComponentNameType { --- -## Step 2: Implement Component Logic & Layout +## Step 2: Implement Component Logic, A11y & Responsive Design Create `src/ui/components///.component.tsx` (or `src/ui/forms//.component.tsx`). -**Rules:** +### 2.1 Core Implementation Rules - Use `classnames` imported as `import cls from 'classnames'`. - Import types from `@/types`. -- Use default parameters for optional props. -- Ensure strict React key handling when mapping arrays: `key={item.id || index}`. +- **Safe `className` Merging**: Always destructure `className` and merge it via `cls('base-class', className, { ... })` **after** or separately from `{...restProps}` so spreading HTML attributes never overwrites internal DaisyUI classes. +- **Prevent Custom Prop Leakage to DOM**: Always destructure custom library props (`isError`, `error`, `label`, `color`, `size`, `bordered`) before spreading `{...restProps}` onto native DOM elements (``, `