@a11ypros/a11y-ui-components 1.0.0 → 1.0.1
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/.storybook/custom.css +69 -0
- package/.storybook/main.ts +46 -0
- package/.storybook/manager.ts +26 -0
- package/.storybook/package.json +6 -0
- package/.storybook/preview.tsx +31 -0
- package/.storybook/public/logo.png +0 -0
- package/.storybook/vite.config.ts +24 -0
- package/.storybook/welcome.mdx +97 -0
- package/DEPLOYMENT.md +154 -0
- package/README.md +227 -0
- package/apps/web/app/(docs)/audit/audit.css +269 -0
- package/apps/web/app/(docs)/audit/page.tsx +271 -0
- package/apps/web/app/(docs)/components/button/page.tsx +49 -0
- package/apps/web/app/(docs)/components/form/page.tsx +92 -0
- package/apps/web/app/(docs)/components/link/page.tsx +31 -0
- package/apps/web/app/(docs)/components/modal/page.tsx +41 -0
- package/apps/web/app/(docs)/components/page.tsx +37 -0
- package/apps/web/app/(docs)/components/table/page.tsx +54 -0
- package/apps/web/app/(docs)/components/tabs/page.tsx +61 -0
- package/apps/web/app/(docs)/components/toast/page.tsx +51 -0
- package/apps/web/app/api/audit/route.ts +128 -0
- package/apps/web/app/favicon.ico +0 -0
- package/apps/web/app/layout.tsx +20 -0
- package/apps/web/app/page.tsx +17 -0
- package/apps/web/app/styles/globals.css +5 -0
- package/apps/web/next-env.d.ts +5 -0
- package/apps/web/next.config.js +21 -0
- package/apps/web/package.json +28 -0
- package/apps/web/public/_headers +17 -0
- package/apps/web/public/_redirects +31 -0
- package/apps/web/public/logo.png +0 -0
- package/apps/web/tsconfig.json +29 -0
- package/netlify/functions/audit.ts +163 -0
- package/netlify.toml +37 -0
- package/package.json +30 -58
- package/packages/design-system/README.md +252 -0
- package/packages/design-system/package.json +68 -0
- package/packages/design-system/scripts/copy-css.js +63 -0
- package/packages/design-system/src/components/Button/Button.stories.tsx +228 -0
- package/packages/design-system/src/components/Button/Button.tsx +137 -0
- package/packages/design-system/src/components/Button/index.ts +3 -0
- package/packages/design-system/src/components/DataTable/DataTable.stories.tsx +211 -0
- package/packages/design-system/src/components/DataTable/DataTable.tsx +293 -0
- package/packages/design-system/src/components/DataTable/index.ts +3 -0
- package/packages/design-system/src/components/Form/Checkbox.stories.tsx +252 -0
- package/packages/design-system/src/components/Form/Checkbox.tsx +114 -0
- package/packages/design-system/src/components/Form/Fieldset.stories.tsx +210 -0
- package/packages/design-system/src/components/Form/Fieldset.tsx +71 -0
- package/packages/design-system/src/components/Form/Input.stories.tsx +164 -0
- package/packages/design-system/src/components/Form/Input.tsx +113 -0
- package/packages/design-system/src/components/Form/Label.tsx +56 -0
- package/packages/design-system/src/components/Form/Radio.stories.tsx +265 -0
- package/packages/design-system/src/components/Form/Radio.tsx +147 -0
- package/packages/design-system/src/components/Form/Select.stories.tsx +295 -0
- package/packages/design-system/src/components/Form/Select.tsx +160 -0
- package/packages/design-system/src/components/Form/Textarea.stories.tsx +253 -0
- package/packages/design-system/src/components/Form/Textarea.tsx +145 -0
- package/packages/design-system/src/components/Form/index.ts +8 -0
- package/packages/design-system/src/components/Link/Link.stories.tsx +128 -0
- package/packages/design-system/src/components/Link/Link.tsx +117 -0
- package/packages/design-system/src/components/Link/index.ts +3 -0
- package/packages/design-system/src/components/Modal/Modal.stories.tsx +165 -0
- package/packages/design-system/src/components/Modal/Modal.tsx +202 -0
- package/packages/design-system/src/components/Modal/index.ts +3 -0
- package/packages/design-system/src/components/Tabs/Tabs.stories.tsx +213 -0
- package/packages/design-system/src/components/Tabs/Tabs.tsx +248 -0
- package/packages/design-system/src/components/Tabs/index.ts +3 -0
- package/packages/design-system/src/components/Toast/Toast.stories.tsx +153 -0
- package/packages/design-system/src/components/Toast/Toast.tsx +175 -0
- package/packages/design-system/src/components/Toast/ToastProvider.tsx +73 -0
- package/packages/design-system/src/components/Toast/index.ts +5 -0
- package/packages/design-system/src/hooks/useAriaLive.ts +51 -0
- package/packages/design-system/src/hooks/useFocusReturn.ts +40 -0
- package/packages/design-system/src/hooks/useFocusTrap.ts +82 -0
- package/{dist/index.js → packages/design-system/src/index.ts} +4 -0
- package/packages/design-system/src/styles/index.ts +3 -0
- package/packages/design-system/src/tokens/breakpoints.ts +28 -0
- package/packages/design-system/src/tokens/colors.ts +98 -0
- package/packages/design-system/src/tokens/index.ts +6 -0
- package/packages/design-system/src/tokens/motion.ts +41 -0
- package/packages/design-system/src/tokens/spacing.ts +24 -0
- package/packages/design-system/src/tokens/theme.ts +19 -0
- package/packages/design-system/src/tokens/typography.ts +64 -0
- package/packages/design-system/src/utils/aria.ts +108 -0
- package/packages/design-system/src/utils/focus.ts +87 -0
- package/packages/design-system/src/utils/index.ts +4 -0
- package/packages/design-system/src/utils/keyboard.ts +77 -0
- package/packages/design-system/tsconfig.json +17 -0
- package/public/logo.png +0 -0
- package/scripts/fix-storybook-paths.js +53 -0
- package/tsconfig.json +20 -0
- package/dist/components/Button/Button.d.ts +0 -37
- package/dist/components/Button/Button.d.ts.map +0 -1
- package/dist/components/Button/Button.js +0 -52
- package/dist/components/Button/index.d.ts +0 -3
- package/dist/components/Button/index.d.ts.map +0 -1
- package/dist/components/Button/index.js +0 -1
- package/dist/components/DataTable/DataTable.d.ts +0 -71
- package/dist/components/DataTable/DataTable.d.ts.map +0 -1
- package/dist/components/DataTable/DataTable.js +0 -122
- package/dist/components/DataTable/index.d.ts +0 -3
- package/dist/components/DataTable/index.d.ts.map +0 -1
- package/dist/components/DataTable/index.js +0 -1
- package/dist/components/Form/Checkbox.d.ts +0 -36
- package/dist/components/Form/Checkbox.d.ts.map +0 -1
- package/dist/components/Form/Checkbox.js +0 -39
- package/dist/components/Form/Fieldset.d.ts +0 -33
- package/dist/components/Form/Fieldset.d.ts.map +0 -1
- package/dist/components/Form/Fieldset.js +0 -34
- package/dist/components/Form/Input.d.ts +0 -37
- package/dist/components/Form/Input.d.ts.map +0 -1
- package/dist/components/Form/Input.js +0 -41
- package/dist/components/Form/Label.d.ts +0 -30
- package/dist/components/Form/Label.d.ts.map +0 -1
- package/dist/components/Form/Label.js +0 -30
- package/dist/components/Form/Radio.d.ts +0 -53
- package/dist/components/Form/Radio.d.ts.map +0 -1
- package/dist/components/Form/Radio.js +0 -39
- package/dist/components/Form/Select.d.ts +0 -51
- package/dist/components/Form/Select.d.ts.map +0 -1
- package/dist/components/Form/Select.js +0 -49
- package/dist/components/Form/Textarea.d.ts +0 -44
- package/dist/components/Form/Textarea.d.ts.map +0 -1
- package/dist/components/Form/Textarea.js +0 -43
- package/dist/components/Form/index.d.ts +0 -8
- package/dist/components/Form/index.d.ts.map +0 -1
- package/dist/components/Form/index.js +0 -7
- package/dist/components/Link/Link.d.ts +0 -34
- package/dist/components/Link/Link.d.ts.map +0 -1
- package/dist/components/Link/Link.js +0 -48
- package/dist/components/Link/index.d.ts +0 -3
- package/dist/components/Link/index.d.ts.map +0 -1
- package/dist/components/Link/index.js +0 -1
- package/dist/components/Modal/Modal.d.ts +0 -64
- package/dist/components/Modal/Modal.d.ts.map +0 -1
- package/dist/components/Modal/Modal.js +0 -108
- package/dist/components/Modal/index.d.ts +0 -3
- package/dist/components/Modal/index.d.ts.map +0 -1
- package/dist/components/Modal/index.js +0 -1
- package/dist/components/Tabs/Tabs.d.ts +0 -63
- package/dist/components/Tabs/Tabs.d.ts.map +0 -1
- package/dist/components/Tabs/Tabs.js +0 -134
- package/dist/components/Tabs/index.d.ts +0 -3
- package/dist/components/Tabs/index.d.ts.map +0 -1
- package/dist/components/Tabs/index.js +0 -1
- package/dist/components/Toast/Toast.d.ts +0 -59
- package/dist/components/Toast/Toast.d.ts.map +0 -1
- package/dist/components/Toast/Toast.js +0 -91
- package/dist/components/Toast/ToastProvider.d.ts +0 -22
- package/dist/components/Toast/ToastProvider.d.ts.map +0 -1
- package/dist/components/Toast/ToastProvider.js +0 -33
- package/dist/components/Toast/index.d.ts +0 -5
- package/dist/components/Toast/index.d.ts.map +0 -1
- package/dist/components/Toast/index.js +0 -2
- package/dist/hooks/useAriaLive.d.ts +0 -9
- package/dist/hooks/useAriaLive.d.ts.map +0 -1
- package/dist/hooks/useAriaLive.js +0 -39
- package/dist/hooks/useFocusReturn.d.ts +0 -9
- package/dist/hooks/useFocusReturn.d.ts.map +0 -1
- package/dist/hooks/useFocusReturn.js +0 -33
- package/dist/hooks/useFocusTrap.d.ts +0 -9
- package/dist/hooks/useFocusTrap.d.ts.map +0 -1
- package/dist/hooks/useFocusTrap.js +0 -68
- package/dist/index.d.ts +0 -22
- package/dist/index.d.ts.map +0 -1
- package/dist/styles/index.d.ts +0 -3
- package/dist/styles/index.d.ts.map +0 -1
- package/dist/styles/index.js +0 -1
- package/dist/tokens/breakpoints.d.ts +0 -25
- package/dist/tokens/breakpoints.d.ts.map +0 -1
- package/dist/tokens/breakpoints.js +0 -23
- package/dist/tokens/colors.d.ts +0 -81
- package/dist/tokens/colors.d.ts.map +0 -1
- package/dist/tokens/colors.js +0 -86
- package/dist/tokens/index.d.ts +0 -6
- package/dist/tokens/index.d.ts.map +0 -1
- package/dist/tokens/index.js +0 -5
- package/dist/tokens/motion.d.ts +0 -30
- package/dist/tokens/motion.d.ts.map +0 -1
- package/dist/tokens/motion.js +0 -34
- package/dist/tokens/spacing.d.ts +0 -22
- package/dist/tokens/spacing.d.ts.map +0 -1
- package/dist/tokens/spacing.js +0 -20
- package/dist/tokens/theme.d.ts +0 -159
- package/dist/tokens/theme.d.ts.map +0 -1
- package/dist/tokens/theme.js +0 -15
- package/dist/tokens/typography.d.ts +0 -45
- package/dist/tokens/typography.d.ts.map +0 -1
- package/dist/tokens/typography.js +0 -56
- package/dist/utils/aria.d.ts +0 -60
- package/dist/utils/aria.d.ts.map +0 -1
- package/dist/utils/aria.js +0 -86
- package/dist/utils/focus.d.ts +0 -30
- package/dist/utils/focus.d.ts.map +0 -1
- package/dist/utils/focus.js +0 -80
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -3
- package/dist/utils/keyboard.d.ts +0 -38
- package/dist/utils/keyboard.d.ts.map +0 -1
- package/dist/utils/keyboard.js +0 -59
- /package/{dist → packages/design-system/src}/components/Button/Button.css +0 -0
- /package/{dist → packages/design-system/src}/components/DataTable/DataTable.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Checkbox.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Fieldset.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Input.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Label.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Radio.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Select.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Textarea.css +0 -0
- /package/{dist → packages/design-system/src}/components/Link/Link.css +0 -0
- /package/{dist → packages/design-system/src}/components/Modal/Modal.css +0 -0
- /package/{dist → packages/design-system/src}/components/Tabs/Tabs.css +0 -0
- /package/{dist → packages/design-system/src}/components/Toast/Toast.css +0 -0
- /package/{dist → packages/design-system/src}/components/Toast/ToastProvider.css +0 -0
- /package/{dist → packages/design-system/src}/styles/components.css +0 -0
- /package/{dist → packages/design-system/src}/styles/global.css +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyboard event utilities
|
|
3
|
+
* Helpers for handling keyboard interactions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type KeyboardHandler = (event: React.KeyboardEvent) => void
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if a key is an activation key (Enter or Space)
|
|
10
|
+
*/
|
|
11
|
+
export function isActivationKey(key: string): boolean {
|
|
12
|
+
return key === 'Enter' || key === ' '
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if a key is an arrow key
|
|
17
|
+
*/
|
|
18
|
+
export function isArrowKey(key: string): boolean {
|
|
19
|
+
return ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(key)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if a key is a navigation key (Home, End, PageUp, PageDown)
|
|
24
|
+
*/
|
|
25
|
+
export function isNavigationKey(key: string): boolean {
|
|
26
|
+
return ['Home', 'End', 'PageUp', 'PageDown'].includes(key)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check if a key is an escape key
|
|
31
|
+
*/
|
|
32
|
+
export function isEscapeKey(key: string): boolean {
|
|
33
|
+
return key === 'Escape'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if modifier keys are pressed
|
|
38
|
+
*/
|
|
39
|
+
export function hasModifierKey(event: React.KeyboardEvent): boolean {
|
|
40
|
+
return event.ctrlKey || event.metaKey || event.altKey || event.shiftKey
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create a keyboard handler that only fires on specific keys
|
|
45
|
+
*/
|
|
46
|
+
export function createKeyHandler(
|
|
47
|
+
keys: string[],
|
|
48
|
+
handler: KeyboardHandler
|
|
49
|
+
): KeyboardHandler {
|
|
50
|
+
return (event: React.KeyboardEvent) => {
|
|
51
|
+
if (keys.includes(event.key)) {
|
|
52
|
+
handler(event)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Create a keyboard handler for activation keys (Enter/Space)
|
|
59
|
+
*/
|
|
60
|
+
export function createActivationHandler(
|
|
61
|
+
handler: KeyboardHandler
|
|
62
|
+
): KeyboardHandler {
|
|
63
|
+
return createKeyHandler(['Enter', ' '], (event) => {
|
|
64
|
+
event.preventDefault()
|
|
65
|
+
handler(event)
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create a keyboard handler for arrow keys
|
|
71
|
+
*/
|
|
72
|
+
export function createArrowKeyHandler(
|
|
73
|
+
handler: KeyboardHandler
|
|
74
|
+
): KeyboardHandler {
|
|
75
|
+
return createKeyHandler(['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'], handler)
|
|
76
|
+
}
|
|
77
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"target": "ES2020",
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"jsx": "react-jsx",
|
|
12
|
+
"noEmit": false
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist", "**/*.stories.tsx", "**/*.test.ts", "**/*.test.tsx"]
|
|
16
|
+
}
|
|
17
|
+
|
package/public/logo.png
ADDED
|
Binary file
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
|
|
6
|
+
const storybookDir = path.join(__dirname, '../apps/web/public/storybook-static')
|
|
7
|
+
const indexHtmlPath = path.join(storybookDir, 'index.html')
|
|
8
|
+
const iframeHtmlPath = path.join(storybookDir, 'iframe.html')
|
|
9
|
+
|
|
10
|
+
function fixHtmlPaths(html) {
|
|
11
|
+
// Replace relative paths with absolute paths
|
|
12
|
+
// ./sb-manager/runtime.js -> /storybook-static/sb-manager/runtime.js
|
|
13
|
+
html = html.replace(/href="\.\//g, 'href="/storybook-static/')
|
|
14
|
+
html = html.replace(/src="\.\//g, 'src="/storybook-static/')
|
|
15
|
+
html = html.replace(/import '\.\//g, "import '/storybook-static/")
|
|
16
|
+
html = html.replace(/import "\.\//g, 'import "/storybook-static/')
|
|
17
|
+
html = html.replace(/url\('\.\//g, "url('/storybook-static/")
|
|
18
|
+
html = html.replace(/url\("\.\//g, 'url("/storybook-static/')
|
|
19
|
+
|
|
20
|
+
// Fix JSON file references (Storybook loads index.json, project.json, etc.)
|
|
21
|
+
html = html.replace(/(["'])(\.\/)?([^"']+\.json)(["'])/g, '$1/storybook-static/$3$4')
|
|
22
|
+
|
|
23
|
+
// Fix logo path if it's using /logo.png
|
|
24
|
+
html = html.replace(/\/logo\.png/g, '/storybook-static/logo.png')
|
|
25
|
+
|
|
26
|
+
// Remove ALL CSP meta tags - we'll handle CSP via HTTP headers only
|
|
27
|
+
// This prevents any conflicts between meta tags and HTTP headers
|
|
28
|
+
html = html.replace(/<meta[^>]*http-equiv=["']Content-Security-Policy["'][^>]*>/gi, '')
|
|
29
|
+
|
|
30
|
+
return html
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Fix index.html
|
|
34
|
+
if (!fs.existsSync(indexHtmlPath)) {
|
|
35
|
+
console.error('Storybook index.html not found at:', indexHtmlPath)
|
|
36
|
+
process.exit(1)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let indexHtml = fs.readFileSync(indexHtmlPath, 'utf8')
|
|
40
|
+
indexHtml = fixHtmlPaths(indexHtml)
|
|
41
|
+
fs.writeFileSync(indexHtmlPath, indexHtml, 'utf8')
|
|
42
|
+
console.log('✅ Fixed Storybook paths in index.html')
|
|
43
|
+
|
|
44
|
+
// Fix iframe.html (where components actually render)
|
|
45
|
+
if (fs.existsSync(iframeHtmlPath)) {
|
|
46
|
+
let iframeHtml = fs.readFileSync(iframeHtmlPath, 'utf8')
|
|
47
|
+
iframeHtml = fixHtmlPaths(iframeHtml)
|
|
48
|
+
fs.writeFileSync(iframeHtmlPath, iframeHtml, 'utf8')
|
|
49
|
+
console.log('✅ Fixed Storybook paths in iframe.html')
|
|
50
|
+
} else {
|
|
51
|
+
console.warn('⚠️ iframe.html not found - components may not load correctly')
|
|
52
|
+
}
|
|
53
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
5
|
+
"jsx": "react-jsx",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleResolution": "bundler",
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"noEmit": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"incremental": true
|
|
17
|
+
},
|
|
18
|
+
"exclude": ["node_modules", "dist", ".next", "storybook-static"]
|
|
19
|
+
}
|
|
20
|
+
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import './Button.css';
|
|
3
|
-
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
4
|
-
/**
|
|
5
|
-
* Visual variant of the button
|
|
6
|
-
*/
|
|
7
|
-
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
|
|
8
|
-
/**
|
|
9
|
-
* Size of the button
|
|
10
|
-
*/
|
|
11
|
-
size?: 'sm' | 'md' | 'lg';
|
|
12
|
-
/**
|
|
13
|
-
* Whether the button is in a loading state
|
|
14
|
-
*/
|
|
15
|
-
loading?: boolean;
|
|
16
|
-
/**
|
|
17
|
-
* ARIA label for the button (required if no visible text)
|
|
18
|
-
*/
|
|
19
|
-
'aria-label'?: string;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Accessible Button component
|
|
23
|
-
*
|
|
24
|
-
* WCAG Compliance:
|
|
25
|
-
* - 2.1.1 Keyboard: Full keyboard support (Enter/Space)
|
|
26
|
-
* - 2.4.7 Focus Visible: Clear focus indicators
|
|
27
|
-
* - 4.1.2 Name, Role, Value: Proper ARIA attributes
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* ```tsx
|
|
31
|
-
* <Button variant="primary" onClick={handleClick}>
|
|
32
|
-
* Click me
|
|
33
|
-
* </Button>
|
|
34
|
-
* ```
|
|
35
|
-
*/
|
|
36
|
-
export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
37
|
-
//# sourceMappingURL=Button.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../src/components/Button/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,cAAc,CAAA;AAErB,MAAM,WAAW,WAAY,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IAChF;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,CAAA;IAEtD;;OAEG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IAEzB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,MAAM,uFA2FlB,CAAA"}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { createActivationHandler } from '../../utils/keyboard';
|
|
4
|
-
import { getAriaLabel, getBusyAttributes } from '../../utils/aria';
|
|
5
|
-
import './Button.css';
|
|
6
|
-
/**
|
|
7
|
-
* Accessible Button component
|
|
8
|
-
*
|
|
9
|
-
* WCAG Compliance:
|
|
10
|
-
* - 2.1.1 Keyboard: Full keyboard support (Enter/Space)
|
|
11
|
-
* - 2.4.7 Focus Visible: Clear focus indicators
|
|
12
|
-
* - 4.1.2 Name, Role, Value: Proper ARIA attributes
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```tsx
|
|
16
|
-
* <Button variant="primary" onClick={handleClick}>
|
|
17
|
-
* Click me
|
|
18
|
-
* </Button>
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
export const Button = React.forwardRef(({ variant = 'primary', size = 'md', loading = false, disabled, children, className = '', onClick, onKeyDown, 'aria-label': ariaLabel, ...props }, ref) => {
|
|
22
|
-
const isDisabled = disabled || loading;
|
|
23
|
-
const handleKeyDown = React.useCallback((event) => {
|
|
24
|
-
// Handle activation keys
|
|
25
|
-
const activationHandler = createActivationHandler((e) => {
|
|
26
|
-
if (!isDisabled && onClick) {
|
|
27
|
-
onClick(e);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
activationHandler(event);
|
|
31
|
-
// Call user's onKeyDown if provided
|
|
32
|
-
if (onKeyDown) {
|
|
33
|
-
onKeyDown(event);
|
|
34
|
-
}
|
|
35
|
-
}, [isDisabled, onClick, onKeyDown]);
|
|
36
|
-
const ariaProps = {
|
|
37
|
-
...getAriaLabel(ariaLabel),
|
|
38
|
-
...getBusyAttributes(loading),
|
|
39
|
-
...props,
|
|
40
|
-
};
|
|
41
|
-
const classes = [
|
|
42
|
-
'btn',
|
|
43
|
-
`btn--${variant}`,
|
|
44
|
-
`btn--${size}`,
|
|
45
|
-
loading && 'btn--loading',
|
|
46
|
-
className,
|
|
47
|
-
]
|
|
48
|
-
.filter(Boolean)
|
|
49
|
-
.join(' ');
|
|
50
|
-
return (_jsxs("button", { ref: ref, type: "button", className: classes, disabled: isDisabled, onClick: onClick, onKeyDown: handleKeyDown, "aria-disabled": isDisabled, ...ariaProps, children: [loading && (_jsx("span", { className: "btn__spinner", "aria-hidden": "true", children: _jsx("svg", { className: "btn__spinner-icon", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("circle", { className: "btn__spinner-circle", cx: "8", cy: "8", r: "6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeDasharray: "31.416", strokeDashoffset: "31.416" }) }) })), _jsx("span", { className: "btn__content", children: children })] }));
|
|
51
|
-
});
|
|
52
|
-
Button.displayName = 'Button';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Button/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { Button } from './Button';
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import './DataTable.css';
|
|
3
|
-
export interface DataTableColumn<T> {
|
|
4
|
-
key: string;
|
|
5
|
-
header: string;
|
|
6
|
-
render?: (row: T, index: number) => React.ReactNode;
|
|
7
|
-
sortable?: boolean;
|
|
8
|
-
width?: string;
|
|
9
|
-
}
|
|
10
|
-
export interface DataTableProps<T> {
|
|
11
|
-
/**
|
|
12
|
-
* Data rows
|
|
13
|
-
*/
|
|
14
|
-
data: T[];
|
|
15
|
-
/**
|
|
16
|
-
* Column definitions
|
|
17
|
-
*/
|
|
18
|
-
columns: DataTableColumn<T>[];
|
|
19
|
-
/**
|
|
20
|
-
* Key function to get unique ID for each row
|
|
21
|
-
*/
|
|
22
|
-
getRowId: (row: T) => string;
|
|
23
|
-
/**
|
|
24
|
-
* Whether rows are selectable
|
|
25
|
-
*/
|
|
26
|
-
selectable?: boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Selected row IDs
|
|
29
|
-
*/
|
|
30
|
-
selectedRows?: string[];
|
|
31
|
-
/**
|
|
32
|
-
* Callback when selection changes
|
|
33
|
-
*/
|
|
34
|
-
onSelectionChange?: (selectedIds: string[]) => void;
|
|
35
|
-
/**
|
|
36
|
-
* Sort configuration
|
|
37
|
-
*/
|
|
38
|
-
sortConfig?: {
|
|
39
|
-
column: string;
|
|
40
|
-
direction: 'asc' | 'desc';
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* Callback when sort changes
|
|
44
|
-
*/
|
|
45
|
-
onSortChange?: (column: string, direction: 'asc' | 'desc') => void;
|
|
46
|
-
/**
|
|
47
|
-
* Caption for the table (required for accessibility)
|
|
48
|
-
*/
|
|
49
|
-
caption?: string;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Accessible DataTable component
|
|
53
|
-
*
|
|
54
|
-
* WCAG Compliance:
|
|
55
|
-
* - 1.3.1 Info and Relationships: Semantic table structure
|
|
56
|
-
* - 2.1.1 Keyboard: Arrow keys, Home/End navigation
|
|
57
|
-
* - 4.1.2 Name, Role, Value: Proper ARIA attributes
|
|
58
|
-
* - 4.1.3 Status Messages: Sort announcements
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* ```tsx
|
|
62
|
-
* <DataTable
|
|
63
|
-
* data={users}
|
|
64
|
-
* columns={columns}
|
|
65
|
-
* getRowId={(user) => user.id}
|
|
66
|
-
* caption="User list"
|
|
67
|
-
* />
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
export declare function DataTable<T extends Record<string, any>>({ data, columns, getRowId, selectable, selectedRows, onSelectionChange, sortConfig, onSortChange, caption, }: DataTableProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
71
|
-
//# sourceMappingURL=DataTable.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/DataTable.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAwC,MAAM,OAAO,CAAA;AAI5D,OAAO,iBAAiB,CAAA;AAExB,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAA;IACnD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B;;OAEG;IACH,IAAI,EAAE,CAAC,EAAE,CAAA;IAET;;OAEG;IACH,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,CAAA;IAE7B;;OAEG;IACH,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAA;IAE5B;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IAEpB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IAEvB;;OAEG;IACH,iBAAiB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IAEnD;;OAEG;IACH,UAAU,CAAC,EAAE;QACX,MAAM,EAAE,MAAM,CAAA;QACd,SAAS,EAAE,KAAK,GAAG,MAAM,CAAA;KAC1B,CAAA;IAED;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,GAAG,MAAM,KAAK,IAAI,CAAA;IAElE;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACvD,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,UAAkB,EAClB,YAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,OAAO,GACR,EAAE,cAAc,CAAC,CAAC,CAAC,2CAoMnB"}
|
|
@@ -1,122 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { DataTable } from './DataTable';
|
|
@@ -1,36 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,39 +0,0 @@
|
|
|
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';
|
|
@@ -1,33 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,34 +0,0 @@
|
|
|
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';
|