Skip to content

Commit ab010d1

Browse files
authored
Merge pull request #2899 from innovaccer/develop
Develop
2 parents 925b44c + 403daed commit ab010d1

497 files changed

Lines changed: 7409 additions & 4736 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cursor/rules/wcag-a11y-audit-report.mdc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
---
22
alwaysApply: true
33
---
4-
54
Use these sources as the primary references:
65
- [a11y-context/wcag-as-json.json](mdc:a11y-context/wcag-as-json.json) for the full WCAG 2.2 success criteria set.
76
- [wcag-audit-patterns.md](mdc:.cursor/rules/wcag-audit-patterns.md) for audit methodology.

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
"typescriptreact",
77
],
88
"editor.codeActionsOnSave": {
9-
"source.fixAll.eslint": true,
9+
"source.fixAll.eslint": "explicit"
1010
},
1111
}

CLAUDE.md

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# AGENTS
2+
3+
This file defines the working conventions for `design-system`.
4+
Follow these instructions for every pull request.
5+
6+
---
7+
8+
## Environment & Tooling
9+
- **Node/NPM:** Node ≥20.15.0 and npm ≥10.7.0.
10+
- **Dependency manager:** Use `npm` with the committed `package-lock.json`.
11+
12+
---
13+
14+
## Design Tokens (MANDATORY)
15+
Design tokens are the source of truth for colors, spacing, radius, elevation, etc.
16+
17+
- **Token sources:**
18+
- Raw tokens: `css/src/tokens/index.css`
19+
- Token mappings & variables: `css/src/variables/index.css`
20+
- **Usage:**
21+
- Reference tokens through CSS variables (e.g., `var(--primary)`, `var(--border-radius-10)`, `var(--shadow-m)`) & only use tokens defined in `css/src/variables/index.css` files as css variables.
22+
- Do not hard-code hex values, pixel sizes, or box-shadow rules outside the token files.
23+
- Add or modify tokens only within the token files.
24+
25+
---
26+
27+
## Design System Components (MANDATORY)
28+
Always use existing design system components before creating new ones or using native HTML elements.
29+
30+
- **Component library:** `core/components/`
31+
- **Available atoms:** Avatar, AvatarGroup, Badge, Breadcrumbs, Button, Caption, Card, Checkbox, Chip, ChipGroup, Divider, Dropdown, Flex, Heading, HelpText, Icon, Input, Label, Legend, Link, LinkButton, Message, Meter, Paragraph, Pills, ProgressBar, ProgressRing, Radio, Row, Column, SegmentedControl, Slider, Spinner, StatusHint, Subheading, SwitchInput, Text, Textarea, Toast, and more.
32+
- **Available molecules:** Chat, ChatMessage, ChipInput, Dialog, Dropzone, EditableChipInput, EditableDropdown, EditableInput, EmptyState, FileList, FileUploader, FullscreenModal, InputMask, KeyValuePair, Modal, Pagination, Placeholder, Popover, Sidesheet, Stepper, Tabs, Tooltip, VerificationCodeInput, and more.
33+
- **Available organisms:** Calendar, ChoiceList, Combobox, DatePicker, DateRangePicker, Grid, HorizontalNav, InlineMessage, List, Listbox, Menu, Navigation, PageHeader, Select, Table, TextField, TimePicker, VerticalNav, and more.
34+
- **Rules:**
35+
- Import from the component's `index.tsx` (e.g., `import { Button } from '@/core/components/atoms/button'`).
36+
- Extend existing components via props rather than wrapping with custom HTML.
37+
- Do not replicate functionality already covered by an existing component.
38+
- When a component doesn't exist yet, follow the component layout structure below and build it from atomic components upward.
39+
40+
---
41+
42+
## Code Style & Structure
43+
- **Languages:** TypeScript & React only.
44+
- **File naming:** PascalCase for component files; use `.tsx` for components.
45+
- **Component layout:**
46+
````
47+
48+
core/
49+
├── index.tsx # Export Components
50+
├── index.type.tsx # Export Components Props/Types
51+
├── components/{atoms|molecules|organisms}/ComponentName/
52+
├── index.tsx # Component Main entry file
53+
├── {ComponentName}.tsx # Component Implementation
54+
├── __tests__/ # Component Unit Tests
55+
├── __stories__/ # Storybook stories
56+
57+
````
58+
- **Types:** Props interfaces named `[ComponentName]Props` extending `BaseProps`. Capitalize type aliases (e.g., `Appearance`, `Size`).
59+
- **Exports:** Named export of components from `index.tsx`; export props from `index.type.tsx`.
60+
- **Testing:** Jest & React Testing Library is used for unit testing and coverage.
61+
- Snapshots and unit tests are written in [COMPONENT_NAME].test.tsx file inside the __tests__ folder.
62+
63+
---
64+
65+
## Styling
66+
- CSS Module has been used to define the class names.
67+
- Create a CSS file inside **css** directory with the file extension `module.css` (e.g avatar.module.css)
68+
- Use **ClassNames**, a utility for conditionally joining CSS classNames together.
69+
- Follow the **BEM Convention** & use CSS Modules for naming CSS classes.
70+
71+
---
72+
73+
## Quality Checks (run before committing)
74+
1. `npm run lint:check` – ESLint for TS/JS.
75+
2. `npm run prettier:check` – Prettier for CSS.
76+
3. `npm run lint:types` – TypeScript type check.
77+
4. `npm test` – Jest unit tests (update tests alongside source changes).
78+
79+
---
80+
81+
## Commit & Branch Conventions
82+
- **Conventional Commits:** `type(component): message`
83+
- Valid types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`
84+
- Keep commits focused; prefer a single commit per PR.
85+
- Branch names should reflect the work (e.g., `feat-button`, `fix-modal-height`).
86+
87+
---
88+
89+
## Documentation & Stories
90+
- Update or add Storybook stories for any visual or API change.
91+
- Update Markdown docs in `docs/` when behavior or usage changes.
92+
- Add/modify Figma files in `figma/` and connect via `figma-code-connect` for new components.
93+
94+
---
95+
96+
## Accessibility (WCAG 2.2 AA — MANDATORY)
97+
All components must meet WCAG 2.2 AA. Treat WCAG as a hard requirement, not a guideline.
98+
99+
### A11y References
100+
- Audit process: `.cursor/rules/wcag-a11y-audit-report.mdc`
101+
- Audit patterns: `.cursor/rules/wcag-audit-patterns.md`
102+
- Guidelines: `.cursor/rules/vercel-a11y-guidelines.mdc`, `.cursor/rules/web-interface-a11y-guidelines.mdc`
103+
- WCAG source data: `a11y-context/wcag-as-json.json`
104+
105+
### Semantic HTML & ARIA
106+
- MUST: Use native semantics (`<button>`, `<a>`, `<label>`, `<table>`) before ARIA.
107+
- MUST: `<button>` for actions; `<a>`/`<Link>` for navigation — never `<div onClick>`.
108+
- MUST: Icon-only buttons must have a descriptive `aria-label`.
109+
- MUST: Decorative icons/images must have `aria-hidden="true"` / `alt=""`.
110+
- MUST: All images need `alt` text (empty string if purely decorative).
111+
- MUST: Custom widgets must have correct ARIA roles, states, and properties (`aria-expanded`, `aria-checked`, `aria-selected`, etc.).
112+
- MUST: Form inputs need `<label>` (via `htmlFor`) or `aria-label`/`aria-labelledby`.
113+
- MUST: Status/error messages use `role="alert"` or `aria-live="polite"` as appropriate.
114+
- MUST: No duplicate `id` attributes in the DOM.
115+
116+
### Keyboard Navigation
117+
- MUST: All interactive elements are fully keyboard accessible.
118+
- MUST: Keyboard interactions follow [WAI-ARIA APG patterns](https://www.w3.org/WAI/ARIA/apg/patterns/).
119+
- MUST: No keyboard traps (unless intentional modal trapping per APG).
120+
- MUST: Focus is managed correctly — trap in modals, return focus on close.
121+
- MUST: Tab order is logical and matches visual order.
122+
123+
### Focus Visibility
124+
- MUST: Visible focus ring on all interactive elements using `:focus-visible`.
125+
- NEVER: `outline: none` / `outline-none` without a visible focus replacement.
126+
- MUST: Focus indicators meet 3:1 contrast ratio against adjacent colors.
127+
- MUST: Focused elements are not obscured by sticky headers or overlays (WCAG 2.4.11).
128+
129+
### Color & Contrast
130+
- MUST: Text contrast ≥ 4.5:1 (normal); ≥ 3:1 (large text ≥18pt or 14pt bold).
131+
- MUST: UI component boundaries and icons ≥ 3:1 contrast ratio.
132+
- MUST: Color is never the only means of conveying information (use icons, text, or patterns as redundant cues).
133+
- MUST: Error states use more than color alone.
134+
135+
### Touch & Target Size
136+
- MUST: Interactive hit targets ≥ 24×24 px (mobile ≥ 44×44 px); expand hit area with padding if visual size is smaller.
137+
- MUST: `touch-action: manipulation` to prevent double-tap zoom delay.
138+
- MUST: No dead zones on checkboxes/radios — label and control share one hit target.
139+
140+
### Motion & Animation
141+
- MUST: Honor `prefers-reduced-motion` — provide reduced variant or disable animation entirely.
142+
- MUST: Animate only compositor-friendly properties (`transform`, `opacity`).
143+
- NEVER: `transition: all` — list properties explicitly.
144+
- MUST: Animations are interruptible and respond to user input.
145+
146+
### Content Handling
147+
- MUST: Text containers handle long content (`truncate`, `line-clamp`, `break-words`).
148+
- MUST: Flex children need `min-w-0` to allow text truncation.
149+
- MUST: Handle empty states — no broken UI for empty strings or arrays.
150+
- MUST: Locale-aware dates/times/numbers (`Intl.DateTimeFormat`, `Intl.NumberFormat`).
151+
152+
### Per-Component Audit (when building or modifying components)
153+
For each component, validate:
154+
1. HTML structure and heading hierarchy.
155+
2. ARIA roles, states, and properties.
156+
3. Keyboard interactions and event handlers.
157+
4. Color contrast of all visual states (default, hover, focus, active, disabled, error).
158+
5. Touch target sizes.
159+
6. Focus handling (visibility, order, trap/return).
160+
7. Screen reader announcements for dynamic content.
161+
8. `prefers-reduced-motion` support for any animations.
162+
163+
### Anti-patterns (always flag and fix)
164+
- `user-scalable=no` or `maximum-scale=1` disabling zoom.
165+
- `onPaste` with `preventDefault` blocking paste.
166+
- `outline: none` without focus-visible replacement.
167+
- `<div>` or `<span>` with click handlers instead of `<button>`.
168+
- Icon buttons without `aria-label`.
169+
- Images without `alt` attribute.
170+
- Form inputs without associated labels.
171+
- Hardcoded color values outside token files (contrast cannot be verified programmatically).
172+
173+
### Resolving Lint Warnings
174+
- Resolve all `jsx-a11y` ESLint warnings before committing.
175+
- Run `npm run lint:check` and fix every a11y violation reported.
176+
177+
---
178+
179+
## Review & Misc
180+
- Avoid committing generated artifacts (`dist/`, `.lib/`) or build output.
181+
- Add yourself to contributors via `all-contributors` when appropriate.
182+
- PRs should include a clear description, pass all CI checks, and request review when ready.
183+
184+
---
185+
186+
Adhering to these guidelines keeps the design system consistent, accessible, and maintainable.

core/accessibility/utils/__tests__/isSpaceKey.test.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,43 @@
22
import isSpaceKey from '../isSpaceKey';
33

44
describe('isSpaceKey', () => {
5-
test('returns true if the space key is pressed', () => {
6-
// Create a mock React keyboard event with 'Space' as the key
5+
test('returns true for space character (modern browsers)', () => {
6+
const spaceKeyEvent = {
7+
key: ' ',
8+
} as React.KeyboardEvent;
9+
10+
expect(isSpaceKey(spaceKeyEvent)).toBe(true);
11+
});
12+
13+
test('returns true for "Space" key value', () => {
714
const spaceKeyEvent = {
815
key: 'Space',
916
} as React.KeyboardEvent;
1017

11-
// Assert that isSpaceKey returns true for the space key event
18+
expect(isSpaceKey(spaceKeyEvent)).toBe(true);
19+
});
20+
21+
test('returns true for "Spacebar" key value (legacy browsers)', () => {
22+
const spaceKeyEvent = {
23+
key: 'Spacebar',
24+
} as React.KeyboardEvent;
25+
1226
expect(isSpaceKey(spaceKeyEvent)).toBe(true);
1327
});
1428

1529
test('returns false if any other key is pressed', () => {
16-
// Create a mock React keyboard event with a non-space key
1730
const enterKeyEvent = {
1831
key: 'Enter',
1932
} as React.KeyboardEvent;
2033

21-
// Assert that isSpaceKey returns false for a non-space key event
2234
expect(isSpaceKey(enterKeyEvent)).toBe(false);
2335
});
2436

2537
test('returns false if the key is undefined', () => {
26-
// Create a mock React keyboard event with an undefined key
2738
const undefinedKeyEvent = {
2839
key: undefined,
2940
} as unknown as React.KeyboardEvent;
3041

31-
// Assert that isSpaceKey returns false for an undefined key event
3242
expect(isSpaceKey(undefinedKeyEvent)).toBe(false);
3343
});
34-
35-
// Add more test cases if you want to cover more scenarios, such as empty string, null, etc.
3644
});

core/accessibility/utils/__tests__/useAccessibilityProps.test.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,31 @@ describe('useAccessibilityProps', () => {
4242
}
4343
});
4444

45-
test('role, tabIndex and aria-label are set correctly', () => {
46-
const { getByRole } = render(<TestComponent role="button" tabIndex={-1} aria-label="test label" />);
45+
test('role, tabIndex and aria-label are set correctly when onClick is provided', () => {
46+
const onClick = jest.fn();
47+
const { getByRole } = render(
48+
<TestComponent onClick={onClick} role="button" tabIndex={-1} aria-label="test label" />
49+
);
4750
const element = getByRole('button');
4851
expect(element).toHaveAttribute('tabIndex', '-1');
4952
expect(element).toHaveAttribute('aria-label', 'test label');
5053
});
5154

55+
test('does not add interactive props when onClick is not provided (avoids nested button/link)', () => {
56+
const { container } = render(<TestComponent role="button" tabIndex={-1} aria-label="test label" />);
57+
const element = container.firstChild as HTMLElement;
58+
expect(element).not.toHaveAttribute('role');
59+
expect(element).not.toHaveAttribute('tabindex');
60+
});
61+
62+
test('preserves non-interactive ARIA props when onClick is not provided', () => {
63+
const { container } = render(<TestComponent aria-label="Message sent" aria-labelledby="id" aria-hidden={true} />);
64+
const element = container.firstChild as HTMLElement;
65+
expect(element).toHaveAttribute('aria-label', 'Message sent');
66+
expect(element).toHaveAttribute('aria-labelledby', 'id');
67+
expect(element).toHaveAttribute('aria-hidden', 'true');
68+
});
69+
5270
test('No trigger for callback on event of not allowed aria roles', () => {
5371
const onClick = jest.fn();
5472
const { getByRole } = render(<TestComponent onClick={onClick} role="tooltip" aria-label="test label" />);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
22

3-
const isSpaceKey = (e: React.KeyboardEvent) => e.key === 'Space';
3+
const isSpaceKey = (e: React.KeyboardEvent) => e.key === ' ' || e.key === 'Space' || e.key === 'Spacebar';
44

55
export default isSpaceKey;

core/accessibility/utils/useAccessibilityProps.ts

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ interface IProps {
99
role?: AriaRoleType;
1010
tabIndex?: number;
1111
'aria-label'?: React.AriaAttributes['aria-label'];
12+
'aria-labelledby'?: React.AriaAttributes['aria-labelledby'];
13+
'aria-describedby'?: React.AriaAttributes['aria-describedby'];
14+
'aria-hidden'?: React.AriaAttributes['aria-hidden'];
1215
}
1316

1417
const allowed: Record<string, Set<KeyboardEventKeyType>> = {
@@ -30,28 +33,37 @@ const isKeyboardInteractionAllowed = (role: AriaRoleType, key: KeyboardEventKeyT
3033
};
3134

3235
const useAccessibilityProps = ({ onClick, onKeyDown, role = 'button', tabIndex, ...rest }: IProps) => {
36+
// Only add interactive props when onClick is provided.
37+
if (!onClick) {
38+
const {
39+
'aria-label': ariaLabel,
40+
'aria-labelledby': ariaLabelledBy,
41+
'aria-describedby': ariaDescribedBy,
42+
'aria-hidden': ariaHidden,
43+
} = rest;
44+
return {
45+
...(ariaLabel != null && { 'aria-label': ariaLabel }),
46+
...(ariaLabelledBy != null && { 'aria-labelledby': ariaLabelledBy }),
47+
...(ariaDescribedBy != null && { 'aria-describedby': ariaDescribedBy }),
48+
...(ariaHidden != null && { 'aria-hidden': ariaHidden }),
49+
};
50+
}
3351
return {
34-
...(onClick
35-
? {
36-
onClick: onClick,
37-
role: role,
38-
tabIndex: tabIndex || 0,
39-
'aria-label': rest['aria-label'],
40-
onKeyDown: (e: React.SyntheticEvent<HTMLElement>) => {
41-
if (onKeyDown) {
42-
onKeyDown(e as React.KeyboardEvent<HTMLElement>);
43-
return;
44-
}
45-
const key = (e as React.KeyboardEvent<HTMLElement>).key;
46-
if (isKeyboardInteractionAllowed(role, key)) {
47-
if (onClick) {
48-
e.preventDefault();
49-
onClick(e as React.MouseEvent<HTMLElement>);
50-
}
51-
}
52-
},
53-
}
54-
: { role, tabIndex, 'aria-label': rest['aria-label'] }),
52+
onClick: onClick,
53+
role: role,
54+
tabIndex: tabIndex ?? 0,
55+
'aria-label': rest['aria-label'],
56+
onKeyDown: (e: React.SyntheticEvent<HTMLElement>) => {
57+
if (onKeyDown) {
58+
onKeyDown(e as React.KeyboardEvent<HTMLElement>);
59+
return;
60+
}
61+
const key = (e as React.KeyboardEvent<HTMLElement>).key;
62+
if (isKeyboardInteractionAllowed(role, key)) {
63+
e.preventDefault();
64+
onClick(e as React.MouseEvent<HTMLElement>);
65+
}
66+
},
5567
};
5668
};
5769

core/ai-components/AIResponse/ChatButton.tsx

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
11
import * as React from 'react';
2-
import classNames from 'classnames';
32
import { ButtonProps } from '@/index.type';
43
import { Button } from '@/index';
5-
import styles from '@css/ai-components/chat.module.css';
64

75
type ChatButtonType = Omit<ButtonProps, 'size' | 'largeIcon' | 'appearance'>;
86

97
export const ChatButton = (props: ChatButtonType) => {
108
const { className, selected, ...rest } = props;
119

12-
const chatButtonClassNames = classNames(
13-
{
14-
[styles['AIResponse-button']]: true,
15-
[styles['AIResponse-button--selected']]: selected,
16-
},
17-
className
18-
);
19-
2010
return (
2111
<Button
2212
data-test="DesignSystem-AIResponse-Button"
@@ -25,7 +15,7 @@ export const ChatButton = (props: ChatButtonType) => {
2515
size="tiny"
2616
largeIcon={true}
2717
appearance="transparent"
28-
className={chatButtonClassNames}
18+
className={className}
2919
/>
3020
);
3121
};

0 commit comments

Comments
 (0)