@qwickapps/react-framework 1.3.2 → 1.3.4
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/README.md +326 -0
- package/dist/components/AccessibilityProvider.d.ts +64 -0
- package/dist/components/AccessibilityProvider.d.ts.map +1 -0
- package/dist/components/Breadcrumbs.d.ts +39 -0
- package/dist/components/Breadcrumbs.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.d.ts +39 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/QwickApp.d.ts.map +1 -1
- package/dist/components/forms/FormBlock.d.ts +1 -1
- package/dist/components/forms/FormBlock.d.ts.map +1 -1
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/input/SwitchInputField.d.ts +28 -0
- package/dist/components/input/SwitchInputField.d.ts.map +1 -0
- package/dist/components/input/index.d.ts +2 -0
- package/dist/components/input/index.d.ts.map +1 -1
- package/dist/components/layout/CollapsibleLayout/CollapsibleLayout.d.ts +34 -0
- package/dist/components/layout/CollapsibleLayout/CollapsibleLayout.d.ts.map +1 -0
- package/dist/components/layout/CollapsibleLayout/index.d.ts +9 -0
- package/dist/components/layout/CollapsibleLayout/index.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts +2 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/index.bundled.css +12 -0
- package/dist/index.esm.js +1678 -25
- package/dist/index.js +1689 -21
- package/dist/schemas/CollapsibleLayoutSchema.d.ts +31 -0
- package/dist/schemas/CollapsibleLayoutSchema.d.ts.map +1 -0
- package/dist/schemas/SwitchInputFieldSchema.d.ts +18 -0
- package/dist/schemas/SwitchInputFieldSchema.d.ts.map +1 -0
- package/dist/types/CollapsibleLayout.d.ts +142 -0
- package/dist/types/CollapsibleLayout.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/AccessibilityProvider.tsx +466 -0
- package/src/components/Breadcrumbs.tsx +223 -0
- package/src/components/ErrorBoundary.tsx +216 -0
- package/src/components/QwickApp.tsx +17 -11
- package/src/components/__tests__/AccessibilityProvider.test.tsx +330 -0
- package/src/components/__tests__/Breadcrumbs.test.tsx +268 -0
- package/src/components/__tests__/ErrorBoundary.test.tsx +163 -0
- package/src/components/forms/FormBlock.tsx +2 -2
- package/src/components/index.ts +3 -0
- package/src/components/input/SwitchInputField.tsx +165 -0
- package/src/components/input/index.ts +2 -0
- package/src/components/layout/CollapsibleLayout/CollapsibleLayout.tsx +554 -0
- package/src/components/layout/CollapsibleLayout/__tests__/CollapsibleLayout.test.tsx +1469 -0
- package/src/components/layout/CollapsibleLayout/index.tsx +17 -0
- package/src/components/layout/index.ts +4 -1
- package/src/components/pages/FormPage.tsx +1 -1
- package/src/schemas/CollapsibleLayoutSchema.ts +276 -0
- package/src/schemas/SwitchInputFieldSchema.ts +99 -0
- package/src/stories/AccessibilityProvider.stories.tsx +284 -0
- package/src/stories/Breadcrumbs.stories.tsx +304 -0
- package/src/stories/CollapsibleLayout.stories.tsx +1566 -0
- package/src/stories/ErrorBoundary.stories.tsx +159 -0
- package/src/types/CollapsibleLayout.ts +231 -0
- package/src/types/index.ts +1 -0
- package/dist/schemas/Builders.d.ts +0 -7
- package/dist/schemas/Builders.d.ts.map +0 -1
- package/dist/schemas/types.d.ts +0 -7
- package/dist/schemas/types.d.ts.map +0 -1
- package/dist/types/DataBinding.d.ts +0 -7
- package/dist/types/DataBinding.d.ts.map +0 -1
- package/dist/types/DataProvider.d.ts +0 -7
- package/dist/types/DataProvider.d.ts.map +0 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CollapsibleLayout Component Exports
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { default } from './CollapsibleLayout';
|
|
8
|
+
export {
|
|
9
|
+
CollapsibleLayout,
|
|
10
|
+
CollapsibleLayoutView,
|
|
11
|
+
useCollapsibleState,
|
|
12
|
+
} from './CollapsibleLayout';
|
|
13
|
+
export type {
|
|
14
|
+
CollapsibleLayoutProps,
|
|
15
|
+
CollapsibleLayoutViewProps,
|
|
16
|
+
UseCollapsibleLayoutState,
|
|
17
|
+
} from './CollapsibleLayout';
|
|
@@ -10,4 +10,7 @@ export { GridLayout } from './GridLayout';
|
|
|
10
10
|
export type { GridLayoutProps } from './GridLayout';
|
|
11
11
|
|
|
12
12
|
export { GridCell } from './GridCell';
|
|
13
|
-
export type { GridCellProps } from './GridCell';
|
|
13
|
+
export type { GridCellProps } from './GridCell';
|
|
14
|
+
|
|
15
|
+
export { CollapsibleLayout, CollapsibleLayoutView, useCollapsibleState } from './CollapsibleLayout';
|
|
16
|
+
export type { CollapsibleLayoutProps, CollapsibleLayoutViewProps, UseCollapsibleLayoutState } from './CollapsibleLayout';
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CollapsibleLayout Schema - Data model for collapsible layout component
|
|
3
|
+
*
|
|
4
|
+
* Advanced expandable/collapsible container with header controls, animations,
|
|
5
|
+
* content management, and state persistence capabilities.
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
Schema,
|
|
12
|
+
Field,
|
|
13
|
+
Editor,
|
|
14
|
+
Model,
|
|
15
|
+
FieldType
|
|
16
|
+
} from '@qwickapps/schema';
|
|
17
|
+
import { IsOptional, IsString, IsBoolean, IsIn } from 'class-validator';
|
|
18
|
+
|
|
19
|
+
@Schema('CollapsibleLayout', '1.0.0')
|
|
20
|
+
export class CollapsibleLayoutModel extends Model {
|
|
21
|
+
|
|
22
|
+
// ========================================
|
|
23
|
+
// Core Collapse Control
|
|
24
|
+
// ========================================
|
|
25
|
+
|
|
26
|
+
@Field({ defaultValue: false })
|
|
27
|
+
@Editor({
|
|
28
|
+
field_type: FieldType.BOOLEAN,
|
|
29
|
+
label: 'Collapsed',
|
|
30
|
+
description: 'Whether the layout is currently collapsed'
|
|
31
|
+
})
|
|
32
|
+
@IsOptional()
|
|
33
|
+
@IsBoolean()
|
|
34
|
+
collapsed?: boolean;
|
|
35
|
+
|
|
36
|
+
@Field({ defaultValue: false })
|
|
37
|
+
@Editor({
|
|
38
|
+
field_type: FieldType.BOOLEAN,
|
|
39
|
+
label: 'Default Collapsed',
|
|
40
|
+
description: 'Initial collapsed state for uncontrolled usage'
|
|
41
|
+
})
|
|
42
|
+
@IsOptional()
|
|
43
|
+
@IsBoolean()
|
|
44
|
+
defaultCollapsed?: boolean;
|
|
45
|
+
|
|
46
|
+
// ========================================
|
|
47
|
+
// Enhanced Header Configuration
|
|
48
|
+
// ========================================
|
|
49
|
+
|
|
50
|
+
@Field()
|
|
51
|
+
@Editor({
|
|
52
|
+
field_type: FieldType.TEXT,
|
|
53
|
+
label: 'Title',
|
|
54
|
+
description: 'Main title displayed in the header',
|
|
55
|
+
placeholder: 'Enter title...'
|
|
56
|
+
})
|
|
57
|
+
@IsOptional()
|
|
58
|
+
@IsString()
|
|
59
|
+
title?: string;
|
|
60
|
+
|
|
61
|
+
@Field()
|
|
62
|
+
@Editor({
|
|
63
|
+
field_type: FieldType.TEXT,
|
|
64
|
+
label: 'Subtitle',
|
|
65
|
+
description: 'Secondary text displayed below the title',
|
|
66
|
+
placeholder: 'Enter subtitle...'
|
|
67
|
+
})
|
|
68
|
+
@IsOptional()
|
|
69
|
+
@IsString()
|
|
70
|
+
subtitle?: string;
|
|
71
|
+
|
|
72
|
+
@Field()
|
|
73
|
+
@Editor({
|
|
74
|
+
field_type: FieldType.TEXT,
|
|
75
|
+
label: 'Lead Icon',
|
|
76
|
+
description: 'Icon displayed before the title (icon identifier or HTML)',
|
|
77
|
+
placeholder: 'Icon identifier...'
|
|
78
|
+
})
|
|
79
|
+
@IsOptional()
|
|
80
|
+
@IsString()
|
|
81
|
+
leadIcon?: string;
|
|
82
|
+
|
|
83
|
+
@Field()
|
|
84
|
+
@Editor({
|
|
85
|
+
field_type: FieldType.TEXTAREA,
|
|
86
|
+
label: 'Header Actions',
|
|
87
|
+
description: 'Additional controls displayed in header (HTML/React content)',
|
|
88
|
+
placeholder: 'Enter header actions HTML...'
|
|
89
|
+
})
|
|
90
|
+
@IsOptional()
|
|
91
|
+
@IsString()
|
|
92
|
+
headerActions?: string;
|
|
93
|
+
|
|
94
|
+
// ========================================
|
|
95
|
+
// Content Management
|
|
96
|
+
// ========================================
|
|
97
|
+
|
|
98
|
+
@Field()
|
|
99
|
+
@Editor({
|
|
100
|
+
field_type: FieldType.TEXTAREA,
|
|
101
|
+
label: 'Collapsed View',
|
|
102
|
+
description: 'Summary content shown when collapsed (HTML supported)',
|
|
103
|
+
placeholder: 'Enter collapsed view content...'
|
|
104
|
+
})
|
|
105
|
+
@IsOptional()
|
|
106
|
+
@IsString()
|
|
107
|
+
collapsedView?: string;
|
|
108
|
+
|
|
109
|
+
@Field()
|
|
110
|
+
@Editor({
|
|
111
|
+
field_type: FieldType.TEXTAREA,
|
|
112
|
+
label: 'Content',
|
|
113
|
+
description: 'Main content shown when expanded (HTML supported)',
|
|
114
|
+
placeholder: 'Enter main content...'
|
|
115
|
+
})
|
|
116
|
+
@IsOptional()
|
|
117
|
+
@IsString()
|
|
118
|
+
children?: string;
|
|
119
|
+
|
|
120
|
+
@Field()
|
|
121
|
+
@Editor({
|
|
122
|
+
field_type: FieldType.TEXTAREA,
|
|
123
|
+
label: 'Footer View',
|
|
124
|
+
description: 'Always visible footer content (HTML supported)',
|
|
125
|
+
placeholder: 'Enter footer content...'
|
|
126
|
+
})
|
|
127
|
+
@IsOptional()
|
|
128
|
+
@IsString()
|
|
129
|
+
footerView?: string;
|
|
130
|
+
|
|
131
|
+
// ========================================
|
|
132
|
+
// Advanced Features
|
|
133
|
+
// ========================================
|
|
134
|
+
|
|
135
|
+
@Field({ defaultValue: 'header' })
|
|
136
|
+
@Editor({
|
|
137
|
+
field_type: FieldType.SELECT,
|
|
138
|
+
label: 'Trigger Area',
|
|
139
|
+
description: 'Area that responds to clicks for toggle functionality',
|
|
140
|
+
validation: {
|
|
141
|
+
options: [
|
|
142
|
+
{ label: 'Button Only', value: 'button' },
|
|
143
|
+
{ label: 'Header Area', value: 'header' },
|
|
144
|
+
{ label: 'Button and Header', value: 'both' }
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
@IsOptional()
|
|
149
|
+
@IsIn(['button', 'header', 'both'])
|
|
150
|
+
triggerArea?: 'button' | 'header' | 'both';
|
|
151
|
+
|
|
152
|
+
@Field({ defaultValue: 'slide' })
|
|
153
|
+
@Editor({
|
|
154
|
+
field_type: FieldType.SELECT,
|
|
155
|
+
label: 'Animation Style',
|
|
156
|
+
description: 'Animation variant for expand/collapse transitions',
|
|
157
|
+
validation: {
|
|
158
|
+
options: [
|
|
159
|
+
{ label: 'Fade', value: 'fade' },
|
|
160
|
+
{ label: 'Slide', value: 'slide' },
|
|
161
|
+
{ label: 'Scale', value: 'scale' }
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
@IsOptional()
|
|
166
|
+
@IsIn(['fade', 'slide', 'scale'])
|
|
167
|
+
animationStyle?: 'fade' | 'slide' | 'scale';
|
|
168
|
+
|
|
169
|
+
@Field({ defaultValue: false })
|
|
170
|
+
@Editor({
|
|
171
|
+
field_type: FieldType.BOOLEAN,
|
|
172
|
+
label: 'Persist State',
|
|
173
|
+
description: 'Save collapse state in local storage'
|
|
174
|
+
})
|
|
175
|
+
@IsOptional()
|
|
176
|
+
@IsBoolean()
|
|
177
|
+
persistState?: boolean;
|
|
178
|
+
|
|
179
|
+
// ========================================
|
|
180
|
+
// Icons Configuration
|
|
181
|
+
// ========================================
|
|
182
|
+
|
|
183
|
+
@Field()
|
|
184
|
+
@Editor({
|
|
185
|
+
field_type: FieldType.TEXT,
|
|
186
|
+
label: 'Collapsed Icon',
|
|
187
|
+
description: 'Icon shown when content is collapsed (default: VisibilityIcon)',
|
|
188
|
+
placeholder: 'Icon identifier...'
|
|
189
|
+
})
|
|
190
|
+
@IsOptional()
|
|
191
|
+
@IsString()
|
|
192
|
+
collapsedIcon?: string;
|
|
193
|
+
|
|
194
|
+
@Field()
|
|
195
|
+
@Editor({
|
|
196
|
+
field_type: FieldType.TEXT,
|
|
197
|
+
label: 'Expanded Icon',
|
|
198
|
+
description: 'Icon shown when content is expanded (default: VisibilityOffIcon)',
|
|
199
|
+
placeholder: 'Icon identifier...'
|
|
200
|
+
})
|
|
201
|
+
@IsOptional()
|
|
202
|
+
@IsString()
|
|
203
|
+
expandedIcon?: string;
|
|
204
|
+
|
|
205
|
+
// ========================================
|
|
206
|
+
// Layout Configuration
|
|
207
|
+
// ========================================
|
|
208
|
+
|
|
209
|
+
@Field({ defaultValue: true })
|
|
210
|
+
@Editor({
|
|
211
|
+
field_type: FieldType.BOOLEAN,
|
|
212
|
+
label: 'Show Divider',
|
|
213
|
+
description: 'Show divider lines between header, content, and footer sections'
|
|
214
|
+
})
|
|
215
|
+
@IsOptional()
|
|
216
|
+
@IsBoolean()
|
|
217
|
+
showDivider?: boolean;
|
|
218
|
+
|
|
219
|
+
// ========================================
|
|
220
|
+
// Framework Integration
|
|
221
|
+
// ========================================
|
|
222
|
+
|
|
223
|
+
@Field({ defaultValue: 'default' })
|
|
224
|
+
@Editor({
|
|
225
|
+
field_type: FieldType.SELECT,
|
|
226
|
+
label: 'Variant',
|
|
227
|
+
description: 'Visual style variant for the layout container',
|
|
228
|
+
validation: {
|
|
229
|
+
options: [
|
|
230
|
+
{ label: 'Default', value: 'default' },
|
|
231
|
+
{ label: 'Outlined', value: 'outlined' },
|
|
232
|
+
{ label: 'Elevated', value: 'elevated' },
|
|
233
|
+
{ label: 'Filled', value: 'filled' }
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
@IsOptional()
|
|
238
|
+
@IsIn(['default', 'outlined', 'elevated', 'filled'])
|
|
239
|
+
variant?: 'default' | 'outlined' | 'elevated' | 'filled';
|
|
240
|
+
|
|
241
|
+
@Field({ defaultValue: 'comfortable' })
|
|
242
|
+
@Editor({
|
|
243
|
+
field_type: FieldType.SELECT,
|
|
244
|
+
label: 'Header Spacing',
|
|
245
|
+
description: 'Padding/spacing within the header area',
|
|
246
|
+
validation: {
|
|
247
|
+
options: [
|
|
248
|
+
{ label: 'Compact', value: 'compact' },
|
|
249
|
+
{ label: 'Comfortable', value: 'comfortable' },
|
|
250
|
+
{ label: 'Spacious', value: 'spacious' }
|
|
251
|
+
]
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
@IsOptional()
|
|
255
|
+
@IsIn(['compact', 'comfortable', 'spacious'])
|
|
256
|
+
headerSpacing?: 'compact' | 'comfortable' | 'spacious';
|
|
257
|
+
|
|
258
|
+
@Field({ defaultValue: 'comfortable' })
|
|
259
|
+
@Editor({
|
|
260
|
+
field_type: FieldType.SELECT,
|
|
261
|
+
label: 'Content Spacing',
|
|
262
|
+
description: 'Padding/spacing within the content area',
|
|
263
|
+
validation: {
|
|
264
|
+
options: [
|
|
265
|
+
{ label: 'Compact', value: 'compact' },
|
|
266
|
+
{ label: 'Comfortable', value: 'comfortable' },
|
|
267
|
+
{ label: 'Spacious', value: 'spacious' }
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
})
|
|
271
|
+
@IsOptional()
|
|
272
|
+
@IsIn(['compact', 'comfortable', 'spacious'])
|
|
273
|
+
contentSpacing?: 'compact' | 'comfortable' | 'spacious';
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export default CollapsibleLayoutModel;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SwitchInputField Schema - Data model for switch input field component
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
Schema,
|
|
9
|
+
Field,
|
|
10
|
+
Editor,
|
|
11
|
+
Model,
|
|
12
|
+
FieldType
|
|
13
|
+
} from '@qwickapps/schema';
|
|
14
|
+
import { IsOptional, IsString, IsBoolean } from 'class-validator';
|
|
15
|
+
|
|
16
|
+
@Schema('SwitchInputField', '1.0.0')
|
|
17
|
+
export class SwitchInputFieldModel extends Model {
|
|
18
|
+
@Field()
|
|
19
|
+
@Editor({
|
|
20
|
+
field_type: FieldType.TEXT,
|
|
21
|
+
label: 'Label',
|
|
22
|
+
description: 'The label text for the switch'
|
|
23
|
+
})
|
|
24
|
+
@IsOptional()
|
|
25
|
+
@IsString()
|
|
26
|
+
label?: string;
|
|
27
|
+
|
|
28
|
+
@Field({ defaultValue: false })
|
|
29
|
+
@Editor({
|
|
30
|
+
field_type: FieldType.BOOLEAN,
|
|
31
|
+
label: 'Checked',
|
|
32
|
+
description: 'Whether the switch is checked'
|
|
33
|
+
})
|
|
34
|
+
@IsOptional()
|
|
35
|
+
@IsBoolean()
|
|
36
|
+
checked?: boolean;
|
|
37
|
+
|
|
38
|
+
@Field({ defaultValue: false })
|
|
39
|
+
@Editor({
|
|
40
|
+
field_type: FieldType.BOOLEAN,
|
|
41
|
+
label: 'Required',
|
|
42
|
+
description: 'Whether the field is required'
|
|
43
|
+
})
|
|
44
|
+
@IsOptional()
|
|
45
|
+
@IsBoolean()
|
|
46
|
+
required?: boolean;
|
|
47
|
+
|
|
48
|
+
@Field({ defaultValue: false })
|
|
49
|
+
@Editor({
|
|
50
|
+
field_type: FieldType.BOOLEAN,
|
|
51
|
+
label: 'Disabled',
|
|
52
|
+
description: 'Whether the field is disabled'
|
|
53
|
+
})
|
|
54
|
+
@IsOptional()
|
|
55
|
+
@IsBoolean()
|
|
56
|
+
disabled?: boolean;
|
|
57
|
+
|
|
58
|
+
@Field()
|
|
59
|
+
@Editor({
|
|
60
|
+
field_type: FieldType.TEXT,
|
|
61
|
+
label: 'Error',
|
|
62
|
+
description: 'Error message to display'
|
|
63
|
+
})
|
|
64
|
+
@IsOptional()
|
|
65
|
+
@IsString()
|
|
66
|
+
error?: string;
|
|
67
|
+
|
|
68
|
+
@Field()
|
|
69
|
+
@Editor({
|
|
70
|
+
field_type: FieldType.TEXT,
|
|
71
|
+
label: 'Helper Text',
|
|
72
|
+
description: 'Helper text to display below the switch'
|
|
73
|
+
})
|
|
74
|
+
@IsOptional()
|
|
75
|
+
@IsString()
|
|
76
|
+
helperText?: string;
|
|
77
|
+
|
|
78
|
+
@Field({ defaultValue: 'medium' })
|
|
79
|
+
@Editor({
|
|
80
|
+
field_type: FieldType.SELECT,
|
|
81
|
+
label: 'Size',
|
|
82
|
+
description: 'Size of the switch'
|
|
83
|
+
})
|
|
84
|
+
@IsOptional()
|
|
85
|
+
@IsString()
|
|
86
|
+
size?: 'small' | 'medium';
|
|
87
|
+
|
|
88
|
+
@Field({ defaultValue: 'primary' })
|
|
89
|
+
@Editor({
|
|
90
|
+
field_type: FieldType.SELECT,
|
|
91
|
+
label: 'Color',
|
|
92
|
+
description: 'Color theme of the switch'
|
|
93
|
+
})
|
|
94
|
+
@IsOptional()
|
|
95
|
+
@IsString()
|
|
96
|
+
color?: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export default SwitchInputFieldModel;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { AccessibilityProvider, useAccessibility } from '../components/AccessibilityProvider';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
// Demo component that uses accessibility features
|
|
6
|
+
const AccessibilityDemo = () => {
|
|
7
|
+
const {
|
|
8
|
+
highContrast,
|
|
9
|
+
reducedMotion,
|
|
10
|
+
largeText,
|
|
11
|
+
isKeyboardUser,
|
|
12
|
+
issues,
|
|
13
|
+
setHighContrast,
|
|
14
|
+
setReducedMotion,
|
|
15
|
+
setLargeText,
|
|
16
|
+
announce,
|
|
17
|
+
announcePolite,
|
|
18
|
+
announceAssertive,
|
|
19
|
+
runAudit,
|
|
20
|
+
clearIssues
|
|
21
|
+
} = useAccessibility();
|
|
22
|
+
|
|
23
|
+
const [message, setMessage] = React.useState('');
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div style={{ padding: '2rem', fontFamily: 'system-ui, sans-serif' }}>
|
|
27
|
+
<h2>Accessibility Provider Demo</h2>
|
|
28
|
+
|
|
29
|
+
{/* System Status */}
|
|
30
|
+
<div style={{
|
|
31
|
+
marginBottom: '2rem',
|
|
32
|
+
padding: '1rem',
|
|
33
|
+
background: '#f5f5f5',
|
|
34
|
+
borderRadius: '8px'
|
|
35
|
+
}}>
|
|
36
|
+
<h3>System Status</h3>
|
|
37
|
+
<ul style={{ listStyle: 'none', padding: 0 }}>
|
|
38
|
+
<li>🎨 High Contrast: {highContrast ? '✅ Enabled' : '❌ Disabled'}</li>
|
|
39
|
+
<li>⚡ Reduced Motion: {reducedMotion ? '✅ Enabled' : '❌ Disabled'}</li>
|
|
40
|
+
<li>🔤 Large Text: {largeText ? '✅ Enabled' : '❌ Disabled'}</li>
|
|
41
|
+
<li>⌨️ Keyboard User: {isKeyboardUser ? '✅ Yes' : '❌ No'}</li>
|
|
42
|
+
<li>🚨 Issues Found: {issues.length}</li>
|
|
43
|
+
</ul>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{/* Controls */}
|
|
47
|
+
<div style={{ marginBottom: '2rem' }}>
|
|
48
|
+
<h3>Accessibility Controls</h3>
|
|
49
|
+
<div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
|
|
50
|
+
<button
|
|
51
|
+
onClick={() => setHighContrast(!highContrast)}
|
|
52
|
+
style={{
|
|
53
|
+
padding: '0.5rem 1rem',
|
|
54
|
+
background: highContrast ? '#000' : '#007cba',
|
|
55
|
+
color: 'white',
|
|
56
|
+
border: 'none',
|
|
57
|
+
borderRadius: '4px'
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
Toggle High Contrast
|
|
61
|
+
</button>
|
|
62
|
+
|
|
63
|
+
<button
|
|
64
|
+
onClick={() => setReducedMotion(!reducedMotion)}
|
|
65
|
+
style={{
|
|
66
|
+
padding: '0.5rem 1rem',
|
|
67
|
+
background: reducedMotion ? '#666' : '#007cba',
|
|
68
|
+
color: 'white',
|
|
69
|
+
border: 'none',
|
|
70
|
+
borderRadius: '4px'
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
Toggle Reduced Motion
|
|
74
|
+
</button>
|
|
75
|
+
|
|
76
|
+
<button
|
|
77
|
+
onClick={() => setLargeText(!largeText)}
|
|
78
|
+
style={{
|
|
79
|
+
padding: '0.5rem 1rem',
|
|
80
|
+
background: largeText ? '#0073aa' : '#007cba',
|
|
81
|
+
color: 'white',
|
|
82
|
+
border: 'none',
|
|
83
|
+
borderRadius: '4px',
|
|
84
|
+
fontSize: largeText ? '1.2em' : '1em'
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
Toggle Large Text
|
|
88
|
+
</button>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
{/* Announcements */}
|
|
93
|
+
<div style={{ marginBottom: '2rem' }}>
|
|
94
|
+
<h3>Screen Reader Announcements</h3>
|
|
95
|
+
<div style={{ marginBottom: '1rem' }}>
|
|
96
|
+
<input
|
|
97
|
+
type="text"
|
|
98
|
+
value={message}
|
|
99
|
+
onChange={(e) => setMessage(e.target.value)}
|
|
100
|
+
placeholder="Type a message to announce..."
|
|
101
|
+
style={{
|
|
102
|
+
padding: '0.5rem',
|
|
103
|
+
marginRight: '0.5rem',
|
|
104
|
+
width: '300px',
|
|
105
|
+
border: '1px solid #ccc',
|
|
106
|
+
borderRadius: '4px'
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
|
111
|
+
<button
|
|
112
|
+
onClick={() => announcePolite(message || 'Polite announcement test')}
|
|
113
|
+
style={{
|
|
114
|
+
padding: '0.5rem 1rem',
|
|
115
|
+
background: '#28a745',
|
|
116
|
+
color: 'white',
|
|
117
|
+
border: 'none',
|
|
118
|
+
borderRadius: '4px'
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
Announce Polite
|
|
122
|
+
</button>
|
|
123
|
+
|
|
124
|
+
<button
|
|
125
|
+
onClick={() => announceAssertive(message || 'Assertive announcement test')}
|
|
126
|
+
style={{
|
|
127
|
+
padding: '0.5rem 1rem',
|
|
128
|
+
background: '#dc3545',
|
|
129
|
+
color: 'white',
|
|
130
|
+
border: 'none',
|
|
131
|
+
borderRadius: '4px'
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
Announce Assertive
|
|
135
|
+
</button>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
{/* Audit Controls */}
|
|
140
|
+
<div style={{ marginBottom: '2rem' }}>
|
|
141
|
+
<h3>Accessibility Audit</h3>
|
|
142
|
+
<div style={{ display: 'flex', gap: '0.5rem', marginBottom: '1rem' }}>
|
|
143
|
+
<button
|
|
144
|
+
onClick={runAudit}
|
|
145
|
+
style={{
|
|
146
|
+
padding: '0.5rem 1rem',
|
|
147
|
+
background: '#6f42c1',
|
|
148
|
+
color: 'white',
|
|
149
|
+
border: 'none',
|
|
150
|
+
borderRadius: '4px'
|
|
151
|
+
}}
|
|
152
|
+
>
|
|
153
|
+
Run Audit
|
|
154
|
+
</button>
|
|
155
|
+
|
|
156
|
+
<button
|
|
157
|
+
onClick={clearIssues}
|
|
158
|
+
style={{
|
|
159
|
+
padding: '0.5rem 1rem',
|
|
160
|
+
background: '#6c757d',
|
|
161
|
+
color: 'white',
|
|
162
|
+
border: 'none',
|
|
163
|
+
borderRadius: '4px'
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
Clear Issues
|
|
167
|
+
</button>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
{issues.length > 0 && (
|
|
171
|
+
<div style={{
|
|
172
|
+
padding: '1rem',
|
|
173
|
+
background: '#fff3cd',
|
|
174
|
+
border: '1px solid #ffeaa7',
|
|
175
|
+
borderRadius: '8px'
|
|
176
|
+
}}>
|
|
177
|
+
<h4>Accessibility Issues ({issues.length})</h4>
|
|
178
|
+
<ul>
|
|
179
|
+
{issues.map((issue, index) => (
|
|
180
|
+
<li key={index} style={{ color: issue.level === 'error' ? '#dc3545' : '#856404' }}>
|
|
181
|
+
<strong>{issue.type}</strong>: {issue.message}
|
|
182
|
+
</li>
|
|
183
|
+
))}
|
|
184
|
+
</ul>
|
|
185
|
+
</div>
|
|
186
|
+
)}
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
{/* Test elements that might have accessibility issues */}
|
|
190
|
+
<div style={{ marginTop: '2rem', padding: '1rem', background: '#e9ecef', borderRadius: '8px' }}>
|
|
191
|
+
<h3>Test Elements for Audit</h3>
|
|
192
|
+
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='50'%3E%3Crect width='100' height='50' fill='%23ddd'/%3E%3C/svg%3E" />
|
|
193
|
+
<button>Unlabeled Button</button>
|
|
194
|
+
<input type="text" placeholder="Unlabeled Input" />
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const meta: Meta<typeof AccessibilityProvider> = {
|
|
201
|
+
title: 'Framework/AccessibilityProvider',
|
|
202
|
+
component: AccessibilityProvider,
|
|
203
|
+
parameters: {
|
|
204
|
+
layout: 'fullscreen',
|
|
205
|
+
docs: {
|
|
206
|
+
description: {
|
|
207
|
+
component: 'Provides comprehensive accessibility context and utilities including system preference detection, keyboard navigation, ARIA announcements, and accessibility auditing.',
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
tags: ['autodocs'],
|
|
212
|
+
argTypes: {
|
|
213
|
+
enableAudit: {
|
|
214
|
+
description: 'Whether to enable automatic accessibility auditing (defaults to development mode)',
|
|
215
|
+
control: 'boolean',
|
|
216
|
+
},
|
|
217
|
+
children: {
|
|
218
|
+
description: 'Child components that will have access to accessibility context',
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export default meta;
|
|
224
|
+
type Story = StoryObj<typeof meta>;
|
|
225
|
+
|
|
226
|
+
// Default story with full demo
|
|
227
|
+
export const Default: Story = {
|
|
228
|
+
args: {
|
|
229
|
+
children: <AccessibilityDemo />,
|
|
230
|
+
enableAudit: true,
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// Story showing basic usage without audit
|
|
235
|
+
export const BasicUsage: Story = {
|
|
236
|
+
args: {
|
|
237
|
+
enableAudit: false,
|
|
238
|
+
children: (
|
|
239
|
+
<div style={{ padding: '2rem' }}>
|
|
240
|
+
<h2>Basic AccessibilityProvider Usage</h2>
|
|
241
|
+
<p>This provider automatically detects system preferences and manages keyboard navigation.</p>
|
|
242
|
+
<button>Try tabbing to this button</button>
|
|
243
|
+
<br /><br />
|
|
244
|
+
<input type="text" placeholder="Type here and use Tab key" />
|
|
245
|
+
</div>
|
|
246
|
+
),
|
|
247
|
+
},
|
|
248
|
+
parameters: {
|
|
249
|
+
docs: {
|
|
250
|
+
description: {
|
|
251
|
+
story: 'Basic usage of AccessibilityProvider without auditing enabled.',
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// Story demonstrating keyboard navigation
|
|
258
|
+
export const KeyboardNavigation: Story = {
|
|
259
|
+
args: {
|
|
260
|
+
children: (
|
|
261
|
+
<div style={{ padding: '2rem' }}>
|
|
262
|
+
<h2>Keyboard Navigation Test</h2>
|
|
263
|
+
<p>Press Tab to navigate between elements. When using keyboard, focus indicators will be enhanced.</p>
|
|
264
|
+
<button>Button 1</button>
|
|
265
|
+
<br /><br />
|
|
266
|
+
<input type="text" placeholder="Input field" />
|
|
267
|
+
<br /><br />
|
|
268
|
+
<select>
|
|
269
|
+
<option>Option 1</option>
|
|
270
|
+
<option>Option 2</option>
|
|
271
|
+
</select>
|
|
272
|
+
<br /><br />
|
|
273
|
+
<a href="#" onClick={(e) => e.preventDefault()}>Link element</a>
|
|
274
|
+
</div>
|
|
275
|
+
),
|
|
276
|
+
},
|
|
277
|
+
parameters: {
|
|
278
|
+
docs: {
|
|
279
|
+
description: {
|
|
280
|
+
story: 'Test keyboard navigation to see enhanced focus indicators when using Tab key.',
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
};
|