@openedx/paragon 22.17.0 → 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/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/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
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { render, screen
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
3
|
import userEvent from '@testing-library/user-event';
|
|
4
4
|
import { IntlProvider } from 'react-intl';
|
|
5
5
|
|
|
6
6
|
import * as popper from '@popperjs/core';
|
|
7
7
|
|
|
8
8
|
import ProductTour from '.';
|
|
9
|
+
import messages from './messages';
|
|
9
10
|
|
|
10
11
|
const popperMock = jest.spyOn(popper, 'createPopper');
|
|
11
12
|
|
|
@@ -24,10 +25,10 @@ describe('<ProductTour />', () => {
|
|
|
24
25
|
const customOnEnd = jest.fn();
|
|
25
26
|
const customOnDismiss = jest.fn();
|
|
26
27
|
const customOnAdvance = jest.fn();
|
|
28
|
+
const customOnBack = jest.fn();
|
|
27
29
|
|
|
28
30
|
const disabledTourData = {
|
|
29
31
|
advanceButtonText: 'Next',
|
|
30
|
-
dismissButtonText: 'Dismiss',
|
|
31
32
|
enabled: false,
|
|
32
33
|
endButtonText: 'Okay',
|
|
33
34
|
onDismiss: handleDismiss,
|
|
@@ -44,7 +45,7 @@ describe('<ProductTour />', () => {
|
|
|
44
45
|
|
|
45
46
|
const tourData = {
|
|
46
47
|
advanceButtonText: 'Next',
|
|
47
|
-
|
|
48
|
+
backButtonText: 'Back',
|
|
48
49
|
enabled: true,
|
|
49
50
|
endButtonText: 'Okay',
|
|
50
51
|
onDismiss: handleDismiss,
|
|
@@ -57,22 +58,19 @@ describe('<ProductTour />', () => {
|
|
|
57
58
|
title: 'Checkpoint 1',
|
|
58
59
|
},
|
|
59
60
|
{
|
|
60
|
-
body: '
|
|
61
|
+
body: 'Checkpoint 2',
|
|
61
62
|
target: '#target-2',
|
|
62
|
-
title: 'Checkpoint 2',
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
|
-
body: '
|
|
65
|
+
body: 'Checkpoint 3',
|
|
66
66
|
target: '#target-3',
|
|
67
|
-
|
|
68
|
-
onDismiss: customOnDismiss,
|
|
67
|
+
onBack: customOnBack,
|
|
69
68
|
advanceButtonText: 'Override advance',
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
backButtonText: 'Override back',
|
|
72
70
|
},
|
|
73
71
|
{
|
|
74
72
|
target: '#target-3',
|
|
75
|
-
|
|
73
|
+
body: 'Checkpoint 4',
|
|
76
74
|
endButtonText: 'End',
|
|
77
75
|
},
|
|
78
76
|
],
|
|
@@ -98,31 +96,25 @@ describe('<ProductTour />', () => {
|
|
|
98
96
|
|
|
99
97
|
describe('one enabled tour', () => {
|
|
100
98
|
describe('with default settings', () => {
|
|
101
|
-
it('renders checkpoint with correct title, body, and
|
|
99
|
+
it('renders checkpoint with correct title, body, and page index', () => {
|
|
102
100
|
render(<ProductTourWrapper tours={[tourData]} />);
|
|
103
101
|
|
|
104
102
|
expect(screen.getByRole('dialog', { name: 'Checkpoint 1' })).toBeInTheDocument();
|
|
105
103
|
expect(screen.getByText('Checkpoint 1')).toBeInTheDocument();
|
|
106
|
-
expect(screen.
|
|
104
|
+
expect(screen.getByText('1 of 4')).toBeInTheDocument();
|
|
107
105
|
});
|
|
108
106
|
|
|
109
107
|
it('onClick of advance button advances to next checkpoint', async () => {
|
|
110
|
-
|
|
108
|
+
render(<ProductTourWrapper tours={[tourData]} />);
|
|
111
109
|
// Verify the first Checkpoint has rendered
|
|
112
110
|
expect(screen.getByRole('heading', { name: 'Checkpoint 1' })).toBeInTheDocument();
|
|
113
111
|
|
|
114
112
|
// Click the advance button
|
|
115
113
|
const advanceButton = screen.getByRole('button', { name: 'Next' });
|
|
116
|
-
await
|
|
117
|
-
await userEvent.click(advanceButton);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
rerender(<ProductTourWrapper tours={[tourData]} />);
|
|
121
|
-
|
|
122
|
-
const heading = screen.getByRole('heading', { name: 'Checkpoint 2' });
|
|
114
|
+
await userEvent.click(advanceButton);
|
|
123
115
|
|
|
124
116
|
// Verify the second Checkpoint has rendered
|
|
125
|
-
expect(
|
|
117
|
+
expect(screen.getByText('Checkpoint 2')).toBeInTheDocument();
|
|
126
118
|
});
|
|
127
119
|
|
|
128
120
|
it('onClick of dismiss button disables tour', async () => {
|
|
@@ -132,20 +124,53 @@ describe('<ProductTour />', () => {
|
|
|
132
124
|
expect(screen.getByRole('dialog', { name: 'Checkpoint 1' })).toBeInTheDocument();
|
|
133
125
|
|
|
134
126
|
// Click the dismiss button
|
|
135
|
-
const
|
|
136
|
-
expect(
|
|
127
|
+
const closeButton = screen.getByRole('button', { name: 'Close tour' });
|
|
128
|
+
expect(closeButton).toBeInTheDocument();
|
|
137
129
|
|
|
138
|
-
await
|
|
139
|
-
await userEvent.click(dismissButton);
|
|
140
|
-
});
|
|
130
|
+
await userEvent.click(closeButton);
|
|
141
131
|
|
|
142
132
|
// Verify no Checkpoints have rendered
|
|
143
133
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
144
134
|
});
|
|
145
135
|
|
|
136
|
+
it('onClick of close icon button disables tour', async () => {
|
|
137
|
+
const user = userEvent.setup();
|
|
138
|
+
render(<ProductTourWrapper tours={[tourData]} />);
|
|
139
|
+
// Advance the tour, close icon only appears after first step
|
|
140
|
+
|
|
141
|
+
await user.click(screen.getByRole('button', { name: 'Next' }));
|
|
142
|
+
|
|
143
|
+
const closeIcon = screen.getByRole('button', { name: messages.closeAltText.defaultMessage });
|
|
144
|
+
expect(closeIcon).toBeInTheDocument();
|
|
145
|
+
|
|
146
|
+
await user.click(closeIcon);
|
|
147
|
+
|
|
148
|
+
expect(handleDismiss).toHaveBeenCalled();
|
|
149
|
+
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('onClick of back button going to the previous checkpoint', async () => {
|
|
153
|
+
const user = userEvent.setup();
|
|
154
|
+
render(<ProductTourWrapper tours={[tourData]} />);
|
|
155
|
+
// Back button only appears when you are not on the first step of the tour
|
|
156
|
+
expect(screen.getByRole('heading', { name: 'Checkpoint 1' })).toBeInTheDocument();
|
|
157
|
+
expect(screen.queryByRole('button', { name: 'Back' })).not.toBeInTheDocument();
|
|
158
|
+
// Advance the tour
|
|
159
|
+
await user.click(screen.getByRole('button', { name: 'Next' }));
|
|
160
|
+
|
|
161
|
+
expect(screen.getByText('Checkpoint 2')).toBeInTheDocument();
|
|
162
|
+
|
|
163
|
+
// Go back in the tour
|
|
164
|
+
const backButton = screen.getByRole('button', { name: 'Back' });
|
|
165
|
+
expect(backButton).toBeInTheDocument();
|
|
166
|
+
await user.click(backButton);
|
|
167
|
+
|
|
168
|
+
expect(screen.getByText('Checkpoint 1')).toBeInTheDocument();
|
|
169
|
+
});
|
|
170
|
+
|
|
146
171
|
it('onClick of end button disables tour', async () => {
|
|
147
172
|
const user = userEvent.setup();
|
|
148
|
-
|
|
173
|
+
render(<ProductTourWrapper tours={[tourData]} />);
|
|
149
174
|
|
|
150
175
|
// Verify a Checkpoint has rendered
|
|
151
176
|
expect(screen.getByRole('dialog', { name: 'Checkpoint 1' })).toBeInTheDocument();
|
|
@@ -157,13 +182,9 @@ describe('<ProductTour />', () => {
|
|
|
157
182
|
const advanceButton2 = screen.getByRole('button', { name: 'Next' });
|
|
158
183
|
await user.click(advanceButton2);
|
|
159
184
|
|
|
160
|
-
rerender(<ProductTourWrapper tours={[tourData]} />);
|
|
161
|
-
|
|
162
185
|
const advanceButton3 = screen.getByRole('button', { name: 'Override advance' });
|
|
163
186
|
await user.click(advanceButton3);
|
|
164
187
|
|
|
165
|
-
rerender(<ProductTourWrapper tours={[tourData]} />);
|
|
166
|
-
|
|
167
188
|
// Click the end button
|
|
168
189
|
const endButton = screen.getByRole('button', { name: 'End' });
|
|
169
190
|
await user.click(endButton);
|
|
@@ -191,7 +212,7 @@ describe('<ProductTour />', () => {
|
|
|
191
212
|
describe('with Checkpoint override settings', () => {
|
|
192
213
|
const overrideTourData = {
|
|
193
214
|
advanceButtonText: 'Next',
|
|
194
|
-
|
|
215
|
+
backButtonText: 'Back',
|
|
195
216
|
enabled: true,
|
|
196
217
|
endButtonText: 'Okay',
|
|
197
218
|
onDismiss: handleDismiss,
|
|
@@ -206,23 +227,21 @@ describe('<ProductTour />', () => {
|
|
|
206
227
|
title: 'Checkpoint 1',
|
|
207
228
|
},
|
|
208
229
|
{
|
|
209
|
-
body: 'Lorem ipsum body',
|
|
210
230
|
target: '#target-2',
|
|
211
|
-
|
|
231
|
+
body: 'Checkpoint 2',
|
|
212
232
|
},
|
|
213
233
|
{
|
|
214
|
-
body: 'Lorem ipsum body',
|
|
215
234
|
target: '#target-3',
|
|
216
|
-
|
|
235
|
+
body: 'Checkpoint 3',
|
|
236
|
+
onBack: customOnBack,
|
|
217
237
|
onDismiss: customOnDismiss,
|
|
218
238
|
onAdvance: customOnAdvance,
|
|
219
239
|
advanceButtonText: 'Override advance',
|
|
220
|
-
|
|
221
|
-
|
|
240
|
+
backButtonText: 'Override back',
|
|
222
241
|
},
|
|
223
242
|
{
|
|
224
243
|
target: '#target-4',
|
|
225
|
-
|
|
244
|
+
body: 'Checkpoint 4',
|
|
226
245
|
endButtonText: 'Override end',
|
|
227
246
|
onEnd: customOnEnd,
|
|
228
247
|
},
|
|
@@ -230,45 +249,38 @@ describe('<ProductTour />', () => {
|
|
|
230
249
|
};
|
|
231
250
|
it('renders correct checkpoint on index override', () => {
|
|
232
251
|
render(<ProductTourWrapper tours={[overrideTourData]} />);
|
|
233
|
-
expect(screen.
|
|
234
|
-
expect(screen.getByRole('heading', { name: 'Checkpoint 3' })).toBeInTheDocument();
|
|
252
|
+
expect(screen.getByText('Checkpoint 3')).toBeInTheDocument();
|
|
235
253
|
});
|
|
236
254
|
|
|
237
255
|
it('applies override for advanceButtonText', async () => {
|
|
238
|
-
|
|
256
|
+
render(<ProductTourWrapper tours={[overrideTourData]} />);
|
|
239
257
|
expect(screen.getByRole('button', { name: 'Override advance' })).toBeInTheDocument();
|
|
240
258
|
const advanceButton = screen.getByRole('button', { name: 'Override advance' });
|
|
241
|
-
await
|
|
242
|
-
await userEvent.click(advanceButton);
|
|
243
|
-
});
|
|
259
|
+
await userEvent.click(advanceButton);
|
|
244
260
|
expect(screen.queryByRole('button', { name: 'Override advance' })).not.toBeInTheDocument();
|
|
245
261
|
expect(customOnAdvance).toHaveBeenCalledTimes(1);
|
|
246
262
|
|
|
247
|
-
rerender(<ProductTourWrapper tours={[overrideTourData]} />);
|
|
248
|
-
|
|
249
263
|
expect(screen.getByText('Checkpoint 4')).toBeInTheDocument();
|
|
250
264
|
});
|
|
251
|
-
it('applies override for
|
|
265
|
+
it('applies override for backButtonText', () => {
|
|
252
266
|
render(<ProductTourWrapper tours={[overrideTourData]} />);
|
|
253
|
-
expect(screen.getByRole('button', { name: 'Override
|
|
267
|
+
expect(screen.getByRole('button', { name: 'Override back' })).toBeInTheDocument();
|
|
254
268
|
});
|
|
269
|
+
|
|
255
270
|
it('calls customHandleDismiss onClick of dismiss button', async () => {
|
|
256
271
|
render(<ProductTourWrapper tours={[overrideTourData]} />);
|
|
257
|
-
const
|
|
258
|
-
await
|
|
259
|
-
|
|
260
|
-
});
|
|
272
|
+
const closeButton = screen.getByRole('button', { name: 'Close tour' });
|
|
273
|
+
await userEvent.click(closeButton);
|
|
274
|
+
|
|
261
275
|
expect(customOnDismiss).toHaveBeenCalledTimes(1);
|
|
262
276
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
263
277
|
});
|
|
264
278
|
it('calls customHandleOnEnd onClick of end button', async () => {
|
|
265
279
|
const user = userEvent.setup();
|
|
266
|
-
|
|
280
|
+
render(<ProductTourWrapper tours={[overrideTourData]} />);
|
|
267
281
|
const advanceButton = screen.getByRole('button', { name: 'Override advance' });
|
|
268
282
|
await user.click(advanceButton);
|
|
269
283
|
|
|
270
|
-
rerender(<ProductTourWrapper tours={[overrideTourData]} />);
|
|
271
|
-
|
|
272
284
|
expect(screen.getByText('Checkpoint 4')).toBeInTheDocument();
|
|
273
285
|
const endButton = screen.getByRole('button', { name: 'Override end' });
|
|
274
286
|
await user.click(endButton);
|
|
@@ -292,7 +304,6 @@ describe('<ProductTour />', () => {
|
|
|
292
304
|
it('does not render', () => {
|
|
293
305
|
const badTourData = {
|
|
294
306
|
advanceButtonText: 'Next',
|
|
295
|
-
dismissButtonText: 'Dismiss',
|
|
296
307
|
enabled: true,
|
|
297
308
|
endButtonText: 'Okay',
|
|
298
309
|
onDismiss: handleDismiss,
|
|
@@ -315,7 +326,6 @@ describe('<ProductTour />', () => {
|
|
|
315
326
|
it('advances to next valid Checkpoint', () => {
|
|
316
327
|
const badTourData = {
|
|
317
328
|
advanceButtonText: 'Next',
|
|
318
|
-
dismissButtonText: 'Dismiss',
|
|
319
329
|
enabled: true,
|
|
320
330
|
endButtonText: 'Okay',
|
|
321
331
|
onDismiss: handleDismiss,
|
|
@@ -348,7 +358,6 @@ describe('<ProductTour />', () => {
|
|
|
348
358
|
it('renders first enabled tour', () => {
|
|
349
359
|
const secondEnabledTourData = {
|
|
350
360
|
advanceButtonText: 'Next',
|
|
351
|
-
dismissButtonText: 'Dismiss',
|
|
352
361
|
enabled: true,
|
|
353
362
|
endButtonText: 'Okay',
|
|
354
363
|
onDismiss: handleDismiss,
|
|
@@ -16,6 +16,12 @@ tours are enabled, `ProductTour` will only render the first enabled in the `tour
|
|
|
16
16
|
`Checkpoints` are rendered in the order they're listed in the checkpoint array.
|
|
17
17
|
The checkpoint objects themselves have additional props that can override the props defined in `ProductTour`.
|
|
18
18
|
|
|
19
|
+
## Usage guidelines
|
|
20
|
+
Best practices for ProductTour includes not overloading the user with a large amount of steps.
|
|
21
|
+
Paragon recommends keeping to no more than 5 steps, as well as any overriden button names to be
|
|
22
|
+
descriptive and readable. Also, we recommend using a title at every step or only at the first step for a
|
|
23
|
+
consistent and accessible experience.
|
|
24
|
+
|
|
19
25
|
## Basic Usage
|
|
20
26
|
|
|
21
27
|
```jsx live
|
|
@@ -24,11 +30,12 @@ The checkpoint objects themselves have additional props that can override the pr
|
|
|
24
30
|
const myFirstTour = {
|
|
25
31
|
tourId: 'myFirstTour',
|
|
26
32
|
advanceButtonText: 'Next',
|
|
27
|
-
|
|
33
|
+
backButtonText: 'Back',
|
|
28
34
|
endButtonText: 'Okay',
|
|
29
35
|
enabled: isTourEnabled,
|
|
30
36
|
onDismiss: () => setIsTourEnabled(false),
|
|
31
37
|
onEnd: () => setIsTourEnabled(false),
|
|
38
|
+
dismissAltText: '',
|
|
32
39
|
checkpoints: [
|
|
33
40
|
{
|
|
34
41
|
advanceButtonText: 'Onward', // Override the default advanceButtonText above
|
|
@@ -36,6 +43,7 @@ The checkpoint objects themselves have additional props that can override the pr
|
|
|
36
43
|
placement: 'top',
|
|
37
44
|
target: '#checkpoint-1',
|
|
38
45
|
title: 'First checkpoint',
|
|
46
|
+
dismissAltText: '',
|
|
39
47
|
},
|
|
40
48
|
{
|
|
41
49
|
body: "Here's the second checkpoint!",
|
|
@@ -46,12 +54,12 @@ The checkpoint objects themselves have additional props that can override the pr
|
|
|
46
54
|
placement: 'right',
|
|
47
55
|
target: '#checkpoint-2',
|
|
48
56
|
title: 'Second checkpoint',
|
|
57
|
+
backButtonText: 'Rewind',
|
|
49
58
|
},
|
|
50
59
|
{
|
|
51
|
-
body: "
|
|
60
|
+
body: "The third checkpoint without a title",
|
|
52
61
|
placement: 'bottom',
|
|
53
62
|
target: '#checkpoint-3',
|
|
54
|
-
title: 'Third checkpoint',
|
|
55
63
|
onEnd: () => {
|
|
56
64
|
console.log('Ended the third checkpoint');
|
|
57
65
|
setIsTourEnabled(false);
|
|
@@ -1,22 +1,39 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
+
import withDeprecatedProps, { DeprTypes } from '../withDeprecatedProps';
|
|
3
4
|
|
|
4
5
|
import Checkpoint from './Checkpoint';
|
|
5
6
|
|
|
6
7
|
const ProductTour = React.forwardRef(({ tours }, ref) => {
|
|
7
8
|
const tourValue = tours.find((tour) => tour.enabled);
|
|
8
9
|
const {
|
|
9
|
-
enabled,
|
|
10
|
-
|
|
10
|
+
enabled,
|
|
11
|
+
checkpoints = [],
|
|
12
|
+
startingIndex,
|
|
13
|
+
onEscape,
|
|
14
|
+
onEnd,
|
|
15
|
+
onBack,
|
|
16
|
+
onDismiss: tourOnDismiss,
|
|
17
|
+
advanceButtonText: tourAdvanceButtonText,
|
|
18
|
+
dismissAltText: tourDismissAltText,
|
|
11
19
|
endButtonText: tourEndButtonText,
|
|
20
|
+
backButtonText: tourBackButtonText,
|
|
12
21
|
} = tourValue || {};
|
|
13
22
|
const [currentCheckpointData, setCurrentCheckpointData] = useState(null);
|
|
14
23
|
const [index, setIndex] = useState(0);
|
|
15
24
|
const [isTourEnabled, setIsTourEnabled] = useState(false);
|
|
16
25
|
const [prunedCheckpoints, setPrunedCheckpoints] = useState([]);
|
|
17
26
|
const {
|
|
18
|
-
title,
|
|
19
|
-
|
|
27
|
+
title,
|
|
28
|
+
body,
|
|
29
|
+
onAdvance,
|
|
30
|
+
onDismiss,
|
|
31
|
+
advanceButtonText,
|
|
32
|
+
dismissAltText,
|
|
33
|
+
endButtonText,
|
|
34
|
+
backButtonText,
|
|
35
|
+
placement,
|
|
36
|
+
target,
|
|
20
37
|
} = currentCheckpointData || {};
|
|
21
38
|
|
|
22
39
|
/**
|
|
@@ -71,6 +88,13 @@ const ProductTour = React.forwardRef(({ tours }, ref) => {
|
|
|
71
88
|
}
|
|
72
89
|
};
|
|
73
90
|
|
|
91
|
+
const handleBack = () => {
|
|
92
|
+
setIndex(index - 1);
|
|
93
|
+
if (onBack) {
|
|
94
|
+
onBack();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
74
98
|
const handleDismiss = () => {
|
|
75
99
|
setIndex(0);
|
|
76
100
|
setIsTourEnabled(false);
|
|
@@ -106,9 +130,11 @@ const ProductTour = React.forwardRef(({ tours }, ref) => {
|
|
|
106
130
|
advanceButtonText={advanceButtonText || tourAdvanceButtonText}
|
|
107
131
|
body={body}
|
|
108
132
|
currentCheckpointData={currentCheckpointData}
|
|
109
|
-
|
|
133
|
+
dismissAltText={dismissAltText || tourDismissAltText}
|
|
110
134
|
endButtonText={endButtonText || tourEndButtonText}
|
|
135
|
+
backButtonText={backButtonText || tourBackButtonText}
|
|
111
136
|
index={index}
|
|
137
|
+
onBack={handleBack}
|
|
112
138
|
onAdvance={handleAdvance}
|
|
113
139
|
onDismiss={handleDismiss}
|
|
114
140
|
onEnd={handleEnd}
|
|
@@ -116,7 +142,6 @@ const ProductTour = React.forwardRef(({ tours }, ref) => {
|
|
|
116
142
|
target={target}
|
|
117
143
|
title={title}
|
|
118
144
|
totalCheckpoints={prunedCheckpoints.length}
|
|
119
|
-
showDismissButton={showDismissButton}
|
|
120
145
|
ref={ref}
|
|
121
146
|
/>
|
|
122
147
|
);
|
|
@@ -125,19 +150,22 @@ const ProductTour = React.forwardRef(({ tours }, ref) => {
|
|
|
125
150
|
ProductTour.defaultProps = {
|
|
126
151
|
tours: {
|
|
127
152
|
advanceButtonText: '',
|
|
153
|
+
backButtonText: '',
|
|
128
154
|
checkpoints: {
|
|
129
155
|
advanceButtonText: '',
|
|
156
|
+
backButtonText: '',
|
|
130
157
|
body: '',
|
|
131
|
-
|
|
158
|
+
dismissAltText: '',
|
|
132
159
|
endButtonText: '',
|
|
133
160
|
onAdvance: () => {},
|
|
134
161
|
onDismiss: () => {},
|
|
162
|
+
onBack: () => {},
|
|
135
163
|
placement: 'top',
|
|
136
164
|
title: '',
|
|
137
|
-
showDismissButton: undefined,
|
|
138
165
|
},
|
|
139
|
-
|
|
166
|
+
dismissAltText: '',
|
|
140
167
|
endButtonText: '',
|
|
168
|
+
onBack: () => {},
|
|
141
169
|
onDismiss: () => {},
|
|
142
170
|
onEnd: () => {},
|
|
143
171
|
onEscape: () => {},
|
|
@@ -149,16 +177,20 @@ ProductTour.propTypes = {
|
|
|
149
177
|
tours: PropTypes.arrayOf(PropTypes.shape({
|
|
150
178
|
/** The text displayed on all buttons used to advance the tour. */
|
|
151
179
|
advanceButtonText: PropTypes.node,
|
|
180
|
+
/** The text displayed on all buttons used to go back in the tour */
|
|
181
|
+
backButtonText: PropTypes.string,
|
|
152
182
|
/** An array comprised of checkpoint objects supporting the following values: */
|
|
153
183
|
checkpoints: PropTypes.arrayOf(PropTypes.shape({
|
|
154
184
|
/** The text displayed on the button used to advance the tour for the given Checkpoint
|
|
155
185
|
* (overrides the* `advanceButtonText` defined in the parent tour object). */
|
|
156
186
|
advanceButtonText: PropTypes.node,
|
|
187
|
+
/** The text displayed on the button used to go back in the tour for the given Checkpoint
|
|
188
|
+
* (overrides the* `backButtonText` defined in the parent tour object). */
|
|
189
|
+
backButtonText: PropTypes.string,
|
|
157
190
|
/** The text displayed in the body of the Checkpoint */
|
|
158
191
|
body: PropTypes.node,
|
|
159
|
-
/** The text
|
|
160
|
-
|
|
161
|
-
dismissButtonText: PropTypes.node,
|
|
192
|
+
/** The text used in the alt for the icon used to dismiss the tour for the given Checkpoint */
|
|
193
|
+
dismissAltText: PropTypes.string,
|
|
162
194
|
/** The text displayed on the button used to end the tour for the given Checkpoint
|
|
163
195
|
* (overrides the `endButtonText` defined in the parent tour object). */
|
|
164
196
|
endButtonText: PropTypes.node,
|
|
@@ -180,15 +212,15 @@ ProductTour.propTypes = {
|
|
|
180
212
|
target: PropTypes.string.isRequired,
|
|
181
213
|
/** The text displayed in the title of the Checkpoint */
|
|
182
214
|
title: PropTypes.node,
|
|
183
|
-
/** Enforces visibility of the dismiss button under all circumstances */
|
|
184
|
-
showDismissButton: PropTypes.bool,
|
|
185
215
|
})),
|
|
186
|
-
/** The text
|
|
187
|
-
|
|
216
|
+
/** The text used in the alt for the icon used to dismiss the tour for the given Checkpoint */
|
|
217
|
+
dismissAltText: PropTypes.string,
|
|
188
218
|
/** Whether the tour is enabled. If there are multiple tours defined, only one should be enabled at a time. */
|
|
189
219
|
enabled: PropTypes.bool.isRequired,
|
|
190
220
|
/** The text displayed on the button used to end the tour. */
|
|
191
221
|
endButtonText: PropTypes.node,
|
|
222
|
+
/** A function that runs when triggering the `onBack` event of the back button. */
|
|
223
|
+
onBack: PropTypes.func,
|
|
192
224
|
/** A function that runs when triggering the `onClick` event of the dismiss button. */
|
|
193
225
|
onDismiss: PropTypes.func,
|
|
194
226
|
/** A function that runs when triggering the `onClick` event of the end button. */
|
|
@@ -202,4 +234,80 @@ ProductTour.propTypes = {
|
|
|
202
234
|
})),
|
|
203
235
|
};
|
|
204
236
|
|
|
205
|
-
|
|
237
|
+
/**
|
|
238
|
+
* Checks if the given object has a deprecated/legacy `dismissButtonText` property.
|
|
239
|
+
* @param {Object} obj - The object to check
|
|
240
|
+
* @returns {boolean} - True if the object has a deprecated/legacy `dismissButtonText` property, false otherwise
|
|
241
|
+
*/
|
|
242
|
+
const hasDismissButtonText = (obj) => {
|
|
243
|
+
if ('dismissButtonText' in obj && !!obj.dismissButtonText) {
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
return false;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
export default withDeprecatedProps(ProductTour, 'ProductTour', {
|
|
250
|
+
tours: {
|
|
251
|
+
deprType: DeprTypes.FORMAT,
|
|
252
|
+
message: "The dismissButtonText options in the 'tours' prop have been moved to 'dismissAltText'.",
|
|
253
|
+
/**
|
|
254
|
+
* Determines whether the given prop value contains the deprecated/legacy `dismissButtonText` property.
|
|
255
|
+
* @param {Object[]} propValue - The tours prop value to check
|
|
256
|
+
* @returns {boolean} True if the prop value contains the deprecated/legacy `dismissButtonText`
|
|
257
|
+
* property, false otherwise
|
|
258
|
+
*/
|
|
259
|
+
expect: (propValue) => {
|
|
260
|
+
if (!Array.isArray(propValue)) {
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
return !propValue.some((tour) => {
|
|
264
|
+
if (hasDismissButtonText(tour)) {
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
return Array.isArray(tour.checkpoints)
|
|
268
|
+
&& tour.checkpoints.some(hasDismissButtonText);
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
/**
|
|
272
|
+
* Transforms the given prop value by updating the
|
|
273
|
+
* deprecated/legacy `dismissButtonText` property to
|
|
274
|
+
* `dismissAltText`, if the prop value is a string. Otherwise,
|
|
275
|
+
* the original `dismissButtonText` property is ignored.
|
|
276
|
+
* @param {Object[]} propValue - The tours prop value to transform
|
|
277
|
+
* @returns {Object[]} The transformed prop value
|
|
278
|
+
*/
|
|
279
|
+
transform: (propValue) => {
|
|
280
|
+
const tours = propValue.map((tour) => {
|
|
281
|
+
const updatedTour = { ...tour };
|
|
282
|
+
|
|
283
|
+
// Replace tour level dismissButtonText with dismissAltText
|
|
284
|
+
if (hasDismissButtonText(tour)) {
|
|
285
|
+
if (typeof tour.dismissButtonText === 'string') {
|
|
286
|
+
updatedTour.dismissAltText = tour.dismissButtonText;
|
|
287
|
+
} else {
|
|
288
|
+
const warningMessage = "[Deprecated] ProductTour: The 'dismissButtonText' options within the 'tours' prop now expects a string";
|
|
289
|
+
// eslint-disable-next-line no-console
|
|
290
|
+
console.warn(warningMessage);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Replace checkpoint level dismissButtonText with dismissAltText
|
|
295
|
+
if (Array.isArray(tour.checkpoints)) {
|
|
296
|
+
updatedTour.checkpoints = tour.checkpoints.map((checkpoint) => {
|
|
297
|
+
if (hasDismissButtonText(checkpoint)) {
|
|
298
|
+
const { dismissButtonText, ...rest } = checkpoint;
|
|
299
|
+
if (typeof dismissButtonText === 'string') {
|
|
300
|
+
return { ...rest, dismissAltText: dismissButtonText };
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return checkpoint;
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
return updatedTour;
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Return the transformed tours
|
|
310
|
+
return tours;
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
});
|
|
@@ -11,6 +11,16 @@ const messages = defineMessages({
|
|
|
11
11
|
defaultMessage: 'Bottom of step {step}',
|
|
12
12
|
description: 'Screen-reader message to notify user that they are located at the bottom of the product tour step.',
|
|
13
13
|
},
|
|
14
|
+
pageIndexText: {
|
|
15
|
+
id: 'pgn.ProductTour.Checkpoint.page-index-text',
|
|
16
|
+
defaultMessage: '{step} of {totalSteps}',
|
|
17
|
+
description: 'Page index showing your place in the ProductTour',
|
|
18
|
+
},
|
|
19
|
+
closeAltText: {
|
|
20
|
+
id: 'pgn.ProductTour.checkpointHeader.close',
|
|
21
|
+
defaultMessage: 'Close tour',
|
|
22
|
+
description: 'Close alternative text for ProductTour component',
|
|
23
|
+
},
|
|
14
24
|
});
|
|
15
25
|
|
|
16
26
|
export default messages;
|
|
@@ -66,10 +66,17 @@ function withDeprecatedProps<T extends Record<string, any>>(
|
|
|
66
66
|
acc[propName] = this.props[propName];
|
|
67
67
|
}
|
|
68
68
|
break;
|
|
69
|
-
case DeprTypes.MOVED_AND_FORMAT:
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
case DeprTypes.MOVED_AND_FORMAT: {
|
|
70
|
+
const propValue = this.props[propName];
|
|
71
|
+
let warningMessage = `${componentName}: The prop '${propName}' has been moved to '${newName}'`;
|
|
72
|
+
if (expect && !expect(propValue)) {
|
|
73
|
+
warningMessage += ' and expects a new format';
|
|
74
|
+
}
|
|
75
|
+
warningMessage += message ? `. ${message}` : '';
|
|
76
|
+
this.warn(warningMessage);
|
|
77
|
+
acc[newName!] = transform ? transform(propValue, this.props) : propValue;
|
|
72
78
|
break;
|
|
79
|
+
}
|
|
73
80
|
default:
|
|
74
81
|
acc[propName] = this.props[propName];
|
|
75
82
|
break;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
const CheckpointBreadcrumbs = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
4
|
-
let {
|
|
5
|
-
currentIndex,
|
|
6
|
-
totalCheckpoints
|
|
7
|
-
} = _ref;
|
|
8
|
-
if (totalCheckpoints === 1) {
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
return /*#__PURE__*/React.createElement("span", {
|
|
12
|
-
className: "pgn__checkpoint-breadcrumb-container",
|
|
13
|
-
ref: ref
|
|
14
|
-
}, new Array(totalCheckpoints).fill(0).map((v, i) => i === currentIndex ? /*#__PURE__*/React.createElement("span", {
|
|
15
|
-
/* eslint-disable-next-line react/no-array-index-key */
|
|
16
|
-
key: i,
|
|
17
|
-
className: "pgn__checkpoint-breadcrumb pgn__checkpoint-breadcrumb_active",
|
|
18
|
-
"data-testid": "pgn__checkpoint-breadcrumb_active"
|
|
19
|
-
}) : /*#__PURE__*/React.createElement("span", {
|
|
20
|
-
/* eslint-disable-next-line react/no-array-index-key */
|
|
21
|
-
key: i,
|
|
22
|
-
className: "pgn__checkpoint-breadcrumb pgn__checkpoint-breadcrumb_inactive",
|
|
23
|
-
"data-testid": "pgn__checkpoint-breadcrumb_inactive"
|
|
24
|
-
})));
|
|
25
|
-
});
|
|
26
|
-
CheckpointBreadcrumbs.defaultProps = {
|
|
27
|
-
currentIndex: null,
|
|
28
|
-
totalCheckpoints: null
|
|
29
|
-
};
|
|
30
|
-
CheckpointBreadcrumbs.propTypes = {
|
|
31
|
-
/** The current index of the parent Checkpoint within the tour. */
|
|
32
|
-
currentIndex: PropTypes.number,
|
|
33
|
-
/** The total number of Checkpoints within the tour. */
|
|
34
|
-
totalCheckpoints: PropTypes.number
|
|
35
|
-
};
|
|
36
|
-
export default CheckpointBreadcrumbs;
|
|
37
|
-
//# sourceMappingURL=CheckpointBreadcrumbs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CheckpointBreadcrumbs.js","names":["React","PropTypes","CheckpointBreadcrumbs","forwardRef","_ref","ref","currentIndex","totalCheckpoints","createElement","className","Array","fill","map","v","i","key","defaultProps","propTypes","number"],"sources":["../../src/ProductTour/CheckpointBreadcrumbs.jsx"],"sourcesContent":["import React from 'react';\nimport PropTypes from 'prop-types';\n\nconst CheckpointBreadcrumbs = React.forwardRef(({ currentIndex, totalCheckpoints }, ref) => {\n if (totalCheckpoints === 1) {\n return null;\n }\n return (\n <span className=\"pgn__checkpoint-breadcrumb-container\" ref={ref}>\n {new Array(totalCheckpoints).fill(0).map((v, i) => (\n i === currentIndex\n ? (\n <span\n /* eslint-disable-next-line react/no-array-index-key */\n key={i}\n className=\"pgn__checkpoint-breadcrumb pgn__checkpoint-breadcrumb_active\"\n data-testid=\"pgn__checkpoint-breadcrumb_active\"\n />\n )\n : (\n <span\n /* eslint-disable-next-line react/no-array-index-key */\n key={i}\n className=\"pgn__checkpoint-breadcrumb pgn__checkpoint-breadcrumb_inactive\"\n data-testid=\"pgn__checkpoint-breadcrumb_inactive\"\n />\n )\n ))}\n </span>\n );\n});\n\nCheckpointBreadcrumbs.defaultProps = {\n currentIndex: null,\n totalCheckpoints: null,\n};\n\nCheckpointBreadcrumbs.propTypes = {\n /** The current index of the parent Checkpoint within the tour. */\n currentIndex: PropTypes.number,\n /** The total number of Checkpoints within the tour. */\n totalCheckpoints: PropTypes.number,\n};\n\nexport default CheckpointBreadcrumbs;\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,OAAOC,SAAS,MAAM,YAAY;AAElC,MAAMC,qBAAqB,gBAAGF,KAAK,CAACG,UAAU,CAAC,CAAAC,IAAA,EAAqCC,GAAG,KAAK;EAAA,IAA5C;IAAEC,YAAY;IAAEC;EAAiB,CAAC,GAAAH,IAAA;EAChF,IAAIG,gBAAgB,KAAK,CAAC,EAAE;IAC1B,OAAO,IAAI;EACb;EACA,oBACEP,KAAA,CAAAQ,aAAA;IAAMC,SAAS,EAAC,sCAAsC;IAACJ,GAAG,EAAEA;EAAI,GAC7D,IAAIK,KAAK,CAACH,gBAAgB,CAAC,CAACI,IAAI,CAAC,CAAC,CAAC,CAACC,GAAG,CAAC,CAACC,CAAC,EAAEC,CAAC,KAC5CA,CAAC,KAAKR,YAAY,gBAEdN,KAAA,CAAAQ,aAAA;IACE;IACAO,GAAG,EAAED,CAAE;IACPL,SAAS,EAAC,8DAA8D;IACxE,eAAY;EAAmC,CAChD,CAAC,gBAGFT,KAAA,CAAAQ,aAAA;IACE;IACAO,GAAG,EAAED,CAAE;IACPL,SAAS,EAAC,gEAAgE;IAC1E,eAAY;EAAqC,CAClD,CAEN,CACG,CAAC;AAEX,CAAC,CAAC;AAEFP,qBAAqB,CAACc,YAAY,GAAG;EACnCV,YAAY,EAAE,IAAI;EAClBC,gBAAgB,EAAE;AACpB,CAAC;AAEDL,qBAAqB,CAACe,SAAS,GAAG;EAChC;EACAX,YAAY,EAAEL,SAAS,CAACiB,MAAM;EAC9B;EACAX,gBAAgB,EAAEN,SAAS,CAACiB;AAC9B,CAAC;AAED,eAAehB,qBAAqB","ignoreList":[]}
|