newspack-components 4.1.0 → 4.3.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 (135) hide show
  1. package/colors/colors.module.scss +1 -1
  2. package/dist/cjs/action-card/index.js +21 -12
  3. package/dist/cjs/action-card/style.scss +32 -14
  4. package/dist/cjs/autocomplete-tokenfield/index.js +2 -2
  5. package/dist/cjs/autocomplete-with-latest-posts/index.js +289 -0
  6. package/dist/cjs/autocomplete-with-latest-posts/style.scss +150 -0
  7. package/dist/cjs/badge/index.js +32 -0
  8. package/dist/cjs/badge/style.scss +36 -0
  9. package/dist/cjs/button/index.js +2 -0
  10. package/dist/cjs/card/core-card.js +141 -0
  11. package/dist/cjs/card/index.js +33 -0
  12. package/dist/cjs/card/style-core.scss +227 -0
  13. package/dist/cjs/card/style.scss +2 -2
  14. package/dist/cjs/card-settings-group/index.js +49 -0
  15. package/dist/cjs/card-settings-group/style.scss +33 -0
  16. package/dist/cjs/card-sortable-list/index.js +390 -0
  17. package/dist/cjs/card-sortable-list/style.scss +48 -0
  18. package/dist/cjs/confirm-dialog/index.js +59 -0
  19. package/dist/cjs/divider/index.js +42 -0
  20. package/dist/cjs/divider/style.scss +51 -0
  21. package/dist/cjs/form-token-field/style.scss +0 -5
  22. package/dist/cjs/grid/index.js +2 -1
  23. package/dist/cjs/grid/style.scss +63 -9
  24. package/dist/cjs/index.js +42 -0
  25. package/dist/cjs/modal/index.js +11 -4
  26. package/dist/cjs/modal/style.scss +43 -13
  27. package/dist/cjs/newspack-icon/style.scss +5 -3
  28. package/dist/cjs/popover/style.scss +1 -1
  29. package/dist/cjs/section-header/index.js +76 -17
  30. package/dist/cjs/section-header/style.scss +109 -4
  31. package/dist/cjs/style.scss +2 -0
  32. package/dist/cjs/waiting/index.js +3 -1
  33. package/dist/cjs/waiting/style.scss +4 -0
  34. package/dist/cjs/web-preview/index.js +98 -39
  35. package/dist/cjs/web-preview/style.scss +20 -53
  36. package/dist/cjs/with-wizard/index.js +1 -1
  37. package/dist/cjs/with-wizard/style.scss +24 -6
  38. package/dist/cjs/with-wizard-screen/style.scss +39 -10
  39. package/dist/cjs/wizard/components/WizardSnackbar.js +70 -0
  40. package/dist/cjs/wizard/components/style.scss +48 -0
  41. package/dist/cjs/wizard/index.js +97 -16
  42. package/dist/cjs/wizard/store/index.js +46 -0
  43. package/dist/esm/action-card/index.js +21 -12
  44. package/dist/esm/action-card/style.scss +32 -14
  45. package/dist/esm/autocomplete-tokenfield/index.js +2 -2
  46. package/dist/esm/autocomplete-with-latest-posts/index.js +281 -0
  47. package/dist/esm/autocomplete-with-latest-posts/style.scss +150 -0
  48. package/dist/esm/badge/index.js +24 -0
  49. package/dist/esm/badge/style.scss +36 -0
  50. package/dist/esm/button/index.js +2 -0
  51. package/dist/esm/card/core-card.js +133 -0
  52. package/dist/esm/card/index.js +33 -0
  53. package/dist/esm/card/style-core.scss +227 -0
  54. package/dist/esm/card/style.scss +2 -2
  55. package/dist/esm/card-settings-group/index.js +42 -0
  56. package/dist/esm/card-settings-group/style.scss +33 -0
  57. package/dist/esm/card-sortable-list/index.js +381 -0
  58. package/dist/esm/card-sortable-list/style.scss +48 -0
  59. package/dist/esm/confirm-dialog/index.js +49 -0
  60. package/dist/esm/divider/index.js +34 -0
  61. package/dist/esm/divider/style.scss +51 -0
  62. package/dist/esm/form-token-field/style.scss +0 -5
  63. package/dist/esm/grid/index.js +2 -1
  64. package/dist/esm/grid/style.scss +63 -9
  65. package/dist/esm/index.js +6 -0
  66. package/dist/esm/modal/index.js +11 -4
  67. package/dist/esm/modal/style.scss +43 -13
  68. package/dist/esm/newspack-icon/style.scss +5 -3
  69. package/dist/esm/popover/style.scss +1 -1
  70. package/dist/esm/section-header/index.js +75 -18
  71. package/dist/esm/section-header/style.scss +109 -4
  72. package/dist/esm/style.scss +2 -0
  73. package/dist/esm/waiting/index.js +3 -1
  74. package/dist/esm/waiting/style.scss +4 -0
  75. package/dist/esm/web-preview/index.js +101 -42
  76. package/dist/esm/web-preview/style.scss +20 -53
  77. package/dist/esm/with-wizard/index.js +1 -1
  78. package/dist/esm/with-wizard/style.scss +24 -6
  79. package/dist/esm/with-wizard-screen/style.scss +39 -10
  80. package/dist/esm/wizard/components/WizardSnackbar.js +64 -0
  81. package/dist/esm/wizard/components/style.scss +48 -0
  82. package/dist/esm/wizard/index.js +101 -20
  83. package/dist/esm/wizard/store/index.js +46 -0
  84. package/package.json +5 -6
  85. package/shared/author-context.js +26 -0
  86. package/shared/hooks/use-coauthors.js +220 -0
  87. package/shared/hooks/use-coauthors.test.js +624 -0
  88. package/shared/hooks/use-custom-byline.js +47 -0
  89. package/shared/hooks/use-custom-byline.test.js +133 -0
  90. package/shared/scss/_mixins.scss +46 -0
  91. package/src/action-card/action-card.d.ts +7 -3
  92. package/src/action-card/index.js +24 -11
  93. package/src/action-card/style.scss +32 -14
  94. package/src/autocomplete-tokenfield/index.js +2 -2
  95. package/src/autocomplete-with-latest-posts/index.js +290 -0
  96. package/src/autocomplete-with-latest-posts/style.scss +150 -0
  97. package/src/badge/index.tsx +24 -0
  98. package/src/badge/style.scss +36 -0
  99. package/src/button/index.tsx +3 -2
  100. package/src/card/core-card.js +147 -0
  101. package/src/card/index.js +39 -1
  102. package/src/card/style-core.scss +227 -0
  103. package/src/card/style.scss +2 -2
  104. package/src/card-settings-group/README.md +43 -0
  105. package/src/card-settings-group/index.tsx +53 -0
  106. package/src/card-settings-group/style.scss +33 -0
  107. package/src/card-sortable-list/README.md +51 -0
  108. package/src/card-sortable-list/index.tsx +412 -0
  109. package/src/card-sortable-list/style.scss +48 -0
  110. package/src/confirm-dialog/index.tsx +64 -0
  111. package/src/divider/README.md +25 -0
  112. package/src/divider/index.js +31 -0
  113. package/src/divider/style.scss +51 -0
  114. package/src/form-token-field/style.scss +0 -5
  115. package/src/grid/index.tsx +2 -1
  116. package/src/grid/style.scss +63 -9
  117. package/src/index.js +6 -0
  118. package/src/modal/index.js +12 -3
  119. package/src/modal/style.scss +43 -13
  120. package/src/newspack-icon/style.scss +5 -3
  121. package/src/popover/style.scss +1 -1
  122. package/src/section-header/index.js +72 -13
  123. package/src/section-header/style.scss +109 -4
  124. package/src/style.scss +2 -0
  125. package/src/waiting/index.js +2 -1
  126. package/src/waiting/style.scss +4 -0
  127. package/src/web-preview/index.js +90 -35
  128. package/src/web-preview/style.scss +20 -53
  129. package/src/with-wizard/index.js +1 -1
  130. package/src/with-wizard/style.scss +24 -6
  131. package/src/with-wizard-screen/style.scss +39 -10
  132. package/src/wizard/components/WizardSnackbar.js +58 -0
  133. package/src/wizard/components/style.scss +48 -0
  134. package/src/wizard/index.js +108 -14
  135. package/src/wizard/store/index.js +26 -0
@@ -6,22 +6,38 @@ import classnames from 'classnames';
6
6
  /**
7
7
  * WordPress dependencies.
8
8
  */
9
- import { useSelect } from '@wordpress/data';
10
- import { useState, forwardRef } from '@wordpress/element';
9
+ import { DropdownMenu, MenuItem } from '@wordpress/components';
10
+ import { useDispatch, useSelect } from '@wordpress/data';
11
+ import { useEffect, useState, forwardRef } from '@wordpress/element';
11
12
  import { __ } from '@wordpress/i18n';
12
- import { category } from '@wordpress/icons';
13
+ import { category, moreVertical } from '@wordpress/icons';
13
14
 
14
15
  /**
15
16
  * Internal dependencies
16
17
  */
17
- import { Footer, Notice, Button, NewspackIcon, TabbedNavigation, PluginInstaller, HandoffMessage } from '../';
18
+ import { Footer, Notice, Button, NewspackIcon, TabbedNavigation, PluginInstaller, SectionHeader, HandoffMessage } from '../';
18
19
  import Router from '../proxied-imports/router';
19
20
  import registerStore, { WIZARD_STORE_NAMESPACE } from './store';
21
+ import WizardSnackbar from './components/WizardSnackbar';
20
22
  import WizardError from './components/WizardError';
21
23
 
22
24
  registerStore();
23
25
 
24
- const { HashRouter, Redirect, Route, Switch } = Router;
26
+ const { HashRouter, Redirect, Route, Switch, useLocation } = Router;
27
+
28
+ /**
29
+ * Reset the header data when a new section is rendered.
30
+ */
31
+ const ResetHeaderData = () => {
32
+ const location = useLocation();
33
+ const { resetHeaderData } = useDispatch( WIZARD_STORE_NAMESPACE );
34
+
35
+ useEffect( () => {
36
+ resetHeaderData();
37
+ }, [ location.pathname, resetHeaderData ] );
38
+
39
+ return null;
40
+ };
25
41
 
26
42
  /**
27
43
  * @typedef {Object} WizardProps
@@ -49,17 +65,25 @@ const Wizard = (
49
65
  sections = [],
50
66
  headerText,
51
67
  apiSlug,
68
+ sharedProps = {},
52
69
  subHeaderText,
53
70
  hasSimpleFooter,
54
71
  className,
55
72
  renderAboveSections,
56
73
  requiredPlugins = [],
57
74
  isInitialFetchTriggered = true,
75
+ fixedHeader = false,
58
76
  },
59
77
  ref
60
78
  ) => {
61
79
  const isLoading = useSelect( select => select( WIZARD_STORE_NAMESPACE ).isLoading() );
62
80
  const isQuietLoading = useSelect( select => select( WIZARD_STORE_NAMESPACE ).isQuietLoading() );
81
+ const headerData = useSelect( select => select( WIZARD_STORE_NAMESPACE ).getHeaderData() );
82
+ const notices = useSelect( select => select( WIZARD_STORE_NAMESPACE ).getNotices() );
83
+ const { actions, backNav, badges, sectionDescription, sectionName, sectionTitle, sectionPrimaryAction, sectionSecondaryAction } = headerData;
84
+
85
+ const mainActions = actions?.filter( action => action.type === 'primary' || action.type === 'secondary' );
86
+ const moreActions = actions?.filter( action => action.type === 'more' );
63
87
 
64
88
  // Trigger initial data fetch. Some sections might not use the wizard data,
65
89
  // but for consistency, fetching is triggered regardless of the section.
@@ -87,11 +111,12 @@ const Wizard = (
87
111
  <div
88
112
  className={ classnames( isLoading ? 'newspack-wizard__is-loading' : 'newspack-wizard__is-loaded', {
89
113
  'newspack-wizard__is-loading-quiet': isQuietLoading,
114
+ 'newspack-wizard__fixed-header': fixedHeader,
90
115
  } ) }
91
116
  >
92
117
  <HashRouter hashType="slash">
93
118
  { newspack_aux_data.is_debug_mode && <Notice debugMode /> }
94
- <div className="bg-white">
119
+ <div className="newspack-wizard__header">
95
120
  <div className="newspack-wizard__header__inner">
96
121
  <div className="newspack-wizard__title">
97
122
  { newspack_urls.dashboard !== urlWithoutHash ? (
@@ -109,11 +134,53 @@ const Wizard = (
109
134
  <NewspackIcon size={ 36 } />
110
135
  ) }
111
136
  <div>
112
- { headerText && <h2>{ headerText }</h2> }
137
+ { headerText && (
138
+ <h2 className="newspack-wizard__header__title">
139
+ { headerText }
140
+ { sectionName && (
141
+ <span className="newspack-wizard__header__section">
142
+ <span className="newspack-wizard__header__section__separator"> / </span> { sectionName }
143
+ </span>
144
+ ) }
145
+ </h2>
146
+ ) }
113
147
  { subHeaderText && <span>{ subHeaderText }</span> }
114
148
  </div>
115
149
  </div>
116
150
  </div>
151
+ { actions?.length > 0 && (
152
+ <div className="newspack-wizard__header__actions">
153
+ { mainActions.map( ( action, index ) => (
154
+ <Button
155
+ key={ index }
156
+ icon={ action.icon }
157
+ variant={ action.type }
158
+ onClick={ action.action }
159
+ disabled={ action.disabled || false }
160
+ isDestructive={ action.destructive || false }
161
+ >
162
+ { action.label }
163
+ </Button>
164
+ ) ) }
165
+ { moreActions.length > 0 && (
166
+ <DropdownMenu icon={ moreVertical } label={ __( 'More', 'newspack-plugin' ) }>
167
+ { () =>
168
+ moreActions.map( ( action, index ) => (
169
+ <MenuItem
170
+ key={ index }
171
+ icon={ action.icon }
172
+ onClick={ action.action }
173
+ disabled={ action.disabled || false }
174
+ isDestructive={ action.destructive || false }
175
+ >
176
+ { action.label }
177
+ </MenuItem>
178
+ ) )
179
+ }
180
+ </DropdownMenu>
181
+ ) }
182
+ </div>
183
+ ) }
117
184
  </div>
118
185
 
119
186
  { displayedSections.length > 1 && (
@@ -123,21 +190,48 @@ const Wizard = (
123
190
  ) }
124
191
  <HandoffMessage />
125
192
 
193
+ { sections.length > 1 && <ResetHeaderData /> }
194
+
126
195
  <Switch>
127
- { displayedSections.map( ( section, index ) => {
196
+ { sections.map( ( section, index ) => {
128
197
  const SectionComponent = section.render;
198
+ const sectionProps = section.props || {};
129
199
  return (
130
- <Route key={ index } exact={ section.exact ?? false } path={ section.path }>
131
- <div className={ classnames( 'newspack-wizard__content', className ) }>
132
- { 'function' === typeof renderAboveSections ? renderAboveSections() : null }
133
- <SectionComponent />
134
- </div>
135
- </Route>
200
+ <Route
201
+ key={ index }
202
+ exact={ section.exact ?? false }
203
+ path={ section.path }
204
+ render={ routerProps => (
205
+ <div className={ classnames( 'newspack-wizard__content', className ) }>
206
+ { 'function' === typeof renderAboveSections ? renderAboveSections() : null }
207
+ { ( sectionTitle || section.title ) && (
208
+ <SectionHeader
209
+ className="newspack-wizard__section-header"
210
+ backNav={ backNav || section.backNav }
211
+ title={ sectionTitle || section.title }
212
+ description={ sectionDescription || section.description }
213
+ badges={ badges || section.badges }
214
+ primaryAction={ sectionPrimaryAction || section.primaryAction }
215
+ secondaryAction={ sectionSecondaryAction || section.secondaryAction }
216
+ heading={ 1 }
217
+ noMargin
218
+ />
219
+ ) }
220
+ <SectionComponent { ...routerProps } { ...sectionProps } { ...sharedProps } />
221
+ </div>
222
+ ) }
223
+ />
136
224
  );
137
225
  } ) }
138
226
  <Redirect to={ displayedSections[ 0 ].path } />
139
227
  </Switch>
140
228
  </HashRouter>
229
+ { notices?.length > 0 &&
230
+ notices.map( ( notice, index ) => (
231
+ <WizardSnackbar key={ notice.id || index } type={ notice.type } id={ notice.id } actions={ notice.actions }>
232
+ { notice.message }
233
+ </WizardSnackbar>
234
+ ) ) }
141
235
  </div>
142
236
  { ! isLoading && <Footer simple={ hasSimpleFooter } /> }
143
237
  </div>
@@ -16,9 +16,18 @@ import { createAction } from './utils.js';
16
16
  export const WIZARD_STORE_NAMESPACE = 'newspack/wizards';
17
17
 
18
18
  const DEFAULT_STATE = {
19
+ headerData: {
20
+ actions: [],
21
+ backNav: '',
22
+ badges: [],
23
+ sectionDescription: '',
24
+ sectionName: '',
25
+ sectionTitle: '',
26
+ },
19
27
  isLoading: false,
20
28
  isQuietLoading: false,
21
29
  apiData: {},
30
+ notices: [],
22
31
  error: null,
23
32
  };
24
33
 
@@ -31,6 +40,10 @@ const clone = objectToClone => JSON.parse( JSON.stringify( objectToClone ) );
31
40
 
32
41
  const reducer = ( state = DEFAULT_STATE, { type, payload = {} } ) => {
33
42
  switch ( type ) {
43
+ case 'SET_HEADER_DATA':
44
+ return { ...state, headerData: { ...state.headerData, ...payload } };
45
+ case 'RESET_HEADER_DATA':
46
+ return { ...state, headerData: { ...DEFAULT_STATE.headerData } };
34
47
  case 'START_LOADING_DATA':
35
48
  if ( payload.isQuietLoading ) {
36
49
  return { ...state, isQuietLoading: true };
@@ -42,8 +55,14 @@ const reducer = ( state = DEFAULT_STATE, { type, payload = {} } ) => {
42
55
  return set( clone( state ), [ 'apiData', payload.slug ], payload.data );
43
56
  case 'UPDATE_WIZARD_SETTINGS':
44
57
  return set( clone( state ), [ 'apiData', payload.slug, ...payload.path ], payload.value );
58
+ case 'ADD_NOTICE':
59
+ return { ...state, notices: [ ...state.notices, payload ] };
60
+ case 'REMOVE_NOTICE':
61
+ return { ...state, notices: state.notices.filter( notice => notice.id !== payload ) };
45
62
  case 'SET_ERROR':
46
63
  return { ...state, error: payload };
64
+ case 'RESET_NOTICES':
65
+ return { ...state, notices: DEFAULT_STATE.notices };
47
66
  default:
48
67
  return state;
49
68
  }
@@ -51,11 +70,16 @@ const reducer = ( state = DEFAULT_STATE, { type, payload = {} } ) => {
51
70
 
52
71
  const actions = {
53
72
  // Regular actions.
73
+ setHeaderData: createAction( 'SET_HEADER_DATA' ),
74
+ resetHeaderData: createAction( 'RESET_HEADER_DATA' ),
54
75
  startLoadingData: createAction( 'START_LOADING_DATA' ),
55
76
  finishLoadingData: createAction( 'FINISH_LOADING_DATA' ),
56
77
  fetchFromAPI: createAction( 'FETCH_FROM_API' ),
57
78
  setAPIDataForWizard: createAction( 'SET_API_DATA' ),
58
79
  updateWizardSettings: createAction( 'UPDATE_WIZARD_SETTINGS' ),
80
+ addNotice: createAction( 'ADD_NOTICE' ),
81
+ removeNotice: createAction( 'REMOVE_NOTICE' ),
82
+ resetNotices: createAction( 'RESET_NOTICES' ),
59
83
  setError: createAction( 'SET_ERROR' ),
60
84
 
61
85
  // Async actions. These will not show up in Redux devtools.
@@ -85,10 +109,12 @@ const actions = {
85
109
  };
86
110
 
87
111
  const selectors = {
112
+ getHeaderData: state => state.headerData,
88
113
  isLoading: state => state.isLoading,
89
114
  isQuietLoading: state => state.isQuietLoading,
90
115
  getWizardAPIData: ( state, slug ) => state.apiData[ slug ] || {},
91
116
  getWizardData: ( state, slug ) => state.apiData[ slug ] ?? {},
117
+ getNotices: state => state.notices,
92
118
  getError: state => state.error,
93
119
  };
94
120