@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.
Files changed (32) hide show
  1. package/dist/Alert/index.scss +5 -0
  2. package/dist/ProductTour/Checkpoint.js +23 -16
  3. package/dist/ProductTour/Checkpoint.js.map +1 -1
  4. package/dist/ProductTour/Checkpoint.scss +8 -36
  5. package/dist/ProductTour/CheckpointActionRow.js +17 -21
  6. package/dist/ProductTour/CheckpointActionRow.js.map +1 -1
  7. package/dist/ProductTour/CheckpointHeader.js +57 -0
  8. package/dist/ProductTour/CheckpointHeader.js.map +1 -0
  9. package/dist/ProductTour/index.js +120 -20
  10. package/dist/ProductTour/index.js.map +1 -1
  11. package/dist/ProductTour/messages.js +10 -0
  12. package/dist/paragon.css +1 -1
  13. package/dist/withDeprecatedProps.js +11 -3
  14. package/dist/withDeprecatedProps.js.map +1 -1
  15. package/package.json +1 -1
  16. package/src/Alert/index.scss +5 -0
  17. package/src/ProductTour/Checkpoint.jsx +22 -16
  18. package/src/ProductTour/Checkpoint.scss +8 -36
  19. package/src/ProductTour/Checkpoint.test.jsx +20 -53
  20. package/src/ProductTour/CheckpointActionRow.jsx +32 -32
  21. package/src/ProductTour/CheckpointHeader.jsx +60 -0
  22. package/src/ProductTour/ProductTour.test.jsx +69 -60
  23. package/src/ProductTour/README.md +11 -3
  24. package/src/ProductTour/index.jsx +125 -17
  25. package/src/ProductTour/messages.js +10 -0
  26. package/src/withDeprecatedProps.tsx +10 -3
  27. package/dist/ProductTour/CheckpointBreadcrumbs.js +0 -37
  28. package/dist/ProductTour/CheckpointBreadcrumbs.js.map +0 -1
  29. package/dist/TransitionReplace/DemoTransitionReplace.js +0 -32
  30. package/dist/TransitionReplace/DemoTransitionReplace.js.map +0 -1
  31. package/src/ProductTour/CheckpointBreadcrumbs.jsx +0 -45
  32. 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
- this.warn(`${componentName}: The prop '${propName}' has been moved to '${newName}' and expects a new format. ${message}`);
55
- acc[newName] = transform(this.props[propName], this.props);
56
- break;
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:\n this.warn(`${componentName}: The prop '${propName}' has been moved to '${newName}' and expects a new format. ${message}`);\n acc[newName!] = transform!(this.props[propName], this.props);\n break;\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":[]}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openedx/paragon",
3
- "version": "22.17.0",
3
+ "version": "22.18.1",
4
4
  "description": "Accessible, responsive UI component library based on Bootstrap.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -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 CheckpointBreadcrumbs from './CheckpointBreadcrumbs';
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
- {(title || !isOnlyCheckpoint) && (
108
- <div className="pgn__checkpoint-header">
109
- <CheckpointTitle>{title}</CheckpointTitle>
110
- <CheckpointBreadcrumbs currentIndex={index} totalCheckpoints={totalCheckpoints} />
111
- </div>
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
- dismissButtonText: null,
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 displayed on the button used to dismiss the tour for the given Checkpoint. */
146
- dismissButtonText: PropTypes.node,
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, 4);
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\.5);
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: 0;
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 correct active breadcrumb', () => {
48
- expect(screen.getByText('Checkpoint title')).toBeInTheDocument();
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('dismiss button onClick calls handleDismiss', async () => {
64
- const dismissButton = screen.getByRole('button', { name: 'Dismiss' });
65
- await userEvent.click(dismissButton);
66
- expect(handleDismiss).toHaveBeenCalledTimes(1);
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 userEvent.click(advanceButton);
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
- dismissButtonText="Dismiss"
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 dismiss buttons)', () => {
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
- dismissButtonText,
9
+ backButtonText,
8
10
  endButtonText,
9
11
  isLastCheckpoint,
10
12
  onAdvance,
11
- onDismiss,
13
+ onBack,
12
14
  onEnd,
13
- showDismissButton,
14
15
  index,
15
- }, ref) => (
16
- <div className="pgn__checkpoint-action-row" ref={ref}>
17
- {(showDismissButton === undefined ? !isLastCheckpoint : showDismissButton) && (
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
- variant="tertiary"
20
- className="pgn__checkpoint-button_dismiss"
21
- onClick={onDismiss}
30
+ autoFocus
31
+ className="pgn__checkpoint-button_advance"
32
+ onClick={isLastCheckpoint ? () => onEnd(index) : () => onAdvance(index)}
22
33
  >
23
- {dismissButtonText}
34
+ {isLastCheckpoint ? endButtonText : advanceButtonText}
24
35
  </Button>
25
- )}
26
- <Button
27
- autoFocus
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
- dismissButtonText: '',
42
+ backButtonText: '',
40
43
  endButtonText: '',
41
44
  isLastCheckpoint: false,
42
- onAdvance: () => {},
43
- onDismiss: () => {},
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 dismiss the tour. */
53
- dismissButtonText: PropTypes.node,
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 dismiss button. */
61
- onDismiss: PropTypes.func,
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;