@openedx/paragon 22.17.0 → 22.18.1
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/Alert/index.scss +5 -0
- 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/Alert/index.scss +5 -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
package/src/Alert/index.scss
CHANGED
|
@@ -15,10 +15,15 @@
|
|
|
15
15
|
padding: $alert-padding-y $alert-padding-x;
|
|
16
16
|
margin-bottom: $alert-margin-bottom;
|
|
17
17
|
border: $alert-border-width solid transparent;
|
|
18
|
+
align-items: center;
|
|
18
19
|
|
|
19
20
|
@include border-radius($alert-border-radius);
|
|
20
21
|
@include pgn-box-shadow(1, "down");
|
|
21
22
|
|
|
23
|
+
&:has(.alert-heading:not(:only-child)) {
|
|
24
|
+
align-items: start;
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
.alert-message-content > :last-child {
|
|
23
28
|
margin-bottom: 0;
|
|
24
29
|
}
|
|
@@ -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
|
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { FormattedMessage, useIntl } from 'react-intl';
|
|
4
|
+
|
|
5
|
+
import Icon from '../Icon';
|
|
6
|
+
import IconButton from '../IconButton';
|
|
7
|
+
import { Close } from '../../icons';
|
|
8
|
+
import CheckpointTitle from './CheckpointTitle';
|
|
9
|
+
import messages from './messages';
|
|
10
|
+
|
|
11
|
+
const CheckpointHeader = React.forwardRef(({
|
|
12
|
+
dismissAltText, index, onDismiss, title, totalCheckpoints,
|
|
13
|
+
}) => {
|
|
14
|
+
const intl = useIntl();
|
|
15
|
+
const oneBasedIndex = index + 1;
|
|
16
|
+
const altText = (dismissAltText && typeof dismissAltText === 'string') ? dismissAltText : intl.formatMessage(messages.closeAltText);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<>
|
|
20
|
+
<header className="pgn__checkpoint-header">
|
|
21
|
+
<span className="pgn__checkpoint-page-index">
|
|
22
|
+
<FormattedMessage
|
|
23
|
+
{...messages.pageIndexText}
|
|
24
|
+
values={{ step: oneBasedIndex, totalSteps: totalCheckpoints }}
|
|
25
|
+
/>
|
|
26
|
+
</span>
|
|
27
|
+
<IconButton
|
|
28
|
+
size="sm"
|
|
29
|
+
iconAs={Icon}
|
|
30
|
+
src={Close}
|
|
31
|
+
alt={altText}
|
|
32
|
+
onClick={onDismiss}
|
|
33
|
+
data-testid="dismiss-tour"
|
|
34
|
+
/>
|
|
35
|
+
</header>
|
|
36
|
+
{title && (<CheckpointTitle>{title}</CheckpointTitle>)}
|
|
37
|
+
</>
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
CheckpointHeader.defaultProps = {
|
|
42
|
+
dismissAltText: null,
|
|
43
|
+
title: '',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
CheckpointHeader.propTypes = {
|
|
47
|
+
/** The text used in the alt for the icon used to dismiss the tour for the given Checkpoint */
|
|
48
|
+
dismissAltText: PropTypes.string,
|
|
49
|
+
/** The current index of the given Checkpoint */
|
|
50
|
+
index: PropTypes.number.isRequired,
|
|
51
|
+
/** A function that runs when triggering the `onClick` event of the dismiss
|
|
52
|
+
* button for the given Checkpoint. */
|
|
53
|
+
onDismiss: PropTypes.func.isRequired,
|
|
54
|
+
/** The text displayed in the title of the Checkpoint */
|
|
55
|
+
title: PropTypes.node,
|
|
56
|
+
/** The total number of Checkpoints in a tour */
|
|
57
|
+
totalCheckpoints: PropTypes.number.isRequired,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default CheckpointHeader;
|