@openedx/paragon 22.16.2 → 22.18.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/Modal/ModalDialog.d.ts +2 -2
- package/dist/Modal/ModalDialog.js +2 -2
- package/dist/Modal/ModalDialog.js.map +1 -1
- package/dist/ProductTour/Checkpoint.js +23 -16
- package/dist/ProductTour/Checkpoint.js.map +1 -1
- package/dist/ProductTour/Checkpoint.scss +8 -36
- package/dist/ProductTour/CheckpointActionRow.js +17 -21
- package/dist/ProductTour/CheckpointActionRow.js.map +1 -1
- package/dist/ProductTour/CheckpointHeader.js +57 -0
- package/dist/ProductTour/CheckpointHeader.js.map +1 -0
- package/dist/ProductTour/index.js +120 -20
- package/dist/ProductTour/index.js.map +1 -1
- package/dist/ProductTour/messages.js +10 -0
- package/dist/paragon.css +1 -1
- package/dist/withDeprecatedProps.js +11 -3
- package/dist/withDeprecatedProps.js.map +1 -1
- package/package.json +1 -1
- package/src/Modal/ModalDialog.tsx +3 -3
- package/src/Modal/README.md +1 -1
- package/src/Modal/alert-modal.mdx +4 -0
- package/src/Modal/fullscreen-modal.mdx +1 -0
- package/src/Modal/marketing-modal.mdx +1 -0
- package/src/Modal/modal-dialog.mdx +2 -0
- package/src/Modal/standard-modal.mdx +1 -0
- package/src/Modal/tests/AlertModal.test.jsx +4 -0
- package/src/Modal/tests/ModalDialog.test.tsx +3 -0
- package/src/ProductTour/Checkpoint.jsx +22 -16
- package/src/ProductTour/Checkpoint.scss +8 -36
- package/src/ProductTour/Checkpoint.test.jsx +20 -53
- package/src/ProductTour/CheckpointActionRow.jsx +32 -32
- package/src/ProductTour/CheckpointHeader.jsx +60 -0
- package/src/ProductTour/ProductTour.test.jsx +69 -60
- package/src/ProductTour/README.md +11 -3
- package/src/ProductTour/index.jsx +125 -17
- package/src/ProductTour/messages.js +10 -0
- package/src/withDeprecatedProps.tsx +10 -3
- package/dist/ProductTour/CheckpointBreadcrumbs.js +0 -37
- package/dist/ProductTour/CheckpointBreadcrumbs.js.map +0 -1
- package/dist/TransitionReplace/DemoTransitionReplace.js +0 -32
- package/dist/TransitionReplace/DemoTransitionReplace.js.map +0 -1
- package/src/ProductTour/CheckpointBreadcrumbs.jsx +0 -45
- package/src/TransitionReplace/DemoTransitionReplace.jsx +0 -57
|
@@ -51,9 +51,17 @@ function withDeprecatedProps(WrappedComponent, componentName, deprecatedProps) {
|
|
|
51
51
|
}
|
|
52
52
|
break;
|
|
53
53
|
case DeprTypes.MOVED_AND_FORMAT:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
{
|
|
55
|
+
const propValue = this.props[propName];
|
|
56
|
+
let warningMessage = `${componentName}: The prop '${propName}' has been moved to '${newName}'`;
|
|
57
|
+
if (expect && !expect(propValue)) {
|
|
58
|
+
warningMessage += ' and expects a new format';
|
|
59
|
+
}
|
|
60
|
+
warningMessage += message ? `. ${message}` : '';
|
|
61
|
+
this.warn(warningMessage);
|
|
62
|
+
acc[newName] = transform ? transform(propValue, this.props) : propValue;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
57
65
|
default:
|
|
58
66
|
acc[propName] = this.props[propName];
|
|
59
67
|
break;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"withDeprecatedProps.js","names":["React","DeprTypes","withDeprecatedProps","WrappedComponent","componentName","deprecatedProps","WithDeprecatedProps","Component","displayName","constructor","props","transformProps","bind","warn","message","process","env","NODE_ENV","console","acc","propName","undefined","deprType","newName","expect","transform","MOVED","REMOVED","FORMAT","MOVED_AND_FORMAT","render","children","transformedProps","Object","keys","reduce","createElement"],"sources":["../src/withDeprecatedProps.tsx"],"sourcesContent":["/* eslint no-console: 0 */\nimport React from 'react';\n\nexport enum DeprTypes {\n MOVED = 'MOVED',\n REMOVED = 'REMOVED',\n FORMAT = 'FORMAT',\n MOVED_AND_FORMAT = 'MOVED_AND_FORMAT',\n}\n\nexport interface DeprecatedProps extends Record<string, any> {\n deprType: DeprTypes,\n newName?: string,\n expect?: (propValue: any) => boolean,\n transform?: (propValue: any, allProps: Record<string, any>) => any,\n message?: string,\n}\n\nfunction withDeprecatedProps<T extends Record<string, any>>(\n WrappedComponent: React.ComponentType<any>,\n componentName: string,\n deprecatedProps: Record<string, DeprecatedProps>,\n) : any {\n class WithDeprecatedProps extends React.Component<T> {\n // eslint-disable-next-line react/static-property-placement\n public static displayName = `withDeprecatedProps(${componentName})`;\n\n constructor(props: T) {\n super(props);\n this.transformProps = this.transformProps.bind(this);\n }\n\n warn(message: string) {\n if (process.env.NODE_ENV === 'development') {\n if (console) { console.warn(`[Deprecated] ${message}`); }\n }\n }\n\n transformProps(acc: Record<string, any>, propName: string) : Record<string, any> {\n if (deprecatedProps[propName] === undefined) {\n acc[propName] = this.props[propName];\n return acc;\n }\n\n const {\n deprType,\n newName,\n expect,\n transform,\n message,\n } = deprecatedProps[propName];\n\n switch (deprType) {\n case DeprTypes.MOVED:\n this.warn(`${componentName}: The prop '${propName}' has been moved to '${newName}'.`);\n acc[newName!] = this.props[propName];\n break;\n case DeprTypes.REMOVED:\n this.warn(`${componentName}: The prop '${propName}' has been removed. '${message}'`);\n break;\n case DeprTypes.FORMAT:\n if (!expect!(this.props[propName])) {\n this.warn(`${componentName}: The prop '${propName}' expects a new format. ${message}`);\n acc[propName] = transform!(this.props[propName], this.props);\n } else {\n acc[propName] = this.props[propName];\n }\n break;\n case DeprTypes.MOVED_AND_FORMAT
|
|
1
|
+
{"version":3,"file":"withDeprecatedProps.js","names":["React","DeprTypes","withDeprecatedProps","WrappedComponent","componentName","deprecatedProps","WithDeprecatedProps","Component","displayName","constructor","props","transformProps","bind","warn","message","process","env","NODE_ENV","console","acc","propName","undefined","deprType","newName","expect","transform","MOVED","REMOVED","FORMAT","MOVED_AND_FORMAT","propValue","warningMessage","render","children","transformedProps","Object","keys","reduce","createElement"],"sources":["../src/withDeprecatedProps.tsx"],"sourcesContent":["/* eslint no-console: 0 */\nimport React from 'react';\n\nexport enum DeprTypes {\n MOVED = 'MOVED',\n REMOVED = 'REMOVED',\n FORMAT = 'FORMAT',\n MOVED_AND_FORMAT = 'MOVED_AND_FORMAT',\n}\n\nexport interface DeprecatedProps extends Record<string, any> {\n deprType: DeprTypes,\n newName?: string,\n expect?: (propValue: any) => boolean,\n transform?: (propValue: any, allProps: Record<string, any>) => any,\n message?: string,\n}\n\nfunction withDeprecatedProps<T extends Record<string, any>>(\n WrappedComponent: React.ComponentType<any>,\n componentName: string,\n deprecatedProps: Record<string, DeprecatedProps>,\n) : any {\n class WithDeprecatedProps extends React.Component<T> {\n // eslint-disable-next-line react/static-property-placement\n public static displayName = `withDeprecatedProps(${componentName})`;\n\n constructor(props: T) {\n super(props);\n this.transformProps = this.transformProps.bind(this);\n }\n\n warn(message: string) {\n if (process.env.NODE_ENV === 'development') {\n if (console) { console.warn(`[Deprecated] ${message}`); }\n }\n }\n\n transformProps(acc: Record<string, any>, propName: string) : Record<string, any> {\n if (deprecatedProps[propName] === undefined) {\n acc[propName] = this.props[propName];\n return acc;\n }\n\n const {\n deprType,\n newName,\n expect,\n transform,\n message,\n } = deprecatedProps[propName];\n\n switch (deprType) {\n case DeprTypes.MOVED:\n this.warn(`${componentName}: The prop '${propName}' has been moved to '${newName}'.`);\n acc[newName!] = this.props[propName];\n break;\n case DeprTypes.REMOVED:\n this.warn(`${componentName}: The prop '${propName}' has been removed. '${message}'`);\n break;\n case DeprTypes.FORMAT:\n if (!expect!(this.props[propName])) {\n this.warn(`${componentName}: The prop '${propName}' expects a new format. ${message}`);\n acc[propName] = transform!(this.props[propName], this.props);\n } else {\n acc[propName] = this.props[propName];\n }\n break;\n case DeprTypes.MOVED_AND_FORMAT: {\n const propValue = this.props[propName];\n let warningMessage = `${componentName}: The prop '${propName}' has been moved to '${newName}'`;\n if (expect && !expect(propValue)) {\n warningMessage += ' and expects a new format';\n }\n warningMessage += message ? `. ${message}` : '';\n this.warn(warningMessage);\n acc[newName!] = transform ? transform(propValue, this.props) : propValue;\n break;\n }\n default:\n acc[propName] = this.props[propName];\n break;\n }\n\n return acc;\n }\n\n render() {\n const { children, ...transformedProps } = Object\n .keys(this.props)\n .reduce(this.transformProps, {});\n\n return (\n <WrappedComponent {...transformedProps as T}>\n {this.props.children || children}\n </WrappedComponent>\n );\n }\n }\n\n return WithDeprecatedProps;\n}\n\nexport default withDeprecatedProps;\n"],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -53,7 +53,7 @@ interface Props {
|
|
|
53
53
|
/** Specifies the z-index of the modal */
|
|
54
54
|
zIndex?: number;
|
|
55
55
|
/** Specifies whether overflow is visible in the modal */
|
|
56
|
-
isOverflowVisible
|
|
56
|
+
isOverflowVisible: boolean;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
function ModalDialog({
|
|
@@ -70,7 +70,7 @@ function ModalDialog({
|
|
|
70
70
|
isFullscreenOnMobile = false,
|
|
71
71
|
isBlocking = false,
|
|
72
72
|
zIndex,
|
|
73
|
-
isOverflowVisible
|
|
73
|
+
isOverflowVisible,
|
|
74
74
|
}: Props) {
|
|
75
75
|
const isMobile = useMediaQuery({ query: '(max-width: 767.98px)' });
|
|
76
76
|
const showFullScreen = (isFullscreenOnMobile && isMobile);
|
|
@@ -163,7 +163,7 @@ ModalDialog.propTypes = {
|
|
|
163
163
|
*/
|
|
164
164
|
zIndex: PropTypes.number,
|
|
165
165
|
/** Specifies whether overflow is visible in the modal */
|
|
166
|
-
isOverflowVisible: PropTypes.bool,
|
|
166
|
+
isOverflowVisible: PropTypes.bool.isRequired,
|
|
167
167
|
};
|
|
168
168
|
|
|
169
169
|
ModalDialog.Header = ModalDialogHeader;
|
package/src/Modal/README.md
CHANGED
|
@@ -36,6 +36,7 @@ This is the alert style `ModalDialog` composition. `AlertModal` passes all of it
|
|
|
36
36
|
<Button variant="danger">Submit</Button>
|
|
37
37
|
</ActionRow>
|
|
38
38
|
)}
|
|
39
|
+
isOverflowVisible={false}
|
|
39
40
|
>
|
|
40
41
|
<p>
|
|
41
42
|
I'm baby palo santo ugh celiac fashion axe. La croix lo-fi venmo whatever.
|
|
@@ -67,6 +68,7 @@ const [isOpen, open, close] = useToggle(false);
|
|
|
67
68
|
<Button variant="primary">Delete</Button>
|
|
68
69
|
</ActionRow>
|
|
69
70
|
)}
|
|
71
|
+
isOverflowVisible={false}
|
|
70
72
|
>
|
|
71
73
|
<p>
|
|
72
74
|
Are your sure you want to delete this file? You can't undo this action.
|
|
@@ -95,6 +97,7 @@ const [isOpen, open, close] = useToggle(false);
|
|
|
95
97
|
<Button variant="danger">Acknowledge errror</Button>
|
|
96
98
|
</ActionRow>
|
|
97
99
|
)}
|
|
100
|
+
isOverflowVisible={false}
|
|
98
101
|
>
|
|
99
102
|
<p>
|
|
100
103
|
An unknown error has occured.
|
|
@@ -123,6 +126,7 @@ const [isOpen, open, close] = useToggle(false);
|
|
|
123
126
|
<Button variant="success">Confirm</Button>
|
|
124
127
|
</ActionRow>
|
|
125
128
|
)}
|
|
129
|
+
isOverflowVisible={false}
|
|
126
130
|
>
|
|
127
131
|
<p>
|
|
128
132
|
All good!
|
|
@@ -50,6 +50,7 @@ The ``MarketingModal`` is a preconfigured `ModalDialog` that accepts an image an
|
|
|
50
50
|
<Button>Submit</Button>
|
|
51
51
|
</ActionRow>
|
|
52
52
|
)}
|
|
53
|
+
isOverflowVisible={false}
|
|
53
54
|
>
|
|
54
55
|
<p>
|
|
55
56
|
I'm baby palo santo ugh celiac fashion axe. La croix lo-fi venmo whatever. Beard man braid migas single-origin coffee forage ramps. Tumeric messenger bag bicycle rights wayfarers, try-hard cronut blue bottle health goth. Sriracha tumblr cardigan, cloud bread succulents tumeric copper mug marfa semiotics woke next level organic roof party +1 try-hard.
|
|
@@ -44,6 +44,7 @@ label for the dialog element.
|
|
|
44
44
|
variant={modalVariant}
|
|
45
45
|
hasCloseButton
|
|
46
46
|
isFullscreenOnMobile
|
|
47
|
+
isOverflowVisible={false}
|
|
47
48
|
>
|
|
48
49
|
<ModalDialog.Header>
|
|
49
50
|
<ModalDialog.Title>
|
|
@@ -114,6 +115,7 @@ label for the dialog element.
|
|
|
114
115
|
size={modalSize}
|
|
115
116
|
variant={modalVariant}
|
|
116
117
|
hasCloseButton
|
|
118
|
+
isOverflowVisible={false}
|
|
117
119
|
>
|
|
118
120
|
<ModalDialog.Hero>
|
|
119
121
|
<ModalDialog.Hero.Background
|
|
@@ -38,6 +38,7 @@ The standard `ModalDialog` composition. `StandardModal` passes all of its props
|
|
|
38
38
|
<Button>Submit</Button>
|
|
39
39
|
</ActionRow>
|
|
40
40
|
)}
|
|
41
|
+
isOverflowVisible={false}
|
|
41
42
|
>
|
|
42
43
|
<p>
|
|
43
44
|
I'm baby palo santo ugh celiac fashion axe. La croix lo-fi venmo whatever. Beard man braid migas single-origin coffee forage ramps. Tumeric messenger bag bicycle rights wayfarers, try-hard cronut blue bottle health goth. Sriracha tumblr cardigan, cloud bread succulents tumeric copper mug marfa semiotics woke next level organic roof party +1 try-hard.
|
|
@@ -42,6 +42,7 @@ describe('<AlertModal />', () => {
|
|
|
42
42
|
isOpen={isOpen}
|
|
43
43
|
onClose={closeFn}
|
|
44
44
|
footerNode={<p>footer</p>}
|
|
45
|
+
isOverflowVisible={false}
|
|
45
46
|
>
|
|
46
47
|
<Body />
|
|
47
48
|
</AlertModal>,
|
|
@@ -60,6 +61,7 @@ describe('<AlertModal />', () => {
|
|
|
60
61
|
onClose={closeFn}
|
|
61
62
|
icon={Info}
|
|
62
63
|
footerNode={<p>footer</p>}
|
|
64
|
+
isOverflowVisible={false}
|
|
63
65
|
>
|
|
64
66
|
<Body />
|
|
65
67
|
</AlertModal>,
|
|
@@ -77,6 +79,7 @@ describe('<AlertModal />', () => {
|
|
|
77
79
|
onClose={closeFn}
|
|
78
80
|
icon={Info}
|
|
79
81
|
footerNode={<p>footer</p>}
|
|
82
|
+
isOverflowVisible={false}
|
|
80
83
|
>
|
|
81
84
|
<Body />
|
|
82
85
|
</AlertModal>,
|
|
@@ -94,6 +97,7 @@ describe('<AlertModal />', () => {
|
|
|
94
97
|
onClose={closeFn}
|
|
95
98
|
icon={Info}
|
|
96
99
|
footerNode={<p>footer</p>}
|
|
100
|
+
isOverflowVisible={false}
|
|
97
101
|
>
|
|
98
102
|
<Body />
|
|
99
103
|
</AlertModal>,
|
|
@@ -14,6 +14,7 @@ describe('ModalDialog', () => {
|
|
|
14
14
|
size="md"
|
|
15
15
|
variant="default"
|
|
16
16
|
hasCloseButton
|
|
17
|
+
isOverflowVisible={false}
|
|
17
18
|
>
|
|
18
19
|
<ModalDialog.Header>
|
|
19
20
|
<ModalDialog.Title>The title</ModalDialog.Title>
|
|
@@ -42,6 +43,7 @@ describe('ModalDialog', () => {
|
|
|
42
43
|
<ModalDialog
|
|
43
44
|
title="My dialog"
|
|
44
45
|
onClose={onClose}
|
|
46
|
+
isOverflowVisible={false}
|
|
45
47
|
>
|
|
46
48
|
<ModalDialog.Header><ModalDialog.Title>The title</ModalDialog.Title></ModalDialog.Header>
|
|
47
49
|
<ModalDialog.Body><p>The hidden content</p></ModalDialog.Body>
|
|
@@ -64,6 +66,7 @@ describe('ModalDialog with Hero', () => {
|
|
|
64
66
|
size="md"
|
|
65
67
|
variant="default"
|
|
66
68
|
hasCloseButton
|
|
69
|
+
isOverflowVisible={false}
|
|
67
70
|
>
|
|
68
71
|
<ModalDialog.Hero>
|
|
69
72
|
<ModalDialog.Hero.Background backgroundSrc="imageurl" />
|
|
@@ -5,16 +5,17 @@ import { createPopper } from '@popperjs/core';
|
|
|
5
5
|
import { FormattedMessage } from 'react-intl';
|
|
6
6
|
|
|
7
7
|
import breakpoints from '../utils/breakpoints';
|
|
8
|
-
|
|
9
8
|
import CheckpointActionRow from './CheckpointActionRow';
|
|
10
9
|
import CheckpointBody from './CheckpointBody';
|
|
11
|
-
import
|
|
12
|
-
import CheckpointTitle from './CheckpointTitle';
|
|
10
|
+
import CheckpointHeader from './CheckpointHeader';
|
|
13
11
|
import messages from './messages';
|
|
14
12
|
|
|
15
13
|
const Checkpoint = React.forwardRef(({
|
|
16
14
|
body,
|
|
15
|
+
dismissAltText,
|
|
17
16
|
index,
|
|
17
|
+
onBack,
|
|
18
|
+
onDismiss,
|
|
18
19
|
placement,
|
|
19
20
|
target,
|
|
20
21
|
title,
|
|
@@ -87,7 +88,6 @@ const Checkpoint = React.forwardRef(({
|
|
|
87
88
|
}, [target, checkpointVisible, placement]);
|
|
88
89
|
|
|
89
90
|
const isLastCheckpoint = index + 1 === totalCheckpoints;
|
|
90
|
-
const isOnlyCheckpoint = totalCheckpoints === 1;
|
|
91
91
|
|
|
92
92
|
return (
|
|
93
93
|
<div
|
|
@@ -104,14 +104,17 @@ const Checkpoint = React.forwardRef(({
|
|
|
104
104
|
values={{ step: index + 1 }}
|
|
105
105
|
/>
|
|
106
106
|
</span>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
<CheckpointHeader
|
|
108
|
+
dismissAltText={dismissAltText}
|
|
109
|
+
index={index}
|
|
110
|
+
onDismiss={onDismiss}
|
|
111
|
+
title={title}
|
|
112
|
+
totalCheckpoints={totalCheckpoints}
|
|
113
|
+
/>
|
|
113
114
|
<CheckpointBody>{body}</CheckpointBody>
|
|
114
115
|
<CheckpointActionRow
|
|
116
|
+
onBack={onBack}
|
|
117
|
+
onDismiss={onDismiss}
|
|
115
118
|
isLastCheckpoint={isLastCheckpoint}
|
|
116
119
|
index={index}
|
|
117
120
|
{...props}
|
|
@@ -129,21 +132,23 @@ const Checkpoint = React.forwardRef(({
|
|
|
129
132
|
|
|
130
133
|
Checkpoint.defaultProps = {
|
|
131
134
|
advanceButtonText: null,
|
|
135
|
+
backButtonText: null,
|
|
132
136
|
body: null,
|
|
133
|
-
|
|
137
|
+
dismissAltText: null,
|
|
134
138
|
endButtonText: null,
|
|
135
139
|
placement: 'top',
|
|
136
140
|
title: null,
|
|
137
|
-
showDismissButton: undefined,
|
|
138
141
|
};
|
|
139
142
|
|
|
140
143
|
Checkpoint.propTypes = {
|
|
141
144
|
/** The text displayed on the button used to advance the tour for the given Checkpoint. */
|
|
142
145
|
advanceButtonText: PropTypes.node,
|
|
146
|
+
/** The text displayed on the button used go back in the tour for the given Checkpoint. */
|
|
147
|
+
backButtonText: PropTypes.string,
|
|
143
148
|
/** The text displayed in the body of the Checkpoint */
|
|
144
149
|
body: PropTypes.node,
|
|
145
|
-
/** The text
|
|
146
|
-
|
|
150
|
+
/** The text used in the alt for the icon used to dismiss the tour for the given Checkpoint */
|
|
151
|
+
dismissAltText: PropTypes.string,
|
|
147
152
|
/** The text displayed on the button used to end the tour for the given Checkpoint. */
|
|
148
153
|
endButtonText: PropTypes.node,
|
|
149
154
|
/** The current index of the given Checkpoint */
|
|
@@ -151,6 +156,9 @@ Checkpoint.propTypes = {
|
|
|
151
156
|
/** A function that runs when triggering the `onClick` event of the advance
|
|
152
157
|
* button for the given Checkpoint. */
|
|
153
158
|
onAdvance: PropTypes.func.isRequired,
|
|
159
|
+
/** A function that runs when triggering the `onBack` event of the back
|
|
160
|
+
* button for the given Checkpoint. */
|
|
161
|
+
onBack: PropTypes.func.isRequired,
|
|
154
162
|
/** A function that runs when triggering the `onClick` event of the dismiss
|
|
155
163
|
* button for the given Checkpoint. */
|
|
156
164
|
onDismiss: PropTypes.func.isRequired,
|
|
@@ -168,8 +176,6 @@ Checkpoint.propTypes = {
|
|
|
168
176
|
title: PropTypes.node,
|
|
169
177
|
/** The total number of Checkpoints in a tour */
|
|
170
178
|
totalCheckpoints: PropTypes.number.isRequired,
|
|
171
|
-
/** Enforces visibility of the dismiss button under all circumstances */
|
|
172
|
-
showDismissButton: PropTypes.bool,
|
|
173
179
|
};
|
|
174
180
|
|
|
175
181
|
export default Checkpoint;
|
|
@@ -10,7 +10,7 @@ $checkpoint-arrow-transparent: solid $checkpoint-arrow-width transparent;
|
|
|
10
10
|
background: $checkpoint-background-color;
|
|
11
11
|
border-top: $checkpoint-border-width solid $checkpoint-border-color;
|
|
12
12
|
border-radius: $border-radius;
|
|
13
|
-
padding: map.get($spacers,
|
|
13
|
+
padding: map.get($spacers, 3\.5);
|
|
14
14
|
box-shadow: 0 .25rem .5rem rgba(0, 0, 0, .3);
|
|
15
15
|
z-index: $checkpoint-z-index;
|
|
16
16
|
max-width: $checkpoint-max-width;
|
|
@@ -47,39 +47,6 @@ $checkpoint-arrow-transparent: solid $checkpoint-arrow-width transparent;
|
|
|
47
47
|
margin-inline-end: map.get($spacers, 2);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
.pgn__checkpoint-breadcrumb {
|
|
51
|
-
height: 6px;
|
|
52
|
-
width: 6px;
|
|
53
|
-
border-radius: 50%;
|
|
54
|
-
|
|
55
|
-
&.pgn__checkpoint-breadcrumb_active {
|
|
56
|
-
background: $checkpoint-breadcrumb-color;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
&.pgn__checkpoint-breadcrumb_inactive {
|
|
60
|
-
border: 1px solid $checkpoint-breadcrumb-color;
|
|
61
|
-
background: transparent;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
&:not(:first-child) {
|
|
65
|
-
margin-left: map.get($spacers, 1\.5);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
[dir="rtl"] & {
|
|
69
|
-
margin-left: map.get($spacers, 1\.5);
|
|
70
|
-
margin-right: 0;
|
|
71
|
-
|
|
72
|
-
&:last-child {
|
|
73
|
-
margin-left: 0;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.pgn__checkpoint-breadcrumb-container {
|
|
79
|
-
display: flex;
|
|
80
|
-
align-items: center;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
50
|
.pgn__checkpoint-body {
|
|
84
51
|
color: $checkpoint-body-color;
|
|
85
52
|
margin-bottom: map.get($spacers, 3\.5);
|
|
@@ -89,13 +56,18 @@ $checkpoint-arrow-transparent: solid $checkpoint-arrow-width transparent;
|
|
|
89
56
|
.pgn__checkpoint-header {
|
|
90
57
|
display: flex;
|
|
91
58
|
justify-content: space-between;
|
|
92
|
-
margin-bottom: map.get($spacers, 2
|
|
59
|
+
margin-bottom: map.get($spacers, 2);
|
|
60
|
+
align-items: center;
|
|
93
61
|
}
|
|
94
62
|
|
|
95
63
|
#pgn__checkpoint-title {
|
|
96
64
|
font-size: $h3-font-size;
|
|
97
65
|
margin-inline-end: map.get($spacers, 2\.5);
|
|
98
|
-
margin-bottom:
|
|
66
|
+
margin-bottom: map.get($spacers, 2);;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.pgn__checkpoint-page-index {
|
|
70
|
+
font-size: $small-font-size;
|
|
99
71
|
}
|
|
100
72
|
}
|
|
101
73
|
|
|
@@ -11,6 +11,7 @@ const popperMock = jest.spyOn(popper, 'createPopper');
|
|
|
11
11
|
|
|
12
12
|
describe('Checkpoint', () => {
|
|
13
13
|
const handleAdvance = jest.fn();
|
|
14
|
+
const handleBack = jest.fn();
|
|
14
15
|
const handleDismiss = jest.fn();
|
|
15
16
|
const handleEnd = jest.fn();
|
|
16
17
|
|
|
@@ -29,11 +30,12 @@ describe('Checkpoint', () => {
|
|
|
29
30
|
<div id="target-element">...</div>
|
|
30
31
|
<Checkpoint
|
|
31
32
|
advanceButtonText="Next"
|
|
33
|
+
backButtonText="Back"
|
|
32
34
|
body="Lorem ipsum checkpoint body"
|
|
33
|
-
dismissButtonText="Dismiss"
|
|
34
35
|
endButtonText="End"
|
|
35
36
|
index={1}
|
|
36
37
|
onAdvance={handleAdvance}
|
|
38
|
+
onBack={handleBack}
|
|
37
39
|
onDismiss={handleDismiss}
|
|
38
40
|
onEnd={handleEnd}
|
|
39
41
|
target="#target-element"
|
|
@@ -44,31 +46,22 @@ describe('Checkpoint', () => {
|
|
|
44
46
|
);
|
|
45
47
|
});
|
|
46
48
|
|
|
47
|
-
it('renders
|
|
48
|
-
expect(screen.
|
|
49
|
-
const breadcrumbs = screen.getAllByTestId('pgn__checkpoint-breadcrumb_', { exact: false });
|
|
50
|
-
expect(breadcrumbs.length).toEqual(5);
|
|
51
|
-
expect(breadcrumbs[0].classList).toContain('pgn__checkpoint-breadcrumb_inactive');
|
|
52
|
-
expect(breadcrumbs[1].classList).toContain('pgn__checkpoint-breadcrumb_active');
|
|
53
|
-
expect(breadcrumbs[2].classList).toContain('pgn__checkpoint-breadcrumb_inactive');
|
|
54
|
-
expect(breadcrumbs[3].classList).toContain('pgn__checkpoint-breadcrumb_inactive');
|
|
55
|
-
expect(breadcrumbs[4].classList).toContain('pgn__checkpoint-breadcrumb_inactive');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('only renders advance and dismiss buttons (i.e. does not render end button)', () => {
|
|
59
|
-
expect(screen.getByRole('button', { name: 'Dismiss' })).toBeInTheDocument();
|
|
49
|
+
it('only renders advance and back buttons (i.e. does not render end button)', () => {
|
|
50
|
+
expect(screen.getByRole('button', { name: 'Back' })).toBeInTheDocument();
|
|
60
51
|
expect(screen.getByRole('button', { name: 'Next' })).toBeInTheDocument();
|
|
61
52
|
});
|
|
62
53
|
|
|
63
|
-
it('
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
it('back button onClick calls handleBack', async () => {
|
|
55
|
+
const user = userEvent.setup();
|
|
56
|
+
const backButton = screen.getByRole('button', { name: 'Back' });
|
|
57
|
+
await user.click(backButton);
|
|
58
|
+
expect(handleBack).toHaveBeenCalledTimes(1);
|
|
67
59
|
});
|
|
68
60
|
|
|
69
61
|
it('advance button onClick calls handleAdvance', async () => {
|
|
62
|
+
const user = userEvent.setup();
|
|
70
63
|
const advanceButton = screen.getByRole('button', { name: 'Next' });
|
|
71
|
-
await
|
|
64
|
+
await user.click(advanceButton);
|
|
72
65
|
expect(handleAdvance).toHaveBeenCalledTimes(1);
|
|
73
66
|
});
|
|
74
67
|
});
|
|
@@ -80,8 +73,9 @@ describe('Checkpoint', () => {
|
|
|
80
73
|
<div id="#last-element" />
|
|
81
74
|
<Checkpoint
|
|
82
75
|
advanceButtonText="Next"
|
|
76
|
+
backButtonText="Back"
|
|
83
77
|
body="Lorem ipsum checkpoint body"
|
|
84
|
-
|
|
78
|
+
dismissAltText="Escape"
|
|
85
79
|
endButtonText="End"
|
|
86
80
|
index={4}
|
|
87
81
|
onAdvance={handleAdvance}
|
|
@@ -95,10 +89,15 @@ describe('Checkpoint', () => {
|
|
|
95
89
|
);
|
|
96
90
|
});
|
|
97
91
|
|
|
98
|
-
it('only renders end button (i.e. neither advance nor
|
|
92
|
+
it('only renders end button (i.e. neither advance nor back buttons)', () => {
|
|
99
93
|
expect(screen.getByText('End', { selector: 'button' })).toBeInTheDocument();
|
|
100
94
|
});
|
|
101
95
|
|
|
96
|
+
it('uses customized alt text on the close icon', () => {
|
|
97
|
+
const closeButton = screen.getByTestId('dismiss-tour');
|
|
98
|
+
expect(closeButton).toHaveAttribute('aria-label', 'Escape');
|
|
99
|
+
});
|
|
100
|
+
|
|
102
101
|
it('end button onClick calls handleEnd', async () => {
|
|
103
102
|
const user = userEvent.setup({ pointerEventsCheck: PointerEventsCheckLevel.Never });
|
|
104
103
|
const endButton = screen.getByText('End', { selector: 'button' });
|
|
@@ -115,7 +114,6 @@ describe('Checkpoint', () => {
|
|
|
115
114
|
<Checkpoint
|
|
116
115
|
advanceButtonText="Next"
|
|
117
116
|
body="Lorem ipsum checkpoint body"
|
|
118
|
-
dismissButtonText="Dismiss"
|
|
119
117
|
endButtonText="End"
|
|
120
118
|
index={0}
|
|
121
119
|
onAdvance={handleAdvance}
|
|
@@ -132,36 +130,5 @@ describe('Checkpoint', () => {
|
|
|
132
130
|
it('only renders end button (i.e. neither advance nor dismiss buttons)', () => {
|
|
133
131
|
expect(screen.getByText('End', { selector: 'button' })).toBeInTheDocument();
|
|
134
132
|
});
|
|
135
|
-
|
|
136
|
-
it('does not render breadcrumbs', () => {
|
|
137
|
-
const breadcrumbs = screen.queryAllByTestId('pgn__checkpoint-breadcrumb_', { exact: false });
|
|
138
|
-
expect(breadcrumbs.length).toEqual(0);
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
describe('only one Checkpoint in Tour and showDismissButton set to true', () => {
|
|
143
|
-
it('it renders dismiss button and end button', () => {
|
|
144
|
-
render(
|
|
145
|
-
<IntlProvider locale="en" messages={{}}>
|
|
146
|
-
<div id="#target-element" />
|
|
147
|
-
<Checkpoint
|
|
148
|
-
advanceButtonText="Next"
|
|
149
|
-
body="Lorem ipsum checkpoint body"
|
|
150
|
-
dismissButtonText="Dismiss"
|
|
151
|
-
endButtonText="End"
|
|
152
|
-
index={0}
|
|
153
|
-
onAdvance={handleAdvance}
|
|
154
|
-
onDismiss={handleDismiss}
|
|
155
|
-
onEnd={handleEnd}
|
|
156
|
-
target="#target-element"
|
|
157
|
-
title="Checkpoint title"
|
|
158
|
-
totalCheckpoints={1}
|
|
159
|
-
showDismissButton
|
|
160
|
-
/>
|
|
161
|
-
</IntlProvider>,
|
|
162
|
-
);
|
|
163
|
-
expect(screen.getByText('Dismiss', { selector: 'button' })).toBeInTheDocument();
|
|
164
|
-
expect(screen.getByText('End', { selector: 'button' })).toBeInTheDocument();
|
|
165
|
-
});
|
|
166
133
|
});
|
|
167
134
|
});
|
|
@@ -1,68 +1,68 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import ActionRow from '../ActionRow';
|
|
3
5
|
import Button from '../Button';
|
|
4
6
|
|
|
5
7
|
const CheckpointActionRow = React.forwardRef(({
|
|
6
8
|
advanceButtonText,
|
|
7
|
-
|
|
9
|
+
backButtonText,
|
|
8
10
|
endButtonText,
|
|
9
11
|
isLastCheckpoint,
|
|
10
12
|
onAdvance,
|
|
11
|
-
|
|
13
|
+
onBack,
|
|
12
14
|
onEnd,
|
|
13
|
-
showDismissButton,
|
|
14
15
|
index,
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
}) => {
|
|
17
|
+
const isFirstCheckpoint = index === 0;
|
|
18
|
+
return (
|
|
19
|
+
<ActionRow className="pgn__checkpoint-action-row">
|
|
20
|
+
{!isFirstCheckpoint && (
|
|
21
|
+
<Button
|
|
22
|
+
className="pgn__checkpoint-button-back"
|
|
23
|
+
variant="tertiary"
|
|
24
|
+
onClick={onBack}
|
|
25
|
+
>
|
|
26
|
+
{backButtonText}
|
|
27
|
+
</Button>
|
|
28
|
+
)}
|
|
18
29
|
<Button
|
|
19
|
-
|
|
20
|
-
className="pgn__checkpoint-
|
|
21
|
-
onClick={
|
|
30
|
+
autoFocus
|
|
31
|
+
className="pgn__checkpoint-button_advance"
|
|
32
|
+
onClick={isLastCheckpoint ? () => onEnd(index) : () => onAdvance(index)}
|
|
22
33
|
>
|
|
23
|
-
{
|
|
34
|
+
{isLastCheckpoint ? endButtonText : advanceButtonText}
|
|
24
35
|
</Button>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
className="pgn__checkpoint-button_advance"
|
|
29
|
-
variant="primary"
|
|
30
|
-
onClick={isLastCheckpoint ? () => onEnd(index) : () => onAdvance(index)}
|
|
31
|
-
>
|
|
32
|
-
{isLastCheckpoint ? endButtonText : advanceButtonText}
|
|
33
|
-
</Button>
|
|
34
|
-
</div>
|
|
35
|
-
));
|
|
36
|
+
</ActionRow>
|
|
37
|
+
);
|
|
38
|
+
});
|
|
36
39
|
|
|
37
40
|
CheckpointActionRow.defaultProps = {
|
|
38
41
|
advanceButtonText: '',
|
|
39
|
-
|
|
42
|
+
backButtonText: '',
|
|
40
43
|
endButtonText: '',
|
|
41
44
|
isLastCheckpoint: false,
|
|
42
|
-
onAdvance: () => {},
|
|
43
|
-
|
|
44
|
-
onEnd: () => {},
|
|
45
|
-
showDismissButton: undefined,
|
|
45
|
+
onAdvance: () => { },
|
|
46
|
+
onBack: () => { },
|
|
47
|
+
onEnd: () => { },
|
|
46
48
|
index: 0,
|
|
47
49
|
};
|
|
48
50
|
|
|
49
51
|
CheckpointActionRow.propTypes = {
|
|
50
52
|
/** The text displayed on the button used to advance the tour. */
|
|
51
53
|
advanceButtonText: PropTypes.node,
|
|
52
|
-
/** The text displayed on the button used to
|
|
53
|
-
|
|
54
|
+
/** The text displayed on the button used to go back on the tour */
|
|
55
|
+
backButtonText: PropTypes.string,
|
|
54
56
|
/** The text displayed on the button used to end the tour. */
|
|
55
57
|
endButtonText: PropTypes.node,
|
|
56
58
|
/** Whether the parent Checkpoint is the last in the tour. */
|
|
57
59
|
isLastCheckpoint: PropTypes.bool,
|
|
58
60
|
/** A function that runs when triggering the `onClick` event of the advance button. */
|
|
59
61
|
onAdvance: PropTypes.func,
|
|
60
|
-
/** A function that runs when triggering the `onClick` event of the
|
|
61
|
-
|
|
62
|
+
/** A function that runs when triggering the `onClick` event of the back button. */
|
|
63
|
+
onBack: PropTypes.func,
|
|
62
64
|
/** A function that runs when triggering the `onClick` event of the advance button if isLastCheckpoint is true. */
|
|
63
65
|
onEnd: PropTypes.func,
|
|
64
|
-
/** Enforces visibility of the dismiss button under all circumstances */
|
|
65
|
-
showDismissButton: PropTypes.bool,
|
|
66
66
|
/** Allows visibility of last index value for onEnd checkpoint compatibility */
|
|
67
67
|
index: PropTypes.number,
|
|
68
68
|
};
|