@thecb/components 9.3.0-beta.2 → 9.3.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/dist/index.cjs.js +840 -617
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +171 -60
- package/dist/index.esm.js +837 -617
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/.DS_Store +0 -0
- package/src/components/atoms/button-with-action/ButtonWithAction.js +76 -70
- package/src/components/atoms/checkbox/Checkbox.js +9 -4
- package/src/components/atoms/checkbox/Checkbox.stories.js +3 -3
- package/src/components/atoms/country-dropdown/CountryDropdown.js +2 -2
- package/src/components/atoms/country-dropdown/CountryDropdown.stories.js +0 -1
- package/src/components/atoms/dropdown/Dropdown.js +77 -44
- package/src/components/atoms/dropdown/Dropdown.theme.js +8 -2
- package/src/components/atoms/form-layouts/FormInput.js +2 -3
- package/src/components/atoms/form-select/FormSelect.js +25 -34
- package/src/components/atoms/form-select/FormSelect.stories.js +2 -2
- package/src/components/atoms/icons/AccountNumberImage.js +2 -0
- package/src/components/atoms/icons/BankIcon.js +2 -0
- package/src/components/atoms/icons/CheckmarkIcon.js +2 -0
- package/src/components/atoms/icons/GenericCard.js +2 -0
- package/src/components/atoms/icons/GenericCardLarge.js +2 -0
- package/src/components/atoms/icons/KebabMenuIcon.d.ts +1 -0
- package/src/components/atoms/icons/KebabMenuIcon.js +38 -0
- package/src/components/atoms/icons/RoutingNumberImage.js +2 -0
- package/src/components/atoms/icons/TrashIcon.js +42 -40
- package/src/components/atoms/icons/icons.stories.js +3 -1
- package/src/components/atoms/icons/index.d.ts +1 -0
- package/src/components/atoms/icons/index.js +3 -1
- package/src/components/atoms/state-province-dropdown/StateProvinceDropdown.js +0 -1
- package/src/components/atoms/state-province-dropdown/StateProvinceDropdown.stories.js +0 -1
- package/src/components/molecules/address-form/AddressForm.js +1 -2
- package/src/components/molecules/email-form/EmailForm.js +3 -1
- package/src/components/molecules/index.d.ts +1 -0
- package/src/components/molecules/index.js +1 -0
- package/src/components/molecules/modal/Modal.js +2 -1
- package/src/components/molecules/payment-form-ach/PaymentFormACH.js +4 -5
- package/src/components/molecules/payment-form-card/PaymentFormCard.js +0 -2
- package/src/components/molecules/phone-form/PhoneForm.js +3 -1
- package/src/components/molecules/popover/Popover.js +1 -1
- package/src/components/molecules/popup-menu/PopupMenu.js +152 -0
- package/src/components/molecules/popup-menu/PopupMenu.stories.js +40 -0
- package/src/components/molecules/popup-menu/PopupMenu.styled.js +20 -0
- package/src/components/molecules/popup-menu/PopupMenu.theme.js +11 -0
- package/src/components/molecules/popup-menu/index.d.ts +25 -0
- package/src/components/molecules/popup-menu/index.js +3 -0
- package/src/components/molecules/popup-menu/popup-menu-item/PopupMenuItem.js +79 -0
- package/src/components/molecules/popup-menu/popup-menu-item/PopupMenuItem.styled.js +27 -0
- package/src/components/molecules/popup-menu/popup-menu-item/PopupMenuItem.theme.js +23 -0
- package/src/components/molecules/radio-section/RadioSection.js +10 -4
- package/src/components/molecules/radio-section/RadioSection.stories.js +1 -0
- package/src/components/molecules/radio-section/radio-button/RadioButton.js +2 -3
- package/src/components/molecules/terms-and-conditions/TermsAndConditions.stories.js +3 -1
- package/src/components/molecules/terms-and-conditions/TermsAndConditionsControlV1.js +0 -1
- package/src/components/molecules/terms-and-conditions/TermsAndConditionsControlV2.js +5 -2
- package/src/components/molecules/toast-notification/ToastNotification.js +75 -0
- package/src/components/molecules/toast-notification/ToastNotification.stories.js +67 -0
- package/src/components/molecules/toast-notification/index.d.ts +18 -0
- package/src/components/molecules/toast-notification/index.js +3 -0
- package/src/constants/colors.d.ts +1 -0
- package/src/constants/colors.js +5 -1
- package/src/hooks/index.js +3 -0
- package/src/hooks/use-toast-notification/index.d.ts +23 -0
- package/src/hooks/use-toast-notification/index.js +38 -0
- package/src/index.d.ts +2 -1
- package/src/index.js +2 -1
- package/src/types/common/ToastVariants.ts +6 -0
- package/src/types/common/index.ts +1 -0
- package/src/util/index.js +10 -2
- package/dist/src/apps/checkout/pages/payment/sub-pages/payment-amount/PaymentAmount_old.js +0 -49322
- package/src/components/.DS_Store +0 -0
- package/src/components/atoms/.DS_Store +0 -0
- package/src/components/atoms/icons/.DS_Store +0 -0
- /package/src/{util/useOutsideClick.js → hooks/use-outside-click/index.js} +0 -0
- /package/src/{util/useScrollTo.js → hooks/use-scroll-to/index.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const KebabMenuIcon: JSX.Element;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
const KebabMenuIcon = () => {
|
|
4
|
+
return (
|
|
5
|
+
<svg
|
|
6
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
7
|
+
width="21"
|
|
8
|
+
height="32"
|
|
9
|
+
viewBox="0 0 21 32"
|
|
10
|
+
fill="none"
|
|
11
|
+
>
|
|
12
|
+
<path
|
|
13
|
+
d="M0 4C0 1.79086 1.79086 0 4 0L17 0C19.2091 0 21 1.79086 21 4V28C21 30.2091 19.2091 32 17 32H4C1.79086 32 0 30.2091 0 28L0 4Z"
|
|
14
|
+
fill="none"
|
|
15
|
+
/>
|
|
16
|
+
<path
|
|
17
|
+
fillRule="evenodd"
|
|
18
|
+
clipRule="evenodd"
|
|
19
|
+
d="M10.5 6C9.39333 6 8.5 6.89333 8.5 8C8.5 9.10667 9.39333 10 10.5 10C11.6067 10 12.5 9.10667 12.5 8C12.5 6.89333 11.6067 6 10.5 6Z"
|
|
20
|
+
fill="#3B5BDB"
|
|
21
|
+
/>
|
|
22
|
+
<path
|
|
23
|
+
fillRule="evenodd"
|
|
24
|
+
clipRule="evenodd"
|
|
25
|
+
d="M10.5 14C9.39333 14 8.5 14.8933 8.5 16C8.5 17.1067 9.39333 18 10.5 18C11.6067 18 12.5 17.1067 12.5 16C12.5 14.8933 11.6067 14 10.5 14Z"
|
|
26
|
+
fill="#3B5BDB"
|
|
27
|
+
/>
|
|
28
|
+
<path
|
|
29
|
+
fillRule="evenodd"
|
|
30
|
+
clipRule="evenodd"
|
|
31
|
+
d="M10.5 22C9.39333 22 8.5 22.9067 8.5 24C8.5 25.0933 9.40667 26 10.5 26C11.5933 26 12.5 25.0933 12.5 24C12.5 22.9067 11.6067 22 10.5 22Z"
|
|
32
|
+
fill="#3B5BDB"
|
|
33
|
+
/>
|
|
34
|
+
</svg>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default KebabMenuIcon;
|
|
@@ -7,6 +7,8 @@ const RoutingNumberImage = () => {
|
|
|
7
7
|
width="371"
|
|
8
8
|
height="164"
|
|
9
9
|
viewBox="0 0 371 164"
|
|
10
|
+
role="img"
|
|
11
|
+
aria-label="A check with the routing number highlighted in the lower left hand corner"
|
|
10
12
|
>
|
|
11
13
|
<g fill="none" fillRule="evenodd" stroke="none" strokeWidth="1">
|
|
12
14
|
<g transform="translate(-365 -522)">
|
|
@@ -2,46 +2,48 @@ import React from "react";
|
|
|
2
2
|
import { fallbackValues } from "./Icons.theme";
|
|
3
3
|
import { themeComponent } from "../../../util/themeUtils";
|
|
4
4
|
|
|
5
|
-
const TrashIcon = ({ themeValues }) =>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<defs>
|
|
15
|
-
<path
|
|
16
|
-
d="M15,7 L14,15.5714286 C14,16.3571429 13.25,17 12.3333333,17 L12.3333333,17 L7.66666667,17 C6.75,17 6,16.3571429 6,15.5714286 L6,15.5714286 L5,7 L15,7 Z M12.1428571,3 L13,4 L16,4 L16,6 L4,6 L4,4 L7,4 L7.85714286,3 L12.1428571,3 Z"
|
|
17
|
-
id="trash-path-1"
|
|
18
|
-
></path>
|
|
19
|
-
</defs>
|
|
20
|
-
<g
|
|
21
|
-
id="trash-Icons-/-Small-/-20px-S-/-Trash---Mobile---20px"
|
|
22
|
-
stroke="none"
|
|
23
|
-
strokeWidth="1"
|
|
24
|
-
fill="none"
|
|
25
|
-
fillRule="evenodd"
|
|
5
|
+
const TrashIcon = ({ themeValues, iconFill }) => {
|
|
6
|
+
return (
|
|
7
|
+
<svg
|
|
8
|
+
width="20px"
|
|
9
|
+
height="20px"
|
|
10
|
+
viewBox="0 0 20 20"
|
|
11
|
+
version="1.1"
|
|
12
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
13
|
+
xmlnsXlink="http://www.w3.org/1999/xlink"
|
|
26
14
|
>
|
|
27
|
-
<
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
mask="
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
15
|
+
<defs>
|
|
16
|
+
<path
|
|
17
|
+
d="M15,7 L14,15.5714286 C14,16.3571429 13.25,17 12.3333333,17 L12.3333333,17 L7.66666667,17 C6.75,17 6,16.3571429 6,15.5714286 L6,15.5714286 L5,7 L15,7 Z M12.1428571,3 L13,4 L16,4 L16,6 L4,6 L4,4 L7,4 L7.85714286,3 L12.1428571,3 Z"
|
|
18
|
+
id="trash-path-1"
|
|
19
|
+
></path>
|
|
20
|
+
</defs>
|
|
21
|
+
<g
|
|
22
|
+
id="trash-Icons-/-Small-/-20px-S-/-Trash---Mobile---20px"
|
|
23
|
+
stroke="none"
|
|
24
|
+
strokeWidth="1"
|
|
25
|
+
fill="none"
|
|
26
|
+
fillRule="evenodd"
|
|
27
|
+
>
|
|
28
|
+
<mask id="trash-mask-2" fill="white">
|
|
29
|
+
<use xlinkHref="#trash-path-1"></use>
|
|
30
|
+
</mask>
|
|
31
|
+
<use
|
|
32
|
+
id="trash-Mask"
|
|
33
|
+
fill={iconFill ?? themeValues.singleIconColor}
|
|
34
|
+
fillRule="nonzero"
|
|
35
|
+
xlinkHref="#trash-path-1"
|
|
36
|
+
></use>
|
|
37
|
+
<polygon
|
|
38
|
+
id="trash-Path"
|
|
39
|
+
fill={iconFill ?? themeValues.singleIconColor}
|
|
40
|
+
fillRule="nonzero"
|
|
41
|
+
mask="url(#trash-mask-2)"
|
|
42
|
+
points="0 0 20 0 20 20 0 20"
|
|
43
|
+
></polygon>
|
|
44
|
+
</g>
|
|
45
|
+
</svg>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
46
48
|
|
|
47
49
|
export default themeComponent(TrashIcon, "Icons", fallbackValues, "primary");
|
|
@@ -38,7 +38,8 @@ import {
|
|
|
38
38
|
SuccessfulIcon,
|
|
39
39
|
VoidedIcon,
|
|
40
40
|
StatusUnknownIcon,
|
|
41
|
-
AutopayIcon
|
|
41
|
+
AutopayIcon,
|
|
42
|
+
KebabMenuIcon
|
|
42
43
|
} from "./index";
|
|
43
44
|
|
|
44
45
|
const story = page({
|
|
@@ -84,3 +85,4 @@ export const successfulIcon = () => <SuccessfulIcon />;
|
|
|
84
85
|
export const voidedIcon = () => <VoidedIcon />;
|
|
85
86
|
export const statusUnknownIcon = () => <StatusUnknownIcon />;
|
|
86
87
|
export const autopayIcon = () => <AutopayIcon />;
|
|
88
|
+
export const kebabMenuIcon = () => <KebabMenuIcon />;
|
|
@@ -86,6 +86,7 @@ import ArrowLeftCircleIconMedium from "./ArrowLeftCircleIconMedium";
|
|
|
86
86
|
import ChargebackIconMedium from "./ChargebackIconMedium";
|
|
87
87
|
import ChargebackReversalIconMedium from "./ChargebackReversalIconMedium";
|
|
88
88
|
import PlusCircleIcon from "./PlusCircleIcon";
|
|
89
|
+
import KebabMenuIcon from "./KebabMenuIcon";
|
|
89
90
|
|
|
90
91
|
export {
|
|
91
92
|
AccountsIcon,
|
|
@@ -175,5 +176,6 @@ export {
|
|
|
175
176
|
ArrowLeftCircleIconMedium,
|
|
176
177
|
ChargebackIconMedium,
|
|
177
178
|
ChargebackReversalIconMedium,
|
|
178
|
-
PlusCircleIcon
|
|
179
|
+
PlusCircleIcon,
|
|
180
|
+
KebabMenuIcon
|
|
179
181
|
};
|
|
@@ -58,7 +58,6 @@ const AddressForm = ({
|
|
|
58
58
|
labelTextWhenNoError="Country"
|
|
59
59
|
errorMessages={countryErrorMessages}
|
|
60
60
|
field={fields.country}
|
|
61
|
-
isRequired={true}
|
|
62
61
|
onChange={value => {
|
|
63
62
|
actions.fields.country.set(value);
|
|
64
63
|
// temporary measure to not dirty fields until
|
|
@@ -72,6 +71,7 @@ const AddressForm = ({
|
|
|
72
71
|
}}
|
|
73
72
|
showErrors={showErrors}
|
|
74
73
|
dataQa="Country"
|
|
74
|
+
isRequired={true}
|
|
75
75
|
/>
|
|
76
76
|
<FormInput
|
|
77
77
|
labelTextWhenNoError="Address"
|
|
@@ -92,7 +92,6 @@ const AddressForm = ({
|
|
|
92
92
|
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
93
93
|
autocompleteValue="address-line2"
|
|
94
94
|
dataQa="Address Line 2"
|
|
95
|
-
isRequired={false}
|
|
96
95
|
/>
|
|
97
96
|
<FormInput
|
|
98
97
|
labelTextWhenNoError="City"
|
|
@@ -19,7 +19,8 @@ const EmailForm = ({
|
|
|
19
19
|
handleSubmit = noop,
|
|
20
20
|
showWalletCheckbox,
|
|
21
21
|
saveToWallet,
|
|
22
|
-
walletCheckboxMarked
|
|
22
|
+
walletCheckboxMarked,
|
|
23
|
+
isRequired = false
|
|
23
24
|
}) => {
|
|
24
25
|
if (clearOnDismount) {
|
|
25
26
|
useEffect(() => () => actions.form.clear(), []);
|
|
@@ -48,6 +49,7 @@ const EmailForm = ({
|
|
|
48
49
|
isEmail
|
|
49
50
|
autocompleteValue="email"
|
|
50
51
|
dataQa="Email address"
|
|
52
|
+
isRequired={isRequired}
|
|
51
53
|
/>
|
|
52
54
|
{showWalletCheckbox && (
|
|
53
55
|
<Checkbox
|
|
@@ -37,5 +37,6 @@ export { default as TabSidebar } from "./tab-sidebar";
|
|
|
37
37
|
export { default as TermsAndConditions } from "./terms-and-conditions";
|
|
38
38
|
export { default as TermsAndConditionsModal } from "./terms-and-conditions-modal";
|
|
39
39
|
export { default as Timeout } from "./timeout";
|
|
40
|
+
export { default as ToastNotification } from "./toast-notification";
|
|
40
41
|
export { default as WelcomeModule } from "./welcome-module";
|
|
41
42
|
export { default as WorkflowTile } from "./workflow-tile";
|
|
@@ -108,8 +108,8 @@ const PaymentFormACH = ({
|
|
|
108
108
|
fieldActions={actions.fields.confirmRoutingNumber}
|
|
109
109
|
showErrors={showErrors}
|
|
110
110
|
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
111
|
-
isRequired={true}
|
|
112
111
|
isNum
|
|
112
|
+
isRequired={true}
|
|
113
113
|
/>
|
|
114
114
|
<FormInput
|
|
115
115
|
labelTextWhenNoError="Account number"
|
|
@@ -118,7 +118,6 @@ const PaymentFormACH = ({
|
|
|
118
118
|
field={fields.accountNumber}
|
|
119
119
|
fieldActions={actions.fields.accountNumber}
|
|
120
120
|
showErrors={showErrors}
|
|
121
|
-
isRequired={true}
|
|
122
121
|
isNum
|
|
123
122
|
helperModal={() => (
|
|
124
123
|
<AccountAndRoutingModal
|
|
@@ -132,22 +131,22 @@ const PaymentFormACH = ({
|
|
|
132
131
|
/>
|
|
133
132
|
)}
|
|
134
133
|
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
134
|
+
isRequired={true}
|
|
135
135
|
/>
|
|
136
136
|
<FormInput
|
|
137
137
|
labelTextWhenNoError="Confirm account number"
|
|
138
138
|
dataQa="Confirm account number"
|
|
139
|
-
isRequired={true}
|
|
140
139
|
errorMessages={confirmAccountNumberErrors}
|
|
141
140
|
field={fields.confirmAccountNumber}
|
|
142
141
|
fieldActions={actions.fields.confirmAccountNumber}
|
|
143
142
|
showErrors={showErrors}
|
|
144
143
|
onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
|
|
144
|
+
isRequired={true}
|
|
145
145
|
isNum
|
|
146
146
|
/>
|
|
147
147
|
{allowBankAccountType && (
|
|
148
148
|
<FormSelect
|
|
149
149
|
labelTextWhenNoError="Account type"
|
|
150
|
-
isRequired={true}
|
|
151
150
|
dataQa="Account type"
|
|
152
151
|
options={[
|
|
153
152
|
{ text: "Select account type", value: "" },
|
|
@@ -158,6 +157,7 @@ const PaymentFormACH = ({
|
|
|
158
157
|
showErrors={showErrors}
|
|
159
158
|
errorMessages={accountTypeErrors}
|
|
160
159
|
field={fields.accountType}
|
|
160
|
+
isRequired={true}
|
|
161
161
|
/>
|
|
162
162
|
)}
|
|
163
163
|
{!hideDefaultPayment && (
|
|
@@ -188,7 +188,6 @@ const PaymentFormACH = ({
|
|
|
188
188
|
showCheckbox={false}
|
|
189
189
|
description="View"
|
|
190
190
|
terms={termsContent}
|
|
191
|
-
isRequired={true}
|
|
192
191
|
/>
|
|
193
192
|
</Cover>
|
|
194
193
|
)}
|
|
@@ -98,7 +98,6 @@ const PaymentFormCard = ({
|
|
|
98
98
|
{!hideZipCode && (
|
|
99
99
|
<CountryDropdown
|
|
100
100
|
labelTextWhenNoError="Country"
|
|
101
|
-
isRequired={true}
|
|
102
101
|
errorMessages={countryErrorMessages}
|
|
103
102
|
field={fields.country}
|
|
104
103
|
onChange={value => {
|
|
@@ -211,7 +210,6 @@ const PaymentFormCard = ({
|
|
|
211
210
|
showCheckbox={false}
|
|
212
211
|
description="View"
|
|
213
212
|
terms={termsContent}
|
|
214
|
-
isRequired={true}
|
|
215
213
|
/>
|
|
216
214
|
</Cover>
|
|
217
215
|
)}
|
|
@@ -19,7 +19,8 @@ const PhoneForm = ({
|
|
|
19
19
|
handleSubmit = noop,
|
|
20
20
|
showWalletCheckbox,
|
|
21
21
|
saveToWallet,
|
|
22
|
-
walletCheckboxMarked
|
|
22
|
+
walletCheckboxMarked,
|
|
23
|
+
isRequired = false
|
|
23
24
|
}) => {
|
|
24
25
|
if (clearOnDismount) {
|
|
25
26
|
useEffect(() => () => actions.form.clear(), []);
|
|
@@ -43,6 +44,7 @@ const PhoneForm = ({
|
|
|
43
44
|
autocompleteValue="tel-national"
|
|
44
45
|
dataQa="Phone number"
|
|
45
46
|
isNum={true}
|
|
47
|
+
isRequired={isRequired}
|
|
46
48
|
/>
|
|
47
49
|
{showWalletCheckbox && (
|
|
48
50
|
<Checkbox
|
|
@@ -4,7 +4,7 @@ import Text from "../../atoms/text";
|
|
|
4
4
|
import Paragraph from "../../atoms/paragraph";
|
|
5
5
|
import { Box } from "../../atoms/layouts";
|
|
6
6
|
import ButtonWithAction from "../../atoms/button-with-action";
|
|
7
|
-
import { useOutsideClick } from "../../../
|
|
7
|
+
import { useOutsideClick } from "../../../hooks";
|
|
8
8
|
import { noop } from "../../../util/general";
|
|
9
9
|
import { fallbackValues } from "./Popover.theme";
|
|
10
10
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect } from "react";
|
|
2
|
+
import { themeComponent } from "../../../util/themeUtils";
|
|
3
|
+
import Text from "../../atoms/text";
|
|
4
|
+
import { Box } from "../../atoms/layouts";
|
|
5
|
+
import PopupMenuItem from "./popup-menu-item/PopupMenuItem";
|
|
6
|
+
import { fallbackValues } from "./PopupMenu.theme";
|
|
7
|
+
import { PopupMenuContainer, PopupMenuTriggerButton } from "./PopupMenu.styled";
|
|
8
|
+
|
|
9
|
+
const PopupMenu = ({
|
|
10
|
+
menuId = "popup-menu",
|
|
11
|
+
menuItems = [],
|
|
12
|
+
themeValues,
|
|
13
|
+
triggerText = "trigger text",
|
|
14
|
+
hasIcon = false,
|
|
15
|
+
icon: Icon,
|
|
16
|
+
iconHelpText = "", // for screen-readers, required if using an icon for trigger
|
|
17
|
+
menuFocus,
|
|
18
|
+
containerExtraStyles,
|
|
19
|
+
textExtraStyles,
|
|
20
|
+
minWidth = "250px",
|
|
21
|
+
maxWidth = "300px",
|
|
22
|
+
height = "auto",
|
|
23
|
+
position,
|
|
24
|
+
transform = "none",
|
|
25
|
+
buttonExtraStyles,
|
|
26
|
+
popupExtraStyles
|
|
27
|
+
}) => {
|
|
28
|
+
const {
|
|
29
|
+
hoverColor,
|
|
30
|
+
activeColor,
|
|
31
|
+
menuTriggerColor,
|
|
32
|
+
backgroundColor
|
|
33
|
+
} = themeValues;
|
|
34
|
+
const { top = `${height}px`, right = "auto", bottom = "auto", left = "0" } =
|
|
35
|
+
position ?? {};
|
|
36
|
+
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
37
|
+
const menuRef = useRef();
|
|
38
|
+
const triggerRef = useRef();
|
|
39
|
+
const toggleMenu = menuState => setIsMenuOpen(menuState);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const checkIfClickedOutside = e => {
|
|
43
|
+
// If the menu is open and the clicked target is not within the menu or the trigger
|
|
44
|
+
if (
|
|
45
|
+
isMenuOpen &&
|
|
46
|
+
menuRef.current &&
|
|
47
|
+
!menuRef.current.contains(e.target) &&
|
|
48
|
+
triggerRef.current &&
|
|
49
|
+
!triggerRef.current.contains(e.target)
|
|
50
|
+
) {
|
|
51
|
+
toggleMenu(false);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
document.addEventListener("click", checkIfClickedOutside);
|
|
56
|
+
|
|
57
|
+
return () => {
|
|
58
|
+
document.removeEventListener("click", checkIfClickedOutside);
|
|
59
|
+
};
|
|
60
|
+
}, [isMenuOpen]);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<PopupMenuContainer extraStyles={containerExtraStyles}>
|
|
64
|
+
<PopupMenuTriggerButton
|
|
65
|
+
ref={triggerRef}
|
|
66
|
+
action={() => {
|
|
67
|
+
toggleMenu(!isMenuOpen);
|
|
68
|
+
}}
|
|
69
|
+
onKeyDown={e => {
|
|
70
|
+
if (e.key === "Escape") {
|
|
71
|
+
toggleMenu(false);
|
|
72
|
+
}
|
|
73
|
+
}}
|
|
74
|
+
contentOverride
|
|
75
|
+
variant="smallGhost"
|
|
76
|
+
tabIndex="0"
|
|
77
|
+
id={menuId}
|
|
78
|
+
borderRadius="8px"
|
|
79
|
+
aria-haspopup="true"
|
|
80
|
+
aria-expanded={isMenuOpen}
|
|
81
|
+
aria-controls={`${menuId}-container`}
|
|
82
|
+
extraStyles={buttonExtraStyles}
|
|
83
|
+
>
|
|
84
|
+
{hasIcon && (
|
|
85
|
+
<>
|
|
86
|
+
<Icon />
|
|
87
|
+
<Box padding="0" srOnly>
|
|
88
|
+
<Text id={`btn${menuId}_info`}>{iconHelpText}</Text>
|
|
89
|
+
</Box>
|
|
90
|
+
</>
|
|
91
|
+
)}
|
|
92
|
+
{!hasIcon && (
|
|
93
|
+
<Text
|
|
94
|
+
color={menuTriggerColor}
|
|
95
|
+
extraStyles={`&:active { color: ${activeColor}; } &:hover { color: ${hoverColor} }; ${textExtraStyles}`}
|
|
96
|
+
>
|
|
97
|
+
{triggerText}
|
|
98
|
+
</Text>
|
|
99
|
+
)}
|
|
100
|
+
</PopupMenuTriggerButton>
|
|
101
|
+
<Box
|
|
102
|
+
as="div"
|
|
103
|
+
id={`${menuId}-container`}
|
|
104
|
+
ref={menuRef}
|
|
105
|
+
onKeyDown={e => {
|
|
106
|
+
if (e.key === "Escape") {
|
|
107
|
+
toggleMenu(false);
|
|
108
|
+
}
|
|
109
|
+
}}
|
|
110
|
+
background={backgroundColor}
|
|
111
|
+
borderRadius="8px"
|
|
112
|
+
boxShadow={`
|
|
113
|
+
0px 7px 32px 0px rgba(41, 42, 51, 0.2),
|
|
114
|
+
0px 1px 4px 0px rgba(41, 42, 51, 0.2),
|
|
115
|
+
0px 1px 8px -1px rgba(41, 42, 51, 0.3);
|
|
116
|
+
`}
|
|
117
|
+
role="menu"
|
|
118
|
+
aria-labelledby={menuId}
|
|
119
|
+
tabIndex={menuFocus && isMenuOpen ? "0" : "-1"}
|
|
120
|
+
minWidth={minWidth}
|
|
121
|
+
maxWidth={maxWidth}
|
|
122
|
+
extraStyles={`
|
|
123
|
+
display: ${isMenuOpen ? "block" : "none"};
|
|
124
|
+
position: absolute;
|
|
125
|
+
padding: 8px 8px 3px 8px;
|
|
126
|
+
top: ${top};
|
|
127
|
+
left: ${left};
|
|
128
|
+
right: ${right};
|
|
129
|
+
bottom: ${bottom};
|
|
130
|
+
height: ${height};
|
|
131
|
+
transform: ${transform};
|
|
132
|
+
${popupExtraStyles};
|
|
133
|
+
`}
|
|
134
|
+
>
|
|
135
|
+
{menuItems.map((item, index) => (
|
|
136
|
+
<PopupMenuItem
|
|
137
|
+
key={index}
|
|
138
|
+
id={`${menuId}-item-${index}`}
|
|
139
|
+
closeMenuCallback={() => {
|
|
140
|
+
toggleMenu(false);
|
|
141
|
+
// focus back to trigger button when menu closes
|
|
142
|
+
triggerRef.current.focus();
|
|
143
|
+
}}
|
|
144
|
+
{...item}
|
|
145
|
+
/>
|
|
146
|
+
))}
|
|
147
|
+
</Box>
|
|
148
|
+
</PopupMenuContainer>
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export default themeComponent(PopupMenu, "PopupMenu", fallbackValues);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { KebabMenuIcon, TrashIcon } from "../../atoms";
|
|
3
|
+
import PopupMenu from "./PopupMenu";
|
|
4
|
+
import page from "../../../../.storybook/page";
|
|
5
|
+
import { noop } from "../../../util/general";
|
|
6
|
+
|
|
7
|
+
const menuItems = [
|
|
8
|
+
{
|
|
9
|
+
text: "Account Details",
|
|
10
|
+
action: noop
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
text: "Remove",
|
|
14
|
+
action: noop,
|
|
15
|
+
isDeleteAction: true,
|
|
16
|
+
hasIcon: true,
|
|
17
|
+
icon: TrashIcon
|
|
18
|
+
}
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const story = page({
|
|
22
|
+
title: "Components|Molecules/PopupMenu",
|
|
23
|
+
Component: PopupMenu
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export const popupMenu = () => (
|
|
27
|
+
<PopupMenu
|
|
28
|
+
hasIcon="true"
|
|
29
|
+
menuItems={menuItems}
|
|
30
|
+
icon={KebabMenuIcon}
|
|
31
|
+
minWidth={"50px"}
|
|
32
|
+
maxWidth={"208px"}
|
|
33
|
+
position={{ top: "0", left: "auto", right: "63px" }}
|
|
34
|
+
menuId={"menuId"}
|
|
35
|
+
containerExtraStyles={`margin-bottom: 100px;`}
|
|
36
|
+
buttonExtraStyles={`margin: 0 0 0 auto;`}
|
|
37
|
+
popupExtraStyles={`padding: 8px 8px 3px 8px;`}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
export default story;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
import { ButtonWithAction } from "../../atoms";
|
|
3
|
+
import { Box } from "../../atoms";
|
|
4
|
+
|
|
5
|
+
export const PopupMenuContainer = styled(Box)`
|
|
6
|
+
display: flex;
|
|
7
|
+
position: relative;
|
|
8
|
+
padding: 0;
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
export const PopupMenuTriggerButton = styled(ButtonWithAction)`
|
|
12
|
+
padding: 10px 15px;
|
|
13
|
+
min-width: auto;
|
|
14
|
+
&:active,
|
|
15
|
+
&:focus {
|
|
16
|
+
outline: none;
|
|
17
|
+
border: 1px solid rgba(196, 206, 244, 1);
|
|
18
|
+
background-color: rgba(235, 239, 251, 1);
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
import Expand from "../../../util/expand";
|
|
3
|
+
|
|
4
|
+
export interface PopupMenuProps {
|
|
5
|
+
menuId?: string;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
triggerText?: string | JSX.Element;
|
|
8
|
+
hasIcon?: boolean;
|
|
9
|
+
iconHelpText?: string; // for screen-readers, required if using an icon for trigger
|
|
10
|
+
menuFocus?: boolean;
|
|
11
|
+
containerExtraStyles?: string;
|
|
12
|
+
textExtraStyles?: string;
|
|
13
|
+
minWidth?: string;
|
|
14
|
+
maxWidth?: string;
|
|
15
|
+
height?: string;
|
|
16
|
+
position?: { top: string; right: string; bottom: string; left: string };
|
|
17
|
+
transform?: string;
|
|
18
|
+
disclosedExtraStyles?: string;
|
|
19
|
+
borderColor?: string;
|
|
20
|
+
backgroundColor?: string;
|
|
21
|
+
popupExtraStyles?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const PopupMenu: React.FC<Expand<PopupMenuProps> &
|
|
25
|
+
React.HTMLAttributes<HTMLElement>>;
|