newspack-components 4.5.0 → 4.6.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.
Files changed (138) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/DEVELOPMENT.md +1 -1
  3. package/dist/cjs/accordion/index.js +43 -26
  4. package/dist/cjs/accordion/index.test.js +80 -0
  5. package/dist/cjs/accordion/style.scss +25 -21
  6. package/dist/cjs/action-card/style.scss +0 -22
  7. package/dist/cjs/breadcrumbs/index.js +80 -0
  8. package/dist/cjs/breadcrumbs/index.test.js +109 -0
  9. package/dist/cjs/breadcrumbs/style.scss +41 -0
  10. package/dist/cjs/card/core-card.js +8 -4
  11. package/dist/cjs/card/index.js +1 -1
  12. package/dist/cjs/card/style-core.scss +14 -0
  13. package/dist/cjs/card/style.scss +0 -9
  14. package/dist/cjs/card-feature/index.js +25 -13
  15. package/dist/cjs/card-settings-group/index.js +3 -0
  16. package/dist/cjs/confirm-dialog/index.js +34 -8
  17. package/dist/cjs/confirm-dialog/index.test.js +173 -0
  18. package/dist/cjs/divider/style.scss +4 -2
  19. package/dist/cjs/grid/style.scss +5 -0
  20. package/dist/cjs/handoff/index.js +8 -5
  21. package/dist/cjs/index.js +34 -1
  22. package/dist/cjs/integration-icon/index.js +40 -0
  23. package/dist/cjs/integration-icon/style.scss +42 -0
  24. package/dist/cjs/integration-icons/index.js +21 -2
  25. package/dist/cjs/modal/style.scss +4 -0
  26. package/dist/cjs/page/index.js +121 -0
  27. package/dist/cjs/page/index.test.js +69 -0
  28. package/dist/cjs/page/style.scss +86 -0
  29. package/dist/cjs/route-match.js +59 -0
  30. package/dist/cjs/route-match.test.js +77 -0
  31. package/dist/cjs/section-header/index.js +8 -1
  32. package/dist/cjs/section-header/style.scss +11 -11
  33. package/dist/cjs/select-control/GroupedSelectControl.js +1 -1
  34. package/dist/cjs/select-control/style.scss +4 -0
  35. package/dist/cjs/style.scss +1 -4
  36. package/dist/cjs/tabbed-navigation/index.js +188 -40
  37. package/dist/cjs/tabbed-navigation/index.test.js +299 -0
  38. package/dist/cjs/tabbed-navigation/style.scss +77 -57
  39. package/dist/cjs/with-wizard/index.js +34 -45
  40. package/dist/cjs/with-wizard-screen/index.js +82 -41
  41. package/dist/cjs/with-wizard-screen/index.test.js +50 -0
  42. package/dist/cjs/with-wizard-screen/style.scss +15 -118
  43. package/dist/cjs/wizard/breadcrumbs-select.js +70 -0
  44. package/dist/cjs/wizard/breadcrumbs-select.test.js +200 -0
  45. package/dist/cjs/wizard/components/WizardError.js +8 -4
  46. package/dist/cjs/wizard/index.js +162 -141
  47. package/dist/esm/accordion/index.js +42 -26
  48. package/dist/esm/accordion/index.test.js +79 -0
  49. package/dist/esm/accordion/style.scss +25 -21
  50. package/dist/esm/action-card/style.scss +0 -22
  51. package/dist/esm/breadcrumbs/index.js +75 -0
  52. package/dist/esm/breadcrumbs/index.test.js +105 -0
  53. package/dist/esm/breadcrumbs/style.scss +41 -0
  54. package/dist/esm/card/core-card.js +8 -4
  55. package/dist/esm/card/index.js +1 -1
  56. package/dist/esm/card/style-core.scss +14 -0
  57. package/dist/esm/card/style.scss +0 -9
  58. package/dist/esm/card-feature/index.js +25 -13
  59. package/dist/esm/card-settings-group/index.js +3 -0
  60. package/dist/esm/confirm-dialog/index.js +34 -8
  61. package/dist/esm/confirm-dialog/index.test.js +173 -0
  62. package/dist/esm/divider/style.scss +4 -2
  63. package/dist/esm/grid/style.scss +5 -0
  64. package/dist/esm/handoff/index.js +7 -5
  65. package/dist/esm/index.js +5 -1
  66. package/dist/esm/integration-icon/index.js +34 -0
  67. package/dist/esm/integration-icon/style.scss +42 -0
  68. package/dist/esm/integration-icons/index.js +22 -1
  69. package/dist/esm/modal/style.scss +4 -0
  70. package/dist/esm/page/index.js +113 -0
  71. package/dist/esm/page/index.test.js +65 -0
  72. package/dist/esm/page/style.scss +86 -0
  73. package/dist/esm/route-match.js +51 -0
  74. package/dist/esm/route-match.test.js +74 -0
  75. package/dist/esm/section-header/index.js +8 -1
  76. package/dist/esm/section-header/style.scss +11 -11
  77. package/dist/esm/select-control/GroupedSelectControl.js +1 -1
  78. package/dist/esm/select-control/style.scss +4 -0
  79. package/dist/esm/style.scss +1 -4
  80. package/dist/esm/tabbed-navigation/index.js +192 -38
  81. package/dist/esm/tabbed-navigation/index.test.js +296 -0
  82. package/dist/esm/tabbed-navigation/style.scss +77 -57
  83. package/dist/esm/with-wizard/index.js +33 -45
  84. package/dist/esm/with-wizard-screen/index.js +78 -41
  85. package/dist/esm/with-wizard-screen/index.test.js +46 -0
  86. package/dist/esm/with-wizard-screen/style.scss +15 -118
  87. package/dist/esm/wizard/breadcrumbs-select.js +63 -0
  88. package/dist/esm/wizard/breadcrumbs-select.test.js +197 -0
  89. package/dist/esm/wizard/components/WizardError.js +7 -5
  90. package/dist/esm/wizard/index.js +165 -144
  91. package/package.json +10 -2
  92. package/src/accordion/index.js +27 -15
  93. package/src/accordion/index.test.js +62 -0
  94. package/src/accordion/style.scss +25 -21
  95. package/src/action-card/style.scss +0 -22
  96. package/src/breadcrumbs/index.js +63 -0
  97. package/src/breadcrumbs/index.test.js +59 -0
  98. package/src/breadcrumbs/style.scss +41 -0
  99. package/src/card/core-card.js +11 -6
  100. package/src/card/index.js +0 -1
  101. package/src/card/style-core.scss +14 -0
  102. package/src/card/style.scss +0 -9
  103. package/src/card-feature/README.md +3 -3
  104. package/src/card-feature/index.tsx +37 -21
  105. package/src/card-settings-group/index.tsx +4 -0
  106. package/src/card-sortable-list/index.tsx +1 -1
  107. package/src/confirm-dialog/index.test.js +142 -0
  108. package/src/confirm-dialog/index.tsx +34 -7
  109. package/src/divider/style.scss +4 -2
  110. package/src/grid/style.scss +5 -0
  111. package/src/handoff/index.js +4 -3
  112. package/src/index.js +5 -1
  113. package/src/integration-icon/index.tsx +35 -0
  114. package/src/integration-icon/style.scss +42 -0
  115. package/src/integration-icons/index.js +23 -0
  116. package/src/modal/style.scss +4 -0
  117. package/src/page/index.js +89 -0
  118. package/src/page/index.test.js +43 -0
  119. package/src/page/style.scss +86 -0
  120. package/src/route-match.js +47 -0
  121. package/src/route-match.test.js +50 -0
  122. package/src/section-header/index.js +7 -3
  123. package/src/section-header/style.scss +11 -11
  124. package/src/select-control/GroupedSelectControl.js +1 -1
  125. package/src/select-control/style.scss +4 -0
  126. package/src/style.scss +1 -4
  127. package/src/tabbed-navigation/index.js +181 -34
  128. package/src/tabbed-navigation/index.test.js +228 -0
  129. package/src/tabbed-navigation/style.scss +77 -57
  130. package/src/with-wizard/index.js +18 -39
  131. package/src/with-wizard-screen/index.js +70 -35
  132. package/src/with-wizard-screen/index.test.js +36 -0
  133. package/src/with-wizard-screen/style.scss +15 -118
  134. package/src/wizard/breadcrumbs-select.js +51 -0
  135. package/src/wizard/breadcrumbs-select.test.js +124 -0
  136. package/src/wizard/components/WizardError.js +4 -3
  137. package/src/wizard/index.js +160 -146
  138. package/tsconfig.json +7 -0
@@ -8,14 +8,15 @@ import classnames from 'classnames';
8
8
  */
9
9
  import { DropdownMenu, MenuGroup, MenuItem } from '@wordpress/components';
10
10
  import { useDispatch, useSelect } from '@wordpress/data';
11
- import { useEffect, useState, forwardRef } from '@wordpress/element';
11
+ import { cloneElement, isValidElement, useEffect, useState, forwardRef } from '@wordpress/element';
12
12
  import { __ } from '@wordpress/i18n';
13
13
  import { category, chevronLeft, moreVertical } from '@wordpress/icons';
14
14
 
15
15
  /**
16
16
  * Internal dependencies
17
17
  */
18
- import { Footer, Notice, Button, NewspackIcon, TabbedNavigation, PluginInstaller, SectionHeader, HandoffMessage } from '../';
18
+ import { Footer, Notice, Button, TabbedNavigation, PluginInstaller, SectionHeader, HandoffMessage, Page } from '../';
19
+ import { activeBreadcrumbs, appendSectionName } from './breadcrumbs-select';
19
20
  import Router from '../proxied-imports/router';
20
21
  import registerStore, { WIZARD_STORE_NAMESPACE } from './store';
21
22
  import WizardSnackbar from './components/WizardSnackbar';
@@ -53,6 +54,37 @@ const ResetHeaderData = () => {
53
54
  return null;
54
55
  };
55
56
 
57
+ /**
58
+ * Wizard header + content region. Rendered inside the wizard's HashRouter so it
59
+ * can read the current route and derive the active-tab breadcrumb.
60
+ */
61
+ const WizardHeaderRegion = ( { hideHeader, headerText, sections, sectionName, subTitle, actions, tabbedNavigation, children } ) => {
62
+ const { pathname } = useLocation();
63
+
64
+ if ( hideHeader ) {
65
+ // Without the Page shell the tabs still own the content: it renders
66
+ // inside the active tab's panel.
67
+ if ( isValidElement( tabbedNavigation ) ) {
68
+ return cloneElement( tabbedNavigation, { content: children } );
69
+ }
70
+ return children;
71
+ }
72
+
73
+ let breadcrumbItems = activeBreadcrumbs( sections, pathname );
74
+ if ( ! breadcrumbItems.length && headerText ) {
75
+ breadcrumbItems = [ { label: headerText } ];
76
+ }
77
+ // Append any render-time leaf crumb(s) the section supplied via
78
+ // headerData.sectionName (deduped against the current trailing label).
79
+ breadcrumbItems = appendSectionName( breadcrumbItems, sectionName );
80
+
81
+ return (
82
+ <Page breadcrumbItems={ breadcrumbItems } subTitle={ subTitle } actions={ actions } tabbedNavigation={ tabbedNavigation }>
83
+ { children }
84
+ </Page>
85
+ );
86
+ };
87
+
56
88
  /**
57
89
  * @typedef {Object} WizardProps
58
90
  * @property {string} headerText The header text.
@@ -86,7 +118,7 @@ const Wizard = (
86
118
  renderAboveSections,
87
119
  requiredPlugins = [],
88
120
  isInitialFetchTriggered = true,
89
- fixedHeader = false,
121
+ hideHeader = false,
90
122
  },
91
123
  ref
92
124
  ) => {
@@ -123,159 +155,141 @@ const Wizard = (
123
155
  // the PluginInstaller. Use it for routing so the installer actually mounts and runs.
124
156
  const routedSections = pluginRequirementsSatisfied ? sections : displayedSections;
125
157
 
126
- const urlWithoutHash = window.location.href.split( '#' )[ 0 ];
158
+ const tabbedNavigation = displayedSections.length > 1 && (
159
+ <TabbedNavigation items={ displayedSections }>
160
+ <WizardError />
161
+ </TabbedNavigation>
162
+ );
163
+
164
+ const content = (
165
+ <>
166
+ <HandoffMessage />
167
+
168
+ { sections.length > 1 && <ResetHeaderData /> }
169
+
170
+ <div className="newspack-wizard__main">
171
+ <Switch>
172
+ { routedSections.map( ( section, index ) => {
173
+ const SectionComponent = section.render;
174
+ const sectionProps = section.props || {};
175
+ return (
176
+ <Route
177
+ key={ index }
178
+ exact={ section.exact ?? false }
179
+ path={ section.path }
180
+ render={ routerProps => (
181
+ <div
182
+ className={ classnames( 'newspack-wizard__content', className, {
183
+ 'newspack-wizard__content--full-width': section.fullWidth,
184
+ } ) }
185
+ >
186
+ { 'function' === typeof renderAboveSections ? renderAboveSections() : null }
187
+ { ( sectionTitle || section.title ) && (
188
+ <SectionHeader
189
+ className="newspack-wizard__section-header"
190
+ backNav={ backNav || section.backNav }
191
+ title={ sectionTitle || section.title }
192
+ description={ sectionDescription || section.description }
193
+ badges={ badges || section.badges }
194
+ menu={ sectionMenu || section.menu }
195
+ primaryAction={ sectionPrimaryAction || section.primaryAction }
196
+ secondaryAction={ sectionSecondaryAction || section.secondaryAction }
197
+ heading={ 2 }
198
+ noMargin
199
+ />
200
+ ) }
201
+ <SectionComponent { ...routerProps } { ...sectionProps } { ...sharedProps } />
202
+ </div>
203
+ ) }
204
+ />
205
+ );
206
+ } ) }
207
+ <Redirect to={ displayedSections[ 0 ].path } />
208
+ </Switch>
209
+ </div>
210
+ </>
211
+ );
212
+
213
+ const headerActions =
214
+ actions?.length > 0 ? (
215
+ <>
216
+ { mainActions.map( ( action, index ) => (
217
+ <Button
218
+ key={ index }
219
+ className="newspack-wizard__actions__main"
220
+ href={ action.href }
221
+ icon={ resolveIcon( action.icon ) }
222
+ variant={ action.type }
223
+ onClick={ action.action }
224
+ disabled={ action.disabled || false }
225
+ isDestructive={ action.destructive || false }
226
+ >
227
+ { action.label }
228
+ </Button>
229
+ ) ) }
230
+ <DropdownMenu
231
+ className={ moreActions?.length === 0 ? 'newspack-wizard__actions__more--primary-only' : '' }
232
+ icon={ moreVertical }
233
+ label={ __( 'More', 'newspack-plugin' ) }
234
+ popoverProps={ { className: 'newspack-wizard__actions__more' } }
235
+ >
236
+ { () =>
237
+ // Split actions into groups whenever an action opts in via `separator: true`.
238
+ // Consecutive MenuGroups render the WordPress-standard divider between them.
239
+ actions
240
+ .reduce( ( groups, action ) => {
241
+ if ( action.separator || groups.length === 0 ) {
242
+ groups.push( [] );
243
+ }
244
+ groups[ groups.length - 1 ].push( action );
245
+ return groups;
246
+ }, [] )
247
+ .map( ( group, groupIndex ) => (
248
+ <MenuGroup key={ groupIndex }>
249
+ { group.map( ( action, index ) => (
250
+ <MenuItem
251
+ key={ index }
252
+ className={
253
+ action.type === 'primary' || action.type === 'secondary'
254
+ ? 'newspack-wizard__actions__more__main'
255
+ : 'newspack-wizard__actions__more__more'
256
+ }
257
+ icon={ action.icon }
258
+ href={ action.href }
259
+ onClick={ action.action }
260
+ disabled={ action.disabled || false }
261
+ isDestructive={ action.destructive || false }
262
+ >
263
+ { action.label }
264
+ </MenuItem>
265
+ ) ) }
266
+ </MenuGroup>
267
+ ) )
268
+ }
269
+ </DropdownMenu>
270
+ </>
271
+ ) : undefined;
127
272
 
128
273
  return (
129
274
  <div ref={ ref }>
130
275
  <div
131
276
  className={ classnames( isLoading ? 'newspack-wizard__is-loading' : 'newspack-wizard__is-loaded', {
132
277
  'newspack-wizard__is-loading-quiet': isQuietLoading,
133
- 'newspack-wizard__fixed-header': fixedHeader,
134
278
  } ) }
135
279
  >
136
280
  <HashRouter hashType="slash">
137
281
  { newspack_aux_data.is_debug_mode && <Notice debugMode /> }
138
- <div className="newspack-wizard__header">
139
- <div className="newspack-wizard__header__inner">
140
- <div className="newspack-wizard__title">
141
- { newspack_urls.dashboard !== urlWithoutHash ? (
142
- <Button
143
- isLink
144
- href={ newspack_urls.dashboard }
145
- label={ __( 'Return to Dashboard', 'newspack-plugin' ) }
146
- showTooltip={ true }
147
- icon={ category }
148
- iconSize={ 36 }
149
- >
150
- <NewspackIcon size={ 36 } />
151
- </Button>
152
- ) : (
153
- <NewspackIcon size={ 36 } />
154
- ) }
155
- <div>
156
- { headerText && (
157
- <h2 className="newspack-wizard__header__title">
158
- { headerText }
159
- { sectionName && (
160
- <span className="newspack-wizard__header__section">
161
- <span className="newspack-wizard__header__section__separator"> / </span> { sectionName }
162
- </span>
163
- ) }
164
- </h2>
165
- ) }
166
- { subHeaderText && <span>{ subHeaderText }</span> }
167
- </div>
168
- </div>
169
- </div>
170
- { actions?.length > 0 && (
171
- <div className="newspack-wizard__header__actions">
172
- { mainActions.map( ( action, index ) => (
173
- <Button
174
- key={ index }
175
- className="newspack-wizard__header__actions__main"
176
- href={ action.href }
177
- icon={ resolveIcon( action.icon ) }
178
- variant={ action.type }
179
- onClick={ action.action }
180
- disabled={ action.disabled || false }
181
- isDestructive={ action.destructive || false }
182
- >
183
- { action.label }
184
- </Button>
185
- ) ) }
186
- <DropdownMenu
187
- className={ moreActions?.length === 0 ? 'newspack-wizard__header__actions__more--primary-only' : '' }
188
- icon={ moreVertical }
189
- label={ __( 'More', 'newspack-plugin' ) }
190
- popoverProps={ { className: 'newspack-wizard__header__actions__more' } }
191
- >
192
- { () =>
193
- // Split actions into groups whenever an action opts in via `separator: true`.
194
- // Consecutive MenuGroups render the WordPress-standard divider between them.
195
- actions
196
- .reduce( ( groups, action ) => {
197
- if ( action.separator || groups.length === 0 ) {
198
- groups.push( [] );
199
- }
200
- groups[ groups.length - 1 ].push( action );
201
- return groups;
202
- }, [] )
203
- .map( ( group, groupIndex ) => (
204
- <MenuGroup key={ groupIndex }>
205
- { group.map( ( action, index ) => (
206
- <MenuItem
207
- key={ index }
208
- className={
209
- action.type === 'primary' || action.type === 'secondary'
210
- ? 'newspack-wizard__header__actions__more__main'
211
- : 'newspack-wizard__header__actions__more__more'
212
- }
213
- icon={ action.icon }
214
- href={ action.href }
215
- onClick={ action.action }
216
- disabled={ action.disabled || false }
217
- isDestructive={ action.destructive || false }
218
- >
219
- { action.label }
220
- </MenuItem>
221
- ) ) }
222
- </MenuGroup>
223
- ) )
224
- }
225
- </DropdownMenu>
226
- </div>
227
- ) }
228
- </div>
229
-
230
- { displayedSections.length > 1 && (
231
- <TabbedNavigation items={ displayedSections }>
232
- <WizardError />
233
- </TabbedNavigation>
234
- ) }
235
- <HandoffMessage />
236
-
237
- { sections.length > 1 && <ResetHeaderData /> }
238
-
239
- <div className="newspack-wizard__main">
240
- <Switch>
241
- { routedSections.map( ( section, index ) => {
242
- const SectionComponent = section.render;
243
- const sectionProps = section.props || {};
244
- return (
245
- <Route
246
- key={ index }
247
- exact={ section.exact ?? false }
248
- path={ section.path }
249
- render={ routerProps => (
250
- <div
251
- className={ classnames( 'newspack-wizard__content', className, {
252
- 'newspack-wizard__content--full-width': section.fullWidth,
253
- } ) }
254
- >
255
- { 'function' === typeof renderAboveSections ? renderAboveSections() : null }
256
- { ( sectionTitle || section.title ) && (
257
- <SectionHeader
258
- className="newspack-wizard__section-header"
259
- backNav={ backNav || section.backNav }
260
- title={ sectionTitle || section.title }
261
- description={ sectionDescription || section.description }
262
- badges={ badges || section.badges }
263
- menu={ sectionMenu || section.menu }
264
- primaryAction={ sectionPrimaryAction || section.primaryAction }
265
- secondaryAction={ sectionSecondaryAction || section.secondaryAction }
266
- heading={ 1 }
267
- noMargin
268
- />
269
- ) }
270
- <SectionComponent { ...routerProps } { ...sectionProps } { ...sharedProps } />
271
- </div>
272
- ) }
273
- />
274
- );
275
- } ) }
276
- <Redirect to={ displayedSections[ 0 ].path } />
277
- </Switch>
278
- </div>
282
+ <WizardHeaderRegion
283
+ hideHeader={ hideHeader }
284
+ headerText={ headerText }
285
+ sections={ routedSections }
286
+ sectionName={ sectionName }
287
+ subTitle={ subHeaderText }
288
+ actions={ headerActions }
289
+ tabbedNavigation={ tabbedNavigation }
290
+ >
291
+ { content }
292
+ </WizardHeaderRegion>
279
293
  </HashRouter>
280
294
  { notices?.length > 0 && (
281
295
  <div className="newspack-wizard__snackbar-list">
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "newspack-scripts/config/tsconfig.json",
3
+ "include": [
4
+ "src",
5
+ "node_modules/newspack-scripts/types"
6
+ ]
7
+ }