@dnanpm/styleguide 3.9.14 → 3.10.0
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/build/cjs/components/Box/Box.d.ts +4 -0
- package/build/cjs/components/Box/Box.js +1 -1
- package/build/cjs/components/Button/Button.d.ts +4 -0
- package/build/cjs/components/Button/Button.js +1 -1
- package/build/cjs/components/ButtonArrow/ButtonArrow.js +9 -2
- package/build/cjs/components/Carousel/Carousel.js +4 -1
- package/build/cjs/components/EnergyLabel/EnergyLabel.d.ts +65 -0
- package/build/cjs/components/EnergyLabel/EnergyLabel.js +98 -0
- package/build/cjs/components/Notification/Notification.js +3 -3
- package/build/cjs/components/Overlay/Overlay.d.ts +64 -3
- package/build/cjs/components/Overlay/Overlay.js +11 -8
- package/build/cjs/components/PriorityNavigation/PriorityNavigation.d.ts +0 -4
- package/build/cjs/components/PriorityNavigation/PriorityNavigation.js +20 -26
- package/build/cjs/components/index.d.ts +1 -0
- package/build/cjs/index.js +2 -0
- package/build/es/components/Box/Box.d.ts +4 -0
- package/build/es/components/Box/Box.js +1 -1
- package/build/es/components/Button/Button.d.ts +4 -0
- package/build/es/components/Button/Button.js +1 -1
- package/build/es/components/ButtonArrow/ButtonArrow.js +10 -3
- package/build/es/components/Carousel/Carousel.js +4 -1
- package/build/es/components/EnergyLabel/EnergyLabel.d.ts +65 -0
- package/build/es/components/EnergyLabel/EnergyLabel.js +90 -0
- package/build/es/components/Notification/Notification.js +3 -3
- package/build/es/components/Overlay/Overlay.d.ts +64 -3
- package/build/es/components/Overlay/Overlay.js +11 -8
- package/build/es/components/PriorityNavigation/PriorityNavigation.d.ts +0 -4
- package/build/es/components/PriorityNavigation/PriorityNavigation.js +20 -26
- package/build/es/components/index.d.ts +1 -0
- package/build/es/index.js +1 -0
- package/package.json +2 -2
|
@@ -65,6 +65,10 @@ interface Props {
|
|
|
65
65
|
* e.g., "important information" or "example."
|
|
66
66
|
*/
|
|
67
67
|
ariaLabel?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Screen reader live region policy for the box content
|
|
70
|
+
*/
|
|
71
|
+
ariaLive?: 'off' | 'polite' | 'assertive';
|
|
68
72
|
/**
|
|
69
73
|
* Allows to pass a role to the component
|
|
70
74
|
*/
|
|
@@ -26,7 +26,7 @@ const BoxWrapper = styled__default.default.div `
|
|
|
26
26
|
`;
|
|
27
27
|
const Box = (_a) => {
|
|
28
28
|
var { elevation = 'none', 'data-testid': dataTestId } = _a, props = tslib.__rest(_a, ["elevation", 'data-testid']);
|
|
29
|
-
return (React__default.default.createElement(BoxWrapper, { id: props.id, "$elevation": elevation, shadow: props.shadow, width: props.width, height: props.height, margin: props.margin, padding: props.padding, className: props.className, "data-testid": dataTestId, role: props.role, "aria-label": props.ariaLabel }, props.children));
|
|
29
|
+
return (React__default.default.createElement(BoxWrapper, { id: props.id, "$elevation": elevation, shadow: props.shadow, width: props.width, height: props.height, margin: props.margin, padding: props.padding, className: props.className, "data-testid": dataTestId, role: props.role, "aria-label": props.ariaLabel, "aria-live": props.ariaLive }, props.children));
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
exports.default = Box;
|
|
@@ -24,6 +24,10 @@ export interface Props {
|
|
|
24
24
|
* Allows to change the type of resulting HTML element from button (`<button></button>`) to anchor (`<a href="..."></a>`)
|
|
25
25
|
*/
|
|
26
26
|
href?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Allows to set the target attribute for the link
|
|
29
|
+
*/
|
|
30
|
+
target?: '_self' | '_blank' | '_parent' | '_top';
|
|
27
31
|
/**
|
|
28
32
|
* Content of Button component
|
|
29
33
|
*/
|
|
@@ -123,7 +123,7 @@ const Element = styled__default.default.button `
|
|
|
123
123
|
/** @visibleName Button */
|
|
124
124
|
const Button = (_a) => {
|
|
125
125
|
var { type = 'submit', 'data-testid': dataTestId, 'data-no-close': dataNoClose, 'data-track-value': dataTrackValue, 'aria-label': ariaLabel } = _a, props = tslib.__rest(_a, ["type", 'data-testid', 'data-no-close', 'data-track-value', 'aria-label']);
|
|
126
|
-
return (React__default.default.createElement(Element, Object.assign({ id: props.id, as: props.href ? 'a' : undefined, type: props.href ? undefined : type, href: props.href, onClick: props.onClick, onMouseDown: props.onMouseDown, small: props.small, darkBg: props.darkBg, fullWidth: props.fullWidth, "$loading": props.loading, tabIndex: props.loading ? -1 : 0, "data-loading": props.loading, className: props.className, "data-testid": dataTestId, "data-no-close": dataNoClose, "data-track-value": dataTrackValue, "aria-label": ariaLabel }, props.dataAttributes, (!props.href && {
|
|
126
|
+
return (React__default.default.createElement(Element, Object.assign({ id: props.id, as: props.href ? 'a' : undefined, type: props.href ? undefined : type, href: props.href, target: props.href ? props.target : undefined, rel: props.target === '_blank' ? 'noopener noreferrer' : undefined, onClick: props.onClick, onMouseDown: props.onMouseDown, small: props.small, darkBg: props.darkBg, fullWidth: props.fullWidth, "$loading": props.loading, tabIndex: props.loading ? -1 : 0, "data-loading": props.loading, className: props.className, "data-testid": dataTestId, "data-no-close": dataNoClose, "data-track-value": dataTrackValue, "aria-label": ariaLabel }, props.dataAttributes, (!props.href && {
|
|
127
127
|
name: props.name,
|
|
128
128
|
disabled: props.disabled,
|
|
129
129
|
})), props.loading ? (React__default.default.createElement(PixelLoader.default, { color: props.darkBg ? theme.default.color.default.white : theme.default.color.default.plum, label: props.loadingLabel })) : (React__default.default.createElement("span", { "data-testid": dataTestId && `${dataTestId}-text`, "data-no-close": dataNoClose }, props.children))));
|
|
@@ -10,7 +10,6 @@ var theme = require('../../themes/theme.js');
|
|
|
10
10
|
var styledUtils = require('../../utils/styledUtils.js');
|
|
11
11
|
var ButtonPrimary = require('../ButtonPrimary/ButtonPrimary.js');
|
|
12
12
|
var ButtonSecondary = require('../ButtonSecondary/ButtonSecondary.js');
|
|
13
|
-
var Icon = require('../Icon/Icon.js');
|
|
14
13
|
|
|
15
14
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
16
15
|
|
|
@@ -73,7 +72,15 @@ const buttonsMap = {
|
|
|
73
72
|
const ButtonArrow = (_a) => {
|
|
74
73
|
var { variant = 'primary', 'data-testid': dataTestId, 'data-track-value': dataTrackValue, 'aria-label': ariaLabel } = _a, props = tslib.__rest(_a, ["variant", 'data-testid', 'data-track-value', 'aria-label']);
|
|
75
74
|
const Element = buttonsMap[variant];
|
|
76
|
-
|
|
75
|
+
let iconElement = null;
|
|
76
|
+
if (props.direction) {
|
|
77
|
+
const IconComponent = iconsMap[props.direction];
|
|
78
|
+
iconElement = React__default.default.createElement(IconComponent, { size: "1rem" });
|
|
79
|
+
}
|
|
80
|
+
else if (React.isValidElement(props.icon)) {
|
|
81
|
+
iconElement = React.cloneElement(props.icon, { size: '1rem' });
|
|
82
|
+
}
|
|
83
|
+
return (React__default.default.createElement(Element, { id: props.id, href: props.href, name: props.name, type: props.type, onClick: props.onClick, onMouseDown: props.onMouseDown, darkBg: props.darkBg, disabled: props.disabled, className: props.className, "data-testid": dataTestId, "data-track-value": dataTrackValue, dataAttributes: props.dataAttributes, "aria-label": ariaLabel }, iconElement));
|
|
77
84
|
};
|
|
78
85
|
|
|
79
86
|
exports.default = ButtonArrow;
|
|
@@ -184,7 +184,8 @@ const Carousel = (_a) => {
|
|
|
184
184
|
};
|
|
185
185
|
const visibleItems = props.visibleItems || calculatedItems;
|
|
186
186
|
const slidesWrapperGapSizePx = 20;
|
|
187
|
-
const
|
|
187
|
+
const slidesCount = React.Children.count(props.children);
|
|
188
|
+
const slideScreensCount = Math.max(1, slidesCount - Math.floor(visibleItems) + 1);
|
|
188
189
|
const step = getStep((_b = props.swipeStep) !== null && _b !== void 0 ? _b : 1, visibleItems);
|
|
189
190
|
const currentStepIndex = Math.ceil(currentIndex / step);
|
|
190
191
|
const totalSwipeSteps = Math.ceil(slideScreensCount / step + ((slideScreensCount - 1) % step !== 0 ? 1 : 0));
|
|
@@ -306,6 +307,8 @@ const Carousel = (_a) => {
|
|
|
306
307
|
setIsSwiping(false);
|
|
307
308
|
};
|
|
308
309
|
const handleSlidesPointerDown = (e) => {
|
|
310
|
+
if (e.button !== 0)
|
|
311
|
+
return;
|
|
309
312
|
if (slidesWrapperRef.current && scrollbarRef.current && knobRef.current) {
|
|
310
313
|
data.startX = e.pageX;
|
|
311
314
|
data.startTime = Date.now();
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface Props {
|
|
3
|
+
/**
|
|
4
|
+
* Unique ID for the component
|
|
5
|
+
*/
|
|
6
|
+
id?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Allows to pass a custom className
|
|
9
|
+
*/
|
|
10
|
+
className?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Allows to pass testid string for testing purposes
|
|
13
|
+
*/
|
|
14
|
+
'data-testid'?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Allows to pass a screen reader label to the component
|
|
17
|
+
*/
|
|
18
|
+
ariaLabel?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Allows to set property `aria-label` for `ButtonClose` element
|
|
21
|
+
*/
|
|
22
|
+
closeButtonLabel?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Allows to set the source of the badge image
|
|
25
|
+
*/
|
|
26
|
+
badgeSrc: string;
|
|
27
|
+
/**
|
|
28
|
+
* Allows to set the title of the modal
|
|
29
|
+
*/
|
|
30
|
+
modalTitle?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Allows to set the source of the image in the modal
|
|
33
|
+
*/
|
|
34
|
+
modalImageSrc?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Allows to set the label of the modal
|
|
37
|
+
*/
|
|
38
|
+
modalLabel?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Allows to set the label of the button that opens the PDF
|
|
41
|
+
*/
|
|
42
|
+
pdfButtonLabel?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Allows to set the href of the PDF file
|
|
45
|
+
*/
|
|
46
|
+
pdfHref?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Allows to set the label for the error state
|
|
49
|
+
*/
|
|
50
|
+
errorLabel?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Allows to set the title for the error state
|
|
53
|
+
*/
|
|
54
|
+
errorTitle?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Allows to hide application from assistive screenreaders and other assistive technologies while the modal is open
|
|
57
|
+
*
|
|
58
|
+
* @default '#__next'
|
|
59
|
+
*/
|
|
60
|
+
modalAppElement?: string;
|
|
61
|
+
}
|
|
62
|
+
/** @visibleName Energy Label */
|
|
63
|
+
declare const EnergyLabel: ({ "data-testid": dataTestId, modalAppElement, ...props }: Props) => React.JSX.Element | null;
|
|
64
|
+
/** @component */
|
|
65
|
+
export default EnergyLabel;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var tslib = require('tslib');
|
|
6
|
+
var React = require('react');
|
|
7
|
+
var icons = require('@dnanpm/icons');
|
|
8
|
+
var Modal = require('../Modal/Modal.js');
|
|
9
|
+
var styled = require('../../themes/styled.js');
|
|
10
|
+
var styledUtils = require('../../utils/styledUtils.js');
|
|
11
|
+
var Button = require('../Button/Button.js');
|
|
12
|
+
var theme = require('../../themes/theme.js');
|
|
13
|
+
|
|
14
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
15
|
+
|
|
16
|
+
var React__default = /*#__PURE__*/_interopDefaultCompat(React);
|
|
17
|
+
|
|
18
|
+
const ERROR_IMAGE = 'https://res.cloudinary.com/dnaoyj/image/upload/v1731073400/Assets/KLT/Enriched%20icons/enriched-error-state.png';
|
|
19
|
+
const ButtonElement = styled.default.button `
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
background-color: transparent;
|
|
24
|
+
border: 0;
|
|
25
|
+
padding: 0;
|
|
26
|
+
cursor: pointer;
|
|
27
|
+
`;
|
|
28
|
+
const StyledModal = styled.default(Modal.default) `
|
|
29
|
+
> div > div {
|
|
30
|
+
max-height: 100%;
|
|
31
|
+
height: 100%;
|
|
32
|
+
width: 100%;
|
|
33
|
+
margin: 0;
|
|
34
|
+
|
|
35
|
+
${styledUtils.media.xs `
|
|
36
|
+
width: 23.438rem;
|
|
37
|
+
height: 50rem;
|
|
38
|
+
border-radius: ${theme.default.radius.default};
|
|
39
|
+
`};
|
|
40
|
+
|
|
41
|
+
> div {
|
|
42
|
+
border-radius: 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
const ModalLabel = styled.default.p `
|
|
47
|
+
font-size: ${theme.default.fontSize.s};
|
|
48
|
+
margin: 0;
|
|
49
|
+
`;
|
|
50
|
+
const ButtonContent = styled.default.span `
|
|
51
|
+
display: inline-flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
gap: ${styledUtils.getMultipliedSize(theme.default.base.baseWidth, 0.5)};
|
|
54
|
+
`;
|
|
55
|
+
const ErrorContainer = styled.default.div `
|
|
56
|
+
display: flex;
|
|
57
|
+
padding-top: ${styledUtils.getMultipliedSize(theme.default.base.baseHeight, 2)};
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
text-align: center;
|
|
60
|
+
align-items: center;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
gap: ${styledUtils.getMultipliedSize(theme.default.base.baseHeight, 3)};
|
|
63
|
+
`;
|
|
64
|
+
const Image = styled.default.img `
|
|
65
|
+
width: ${p => p.imgWidth};
|
|
66
|
+
`;
|
|
67
|
+
/** @visibleName Energy Label */
|
|
68
|
+
const EnergyLabel = (_a) => {
|
|
69
|
+
var { 'data-testid': dataTestId, modalAppElement = '#__next' } = _a, props = tslib.__rest(_a, ['data-testid', "modalAppElement"]);
|
|
70
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
71
|
+
const [hasError, setHasError] = React.useState(false);
|
|
72
|
+
const [imgError, setImgError] = React.useState(false);
|
|
73
|
+
const handleOpen = () => {
|
|
74
|
+
setIsOpen(true);
|
|
75
|
+
};
|
|
76
|
+
const handleClose = () => {
|
|
77
|
+
setIsOpen(false);
|
|
78
|
+
};
|
|
79
|
+
const renderFooter = () => (React__default.default.createElement(Button.default, { fullWidth: true, href: props.pdfHref, target: "_blank" },
|
|
80
|
+
React__default.default.createElement(ButtonContent, null,
|
|
81
|
+
props.pdfButtonLabel,
|
|
82
|
+
" ",
|
|
83
|
+
React__default.default.createElement(icons.Open, null))));
|
|
84
|
+
if (imgError) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
return (React__default.default.createElement(React__default.default.Fragment, null,
|
|
88
|
+
React__default.default.createElement(ButtonElement, { onClick: handleOpen, "aria-label": props.ariaLabel, "data-testid": dataTestId, id: props.id, className: props.className },
|
|
89
|
+
React__default.default.createElement(Image, { src: props.badgeSrc, alt: "", "aria-hidden": true, imgWidth: "55px", onError: () => setImgError(true) })),
|
|
90
|
+
React__default.default.createElement(StyledModal, { appElement: modalAppElement, isOpen: isOpen, onRequestClose: handleClose, title: props.modalTitle, closeLabel: props.closeButtonLabel, footer: renderFooter() }, hasError || !props.modalImageSrc ? (React__default.default.createElement(ErrorContainer, null,
|
|
91
|
+
React__default.default.createElement("h3", null, props.errorTitle),
|
|
92
|
+
React__default.default.createElement(Image, { src: ERROR_IMAGE, alt: "", imgWidth: "160px" }),
|
|
93
|
+
React__default.default.createElement("p", null, props.errorLabel))) : (React__default.default.createElement(React__default.default.Fragment, null,
|
|
94
|
+
React__default.default.createElement(Image, { src: props.modalImageSrc, alt: "", imgWidth: "100%", onError: () => setHasError(true) }),
|
|
95
|
+
React__default.default.createElement(ModalLabel, null, props.modalLabel))))));
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
exports.default = EnergyLabel;
|
|
@@ -33,7 +33,7 @@ const NotificationWrapper = styled__default.default.div `
|
|
|
33
33
|
${sharedStyles}
|
|
34
34
|
border-color: ${({ $type }) => theme.default.color.notification[$type]};
|
|
35
35
|
`;
|
|
36
|
-
const StaticWrapper = styled__default.default.
|
|
36
|
+
const StaticWrapper = styled__default.default.div `
|
|
37
37
|
${sharedStyles}
|
|
38
38
|
border-color: ${({ $type }) => theme.default.color.notification[$type]};
|
|
39
39
|
`;
|
|
@@ -43,7 +43,7 @@ const IconWrapper = styled__default.default.div `
|
|
|
43
43
|
padding: 0.5rem;
|
|
44
44
|
background-color: ${({ $type }) => theme.default.color.notification[$type]};
|
|
45
45
|
`;
|
|
46
|
-
const ContentWrapper = styled__default.default.
|
|
46
|
+
const ContentWrapper = styled__default.default.div `
|
|
47
47
|
margin: auto 0;
|
|
48
48
|
padding: 0.5rem 0;
|
|
49
49
|
width: 100%;
|
|
@@ -68,7 +68,7 @@ const Notification = ({ type = 'info', 'data-testid': dataTestId, isStatic = fal
|
|
|
68
68
|
React__default.default.createElement(ContentWrapper, null, children),
|
|
69
69
|
closeButton && (React__default.default.createElement(ButtonCloseStyled, { onClick: onClickCloseButton, "aria-label": closeButtonLabel },
|
|
70
70
|
React__default.default.createElement(Icon.default, { icon: icons.Close, color: "currentColor", "aria-hidden": true })))));
|
|
71
|
-
return isStatic ? (React__default.default.createElement(StaticWrapper, { "$type": type, className: className, "data-testid": dataTestId, "aria-label": ariaLabel }, renderContent())) : (React__default.default.createElement(NotificationWrapper, { "$type": type, className: className, "data-testid": dataTestId, role: "alert" }, renderContent()));
|
|
71
|
+
return isStatic ? (React__default.default.createElement(StaticWrapper, { "$type": type, className: className, "data-testid": dataTestId, "aria-label": ariaLabel, role: "region" }, renderContent())) : (React__default.default.createElement(NotificationWrapper, { "$type": type, className: className, "data-testid": dataTestId, role: "alert" }, renderContent()));
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
exports.default = Notification;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { MouseEvent, ReactNode } from 'react';
|
|
1
|
+
import type { MouseEvent, ReactNode, KeyboardEvent } from 'react';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
|
|
3
|
+
type AriaLandmarkRoles = 'none' | 'main' | 'navigation' | 'banner' | 'complementary' | 'contentinfo' | 'form' | 'region' | 'search';
|
|
4
|
+
type Html5Tag = 'main' | 'div' | 'section' | 'article' | 'aside' | 'nav' | 'header' | 'footer';
|
|
5
|
+
interface BaseProps {
|
|
4
6
|
/**
|
|
5
7
|
* Unique ID for the component
|
|
6
8
|
*/
|
|
@@ -13,6 +15,11 @@ interface Props {
|
|
|
13
15
|
* On overlay element mouse click
|
|
14
16
|
*/
|
|
15
17
|
onClick?: (e: MouseEvent<HTMLElement>) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Callback function for keydown events on the overlay element to be used with prop `onClick`.
|
|
20
|
+
* This can be used to handle keyboard interactions, such as closing the overlay with the Escape key.
|
|
21
|
+
*/
|
|
22
|
+
onKeyDown?: (e: KeyboardEvent<HTMLElement>) => void;
|
|
16
23
|
/**
|
|
17
24
|
* Allows to set DOM node to which Overlay component will be appended. The node must already exist.
|
|
18
25
|
* Use `false` to render in place without use of `createPortal`
|
|
@@ -42,8 +49,62 @@ interface Props {
|
|
|
42
49
|
* Allows to pass a custom className
|
|
43
50
|
*/
|
|
44
51
|
className?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Specifies the HTML5 tag to be used for the content wrapper.
|
|
54
|
+
* It is recommended to use native semantic HTML elements whenever possible.
|
|
55
|
+
* If a suitable semantic element is not available, a `<div>` with an appropriate ARIA role should be used (role from `role`).
|
|
56
|
+
* ARIA roles from https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles
|
|
57
|
+
*
|
|
58
|
+
* @param {Html5Tag} main Uses `<main>` tag for the content wrapper.
|
|
59
|
+
* @param {Html5Tag} div Uses `<div>` tag for the content wrapper.
|
|
60
|
+
* @param {Html5Tag} section Uses `<section>` tag for the content wrapper.
|
|
61
|
+
* @param {Html5Tag} article Uses `<article>` tag for the content wrapper.
|
|
62
|
+
* @param {Html5Tag} aside Uses `<aside>` tag for the content wrapper.
|
|
63
|
+
* @param {Html5Tag} nav Uses `<nav>` tag for the content wrapper.
|
|
64
|
+
* @param {Html5Tag} header Uses `<header>` tag for the content wrapper.
|
|
65
|
+
* @param {Html5Tag} footer Uses `<footer>` tag for the content wrapper.
|
|
66
|
+
*
|
|
67
|
+
* @default 'section'
|
|
68
|
+
*/
|
|
69
|
+
as?: Html5Tag;
|
|
70
|
+
/**
|
|
71
|
+
* Specifies the ARIA landmark role for the content wrapper and is used when `as` is a div.
|
|
72
|
+
* It is always recommended to use native semantic HTML elements from `as` whenever possible.
|
|
73
|
+
* ARIA roles from https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles
|
|
74
|
+
*
|
|
75
|
+
* @param {AriaLandmarkRoles} main Represents the main content with `role="main"` (acting as <main>).
|
|
76
|
+
* @param {AriaLandmarkRoles} navigation Represents navigation links with `role="navigation"` (acting as <nav>).
|
|
77
|
+
* @param {AriaLandmarkRoles} banner Represents introductory content with `role="banner"` (acting as <header>).
|
|
78
|
+
* @param {AriaLandmarkRoles} complementary Represents related content with `role="complementary"` (acting as <aside>).
|
|
79
|
+
* @param {AriaLandmarkRoles} contentinfo Represents footer information with `role="contentinfo"` (acting as <footer>).
|
|
80
|
+
* @param {AriaLandmarkRoles} form Indicates an interactive form with `role="form"` (acting as <form>).
|
|
81
|
+
* @param {AriaLandmarkRoles} region Represents an important section with `role="region"` (acting as <section>).
|
|
82
|
+
* @param {AriaLandmarkRoles} search Represents a search area with `role="search"` (acting as <search>).
|
|
83
|
+
*/
|
|
84
|
+
role?: AriaLandmarkRoles;
|
|
85
|
+
/**
|
|
86
|
+
* If a role from `role` is used more than once on a page, the aria-label attribute should also be used in order to distinguish between the two regions.
|
|
87
|
+
*/
|
|
88
|
+
ariaLabel?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Ensures that overlays maintain proper focus management and accessibility.
|
|
91
|
+
* If `isFocusTrapped` is true, an appropriate ARIA role should be used (role from `focusableTrappedWidgetRole`).
|
|
92
|
+
* If `isFocusTrapped` is false, the tabbable element will not have an ARIA role.
|
|
93
|
+
*
|
|
94
|
+
* @default true
|
|
95
|
+
*/
|
|
96
|
+
isFocusTrapped?: boolean;
|
|
45
97
|
}
|
|
98
|
+
type DivProps = BaseProps & {
|
|
99
|
+
as?: 'div';
|
|
100
|
+
role: AriaLandmarkRoles;
|
|
101
|
+
};
|
|
102
|
+
type NonDivProps = BaseProps & {
|
|
103
|
+
as?: Exclude<Html5Tag, 'div'>;
|
|
104
|
+
role?: undefined;
|
|
105
|
+
};
|
|
106
|
+
type Props = DivProps | NonDivProps;
|
|
46
107
|
/** @visibleName Overlay */
|
|
47
|
-
declare const Overlay: ({ portalContainer: portalContainerSelector, appElement: appElementSelector, preventBodyScroll, "data-testid": dataTestId, ...props }: Props) => React.JSX.Element;
|
|
108
|
+
declare const Overlay: ({ portalContainer: portalContainerSelector, appElement: appElementSelector, preventBodyScroll, "data-testid": dataTestId, as, role, isFocusTrapped, ...props }: Props) => React.JSX.Element;
|
|
48
109
|
/** @component */
|
|
49
110
|
export default Overlay;
|
|
@@ -33,28 +33,31 @@ const Element = styled.default.div `
|
|
|
33
33
|
`;
|
|
34
34
|
/** @visibleName Overlay */
|
|
35
35
|
const Overlay = (_a) => {
|
|
36
|
-
var { portalContainer: portalContainerSelector = 'body', appElement: appElementSelector = '#__next', preventBodyScroll = true, 'data-testid': dataTestId } = _a, props = tslib.__rest(_a, ["portalContainer", "appElement", "preventBodyScroll", 'data-testid']);
|
|
36
|
+
var { portalContainer: portalContainerSelector = 'body', appElement: appElementSelector = '#__next', preventBodyScroll = true, 'data-testid': dataTestId, as = 'section', role, isFocusTrapped = true } = _a, props = tslib.__rest(_a, ["portalContainer", "appElement", "preventBodyScroll", 'data-testid', "as", "role", "isFocusTrapped"]);
|
|
37
37
|
const contentRef = React.useRef(null);
|
|
38
38
|
const portalContainer = portalContainerSelector
|
|
39
39
|
? document.querySelector(portalContainerSelector)
|
|
40
40
|
: null;
|
|
41
41
|
const appElement = document.querySelector(appElementSelector);
|
|
42
|
-
const overlayElement = (React__default.default.createElement(Element, { id: props.id, onClick: props.onClick, className: props.className, "data-testid": dataTestId },
|
|
43
|
-
React__default.default.createElement("div", { ref: contentRef, tabIndex: -1 }, props.children)));
|
|
42
|
+
const overlayElement = (React__default.default.createElement(Element, { id: props.id, onClick: props.onClick, className: props.className, "data-testid": dataTestId, onKeyDown: props.onKeyDown, "aria-label": props.ariaLabel, as: as, role: as === 'div' ? role : undefined },
|
|
43
|
+
React__default.default.createElement("div", { ref: contentRef, tabIndex: -1, "data-testid": dataTestId ? `${dataTestId}-content` : '' }, props.children)));
|
|
44
44
|
React.useEffect(() => {
|
|
45
45
|
if (preventBodyScroll) {
|
|
46
46
|
document.body.style.setProperty('overflow', 'hidden');
|
|
47
|
+
document.body.style.setProperty('position', 'relative');
|
|
48
|
+
document.body.style.setProperty('top', '0');
|
|
49
|
+
document.body.style.setProperty('left', '0');
|
|
47
50
|
return () => {
|
|
48
51
|
document.body.style.removeProperty('overflow');
|
|
49
52
|
};
|
|
50
53
|
}
|
|
51
54
|
return undefined;
|
|
52
|
-
});
|
|
55
|
+
}, [preventBodyScroll]);
|
|
53
56
|
React.useEffect(() => {
|
|
54
57
|
var _a;
|
|
55
58
|
if (appElement) {
|
|
56
59
|
const focusTrapStart = document.createElement('div');
|
|
57
|
-
focusTrapStart.setAttribute('tabindex', '0');
|
|
60
|
+
focusTrapStart.setAttribute('tabindex', isFocusTrapped ? '0' : '-1');
|
|
58
61
|
focusTrapStart.addEventListener('focus', () => {
|
|
59
62
|
var _a;
|
|
60
63
|
(_a = contentRef === null || contentRef === void 0 ? void 0 : contentRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
@@ -67,15 +70,15 @@ const Overlay = (_a) => {
|
|
|
67
70
|
(_a = contentRef === null || contentRef === void 0 ? void 0 : contentRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
68
71
|
document.body.prepend(focusTrapStart);
|
|
69
72
|
document.body.append(focusTrapEnd);
|
|
70
|
-
appElement.setAttribute('
|
|
73
|
+
appElement.setAttribute('inert', 'true');
|
|
71
74
|
return () => {
|
|
72
75
|
document.body.removeChild(focusTrapStart);
|
|
73
76
|
document.body.removeChild(focusTrapEnd);
|
|
74
|
-
appElement.removeAttribute('
|
|
77
|
+
appElement.removeAttribute('inert');
|
|
75
78
|
};
|
|
76
79
|
}
|
|
77
80
|
return undefined;
|
|
78
|
-
});
|
|
81
|
+
}, [appElement, contentRef, isFocusTrapped]);
|
|
79
82
|
return portalContainerSelector === false
|
|
80
83
|
? overlayElement
|
|
81
84
|
: reactDom.createPortal(overlayElement, portalContainer !== null && portalContainer !== void 0 ? portalContainer : document.body);
|
|
@@ -41,10 +41,6 @@ interface Props {
|
|
|
41
41
|
* Allows to pass a custom className
|
|
42
42
|
*/
|
|
43
43
|
className?: string;
|
|
44
|
-
/**
|
|
45
|
-
* Allows to pass a custom aria-label for mobile dropdown button
|
|
46
|
-
*/
|
|
47
|
-
dropdownButtonAriaLabel?: string;
|
|
48
44
|
/**
|
|
49
45
|
* Allows to pass testid string for testing purposes
|
|
50
46
|
*/
|
|
@@ -276,14 +276,20 @@ const PriorityNavigation = (_a) => {
|
|
|
276
276
|
});
|
|
277
277
|
React.useEffect(() => {
|
|
278
278
|
if (!isMobile) {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
279
|
+
requestAnimationFrame(() => {
|
|
280
|
+
dispatch({
|
|
281
|
+
type: 'resetNavigationState',
|
|
282
|
+
payload: {
|
|
283
|
+
navigationItems: props.children,
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
setTimeout(() => {
|
|
287
|
+
checkHorizontalOverflow();
|
|
288
|
+
}, 0);
|
|
284
289
|
});
|
|
285
290
|
}
|
|
286
291
|
setIsMobileNavigationOpen(false);
|
|
292
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
287
293
|
}, [isMobile, props.children]);
|
|
288
294
|
React.useLayoutEffect(() => {
|
|
289
295
|
if (!isMobile) {
|
|
@@ -299,31 +305,18 @@ const PriorityNavigation = (_a) => {
|
|
|
299
305
|
checkHorizontalOverflow,
|
|
300
306
|
]);
|
|
301
307
|
const handleNavigationListKeyDown = React.useCallback((e) => {
|
|
302
|
-
var _a;
|
|
303
|
-
if (isMobile && isMobileNavigationOpen) {
|
|
308
|
+
var _a, _b;
|
|
309
|
+
if (isMobile && isMobileNavigationOpen && e.key === 'Tab') {
|
|
304
310
|
const focusableElements = (_a = navigationListRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll('a, button, input, [tabindex]:not([tabindex="-1"])');
|
|
305
311
|
const lastElement = focusableElements === null || focusableElements === void 0 ? void 0 : focusableElements[focusableElements.length - 1];
|
|
306
|
-
|
|
312
|
+
const goingForward = !e.shiftKey;
|
|
313
|
+
if (goingForward && document.activeElement === lastElement) {
|
|
307
314
|
e.preventDefault();
|
|
308
315
|
setIsMobileNavigationOpen(false);
|
|
316
|
+
(_b = dropdownButtonRef.current) === null || _b === void 0 ? void 0 : _b.focus();
|
|
309
317
|
}
|
|
310
318
|
}
|
|
311
319
|
}, [isMobile, isMobileNavigationOpen]);
|
|
312
|
-
const handleNavigationListFocus = React.useCallback(() => {
|
|
313
|
-
if (isMobile && isMobileNavigationOpen) {
|
|
314
|
-
setTimeout(() => {
|
|
315
|
-
var _a, _b;
|
|
316
|
-
const focusableElements = (_a = navigationListRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll('a, button, input, [tabindex]:not([tabindex="-1"])');
|
|
317
|
-
if (!focusableElements || focusableElements.length === 0)
|
|
318
|
-
return;
|
|
319
|
-
const lastElement = focusableElements[focusableElements.length - 1];
|
|
320
|
-
if (document.activeElement === lastElement) {
|
|
321
|
-
setIsMobileNavigationOpen(false);
|
|
322
|
-
(_b = dropdownButtonRef.current) === null || _b === void 0 ? void 0 : _b.focus();
|
|
323
|
-
}
|
|
324
|
-
}, 0);
|
|
325
|
-
}
|
|
326
|
-
}, [isMobile, isMobileNavigationOpen]);
|
|
327
320
|
const handleItemClick = (e) => {
|
|
328
321
|
setIsMobileNavigationOpen(false);
|
|
329
322
|
if (props.onClick) {
|
|
@@ -339,7 +332,7 @@ const PriorityNavigation = (_a) => {
|
|
|
339
332
|
props.categoryLabel && React__default.default.createElement(Category, null, props.categoryLabel),
|
|
340
333
|
selectedItem),
|
|
341
334
|
React__default.default.createElement(Icon.default, { icon: isMobileNavigationOpen ? icons.OvalChevronUp : icons.OvalChevronDown, size: "2.5rem" }))),
|
|
342
|
-
React__default.default.createElement(NavigationList, { ref: navigationListRef, isMobileNavigationOpen: isMobileNavigationOpen, onKeyDown: handleNavigationListKeyDown
|
|
335
|
+
React__default.default.createElement(NavigationList, { ref: navigationListRef, isMobileNavigationOpen: isMobileNavigationOpen, onKeyDown: handleNavigationListKeyDown }, React.Children.map([...state.navigationItems, ...(isMobile ? state.dropdownItems : [])], (navigationItem, index) => {
|
|
343
336
|
if (React.isValidElement(navigationItem) &&
|
|
344
337
|
navigationItem.type === PriorityNavigationItem.default) {
|
|
345
338
|
return (React__default.default.createElement(PriorityNavigationItem.default, { id: navigationItem.props.id, key: navigationItem.key, onClick: handleItemClick, onKeyDown: navigationItem.props.onKeyDown || props.onKeyDown, isActive: navigationItem.props.isActive, className: navigationItem.props.className, "data-testid": navigationItem.props['data-testid'], ref: instance => {
|
|
@@ -361,8 +354,9 @@ const PriorityNavigation = (_a) => {
|
|
|
361
354
|
!isMobile && Boolean(state.dropdownItems.length) && (React__default.default.createElement(React__default.default.Fragment, null,
|
|
362
355
|
React__default.default.createElement("div", null,
|
|
363
356
|
React__default.default.createElement(ButtonIcon.default, { ref: dropdownButtonRef, ariaLabel: openMoreSubpagesAriaLabel, ariaExpanded: isDropdownListOpen, onClick: toggleDropdown, icon: isDropdownListOpen ? icons.ChevronUp : icons.ChevronDown, isReversed: true, "data-testid": "dropdown-button" }, dropdownButtonLabel)),
|
|
364
|
-
React__default.default.createElement(DropdownList, { isDropdownListOpen: isDropdownListOpen }, state.dropdownItems.map(dropdownItem => React.isValidElement(dropdownItem) &&
|
|
365
|
-
dropdownItem.type === PriorityNavigationItem.default && (React__default.default.createElement(PriorityNavigationItem.default, { id: dropdownItem.props.id, key: dropdownItem.key, onClick: dropdownItem.props.onClick || props.onClick, onKeyDown: dropdownItem.props.onKeyDown ||
|
|
357
|
+
isDropdownListOpen && (React__default.default.createElement(DropdownList, { isDropdownListOpen: isDropdownListOpen }, state.dropdownItems.map(dropdownItem => React.isValidElement(dropdownItem) &&
|
|
358
|
+
dropdownItem.type === PriorityNavigationItem.default && (React__default.default.createElement(PriorityNavigationItem.default, { id: dropdownItem.props.id, key: dropdownItem.key, onClick: dropdownItem.props.onClick || props.onClick, onKeyDown: dropdownItem.props.onKeyDown ||
|
|
359
|
+
props.onKeyDown, isActive: dropdownItem.props.isActive, className: dropdownItem.props.className, "data-testid": dropdownItem.props['data-testid'] }, dropdownItem.props.children)))))))))));
|
|
366
360
|
};
|
|
367
361
|
|
|
368
362
|
exports.default = PriorityNavigation;
|
|
@@ -18,6 +18,7 @@ export { default as Divider } from './Divider/Divider';
|
|
|
18
18
|
export { default as DnaLogo } from './DnaLogo/DnaLogo';
|
|
19
19
|
export { default as Drawer } from './Drawer/Drawer';
|
|
20
20
|
export { default as EmptyState } from './EmptyState/EmptyState';
|
|
21
|
+
export { default as EnergyLabel } from './EnergyLabel/EnergyLabel';
|
|
21
22
|
export { default as Expander } from './Expander/Expander';
|
|
22
23
|
export { default as Floater } from './Floater/Floater';
|
|
23
24
|
export { default as Footer } from './Footer/Footer';
|
package/build/cjs/index.js
CHANGED
|
@@ -20,6 +20,7 @@ var Divider = require('./components/Divider/Divider.js');
|
|
|
20
20
|
var DnaLogo = require('./components/DnaLogo/DnaLogo.js');
|
|
21
21
|
var Drawer = require('./components/Drawer/Drawer.js');
|
|
22
22
|
var EmptyState = require('./components/EmptyState/EmptyState.js');
|
|
23
|
+
var EnergyLabel = require('./components/EnergyLabel/EnergyLabel.js');
|
|
23
24
|
var Expander = require('./components/Expander/Expander.js');
|
|
24
25
|
var Floater = require('./components/Floater/Floater.js');
|
|
25
26
|
var Footer = require('./components/Footer/Footer.js');
|
|
@@ -189,6 +190,7 @@ exports.Divider = Divider.default;
|
|
|
189
190
|
exports.DnaLogo = DnaLogo.default;
|
|
190
191
|
exports.Drawer = Drawer.default;
|
|
191
192
|
exports.EmptyState = EmptyState.default;
|
|
193
|
+
exports.EnergyLabel = EnergyLabel.default;
|
|
192
194
|
exports.Expander = Expander.default;
|
|
193
195
|
exports.Floater = Floater.default;
|
|
194
196
|
exports.Footer = Footer.default;
|
|
@@ -65,6 +65,10 @@ interface Props {
|
|
|
65
65
|
* e.g., "important information" or "example."
|
|
66
66
|
*/
|
|
67
67
|
ariaLabel?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Screen reader live region policy for the box content
|
|
70
|
+
*/
|
|
71
|
+
ariaLive?: 'off' | 'polite' | 'assertive';
|
|
68
72
|
/**
|
|
69
73
|
* Allows to pass a role to the component
|
|
70
74
|
*/
|
|
@@ -17,7 +17,7 @@ const BoxWrapper = styled__default.div `
|
|
|
17
17
|
`;
|
|
18
18
|
const Box = (_a) => {
|
|
19
19
|
var { elevation = 'none', 'data-testid': dataTestId } = _a, props = __rest(_a, ["elevation", 'data-testid']);
|
|
20
|
-
return (React__default.createElement(BoxWrapper, { id: props.id, "$elevation": elevation, shadow: props.shadow, width: props.width, height: props.height, margin: props.margin, padding: props.padding, className: props.className, "data-testid": dataTestId, role: props.role, "aria-label": props.ariaLabel }, props.children));
|
|
20
|
+
return (React__default.createElement(BoxWrapper, { id: props.id, "$elevation": elevation, shadow: props.shadow, width: props.width, height: props.height, margin: props.margin, padding: props.padding, className: props.className, "data-testid": dataTestId, role: props.role, "aria-label": props.ariaLabel, "aria-live": props.ariaLive }, props.children));
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
export { Box as default };
|
|
@@ -24,6 +24,10 @@ export interface Props {
|
|
|
24
24
|
* Allows to change the type of resulting HTML element from button (`<button></button>`) to anchor (`<a href="..."></a>`)
|
|
25
25
|
*/
|
|
26
26
|
href?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Allows to set the target attribute for the link
|
|
29
|
+
*/
|
|
30
|
+
target?: '_self' | '_blank' | '_parent' | '_top';
|
|
27
31
|
/**
|
|
28
32
|
* Content of Button component
|
|
29
33
|
*/
|
|
@@ -114,7 +114,7 @@ const Element = styled__default.button `
|
|
|
114
114
|
/** @visibleName Button */
|
|
115
115
|
const Button = (_a) => {
|
|
116
116
|
var { type = 'submit', 'data-testid': dataTestId, 'data-no-close': dataNoClose, 'data-track-value': dataTrackValue, 'aria-label': ariaLabel } = _a, props = __rest(_a, ["type", 'data-testid', 'data-no-close', 'data-track-value', 'aria-label']);
|
|
117
|
-
return (React__default.createElement(Element, Object.assign({ id: props.id, as: props.href ? 'a' : undefined, type: props.href ? undefined : type, href: props.href, onClick: props.onClick, onMouseDown: props.onMouseDown, small: props.small, darkBg: props.darkBg, fullWidth: props.fullWidth, "$loading": props.loading, tabIndex: props.loading ? -1 : 0, "data-loading": props.loading, className: props.className, "data-testid": dataTestId, "data-no-close": dataNoClose, "data-track-value": dataTrackValue, "aria-label": ariaLabel }, props.dataAttributes, (!props.href && {
|
|
117
|
+
return (React__default.createElement(Element, Object.assign({ id: props.id, as: props.href ? 'a' : undefined, type: props.href ? undefined : type, href: props.href, target: props.href ? props.target : undefined, rel: props.target === '_blank' ? 'noopener noreferrer' : undefined, onClick: props.onClick, onMouseDown: props.onMouseDown, small: props.small, darkBg: props.darkBg, fullWidth: props.fullWidth, "$loading": props.loading, tabIndex: props.loading ? -1 : 0, "data-loading": props.loading, className: props.className, "data-testid": dataTestId, "data-no-close": dataNoClose, "data-track-value": dataTrackValue, "aria-label": ariaLabel }, props.dataAttributes, (!props.href && {
|
|
118
118
|
name: props.name,
|
|
119
119
|
disabled: props.disabled,
|
|
120
120
|
})), props.loading ? (React__default.createElement(PixelLoader, { color: props.darkBg ? theme.color.default.white : theme.color.default.plum, label: props.loadingLabel })) : (React__default.createElement("span", { "data-testid": dataTestId && `${dataTestId}-text`, "data-no-close": dataNoClose }, props.children))));
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { __rest } from 'tslib';
|
|
2
2
|
import { ChevronUp, ChevronRight, ChevronDown, ChevronLeft } from '@dnanpm/icons';
|
|
3
|
-
import React__default, { isValidElement } from 'react';
|
|
3
|
+
import React__default, { isValidElement, cloneElement } from 'react';
|
|
4
4
|
import styled__default from 'styled-components';
|
|
5
5
|
import theme from '../../themes/theme.js';
|
|
6
6
|
import { getMultipliedSize } from '../../utils/styledUtils.js';
|
|
7
7
|
import ButtonPrimary from '../ButtonPrimary/ButtonPrimary.js';
|
|
8
8
|
import ButtonSecondary from '../ButtonSecondary/ButtonSecondary.js';
|
|
9
|
-
import Icon from '../Icon/Icon.js';
|
|
10
9
|
|
|
11
10
|
const iconsMap = {
|
|
12
11
|
up: ChevronUp,
|
|
@@ -64,7 +63,15 @@ const buttonsMap = {
|
|
|
64
63
|
const ButtonArrow = (_a) => {
|
|
65
64
|
var { variant = 'primary', 'data-testid': dataTestId, 'data-track-value': dataTrackValue, 'aria-label': ariaLabel } = _a, props = __rest(_a, ["variant", 'data-testid', 'data-track-value', 'aria-label']);
|
|
66
65
|
const Element = buttonsMap[variant];
|
|
67
|
-
|
|
66
|
+
let iconElement = null;
|
|
67
|
+
if (props.direction) {
|
|
68
|
+
const IconComponent = iconsMap[props.direction];
|
|
69
|
+
iconElement = React__default.createElement(IconComponent, { size: "1rem" });
|
|
70
|
+
}
|
|
71
|
+
else if (isValidElement(props.icon)) {
|
|
72
|
+
iconElement = cloneElement(props.icon, { size: '1rem' });
|
|
73
|
+
}
|
|
74
|
+
return (React__default.createElement(Element, { id: props.id, href: props.href, name: props.name, type: props.type, onClick: props.onClick, onMouseDown: props.onMouseDown, darkBg: props.darkBg, disabled: props.disabled, className: props.className, "data-testid": dataTestId, "data-track-value": dataTrackValue, dataAttributes: props.dataAttributes, "aria-label": ariaLabel }, iconElement));
|
|
68
75
|
};
|
|
69
76
|
|
|
70
77
|
export { ButtonArrow as default };
|
|
@@ -176,7 +176,8 @@ const Carousel = (_a) => {
|
|
|
176
176
|
};
|
|
177
177
|
const visibleItems = props.visibleItems || calculatedItems;
|
|
178
178
|
const slidesWrapperGapSizePx = 20;
|
|
179
|
-
const
|
|
179
|
+
const slidesCount = Children.count(props.children);
|
|
180
|
+
const slideScreensCount = Math.max(1, slidesCount - Math.floor(visibleItems) + 1);
|
|
180
181
|
const step = getStep((_b = props.swipeStep) !== null && _b !== void 0 ? _b : 1, visibleItems);
|
|
181
182
|
const currentStepIndex = Math.ceil(currentIndex / step);
|
|
182
183
|
const totalSwipeSteps = Math.ceil(slideScreensCount / step + ((slideScreensCount - 1) % step !== 0 ? 1 : 0));
|
|
@@ -298,6 +299,8 @@ const Carousel = (_a) => {
|
|
|
298
299
|
setIsSwiping(false);
|
|
299
300
|
};
|
|
300
301
|
const handleSlidesPointerDown = (e) => {
|
|
302
|
+
if (e.button !== 0)
|
|
303
|
+
return;
|
|
301
304
|
if (slidesWrapperRef.current && scrollbarRef.current && knobRef.current) {
|
|
302
305
|
data.startX = e.pageX;
|
|
303
306
|
data.startTime = Date.now();
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface Props {
|
|
3
|
+
/**
|
|
4
|
+
* Unique ID for the component
|
|
5
|
+
*/
|
|
6
|
+
id?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Allows to pass a custom className
|
|
9
|
+
*/
|
|
10
|
+
className?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Allows to pass testid string for testing purposes
|
|
13
|
+
*/
|
|
14
|
+
'data-testid'?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Allows to pass a screen reader label to the component
|
|
17
|
+
*/
|
|
18
|
+
ariaLabel?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Allows to set property `aria-label` for `ButtonClose` element
|
|
21
|
+
*/
|
|
22
|
+
closeButtonLabel?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Allows to set the source of the badge image
|
|
25
|
+
*/
|
|
26
|
+
badgeSrc: string;
|
|
27
|
+
/**
|
|
28
|
+
* Allows to set the title of the modal
|
|
29
|
+
*/
|
|
30
|
+
modalTitle?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Allows to set the source of the image in the modal
|
|
33
|
+
*/
|
|
34
|
+
modalImageSrc?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Allows to set the label of the modal
|
|
37
|
+
*/
|
|
38
|
+
modalLabel?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Allows to set the label of the button that opens the PDF
|
|
41
|
+
*/
|
|
42
|
+
pdfButtonLabel?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Allows to set the href of the PDF file
|
|
45
|
+
*/
|
|
46
|
+
pdfHref?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Allows to set the label for the error state
|
|
49
|
+
*/
|
|
50
|
+
errorLabel?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Allows to set the title for the error state
|
|
53
|
+
*/
|
|
54
|
+
errorTitle?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Allows to hide application from assistive screenreaders and other assistive technologies while the modal is open
|
|
57
|
+
*
|
|
58
|
+
* @default '#__next'
|
|
59
|
+
*/
|
|
60
|
+
modalAppElement?: string;
|
|
61
|
+
}
|
|
62
|
+
/** @visibleName Energy Label */
|
|
63
|
+
declare const EnergyLabel: ({ "data-testid": dataTestId, modalAppElement, ...props }: Props) => React.JSX.Element | null;
|
|
64
|
+
/** @component */
|
|
65
|
+
export default EnergyLabel;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { __rest } from 'tslib';
|
|
2
|
+
import React__default, { useState } from 'react';
|
|
3
|
+
import { Open } from '@dnanpm/icons';
|
|
4
|
+
import Modal from '../Modal/Modal.js';
|
|
5
|
+
import styled from '../../themes/styled.js';
|
|
6
|
+
import { media, getMultipliedSize } from '../../utils/styledUtils.js';
|
|
7
|
+
import Button from '../Button/Button.js';
|
|
8
|
+
import theme from '../../themes/theme.js';
|
|
9
|
+
|
|
10
|
+
const ERROR_IMAGE = 'https://res.cloudinary.com/dnaoyj/image/upload/v1731073400/Assets/KLT/Enriched%20icons/enriched-error-state.png';
|
|
11
|
+
const ButtonElement = styled.button `
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
background-color: transparent;
|
|
16
|
+
border: 0;
|
|
17
|
+
padding: 0;
|
|
18
|
+
cursor: pointer;
|
|
19
|
+
`;
|
|
20
|
+
const StyledModal = styled(Modal) `
|
|
21
|
+
> div > div {
|
|
22
|
+
max-height: 100%;
|
|
23
|
+
height: 100%;
|
|
24
|
+
width: 100%;
|
|
25
|
+
margin: 0;
|
|
26
|
+
|
|
27
|
+
${media.xs `
|
|
28
|
+
width: 23.438rem;
|
|
29
|
+
height: 50rem;
|
|
30
|
+
border-radius: ${theme.radius.default};
|
|
31
|
+
`};
|
|
32
|
+
|
|
33
|
+
> div {
|
|
34
|
+
border-radius: 0;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
const ModalLabel = styled.p `
|
|
39
|
+
font-size: ${theme.fontSize.s};
|
|
40
|
+
margin: 0;
|
|
41
|
+
`;
|
|
42
|
+
const ButtonContent = styled.span `
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
gap: ${getMultipliedSize(theme.base.baseWidth, 0.5)};
|
|
46
|
+
`;
|
|
47
|
+
const ErrorContainer = styled.div `
|
|
48
|
+
display: flex;
|
|
49
|
+
padding-top: ${getMultipliedSize(theme.base.baseHeight, 2)};
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
text-align: center;
|
|
52
|
+
align-items: center;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
gap: ${getMultipliedSize(theme.base.baseHeight, 3)};
|
|
55
|
+
`;
|
|
56
|
+
const Image = styled.img `
|
|
57
|
+
width: ${p => p.imgWidth};
|
|
58
|
+
`;
|
|
59
|
+
/** @visibleName Energy Label */
|
|
60
|
+
const EnergyLabel = (_a) => {
|
|
61
|
+
var { 'data-testid': dataTestId, modalAppElement = '#__next' } = _a, props = __rest(_a, ['data-testid', "modalAppElement"]);
|
|
62
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
63
|
+
const [hasError, setHasError] = useState(false);
|
|
64
|
+
const [imgError, setImgError] = useState(false);
|
|
65
|
+
const handleOpen = () => {
|
|
66
|
+
setIsOpen(true);
|
|
67
|
+
};
|
|
68
|
+
const handleClose = () => {
|
|
69
|
+
setIsOpen(false);
|
|
70
|
+
};
|
|
71
|
+
const renderFooter = () => (React__default.createElement(Button, { fullWidth: true, href: props.pdfHref, target: "_blank" },
|
|
72
|
+
React__default.createElement(ButtonContent, null,
|
|
73
|
+
props.pdfButtonLabel,
|
|
74
|
+
" ",
|
|
75
|
+
React__default.createElement(Open, null))));
|
|
76
|
+
if (imgError) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
return (React__default.createElement(React__default.Fragment, null,
|
|
80
|
+
React__default.createElement(ButtonElement, { onClick: handleOpen, "aria-label": props.ariaLabel, "data-testid": dataTestId, id: props.id, className: props.className },
|
|
81
|
+
React__default.createElement(Image, { src: props.badgeSrc, alt: "", "aria-hidden": true, imgWidth: "55px", onError: () => setImgError(true) })),
|
|
82
|
+
React__default.createElement(StyledModal, { appElement: modalAppElement, isOpen: isOpen, onRequestClose: handleClose, title: props.modalTitle, closeLabel: props.closeButtonLabel, footer: renderFooter() }, hasError || !props.modalImageSrc ? (React__default.createElement(ErrorContainer, null,
|
|
83
|
+
React__default.createElement("h3", null, props.errorTitle),
|
|
84
|
+
React__default.createElement(Image, { src: ERROR_IMAGE, alt: "", imgWidth: "160px" }),
|
|
85
|
+
React__default.createElement("p", null, props.errorLabel))) : (React__default.createElement(React__default.Fragment, null,
|
|
86
|
+
React__default.createElement(Image, { src: props.modalImageSrc, alt: "", imgWidth: "100%", onError: () => setHasError(true) }),
|
|
87
|
+
React__default.createElement(ModalLabel, null, props.modalLabel))))));
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export { EnergyLabel as default };
|
|
@@ -24,7 +24,7 @@ const NotificationWrapper = styled__default.div `
|
|
|
24
24
|
${sharedStyles}
|
|
25
25
|
border-color: ${({ $type }) => theme.color.notification[$type]};
|
|
26
26
|
`;
|
|
27
|
-
const StaticWrapper = styled__default.
|
|
27
|
+
const StaticWrapper = styled__default.div `
|
|
28
28
|
${sharedStyles}
|
|
29
29
|
border-color: ${({ $type }) => theme.color.notification[$type]};
|
|
30
30
|
`;
|
|
@@ -34,7 +34,7 @@ const IconWrapper = styled__default.div `
|
|
|
34
34
|
padding: 0.5rem;
|
|
35
35
|
background-color: ${({ $type }) => theme.color.notification[$type]};
|
|
36
36
|
`;
|
|
37
|
-
const ContentWrapper = styled__default.
|
|
37
|
+
const ContentWrapper = styled__default.div `
|
|
38
38
|
margin: auto 0;
|
|
39
39
|
padding: 0.5rem 0;
|
|
40
40
|
width: 100%;
|
|
@@ -59,7 +59,7 @@ const Notification = ({ type = 'info', 'data-testid': dataTestId, isStatic = fal
|
|
|
59
59
|
React__default.createElement(ContentWrapper, null, children),
|
|
60
60
|
closeButton && (React__default.createElement(ButtonCloseStyled, { onClick: onClickCloseButton, "aria-label": closeButtonLabel },
|
|
61
61
|
React__default.createElement(Icon, { icon: Close, color: "currentColor", "aria-hidden": true })))));
|
|
62
|
-
return isStatic ? (React__default.createElement(StaticWrapper, { "$type": type, className: className, "data-testid": dataTestId, "aria-label": ariaLabel }, renderContent())) : (React__default.createElement(NotificationWrapper, { "$type": type, className: className, "data-testid": dataTestId, role: "alert" }, renderContent()));
|
|
62
|
+
return isStatic ? (React__default.createElement(StaticWrapper, { "$type": type, className: className, "data-testid": dataTestId, "aria-label": ariaLabel, role: "region" }, renderContent())) : (React__default.createElement(NotificationWrapper, { "$type": type, className: className, "data-testid": dataTestId, role: "alert" }, renderContent()));
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
export { Notification as default };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { MouseEvent, ReactNode } from 'react';
|
|
1
|
+
import type { MouseEvent, ReactNode, KeyboardEvent } from 'react';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
|
|
3
|
+
type AriaLandmarkRoles = 'none' | 'main' | 'navigation' | 'banner' | 'complementary' | 'contentinfo' | 'form' | 'region' | 'search';
|
|
4
|
+
type Html5Tag = 'main' | 'div' | 'section' | 'article' | 'aside' | 'nav' | 'header' | 'footer';
|
|
5
|
+
interface BaseProps {
|
|
4
6
|
/**
|
|
5
7
|
* Unique ID for the component
|
|
6
8
|
*/
|
|
@@ -13,6 +15,11 @@ interface Props {
|
|
|
13
15
|
* On overlay element mouse click
|
|
14
16
|
*/
|
|
15
17
|
onClick?: (e: MouseEvent<HTMLElement>) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Callback function for keydown events on the overlay element to be used with prop `onClick`.
|
|
20
|
+
* This can be used to handle keyboard interactions, such as closing the overlay with the Escape key.
|
|
21
|
+
*/
|
|
22
|
+
onKeyDown?: (e: KeyboardEvent<HTMLElement>) => void;
|
|
16
23
|
/**
|
|
17
24
|
* Allows to set DOM node to which Overlay component will be appended. The node must already exist.
|
|
18
25
|
* Use `false` to render in place without use of `createPortal`
|
|
@@ -42,8 +49,62 @@ interface Props {
|
|
|
42
49
|
* Allows to pass a custom className
|
|
43
50
|
*/
|
|
44
51
|
className?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Specifies the HTML5 tag to be used for the content wrapper.
|
|
54
|
+
* It is recommended to use native semantic HTML elements whenever possible.
|
|
55
|
+
* If a suitable semantic element is not available, a `<div>` with an appropriate ARIA role should be used (role from `role`).
|
|
56
|
+
* ARIA roles from https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles
|
|
57
|
+
*
|
|
58
|
+
* @param {Html5Tag} main Uses `<main>` tag for the content wrapper.
|
|
59
|
+
* @param {Html5Tag} div Uses `<div>` tag for the content wrapper.
|
|
60
|
+
* @param {Html5Tag} section Uses `<section>` tag for the content wrapper.
|
|
61
|
+
* @param {Html5Tag} article Uses `<article>` tag for the content wrapper.
|
|
62
|
+
* @param {Html5Tag} aside Uses `<aside>` tag for the content wrapper.
|
|
63
|
+
* @param {Html5Tag} nav Uses `<nav>` tag for the content wrapper.
|
|
64
|
+
* @param {Html5Tag} header Uses `<header>` tag for the content wrapper.
|
|
65
|
+
* @param {Html5Tag} footer Uses `<footer>` tag for the content wrapper.
|
|
66
|
+
*
|
|
67
|
+
* @default 'section'
|
|
68
|
+
*/
|
|
69
|
+
as?: Html5Tag;
|
|
70
|
+
/**
|
|
71
|
+
* Specifies the ARIA landmark role for the content wrapper and is used when `as` is a div.
|
|
72
|
+
* It is always recommended to use native semantic HTML elements from `as` whenever possible.
|
|
73
|
+
* ARIA roles from https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles
|
|
74
|
+
*
|
|
75
|
+
* @param {AriaLandmarkRoles} main Represents the main content with `role="main"` (acting as <main>).
|
|
76
|
+
* @param {AriaLandmarkRoles} navigation Represents navigation links with `role="navigation"` (acting as <nav>).
|
|
77
|
+
* @param {AriaLandmarkRoles} banner Represents introductory content with `role="banner"` (acting as <header>).
|
|
78
|
+
* @param {AriaLandmarkRoles} complementary Represents related content with `role="complementary"` (acting as <aside>).
|
|
79
|
+
* @param {AriaLandmarkRoles} contentinfo Represents footer information with `role="contentinfo"` (acting as <footer>).
|
|
80
|
+
* @param {AriaLandmarkRoles} form Indicates an interactive form with `role="form"` (acting as <form>).
|
|
81
|
+
* @param {AriaLandmarkRoles} region Represents an important section with `role="region"` (acting as <section>).
|
|
82
|
+
* @param {AriaLandmarkRoles} search Represents a search area with `role="search"` (acting as <search>).
|
|
83
|
+
*/
|
|
84
|
+
role?: AriaLandmarkRoles;
|
|
85
|
+
/**
|
|
86
|
+
* If a role from `role` is used more than once on a page, the aria-label attribute should also be used in order to distinguish between the two regions.
|
|
87
|
+
*/
|
|
88
|
+
ariaLabel?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Ensures that overlays maintain proper focus management and accessibility.
|
|
91
|
+
* If `isFocusTrapped` is true, an appropriate ARIA role should be used (role from `focusableTrappedWidgetRole`).
|
|
92
|
+
* If `isFocusTrapped` is false, the tabbable element will not have an ARIA role.
|
|
93
|
+
*
|
|
94
|
+
* @default true
|
|
95
|
+
*/
|
|
96
|
+
isFocusTrapped?: boolean;
|
|
45
97
|
}
|
|
98
|
+
type DivProps = BaseProps & {
|
|
99
|
+
as?: 'div';
|
|
100
|
+
role: AriaLandmarkRoles;
|
|
101
|
+
};
|
|
102
|
+
type NonDivProps = BaseProps & {
|
|
103
|
+
as?: Exclude<Html5Tag, 'div'>;
|
|
104
|
+
role?: undefined;
|
|
105
|
+
};
|
|
106
|
+
type Props = DivProps | NonDivProps;
|
|
46
107
|
/** @visibleName Overlay */
|
|
47
|
-
declare const Overlay: ({ portalContainer: portalContainerSelector, appElement: appElementSelector, preventBodyScroll, "data-testid": dataTestId, ...props }: Props) => React.JSX.Element;
|
|
108
|
+
declare const Overlay: ({ portalContainer: portalContainerSelector, appElement: appElementSelector, preventBodyScroll, "data-testid": dataTestId, as, role, isFocusTrapped, ...props }: Props) => React.JSX.Element;
|
|
48
109
|
/** @component */
|
|
49
110
|
export default Overlay;
|
|
@@ -25,28 +25,31 @@ const Element = styled.div `
|
|
|
25
25
|
`;
|
|
26
26
|
/** @visibleName Overlay */
|
|
27
27
|
const Overlay = (_a) => {
|
|
28
|
-
var { portalContainer: portalContainerSelector = 'body', appElement: appElementSelector = '#__next', preventBodyScroll = true, 'data-testid': dataTestId } = _a, props = __rest(_a, ["portalContainer", "appElement", "preventBodyScroll", 'data-testid']);
|
|
28
|
+
var { portalContainer: portalContainerSelector = 'body', appElement: appElementSelector = '#__next', preventBodyScroll = true, 'data-testid': dataTestId, as = 'section', role, isFocusTrapped = true } = _a, props = __rest(_a, ["portalContainer", "appElement", "preventBodyScroll", 'data-testid', "as", "role", "isFocusTrapped"]);
|
|
29
29
|
const contentRef = useRef(null);
|
|
30
30
|
const portalContainer = portalContainerSelector
|
|
31
31
|
? document.querySelector(portalContainerSelector)
|
|
32
32
|
: null;
|
|
33
33
|
const appElement = document.querySelector(appElementSelector);
|
|
34
|
-
const overlayElement = (React__default.createElement(Element, { id: props.id, onClick: props.onClick, className: props.className, "data-testid": dataTestId },
|
|
35
|
-
React__default.createElement("div", { ref: contentRef, tabIndex: -1 }, props.children)));
|
|
34
|
+
const overlayElement = (React__default.createElement(Element, { id: props.id, onClick: props.onClick, className: props.className, "data-testid": dataTestId, onKeyDown: props.onKeyDown, "aria-label": props.ariaLabel, as: as, role: as === 'div' ? role : undefined },
|
|
35
|
+
React__default.createElement("div", { ref: contentRef, tabIndex: -1, "data-testid": dataTestId ? `${dataTestId}-content` : '' }, props.children)));
|
|
36
36
|
useEffect(() => {
|
|
37
37
|
if (preventBodyScroll) {
|
|
38
38
|
document.body.style.setProperty('overflow', 'hidden');
|
|
39
|
+
document.body.style.setProperty('position', 'relative');
|
|
40
|
+
document.body.style.setProperty('top', '0');
|
|
41
|
+
document.body.style.setProperty('left', '0');
|
|
39
42
|
return () => {
|
|
40
43
|
document.body.style.removeProperty('overflow');
|
|
41
44
|
};
|
|
42
45
|
}
|
|
43
46
|
return undefined;
|
|
44
|
-
});
|
|
47
|
+
}, [preventBodyScroll]);
|
|
45
48
|
useEffect(() => {
|
|
46
49
|
var _a;
|
|
47
50
|
if (appElement) {
|
|
48
51
|
const focusTrapStart = document.createElement('div');
|
|
49
|
-
focusTrapStart.setAttribute('tabindex', '0');
|
|
52
|
+
focusTrapStart.setAttribute('tabindex', isFocusTrapped ? '0' : '-1');
|
|
50
53
|
focusTrapStart.addEventListener('focus', () => {
|
|
51
54
|
var _a;
|
|
52
55
|
(_a = contentRef === null || contentRef === void 0 ? void 0 : contentRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
@@ -59,15 +62,15 @@ const Overlay = (_a) => {
|
|
|
59
62
|
(_a = contentRef === null || contentRef === void 0 ? void 0 : contentRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
60
63
|
document.body.prepend(focusTrapStart);
|
|
61
64
|
document.body.append(focusTrapEnd);
|
|
62
|
-
appElement.setAttribute('
|
|
65
|
+
appElement.setAttribute('inert', 'true');
|
|
63
66
|
return () => {
|
|
64
67
|
document.body.removeChild(focusTrapStart);
|
|
65
68
|
document.body.removeChild(focusTrapEnd);
|
|
66
|
-
appElement.removeAttribute('
|
|
69
|
+
appElement.removeAttribute('inert');
|
|
67
70
|
};
|
|
68
71
|
}
|
|
69
72
|
return undefined;
|
|
70
|
-
});
|
|
73
|
+
}, [appElement, contentRef, isFocusTrapped]);
|
|
71
74
|
return portalContainerSelector === false
|
|
72
75
|
? overlayElement
|
|
73
76
|
: createPortal(overlayElement, portalContainer !== null && portalContainer !== void 0 ? portalContainer : document.body);
|
|
@@ -41,10 +41,6 @@ interface Props {
|
|
|
41
41
|
* Allows to pass a custom className
|
|
42
42
|
*/
|
|
43
43
|
className?: string;
|
|
44
|
-
/**
|
|
45
|
-
* Allows to pass a custom aria-label for mobile dropdown button
|
|
46
|
-
*/
|
|
47
|
-
dropdownButtonAriaLabel?: string;
|
|
48
44
|
/**
|
|
49
45
|
* Allows to pass testid string for testing purposes
|
|
50
46
|
*/
|
|
@@ -268,14 +268,20 @@ const PriorityNavigation = (_a) => {
|
|
|
268
268
|
});
|
|
269
269
|
useEffect(() => {
|
|
270
270
|
if (!isMobile) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
271
|
+
requestAnimationFrame(() => {
|
|
272
|
+
dispatch({
|
|
273
|
+
type: 'resetNavigationState',
|
|
274
|
+
payload: {
|
|
275
|
+
navigationItems: props.children,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
setTimeout(() => {
|
|
279
|
+
checkHorizontalOverflow();
|
|
280
|
+
}, 0);
|
|
276
281
|
});
|
|
277
282
|
}
|
|
278
283
|
setIsMobileNavigationOpen(false);
|
|
284
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
279
285
|
}, [isMobile, props.children]);
|
|
280
286
|
useLayoutEffect(() => {
|
|
281
287
|
if (!isMobile) {
|
|
@@ -291,31 +297,18 @@ const PriorityNavigation = (_a) => {
|
|
|
291
297
|
checkHorizontalOverflow,
|
|
292
298
|
]);
|
|
293
299
|
const handleNavigationListKeyDown = useCallback((e) => {
|
|
294
|
-
var _a;
|
|
295
|
-
if (isMobile && isMobileNavigationOpen) {
|
|
300
|
+
var _a, _b;
|
|
301
|
+
if (isMobile && isMobileNavigationOpen && e.key === 'Tab') {
|
|
296
302
|
const focusableElements = (_a = navigationListRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll('a, button, input, [tabindex]:not([tabindex="-1"])');
|
|
297
303
|
const lastElement = focusableElements === null || focusableElements === void 0 ? void 0 : focusableElements[focusableElements.length - 1];
|
|
298
|
-
|
|
304
|
+
const goingForward = !e.shiftKey;
|
|
305
|
+
if (goingForward && document.activeElement === lastElement) {
|
|
299
306
|
e.preventDefault();
|
|
300
307
|
setIsMobileNavigationOpen(false);
|
|
308
|
+
(_b = dropdownButtonRef.current) === null || _b === void 0 ? void 0 : _b.focus();
|
|
301
309
|
}
|
|
302
310
|
}
|
|
303
311
|
}, [isMobile, isMobileNavigationOpen]);
|
|
304
|
-
const handleNavigationListFocus = useCallback(() => {
|
|
305
|
-
if (isMobile && isMobileNavigationOpen) {
|
|
306
|
-
setTimeout(() => {
|
|
307
|
-
var _a, _b;
|
|
308
|
-
const focusableElements = (_a = navigationListRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll('a, button, input, [tabindex]:not([tabindex="-1"])');
|
|
309
|
-
if (!focusableElements || focusableElements.length === 0)
|
|
310
|
-
return;
|
|
311
|
-
const lastElement = focusableElements[focusableElements.length - 1];
|
|
312
|
-
if (document.activeElement === lastElement) {
|
|
313
|
-
setIsMobileNavigationOpen(false);
|
|
314
|
-
(_b = dropdownButtonRef.current) === null || _b === void 0 ? void 0 : _b.focus();
|
|
315
|
-
}
|
|
316
|
-
}, 0);
|
|
317
|
-
}
|
|
318
|
-
}, [isMobile, isMobileNavigationOpen]);
|
|
319
312
|
const handleItemClick = (e) => {
|
|
320
313
|
setIsMobileNavigationOpen(false);
|
|
321
314
|
if (props.onClick) {
|
|
@@ -331,7 +324,7 @@ const PriorityNavigation = (_a) => {
|
|
|
331
324
|
props.categoryLabel && React__default.createElement(Category, null, props.categoryLabel),
|
|
332
325
|
selectedItem),
|
|
333
326
|
React__default.createElement(Icon, { icon: isMobileNavigationOpen ? OvalChevronUp : OvalChevronDown, size: "2.5rem" }))),
|
|
334
|
-
React__default.createElement(NavigationList, { ref: navigationListRef, isMobileNavigationOpen: isMobileNavigationOpen, onKeyDown: handleNavigationListKeyDown
|
|
327
|
+
React__default.createElement(NavigationList, { ref: navigationListRef, isMobileNavigationOpen: isMobileNavigationOpen, onKeyDown: handleNavigationListKeyDown }, Children.map([...state.navigationItems, ...(isMobile ? state.dropdownItems : [])], (navigationItem, index) => {
|
|
335
328
|
if (isValidElement(navigationItem) &&
|
|
336
329
|
navigationItem.type === PriorityNavigationItem) {
|
|
337
330
|
return (React__default.createElement(PriorityNavigationItem, { id: navigationItem.props.id, key: navigationItem.key, onClick: handleItemClick, onKeyDown: navigationItem.props.onKeyDown || props.onKeyDown, isActive: navigationItem.props.isActive, className: navigationItem.props.className, "data-testid": navigationItem.props['data-testid'], ref: instance => {
|
|
@@ -353,8 +346,9 @@ const PriorityNavigation = (_a) => {
|
|
|
353
346
|
!isMobile && Boolean(state.dropdownItems.length) && (React__default.createElement(React__default.Fragment, null,
|
|
354
347
|
React__default.createElement("div", null,
|
|
355
348
|
React__default.createElement(ButtonIcon, { ref: dropdownButtonRef, ariaLabel: openMoreSubpagesAriaLabel, ariaExpanded: isDropdownListOpen, onClick: toggleDropdown, icon: isDropdownListOpen ? ChevronUp : ChevronDown, isReversed: true, "data-testid": "dropdown-button" }, dropdownButtonLabel)),
|
|
356
|
-
React__default.createElement(DropdownList, { isDropdownListOpen: isDropdownListOpen }, state.dropdownItems.map(dropdownItem => isValidElement(dropdownItem) &&
|
|
357
|
-
dropdownItem.type === PriorityNavigationItem && (React__default.createElement(PriorityNavigationItem, { id: dropdownItem.props.id, key: dropdownItem.key, onClick: dropdownItem.props.onClick || props.onClick, onKeyDown: dropdownItem.props.onKeyDown ||
|
|
349
|
+
isDropdownListOpen && (React__default.createElement(DropdownList, { isDropdownListOpen: isDropdownListOpen }, state.dropdownItems.map(dropdownItem => isValidElement(dropdownItem) &&
|
|
350
|
+
dropdownItem.type === PriorityNavigationItem && (React__default.createElement(PriorityNavigationItem, { id: dropdownItem.props.id, key: dropdownItem.key, onClick: dropdownItem.props.onClick || props.onClick, onKeyDown: dropdownItem.props.onKeyDown ||
|
|
351
|
+
props.onKeyDown, isActive: dropdownItem.props.isActive, className: dropdownItem.props.className, "data-testid": dropdownItem.props['data-testid'] }, dropdownItem.props.children)))))))))));
|
|
358
352
|
};
|
|
359
353
|
|
|
360
354
|
export { PriorityNavigation as default };
|
|
@@ -18,6 +18,7 @@ export { default as Divider } from './Divider/Divider';
|
|
|
18
18
|
export { default as DnaLogo } from './DnaLogo/DnaLogo';
|
|
19
19
|
export { default as Drawer } from './Drawer/Drawer';
|
|
20
20
|
export { default as EmptyState } from './EmptyState/EmptyState';
|
|
21
|
+
export { default as EnergyLabel } from './EnergyLabel/EnergyLabel';
|
|
21
22
|
export { default as Expander } from './Expander/Expander';
|
|
22
23
|
export { default as Floater } from './Floater/Floater';
|
|
23
24
|
export { default as Footer } from './Footer/Footer';
|
package/build/es/index.js
CHANGED
|
@@ -18,6 +18,7 @@ export { default as Divider } from './components/Divider/Divider.js';
|
|
|
18
18
|
export { default as DnaLogo } from './components/DnaLogo/DnaLogo.js';
|
|
19
19
|
export { default as Drawer } from './components/Drawer/Drawer.js';
|
|
20
20
|
export { default as EmptyState } from './components/EmptyState/EmptyState.js';
|
|
21
|
+
export { default as EnergyLabel } from './components/EnergyLabel/EnergyLabel.js';
|
|
21
22
|
export { default as Expander } from './components/Expander/Expander.js';
|
|
22
23
|
export { default as Floater } from './components/Floater/Floater.js';
|
|
23
24
|
export { default as Footer } from './components/Footer/Footer.js';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dnanpm/styleguide",
|
|
3
3
|
"sideEffects": false,
|
|
4
|
-
"version": "v3.
|
|
4
|
+
"version": "v3.10.0",
|
|
5
5
|
"main": "build/cjs/index.js",
|
|
6
6
|
"module": "build/es/index.js",
|
|
7
7
|
"jsnext:main": "build/es/index.js",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"eslint-config-airbnb": "^19.0.4",
|
|
71
71
|
"eslint-config-airbnb-typescript": "^17.1.0",
|
|
72
72
|
"eslint-config-prettier": "^8.10.0",
|
|
73
|
-
"eslint-plugin-import": "2.
|
|
73
|
+
"eslint-plugin-import": "2.32.0",
|
|
74
74
|
"eslint-plugin-jsdoc": "^39.9.1",
|
|
75
75
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
76
76
|
"eslint-plugin-prefer-arrow": "^1.2.3",
|