@a11ypros/a11y-ui-components 1.0.1 → 1.0.3

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.
Files changed (217) hide show
  1. package/README.md +182 -157
  2. package/dist/components/Button/Button.d.ts +37 -0
  3. package/dist/components/Button/Button.d.ts.map +1 -0
  4. package/dist/components/Button/Button.js +52 -0
  5. package/dist/components/Button/index.d.ts +3 -0
  6. package/dist/components/Button/index.d.ts.map +1 -0
  7. package/dist/components/Button/index.js +1 -0
  8. package/dist/components/DataTable/DataTable.d.ts +71 -0
  9. package/dist/components/DataTable/DataTable.d.ts.map +1 -0
  10. package/dist/components/DataTable/DataTable.js +122 -0
  11. package/dist/components/DataTable/index.d.ts +3 -0
  12. package/dist/components/DataTable/index.d.ts.map +1 -0
  13. package/dist/components/DataTable/index.js +1 -0
  14. package/dist/components/Form/Checkbox.d.ts +36 -0
  15. package/dist/components/Form/Checkbox.d.ts.map +1 -0
  16. package/dist/components/Form/Checkbox.js +39 -0
  17. package/dist/components/Form/Fieldset.d.ts +33 -0
  18. package/dist/components/Form/Fieldset.d.ts.map +1 -0
  19. package/dist/components/Form/Fieldset.js +34 -0
  20. package/dist/components/Form/Input.d.ts +37 -0
  21. package/dist/components/Form/Input.d.ts.map +1 -0
  22. package/dist/components/Form/Input.js +41 -0
  23. package/dist/components/Form/Label.d.ts +30 -0
  24. package/dist/components/Form/Label.d.ts.map +1 -0
  25. package/dist/components/Form/Label.js +30 -0
  26. package/dist/components/Form/Radio.d.ts +53 -0
  27. package/dist/components/Form/Radio.d.ts.map +1 -0
  28. package/dist/components/Form/Radio.js +39 -0
  29. package/dist/components/Form/Select.d.ts +51 -0
  30. package/dist/components/Form/Select.d.ts.map +1 -0
  31. package/dist/components/Form/Select.js +49 -0
  32. package/dist/components/Form/Textarea.d.ts +44 -0
  33. package/dist/components/Form/Textarea.d.ts.map +1 -0
  34. package/dist/components/Form/Textarea.js +43 -0
  35. package/dist/components/Form/index.d.ts +8 -0
  36. package/dist/components/Form/index.d.ts.map +1 -0
  37. package/dist/components/Form/index.js +7 -0
  38. package/dist/components/Link/Link.d.ts +34 -0
  39. package/dist/components/Link/Link.d.ts.map +1 -0
  40. package/dist/components/Link/Link.js +48 -0
  41. package/dist/components/Link/index.d.ts +3 -0
  42. package/dist/components/Link/index.d.ts.map +1 -0
  43. package/dist/components/Link/index.js +1 -0
  44. package/dist/components/Modal/Modal.d.ts +64 -0
  45. package/dist/components/Modal/Modal.d.ts.map +1 -0
  46. package/dist/components/Modal/Modal.js +108 -0
  47. package/dist/components/Modal/index.d.ts +3 -0
  48. package/dist/components/Modal/index.d.ts.map +1 -0
  49. package/dist/components/Modal/index.js +1 -0
  50. package/dist/components/Tabs/Tabs.d.ts +63 -0
  51. package/dist/components/Tabs/Tabs.d.ts.map +1 -0
  52. package/dist/components/Tabs/Tabs.js +134 -0
  53. package/dist/components/Tabs/index.d.ts +3 -0
  54. package/dist/components/Tabs/index.d.ts.map +1 -0
  55. package/dist/components/Tabs/index.js +1 -0
  56. package/dist/components/Toast/Toast.d.ts +59 -0
  57. package/dist/components/Toast/Toast.d.ts.map +1 -0
  58. package/dist/components/Toast/Toast.js +91 -0
  59. package/dist/components/Toast/ToastProvider.d.ts +22 -0
  60. package/dist/components/Toast/ToastProvider.d.ts.map +1 -0
  61. package/dist/components/Toast/ToastProvider.js +33 -0
  62. package/dist/components/Toast/index.d.ts +5 -0
  63. package/dist/components/Toast/index.d.ts.map +1 -0
  64. package/dist/components/Toast/index.js +2 -0
  65. package/dist/hooks/useAriaLive.d.ts +9 -0
  66. package/dist/hooks/useAriaLive.d.ts.map +1 -0
  67. package/dist/hooks/useAriaLive.js +39 -0
  68. package/dist/hooks/useFocusReturn.d.ts +9 -0
  69. package/dist/hooks/useFocusReturn.d.ts.map +1 -0
  70. package/dist/hooks/useFocusReturn.js +33 -0
  71. package/dist/hooks/useFocusTrap.d.ts +9 -0
  72. package/dist/hooks/useFocusTrap.d.ts.map +1 -0
  73. package/dist/hooks/useFocusTrap.js +68 -0
  74. package/dist/index.d.ts +22 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/{packages/design-system/src/index.ts → dist/index.js} +0 -4
  77. package/dist/styles/index.d.ts +3 -0
  78. package/dist/styles/index.d.ts.map +1 -0
  79. package/dist/styles/index.js +1 -0
  80. package/dist/tokens/breakpoints.d.ts +25 -0
  81. package/dist/tokens/breakpoints.d.ts.map +1 -0
  82. package/dist/tokens/breakpoints.js +23 -0
  83. package/dist/tokens/colors.d.ts +81 -0
  84. package/dist/tokens/colors.d.ts.map +1 -0
  85. package/dist/tokens/colors.js +86 -0
  86. package/dist/tokens/index.d.ts +6 -0
  87. package/dist/tokens/index.d.ts.map +1 -0
  88. package/dist/tokens/index.js +5 -0
  89. package/dist/tokens/motion.d.ts +30 -0
  90. package/dist/tokens/motion.d.ts.map +1 -0
  91. package/dist/tokens/motion.js +34 -0
  92. package/dist/tokens/spacing.d.ts +22 -0
  93. package/dist/tokens/spacing.d.ts.map +1 -0
  94. package/dist/tokens/spacing.js +20 -0
  95. package/dist/tokens/theme.d.ts +159 -0
  96. package/dist/tokens/theme.d.ts.map +1 -0
  97. package/dist/tokens/theme.js +15 -0
  98. package/dist/tokens/typography.d.ts +45 -0
  99. package/dist/tokens/typography.d.ts.map +1 -0
  100. package/dist/tokens/typography.js +56 -0
  101. package/dist/utils/aria.d.ts +60 -0
  102. package/dist/utils/aria.d.ts.map +1 -0
  103. package/dist/utils/aria.js +86 -0
  104. package/dist/utils/focus.d.ts +30 -0
  105. package/dist/utils/focus.d.ts.map +1 -0
  106. package/dist/utils/focus.js +80 -0
  107. package/dist/utils/index.d.ts +4 -0
  108. package/dist/utils/index.d.ts.map +1 -0
  109. package/dist/utils/index.js +3 -0
  110. package/dist/utils/keyboard.d.ts +38 -0
  111. package/dist/utils/keyboard.d.ts.map +1 -0
  112. package/dist/utils/keyboard.js +59 -0
  113. package/package.json +49 -31
  114. package/.storybook/custom.css +0 -69
  115. package/.storybook/main.ts +0 -46
  116. package/.storybook/manager.ts +0 -26
  117. package/.storybook/package.json +0 -6
  118. package/.storybook/preview.tsx +0 -31
  119. package/.storybook/public/logo.png +0 -0
  120. package/.storybook/vite.config.ts +0 -24
  121. package/.storybook/welcome.mdx +0 -97
  122. package/DEPLOYMENT.md +0 -154
  123. package/apps/web/app/(docs)/audit/audit.css +0 -269
  124. package/apps/web/app/(docs)/audit/page.tsx +0 -271
  125. package/apps/web/app/(docs)/components/button/page.tsx +0 -49
  126. package/apps/web/app/(docs)/components/form/page.tsx +0 -92
  127. package/apps/web/app/(docs)/components/link/page.tsx +0 -31
  128. package/apps/web/app/(docs)/components/modal/page.tsx +0 -41
  129. package/apps/web/app/(docs)/components/page.tsx +0 -37
  130. package/apps/web/app/(docs)/components/table/page.tsx +0 -54
  131. package/apps/web/app/(docs)/components/tabs/page.tsx +0 -61
  132. package/apps/web/app/(docs)/components/toast/page.tsx +0 -51
  133. package/apps/web/app/api/audit/route.ts +0 -128
  134. package/apps/web/app/favicon.ico +0 -0
  135. package/apps/web/app/layout.tsx +0 -20
  136. package/apps/web/app/page.tsx +0 -17
  137. package/apps/web/app/styles/globals.css +0 -5
  138. package/apps/web/next-env.d.ts +0 -5
  139. package/apps/web/next.config.js +0 -21
  140. package/apps/web/package.json +0 -28
  141. package/apps/web/public/_headers +0 -17
  142. package/apps/web/public/_redirects +0 -31
  143. package/apps/web/public/logo.png +0 -0
  144. package/apps/web/tsconfig.json +0 -29
  145. package/netlify/functions/audit.ts +0 -163
  146. package/netlify.toml +0 -37
  147. package/packages/design-system/README.md +0 -252
  148. package/packages/design-system/package.json +0 -68
  149. package/packages/design-system/scripts/copy-css.js +0 -63
  150. package/packages/design-system/src/components/Button/Button.stories.tsx +0 -228
  151. package/packages/design-system/src/components/Button/Button.tsx +0 -137
  152. package/packages/design-system/src/components/Button/index.ts +0 -3
  153. package/packages/design-system/src/components/DataTable/DataTable.stories.tsx +0 -211
  154. package/packages/design-system/src/components/DataTable/DataTable.tsx +0 -293
  155. package/packages/design-system/src/components/DataTable/index.ts +0 -3
  156. package/packages/design-system/src/components/Form/Checkbox.stories.tsx +0 -252
  157. package/packages/design-system/src/components/Form/Checkbox.tsx +0 -114
  158. package/packages/design-system/src/components/Form/Fieldset.stories.tsx +0 -210
  159. package/packages/design-system/src/components/Form/Fieldset.tsx +0 -71
  160. package/packages/design-system/src/components/Form/Input.stories.tsx +0 -164
  161. package/packages/design-system/src/components/Form/Input.tsx +0 -113
  162. package/packages/design-system/src/components/Form/Label.tsx +0 -56
  163. package/packages/design-system/src/components/Form/Radio.stories.tsx +0 -265
  164. package/packages/design-system/src/components/Form/Radio.tsx +0 -147
  165. package/packages/design-system/src/components/Form/Select.stories.tsx +0 -295
  166. package/packages/design-system/src/components/Form/Select.tsx +0 -160
  167. package/packages/design-system/src/components/Form/Textarea.stories.tsx +0 -253
  168. package/packages/design-system/src/components/Form/Textarea.tsx +0 -145
  169. package/packages/design-system/src/components/Form/index.ts +0 -8
  170. package/packages/design-system/src/components/Link/Link.stories.tsx +0 -128
  171. package/packages/design-system/src/components/Link/Link.tsx +0 -117
  172. package/packages/design-system/src/components/Link/index.ts +0 -3
  173. package/packages/design-system/src/components/Modal/Modal.stories.tsx +0 -165
  174. package/packages/design-system/src/components/Modal/Modal.tsx +0 -202
  175. package/packages/design-system/src/components/Modal/index.ts +0 -3
  176. package/packages/design-system/src/components/Tabs/Tabs.stories.tsx +0 -213
  177. package/packages/design-system/src/components/Tabs/Tabs.tsx +0 -248
  178. package/packages/design-system/src/components/Tabs/index.ts +0 -3
  179. package/packages/design-system/src/components/Toast/Toast.stories.tsx +0 -153
  180. package/packages/design-system/src/components/Toast/Toast.tsx +0 -175
  181. package/packages/design-system/src/components/Toast/ToastProvider.tsx +0 -73
  182. package/packages/design-system/src/components/Toast/index.ts +0 -5
  183. package/packages/design-system/src/hooks/useAriaLive.ts +0 -51
  184. package/packages/design-system/src/hooks/useFocusReturn.ts +0 -40
  185. package/packages/design-system/src/hooks/useFocusTrap.ts +0 -82
  186. package/packages/design-system/src/styles/index.ts +0 -3
  187. package/packages/design-system/src/tokens/breakpoints.ts +0 -28
  188. package/packages/design-system/src/tokens/colors.ts +0 -98
  189. package/packages/design-system/src/tokens/index.ts +0 -6
  190. package/packages/design-system/src/tokens/motion.ts +0 -41
  191. package/packages/design-system/src/tokens/spacing.ts +0 -24
  192. package/packages/design-system/src/tokens/theme.ts +0 -19
  193. package/packages/design-system/src/tokens/typography.ts +0 -64
  194. package/packages/design-system/src/utils/aria.ts +0 -108
  195. package/packages/design-system/src/utils/focus.ts +0 -87
  196. package/packages/design-system/src/utils/index.ts +0 -4
  197. package/packages/design-system/src/utils/keyboard.ts +0 -77
  198. package/packages/design-system/tsconfig.json +0 -17
  199. package/public/logo.png +0 -0
  200. package/scripts/fix-storybook-paths.js +0 -53
  201. package/tsconfig.json +0 -20
  202. /package/{packages/design-system/src → dist}/components/Button/Button.css +0 -0
  203. /package/{packages/design-system/src → dist}/components/DataTable/DataTable.css +0 -0
  204. /package/{packages/design-system/src → dist}/components/Form/Checkbox.css +0 -0
  205. /package/{packages/design-system/src → dist}/components/Form/Fieldset.css +0 -0
  206. /package/{packages/design-system/src → dist}/components/Form/Input.css +0 -0
  207. /package/{packages/design-system/src → dist}/components/Form/Label.css +0 -0
  208. /package/{packages/design-system/src → dist}/components/Form/Radio.css +0 -0
  209. /package/{packages/design-system/src → dist}/components/Form/Select.css +0 -0
  210. /package/{packages/design-system/src → dist}/components/Form/Textarea.css +0 -0
  211. /package/{packages/design-system/src → dist}/components/Link/Link.css +0 -0
  212. /package/{packages/design-system/src → dist}/components/Modal/Modal.css +0 -0
  213. /package/{packages/design-system/src → dist}/components/Tabs/Tabs.css +0 -0
  214. /package/{packages/design-system/src → dist}/components/Toast/Toast.css +0 -0
  215. /package/{packages/design-system/src → dist}/components/Toast/ToastProvider.css +0 -0
  216. /package/{packages/design-system/src → dist}/styles/components.css +0 -0
  217. /package/{packages/design-system/src → dist}/styles/global.css +0 -0
@@ -0,0 +1,122 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useRef, useCallback } from 'react';
4
+ import { useAriaLive } from '../../hooks/useAriaLive';
5
+ import { isNavigationKey } from '../../utils/keyboard';
6
+ import { Checkbox } from '../Form/Checkbox';
7
+ import './DataTable.css';
8
+ /**
9
+ * Accessible DataTable component
10
+ *
11
+ * WCAG Compliance:
12
+ * - 1.3.1 Info and Relationships: Semantic table structure
13
+ * - 2.1.1 Keyboard: Arrow keys, Home/End navigation
14
+ * - 4.1.2 Name, Role, Value: Proper ARIA attributes
15
+ * - 4.1.3 Status Messages: Sort announcements
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * <DataTable
20
+ * data={users}
21
+ * columns={columns}
22
+ * getRowId={(user) => user.id}
23
+ * caption="User list"
24
+ * />
25
+ * ```
26
+ */
27
+ export function DataTable({ data, columns, getRowId, selectable = false, selectedRows = [], onSelectionChange, sortConfig, onSortChange, caption, }) {
28
+ const [focusedRow, setFocusedRow] = useState(null);
29
+ const tableRef = useRef(null);
30
+ const rowRefs = useRef(new Map());
31
+ // Announce sort changes
32
+ const sortAnnouncement = sortConfig
33
+ ? `Sorted by ${columns.find((c) => c.key === sortConfig.column)?.header || sortConfig.column}, ${sortConfig.direction === 'asc' ? 'ascending' : 'descending'}`
34
+ : undefined;
35
+ useAriaLive(sortAnnouncement, 'polite');
36
+ const handleSelectAll = useCallback(() => {
37
+ if (!onSelectionChange)
38
+ return;
39
+ const allSelected = selectedRows.length === data.length;
40
+ if (allSelected) {
41
+ onSelectionChange([]);
42
+ }
43
+ else {
44
+ onSelectionChange(data.map(getRowId));
45
+ }
46
+ }, [data, selectedRows, onSelectionChange, getRowId]);
47
+ const handleSelectRow = useCallback((rowId) => {
48
+ if (!onSelectionChange)
49
+ return;
50
+ const isSelected = selectedRows.includes(rowId);
51
+ if (isSelected) {
52
+ onSelectionChange(selectedRows.filter((id) => id !== rowId));
53
+ }
54
+ else {
55
+ onSelectionChange([...selectedRows, rowId]);
56
+ }
57
+ }, [selectedRows, onSelectionChange]);
58
+ const handleSort = useCallback((columnKey) => {
59
+ if (!onSortChange || !columns.find((c) => c.key === columnKey)?.sortable) {
60
+ return;
61
+ }
62
+ const newDirection = sortConfig?.column === columnKey && sortConfig.direction === 'asc'
63
+ ? 'desc'
64
+ : 'asc';
65
+ onSortChange(columnKey, newDirection);
66
+ }, [onSortChange, sortConfig, columns]);
67
+ const handleKeyDown = useCallback((event, rowId, index) => {
68
+ const row = rowRefs.current.get(rowId);
69
+ if (!row)
70
+ return;
71
+ if (isNavigationKey(event.key)) {
72
+ event.preventDefault();
73
+ let newIndex = index;
74
+ switch (event.key) {
75
+ case 'ArrowDown':
76
+ newIndex = Math.min(index + 1, data.length - 1);
77
+ break;
78
+ case 'ArrowUp':
79
+ newIndex = Math.max(index - 1, 0);
80
+ break;
81
+ case 'Home':
82
+ newIndex = 0;
83
+ break;
84
+ case 'End':
85
+ newIndex = data.length - 1;
86
+ break;
87
+ }
88
+ const newRowId = getRowId(data[newIndex]);
89
+ setFocusedRow(newRowId);
90
+ rowRefs.current.get(newRowId)?.focus();
91
+ }
92
+ else if (event.key === ' ' && selectable) {
93
+ event.preventDefault();
94
+ handleSelectRow(rowId);
95
+ }
96
+ }, [data, selectable, handleSelectRow, getRowId]);
97
+ const allSelected = data.length > 0 && selectedRows.length === data.length;
98
+ const someSelected = selectedRows.length > 0 && selectedRows.length < data.length;
99
+ return (_jsx("div", { className: "data-table-wrapper", children: _jsxs("table", { ref: tableRef, className: "data-table", "aria-label": caption, children: [caption && _jsx("caption", { className: "data-table-caption", children: caption }), _jsx("thead", { children: _jsxs("tr", { children: [selectable && (_jsx("th", { scope: "col", className: "data-table-header data-table-header--checkbox", children: _jsx(Checkbox, { checked: allSelected, "aria-label": "Select all rows", onChange: handleSelectAll, "aria-checked": allSelected ? 'true' : someSelected ? 'mixed' : 'false' }) })), columns.map((column) => (_jsx("th", { scope: "col", className: `data-table-header ${column.sortable ? 'data-table-header--sortable' : ''}`, style: column.width ? { width: column.width } : undefined, "aria-sort": sortConfig?.column === column.key
100
+ ? sortConfig.direction === 'asc'
101
+ ? 'ascending'
102
+ : 'descending'
103
+ : 'none', children: column.sortable ? (_jsxs("button", { type: "button", className: "data-table-sort-button", "aria-label": sortConfig?.column === column.key
104
+ ? `${column.header}, sorted ${sortConfig.direction === 'asc' ? 'ascending' : 'descending'}, activate to sort ${sortConfig.direction === 'asc' ? 'descending' : 'ascending'}`
105
+ : `Sort by ${column.header}`, onClick: () => handleSort(column.key), children: [column.header, sortConfig?.column === column.key && (_jsx("span", { className: "data-table-sort-indicator", "aria-hidden": "true", children: sortConfig.direction === 'asc' ? ' ↑' : ' ↓' }))] })) : (column.header) }, column.key)))] }) }), _jsx("tbody", { children: data.map((row, index) => {
106
+ const rowId = getRowId(row);
107
+ const isSelected = selectedRows.includes(rowId);
108
+ const isFocused = focusedRow === rowId;
109
+ return (_jsxs("tr", { ref: (el) => {
110
+ if (el) {
111
+ rowRefs.current.set(rowId, el);
112
+ }
113
+ else {
114
+ rowRefs.current.delete(rowId);
115
+ }
116
+ }, className: `data-table-row ${isSelected ? 'data-table-row--selected' : ''} ${isFocused ? 'data-table-row--focused' : ''}`, tabIndex: 0, "aria-selected": selectable ? isSelected : undefined, onKeyDown: (e) => handleKeyDown(e, rowId, index), onClick: () => {
117
+ if (selectable) {
118
+ handleSelectRow(rowId);
119
+ }
120
+ }, children: [selectable && (_jsx("td", { className: "data-table-cell data-table-cell--checkbox", children: _jsx(Checkbox, { checked: isSelected, "aria-label": `Select row ${index + 1}`, onChange: () => handleSelectRow(rowId), onClick: (e) => e.stopPropagation() }) })), columns.map((column) => (_jsx("td", { className: "data-table-cell", children: column.render ? column.render(row, index) : row[column.key] }, column.key)))] }, rowId));
121
+ }) })] }) }));
122
+ }
@@ -0,0 +1,3 @@
1
+ export { DataTable } from './DataTable';
2
+ export type { DataTableProps, DataTableColumn } from './DataTable';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1 @@
1
+ export { DataTable } from './DataTable';
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import './Checkbox.css';
3
+ export interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
4
+ /**
5
+ * Label for the checkbox
6
+ */
7
+ label?: string;
8
+ /**
9
+ * Error message to display
10
+ */
11
+ error?: string;
12
+ /**
13
+ * Helper text to display
14
+ */
15
+ helperText?: string;
16
+ }
17
+ /**
18
+ * Accessible Checkbox component
19
+ *
20
+ * WCAG Compliance:
21
+ * - 1.3.1 Info and Relationships: Proper label-input association
22
+ * - 2.5.3 Label in Name: Label text matches accessible name
23
+ * - 4.1.2 Name, Role, Value: Proper ARIA attributes
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * <Checkbox
28
+ * id="agree"
29
+ * label="I agree to the terms"
30
+ * checked={checked}
31
+ * onChange={handleChange}
32
+ * />
33
+ * ```
34
+ */
35
+ export declare const Checkbox: React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLInputElement>>;
36
+ //# sourceMappingURL=Checkbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../../src/components/Form/Checkbox.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,gBAAgB,CAAA;AAEvB,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC9F;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,QAAQ,wFAqEpB,CAAA"}
@@ -0,0 +1,39 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from 'react';
4
+ import { combineAriaDescribedBy } from '../../utils/aria';
5
+ import './Checkbox.css';
6
+ /**
7
+ * Accessible Checkbox component
8
+ *
9
+ * WCAG Compliance:
10
+ * - 1.3.1 Info and Relationships: Proper label-input association
11
+ * - 2.5.3 Label in Name: Label text matches accessible name
12
+ * - 4.1.2 Name, Role, Value: Proper ARIA attributes
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * <Checkbox
17
+ * id="agree"
18
+ * label="I agree to the terms"
19
+ * checked={checked}
20
+ * onChange={handleChange}
21
+ * />
22
+ * ```
23
+ */
24
+ export const Checkbox = React.forwardRef(({ id, label, error, helperText, className = '', 'aria-describedby': ariaDescribedBy, ...props }, ref) => {
25
+ const checkboxId = React.useId();
26
+ const finalId = id || `checkbox-${checkboxId}`;
27
+ const errorId = error ? `${finalId}-error` : undefined;
28
+ const helperId = helperText ? `${finalId}-helper` : undefined;
29
+ const describedBy = combineAriaDescribedBy(ariaDescribedBy, errorId, helperId);
30
+ const classes = [
31
+ 'form-checkbox',
32
+ error && 'form-checkbox--error',
33
+ className,
34
+ ]
35
+ .filter(Boolean)
36
+ .join(' ');
37
+ return (_jsxs("div", { className: "form-checkbox-wrapper", children: [_jsxs("div", { className: "form-checkbox-input-wrapper", children: [_jsx("input", { ref: ref, id: finalId, type: "checkbox", className: classes, "aria-invalid": error ? true : undefined, "aria-describedby": describedBy, required: props.required ? true : undefined, ...props }), label && (_jsxs("label", { htmlFor: finalId, className: "form-checkbox-label", children: [label, props.required && (_jsxs("span", { className: "form-label__required", "aria-hidden": "true", children: [' ', "*"] }))] }))] }), helperText && !error && (_jsx("span", { id: helperId, className: "form-helper-text", children: helperText })), error && (_jsx("span", { id: errorId, className: "form-error-text", role: "alert", children: error }))] }));
38
+ });
39
+ Checkbox.displayName = 'Checkbox';
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import './Fieldset.css';
3
+ export interface FieldsetProps extends React.FieldsetHTMLAttributes<HTMLFieldSetElement> {
4
+ /**
5
+ * Legend text for the fieldset
6
+ */
7
+ legend?: string;
8
+ /**
9
+ * Whether the legend is visually hidden (but still accessible)
10
+ */
11
+ legendHidden?: boolean;
12
+ /**
13
+ * Whether to show a required field explanation. Use this if the fieldset contains required fields.
14
+ */
15
+ required?: boolean;
16
+ }
17
+ /**
18
+ * Accessible Fieldset component
19
+ *
20
+ * WCAG Compliance:
21
+ * - 1.3.1 Info and Relationships: Proper fieldset/legend structure
22
+ * - 4.1.2 Name, Role, Value: Proper semantic HTML
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * <Fieldset legend="Shipping Address">
27
+ * <Input label="Street" />
28
+ * <Input label="City" />
29
+ * </Fieldset>
30
+ * ```
31
+ */
32
+ export declare const Fieldset: React.ForwardRefExoticComponent<FieldsetProps & React.RefAttributes<HTMLFieldSetElement>>;
33
+ //# sourceMappingURL=Fieldset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Fieldset.d.ts","sourceRoot":"","sources":["../../../src/components/Form/Fieldset.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,gBAAgB,CAAA;AAEvB,MAAM,WAAW,aAAc,SAAQ,KAAK,CAAC,sBAAsB,CAAC,mBAAmB,CAAC;IACtF;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IAEtB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,QAAQ,2FAgCpB,CAAA"}
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import './Fieldset.css';
4
+ /**
5
+ * Accessible Fieldset component
6
+ *
7
+ * WCAG Compliance:
8
+ * - 1.3.1 Info and Relationships: Proper fieldset/legend structure
9
+ * - 4.1.2 Name, Role, Value: Proper semantic HTML
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * <Fieldset legend="Shipping Address">
14
+ * <Input label="Street" />
15
+ * <Input label="City" />
16
+ * </Fieldset>
17
+ * ```
18
+ */
19
+ export const Fieldset = React.forwardRef(({ legend, legendHidden = false, required = false, className = '', children, ...props }, ref) => {
20
+ const classes = [
21
+ 'form-fieldset',
22
+ className,
23
+ ]
24
+ .filter(Boolean)
25
+ .join(' ');
26
+ const legendClasses = [
27
+ 'form-legend',
28
+ legendHidden && 'form-legend--hidden',
29
+ ]
30
+ .filter(Boolean)
31
+ .join(' ');
32
+ return (_jsxs("fieldset", { ref: ref, className: classes, ...props, children: [legend && (_jsx("legend", { className: legendClasses, children: legend })), required && (_jsxs("p", { className: "fieldset__required", children: [' ', _jsx("span", { className: 'fieldset__required-indicator', children: "*" }), " indicates a required field."] })), children] }));
33
+ });
34
+ Fieldset.displayName = 'Fieldset';
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import './Input.css';
3
+ export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
4
+ /**
5
+ * Error message to display
6
+ */
7
+ error?: string;
8
+ /**
9
+ * Helper text to display below the input
10
+ */
11
+ helperText?: string;
12
+ /**
13
+ * Label for the input (creates associated label)
14
+ */
15
+ label?: string;
16
+ }
17
+ /**
18
+ * Accessible Input component
19
+ *
20
+ * WCAG Compliance:
21
+ * - 1.3.1 Info and Relationships: Proper label-input association
22
+ * - 2.5.3 Label in Name: Label text matches accessible name
23
+ * - 4.1.2 Name, Role, Value: Proper ARIA attributes
24
+ * - 4.1.3 Status Messages: Error messages announced
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * <Input
29
+ * id="email"
30
+ * type="email"
31
+ * label="Email address"
32
+ * error="Please enter a valid email"
33
+ * />
34
+ * ```
35
+ */
36
+ export declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
37
+ //# sourceMappingURL=Input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../../src/components/Form/Input.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,aAAa,CAAA;AAEpB,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC;IAC7E;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,KAAK,qFAmEjB,CAAA"}
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
+ import React from 'react';
4
+ import { combineAriaDescribedBy } from '../../utils/aria';
5
+ import './Input.css';
6
+ /**
7
+ * Accessible Input component
8
+ *
9
+ * WCAG Compliance:
10
+ * - 1.3.1 Info and Relationships: Proper label-input association
11
+ * - 2.5.3 Label in Name: Label text matches accessible name
12
+ * - 4.1.2 Name, Role, Value: Proper ARIA attributes
13
+ * - 4.1.3 Status Messages: Error messages announced
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <Input
18
+ * id="email"
19
+ * type="email"
20
+ * label="Email address"
21
+ * error="Please enter a valid email"
22
+ * />
23
+ * ```
24
+ */
25
+ export const Input = React.forwardRef(({ id, error, helperText, label, className = '', 'aria-describedby': ariaDescribedBy, ...props }, ref) => {
26
+ const inputId = React.useId();
27
+ const finalId = id || `input-${inputId}`;
28
+ const errorId = error ? `${finalId}-error` : undefined;
29
+ const helperId = helperText ? `${finalId}-helper` : undefined;
30
+ const describedBy = combineAriaDescribedBy(ariaDescribedBy, errorId, helperId);
31
+ const classes = [
32
+ 'form-input',
33
+ error && 'form-input--error',
34
+ props.disabled && 'form-input--disabled',
35
+ className,
36
+ ]
37
+ .filter(Boolean)
38
+ .join(' ');
39
+ return (_jsxs("div", { className: "form-input-wrapper", children: [label && (_jsxs("label", { htmlFor: finalId, className: "form-label", children: [label, props.required && (_jsxs("span", { className: "form-label__required", "aria-hidden": "true", children: [' ', "*"] }))] })), _jsx("input", { ref: ref, id: finalId, className: classes, "aria-invalid": error ? true : undefined, "aria-describedby": describedBy, required: props.required ? true : undefined, ...props }), helperText && !error && (_jsx("span", { id: helperId, className: "form-helper-text", children: helperText })), error && (_jsx("span", { id: errorId, className: "form-error-text", role: "alert", children: error }))] }));
40
+ });
41
+ Input.displayName = 'Input';
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import './Label.css';
3
+ export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
4
+ /**
5
+ * Whether this label is required (shows asterisk)
6
+ */
7
+ required?: boolean;
8
+ /**
9
+ * ID of the input this label is associated with
10
+ */
11
+ htmlFor?: string;
12
+ }
13
+ /**
14
+ * Accessible Label component
15
+ *
16
+ * WCAG Compliance:
17
+ * - 1.3.1 Info and Relationships: Proper label-input association
18
+ * - 2.5.3 Label in Name: Label text matches accessible name
19
+ * - 4.1.2 Name, Role, Value: Proper semantic HTML
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * <Label htmlFor="email" required>
24
+ * Email address
25
+ * </Label>
26
+ * <Input id="email" type="email" />
27
+ * ```
28
+ */
29
+ export declare const Label: React.ForwardRefExoticComponent<LabelProps & React.RefAttributes<HTMLLabelElement>>;
30
+ //# sourceMappingURL=Label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Label.d.ts","sourceRoot":"","sources":["../../../src/components/Form/Label.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,aAAa,CAAA;AAEpB,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC;IAC7E;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAElB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,KAAK,qFAqBjB,CAAA"}
@@ -0,0 +1,30 @@
1
+ import { jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import './Label.css';
4
+ /**
5
+ * Accessible Label component
6
+ *
7
+ * WCAG Compliance:
8
+ * - 1.3.1 Info and Relationships: Proper label-input association
9
+ * - 2.5.3 Label in Name: Label text matches accessible name
10
+ * - 4.1.2 Name, Role, Value: Proper semantic HTML
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * <Label htmlFor="email" required>
15
+ * Email address
16
+ * </Label>
17
+ * <Input id="email" type="email" />
18
+ * ```
19
+ */
20
+ export const Label = React.forwardRef(({ required = false, className = '', children, ...props }, ref) => {
21
+ const classes = [
22
+ 'form-label',
23
+ required && 'form-label--required',
24
+ className,
25
+ ]
26
+ .filter(Boolean)
27
+ .join(' ');
28
+ return (_jsxs("label", { ref: ref, className: classes, ...props, children: [children, required && (_jsxs("span", { className: "form-label__required", "aria-hidden": "true", children: [' ', "*"] }))] }));
29
+ });
30
+ Label.displayName = 'Label';
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import './Radio.css';
3
+ export interface RadioOption {
4
+ value: string;
5
+ label: string;
6
+ disabled?: boolean;
7
+ }
8
+ export interface RadioProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
9
+ /**
10
+ * Options for the radio group
11
+ */
12
+ options: RadioOption[];
13
+ /**
14
+ * Name attribute for the radio group (required)
15
+ */
16
+ name: string;
17
+ /**
18
+ * Label for the radio group
19
+ */
20
+ label?: string;
21
+ /**
22
+ * Error message to display
23
+ */
24
+ error?: string;
25
+ /**
26
+ * Helper text to display
27
+ */
28
+ helperText?: string;
29
+ }
30
+ /**
31
+ * Accessible Radio component (radio group)
32
+ *
33
+ * WCAG Compliance:
34
+ * - 1.3.1 Info and Relationships: Proper fieldset/legend structure
35
+ * - 2.1.1 Keyboard: Full keyboard navigation
36
+ * - 4.1.2 Name, Role, Value: Proper ARIA attributes
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ * <Radio
41
+ * name="size"
42
+ * label="Size"
43
+ * options={[
44
+ * { value: 's', label: 'Small' },
45
+ * { value: 'm', label: 'Medium' },
46
+ * ]}
47
+ * value={selected}
48
+ * onChange={handleChange}
49
+ * />
50
+ * ```
51
+ */
52
+ export declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLInputElement>>;
53
+ //# sourceMappingURL=Radio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Radio.d.ts","sourceRoot":"","sources":["../../../src/components/Form/Radio.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,aAAa,CAAA;AAEpB,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,UAAW,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC3F;;OAEG;IACH,OAAO,EAAE,WAAW,EAAE,CAAA;IAEtB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IAEZ;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,KAAK,qFAkFjB,CAAA"}
@@ -0,0 +1,39 @@
1
+ 'use client';
2
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
+ import React from 'react';
4
+ import { combineAriaDescribedBy } from '../../utils/aria';
5
+ import './Radio.css';
6
+ /**
7
+ * Accessible Radio component (radio group)
8
+ *
9
+ * WCAG Compliance:
10
+ * - 1.3.1 Info and Relationships: Proper fieldset/legend structure
11
+ * - 2.1.1 Keyboard: Full keyboard navigation
12
+ * - 4.1.2 Name, Role, Value: Proper ARIA attributes
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * <Radio
17
+ * name="size"
18
+ * label="Size"
19
+ * options={[
20
+ * { value: 's', label: 'Small' },
21
+ * { value: 'm', label: 'Medium' },
22
+ * ]}
23
+ * value={selected}
24
+ * onChange={handleChange}
25
+ * />
26
+ * ```
27
+ */
28
+ export const Radio = React.forwardRef(({ name, options, label, error, helperText, className = '', value, onChange, disabled, required, 'aria-describedby': ariaDescribedBy, ...props }, ref) => {
29
+ const groupId = React.useId();
30
+ const errorId = error ? `radio-${groupId}-error` : undefined;
31
+ const helperId = helperText ? `radio-${groupId}-helper` : undefined;
32
+ const describedBy = combineAriaDescribedBy(ariaDescribedBy, errorId, helperId);
33
+ return (_jsxs("div", { className: "form-radio-wrapper", children: [label && (_jsx("div", { className: "form-radio-label", role: "group", "aria-labelledby": label ? `radio-label-${groupId}` : undefined, children: _jsxs("span", { id: `radio-label-${groupId}`, className: "form-label", children: [label, required && (_jsxs("span", { className: "form-label__required", "aria-hidden": "true", children: [' ', "*"] }))] }) })), _jsx("div", { className: "form-radio-group", role: "radiogroup", "aria-describedby": describedBy, "aria-invalid": error ? true : undefined, children: options.map((option, index) => {
34
+ const optionId = `radio-${groupId}-${index}`;
35
+ const isChecked = value === option.value;
36
+ return (_jsxs("div", { className: "form-radio-option", children: [_jsx("input", { ref: index === 0 ? ref : undefined, id: optionId, type: "radio", name: name, value: option.value, checked: isChecked, onChange: onChange, disabled: option.disabled || disabled, required: required, className: "form-radio", ...props }), _jsx("label", { htmlFor: optionId, className: "form-radio-label", children: option.label })] }, option.value));
37
+ }) }), helperText && !error && (_jsx("span", { id: helperId, className: "form-helper-text", children: helperText })), error && (_jsx("span", { id: errorId, className: "form-error-text", role: "alert", children: error }))] }));
38
+ });
39
+ Radio.displayName = 'Radio';
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+ import './Select.css';
3
+ export interface SelectOption {
4
+ value: string;
5
+ label: string;
6
+ disabled?: boolean;
7
+ }
8
+ export interface SelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'children'> {
9
+ /**
10
+ * Options for the select
11
+ */
12
+ options: SelectOption[];
13
+ /**
14
+ * Error message to display
15
+ */
16
+ error?: string;
17
+ /**
18
+ * Helper text to display below the select
19
+ */
20
+ helperText?: string;
21
+ /**
22
+ * Label for the select
23
+ */
24
+ label?: string;
25
+ /**
26
+ * Placeholder option text
27
+ */
28
+ placeholder?: string;
29
+ }
30
+ /**
31
+ * Accessible Select component
32
+ *
33
+ * WCAG Compliance:
34
+ * - 1.3.1 Info and Relationships: Proper label-select association
35
+ * - 2.1.1 Keyboard: Full keyboard navigation
36
+ * - 4.1.2 Name, Role, Value: Proper ARIA attributes
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ * <Select
41
+ * id="country"
42
+ * label="Country"
43
+ * options={[
44
+ * { value: 'us', label: 'United States' },
45
+ * { value: 'ca', label: 'Canada' },
46
+ * ]}
47
+ * />
48
+ * ```
49
+ */
50
+ export declare const Select: React.ForwardRefExoticComponent<SelectProps & React.RefAttributes<HTMLSelectElement>>;
51
+ //# sourceMappingURL=Select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../../src/components/Form/Select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,cAAc,CAAA;AAErB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,UAAU,CAAC;IAClG;;OAEG;IACH,OAAO,EAAE,YAAY,EAAE,CAAA;IAEvB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,MAAM,uFAgGlB,CAAA"}
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
+ import React from 'react';
4
+ import { combineAriaDescribedBy } from '../../utils/aria';
5
+ import './Select.css';
6
+ /**
7
+ * Accessible Select component
8
+ *
9
+ * WCAG Compliance:
10
+ * - 1.3.1 Info and Relationships: Proper label-select association
11
+ * - 2.1.1 Keyboard: Full keyboard navigation
12
+ * - 4.1.2 Name, Role, Value: Proper ARIA attributes
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * <Select
17
+ * id="country"
18
+ * label="Country"
19
+ * options={[
20
+ * { value: 'us', label: 'United States' },
21
+ * { value: 'ca', label: 'Canada' },
22
+ * ]}
23
+ * />
24
+ * ```
25
+ */
26
+ export const Select = React.forwardRef(({ id, options, error, helperText, label, placeholder, className = '', 'aria-describedby': ariaDescribedBy, ...props }, ref) => {
27
+ const selectId = React.useId();
28
+ const finalId = id || `select-${selectId}`;
29
+ const errorId = error ? `${finalId}-error` : undefined;
30
+ const helperId = helperText ? `${finalId}-helper` : undefined;
31
+ const describedBy = combineAriaDescribedBy(ariaDescribedBy, errorId, helperId);
32
+ const handleKeyDown = React.useCallback((event) => {
33
+ // Arrow keys are handled natively by select
34
+ // But we can add custom handling if needed
35
+ if (props.onKeyDown) {
36
+ props.onKeyDown(event);
37
+ }
38
+ }, [props.onKeyDown]);
39
+ const classes = [
40
+ 'form-select',
41
+ error && 'form-select--error',
42
+ props.disabled && 'form-select--disabled',
43
+ className,
44
+ ]
45
+ .filter(Boolean)
46
+ .join(' ');
47
+ return (_jsxs("div", { className: "form-select-wrapper", children: [label && (_jsxs("label", { htmlFor: finalId, className: "form-label", children: [label, props.required && (_jsxs("span", { className: "form-label__required", "aria-hidden": "true", children: [' ', "*"] }))] })), _jsxs("select", { ref: ref, id: finalId, className: classes, "aria-invalid": error ? true : undefined, "aria-describedby": describedBy, onKeyDown: handleKeyDown, required: props.required ? true : undefined, ...props, children: [placeholder && (_jsx("option", { value: "", disabled: true, children: placeholder })), options.map((option) => (_jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)))] }), helperText && !error && (_jsx("span", { id: helperId, className: "form-helper-text", children: helperText })), error && (_jsx("span", { id: errorId, className: "form-error-text", role: "alert", children: error }))] }));
48
+ });
49
+ Select.displayName = 'Select';