@shopgate/pwa-ui-shared 7.30.0-alpha.6 → 7.30.0-alpha.8
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/AccordionContainer/index.js +39 -5
- package/AccordionContainer/spec.js +25 -2
- package/ActionButton/index.js +63 -7
- package/ActionButton/spec.js +59 -2
- package/ActionButton/style.js +22 -1
- package/AddToCartButton/index.js +184 -27
- package/AddToCartButton/mock.js +18 -4
- package/AddToCartButton/spec.js +51 -2
- package/AddToCartButton/style.js +127 -11
- package/Availability/index.js +34 -2
- package/Availability/spec.js +41 -1
- package/Availability/style.js +19 -1
- package/Button/index.js +76 -5
- package/Button/spec.js +33 -1
- package/Button/style.js +130 -21
- package/ButtonLink/connector.js +11 -2
- package/ButtonLink/index.js +44 -6
- package/ButtonLink/spec.js +23 -1
- package/Card/index.js +19 -2
- package/Card/style.js +11 -1
- package/CardList/components/Item/index.js +26 -2
- package/CardList/components/Item/style.js +7 -1
- package/CardList/index.js +34 -3
- package/CartTotalLine/components/Amount/index.js +28 -2
- package/CartTotalLine/components/Amount/style.js +8 -1
- package/CartTotalLine/components/Hint/index.js +23 -2
- package/CartTotalLine/components/Hint/style.js +12 -1
- package/CartTotalLine/components/Label/index.js +36 -2
- package/CartTotalLine/components/Label/style.js +17 -1
- package/CartTotalLine/components/Spacer/index.js +16 -2
- package/CartTotalLine/index.js +39 -2
- package/CartTotalLine/style.js +31 -1
- package/Checkbox/index.js +31 -2
- package/Checkbox/style.js +18 -1
- package/Chip/index.js +61 -2
- package/Chip/spec.js +24 -1
- package/Chip/style.js +71 -3
- package/ContextMenu/ContextMenu.hooks.js +6 -2
- package/ContextMenu/ContextMenuProvider.context.js +9 -3
- package/ContextMenu/ContextMenuProvider.js +21 -2
- package/ContextMenu/components/Item/index.js +67 -5
- package/ContextMenu/components/Item/style.js +32 -3
- package/ContextMenu/components/Position/index.js +61 -10
- package/ContextMenu/components/Position/style.js +11 -1
- package/ContextMenu/index.js +124 -3
- package/ContextMenu/spec.js +101 -2
- package/ContextMenu/style.js +45 -1
- package/Dialog/components/BasicDialog/index.js +5 -1
- package/Dialog/components/HtmlContentDialog/index.js +22 -2
- package/Dialog/components/HtmlContentDialog/spec.js +59 -1
- package/Dialog/components/PipelineErrorDialog/index.js +114 -25
- package/Dialog/components/PipelineErrorDialog/spec.js +92 -12
- package/Dialog/components/TextMessageDialog/index.js +28 -2
- package/Dialog/components/TextMessageDialog/spec.js +59 -1
- package/Dialog/components/VariantSelectModal/connector.js +11 -2
- package/Dialog/components/VariantSelectModal/index.js +65 -6
- package/Dialog/components/VariantSelectModal/spec.js +51 -2
- package/Dialog/constants.js +6 -1
- package/Dialog/index.js +114 -7
- package/Dialog/spec.js +81 -3
- package/DiscountBadge/index.js +30 -2
- package/DiscountBadge/spec.js +19 -1
- package/DiscountBadge/style.js +34 -2
- package/FavoritesButton/connector.js +18 -3
- package/FavoritesButton/index.js +118 -15
- package/FavoritesButton/mock.js +50 -4
- package/FavoritesButton/spec.js +120 -2
- package/FavoritesButton/style.js +26 -1
- package/Form/Builder/builders/buildCountryList.js +40 -6
- package/Form/Builder/builders/buildFormDefaults.js +35 -6
- package/Form/Builder/builders/buildFormElements.js +68 -10
- package/Form/Builder/builders/buildProvinceList.js +19 -2
- package/Form/Builder/builders/buildValidationErrorList.js +7 -2
- package/Form/Builder/classes/ActionListener/constants.js +22 -2
- package/Form/Builder/classes/ActionListener/index.js +441 -93
- package/Form/Builder/classes/ActionListener/spec.js +321 -19
- package/Form/Builder/components/CheckboxElement.js +35 -3
- package/Form/Builder/components/CountryElement.js +40 -3
- package/Form/Builder/components/ProvinceElement.js +40 -3
- package/Form/Builder/components/RadioElement.js +41 -3
- package/Form/Builder/components/SelectElement.js +39 -3
- package/Form/Builder/components/TextElement.js +49 -4
- package/Form/Builder/elementTypes.js +11 -1
- package/Form/Builder/index.js +298 -52
- package/Form/Builder/iso-3166-2.js +4943 -1
- package/Form/Builder/spec.js +300 -16
- package/Form/Checkbox/index.js +66 -4
- package/Form/Checkbox/style.js +25 -2
- package/Form/InfoField/index.js +50 -2
- package/Form/InfoField/spec.js +9 -1
- package/Form/InfoField/style.js +11 -1
- package/Form/Password/index.js +51 -6
- package/Form/Password/spec.js +34 -1
- package/Form/Password/style.js +11 -1
- package/Form/RadioGroup/components/Item/index.js +59 -3
- package/Form/RadioGroup/components/Item/style.js +32 -2
- package/Form/RadioGroup/index.js +101 -9
- package/Form/RadioGroup/spec.js +83 -3
- package/Form/RadioGroup/style.js +18 -2
- package/Form/Select/index.js +158 -10
- package/Form/Select/spec.js +36 -5
- package/Form/Select/style.js +27 -1
- package/Form/SelectContextChoices/index.js +77 -3
- package/Form/SelectContextChoices/spec.js +33 -4
- package/Form/SelectContextChoices/style.js +23 -1
- package/Form/TextField/index.js +92 -8
- package/Form/TextField/spec.js +110 -1
- package/Form/TextField/style.js +66 -8
- package/Form/index.js +54 -13
- package/FormElement/components/ErrorText/index.js +31 -2
- package/FormElement/components/ErrorText/style.js +13 -1
- package/FormElement/components/Label/index.js +35 -2
- package/FormElement/components/Label/style.js +76 -8
- package/FormElement/components/Placeholder/index.js +26 -2
- package/FormElement/components/Placeholder/style.js +48 -6
- package/FormElement/components/Underline/index.js +18 -2
- package/FormElement/components/Underline/style.js +51 -4
- package/FormElement/index.js +91 -6
- package/FormElement/spec.js +67 -2
- package/FormElement/style.js +13 -2
- package/Glow/index.js +90 -7
- package/Glow/spec.js +9 -1
- package/Glow/style.js +18 -1
- package/IndicatorCircle/index.js +33 -3
- package/IndicatorCircle/spec.js +28 -1
- package/IndicatorCircle/style.js +57 -3
- package/LoadingIndicator/index.js +29 -2
- package/LoadingIndicator/style.js +20 -1
- package/Manufacturer/index.js +20 -2
- package/Manufacturer/style.js +5 -1
- package/MessageBar/index.js +36 -2
- package/MessageBar/spec.js +79 -1
- package/MessageBar/style.js +38 -1
- package/NoResults/components/Icon/index.js +130 -2
- package/NoResults/components/Icon/style.js +17 -1
- package/NoResults/index.js +46 -2
- package/NoResults/style.js +31 -1
- package/Placeholder/index.js +25 -3
- package/Placeholder/style.js +11 -1
- package/PlaceholderLabel/index.js +27 -2
- package/PlaceholderLabel/spec.js +19 -1
- package/PlaceholderLabel/style.js +12 -1
- package/PlaceholderParagraph/index.js +36 -2
- package/PlaceholderParagraph/spec.js +19 -1
- package/Price/index.js +88 -7
- package/Price/style.js +22 -1
- package/PriceInfo/index.js +20 -2
- package/PriceInfo/style.js +5 -1
- package/PriceStriked/index.js +83 -12
- package/PriceStriked/style.js +33 -3
- package/ProductProperties/index.js +32 -2
- package/ProgressBar/index.js +101 -13
- package/ProgressBar/spec.js +13 -1
- package/ProgressBar/style.js +83 -2
- package/RadioButton/index.js +18 -2
- package/RadioButton/spec.js +21 -1
- package/RadioButton/style.js +21 -1
- package/RatingNumber/index.js +29 -2
- package/RatingStars/constants.js +2 -1
- package/RatingStars/index.js +130 -12
- package/RatingStars/spec.js +90 -3
- package/RatingStars/style.js +51 -2
- package/Ripple/components/RippleAnimation/index.js +88 -6
- package/Ripple/index.js +218 -40
- package/Ripple/style.js +18 -1
- package/RippleButton/index.js +52 -5
- package/RippleButton/spec.js +45 -1
- package/ScannerOverlay/components/CameraOverlay/index.js +13 -2
- package/ScannerOverlay/components/CameraOverlay/style.js +41 -1
- package/ScannerOverlay/components/ScannerBar/components/FlashlightButton/index.js +34 -2
- package/ScannerOverlay/components/ScannerBar/components/FlashlightButton/style.js +28 -1
- package/ScannerOverlay/components/ScannerBar/components/ScannerInstructions/index.js +11 -2
- package/ScannerOverlay/components/ScannerBar/index.js +31 -2
- package/ScannerOverlay/components/ScannerBar/style.js +20 -1
- package/ScannerOverlay/index.js +47 -7
- package/Sheet/components/Header/components/SearchBar/index.js +46 -2
- package/Sheet/components/Header/components/SearchBar/spec.js +21 -3
- package/Sheet/components/Header/components/SearchBar/style.js +47 -1
- package/Sheet/components/Header/index.js +75 -7
- package/Sheet/components/Header/spec.js +14 -1
- package/Sheet/components/Header/style.js +50 -1
- package/Sheet/index.js +170 -17
- package/Sheet/spec.js +85 -5
- package/Sheet/style.js +143 -2
- package/TaxDisclaimer/index.js +34 -4
- package/TaxDisclaimer/spec.js +31 -3
- package/TaxDisclaimer/style.js +9 -1
- package/TextField/components/ErrorText/index.js +33 -2
- package/TextField/components/ErrorText/style.js +25 -3
- package/TextField/components/FormElement/index.js +19 -2
- package/TextField/components/FormElement/style.js +32 -4
- package/TextField/components/Hint/index.js +21 -2
- package/TextField/components/Hint/style.js +40 -5
- package/TextField/components/Label/index.js +32 -3
- package/TextField/components/Label/style.js +68 -8
- package/TextField/components/Underline/index.js +19 -2
- package/TextField/components/Underline/style.js +51 -4
- package/TextField/index.js +189 -27
- package/TextField/spec.js +128 -3
- package/TextField/style.js +34 -4
- package/ToggleIcon/index.js +58 -8
- package/ToggleIcon/spec.js +35 -1
- package/icons/AccountBoxIcon.js +11 -2
- package/icons/AddMoreIcon.js +11 -2
- package/icons/ArrowDropIcon.js +11 -2
- package/icons/ArrowIcon.js +21 -2
- package/icons/BarcodeScannerIcon.js +11 -2
- package/icons/BoxIcon.js +11 -2
- package/icons/BrowseIcon.js +11 -2
- package/icons/BurgerIcon.js +11 -2
- package/icons/CalendarIcon.js +15 -3
- package/icons/CartCouponIcon.js +72 -2
- package/icons/CartIcon.js +11 -2
- package/icons/CartPlusIcon.js +11 -2
- package/icons/CheckIcon.js +11 -2
- package/icons/CheckedIcon.js +11 -2
- package/icons/ChevronIcon.js +11 -2
- package/icons/CreditCardIcon.js +11 -2
- package/icons/CrossIcon.js +11 -2
- package/icons/DescriptionIcon.js +11 -2
- package/icons/FilterIcon.js +11 -2
- package/icons/FlashDisabledIcon.js +11 -2
- package/icons/FlashEnabledIcon.js +11 -2
- package/icons/GridIcon.js +11 -2
- package/icons/HeartIcon.js +11 -2
- package/icons/HeartOutlineIcon.js +11 -2
- package/icons/HeartPlusIcon.js +12 -2
- package/icons/HeartPlusOutlineIcon.js +12 -2
- package/icons/HomeIcon.js +11 -2
- package/icons/InfoIcon.js +11 -2
- package/icons/InfoOutlineIcon.js +11 -2
- package/icons/ListIcon.js +11 -2
- package/icons/LocalShippingIcon.js +11 -2
- package/icons/LocationIcon.js +13 -3
- package/icons/LocatorIcon.js +11 -2
- package/icons/LockIcon.js +11 -2
- package/icons/LogoutIcon.js +11 -2
- package/icons/MagnifierIcon.js +11 -2
- package/icons/MapMarkerIcon.js +24 -3
- package/icons/MoreIcon.js +11 -2
- package/icons/MoreVertIcon.js +11 -2
- package/icons/NotificationIcon.js +14 -3
- package/icons/PersonIcon.js +12 -2
- package/icons/PhoneIcon.js +13 -3
- package/icons/PlaceholderIcon.js +11 -2
- package/icons/RadioCheckedIcon.js +11 -2
- package/icons/RadioUncheckedIcon.js +11 -2
- package/icons/SecurityIcon.js +11 -2
- package/icons/ShippingMethodIcon.js +18 -3
- package/icons/ShoppingCartIcon.js +11 -2
- package/icons/SortIcon.js +11 -2
- package/icons/StarHalfIcon.js +18 -2
- package/icons/StarIcon.js +18 -2
- package/icons/StarOutlineIcon.js +11 -2
- package/icons/StopIcon.js +11 -2
- package/icons/TickIcon.js +11 -2
- package/icons/TimeIcon.js +14 -3
- package/icons/TrashIcon.js +11 -2
- package/icons/TrashOutlineIcon.js +12 -2
- package/icons/UncheckedIcon.js +11 -2
- package/icons/ViewListIcon.js +11 -2
- package/icons/VisibilityIcon.js +11 -2
- package/icons/VisibilityOffIcon.js +11 -2
- package/icons/WarningIcon.js +11 -2
- package/index.js +13 -1
- package/package.json +5 -5
package/Chip/style.js
CHANGED
|
@@ -1,10 +1,78 @@
|
|
|
1
|
-
|
|
1
|
+
import { css } from 'glamor';
|
|
2
|
+
import { responsiveMediaQuery } from '@shopgate/engage/styles';
|
|
3
|
+
import { hasNewServices } from '@shopgate/engage/core/helpers';
|
|
4
|
+
import { themeConfig } from '@shopgate/engage';
|
|
5
|
+
|
|
6
|
+
/**
|
|
2
7
|
* Gets a basic style object for the chip layout.
|
|
3
8
|
* @param {boolean} hasRemoveButton Whether this chip has a remove button.
|
|
4
9
|
* @returns {Object} The style object.
|
|
5
|
-
*/
|
|
10
|
+
*/
|
|
11
|
+
const chipBase = (hasRemoveButton = true) => ({
|
|
12
|
+
display: 'flex',
|
|
13
|
+
alignItems: 'center',
|
|
14
|
+
borderRadius: 26,
|
|
15
|
+
outline: 0,
|
|
16
|
+
height: 26,
|
|
17
|
+
paddingRight: themeConfig.variables.gap.small,
|
|
18
|
+
paddingLeft: themeConfig.variables.gap.small * (hasRemoveButton ? 0.5 : 1),
|
|
19
|
+
marginRight: 5,
|
|
20
|
+
marginTop: 4,
|
|
21
|
+
marginBottom: 4,
|
|
22
|
+
minWidth: 0
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
6
26
|
* Gets a style class for the chip layout.
|
|
7
27
|
* @param {boolean} hasRemoveButton Whether this chip has a remove button.
|
|
8
28
|
* @param {boolean} inverted Whether the colors of the chip are inverted.
|
|
9
29
|
* @returns {string} The style class name.
|
|
10
|
-
*/
|
|
30
|
+
*/
|
|
31
|
+
const chip = (hasRemoveButton = true, inverted = false) => css({
|
|
32
|
+
...chipBase(hasRemoveButton),
|
|
33
|
+
...(hasNewServices() ? {
|
|
34
|
+
backgroundColor: inverted ? 'var(--color-primary)' : 'var(--color-primary-contrast)',
|
|
35
|
+
color: inverted ? 'var(--color-primary-contrast)' : 'var(--color-primary)'
|
|
36
|
+
} : {
|
|
37
|
+
backgroundColor: inverted ? 'var(--color-secondary)' : 'var(--color-secondary-contrast)',
|
|
38
|
+
color: inverted ? 'var(--color-secondary-contrast)' : 'var(--color-secondary)',
|
|
39
|
+
'--color-text-high-emphasis': inverted ? 'var(--color-secondary-contrast)' : 'var(--color-secondary)'
|
|
40
|
+
})
|
|
41
|
+
}).toString();
|
|
42
|
+
const removeButton = css({
|
|
43
|
+
flexShrink: 0,
|
|
44
|
+
padding: 0,
|
|
45
|
+
[responsiveMediaQuery('>xs', {
|
|
46
|
+
webOnly: true
|
|
47
|
+
})]: {
|
|
48
|
+
padding: '0 5px',
|
|
49
|
+
fontSize: '1.125rem'
|
|
50
|
+
}
|
|
51
|
+
}).toString();
|
|
52
|
+
const name = css({
|
|
53
|
+
paddingLeft: themeConfig.variables.gap.small * 0.5,
|
|
54
|
+
paddingRight: themeConfig.variables.gap.small * 0.5,
|
|
55
|
+
paddingTop: 3,
|
|
56
|
+
paddingBottom: 3,
|
|
57
|
+
fontSize: 12,
|
|
58
|
+
fontWeight: 500,
|
|
59
|
+
textOverflow: 'ellipsis',
|
|
60
|
+
maxWidth: '100%',
|
|
61
|
+
whiteSpace: 'nowrap',
|
|
62
|
+
overflow: 'hidden',
|
|
63
|
+
display: 'block',
|
|
64
|
+
lineHeight: '1',
|
|
65
|
+
color: 'inherit',
|
|
66
|
+
[responsiveMediaQuery('>xs', {
|
|
67
|
+
webOnly: true
|
|
68
|
+
})]: {
|
|
69
|
+
fontSize: '0.875rem',
|
|
70
|
+
lineHeight: '1.25rem',
|
|
71
|
+
padding: '6px 8px 6px 0'
|
|
72
|
+
}
|
|
73
|
+
}).toString();
|
|
74
|
+
export default {
|
|
75
|
+
chip,
|
|
76
|
+
removeButton,
|
|
77
|
+
name
|
|
78
|
+
};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import{useContext}from'react';
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import ContextMenuContext from "./ContextMenuProvider.context";
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line valid-jsdoc
|
|
2
5
|
/**
|
|
3
6
|
* Returns the value of the context menu provider state.
|
|
4
|
-
*/
|
|
7
|
+
*/
|
|
8
|
+
export const useContextMenu = () => useContext(ContextMenuContext);
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import{createContext}from'react'
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
2
4
|
* @typedef {Object} ContextMenuContextType
|
|
3
5
|
* @property {(event: React.MouseEvent<HTMLButtonElement>) => void} handleMenuToggle
|
|
4
6
|
* Toggles visibility of the ContextMenu.
|
|
5
|
-
*/
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
6
10
|
* @type {ContextMenuContextType}
|
|
7
|
-
*/
|
|
11
|
+
*/
|
|
12
|
+
const initialContext = {};
|
|
13
|
+
export default /*#__PURE__*/createContext(initialContext);
|
|
@@ -1,8 +1,27 @@
|
|
|
1
|
-
import React,{useMemo}from'react';
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import Context from "./ContextMenuProvider.context";
|
|
4
|
+
|
|
5
|
+
/**
|
|
2
6
|
* The Context Menu Provider
|
|
3
7
|
*
|
|
4
8
|
* @param {Object} props The component props.
|
|
5
9
|
* @param {Object} props.children The component props.
|
|
6
10
|
* @param {Object} props.handleMenuToggle Toggles visibility of the ContextMenu.
|
|
7
11
|
* @returns {JSX}
|
|
8
|
-
*/
|
|
12
|
+
*/
|
|
13
|
+
const ContextMenuProvider = ({
|
|
14
|
+
children,
|
|
15
|
+
handleMenuToggle
|
|
16
|
+
}) => {
|
|
17
|
+
const value = useMemo(() => ({
|
|
18
|
+
handleMenuToggle
|
|
19
|
+
}), [handleMenuToggle]);
|
|
20
|
+
return /*#__PURE__*/React.createElement(Context.Provider, {
|
|
21
|
+
value: value
|
|
22
|
+
}, children);
|
|
23
|
+
};
|
|
24
|
+
ContextMenuProvider.defaultProps = {
|
|
25
|
+
handleMenuToggle: () => {}
|
|
26
|
+
};
|
|
27
|
+
export default ContextMenuProvider;
|
|
@@ -1,16 +1,78 @@
|
|
|
1
|
-
import React,{useCallback}
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import noop from 'lodash/noop';
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
import Glow from "../../../Glow";
|
|
6
|
+
import { getItemClass } from "./style";
|
|
7
|
+
import { useContextMenu } from "../../ContextMenu.hooks";
|
|
8
|
+
|
|
9
|
+
/**
|
|
2
10
|
* A delay in ms after that the closeMenu callback gets triggered.
|
|
3
11
|
* @type {number}
|
|
4
|
-
*/
|
|
12
|
+
*/
|
|
13
|
+
const CLOSE_DELAY = 250;
|
|
14
|
+
|
|
15
|
+
/**
|
|
5
16
|
* The Context Menu Item component.
|
|
6
17
|
* @param {Object} props The component props.
|
|
7
18
|
* @returns {JSX.Element}
|
|
8
|
-
*/
|
|
19
|
+
*/
|
|
20
|
+
const Item = ({
|
|
21
|
+
children,
|
|
22
|
+
onClick,
|
|
23
|
+
disabled,
|
|
24
|
+
autoClose,
|
|
25
|
+
className
|
|
26
|
+
}) => {
|
|
27
|
+
const {
|
|
28
|
+
handleMenuToggle
|
|
29
|
+
} = useContextMenu();
|
|
30
|
+
|
|
31
|
+
/**
|
|
9
32
|
* Handles the click event.
|
|
10
33
|
* @param {Event} event The click event.
|
|
11
34
|
* @returns {void}
|
|
12
|
-
*/
|
|
35
|
+
*/
|
|
36
|
+
const handleClick = useCallback(event => {
|
|
37
|
+
event.persist();
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
if (autoClose) {
|
|
40
|
+
handleMenuToggle(event);
|
|
41
|
+
}
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
onClick(event);
|
|
44
|
+
}, 0);
|
|
45
|
+
}, autoClose ? CLOSE_DELAY : 0);
|
|
46
|
+
}, [autoClose, handleMenuToggle, onClick]);
|
|
47
|
+
|
|
48
|
+
/**
|
|
13
49
|
* Handles the keypress event for screen readers.
|
|
14
50
|
* @param {Event} event The click event.
|
|
15
51
|
* @returns {void}
|
|
16
|
-
*/
|
|
52
|
+
*/
|
|
53
|
+
const handleKeyPress = useCallback(event => {
|
|
54
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
handleClick(event);
|
|
57
|
+
}
|
|
58
|
+
}, [handleClick]);
|
|
59
|
+
return /*#__PURE__*/React.createElement(Glow, {
|
|
60
|
+
disabled: disabled
|
|
61
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
62
|
+
className: classNames(getItemClass(disabled), className),
|
|
63
|
+
onClick: disabled ? noop : handleClick,
|
|
64
|
+
"data-test-id": "contextMenuButton",
|
|
65
|
+
"aria-disabled": disabled,
|
|
66
|
+
role: "button",
|
|
67
|
+
tabIndex: disabled ? -1 : 0,
|
|
68
|
+
onKeyDown: !disabled ? handleKeyPress : undefined
|
|
69
|
+
}, children));
|
|
70
|
+
};
|
|
71
|
+
Item.defaultProps = {
|
|
72
|
+
autoClose: true,
|
|
73
|
+
children: null,
|
|
74
|
+
className: '',
|
|
75
|
+
onClick: () => {},
|
|
76
|
+
disabled: false
|
|
77
|
+
};
|
|
78
|
+
export default Item;
|
|
@@ -1,5 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
import { css } from 'glamor';
|
|
2
|
+
import Color from 'color';
|
|
3
|
+
import { themeConfig } from '@shopgate/pwa-common/helpers/config';
|
|
4
|
+
import { getCSSCustomProp } from '@shopgate/engage/styles';
|
|
5
|
+
|
|
6
|
+
/**
|
|
2
7
|
* Get the item class.
|
|
3
|
-
* @param {
|
|
8
|
+
* @param {boolean} disabled Disabled.
|
|
4
9
|
* @returns {string}
|
|
5
|
-
*/
|
|
10
|
+
*/
|
|
11
|
+
export const getItemClass = disabled => {
|
|
12
|
+
let background = themeConfig.colors.shade8;
|
|
13
|
+
const customPropColor = getCSSCustomProp('--color-primary');
|
|
14
|
+
if (customPropColor) {
|
|
15
|
+
background = Color(customPropColor).alpha(0.04);
|
|
16
|
+
}
|
|
17
|
+
return css({
|
|
18
|
+
position: 'relative',
|
|
19
|
+
whiteSpace: 'nowrap',
|
|
20
|
+
marginBottom: 2,
|
|
21
|
+
padding: `${themeConfig.variables.gap.big * 0.875}px ${themeConfig.variables.gap.big * 1.375}px`,
|
|
22
|
+
lineHeight: 1,
|
|
23
|
+
zIndex: 1,
|
|
24
|
+
color: disabled ? 'var(--color-text-low-emphasis)' : 'inherits',
|
|
25
|
+
...(!disabled ? {
|
|
26
|
+
cursor: 'pointer',
|
|
27
|
+
':hover': {
|
|
28
|
+
background
|
|
29
|
+
}
|
|
30
|
+
} : {
|
|
31
|
+
cursor: 'default'
|
|
32
|
+
})
|
|
33
|
+
}).toString();
|
|
34
|
+
};
|
|
@@ -1,16 +1,67 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import clamp from 'lodash/clamp';
|
|
4
|
+
import styles from "./style";
|
|
5
|
+
|
|
6
|
+
/**
|
|
2
7
|
* The Context Menu Position component.
|
|
3
|
-
*/
|
|
8
|
+
*/
|
|
9
|
+
class Position extends Component {
|
|
10
|
+
/**
|
|
4
11
|
* The Constructor.
|
|
5
12
|
* @param {Object} props The component props.
|
|
6
|
-
*/
|
|
13
|
+
*/
|
|
14
|
+
constructor(props) {
|
|
15
|
+
super(props);
|
|
16
|
+
this.elementRef = null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
7
20
|
* Calculate and apply the correct menu position after mounting.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
*/
|
|
22
|
+
componentDidMount() {
|
|
23
|
+
const {
|
|
24
|
+
offset
|
|
25
|
+
} = this.props;
|
|
26
|
+
|
|
27
|
+
// Get ref to the actual child DOM element and calculate bounding rect.
|
|
28
|
+
const [child] = this.elementRef.childNodes;
|
|
29
|
+
const bounds = child.getBoundingClientRect();
|
|
30
|
+
|
|
31
|
+
// Get window dimensions
|
|
32
|
+
const width = window.innerWidth;
|
|
33
|
+
const height = window.innerHeight;
|
|
34
|
+
|
|
35
|
+
// Get the outer gap from styles
|
|
36
|
+
const gap = styles.outerGap;
|
|
37
|
+
|
|
38
|
+
// Calculate clamped menu position
|
|
39
|
+
const left = clamp(offset.left, 0, width - bounds.width - gap * 2);
|
|
40
|
+
const top = clamp(offset.top - gap, 0, height - bounds.height - gap * 2);
|
|
41
|
+
|
|
42
|
+
// Assign position directly w/o re-rendering the component
|
|
43
|
+
this.elementRef.style.left = `${left}px`;
|
|
44
|
+
this.elementRef.style.top = `${top}px`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
14
48
|
* Renders the component.
|
|
15
49
|
* @returns {JSX}
|
|
16
|
-
*/
|
|
50
|
+
*/
|
|
51
|
+
render() {
|
|
52
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
53
|
+
ref: ref => {
|
|
54
|
+
this.elementRef = ref;
|
|
55
|
+
},
|
|
56
|
+
className: styles.container
|
|
57
|
+
}, this.props.children);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
Position.defaultProps = {
|
|
61
|
+
children: null,
|
|
62
|
+
offset: {
|
|
63
|
+
top: 0,
|
|
64
|
+
left: 0
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
export default Position;
|
|
@@ -1 +1,11 @@
|
|
|
1
|
-
import{css}from'glamor';
|
|
1
|
+
import { css } from 'glamor';
|
|
2
|
+
import { themeConfig } from '@shopgate/pwa-common/helpers/config';
|
|
3
|
+
const outerGap = themeConfig.variables.gap.small;
|
|
4
|
+
const container = css({
|
|
5
|
+
position: 'absolute',
|
|
6
|
+
margin: outerGap
|
|
7
|
+
}).toString();
|
|
8
|
+
export default {
|
|
9
|
+
container,
|
|
10
|
+
outerGap
|
|
11
|
+
};
|
package/ContextMenu/index.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { ConnectedReactPortal } from '@shopgate/engage/components';
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
import Backdrop from '@shopgate/pwa-common/components/Backdrop';
|
|
6
|
+
import { FocusTrap } from '@shopgate/engage/a11y/components';
|
|
7
|
+
import { i18n } from '@shopgate/engage/core';
|
|
8
|
+
import MoreVertIcon from "../icons/MoreVertIcon";
|
|
9
|
+
import Position from "./components/Position";
|
|
10
|
+
import Item from "./components/Item";
|
|
11
|
+
import styles from "./style";
|
|
12
|
+
import ContextMenuProvider from "./ContextMenuProvider";
|
|
13
|
+
|
|
14
|
+
/**
|
|
2
15
|
* The Context Menu component.
|
|
3
16
|
* @param {Object} props The component props.
|
|
4
17
|
* @param {Object} props.children The menu items.
|
|
@@ -11,7 +24,115 @@ function _defineProperty(obj,key,value){if(key in obj){Object.defineProperty(obj
|
|
|
11
24
|
* @param {boolean} props.scroll Whether the menu should be scrollable.
|
|
12
25
|
* @param {boolean} props.showToggle Whether the toggle button should be shown.
|
|
13
26
|
* @returns {JSX}
|
|
14
|
-
*/
|
|
27
|
+
*/
|
|
28
|
+
const ContextMenu = props => {
|
|
29
|
+
const {
|
|
30
|
+
children,
|
|
31
|
+
classes,
|
|
32
|
+
disabled,
|
|
33
|
+
showToggle,
|
|
34
|
+
scroll,
|
|
35
|
+
isOpened,
|
|
36
|
+
onStateChange
|
|
37
|
+
} = props;
|
|
38
|
+
const [active, setActive] = useState(isOpened);
|
|
39
|
+
const elementRef = useRef(null);
|
|
40
|
+
const menuRef = useRef(null);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (typeof isOpened === 'boolean' && active !== isOpened) {
|
|
43
|
+
setActive(isOpened);
|
|
44
|
+
}
|
|
45
|
+
}, [active, isOpened]);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (active && menuRef.current) {
|
|
48
|
+
menuRef.current.focus();
|
|
49
|
+
}
|
|
50
|
+
}, [active]);
|
|
51
|
+
|
|
52
|
+
/**
|
|
15
53
|
* Handles the menu toggle.
|
|
16
54
|
* @param {Object} e The event object.
|
|
17
|
-
*/
|
|
55
|
+
*/
|
|
56
|
+
const handleMenuToggle = useCallback(e => {
|
|
57
|
+
if (e) {
|
|
58
|
+
e.preventDefault();
|
|
59
|
+
e.stopPropagation();
|
|
60
|
+
}
|
|
61
|
+
if (elementRef.current) {
|
|
62
|
+
setActive(prevActive => {
|
|
63
|
+
const newState = !prevActive;
|
|
64
|
+
if (onStateChange) {
|
|
65
|
+
onStateChange({
|
|
66
|
+
active: newState
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return newState;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}, [onStateChange]);
|
|
73
|
+
const offset = elementRef.current ? elementRef.current.getBoundingClientRect() : {
|
|
74
|
+
top: 0,
|
|
75
|
+
left: 0
|
|
76
|
+
};
|
|
77
|
+
const useScroll = typeof scroll === 'boolean' && !!scroll;
|
|
78
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
79
|
+
"data-test-id": "contextMenu",
|
|
80
|
+
ref: elementRef,
|
|
81
|
+
className: classNames(styles.container, classes.container, 'ui-shared__context-menu')
|
|
82
|
+
}, showToggle && /*#__PURE__*/React.createElement("button", {
|
|
83
|
+
className: classNames(styles.button, classes.button, {
|
|
84
|
+
[styles.disabled]: disabled
|
|
85
|
+
}),
|
|
86
|
+
onClick: handleMenuToggle,
|
|
87
|
+
disabled: disabled,
|
|
88
|
+
type: "button",
|
|
89
|
+
"aria-label": i18n.text('navigation.open_menu'),
|
|
90
|
+
"aria-haspopup": "true",
|
|
91
|
+
"aria-expanded": active,
|
|
92
|
+
"aria-controls": "contextMenuDialog"
|
|
93
|
+
}, /*#__PURE__*/React.createElement(MoreVertIcon, {
|
|
94
|
+
"aria-hidden": true
|
|
95
|
+
})), /*#__PURE__*/React.createElement(ConnectedReactPortal, {
|
|
96
|
+
isOpened: active
|
|
97
|
+
}, /*#__PURE__*/React.createElement(FocusTrap, {
|
|
98
|
+
active: active
|
|
99
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
100
|
+
className: styles.overlay
|
|
101
|
+
}, /*#__PURE__*/React.createElement(Backdrop, {
|
|
102
|
+
isVisible: true,
|
|
103
|
+
level: 0,
|
|
104
|
+
opacity: 0,
|
|
105
|
+
onClick: handleMenuToggle
|
|
106
|
+
}), /*#__PURE__*/React.createElement(Position, {
|
|
107
|
+
offset: offset
|
|
108
|
+
}, /*#__PURE__*/React.createElement(ContextMenuProvider, {
|
|
109
|
+
handleMenuToggle: handleMenuToggle
|
|
110
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
111
|
+
className: classNames(styles.menu, {
|
|
112
|
+
[styles.scrollable]: useScroll
|
|
113
|
+
}),
|
|
114
|
+
ref: menuRef,
|
|
115
|
+
tabIndex: "-1",
|
|
116
|
+
"aria-modal": "true",
|
|
117
|
+
role: "dialog"
|
|
118
|
+
}, children, /*#__PURE__*/React.createElement("button", {
|
|
119
|
+
onClick: handleMenuToggle,
|
|
120
|
+
className: "sr-only",
|
|
121
|
+
"aria-label": i18n.text('common.close'),
|
|
122
|
+
type: "button"
|
|
123
|
+
}, i18n.text('common.close')))))))));
|
|
124
|
+
};
|
|
125
|
+
ContextMenu.defaultProps = {
|
|
126
|
+
children: null,
|
|
127
|
+
classes: {
|
|
128
|
+
container: '',
|
|
129
|
+
button: ''
|
|
130
|
+
},
|
|
131
|
+
disabled: false,
|
|
132
|
+
showToggle: true,
|
|
133
|
+
isOpened: null,
|
|
134
|
+
onStateChange: null,
|
|
135
|
+
scroll: null
|
|
136
|
+
};
|
|
137
|
+
ContextMenu.Item = Item;
|
|
138
|
+
export default ContextMenu;
|
package/ContextMenu/spec.js
CHANGED
|
@@ -1,3 +1,102 @@
|
|
|
1
|
-
import React from'react';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { mount, shallow } from 'enzyme';
|
|
3
|
+
import { act } from 'react-dom/test-utils';
|
|
4
|
+
import Backdrop from '@shopgate/pwa-common/components/Backdrop';
|
|
5
|
+
import ContextMenu from "./index";
|
|
6
|
+
jest.mock('@shopgate/engage/components');
|
|
7
|
+
global.requestAnimationFrame = fn => fn();
|
|
8
|
+
jest.useFakeTimers();
|
|
9
|
+
describe('<ContextMenu />', () => {
|
|
10
|
+
const mockItemAClick = jest.fn();
|
|
11
|
+
const mockItemBClick = jest.fn();
|
|
12
|
+
const numMenuItems = 2;
|
|
13
|
+
describe('Snapshot test', () => {
|
|
14
|
+
it('should match snapshot', () => {
|
|
15
|
+
const wrapper = shallow(/*#__PURE__*/React.createElement(ContextMenu, {
|
|
16
|
+
isOpened: true
|
|
17
|
+
}, /*#__PURE__*/React.createElement(ContextMenu.Item, {
|
|
18
|
+
onClick: mockItemAClick,
|
|
19
|
+
className: "menu-active-item"
|
|
20
|
+
}, 'Item A'), /*#__PURE__*/React.createElement(ContextMenu.Item, {
|
|
21
|
+
onClick: mockItemBClick,
|
|
22
|
+
className: "menu-active-item"
|
|
23
|
+
}, 'Item B')));
|
|
24
|
+
expect(wrapper).toMatchSnapshot();
|
|
25
|
+
});
|
|
26
|
+
it('should match snapshot without toggle', () => {
|
|
27
|
+
const wrapper = shallow(/*#__PURE__*/React.createElement(ContextMenu, {
|
|
28
|
+
isOpened: true,
|
|
29
|
+
showToggle: false
|
|
30
|
+
}, /*#__PURE__*/React.createElement(ContextMenu.Item, {
|
|
31
|
+
onClick: mockItemAClick,
|
|
32
|
+
className: "menu-active-item"
|
|
33
|
+
}, 'Item A')));
|
|
34
|
+
expect(wrapper).toMatchSnapshot();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('Given the component was mounted to the DOM', () => {
|
|
38
|
+
let renderedElement;
|
|
39
|
+
|
|
40
|
+
/**
|
|
2
41
|
* The view component
|
|
3
|
-
*/
|
|
42
|
+
*/
|
|
43
|
+
const renderComponent = () => {
|
|
44
|
+
renderedElement = mount(/*#__PURE__*/React.createElement(ContextMenu, null, /*#__PURE__*/React.createElement(ContextMenu.Item, {
|
|
45
|
+
onClick: mockItemAClick
|
|
46
|
+
}, "Item A"), /*#__PURE__*/React.createElement(ContextMenu.Item, {
|
|
47
|
+
onClick: mockItemBClick
|
|
48
|
+
}, "Item B")));
|
|
49
|
+
};
|
|
50
|
+
beforeEach(renderComponent);
|
|
51
|
+
it('should match snapshot', () => {
|
|
52
|
+
expect(renderedElement).toMatchSnapshot();
|
|
53
|
+
});
|
|
54
|
+
it('should have active state set to false', () => {
|
|
55
|
+
expect(renderedElement.find('ConnectedReactPortal').prop('isOpened')).toBe(null);
|
|
56
|
+
});
|
|
57
|
+
it('should render the toggle button', () => {
|
|
58
|
+
expect(renderedElement.find('button').length).toBe(1);
|
|
59
|
+
});
|
|
60
|
+
it('should not render any context menu items', () => {
|
|
61
|
+
expect(renderedElement.find(ContextMenu.Item).length).toBe(0);
|
|
62
|
+
});
|
|
63
|
+
describe('Given toggle button gets clicked', () => {
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
renderedElement.find('button').simulate('click');
|
|
66
|
+
renderedElement.update();
|
|
67
|
+
});
|
|
68
|
+
it('should have active state set to true', () => {
|
|
69
|
+
expect(renderedElement.find('ConnectedReactPortal').prop('isOpened')).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
it('should render the actual context menu w/ items', () => {
|
|
72
|
+
expect(renderedElement.find(ContextMenu.Item).length).toBe(numMenuItems);
|
|
73
|
+
});
|
|
74
|
+
describe('Given the first item gets clicked', () => {
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
act(() => {
|
|
77
|
+
renderedElement.find(ContextMenu.Item).first().children().find('[data-test-id="contextMenuButton"]').first().simulate('click');
|
|
78
|
+
jest.runAllTimers();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
it('should call the related click handler', () => {
|
|
82
|
+
expect(mockItemAClick).toBeCalled();
|
|
83
|
+
});
|
|
84
|
+
it('should close the context menu', () => {
|
|
85
|
+
renderedElement.update();
|
|
86
|
+
expect(renderedElement.find(ContextMenu.Item).length).toBe(0);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('Given the backdrop gets clicked', () => {
|
|
90
|
+
beforeEach(() => {
|
|
91
|
+
renderedElement.find(Backdrop).children().find('[onClick]').simulate('click');
|
|
92
|
+
});
|
|
93
|
+
it('should have active state reset to false', () => {
|
|
94
|
+
expect(renderedElement.find('ConnectedReactPortal').prop('isOpened')).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
it('should close the context menu', () => {
|
|
97
|
+
expect(renderedElement.find(ContextMenu.Item).length).toBe(0);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|