@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,372 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Header Component
|
|
6
|
+
*
|
|
7
|
+
* A responsive navigation header component that adapts between desktop and mobile layouts.
|
|
8
|
+
* Features keyboard navigation, screen reader support, and ARIA compliance.
|
|
9
|
+
*
|
|
10
|
+
* @component
|
|
11
|
+
* @example
|
|
12
|
+
* ```jsx
|
|
13
|
+
* <Header
|
|
14
|
+
* logo={<Logo />}
|
|
15
|
+
* navigation={navigationItems}
|
|
16
|
+
* mapMenuItems={mobileMenuItems}
|
|
17
|
+
* hasAuth={true}
|
|
18
|
+
* isMenuOpen={false}
|
|
19
|
+
* isEllipsesOpen={false}
|
|
20
|
+
* onMenuToggle={(isOpen) => setMenuOpen(isOpen)}
|
|
21
|
+
* onEllipsesToggle={(isOpen) => setEllipsesOpen(isOpen)}
|
|
22
|
+
* ariaLabel="Main navigation"
|
|
23
|
+
* menuAriaLabel="Navigation menu"
|
|
24
|
+
* userMenuAriaLabel="User menu"
|
|
25
|
+
* avatarSrc="path/to/avatar.jpg"
|
|
26
|
+
* />
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @param {object} props - Component props
|
|
30
|
+
* @param {string} [props.componentName='header'] - Base component CSS class name
|
|
31
|
+
* @param {string} [props.additionalClassName=''] - Additional CSS classes to apply
|
|
32
|
+
* @param {React.ReactNode} props.logo - Logo element to display in header (required for branding)
|
|
33
|
+
* @param {React.ReactNode} props.navigation - Desktop navigation items (required for navigation)
|
|
34
|
+
* @param {React.ReactNode} props.mapMenuItems - Mobile navigation menu items (required for mobile UX)
|
|
35
|
+
* @param {boolean} [props.hasAuth=false] - Whether user is authenticated (affects UI layout)
|
|
36
|
+
* @param {React.ReactNode} [props.signInLink] - Sign in link component for unauthenticated users
|
|
37
|
+
* @param {React.ReactNode} [props.signUpLink] - Sign up link component for unauthenticated users
|
|
38
|
+
* @param {string} [props.avatarSrc] - User avatar image source for authenticated users
|
|
39
|
+
* @param {React.ReactNode[]} [props.ellipsesList=[]] - List of user menu items for dropdown
|
|
40
|
+
* @param {React.ReactNode} [props.search] - Search component for header search functionality
|
|
41
|
+
* @param {boolean} props.isMenuOpen - Whether mobile menu is currently open (controlled state, required)
|
|
42
|
+
* @param {boolean} props.isEllipsesOpen - Whether user dropdown menu is currently open (controlled state, required)
|
|
43
|
+
* @param {function} props.onMenuToggle - Callback when mobile menu toggle is triggered (required for state management)
|
|
44
|
+
* @param {function} props.onEllipsesToggle - Callback when user dropdown toggle is triggered (required for state management)
|
|
45
|
+
* @param {function} [props.onKeyDown] - Additional callback for custom keyboard event handling
|
|
46
|
+
* @param {string} props.ariaLabel - ARIA label for the header element (required for accessibility)
|
|
47
|
+
* @param {string} props.menuAriaLabel - ARIA label for the navigation menu (required for screen readers)
|
|
48
|
+
* @param {string} props.userMenuAriaLabel - ARIA label for the user menu (required for screen readers)
|
|
49
|
+
*/
|
|
50
|
+
'react';
|
|
51
|
+
|
|
52
|
+
Object.defineProperty(exports, "__esModule", {
|
|
53
|
+
value: true
|
|
54
|
+
});
|
|
55
|
+
exports.Header = void 0;
|
|
56
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
57
|
+
var _propTypes = require("prop-types");
|
|
58
|
+
var _Avatar = require("./Avatar");
|
|
59
|
+
var _SideMenu = require("./SideMenu");
|
|
60
|
+
var _Close = require("../icons/Close");
|
|
61
|
+
var _Hamburger = require("../icons/Hamburger");
|
|
62
|
+
var _useWindowSize = require("../utils/useWindowSize");
|
|
63
|
+
var _accessibility = require("../utils/accessibility");
|
|
64
|
+
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); }
|
|
65
|
+
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); } // components
|
|
66
|
+
// utils
|
|
67
|
+
/**
|
|
68
|
+
* Header Component
|
|
69
|
+
*
|
|
70
|
+
* A responsive navigation header component that adapts between desktop and mobile layouts.
|
|
71
|
+
* Features keyboard navigation, screen reader support, and ARIA compliance.
|
|
72
|
+
*
|
|
73
|
+
* @component
|
|
74
|
+
* @example
|
|
75
|
+
* ```jsx
|
|
76
|
+
* <Header
|
|
77
|
+
* logo={<Logo />}
|
|
78
|
+
* navigation={navigationItems}
|
|
79
|
+
* hasAuth={true}
|
|
80
|
+
* isMenuOpen={false}
|
|
81
|
+
* onMenuToggle={(isOpen) => setMenuOpen(isOpen)}
|
|
82
|
+
* onEllipsesToggle={(isOpen) => setEllipsesOpen(isOpen)}
|
|
83
|
+
* avatarSrc="path/to/avatar.jpg"
|
|
84
|
+
* />
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
const Header = exports.Header = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
88
|
+
// Core props
|
|
89
|
+
id,
|
|
90
|
+
className = '',
|
|
91
|
+
// Content props
|
|
92
|
+
logo = null,
|
|
93
|
+
navigation = null,
|
|
94
|
+
mapMenuItems = null,
|
|
95
|
+
signInLink = null,
|
|
96
|
+
signUpLink = null,
|
|
97
|
+
avatarSrc = null,
|
|
98
|
+
ellipsesList = [],
|
|
99
|
+
search = null,
|
|
100
|
+
// Behavior props
|
|
101
|
+
hasAuth = false,
|
|
102
|
+
isMenuOpen = false,
|
|
103
|
+
isEllipsesOpen = false,
|
|
104
|
+
// Event props
|
|
105
|
+
onMenuToggle = null,
|
|
106
|
+
onEllipsesToggle = null,
|
|
107
|
+
onKeyDown = null,
|
|
108
|
+
// Accessibility props
|
|
109
|
+
ariaLabel = 'Main navigation',
|
|
110
|
+
menuAriaLabel = 'Navigation menu',
|
|
111
|
+
userMenuAriaLabel = 'User menu',
|
|
112
|
+
// Legacy props (for backward compatibility - internal use only)
|
|
113
|
+
componentName = 'header',
|
|
114
|
+
additionalClassName = '',
|
|
115
|
+
...restProps
|
|
116
|
+
}, ref) => {
|
|
117
|
+
// Handle legacy prop mapping
|
|
118
|
+
const finalId = id || `header-${Math.random().toString(36).substr(2, 9)}`;
|
|
119
|
+
const finalClassName = className || additionalClassName;
|
|
120
|
+
const size = (0, _useWindowSize.useWindowSize)();
|
|
121
|
+
const isMobile = size.width < 768;
|
|
122
|
+
|
|
123
|
+
// Handle keyboard navigation
|
|
124
|
+
const handleKeyDown = event => {
|
|
125
|
+
const {
|
|
126
|
+
key,
|
|
127
|
+
target
|
|
128
|
+
} = event;
|
|
129
|
+
|
|
130
|
+
// Escape key closes any open menus
|
|
131
|
+
if (key === 'Escape') {
|
|
132
|
+
if (isMenuOpen && onMenuToggle) {
|
|
133
|
+
onMenuToggle(false);
|
|
134
|
+
(0, _accessibility.announceToScreenReader)('Menu closed');
|
|
135
|
+
}
|
|
136
|
+
if (isEllipsesOpen && onEllipsesToggle) {
|
|
137
|
+
onEllipsesToggle(false);
|
|
138
|
+
(0, _accessibility.announceToScreenReader)('User menu closed');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Tab key management for focus trapping in mobile menu
|
|
143
|
+
if (key === 'Tab' && isMenuOpen && isMobile) {
|
|
144
|
+
const focusableElements = target.closest('.side-menu')?.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
|
145
|
+
if (focusableElements && focusableElements.length > 0) {
|
|
146
|
+
const firstElement = focusableElements[0];
|
|
147
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
148
|
+
if (event.shiftKey && target === firstElement) {
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
(0, _accessibility.safeFocus)(lastElement);
|
|
151
|
+
} else if (!event.shiftKey && target === lastElement) {
|
|
152
|
+
event.preventDefault();
|
|
153
|
+
(0, _accessibility.safeFocus)(firstElement);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (onKeyDown) onKeyDown(event);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Hamburger and Close icons
|
|
161
|
+
const hamburgerIcon = /*#__PURE__*/_react.default.createElement(_Hamburger.Hamburger, null);
|
|
162
|
+
const closeIcon = /*#__PURE__*/_react.default.createElement(_Close.Close, null);
|
|
163
|
+
|
|
164
|
+
// Handlers for menu and ellipses
|
|
165
|
+
const handleMenuToggle = () => {
|
|
166
|
+
if (onMenuToggle) onMenuToggle(!isMenuOpen);
|
|
167
|
+
};
|
|
168
|
+
const handleEllipsesToggle = () => {
|
|
169
|
+
if (onEllipsesToggle) onEllipsesToggle(!isEllipsesOpen);
|
|
170
|
+
};
|
|
171
|
+
return /*#__PURE__*/_react.default.createElement("header", _extends({
|
|
172
|
+
ref: ref,
|
|
173
|
+
id: finalId,
|
|
174
|
+
className: `${componentName} ${finalClassName}`,
|
|
175
|
+
"data-testid": componentName,
|
|
176
|
+
role: "banner",
|
|
177
|
+
"aria-label": ariaLabel,
|
|
178
|
+
onKeyDown: handleKeyDown
|
|
179
|
+
}, restProps), /*#__PURE__*/_react.default.createElement("div", {
|
|
180
|
+
className: isMobile ? 'container-mobile' : 'container-large'
|
|
181
|
+
}, logo, search, isMobile ? /*#__PURE__*/_react.default.createElement(_SideMenu.SideMenu, {
|
|
182
|
+
direction: "vertical",
|
|
183
|
+
position: "top",
|
|
184
|
+
isOpen: isMenuOpen,
|
|
185
|
+
onToggle: handleMenuToggle,
|
|
186
|
+
onOpen: () => (0, _accessibility.announceToScreenReader)('Menu opened'),
|
|
187
|
+
onClose: () => (0, _accessibility.announceToScreenReader)('Menu closed'),
|
|
188
|
+
closeOnBackdrop: true,
|
|
189
|
+
closeOnEscape: true,
|
|
190
|
+
ariaLabel: menuAriaLabel,
|
|
191
|
+
trigger: /*#__PURE__*/_react.default.createElement("button", {
|
|
192
|
+
type: "button",
|
|
193
|
+
"aria-label": `${isMenuOpen ? 'Close' : 'Open'} ${menuAriaLabel.toLowerCase()}`,
|
|
194
|
+
"aria-expanded": isMenuOpen,
|
|
195
|
+
"aria-controls": "mobile-navigation",
|
|
196
|
+
onClick: handleMenuToggle,
|
|
197
|
+
className: "mobile-menu-trigger"
|
|
198
|
+
}, hamburgerIcon),
|
|
199
|
+
rightIconClose: /*#__PURE__*/_react.default.createElement("button", {
|
|
200
|
+
type: "button",
|
|
201
|
+
"aria-label": "Close menu",
|
|
202
|
+
onClick: handleMenuToggle,
|
|
203
|
+
className: "mobile-menu-close"
|
|
204
|
+
}, closeIcon),
|
|
205
|
+
menuItems: mapMenuItems,
|
|
206
|
+
navigation: navigation,
|
|
207
|
+
signInLink: signInLink,
|
|
208
|
+
signUpLink: signUpLink,
|
|
209
|
+
avatarSrc: avatarSrc,
|
|
210
|
+
ellipsesList: ellipsesList,
|
|
211
|
+
userMenuAriaLabel: userMenuAriaLabel,
|
|
212
|
+
isEllipsesOpen: isEllipsesOpen,
|
|
213
|
+
onEllipsesToggle: handleEllipsesToggle
|
|
214
|
+
}) : /*#__PURE__*/_react.default.createElement("nav", {
|
|
215
|
+
className: "navigation",
|
|
216
|
+
"data-testid": "navigation",
|
|
217
|
+
role: "navigation",
|
|
218
|
+
"aria-label": menuAriaLabel
|
|
219
|
+
}, /*#__PURE__*/_react.default.createElement("ul", {
|
|
220
|
+
className: "unordered-list",
|
|
221
|
+
role: "menubar",
|
|
222
|
+
"aria-orientation": "horizontal"
|
|
223
|
+
}, navigation), /*#__PURE__*/_react.default.createElement("ul", {
|
|
224
|
+
className: "unordered-list auth-menu",
|
|
225
|
+
role: "menubar",
|
|
226
|
+
"aria-label": "Authentication"
|
|
227
|
+
}, hasAuth ? /*#__PURE__*/_react.default.createElement("li", {
|
|
228
|
+
className: "list-item",
|
|
229
|
+
role: "none"
|
|
230
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
231
|
+
className: "head"
|
|
232
|
+
}, /*#__PURE__*/_react.default.createElement("button", {
|
|
233
|
+
type: "button",
|
|
234
|
+
className: "ellipses",
|
|
235
|
+
onClick: handleEllipsesToggle,
|
|
236
|
+
"aria-label": `${isEllipsesOpen ? 'Close' : 'Open'} ${userMenuAriaLabel.toLowerCase()}`,
|
|
237
|
+
"aria-expanded": isEllipsesOpen,
|
|
238
|
+
"aria-haspopup": "menu",
|
|
239
|
+
"aria-controls": "user-menu-dropdown",
|
|
240
|
+
id: "user-menu-button"
|
|
241
|
+
}, /*#__PURE__*/_react.default.createElement(_Avatar.Avatar, {
|
|
242
|
+
dimensions: "50px",
|
|
243
|
+
source: avatarSrc,
|
|
244
|
+
alt: "User avatar"
|
|
245
|
+
}), isEllipsesOpen && /*#__PURE__*/_react.default.createElement("div", {
|
|
246
|
+
className: "ellipses-content",
|
|
247
|
+
role: "menu",
|
|
248
|
+
"aria-labelledby": "user-menu-button",
|
|
249
|
+
id: "user-menu-dropdown"
|
|
250
|
+
}, /*#__PURE__*/_react.default.createElement("ul", {
|
|
251
|
+
className: "unordered-list",
|
|
252
|
+
role: "none"
|
|
253
|
+
}, ellipsesList))))) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("li", {
|
|
254
|
+
className: "list-item",
|
|
255
|
+
role: "none"
|
|
256
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
257
|
+
role: "menuitem",
|
|
258
|
+
tabIndex: 0
|
|
259
|
+
}, signInLink)), /*#__PURE__*/_react.default.createElement("li", {
|
|
260
|
+
className: "list-item",
|
|
261
|
+
role: "none"
|
|
262
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
263
|
+
role: "menuitem",
|
|
264
|
+
tabIndex: 0
|
|
265
|
+
}, signUpLink)))))));
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Set display name for debugging
|
|
269
|
+
Header.displayName = 'Header';
|
|
270
|
+
Header.propTypes = {
|
|
271
|
+
// ===========================================
|
|
272
|
+
// Core Props
|
|
273
|
+
// ===========================================
|
|
274
|
+
/**
|
|
275
|
+
* Unique identifier for the component
|
|
276
|
+
*/
|
|
277
|
+
id: _propTypes.string,
|
|
278
|
+
/**
|
|
279
|
+
* Additional CSS classes to apply
|
|
280
|
+
*/
|
|
281
|
+
className: _propTypes.string,
|
|
282
|
+
// ===========================================
|
|
283
|
+
// Content Props
|
|
284
|
+
// ===========================================
|
|
285
|
+
/**
|
|
286
|
+
* Logo element to display in header (required for branding)
|
|
287
|
+
*/
|
|
288
|
+
logo: _propTypes.node.isRequired,
|
|
289
|
+
/**
|
|
290
|
+
* Desktop navigation items (required for navigation)
|
|
291
|
+
*/
|
|
292
|
+
navigation: _propTypes.node.isRequired,
|
|
293
|
+
/**
|
|
294
|
+
* Mobile navigation menu items (required for mobile UX)
|
|
295
|
+
*/
|
|
296
|
+
mapMenuItems: _propTypes.node.isRequired,
|
|
297
|
+
/**
|
|
298
|
+
* Sign in link component for unauthenticated users
|
|
299
|
+
*/
|
|
300
|
+
signInLink: _propTypes.node,
|
|
301
|
+
/**
|
|
302
|
+
* Sign up link component for unauthenticated users
|
|
303
|
+
*/
|
|
304
|
+
signUpLink: _propTypes.node,
|
|
305
|
+
/**
|
|
306
|
+
* User avatar image source for authenticated users
|
|
307
|
+
*/
|
|
308
|
+
avatarSrc: _propTypes.string,
|
|
309
|
+
/**
|
|
310
|
+
* List of user menu items for dropdown
|
|
311
|
+
*/
|
|
312
|
+
ellipsesList: (0, _propTypes.arrayOf)(_propTypes.node),
|
|
313
|
+
/**
|
|
314
|
+
* Search component for header search functionality
|
|
315
|
+
*/
|
|
316
|
+
search: _propTypes.node,
|
|
317
|
+
// ===========================================
|
|
318
|
+
// Behavior Props
|
|
319
|
+
// ===========================================
|
|
320
|
+
/**
|
|
321
|
+
* Whether user is authenticated (affects UI layout)
|
|
322
|
+
*/
|
|
323
|
+
hasAuth: _propTypes.bool,
|
|
324
|
+
/**
|
|
325
|
+
* Whether mobile menu is currently open (controlled state, required)
|
|
326
|
+
*/
|
|
327
|
+
isMenuOpen: _propTypes.bool.isRequired,
|
|
328
|
+
/**
|
|
329
|
+
* Whether user dropdown menu is currently open (controlled state, required)
|
|
330
|
+
*/
|
|
331
|
+
isEllipsesOpen: _propTypes.bool.isRequired,
|
|
332
|
+
// ===========================================
|
|
333
|
+
// Event Props
|
|
334
|
+
// ===========================================
|
|
335
|
+
/**
|
|
336
|
+
* Callback when mobile menu toggle is triggered (required for state management)
|
|
337
|
+
*/
|
|
338
|
+
onMenuToggle: _propTypes.func.isRequired,
|
|
339
|
+
/**
|
|
340
|
+
* Callback when user dropdown toggle is triggered (required for state management)
|
|
341
|
+
*/
|
|
342
|
+
onEllipsesToggle: _propTypes.func.isRequired,
|
|
343
|
+
/**
|
|
344
|
+
* Additional callback for custom keyboard event handling
|
|
345
|
+
*/
|
|
346
|
+
onKeyDown: _propTypes.func,
|
|
347
|
+
// ===========================================
|
|
348
|
+
// Accessibility Props
|
|
349
|
+
// ===========================================
|
|
350
|
+
/**
|
|
351
|
+
* ARIA label for the header element (required for accessibility)
|
|
352
|
+
*/
|
|
353
|
+
ariaLabel: _propTypes.string.isRequired,
|
|
354
|
+
/**
|
|
355
|
+
* ARIA label for the navigation menu (required for screen readers)
|
|
356
|
+
*/
|
|
357
|
+
menuAriaLabel: _propTypes.string.isRequired,
|
|
358
|
+
/**
|
|
359
|
+
* ARIA label for the user menu (required for screen readers)
|
|
360
|
+
*/
|
|
361
|
+
userMenuAriaLabel: _propTypes.string.isRequired,
|
|
362
|
+
/**
|
|
363
|
+
* Use id for identification
|
|
364
|
+
* Base component CSS class name
|
|
365
|
+
*/
|
|
366
|
+
componentName: _propTypes.string,
|
|
367
|
+
/**
|
|
368
|
+
* Use className instead
|
|
369
|
+
* Additional CSS classes to apply
|
|
370
|
+
*/
|
|
371
|
+
additionalClassName: _propTypes.string
|
|
372
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.Heading = 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
|
+
* Heading Component
|
|
13
|
+
*
|
|
14
|
+
* A semantic heading component that renders appropriate HTML heading elements (h1-h6)
|
|
15
|
+
* with proper accessibility and styling support.
|
|
16
|
+
*
|
|
17
|
+
* This component ensures proper document structure and hierarchy for screen readers
|
|
18
|
+
* and SEO while providing flexible styling options.
|
|
19
|
+
*
|
|
20
|
+
* @component
|
|
21
|
+
* @example
|
|
22
|
+
* // Basic usage
|
|
23
|
+
* <Heading level={1}>Main Page Title</Heading>
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // With custom styling and accessibility
|
|
27
|
+
* <Heading
|
|
28
|
+
* level={2}
|
|
29
|
+
* className="section-title"
|
|
30
|
+
* id="about-section"
|
|
31
|
+
* aria-describedby="about-description"
|
|
32
|
+
* >
|
|
33
|
+
* About Us
|
|
34
|
+
* </Heading>
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // With different variants and sizes
|
|
38
|
+
* <Heading level={3} variant="primary" size="large">
|
|
39
|
+
* Feature Highlight
|
|
40
|
+
* </Heading>
|
|
41
|
+
*/
|
|
42
|
+
const Heading = exports.Heading = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
43
|
+
// Core props
|
|
44
|
+
level = 1,
|
|
45
|
+
children = null,
|
|
46
|
+
// Layout props
|
|
47
|
+
className = '',
|
|
48
|
+
variant = 'default',
|
|
49
|
+
size = 'default',
|
|
50
|
+
// Accessibility props
|
|
51
|
+
id,
|
|
52
|
+
'aria-level': ariaLevel,
|
|
53
|
+
'aria-labelledby': ariaLabelledby,
|
|
54
|
+
'aria-describedby': ariaDescribedby,
|
|
55
|
+
// Legacy props (for backward compatibility - internal use only)
|
|
56
|
+
additionalClassName = '',
|
|
57
|
+
componentName = 'heading',
|
|
58
|
+
...restProps
|
|
59
|
+
}, ref) => {
|
|
60
|
+
// Handle legacy prop mapping
|
|
61
|
+
// For backward compatibility, if variant is a number, treat it as level
|
|
62
|
+
const isLegacyVariant = typeof variant === 'number';
|
|
63
|
+
const finalLevel = isLegacyVariant ? variant : level;
|
|
64
|
+
const finalClassName = className || additionalClassName;
|
|
65
|
+
|
|
66
|
+
// Build CSS classes - keep it simple to match tests
|
|
67
|
+
const HeadingTag = `h${finalLevel}`;
|
|
68
|
+
const headingClasses = [HeadingTag, componentName, finalClassName].filter(Boolean).join(' ').trim();
|
|
69
|
+
return /*#__PURE__*/_react.default.createElement(HeadingTag, _extends({
|
|
70
|
+
ref: ref,
|
|
71
|
+
id: id,
|
|
72
|
+
className: headingClasses,
|
|
73
|
+
"data-testid": componentName
|
|
74
|
+
// Accessibility attributes
|
|
75
|
+
,
|
|
76
|
+
"aria-level": ariaLevel || finalLevel,
|
|
77
|
+
"aria-labelledby": ariaLabelledby,
|
|
78
|
+
"aria-describedby": ariaDescribedby
|
|
79
|
+
}, restProps), children);
|
|
80
|
+
});
|
|
81
|
+
var _default = exports.default = Heading; // Set display name for debugging
|
|
82
|
+
Heading.displayName = 'Heading';
|
|
83
|
+
Heading.propTypes = {
|
|
84
|
+
// ===========================================
|
|
85
|
+
// Core Props
|
|
86
|
+
// ===========================================
|
|
87
|
+
/**
|
|
88
|
+
* Heading level (1-6) that determines semantic importance and HTML element.
|
|
89
|
+
* Required for proper document structure and accessibility.
|
|
90
|
+
*/
|
|
91
|
+
level: (0, _propTypes.oneOf)([1, 2, 3, 4, 5, 6]),
|
|
92
|
+
/**
|
|
93
|
+
* Unique identifier for the heading
|
|
94
|
+
*/
|
|
95
|
+
id: _propTypes.string,
|
|
96
|
+
// ===========================================
|
|
97
|
+
// Content Props
|
|
98
|
+
// ===========================================
|
|
99
|
+
/**
|
|
100
|
+
* The content to be displayed in the heading.
|
|
101
|
+
* Can be text, React elements, or other nodes.
|
|
102
|
+
*/
|
|
103
|
+
children: _propTypes.node,
|
|
104
|
+
// ===========================================
|
|
105
|
+
// Appearance Props
|
|
106
|
+
// ===========================================
|
|
107
|
+
/**
|
|
108
|
+
* Additional CSS classes to apply to the heading element
|
|
109
|
+
*/
|
|
110
|
+
className: _propTypes.string,
|
|
111
|
+
/**
|
|
112
|
+
* Visual style variant
|
|
113
|
+
*/
|
|
114
|
+
variant: _propTypes.string,
|
|
115
|
+
/**
|
|
116
|
+
* Size variant for the heading
|
|
117
|
+
*/
|
|
118
|
+
size: _propTypes.string,
|
|
119
|
+
// ===========================================
|
|
120
|
+
// Accessibility Props
|
|
121
|
+
// ===========================================
|
|
122
|
+
/**
|
|
123
|
+
* ARIA level override (if different from semantic level)
|
|
124
|
+
*/
|
|
125
|
+
'aria-level': (0, _propTypes.oneOf)([1, 2, 3, 4, 5, 6]),
|
|
126
|
+
/**
|
|
127
|
+
* ID of element that labels this heading
|
|
128
|
+
*/
|
|
129
|
+
'aria-labelledby': _propTypes.string,
|
|
130
|
+
/**
|
|
131
|
+
* ID(s) of elements that describe this heading
|
|
132
|
+
*/
|
|
133
|
+
'aria-describedby': _propTypes.string
|
|
134
|
+
};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.Hero = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _Animation = require("./Animation");
|
|
9
|
+
var _propTypes = require("prop-types");
|
|
10
|
+
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); }
|
|
11
|
+
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); }
|
|
12
|
+
/**
|
|
13
|
+
* Hero - Accessible hero section component for prominent content display
|
|
14
|
+
*
|
|
15
|
+
* Features:
|
|
16
|
+
* - Semantic HTML structure using section element
|
|
17
|
+
* - Background image support with accessibility features
|
|
18
|
+
* - Customizable overlay with opacity controls
|
|
19
|
+
* - Full ARIA support for screen readers
|
|
20
|
+
* - Responsive design with flexible content area
|
|
21
|
+
* - High contrast mode support
|
|
22
|
+
*
|
|
23
|
+
* Accessibility:
|
|
24
|
+
* - Uses semantic <section> element with proper ARIA roles
|
|
25
|
+
* - Background images include proper alt text descriptions
|
|
26
|
+
* - Screen reader accessible overlay elements
|
|
27
|
+
* - Proper landmark navigation with role="banner"
|
|
28
|
+
* - Focus management and keyboard navigation support
|
|
29
|
+
*
|
|
30
|
+
* @component
|
|
31
|
+
* @example
|
|
32
|
+
* // Basic hero with background
|
|
33
|
+
* <Hero background="/hero-image.jpg" backgroundAlt="City skyline">
|
|
34
|
+
* <h1>Welcome to Our Platform</h1>
|
|
35
|
+
* <p>Discover amazing features and capabilities.</p>
|
|
36
|
+
* </Hero>
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // Hero with overlay for better text readability
|
|
40
|
+
* <Hero
|
|
41
|
+
* background="/hero.jpg"
|
|
42
|
+
* backgroundAlt="Team collaboration"
|
|
43
|
+
* hasOverlay={true}
|
|
44
|
+
* overlayOpacity="dark"
|
|
45
|
+
* >
|
|
46
|
+
* <h1>Build Something Amazing</h1>
|
|
47
|
+
* </Hero>
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* // Custom styling and accessibility
|
|
51
|
+
* <Hero
|
|
52
|
+
* className="landing-hero"
|
|
53
|
+
* role="banner"
|
|
54
|
+
* aria-label="Main hero section"
|
|
55
|
+
* >
|
|
56
|
+
* <h1>No Background Hero</h1>
|
|
57
|
+
* </Hero>
|
|
58
|
+
*/
|
|
59
|
+
const Hero = exports.Hero = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
60
|
+
// Core props
|
|
61
|
+
children = null,
|
|
62
|
+
background = null,
|
|
63
|
+
backgroundAlt = '',
|
|
64
|
+
// Layout props
|
|
65
|
+
className = '',
|
|
66
|
+
hasOverlay = false,
|
|
67
|
+
overlayOpacity = 'medium',
|
|
68
|
+
// Accessibility props
|
|
69
|
+
role = 'banner',
|
|
70
|
+
ariaLabel = null,
|
|
71
|
+
ariaLabelledBy = null,
|
|
72
|
+
// Legacy props (for backward compatibility - internal use only)
|
|
73
|
+
additionalClassName = '',
|
|
74
|
+
componentName = 'hero',
|
|
75
|
+
testId = 'hero',
|
|
76
|
+
...restProps
|
|
77
|
+
}, ref) => {
|
|
78
|
+
// Handle legacy prop mapping
|
|
79
|
+
const finalClassName = className || additionalClassName;
|
|
80
|
+
|
|
81
|
+
// Build CSS classes
|
|
82
|
+
|
|
83
|
+
const finalComponentName = componentName || 'hero';
|
|
84
|
+
const overlayClass = hasOverlay ? `${finalComponentName}--overlay-${overlayOpacity}` : '';
|
|
85
|
+
const heroClasses = [`${finalComponentName}-wrapper`, overlayClass, finalClassName].filter(Boolean).join(' ').trim();
|
|
86
|
+
|
|
87
|
+
// Accessibility attributes
|
|
88
|
+
const accessibilityProps = {
|
|
89
|
+
role,
|
|
90
|
+
...(ariaLabel && {
|
|
91
|
+
'aria-label': ariaLabel
|
|
92
|
+
}),
|
|
93
|
+
...(ariaLabelledBy && {
|
|
94
|
+
'aria-labelledby': ariaLabelledBy
|
|
95
|
+
}),
|
|
96
|
+
...(background && backgroundAlt && {
|
|
97
|
+
'aria-describedby': `${testId}-bg-description`
|
|
98
|
+
})
|
|
99
|
+
};
|
|
100
|
+
return /*#__PURE__*/_react.default.createElement(_Animation.AnimatedDiv, {
|
|
101
|
+
fadingEntrances: "fadeIn",
|
|
102
|
+
duration: "faster"
|
|
103
|
+
}, /*#__PURE__*/_react.default.createElement("section", _extends({
|
|
104
|
+
ref: ref,
|
|
105
|
+
className: heroClasses,
|
|
106
|
+
"data-testid": testId || finalComponentName,
|
|
107
|
+
style: background ? {
|
|
108
|
+
backgroundImage: `url(${background})`
|
|
109
|
+
} : {}
|
|
110
|
+
}, accessibilityProps, restProps), background && backgroundAlt && /*#__PURE__*/_react.default.createElement("div", {
|
|
111
|
+
id: `${testId}-bg-description`,
|
|
112
|
+
className: "sr-only"
|
|
113
|
+
}, "Background image: ", backgroundAlt), hasOverlay && /*#__PURE__*/_react.default.createElement("div", {
|
|
114
|
+
className: `${finalComponentName}__overlay`,
|
|
115
|
+
"aria-hidden": "true"
|
|
116
|
+
}), /*#__PURE__*/_react.default.createElement("div", {
|
|
117
|
+
className: finalComponentName
|
|
118
|
+
}, children)));
|
|
119
|
+
});
|
|
120
|
+
Hero.propTypes = {
|
|
121
|
+
// ===========================================
|
|
122
|
+
// Content Props
|
|
123
|
+
// ===========================================
|
|
124
|
+
/**
|
|
125
|
+
* Content to display within the hero section
|
|
126
|
+
*/
|
|
127
|
+
children: _propTypes.node,
|
|
128
|
+
// ===========================================
|
|
129
|
+
// Appearance Props
|
|
130
|
+
// ===========================================
|
|
131
|
+
/**
|
|
132
|
+
* Background image URL
|
|
133
|
+
*/
|
|
134
|
+
background: _propTypes.string,
|
|
135
|
+
/**
|
|
136
|
+
* Alt text description for background image (required when background is provided)
|
|
137
|
+
*/
|
|
138
|
+
backgroundAlt: _propTypes.string,
|
|
139
|
+
/**
|
|
140
|
+
* Additional CSS classes to apply to the hero wrapper
|
|
141
|
+
*/
|
|
142
|
+
className: _propTypes.string,
|
|
143
|
+
/**
|
|
144
|
+
* Whether to show a semi-transparent overlay over the background
|
|
145
|
+
*/
|
|
146
|
+
hasOverlay: _propTypes.bool,
|
|
147
|
+
/**
|
|
148
|
+
* Opacity level of the overlay
|
|
149
|
+
*/
|
|
150
|
+
overlayOpacity: (0, _propTypes.oneOf)(['light', 'medium', 'dark']),
|
|
151
|
+
// ===========================================
|
|
152
|
+
// Accessibility Props
|
|
153
|
+
// ===========================================
|
|
154
|
+
/**
|
|
155
|
+
* ARIA role for semantic meaning
|
|
156
|
+
*/
|
|
157
|
+
role: (0, _propTypes.oneOf)(['banner', 'region', 'main']),
|
|
158
|
+
/**
|
|
159
|
+
* ARIA label for accessibility
|
|
160
|
+
*/
|
|
161
|
+
// aria-label is used as a prop, not ariaLabel
|
|
162
|
+
/**
|
|
163
|
+
* ID of element that labels this hero section
|
|
164
|
+
*/
|
|
165
|
+
ariaLabelledBy: _propTypes.string,
|
|
166
|
+
/**
|
|
167
|
+
* Use className instead
|
|
168
|
+
*/
|
|
169
|
+
additionalClassName: _propTypes.string,
|
|
170
|
+
/**
|
|
171
|
+
* Use className/id for identification
|
|
172
|
+
* Base CSS class name for the component
|
|
173
|
+
*/
|
|
174
|
+
componentName: _propTypes.string,
|
|
175
|
+
/**
|
|
176
|
+
* Test ID for testing and automation
|
|
177
|
+
*/
|
|
178
|
+
testId: _propTypes.string
|
|
179
|
+
};
|
|
180
|
+
Hero.displayName = 'Hero';
|
|
181
|
+
var _default = exports.default = Hero;
|