@thecb/components 10.6.1-beta.0 → 10.6.2-beta.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 +1055 -304
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +42 -18
- package/dist/index.esm.js +1051 -304
- package/dist/index.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/components/atoms/icons/DisabledAccountsAddIcon.js +200 -0
- package/src/components/atoms/icons/DisabledPaymentMethodsAddIcon.js +62 -0
- package/src/components/atoms/icons/DisabledPropertiesAddIcon.js +54 -0
- package/src/components/atoms/icons/PropertiesAddIcon.js +1 -0
- package/src/components/atoms/icons/WalletIconSmall.js +3 -7
- package/src/components/atoms/icons/icons.stories.js +11 -1
- package/src/components/atoms/icons/index.js +7 -1
- package/src/components/atoms/index.js +1 -0
- package/src/components/atoms/placeholder/Placeholder.js +150 -108
- package/src/components/atoms/placeholder/Placeholder.stories.js +2 -0
- package/src/components/atoms/placeholder/Placeholder.theme.js +8 -2
- package/src/components/atoms/spinner/Spinner.js +13 -5
- package/src/components/atoms/spinner/index.d.ts +4 -0
- package/src/components/atoms/toggle-switch/ToggleSwitch.js +33 -61
- package/src/components/atoms/toggle-switch/ToggleSwitch.stories.js +2 -3
- package/src/components/atoms/toggle-switch/ToggleSwitch.theme.js +5 -5
- package/src/components/atoms/wallet-name/WalletName.js +102 -0
- package/src/components/atoms/wallet-name/WalletName.stories.js +24 -0
- package/src/components/atoms/wallet-name/index.d.ts +15 -0
- package/src/components/atoms/wallet-name/index.js +3 -0
- package/src/components/molecules/editable-list/EditableList.js +3 -1
- package/src/components/molecules/editable-list/EditableList.stories.js +1 -3
- package/src/components/molecules/link-card/LinkCard.theme.js +7 -21
- package/src/components/molecules/modal/Modal.js +6 -217
- package/src/components/molecules/modal/Modal.stories.js +57 -13
- package/src/components/molecules/modal/ModalControlV1.js +234 -0
- package/src/components/molecules/modal/ModalControlV2.js +218 -0
- package/src/components/molecules/modal/__private__/ButtonLayoutWrapper.js +24 -0
- package/src/components/molecules/modal/__private__/CancelButton.js +36 -0
- package/src/components/molecules/modal/__private__/CloseButton.js +34 -0
- package/src/components/molecules/modal/__private__/CloseIconButton.js +39 -0
- package/src/components/molecules/modal/__private__/ContinueButton.js +45 -0
- package/src/components/molecules/modal/__private__/index.d.ts +59 -0
- package/src/components/molecules/modal/__private__/index.js +5 -0
- package/src/components/molecules/partial-amount-form/PartialAmountField.js +49 -0
- package/src/components/molecules/partial-amount-form/PartialAmountForm.js +8 -22
- package/src/components/molecules/radio-section/InnerRadioSection.js +7 -2
- package/src/components/molecules/registration-form/RegistrationForm.js +6 -3
- package/src/components/molecules/registration-form/RegistrationForm.state.js +4 -3
- package/src/constants/style_constants.d.ts +18 -6
- package/src/constants/style_constants.js +23 -20
- package/src/components/atoms/.DS_Store +0 -0
- package/src/components/atoms/icons/.DS_Store +0 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import React, { Fragment, useContext, useEffect, useRef } from "react";
|
|
2
|
+
import { ThemeContext } from "styled-components";
|
|
3
|
+
import AriaModal from "react-aria-modal";
|
|
4
|
+
import { WHITE, ATHENS_GREY, SILVER_GREY } from "../../../constants/colors";
|
|
5
|
+
import { FONT_WEIGHT_SEMIBOLD } from "../../../constants/style_constants";
|
|
6
|
+
import Paragraph from "../../atoms/paragraph";
|
|
7
|
+
import Title from "../../atoms/title";
|
|
8
|
+
import ButtonWithAction from "../../atoms/button-with-action";
|
|
9
|
+
import { Box, Stack, Cluster } from "../../atoms/layouts";
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
Default Modal molecule
|
|
13
|
+
Uses react-aria-modal behind the scenes for a11y purposes
|
|
14
|
+
Styling accomplished with our atoms / layout primitives
|
|
15
|
+
|
|
16
|
+
Cancel button will (for now) always use hideModal as its action
|
|
17
|
+
Continue button takes an action, if you want to navigate to
|
|
18
|
+
a different route (as with a link) connect() and use "push" from @thecb/connected-react-router
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const getApplicationNode = () => document.getElementById("root");
|
|
22
|
+
|
|
23
|
+
const Modal = ({
|
|
24
|
+
hideModal,
|
|
25
|
+
onExit = hideModal,
|
|
26
|
+
continueAction,
|
|
27
|
+
isContinueActionDisabled = false,
|
|
28
|
+
cancelAction,
|
|
29
|
+
modalOpen,
|
|
30
|
+
modalHeaderText,
|
|
31
|
+
modalBodyText,
|
|
32
|
+
cancelButtonText = "Cancel",
|
|
33
|
+
continueButtonText = "Continue",
|
|
34
|
+
closeButtonText = "Close",
|
|
35
|
+
modalHeaderBg = WHITE,
|
|
36
|
+
modalBodyBg = ATHENS_GREY,
|
|
37
|
+
useDangerButton = false,
|
|
38
|
+
defaultWrapper = true,
|
|
39
|
+
onlyCloseButton = false,
|
|
40
|
+
noButtons = false, // for instances where modal is closed automatically
|
|
41
|
+
maxHeight,
|
|
42
|
+
underlayClickExits = true,
|
|
43
|
+
noBorder,
|
|
44
|
+
customWidth,
|
|
45
|
+
isLoading,
|
|
46
|
+
buttonExtraStyles,
|
|
47
|
+
children,
|
|
48
|
+
dataQa = null,
|
|
49
|
+
initialFocusSelector = "",
|
|
50
|
+
blurUnderlay = true
|
|
51
|
+
}) => {
|
|
52
|
+
const { isMobile } = useContext(ThemeContext);
|
|
53
|
+
|
|
54
|
+
// `AriaModal` uses `focus-trap` as a transient dependency. It automatically looks
|
|
55
|
+
// for a tabbable node when the modal mounts. When it doesn't find one, it looks to
|
|
56
|
+
// the `fallbackFocus` option. However, React does not guarantee the ref supplied to
|
|
57
|
+
// this option will be populated on initial render when `focus-trap` is checking
|
|
58
|
+
// these option. When there are no buttons in the modal, this causes an error.
|
|
59
|
+
// Because `focus-trap` cannot be disabled, the ref itself requires a default value
|
|
60
|
+
// to satisfy `focus-trap` until React populates it with the real ref value.
|
|
61
|
+
//
|
|
62
|
+
// See:
|
|
63
|
+
// - https://react.dev/reference/react/useRef#caveats
|
|
64
|
+
// - https://github.com/davidtheclark/react-aria-modal/pull/103
|
|
65
|
+
// - https://github.com/focus-trap/focus-trap?tab=readme-ov-file#createoptions
|
|
66
|
+
const modalContainerRef = useRef("#react-aria-modal-dialog");
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div ref={modalContainerRef} tabIndex="-1" data-qa={dataQa}>
|
|
70
|
+
{modalOpen && (
|
|
71
|
+
<AriaModal
|
|
72
|
+
focusTrapOptions={{
|
|
73
|
+
// fallback to resolve Jest unit test errors when tabbable doesn't exist in jsdom https://github.com/focus-trap/focus-trap-react/issues/91
|
|
74
|
+
fallbackFocus: modalContainerRef?.current
|
|
75
|
+
}}
|
|
76
|
+
onExit={onExit}
|
|
77
|
+
getApplicationNode={getApplicationNode}
|
|
78
|
+
titleText={modalHeaderText}
|
|
79
|
+
underlayStyle={{
|
|
80
|
+
display: "flex",
|
|
81
|
+
flexDirection: "column",
|
|
82
|
+
justifyContent: "center",
|
|
83
|
+
alignItems: "center",
|
|
84
|
+
background: "rgba(41, 42, 51, 0.45)",
|
|
85
|
+
backdropFilter: blurUnderlay ? "blur(4px)" : "none",
|
|
86
|
+
WebkitBackdropFilter: blurUnderlay ? "blur(4px)" : "none"
|
|
87
|
+
}}
|
|
88
|
+
dialogStyle={{
|
|
89
|
+
width: customWidth || "615px",
|
|
90
|
+
overflow: "auto"
|
|
91
|
+
}}
|
|
92
|
+
underlayClickExits={underlayClickExits}
|
|
93
|
+
aria-modal={true}
|
|
94
|
+
initialFocus={initialFocusSelector}
|
|
95
|
+
focusDialog={!initialFocusSelector} // Focus the dialogue box itself if no selector for initial focus was provided
|
|
96
|
+
>
|
|
97
|
+
<Box
|
|
98
|
+
padding="0"
|
|
99
|
+
borderRadius="2px"
|
|
100
|
+
boxShadow="inset 0px -2px 0px 0px rgb(0, 80, 149)"
|
|
101
|
+
>
|
|
102
|
+
<Box background={modalHeaderBg} padding="1.5rem">
|
|
103
|
+
<Cluster justify="flex-start" align="center">
|
|
104
|
+
<Title as="h2" weight={FONT_WEIGHT_SEMIBOLD} fontSize="1.25rem">
|
|
105
|
+
{modalHeaderText}
|
|
106
|
+
</Title>
|
|
107
|
+
</Cluster>
|
|
108
|
+
</Box>
|
|
109
|
+
<Box background={modalBodyBg} padding="1.5rem">
|
|
110
|
+
<Stack childGap="1.5rem">
|
|
111
|
+
<Box
|
|
112
|
+
borderWidthOverride={noBorder && "0 0 2px 0"}
|
|
113
|
+
borderColor={!noBorder && SILVER_GREY}
|
|
114
|
+
padding={!noBorder && "0 0 1.5rem 0"}
|
|
115
|
+
extraStyles={
|
|
116
|
+
maxHeight ? `max-height: ${maxHeight}; overflow: auto;` : ``
|
|
117
|
+
}
|
|
118
|
+
>
|
|
119
|
+
{defaultWrapper ? (
|
|
120
|
+
<Paragraph variant="p">{modalBodyText}</Paragraph>
|
|
121
|
+
) : (
|
|
122
|
+
<Box padding={maxHeight ? "0 0 1rem 0" : "0"}>
|
|
123
|
+
{modalBodyText}
|
|
124
|
+
</Box>
|
|
125
|
+
)}
|
|
126
|
+
</Box>
|
|
127
|
+
<Box padding="0">
|
|
128
|
+
<Stack
|
|
129
|
+
direction="row"
|
|
130
|
+
justify="flex-end"
|
|
131
|
+
align="center"
|
|
132
|
+
childGap="0rem"
|
|
133
|
+
>
|
|
134
|
+
{!noButtons && (
|
|
135
|
+
<>
|
|
136
|
+
{!onlyCloseButton ? (
|
|
137
|
+
<Fragment>
|
|
138
|
+
{isMobile ? (
|
|
139
|
+
<Stack childGap="1rem" direction="row">
|
|
140
|
+
<Box width="100%" padding="0">
|
|
141
|
+
<ButtonWithAction
|
|
142
|
+
variant="secondary"
|
|
143
|
+
action={
|
|
144
|
+
cancelAction ? cancelAction : hideModal
|
|
145
|
+
}
|
|
146
|
+
text={cancelButtonText}
|
|
147
|
+
dataQa={cancelButtonText}
|
|
148
|
+
extraStyles={buttonExtraStyles}
|
|
149
|
+
className="modal-cancel-button"
|
|
150
|
+
role="button"
|
|
151
|
+
name={cancelButtonText}
|
|
152
|
+
/>
|
|
153
|
+
</Box>
|
|
154
|
+
<Box width="100%" padding="0">
|
|
155
|
+
<ButtonWithAction
|
|
156
|
+
variant={
|
|
157
|
+
useDangerButton ? "danger" : "primary"
|
|
158
|
+
}
|
|
159
|
+
action={continueAction}
|
|
160
|
+
text={continueButtonText}
|
|
161
|
+
dataQa={continueButtonText}
|
|
162
|
+
isLoading={isLoading}
|
|
163
|
+
disabled={isContinueActionDisabled}
|
|
164
|
+
extraStyles={buttonExtraStyles}
|
|
165
|
+
className="modal-continue-button"
|
|
166
|
+
role="button"
|
|
167
|
+
name={continueButtonText}
|
|
168
|
+
/>
|
|
169
|
+
</Box>
|
|
170
|
+
</Stack>
|
|
171
|
+
) : (
|
|
172
|
+
<Stack
|
|
173
|
+
childGap="1rem"
|
|
174
|
+
direction="row"
|
|
175
|
+
justify="flex-end"
|
|
176
|
+
>
|
|
177
|
+
<ButtonWithAction
|
|
178
|
+
variant="secondary"
|
|
179
|
+
action={
|
|
180
|
+
cancelAction ? cancelAction : hideModal
|
|
181
|
+
}
|
|
182
|
+
text={cancelButtonText}
|
|
183
|
+
dataQa={cancelButtonText}
|
|
184
|
+
extraStyles={buttonExtraStyles}
|
|
185
|
+
className="modal-cancel-button"
|
|
186
|
+
role="button"
|
|
187
|
+
name={cancelButtonText}
|
|
188
|
+
/>
|
|
189
|
+
<ButtonWithAction
|
|
190
|
+
variant={
|
|
191
|
+
useDangerButton ? "danger" : "primary"
|
|
192
|
+
}
|
|
193
|
+
action={continueAction}
|
|
194
|
+
text={continueButtonText}
|
|
195
|
+
dataQa={continueButtonText}
|
|
196
|
+
isLoading={isLoading}
|
|
197
|
+
disabled={isContinueActionDisabled}
|
|
198
|
+
extraStyles={buttonExtraStyles}
|
|
199
|
+
className="modal-continue-button"
|
|
200
|
+
role="button"
|
|
201
|
+
name={continueButtonText}
|
|
202
|
+
/>
|
|
203
|
+
</Stack>
|
|
204
|
+
)}
|
|
205
|
+
</Fragment>
|
|
206
|
+
) : (
|
|
207
|
+
<Box padding="0.5rem">
|
|
208
|
+
<ButtonWithAction
|
|
209
|
+
action={hideModal}
|
|
210
|
+
variant="primary"
|
|
211
|
+
text={closeButtonText}
|
|
212
|
+
dataQa={closeButtonText}
|
|
213
|
+
extraStyles={buttonExtraStyles}
|
|
214
|
+
className="modal-close-button"
|
|
215
|
+
role="button"
|
|
216
|
+
name={closeButtonText}
|
|
217
|
+
/>
|
|
218
|
+
</Box>
|
|
219
|
+
)}
|
|
220
|
+
</>
|
|
221
|
+
)}
|
|
222
|
+
</Stack>
|
|
223
|
+
</Box>
|
|
224
|
+
</Stack>
|
|
225
|
+
</Box>
|
|
226
|
+
</Box>
|
|
227
|
+
</AriaModal>
|
|
228
|
+
)}
|
|
229
|
+
{children}
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
export default Modal;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import React, { useContext, useRef } from "react";
|
|
2
|
+
import AriaModal from "react-aria-modal";
|
|
3
|
+
import { ThemeContext } from "styled-components";
|
|
4
|
+
import { WHITE, ATHENS_GREY, SILVER_GREY } from "../../../constants/colors";
|
|
5
|
+
import {
|
|
6
|
+
BORDER_RADIUS,
|
|
7
|
+
BORDER_THIN,
|
|
8
|
+
FONT_SIZE,
|
|
9
|
+
FONT_WEIGHT_SEMIBOLD,
|
|
10
|
+
SPACING
|
|
11
|
+
} from "../../../constants/style_constants";
|
|
12
|
+
import { noop } from "../../../util/general";
|
|
13
|
+
import Paragraph from "../../atoms/paragraph";
|
|
14
|
+
import Title from "../../atoms/title";
|
|
15
|
+
import { Box, Cluster } from "../../atoms/layouts";
|
|
16
|
+
import withWindowSize from "../../withWindowSize";
|
|
17
|
+
import {
|
|
18
|
+
ButtonLayoutWrapper,
|
|
19
|
+
CancelButton,
|
|
20
|
+
CloseButton,
|
|
21
|
+
CloseIconButton,
|
|
22
|
+
ContinueButton
|
|
23
|
+
} from "./__private__";
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
Default Modal molecule
|
|
27
|
+
Uses react-aria-modal behind the scenes for a11y purposes
|
|
28
|
+
Styling accomplished with our atoms / layout primitives
|
|
29
|
+
|
|
30
|
+
Cancel button will (for now) always use hideModal as its action
|
|
31
|
+
Continue button takes an action, if you want to navigate to
|
|
32
|
+
a different route (as with a link) connect() and use "push" from @thecb/connected-react-router
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const getApplicationNode = () => document.getElementById("root");
|
|
36
|
+
|
|
37
|
+
const Modal = ({
|
|
38
|
+
blurUnderlay = true,
|
|
39
|
+
buttonExtraStyles = "",
|
|
40
|
+
cancelAction = noop,
|
|
41
|
+
cancelButtonText = "Cancel",
|
|
42
|
+
cancelButtonVariant = "secondary",
|
|
43
|
+
children = [],
|
|
44
|
+
closeButtonText = "Close",
|
|
45
|
+
continueAction = noop,
|
|
46
|
+
continueButtonText = "Continue",
|
|
47
|
+
continueButtonVariant = "primary",
|
|
48
|
+
continueURL = "",
|
|
49
|
+
customWidth = "",
|
|
50
|
+
dataQa = null,
|
|
51
|
+
defaultWrapper = true,
|
|
52
|
+
hideModal = noop,
|
|
53
|
+
initialFocusSelector = "",
|
|
54
|
+
isContinueActionDisabled = false,
|
|
55
|
+
isLoading = false,
|
|
56
|
+
maxHeight = "",
|
|
57
|
+
modalBodyBg = "",
|
|
58
|
+
modalBodyText = "",
|
|
59
|
+
modalHeaderBg = "",
|
|
60
|
+
modalHeaderText = "",
|
|
61
|
+
modalOpen = false,
|
|
62
|
+
noButtons = false, // for instances where modal is closed automatically
|
|
63
|
+
onExit = hideModal,
|
|
64
|
+
onlyCloseButton = false,
|
|
65
|
+
onlyContinueButton = false,
|
|
66
|
+
showCloseIconButton = false,
|
|
67
|
+
underlayClickExits = true,
|
|
68
|
+
useDangerButton = false
|
|
69
|
+
}) => {
|
|
70
|
+
const { isMobile } = useContext(ThemeContext);
|
|
71
|
+
|
|
72
|
+
// `AriaModal` uses `focus-trap` as a transient dependency. It automatically looks
|
|
73
|
+
// for a tabbable node when the modal mounts. When it doesn't find one, it looks to
|
|
74
|
+
// the `fallbackFocus` option. However, React does not guarantee the ref supplied to
|
|
75
|
+
// this option will be populated on initial render when `focus-trap` is checking
|
|
76
|
+
// these option. When there are no buttons in the modal, this causes an error.
|
|
77
|
+
// Because `focus-trap` cannot be disabled, the ref itself requires a default value
|
|
78
|
+
// to satisfy `focus-trap` until React populates it with the real ref value.
|
|
79
|
+
//
|
|
80
|
+
// See:
|
|
81
|
+
// - https://react.dev/reference/react/useRef#caveats
|
|
82
|
+
// - https://github.com/davidtheclark/react-aria-modal/pull/103
|
|
83
|
+
// - https://github.com/focus-trap/focus-trap?tab=readme-ov-file#createoptions
|
|
84
|
+
const modalContainerRef = useRef("#react-aria-modal-dialog");
|
|
85
|
+
|
|
86
|
+
const hasCloseButton = onlyCloseButton && !noButtons;
|
|
87
|
+
const hasCancelButton = !onlyContinueButton && !onlyCloseButton && !noButtons;
|
|
88
|
+
const hasContinueButton =
|
|
89
|
+
(onlyContinueButton && !noButtons) || (!onlyCloseButton && !noButtons);
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<div ref={modalContainerRef} tabIndex="-1" data-qa={dataQa}>
|
|
93
|
+
{modalOpen && (
|
|
94
|
+
<AriaModal
|
|
95
|
+
focusTrapOptions={{
|
|
96
|
+
// fallback to resolve Jest unit test errors when tabbable doesn't exist in jsdom https://github.com/focus-trap/focus-trap-react/issues/91
|
|
97
|
+
fallbackFocus: modalContainerRef?.current
|
|
98
|
+
}}
|
|
99
|
+
onExit={onExit}
|
|
100
|
+
getApplicationNode={getApplicationNode}
|
|
101
|
+
titleText={modalHeaderText}
|
|
102
|
+
underlayStyle={{
|
|
103
|
+
display: "flex",
|
|
104
|
+
flexDirection: "column",
|
|
105
|
+
justifyContent: "center",
|
|
106
|
+
alignItems: "center",
|
|
107
|
+
background: "rgba(41, 42, 51, 0.45)",
|
|
108
|
+
backdropFilter: blurUnderlay ? "blur(4px)" : "none",
|
|
109
|
+
WebkitBackdropFilter: blurUnderlay ? "blur(4px)" : "none"
|
|
110
|
+
}}
|
|
111
|
+
dialogStyle={{
|
|
112
|
+
borderRadius: BORDER_RADIUS.MD,
|
|
113
|
+
margin: SPACING.XS,
|
|
114
|
+
overflow: "auto",
|
|
115
|
+
width: isMobile ? "" : customWidth || "576px"
|
|
116
|
+
}}
|
|
117
|
+
underlayClickExits={underlayClickExits}
|
|
118
|
+
aria-modal={true}
|
|
119
|
+
initialFocus={initialFocusSelector}
|
|
120
|
+
focusDialog={!initialFocusSelector} // Focus the dialogue box itself if no selector for initial focus was provided
|
|
121
|
+
>
|
|
122
|
+
<Box padding="0" boxShadow="inset 0px -2px 0px 0px rgb(0, 80, 149)">
|
|
123
|
+
<Box
|
|
124
|
+
background={modalHeaderBg || WHITE}
|
|
125
|
+
borderColor={SILVER_GREY}
|
|
126
|
+
borderWidthOverride={`0 0 ${BORDER_THIN} 0`}
|
|
127
|
+
padding={`${SPACING.XS} ${SPACING.MD}`}
|
|
128
|
+
>
|
|
129
|
+
<Cluster
|
|
130
|
+
justify={showCloseIconButton ? "space-between" : "flex-start"}
|
|
131
|
+
align={
|
|
132
|
+
showCloseIconButton && isMobile ? "flex-start" : "center"
|
|
133
|
+
}
|
|
134
|
+
nowrap
|
|
135
|
+
>
|
|
136
|
+
<Title
|
|
137
|
+
as="h2"
|
|
138
|
+
weight={FONT_WEIGHT_SEMIBOLD}
|
|
139
|
+
fontSize={isMobile ? FONT_SIZE.MD : FONT_SIZE.LG}
|
|
140
|
+
>
|
|
141
|
+
{modalHeaderText}
|
|
142
|
+
</Title>
|
|
143
|
+
{showCloseIconButton && (
|
|
144
|
+
<CloseIconButton hideModal={hideModal} />
|
|
145
|
+
)}
|
|
146
|
+
</Cluster>
|
|
147
|
+
</Box>
|
|
148
|
+
<Box background={modalBodyBg || ATHENS_GREY} padding="0">
|
|
149
|
+
<Box
|
|
150
|
+
padding={SPACING.MD}
|
|
151
|
+
borderWidthOverride={!noButtons && `0 0 ${BORDER_THIN} 0`}
|
|
152
|
+
borderColor={!noButtons && SILVER_GREY}
|
|
153
|
+
extraStyles={
|
|
154
|
+
maxHeight ? `max-height: ${maxHeight}; overflow: auto;` : ``
|
|
155
|
+
}
|
|
156
|
+
>
|
|
157
|
+
{defaultWrapper ? (
|
|
158
|
+
<Paragraph variant={isMobile ? "pS" : "p"}>
|
|
159
|
+
{modalBodyText}
|
|
160
|
+
</Paragraph>
|
|
161
|
+
) : (
|
|
162
|
+
<Box padding={maxHeight ? `0 0 ${SPACING.XS} 0` : "0"}>
|
|
163
|
+
{modalBodyText}
|
|
164
|
+
</Box>
|
|
165
|
+
)}
|
|
166
|
+
</Box>
|
|
167
|
+
{noButtons ? (
|
|
168
|
+
<></>
|
|
169
|
+
) : (
|
|
170
|
+
<ButtonLayoutWrapper isMobile={isMobile}>
|
|
171
|
+
{[
|
|
172
|
+
hasCancelButton && (
|
|
173
|
+
<CancelButton
|
|
174
|
+
buttonExtraStyles={buttonExtraStyles}
|
|
175
|
+
cancelAction={cancelAction}
|
|
176
|
+
cancelButtonText={cancelButtonText}
|
|
177
|
+
cancelButtonVariant={cancelButtonVariant}
|
|
178
|
+
hideModal={hideModal}
|
|
179
|
+
isMobile={isMobile}
|
|
180
|
+
key="cancel"
|
|
181
|
+
/>
|
|
182
|
+
),
|
|
183
|
+
hasContinueButton && (
|
|
184
|
+
<ContinueButton
|
|
185
|
+
buttonExtraStyles={buttonExtraStyles}
|
|
186
|
+
continueAction={continueAction}
|
|
187
|
+
continueButtonText={continueButtonText}
|
|
188
|
+
continueURL={continueURL}
|
|
189
|
+
continueButtonVariant={continueButtonVariant}
|
|
190
|
+
isContinueActionDisabled={isContinueActionDisabled}
|
|
191
|
+
isLoading={isLoading}
|
|
192
|
+
isMobile={isMobile}
|
|
193
|
+
key="continue"
|
|
194
|
+
useDangerButton={useDangerButton}
|
|
195
|
+
/>
|
|
196
|
+
),
|
|
197
|
+
hasCloseButton && (
|
|
198
|
+
<CloseButton
|
|
199
|
+
buttonExtraStyles={buttonExtraStyles}
|
|
200
|
+
closeButtonText={closeButtonText}
|
|
201
|
+
hideModal={hideModal}
|
|
202
|
+
isMobile={isMobile}
|
|
203
|
+
key="close"
|
|
204
|
+
/>
|
|
205
|
+
)
|
|
206
|
+
].filter(button => button)}
|
|
207
|
+
</ButtonLayoutWrapper>
|
|
208
|
+
)}
|
|
209
|
+
</Box>
|
|
210
|
+
</Box>
|
|
211
|
+
</AriaModal>
|
|
212
|
+
)}
|
|
213
|
+
{children}
|
|
214
|
+
</div>
|
|
215
|
+
);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export default withWindowSize(Modal);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SPACING } from "../../../../constants/style_constants";
|
|
3
|
+
import { Box, Stack } from "../../../atoms/layouts";
|
|
4
|
+
|
|
5
|
+
export const ButtonLayoutWrapper = ({ children = [], isMobile = false }) => {
|
|
6
|
+
const safeChildren = Array.isArray(children) ? children : [children];
|
|
7
|
+
const flexGrow = isMobile ? "flex-grow: 1;" : "";
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<Box padding={SPACING.MD}>
|
|
11
|
+
<Stack childGap="1rem" direction="row" justify="flex-end">
|
|
12
|
+
{safeChildren.map((child, index) => {
|
|
13
|
+
return (
|
|
14
|
+
<Box padding="0" extraStyles={flexGrow} key={index}>
|
|
15
|
+
{child}
|
|
16
|
+
</Box>
|
|
17
|
+
);
|
|
18
|
+
})}
|
|
19
|
+
</Stack>
|
|
20
|
+
</Box>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default ButtonLayoutWrapper;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
BORDER_RADIUS,
|
|
4
|
+
FONT_SIZE
|
|
5
|
+
} from "../../../../constants/style_constants";
|
|
6
|
+
import { noop } from "../../../../util/general";
|
|
7
|
+
import ButtonWithAction from "../../../atoms/button-with-action/ButtonWithAction";
|
|
8
|
+
|
|
9
|
+
export const CancelButton = ({
|
|
10
|
+
buttonExtraStyles = "",
|
|
11
|
+
cancelAction = noop,
|
|
12
|
+
cancelButtonText = "",
|
|
13
|
+
cancelButtonVariant = "secondary",
|
|
14
|
+
hideModal = noop,
|
|
15
|
+
isMobile = false
|
|
16
|
+
}) => {
|
|
17
|
+
const fontSize = `font-size: ${isMobile ? FONT_SIZE.XS : FONT_SIZE.SM};`;
|
|
18
|
+
const width = isMobile ? "width: 100%;" : "";
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<ButtonWithAction
|
|
22
|
+
action={cancelAction ? cancelAction : hideModal}
|
|
23
|
+
borderRadius={BORDER_RADIUS.MD}
|
|
24
|
+
className="modal-cancel-button"
|
|
25
|
+
dataQa={cancelButtonText}
|
|
26
|
+
extraStyles={`${buttonExtraStyles}; margin: 0; ${width}`}
|
|
27
|
+
name={cancelButtonText}
|
|
28
|
+
role="button"
|
|
29
|
+
text={cancelButtonText}
|
|
30
|
+
textExtraStyles={`${fontSize}`}
|
|
31
|
+
variant={cancelButtonVariant}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default CancelButton;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
BORDER_RADIUS,
|
|
4
|
+
FONT_SIZE
|
|
5
|
+
} from "../../../../constants/style_constants";
|
|
6
|
+
import { noop } from "../../../../util/general";
|
|
7
|
+
import ButtonWithAction from "../../../atoms/button-with-action/ButtonWithAction";
|
|
8
|
+
|
|
9
|
+
export const CloseButton = ({
|
|
10
|
+
buttonExtraStyles = "",
|
|
11
|
+
closeButtonText = "",
|
|
12
|
+
hideModal = noop,
|
|
13
|
+
isMobile = false
|
|
14
|
+
}) => {
|
|
15
|
+
const fontSize = `font-size: ${isMobile ? FONT_SIZE.XS : FONT_SIZE.SM};`;
|
|
16
|
+
const width = isMobile ? "width: 100%;" : "";
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<ButtonWithAction
|
|
20
|
+
action={hideModal}
|
|
21
|
+
borderRadius={BORDER_RADIUS.MD}
|
|
22
|
+
className="modal-close-button"
|
|
23
|
+
dataQa={closeButtonText}
|
|
24
|
+
extraStyles={`${buttonExtraStyles}; margin: 0; ${width}`}
|
|
25
|
+
name={closeButtonText}
|
|
26
|
+
role="button"
|
|
27
|
+
text={closeButtonText}
|
|
28
|
+
textExtraStyles={`${fontSize}`}
|
|
29
|
+
variant="primary"
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default CloseButton;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { noop } from "../../../../util/general";
|
|
3
|
+
import ButtonWithAction from "../../../atoms/button-with-action/ButtonWithAction";
|
|
4
|
+
import { CloseIcon } from "../../../atoms/icons/CloseIcon";
|
|
5
|
+
|
|
6
|
+
export const CloseIconButton = ({
|
|
7
|
+
buttonExtraStyles = "",
|
|
8
|
+
hideModal = noop,
|
|
9
|
+
iconWidth = "24",
|
|
10
|
+
iconHeight = "24",
|
|
11
|
+
ariaLabel = "Close Modal"
|
|
12
|
+
}) => {
|
|
13
|
+
return (
|
|
14
|
+
<ButtonWithAction
|
|
15
|
+
action={hideModal}
|
|
16
|
+
aria-label={ariaLabel}
|
|
17
|
+
contentOverride
|
|
18
|
+
extraStyles={`
|
|
19
|
+
min-height: auto;
|
|
20
|
+
min-width: auto;
|
|
21
|
+
height: 1.5rem;
|
|
22
|
+
margin: 0 0 0 0.5rem;
|
|
23
|
+
&:focus {
|
|
24
|
+
outline-offset: -3px;
|
|
25
|
+
}
|
|
26
|
+
`}
|
|
27
|
+
variant="smallGhost"
|
|
28
|
+
>
|
|
29
|
+
<CloseIcon
|
|
30
|
+
role="img"
|
|
31
|
+
aria-hidden="true"
|
|
32
|
+
iconWidth={iconWidth}
|
|
33
|
+
iconHeight={iconHeight}
|
|
34
|
+
/>
|
|
35
|
+
</ButtonWithAction>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default CloseIconButton;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
BORDER_RADIUS,
|
|
4
|
+
FONT_SIZE
|
|
5
|
+
} from "../../../../constants/style_constants";
|
|
6
|
+
import { noop } from "../../../../util/general";
|
|
7
|
+
import ButtonWithAction from "../../../atoms/button-with-action/ButtonWithAction";
|
|
8
|
+
import ButtonWithLink from "../../../atoms/button-with-link/ButtonWithLink";
|
|
9
|
+
|
|
10
|
+
export const ContinueButton = ({
|
|
11
|
+
buttonExtraStyles = "",
|
|
12
|
+
continueAction = noop,
|
|
13
|
+
continueButtonText = "",
|
|
14
|
+
continueURL = "",
|
|
15
|
+
continueButtonVariant = "primary",
|
|
16
|
+
isContinueActionDisabled = false,
|
|
17
|
+
isLoading = false,
|
|
18
|
+
isMobile = false,
|
|
19
|
+
useDangerButton = false
|
|
20
|
+
}) => {
|
|
21
|
+
const ContinueButtonAtom = continueURL ? ButtonWithLink : ButtonWithAction;
|
|
22
|
+
const fontSize = `font-size: ${isMobile ? FONT_SIZE.XS : FONT_SIZE.SM};`;
|
|
23
|
+
const width = isMobile ? "width: 100%;" : "";
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<ContinueButtonAtom
|
|
27
|
+
action={continueAction}
|
|
28
|
+
borderRadius={BORDER_RADIUS.MD}
|
|
29
|
+
className="modal-continue-button"
|
|
30
|
+
dataQa={continueButtonText}
|
|
31
|
+
disabled={isContinueActionDisabled}
|
|
32
|
+
extraStyles={`${buttonExtraStyles}; margin: 0; ${width}`}
|
|
33
|
+
isLoading={isLoading}
|
|
34
|
+
linkExtraStyles={`display: inline-block; ${width}`}
|
|
35
|
+
name={continueButtonText}
|
|
36
|
+
role="button"
|
|
37
|
+
text={continueButtonText}
|
|
38
|
+
textExtraStyles={`${fontSize}`}
|
|
39
|
+
url={continueURL}
|
|
40
|
+
variant={useDangerButton ? "danger" : continueButtonVariant}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default ContinueButton;
|