newspack-components 4.10.0 → 4.12.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/DEVELOPMENT.md +80 -38
- package/dist/cjs/breadcrumbs/index.js +1 -0
- package/dist/cjs/breadcrumbs/style.scss +3 -10
- package/dist/cjs/card/style-core.scss +1 -1
- package/dist/cjs/card-sortable-list/index.js +23 -12
- package/dist/cjs/card-sortable-list/index.test.js +21 -0
- package/dist/cjs/card-sortable-list/style.scss +8 -0
- package/dist/cjs/drawer/body.js +4 -6
- package/dist/cjs/drawer/content.js +7 -37
- package/dist/cjs/drawer/footer.js +10 -1
- package/dist/cjs/drawer/header.js +9 -1
- package/dist/cjs/drawer/index.test.js +3 -4
- package/dist/cjs/drawer/slots.test.js +25 -67
- package/dist/cjs/drawer/style.scss +0 -18
- package/dist/cjs/index.js +14 -0
- package/dist/cjs/page/index.js +10 -2
- package/dist/cjs/page/style.scss +24 -2
- package/dist/cjs/reorder-modal/index.js +521 -0
- package/dist/cjs/reorder-modal/index.test.js +1223 -0
- package/dist/cjs/reorder-modal/style.scss +114 -0
- package/dist/cjs/tabbed-navigation/style.scss +5 -0
- package/dist/cjs/tooltip-badge/index.js +50 -0
- package/dist/cjs/tooltip-badge/index.test.js +30 -0
- package/dist/cjs/wizard/index.js +21 -8
- package/dist/cjs/wizard/index.test.js +78 -0
- package/dist/cjs/wizard/store/index.js +2 -1
- package/dist/esm/breadcrumbs/index.js +1 -0
- package/dist/esm/breadcrumbs/style.scss +3 -10
- package/dist/esm/card/style-core.scss +1 -1
- package/dist/esm/card-sortable-list/index.js +25 -13
- package/dist/esm/card-sortable-list/index.test.js +21 -0
- package/dist/esm/card-sortable-list/style.scss +8 -0
- package/dist/esm/drawer/body.js +4 -5
- package/dist/esm/drawer/content.js +7 -36
- package/dist/esm/drawer/footer.js +10 -1
- package/dist/esm/drawer/header.js +9 -1
- package/dist/esm/drawer/index.test.js +3 -4
- package/dist/esm/drawer/slots.test.js +25 -67
- package/dist/esm/drawer/style.scss +0 -18
- package/dist/esm/index.js +2 -0
- package/dist/esm/page/index.js +10 -2
- package/dist/esm/page/style.scss +24 -2
- package/dist/esm/reorder-modal/index.js +514 -0
- package/dist/esm/reorder-modal/index.test.js +1224 -0
- package/dist/esm/reorder-modal/style.scss +114 -0
- package/dist/esm/tabbed-navigation/style.scss +5 -0
- package/dist/esm/tooltip-badge/index.js +43 -0
- package/dist/esm/tooltip-badge/index.test.js +26 -0
- package/dist/esm/wizard/index.js +23 -12
- package/dist/esm/wizard/index.test.js +78 -0
- package/dist/esm/wizard/store/index.js +2 -1
- package/package.json +2 -2
- package/src/breadcrumbs/index.js +1 -1
- package/src/breadcrumbs/style.scss +3 -10
- package/src/card/style-core.scss +1 -1
- package/src/card-form/README.md +2 -2
- package/src/card-sortable-list/index.test.js +13 -0
- package/src/card-sortable-list/index.tsx +31 -12
- package/src/card-sortable-list/style.scss +8 -0
- package/src/drawer/README.md +10 -16
- package/src/drawer/body.tsx +4 -5
- package/src/drawer/content.tsx +7 -33
- package/src/drawer/footer.tsx +8 -1
- package/src/drawer/header.tsx +8 -1
- package/src/drawer/index.test.js +3 -4
- package/src/drawer/slots.test.js +14 -48
- package/src/drawer/style.scss +0 -18
- package/src/drawer/types.ts +8 -3
- package/src/index.js +2 -0
- package/src/page/index.js +7 -3
- package/src/page/style.scss +24 -2
- package/src/reorder-modal/index.js +484 -0
- package/src/reorder-modal/index.test.js +566 -0
- package/src/reorder-modal/style.scss +114 -0
- package/src/tabbed-navigation/style.scss +5 -0
- package/src/tooltip-badge/index.test.js +24 -0
- package/src/tooltip-badge/index.tsx +40 -0
- package/src/wizard/index.js +25 -14
- package/src/wizard/index.test.js +38 -0
- package/src/wizard/store/index.js +1 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import TooltipBadge from './';
|
|
10
|
+
|
|
11
|
+
describe( 'TooltipBadge', () => {
|
|
12
|
+
it( 'describes its focusable trigger with the tooltip text, keeping it out of a surrounding heading name', () => {
|
|
13
|
+
render(
|
|
14
|
+
<h3>
|
|
15
|
+
Registration wall
|
|
16
|
+
<TooltipBadge label="Overrides paid access" tooltip="This grants registered readers access." intent="low" />
|
|
17
|
+
</h3>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
expect( screen.getByRole( 'heading', { name: 'Registration wall Overrides paid access' } ) ).toBeInTheDocument();
|
|
21
|
+
const trigger = screen.getByText( 'Overrides paid access' ).closest( '[tabindex="0"]' );
|
|
22
|
+
expect( trigger ).toHaveAccessibleDescription( 'This grants registered readers access.' );
|
|
23
|
+
} );
|
|
24
|
+
} );
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useInstanceId } from '@wordpress/compose';
|
|
5
|
+
import { Badge, Tooltip } from '@wordpress/ui';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import type { BadgeIntent } from '../types';
|
|
11
|
+
|
|
12
|
+
type TooltipBadgeProps = {
|
|
13
|
+
label: string;
|
|
14
|
+
tooltip: string;
|
|
15
|
+
intent?: BadgeIntent;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A badge whose tooltip explains it. The tooltip serves mouse and keyboard users;
|
|
20
|
+
* screen readers get the same text as the trigger's description, which keeps it
|
|
21
|
+
* out of the accessible name of a heading the badge sits in.
|
|
22
|
+
*/
|
|
23
|
+
const TooltipBadge = ( { label, tooltip, intent = 'none' }: TooltipBadgeProps ) => {
|
|
24
|
+
const descriptionId = useInstanceId( TooltipBadge, 'newspack-tooltip-badge' );
|
|
25
|
+
return (
|
|
26
|
+
<>
|
|
27
|
+
<Tooltip.Root>
|
|
28
|
+
<Tooltip.Trigger render={ <span tabIndex={ 0 } aria-describedby={ descriptionId } /> }>
|
|
29
|
+
<Badge intent={ intent }>{ label }</Badge>
|
|
30
|
+
</Tooltip.Trigger>
|
|
31
|
+
<Tooltip.Popup>{ tooltip }</Tooltip.Popup>
|
|
32
|
+
</Tooltip.Root>
|
|
33
|
+
<span id={ descriptionId } hidden>
|
|
34
|
+
{ tooltip }
|
|
35
|
+
</span>
|
|
36
|
+
</>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default TooltipBadge;
|
package/src/wizard/index.js
CHANGED
|
@@ -6,16 +6,8 @@ import classnames from 'classnames';
|
|
|
6
6
|
/**
|
|
7
7
|
* WordPress dependencies.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
MenuGroup,
|
|
12
|
-
MenuItem,
|
|
13
|
-
Notice,
|
|
14
|
-
SlotFillProvider,
|
|
15
|
-
createSlotFill,
|
|
16
|
-
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
|
|
17
|
-
__experimentalVStack as VStack,
|
|
18
|
-
} from '@wordpress/components';
|
|
9
|
+
import { DropdownMenu, MenuGroup, MenuItem, Notice, SlotFillProvider, createSlotFill } from '@wordpress/components';
|
|
10
|
+
import { Stack } from '@wordpress/ui';
|
|
19
11
|
import { useDispatch, useSelect } from '@wordpress/data';
|
|
20
12
|
import { cloneElement, createInterpolateElement, isValidElement, useLayoutEffect, useRef, useState, forwardRef } from '@wordpress/element';
|
|
21
13
|
import { __ } from '@wordpress/i18n';
|
|
@@ -111,6 +103,7 @@ const WizardHeaderRegion = ( {
|
|
|
111
103
|
sections,
|
|
112
104
|
sectionName,
|
|
113
105
|
subTitle,
|
|
106
|
+
headerSubTitle,
|
|
114
107
|
actions,
|
|
115
108
|
tabbedNavigation: wizardTabbedNavigation,
|
|
116
109
|
children,
|
|
@@ -134,6 +127,17 @@ const WizardHeaderRegion = ( {
|
|
|
134
127
|
}
|
|
135
128
|
}
|
|
136
129
|
|
|
130
|
+
// WizardError normally mounts inside the tab bar, so a route without one renders it itself.
|
|
131
|
+
if ( activeSection( sections, pathname )?.hideTabbedNavigation && tabbedNavigation ) {
|
|
132
|
+
tabbedNavigation = null;
|
|
133
|
+
children = (
|
|
134
|
+
<>
|
|
135
|
+
<WizardError />
|
|
136
|
+
{ children }
|
|
137
|
+
</>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
137
141
|
if ( hideHeader ) {
|
|
138
142
|
// Without the Page shell the tabs still own the content: it renders
|
|
139
143
|
// inside the active tab's panel.
|
|
@@ -154,7 +158,12 @@ const WizardHeaderRegion = ( {
|
|
|
154
158
|
const sectionSubTitle = activeSection( sections, pathname )?.subHeaderText;
|
|
155
159
|
|
|
156
160
|
return (
|
|
157
|
-
<Page
|
|
161
|
+
<Page
|
|
162
|
+
breadcrumbItems={ breadcrumbItems }
|
|
163
|
+
subTitle={ headerSubTitle || ( sectionSubTitle ?? subTitle ) }
|
|
164
|
+
actions={ actions }
|
|
165
|
+
tabbedNavigation={ tabbedNavigation }
|
|
166
|
+
>
|
|
158
167
|
{ children }
|
|
159
168
|
</Page>
|
|
160
169
|
);
|
|
@@ -166,7 +175,7 @@ const WizardHeaderRegion = ( {
|
|
|
166
175
|
* @property {string} [subHeaderText] The sub-header text, optional.
|
|
167
176
|
* @property {string} [apiSlug] The API slug, optional.
|
|
168
177
|
* @property {string} [className] CSS classes, optional.
|
|
169
|
-
* @property {any[]} sections Array of sections. A section's own `subHeaderText` replaces the wizard's while it is active.
|
|
178
|
+
* @property {any[]} sections Array of sections. A section's own `subHeaderText` replaces the wizard's while it is active, and `hideTabbedNavigation` hides the tab bar on its route.
|
|
170
179
|
* Its optional `tabbedNavigation( params )` returns the tab items shown while its route matches.
|
|
171
180
|
* @property {boolean} [hasSimpleFooter] Indicates if a simple footer is used, optional.
|
|
172
181
|
* @property {() => void} [renderAboveSections] Function to render content above sections, optional.
|
|
@@ -215,6 +224,7 @@ const Wizard = (
|
|
|
215
224
|
sectionTitle,
|
|
216
225
|
sectionPrimaryAction,
|
|
217
226
|
sectionSecondaryAction,
|
|
227
|
+
subTitle: headerSubTitle,
|
|
218
228
|
} = headerData;
|
|
219
229
|
|
|
220
230
|
const mainActions = actions?.filter( action => action.type === 'primary' || action.type === 'secondary' );
|
|
@@ -268,10 +278,10 @@ const Wizard = (
|
|
|
268
278
|
<>
|
|
269
279
|
{ ! pluginRequirementsKnown && (
|
|
270
280
|
<div className="newspack-wizard__loader">
|
|
271
|
-
<
|
|
281
|
+
<Stack direction="column" align="center" gap="sm">
|
|
272
282
|
<Waiting noMargin />
|
|
273
283
|
<strong>{ __( 'Fetching…', 'newspack-plugin' ) }</strong>
|
|
274
|
-
</
|
|
284
|
+
</Stack>
|
|
275
285
|
</div>
|
|
276
286
|
) }
|
|
277
287
|
<div hidden={ ! pluginRequirementsKnown }>
|
|
@@ -443,6 +453,7 @@ const Wizard = (
|
|
|
443
453
|
sections={ routedSections }
|
|
444
454
|
sectionName={ sectionName }
|
|
445
455
|
subTitle={ subHeaderText }
|
|
456
|
+
headerSubTitle={ headerSubTitle }
|
|
446
457
|
actions={ headerActions }
|
|
447
458
|
tabbedNavigation={ tabbedNavigation }
|
|
448
459
|
>
|
package/src/wizard/index.test.js
CHANGED
|
@@ -320,3 +320,41 @@ describe( 'Wizard section subtitle', () => {
|
|
|
320
320
|
expect( subtitle() ).toBe( 'Wizard intro' );
|
|
321
321
|
} );
|
|
322
322
|
} );
|
|
323
|
+
|
|
324
|
+
describe( 'Wizard tab bar on hidden routes', () => {
|
|
325
|
+
beforeEach( () => {
|
|
326
|
+
apiFetch.mockReset();
|
|
327
|
+
apiFetch.mockResolvedValue( {} );
|
|
328
|
+
} );
|
|
329
|
+
|
|
330
|
+
it( 'hides the tab bar on a hideTabbedNavigation route and restores it on a tab route', async () => {
|
|
331
|
+
window.location.hash = '#/list';
|
|
332
|
+
render(
|
|
333
|
+
<Wizard
|
|
334
|
+
headerText="Test wizard"
|
|
335
|
+
sections={ [
|
|
336
|
+
{ label: 'List', path: '/list', exact: true, render: () => <div>List view</div> },
|
|
337
|
+
{ label: 'Other', path: '/other', exact: true, render: () => <div>Other view</div> },
|
|
338
|
+
{ path: '/list/:id', isHidden: true, hideTabbedNavigation: true, render: () => <div>Item view</div> },
|
|
339
|
+
] }
|
|
340
|
+
/>
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
expect( await screen.findByText( 'List view' ) ).toBeInTheDocument();
|
|
344
|
+
expect( screen.getByRole( 'tab', { name: 'Other' } ) ).toBeInTheDocument();
|
|
345
|
+
|
|
346
|
+
act( () => {
|
|
347
|
+
window.location.hash = '#/list/esp';
|
|
348
|
+
window.dispatchEvent( new HashChangeEvent( 'hashchange' ) );
|
|
349
|
+
} );
|
|
350
|
+
expect( await screen.findByText( 'Item view' ) ).toBeInTheDocument();
|
|
351
|
+
expect( screen.queryByRole( 'tab' ) ).not.toBeInTheDocument();
|
|
352
|
+
|
|
353
|
+
act( () => {
|
|
354
|
+
window.location.hash = '#/list';
|
|
355
|
+
window.dispatchEvent( new HashChangeEvent( 'hashchange' ) );
|
|
356
|
+
} );
|
|
357
|
+
expect( await screen.findByText( 'List view' ) ).toBeInTheDocument();
|
|
358
|
+
expect( screen.getByRole( 'tab', { name: 'Other' } ) ).toBeInTheDocument();
|
|
359
|
+
} );
|
|
360
|
+
} );
|