@abstraks-dev/ui-library 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/LICENSE +21 -0
- package/README.md +708 -0
- package/dist/__tests__/Anchor.test.js +145 -0
- package/dist/__tests__/ArrowRight.test.js +91 -0
- package/dist/__tests__/Avatar.test.js +123 -0
- package/dist/__tests__/Button.test.js +82 -0
- package/dist/__tests__/Card.test.js +198 -0
- package/dist/__tests__/CheckCircle.test.js +98 -0
- package/dist/__tests__/Checkbox.test.js +161 -0
- package/dist/__tests__/ChevronDown.test.js +73 -0
- package/dist/__tests__/Close.test.js +98 -0
- package/dist/__tests__/EditSquare.test.js +99 -0
- package/dist/__tests__/Error.test.js +74 -0
- package/dist/__tests__/Footer.test.js +66 -0
- package/dist/__tests__/Heading.test.js +227 -0
- package/dist/__tests__/Hero.test.js +74 -0
- package/dist/__tests__/Label.test.js +123 -0
- package/dist/__tests__/Loader.test.js +115 -0
- package/dist/__tests__/MenuHover.test.js +137 -0
- package/dist/__tests__/Paragraph.test.js +93 -0
- package/dist/__tests__/PlusCircle.test.js +99 -0
- package/dist/__tests__/Radio.test.js +153 -0
- package/dist/__tests__/Select.test.js +187 -0
- package/dist/__tests__/Tabs.test.js +162 -0
- package/dist/__tests__/TextArea.test.js +127 -0
- package/dist/__tests__/TextInput.test.js +181 -0
- package/dist/__tests__/Toggle.test.js +120 -0
- package/dist/__tests__/TrashX.test.js +99 -0
- package/dist/__tests__/useHeadingAccessibility.test.js +144 -0
- package/dist/components/Anchor.js +131 -0
- package/dist/components/Animation.js +129 -0
- package/dist/components/AnimationGroup.js +207 -0
- package/dist/components/AnimationToggle.js +216 -0
- package/dist/components/Avatar.js +153 -0
- package/dist/components/Button.js +218 -0
- package/dist/components/Card.js +222 -0
- package/dist/components/Checkbox.js +305 -0
- package/dist/components/Crud.js +564 -0
- package/dist/components/DragAndDrop.js +337 -0
- package/dist/components/Error.js +206 -0
- package/dist/components/Footer.js +99 -0
- package/dist/components/Form.js +412 -0
- package/dist/components/Header.js +372 -0
- package/dist/components/Heading.js +134 -0
- package/dist/components/Hero.js +181 -0
- package/dist/components/Label.js +256 -0
- package/dist/components/Loader.js +302 -0
- package/dist/components/MenuHover.js +114 -0
- package/dist/components/Paragraph.js +128 -0
- package/dist/components/Prompt.js +61 -0
- package/dist/components/Radio.js +254 -0
- package/dist/components/Select.js +422 -0
- package/dist/components/SideMenu.js +313 -0
- package/dist/components/Tabs.js +297 -0
- package/dist/components/TextArea.js +370 -0
- package/dist/components/TextInput.js +286 -0
- package/dist/components/Toggle.js +186 -0
- package/dist/components/crudFiles/CrudEditBase.js +150 -0
- package/dist/components/crudFiles/CrudViewBase.js +39 -0
- package/dist/components/crudFiles/crudDevelopment.js +118 -0
- package/dist/components/crudFiles/crudEditHandlers.js +50 -0
- package/dist/constants/animation.js +30 -0
- package/dist/icons/ArrowIcon.js +32 -0
- package/dist/icons/ArrowRight.js +33 -0
- package/dist/icons/CheckCircle.js +33 -0
- package/dist/icons/ChevronDown.js +28 -0
- package/dist/icons/Close.js +33 -0
- package/dist/icons/EditSquare.js +33 -0
- package/dist/icons/Ellipses.js +34 -0
- package/dist/icons/Hamburger.js +39 -0
- package/dist/icons/LoadingSpinner.js +42 -0
- package/dist/icons/PlusCircle.js +33 -0
- package/dist/icons/SaveIcon.js +32 -0
- package/dist/icons/TrashX.js +33 -0
- package/dist/icons/__tests__/CheckCircle.test.js +9 -0
- package/dist/icons/__tests__/ChevronDown.test.js +9 -0
- package/dist/icons/__tests__/Close.test.js +9 -0
- package/dist/icons/__tests__/EditSquare.test.js +9 -0
- package/dist/icons/__tests__/PlusCircle.test.js +9 -0
- package/dist/icons/__tests__/TrashX.test.js +9 -0
- package/dist/icons/index.js +89 -0
- package/dist/index.js +332 -0
- package/dist/setupTests.js +3 -0
- package/dist/styles/_variables.scss +286 -0
- package/dist/styles/anchor.scss +40 -0
- package/dist/styles/animation-accessibility.scss +96 -0
- package/dist/styles/animation-toggle.scss +233 -0
- package/dist/styles/animation.scss +3781 -0
- package/dist/styles/avatar.scss +285 -0
- package/dist/styles/button.scss +430 -0
- package/dist/styles/card.scss +210 -0
- package/dist/styles/checkbox.scss +160 -0
- package/dist/styles/crud.scss +474 -0
- package/dist/styles/dragAndDrop.scss +312 -0
- package/dist/styles/error.scss +232 -0
- package/dist/styles/footer.scss +58 -0
- package/dist/styles/form.scss +420 -0
- package/dist/styles/grid.scss +29 -0
- package/dist/styles/header.scss +276 -0
- package/dist/styles/heading.scss +118 -0
- package/dist/styles/hero.scss +185 -0
- package/dist/styles/htmlElements.scss +20 -0
- package/dist/styles/image.scss +9 -0
- package/dist/styles/label.scss +340 -0
- package/dist/styles/list-item.scss +5 -0
- package/dist/styles/loader.scss +354 -0
- package/dist/styles/logo.scss +19 -0
- package/dist/styles/main.css +9056 -0
- package/dist/styles/main.css.map +1 -0
- package/dist/styles/main.scss +0 -0
- package/dist/styles/menu-hover.scss +30 -0
- package/dist/styles/paragraph.scss +88 -0
- package/dist/styles/prompt.scss +51 -0
- package/dist/styles/radio.scss +202 -0
- package/dist/styles/select.scss +363 -0
- package/dist/styles/side-menu.scss +334 -0
- package/dist/styles/tabs.scss +540 -0
- package/dist/styles/text-area.scss +388 -0
- package/dist/styles/text-input.scss +171 -0
- package/dist/styles/toggle.scss +0 -0
- package/dist/styles/unordered-list.scss +8 -0
- package/dist/utils/ScrollHandler.js +30 -0
- package/dist/utils/accessibility.js +128 -0
- package/dist/utils/heroUtils.js +316 -0
- package/dist/utils/index.js +104 -0
- package/dist/utils/inputValidation.js +29 -0
- package/dist/utils/keyboardNavigation.js +536 -0
- package/dist/utils/labelUtils.js +708 -0
- package/dist/utils/loaderUtils.js +387 -0
- package/dist/utils/menuUtils.js +575 -0
- package/dist/utils/useHeadingAccessibility.js +298 -0
- package/dist/utils/useRadioGroup.js +260 -0
- package/dist/utils/useSelectAccessibility.js +426 -0
- package/dist/utils/useTabsAccessibility.js +278 -0
- package/dist/utils/useTextAreaAccessibility.js +255 -0
- package/dist/utils/useTextInputAccessibility.js +295 -0
- package/dist/utils/useTypographyAccessibility.js +168 -0
- package/dist/utils/useWindowSize.js +32 -0
- package/dist/utils/utils/ScrollHandler.js +26 -0
- package/dist/utils/utils/accessibility.js +133 -0
- package/dist/utils/utils/heroUtils.js +348 -0
- package/dist/utils/utils/index.js +9 -0
- package/dist/utils/utils/inputValidation.js +22 -0
- package/dist/utils/utils/keyboardNavigation.js +664 -0
- package/dist/utils/utils/labelUtils.js +772 -0
- package/dist/utils/utils/loaderUtils.js +436 -0
- package/dist/utils/utils/menuUtils.js +651 -0
- package/dist/utils/utils/useHeadingAccessibility.js +334 -0
- package/dist/utils/utils/useRadioGroup.js +311 -0
- package/dist/utils/utils/useSelectAccessibility.js +498 -0
- package/dist/utils/utils/useTabsAccessibility.js +316 -0
- package/dist/utils/utils/useTextAreaAccessibility.js +303 -0
- package/dist/utils/utils/useTextInputAccessibility.js +338 -0
- package/dist/utils/utils/useTypographyAccessibility.js +180 -0
- package/dist/utils/utils/useWindowSize.js +26 -0
- package/dist/utils/utils/validation.js +131 -0
- package/dist/utils/validation.js +139 -0
- package/package.json +90 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.Label = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _propTypes = require("prop-types");
|
|
9
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
10
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
11
|
+
/**
|
|
12
|
+
* Label - Accessible form label component with validation and help text support
|
|
13
|
+
*
|
|
14
|
+
* Features:
|
|
15
|
+
* - Semantic HTML label element with proper form association
|
|
16
|
+
* - Required field indicators with multiple display options
|
|
17
|
+
* - Validation states with contextual messaging
|
|
18
|
+
* - Help text support for user guidance
|
|
19
|
+
* - Multiple size and weight variants
|
|
20
|
+
* - Full ARIA support for screen readers
|
|
21
|
+
* - Error, success, and warning state handling
|
|
22
|
+
*
|
|
23
|
+
* Accessibility:
|
|
24
|
+
* - Uses semantic <label> element with htmlFor association
|
|
25
|
+
* - Proper ARIA attributes for form control relationships
|
|
26
|
+
* - Screen reader announcements for validation states
|
|
27
|
+
* - Required field indicators with semantic meaning
|
|
28
|
+
* - Help text and error messages properly associated
|
|
29
|
+
* - Focus management and keyboard navigation support
|
|
30
|
+
*
|
|
31
|
+
* @component
|
|
32
|
+
* @example
|
|
33
|
+
* // Basic form label
|
|
34
|
+
* <Label labelFor="email" labelText="Email Address">
|
|
35
|
+
* <input id="email" type="email" />
|
|
36
|
+
* </Label>
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // Required field with validation
|
|
40
|
+
* <Label
|
|
41
|
+
* labelFor="password"
|
|
42
|
+
* labelText="Password"
|
|
43
|
+
* required={true}
|
|
44
|
+
* validationState="error"
|
|
45
|
+
* errorText="Password must be at least 8 characters"
|
|
46
|
+
* >
|
|
47
|
+
* <input id="password" type="password" />
|
|
48
|
+
* </Label>
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // With help text and custom styling
|
|
52
|
+
* <Label
|
|
53
|
+
* labelFor="username"
|
|
54
|
+
* labelText="Username"
|
|
55
|
+
* helpText="Choose a unique username"
|
|
56
|
+
* size="large"
|
|
57
|
+
* weight="bold"
|
|
58
|
+
* className="custom-label"
|
|
59
|
+
* >
|
|
60
|
+
* <input id="username" type="text" />
|
|
61
|
+
* </Label>
|
|
62
|
+
*/
|
|
63
|
+
const Label = exports.Label = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
64
|
+
// Core props
|
|
65
|
+
labelFor = '',
|
|
66
|
+
labelText = '',
|
|
67
|
+
children = null,
|
|
68
|
+
required = false,
|
|
69
|
+
// Layout props
|
|
70
|
+
className = '',
|
|
71
|
+
size = 'medium',
|
|
72
|
+
weight = 'medium',
|
|
73
|
+
disabled = false,
|
|
74
|
+
// Validation props
|
|
75
|
+
validationState = 'default',
|
|
76
|
+
requiredIndicator = 'asterisk',
|
|
77
|
+
helpText = '',
|
|
78
|
+
errorText = '',
|
|
79
|
+
successText = '',
|
|
80
|
+
// Accessibility props
|
|
81
|
+
ariaDescribedBy = '',
|
|
82
|
+
// Legacy props (for backward compatibility - internal use only)
|
|
83
|
+
additionalClassName = '',
|
|
84
|
+
componentName = 'label',
|
|
85
|
+
testId = 'label',
|
|
86
|
+
...restProps
|
|
87
|
+
}, ref) => {
|
|
88
|
+
// Handle legacy prop mapping
|
|
89
|
+
const finalClassName = className || additionalClassName;
|
|
90
|
+
|
|
91
|
+
// Generate unique IDs for accessibility
|
|
92
|
+
const helpId = helpText ? `${testId}-help` : '';
|
|
93
|
+
const errorId = errorText ? `${testId}-error` : '';
|
|
94
|
+
const successId = successText ? `${testId}-success` : '';
|
|
95
|
+
|
|
96
|
+
// Build aria-describedby attribute
|
|
97
|
+
const describedBy = [ariaDescribedBy, helpId, errorId, successId].filter(Boolean).join(' ');
|
|
98
|
+
|
|
99
|
+
// Build CSS classes
|
|
100
|
+
const labelClasses = [componentName, finalClassName, `${componentName}--${size}`, `${componentName}--${weight}`, validationState !== 'default' && `${componentName}--${validationState}`, disabled && `${componentName}--disabled`, required && `${componentName}--required`].filter(Boolean).join(' ');
|
|
101
|
+
|
|
102
|
+
// Required indicator rendering
|
|
103
|
+
const renderRequiredIndicator = () => {
|
|
104
|
+
if (!required) return null;
|
|
105
|
+
switch (requiredIndicator) {
|
|
106
|
+
case 'asterisk':
|
|
107
|
+
return /*#__PURE__*/_react.default.createElement("span", {
|
|
108
|
+
className: "required",
|
|
109
|
+
"aria-label": "required"
|
|
110
|
+
}, "*");
|
|
111
|
+
case 'text':
|
|
112
|
+
return /*#__PURE__*/_react.default.createElement("span", {
|
|
113
|
+
className: "required-text"
|
|
114
|
+
}, "(required)");
|
|
115
|
+
case 'none':
|
|
116
|
+
return null;
|
|
117
|
+
default:
|
|
118
|
+
return /*#__PURE__*/_react.default.createElement("span", {
|
|
119
|
+
className: `${componentName}__required`,
|
|
120
|
+
"aria-label": "required"
|
|
121
|
+
}, "*");
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Validation message rendering
|
|
126
|
+
const renderValidationMessage = () => {
|
|
127
|
+
if (validationState === 'error' && errorText) {
|
|
128
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
129
|
+
id: errorId,
|
|
130
|
+
className: `${componentName}__message ${componentName}__message--error`,
|
|
131
|
+
role: "alert",
|
|
132
|
+
"aria-live": "polite"
|
|
133
|
+
}, errorText);
|
|
134
|
+
}
|
|
135
|
+
if (validationState === 'success' && successText) {
|
|
136
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
137
|
+
id: successId,
|
|
138
|
+
className: `${componentName}__message ${componentName}__message--success`,
|
|
139
|
+
role: "status",
|
|
140
|
+
"aria-live": "polite"
|
|
141
|
+
}, successText);
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// Help text rendering
|
|
147
|
+
const renderHelpText = () => {
|
|
148
|
+
if (!helpText) return null;
|
|
149
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
150
|
+
id: helpId,
|
|
151
|
+
className: `${componentName}__help`
|
|
152
|
+
}, helpText);
|
|
153
|
+
};
|
|
154
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
155
|
+
className: `${componentName}-wrapper`,
|
|
156
|
+
"data-testid": testId
|
|
157
|
+
}, /*#__PURE__*/_react.default.createElement("label", _extends({
|
|
158
|
+
ref: ref,
|
|
159
|
+
className: labelClasses,
|
|
160
|
+
htmlFor: labelFor,
|
|
161
|
+
"aria-describedby": describedBy || undefined
|
|
162
|
+
}, restProps), /*#__PURE__*/_react.default.createElement("span", {
|
|
163
|
+
className: "label-text"
|
|
164
|
+
}, labelText && labelText, required && renderRequiredIndicator())), renderHelpText(), children, renderValidationMessage());
|
|
165
|
+
});
|
|
166
|
+
Label.propTypes = {
|
|
167
|
+
// ===========================================
|
|
168
|
+
// Core Props
|
|
169
|
+
// ===========================================
|
|
170
|
+
/**
|
|
171
|
+
* ID of the form control this label is associated with (required for accessibility)
|
|
172
|
+
*/
|
|
173
|
+
labelFor: _propTypes.string,
|
|
174
|
+
/**
|
|
175
|
+
* The text content of the label (required)
|
|
176
|
+
*/
|
|
177
|
+
labelText: _propTypes.string,
|
|
178
|
+
// ===========================================
|
|
179
|
+
// Content Props
|
|
180
|
+
// ===========================================
|
|
181
|
+
/**
|
|
182
|
+
* Form control element(s) to be labeled
|
|
183
|
+
*/
|
|
184
|
+
children: _propTypes.node,
|
|
185
|
+
// ===========================================
|
|
186
|
+
// Appearance Props
|
|
187
|
+
// ===========================================
|
|
188
|
+
/**
|
|
189
|
+
* Additional CSS classes to apply to the label wrapper
|
|
190
|
+
*/
|
|
191
|
+
className: _propTypes.string,
|
|
192
|
+
/**
|
|
193
|
+
* Size variant of the label
|
|
194
|
+
*/
|
|
195
|
+
size: (0, _propTypes.oneOf)(['small', 'medium', 'large']),
|
|
196
|
+
/**
|
|
197
|
+
* Font weight of the label text
|
|
198
|
+
*/
|
|
199
|
+
weight: (0, _propTypes.oneOf)(['light', 'normal', 'medium', 'bold']),
|
|
200
|
+
// ===========================================
|
|
201
|
+
// Behavior Props
|
|
202
|
+
// ===========================================
|
|
203
|
+
/**
|
|
204
|
+
* Whether the associated form control is required
|
|
205
|
+
*/
|
|
206
|
+
required: _propTypes.bool,
|
|
207
|
+
/**
|
|
208
|
+
* Whether the associated form control is disabled
|
|
209
|
+
*/
|
|
210
|
+
disabled: _propTypes.bool,
|
|
211
|
+
// ===========================================
|
|
212
|
+
// Validation Props
|
|
213
|
+
// ===========================================
|
|
214
|
+
/**
|
|
215
|
+
* Validation state of the associated form control
|
|
216
|
+
*/
|
|
217
|
+
validationState: (0, _propTypes.oneOf)(['default', 'success', 'warning', 'error']),
|
|
218
|
+
/**
|
|
219
|
+
* How to display the required indicator
|
|
220
|
+
*/
|
|
221
|
+
requiredIndicator: (0, _propTypes.oneOf)(['asterisk', 'text', 'none']),
|
|
222
|
+
/**
|
|
223
|
+
* Helpful text to display below the label
|
|
224
|
+
*/
|
|
225
|
+
helpText: _propTypes.string,
|
|
226
|
+
/**
|
|
227
|
+
* Error message to display when validationState is 'error'
|
|
228
|
+
*/
|
|
229
|
+
errorText: _propTypes.string,
|
|
230
|
+
/**
|
|
231
|
+
* Success message to display when validationState is 'success'
|
|
232
|
+
*/
|
|
233
|
+
successText: _propTypes.string,
|
|
234
|
+
// ===========================================
|
|
235
|
+
// Accessibility Props
|
|
236
|
+
// ===========================================
|
|
237
|
+
/**
|
|
238
|
+
* Space-separated list of element IDs that describe the label
|
|
239
|
+
*/
|
|
240
|
+
ariaDescribedBy: _propTypes.string,
|
|
241
|
+
/**
|
|
242
|
+
* Use className instead
|
|
243
|
+
*/
|
|
244
|
+
additionalClassName: _propTypes.string,
|
|
245
|
+
/**
|
|
246
|
+
* Use labelFor/id for identification
|
|
247
|
+
* Base CSS class name for the component
|
|
248
|
+
*/
|
|
249
|
+
componentName: _propTypes.string,
|
|
250
|
+
/**
|
|
251
|
+
* Test ID for testing and automation
|
|
252
|
+
*/
|
|
253
|
+
testId: _propTypes.string
|
|
254
|
+
};
|
|
255
|
+
Label.displayName = 'Label';
|
|
256
|
+
var _default = exports.default = Label;
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.Loader = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _Animation = _interopRequireDefault(require("./Animation"));
|
|
9
|
+
var _propTypes = require("prop-types");
|
|
10
|
+
var _Heading = _interopRequireDefault(require("./Heading"));
|
|
11
|
+
var _icons = require("../icons");
|
|
12
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
14
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
15
|
+
/**
|
|
16
|
+
* Loader - Accessible loading component for full-page or section loading states
|
|
17
|
+
*
|
|
18
|
+
* Features:
|
|
19
|
+
* - Multiple size variants with responsive design (sm, md, lg, xl)
|
|
20
|
+
* - Multiple animation variants (svg, spinner, dots, pulse)
|
|
21
|
+
* - Optional heading text with customizable size
|
|
22
|
+
* - Comprehensive ARIA labeling and screen reader support
|
|
23
|
+
* - Respects prefers-reduced-motion for accessibility
|
|
24
|
+
* - High contrast mode support
|
|
25
|
+
* - Keyboard navigation friendly
|
|
26
|
+
* - Semantic HTML structure with proper loading states
|
|
27
|
+
*
|
|
28
|
+
* Accessibility:
|
|
29
|
+
* - Uses proper ARIA roles (status) and labels
|
|
30
|
+
* - Announces loading state to screen readers with aria-live
|
|
31
|
+
* - Respects user motion preferences
|
|
32
|
+
* - High contrast mode compatible
|
|
33
|
+
* - Focus management ready
|
|
34
|
+
* - Screen reader friendly announcements
|
|
35
|
+
*
|
|
36
|
+
* @component
|
|
37
|
+
* @example
|
|
38
|
+
* // Basic usage
|
|
39
|
+
* <Loader />
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* // With custom text and variant
|
|
43
|
+
* <Loader
|
|
44
|
+
* size="lg"
|
|
45
|
+
* variant="pulse"
|
|
46
|
+
* headerText="Loading your dashboard..."
|
|
47
|
+
* aria-label="Dashboard loading in progress"
|
|
48
|
+
* />
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // Minimal loader without header
|
|
52
|
+
* <Loader
|
|
53
|
+
* size="sm"
|
|
54
|
+
* showHeader={false}
|
|
55
|
+
* variant="dots"
|
|
56
|
+
* className="custom-loader"
|
|
57
|
+
* />
|
|
58
|
+
*/
|
|
59
|
+
// Helper functions moved outside for clarity and performance
|
|
60
|
+
const sizeMap = {
|
|
61
|
+
sm: {
|
|
62
|
+
container: '150px',
|
|
63
|
+
spinner: 24,
|
|
64
|
+
dots: 6
|
|
65
|
+
},
|
|
66
|
+
md: {
|
|
67
|
+
container: '200px',
|
|
68
|
+
spinner: 32,
|
|
69
|
+
dots: 8
|
|
70
|
+
},
|
|
71
|
+
lg: {
|
|
72
|
+
container: '250px',
|
|
73
|
+
spinner: 40,
|
|
74
|
+
dots: 10
|
|
75
|
+
},
|
|
76
|
+
xl: {
|
|
77
|
+
container: '300px',
|
|
78
|
+
spinner: 48,
|
|
79
|
+
dots: 12
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
function getClassNames(componentName, size, variant, finalClassName) {
|
|
83
|
+
const classes = [componentName, `${componentName}--${size}`, `${componentName}--${variant}`, finalClassName].filter(Boolean);
|
|
84
|
+
return classes.join(' ');
|
|
85
|
+
}
|
|
86
|
+
function renderDots(componentName, currentSize, fill) {
|
|
87
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
88
|
+
className: `${componentName}__dots`,
|
|
89
|
+
"aria-hidden": "true"
|
|
90
|
+
}, [...Array(3)].map((_, index) => /*#__PURE__*/_react.default.createElement("div", {
|
|
91
|
+
key: index,
|
|
92
|
+
className: `${componentName}__dot`,
|
|
93
|
+
style: {
|
|
94
|
+
width: currentSize.dots,
|
|
95
|
+
height: currentSize.dots,
|
|
96
|
+
backgroundColor: fill,
|
|
97
|
+
animationDelay: `${index * 0.2}s`
|
|
98
|
+
}
|
|
99
|
+
})));
|
|
100
|
+
}
|
|
101
|
+
function renderPulse(componentName, currentSize, fill) {
|
|
102
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
103
|
+
className: `${componentName}__pulse`,
|
|
104
|
+
style: {
|
|
105
|
+
width: currentSize.container,
|
|
106
|
+
height: currentSize.container,
|
|
107
|
+
backgroundColor: fill
|
|
108
|
+
},
|
|
109
|
+
"aria-hidden": "true"
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function renderSpinner(variant, componentName, currentSize, fill) {
|
|
113
|
+
switch (variant) {
|
|
114
|
+
case 'spinner':
|
|
115
|
+
return /*#__PURE__*/_react.default.createElement(_icons.LoadingSpinner, {
|
|
116
|
+
componentName: `${componentName}__spinner`,
|
|
117
|
+
dimensions: currentSize.spinner,
|
|
118
|
+
fill: fill
|
|
119
|
+
});
|
|
120
|
+
case 'dots':
|
|
121
|
+
return renderDots(componentName, currentSize, fill);
|
|
122
|
+
case 'pulse':
|
|
123
|
+
return renderPulse(componentName, currentSize, fill);
|
|
124
|
+
case 'svg':
|
|
125
|
+
default:
|
|
126
|
+
// Default SVG animation variant
|
|
127
|
+
return /*#__PURE__*/_react.default.createElement("svg", {
|
|
128
|
+
className: `${componentName}__svg`,
|
|
129
|
+
version: "1.1",
|
|
130
|
+
x: "0px",
|
|
131
|
+
y: "0px",
|
|
132
|
+
viewBox: "0 0 100 100",
|
|
133
|
+
enableBackground: "new 0 0 0 0",
|
|
134
|
+
width: currentSize.container,
|
|
135
|
+
height: currentSize.container,
|
|
136
|
+
"aria-hidden": "true",
|
|
137
|
+
role: "img",
|
|
138
|
+
focusable: "false"
|
|
139
|
+
}, /*#__PURE__*/_react.default.createElement("path", {
|
|
140
|
+
className: `${componentName}__path`,
|
|
141
|
+
fill: fill,
|
|
142
|
+
d: "M73,50c0-12.7-10.3-23-23-23S27,37.3,27,50 M30.9,50c0-10.5,8.5-19.1,19.1-19.1S69.1,39.5,69.1,50"
|
|
143
|
+
}, /*#__PURE__*/_react.default.createElement("animateTransform", {
|
|
144
|
+
attributeName: "transform",
|
|
145
|
+
attributeType: "XML",
|
|
146
|
+
type: "rotate",
|
|
147
|
+
dur: "1s",
|
|
148
|
+
from: "0 50 50",
|
|
149
|
+
to: "360 50 50",
|
|
150
|
+
repeatCount: "indefinite"
|
|
151
|
+
})));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const Loader = exports.Loader = /*#__PURE__*/(0, _react.forwardRef)(function Loader({
|
|
155
|
+
headerText,
|
|
156
|
+
children,
|
|
157
|
+
className,
|
|
158
|
+
size = 'md',
|
|
159
|
+
variant = 'svg',
|
|
160
|
+
fill = '#333',
|
|
161
|
+
showHeader = true,
|
|
162
|
+
titleSize = 3,
|
|
163
|
+
ariaLabel,
|
|
164
|
+
additionalClassName,
|
|
165
|
+
componentName = 'ui-loader',
|
|
166
|
+
testId,
|
|
167
|
+
...props
|
|
168
|
+
}, ref) {
|
|
169
|
+
// Remove svgSize from props before spreading onto the root div
|
|
170
|
+
const {
|
|
171
|
+
svgSize,
|
|
172
|
+
...restProps
|
|
173
|
+
} = props;
|
|
174
|
+
const finalClassName = className || additionalClassName;
|
|
175
|
+
const currentSize = sizeMap[size] || sizeMap.md;
|
|
176
|
+
|
|
177
|
+
// Always show a heading, defaulting to 'Loading...' if headerText is not provided
|
|
178
|
+
const headingText = showHeader ? headerText || 'Loading...' : null;
|
|
179
|
+
function renderSpinnerWithSvgSize(variant, componentName, currentSize, fill, svgSize) {
|
|
180
|
+
if (variant === 'svg' && svgSize) {
|
|
181
|
+
return /*#__PURE__*/_react.default.createElement("svg", {
|
|
182
|
+
className: `${componentName}__svg`,
|
|
183
|
+
version: "1.1",
|
|
184
|
+
x: "0px",
|
|
185
|
+
y: "0px",
|
|
186
|
+
viewBox: "0 0 100 100",
|
|
187
|
+
enableBackground: "new 0 0 0 0",
|
|
188
|
+
width: svgSize,
|
|
189
|
+
height: svgSize,
|
|
190
|
+
"aria-hidden": "true",
|
|
191
|
+
role: "img",
|
|
192
|
+
focusable: "false"
|
|
193
|
+
}, /*#__PURE__*/_react.default.createElement("path", {
|
|
194
|
+
className: `${componentName}__path`,
|
|
195
|
+
fill: fill,
|
|
196
|
+
d: "M73,50c0-12.7-10.3-23-23-23S27,37.3,27,50 M30.9,50c0-10.5,8.5-19.1,19.1-19.1S69.1,39.5,69.1,50"
|
|
197
|
+
}, /*#__PURE__*/_react.default.createElement("animateTransform", {
|
|
198
|
+
attributeName: "transform",
|
|
199
|
+
attributeType: "XML",
|
|
200
|
+
type: "rotate",
|
|
201
|
+
dur: "1s",
|
|
202
|
+
from: "0 50 50",
|
|
203
|
+
to: "360 50 50",
|
|
204
|
+
repeatCount: "indefinite"
|
|
205
|
+
})));
|
|
206
|
+
}
|
|
207
|
+
return renderSpinner(variant, componentName, currentSize, fill);
|
|
208
|
+
}
|
|
209
|
+
const loaderContent = /*#__PURE__*/_react.default.createElement("div", _extends({
|
|
210
|
+
ref: ref,
|
|
211
|
+
className: getClassNames(componentName, size, variant, finalClassName),
|
|
212
|
+
"data-testid": testId || componentName,
|
|
213
|
+
role: "status",
|
|
214
|
+
"aria-label": ariaLabel || headingText,
|
|
215
|
+
"aria-live": "polite",
|
|
216
|
+
"aria-busy": "true"
|
|
217
|
+
}, restProps), showHeader && /*#__PURE__*/_react.default.createElement(_Heading.default, {
|
|
218
|
+
variant: titleSize,
|
|
219
|
+
componentName: `${componentName}__heading`,
|
|
220
|
+
id: `${componentName}-heading`
|
|
221
|
+
}, headingText), /*#__PURE__*/_react.default.createElement("div", {
|
|
222
|
+
className: `${componentName}__wrapper`,
|
|
223
|
+
"aria-describedby": showHeader ? `${componentName}-heading` : undefined
|
|
224
|
+
}, renderSpinnerWithSvgSize(variant, componentName, currentSize, fill, svgSize)), children && /*#__PURE__*/_react.default.createElement("div", {
|
|
225
|
+
className: `${componentName}__content`
|
|
226
|
+
}, children));
|
|
227
|
+
|
|
228
|
+
// Animation wrapper for entrance effect
|
|
229
|
+
return /*#__PURE__*/_react.default.createElement(_Animation.default, {
|
|
230
|
+
fadingEntrances: "fadeIn",
|
|
231
|
+
duration: "faster"
|
|
232
|
+
}, loaderContent);
|
|
233
|
+
});
|
|
234
|
+
Loader.propTypes = {
|
|
235
|
+
// ===========================================
|
|
236
|
+
// Core Props
|
|
237
|
+
// ===========================================
|
|
238
|
+
/**
|
|
239
|
+
* Header text to display above the loader
|
|
240
|
+
* @required Recommended for screen reader accessibility
|
|
241
|
+
*/
|
|
242
|
+
headerText: _propTypes.string,
|
|
243
|
+
// ===========================================
|
|
244
|
+
// Content Props
|
|
245
|
+
// ===========================================
|
|
246
|
+
/**
|
|
247
|
+
* Additional content to display below the loader
|
|
248
|
+
*/
|
|
249
|
+
children: _propTypes.node,
|
|
250
|
+
// ===========================================
|
|
251
|
+
// Appearance Props
|
|
252
|
+
// ===========================================
|
|
253
|
+
/**
|
|
254
|
+
* Additional CSS classes to apply to the loader wrapper
|
|
255
|
+
*/
|
|
256
|
+
className: _propTypes.string,
|
|
257
|
+
/**
|
|
258
|
+
* Size variant for the loader - affects overall dimensions and spacing
|
|
259
|
+
* @required Recommended to be explicit about sizing for consistent UX
|
|
260
|
+
*/
|
|
261
|
+
size: (0, _propTypes.oneOf)(['sm', 'md', 'lg', 'xl']),
|
|
262
|
+
/**
|
|
263
|
+
* Loader animation variant - different visual styles for various contexts
|
|
264
|
+
* @required Recommended for intentional animation choice
|
|
265
|
+
*/
|
|
266
|
+
variant: (0, _propTypes.oneOf)(['svg', 'spinner', 'dots', 'pulse']),
|
|
267
|
+
/**
|
|
268
|
+
* Fill color for the loader animations - should match design system
|
|
269
|
+
*/
|
|
270
|
+
fill: _propTypes.string,
|
|
271
|
+
/**
|
|
272
|
+
* Whether to show the header text - should be true for accessibility unless redundant
|
|
273
|
+
*/
|
|
274
|
+
showHeader: _propTypes.bool,
|
|
275
|
+
/**
|
|
276
|
+
* Heading size variant (1-6) - semantic heading level
|
|
277
|
+
*/
|
|
278
|
+
titleSize: (0, _propTypes.oneOf)([1, 2, 3, 4, 5, 6]),
|
|
279
|
+
// ===========================================
|
|
280
|
+
// Accessibility Props
|
|
281
|
+
// ===========================================
|
|
282
|
+
/**
|
|
283
|
+
* Accessible label for the loader - overrides headerText for ARIA
|
|
284
|
+
*/
|
|
285
|
+
// aria-label is used as a prop, not ariaLabel
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Use className instead
|
|
289
|
+
*/
|
|
290
|
+
additionalClassName: _propTypes.string,
|
|
291
|
+
/**
|
|
292
|
+
* Use className/id for identification
|
|
293
|
+
* Base CSS class name for the component
|
|
294
|
+
*/
|
|
295
|
+
componentName: _propTypes.string,
|
|
296
|
+
/**
|
|
297
|
+
* Custom test ID for automated testing
|
|
298
|
+
*/
|
|
299
|
+
testId: _propTypes.string
|
|
300
|
+
};
|
|
301
|
+
Loader.displayName = 'Loader';
|
|
302
|
+
var _default = exports.default = Loader;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.MenuHover = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _propTypes = require("prop-types");
|
|
9
|
+
var _uuid = require("uuid");
|
|
10
|
+
var _ChevronDown = require("../icons/ChevronDown");
|
|
11
|
+
var _ArrowRight = require("../icons/ArrowRight");
|
|
12
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
13
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
14
|
+
/**
|
|
15
|
+
* MenuHover Component
|
|
16
|
+
*
|
|
17
|
+
* A stateless toggle component that displays different content based on its open/closed state.
|
|
18
|
+
* Features keyboard navigation and accessibility support.
|
|
19
|
+
*
|
|
20
|
+
* @component
|
|
21
|
+
* @example
|
|
22
|
+
* ```jsx
|
|
23
|
+
* <MenuHover
|
|
24
|
+
* isOpen={isMenuOpen}
|
|
25
|
+
* onToggle={(isOpen) => setMenuOpen(isOpen)}
|
|
26
|
+
* isOpenChildren={<div>Open menu content</div>}
|
|
27
|
+
* isClosedChildren={<div>Closed menu content</div>}
|
|
28
|
+
* ariaLabel="Navigation menu"
|
|
29
|
+
* />
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @param {object} props - Component props
|
|
33
|
+
* @param {React.ReactNode} props.isOpenChildren - Content to display when menu is open
|
|
34
|
+
* @param {React.ReactNode} props.isClosedChildren - Content to display when menu is closed
|
|
35
|
+
* @param {boolean} props.isOpen - Whether the menu is currently open (controlled state)
|
|
36
|
+
* @param {function} props.onToggle - Callback when menu toggle is triggered
|
|
37
|
+
* @param {string} [props.className=''] - Additional CSS classes to apply
|
|
38
|
+
* @param {string} [props.id] - Unique identifier for the component
|
|
39
|
+
* @param {string} [props.ariaLabel] - Accessible label for the menu
|
|
40
|
+
* @returns {JSX.Element} The MenuHover component
|
|
41
|
+
*/
|
|
42
|
+
const MenuHover = exports.MenuHover = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
43
|
+
// Core props
|
|
44
|
+
id,
|
|
45
|
+
className = '',
|
|
46
|
+
// Content props
|
|
47
|
+
isOpenChildren,
|
|
48
|
+
isClosedChildren,
|
|
49
|
+
// Behavior props
|
|
50
|
+
isOpen = false,
|
|
51
|
+
// Event props
|
|
52
|
+
onToggle = () => {},
|
|
53
|
+
// Accessibility props
|
|
54
|
+
'aria-label': ariaLabel = 'Menu',
|
|
55
|
+
// Legacy props (for backward compatibility - internal use only)
|
|
56
|
+
componentName = 'menu-hover',
|
|
57
|
+
additionalClassName = '',
|
|
58
|
+
...restProps
|
|
59
|
+
}, ref) => {
|
|
60
|
+
// Handle legacy prop mapping
|
|
61
|
+
const finalId = id || `menu-hover-${Math.random().toString(36).substr(2, 9)}`;
|
|
62
|
+
const finalClassName = className || additionalClassName;
|
|
63
|
+
|
|
64
|
+
// Handle click and keyboard interactions
|
|
65
|
+
const handleToggle = () => {
|
|
66
|
+
onToggle(!isOpen);
|
|
67
|
+
};
|
|
68
|
+
const handleKeyDown = event => {
|
|
69
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
handleToggle();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
return /*#__PURE__*/_react.default.createElement("div", _extends({
|
|
75
|
+
ref: ref,
|
|
76
|
+
id: finalId,
|
|
77
|
+
className: `${componentName} ${isOpen ? 'open' : ''} ${finalClassName}`,
|
|
78
|
+
"data-testid": componentName,
|
|
79
|
+
role: "button",
|
|
80
|
+
tabIndex: 0,
|
|
81
|
+
"aria-label": ariaLabel,
|
|
82
|
+
"aria-expanded": isOpen,
|
|
83
|
+
onClick: handleToggle,
|
|
84
|
+
onKeyDown: handleKeyDown
|
|
85
|
+
}, restProps), isOpen ? isOpenChildren : isClosedChildren);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Configure component PropTypes
|
|
89
|
+
MenuHover.propTypes = {
|
|
90
|
+
// Core props
|
|
91
|
+
/** Unique identifier for the component */
|
|
92
|
+
id: _propTypes.string,
|
|
93
|
+
/** Additional CSS classes to apply */
|
|
94
|
+
className: _propTypes.string,
|
|
95
|
+
// Content props
|
|
96
|
+
/** Content to display when menu is open */
|
|
97
|
+
isOpenChildren: _propTypes.node,
|
|
98
|
+
/** Content to display when menu is closed */
|
|
99
|
+
isClosedChildren: _propTypes.node,
|
|
100
|
+
// Behavior props
|
|
101
|
+
/** Whether the menu is currently open (controlled state) */
|
|
102
|
+
isOpen: _propTypes.bool,
|
|
103
|
+
// Event props
|
|
104
|
+
/** Callback when menu toggle is triggered */
|
|
105
|
+
onToggle: _propTypes.func,
|
|
106
|
+
// Accessibility props
|
|
107
|
+
/** Accessible label for the menu */
|
|
108
|
+
'aria-label': _propTypes.string,
|
|
109
|
+
// Legacy props (for backward compatibility - internal use only)
|
|
110
|
+
/** Base component name for CSS classes and data attributes */
|
|
111
|
+
componentName: _propTypes.string,
|
|
112
|
+
/** Additional CSS class name for custom styling */
|
|
113
|
+
additionalClassName: _propTypes.string
|
|
114
|
+
};
|