newspack-components 4.6.2 → 4.7.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 +23 -0
- package/colors/colors.module.scss +13 -7
- package/dist/cjs/autocomplete-tokenfield/style.scss +4 -2
- package/dist/cjs/breadcrumbs/format-count.js +33 -0
- package/dist/cjs/breadcrumbs/index.js +78 -20
- package/dist/cjs/breadcrumbs/index.test.js +110 -0
- package/dist/cjs/breadcrumbs/style.scss +6 -0
- package/dist/cjs/button/index.js +41 -17
- package/dist/cjs/button/index.test.js +273 -0
- package/dist/cjs/card/core-card.js +9 -4
- package/dist/cjs/card/core-card.test.js +29 -0
- package/dist/cjs/card/index.js +4 -2
- package/dist/cjs/card/style-core.scss +59 -0
- package/dist/cjs/card-feature/index.js +7 -1
- package/dist/cjs/card-feature/index.test.js +92 -0
- package/dist/cjs/color-picker/index.js +1 -1
- package/dist/cjs/confirm-dialog/index.js +26 -9
- package/dist/cjs/confirm-dialog/index.test.js +176 -0
- package/dist/cjs/dataviews/index.js +11 -2
- package/dist/cjs/divider/index.js +13 -0
- package/dist/cjs/drawer/action.js +74 -0
- package/dist/cjs/drawer/body.js +79 -0
- package/dist/cjs/drawer/close-icon.js +45 -0
- package/dist/cjs/drawer/content.js +72 -0
- package/dist/cjs/drawer/context.js +23 -0
- package/dist/cjs/drawer/context.test.js +43 -0
- package/dist/cjs/drawer/divider.js +24 -0
- package/dist/cjs/drawer/footer.js +26 -0
- package/dist/cjs/drawer/header.js +26 -0
- package/dist/cjs/drawer/index.js +42 -0
- package/dist/cjs/drawer/index.test.js +1287 -0
- package/dist/cjs/drawer/root.js +350 -0
- package/dist/cjs/drawer/slots.test.js +422 -0
- package/dist/cjs/drawer/style.scss +205 -0
- package/dist/cjs/drawer/title.js +54 -0
- package/dist/cjs/drawer/types.js +5 -0
- package/dist/cjs/drawer/use-exit-animation.js +59 -0
- package/dist/cjs/hooks/use-confirm-dialog.js +15 -3
- package/dist/cjs/hooks/use-unsaved-changes-dialog.js +19 -4
- package/dist/cjs/index.js +28 -1
- package/dist/cjs/page/index.js +1 -1
- package/dist/cjs/page-control/index.js +82 -0
- package/dist/cjs/plugin-settings/SettingsSection.js +7 -1
- package/dist/cjs/section-header/index.js +5 -2
- package/dist/cjs/section-header/style.scss +25 -12
- package/dist/cjs/table-card/index.js +67 -0
- package/dist/cjs/table-card/index.test.js +153 -0
- package/dist/cjs/with-wizard-screen/style.scss +8 -0
- package/dist/cjs/wizard/index.js +102 -38
- package/dist/esm/autocomplete-tokenfield/style.scss +4 -2
- package/dist/esm/breadcrumbs/format-count.js +27 -0
- package/dist/esm/breadcrumbs/index.js +81 -20
- package/dist/esm/breadcrumbs/index.test.js +110 -0
- package/dist/esm/breadcrumbs/style.scss +6 -0
- package/dist/esm/button/index.js +42 -18
- package/dist/esm/button/index.test.js +273 -0
- package/dist/esm/card/core-card.js +9 -4
- package/dist/esm/card/core-card.test.js +29 -0
- package/dist/esm/card/index.js +4 -2
- package/dist/esm/card/style-core.scss +59 -0
- package/dist/esm/card-feature/index.js +8 -2
- package/dist/esm/card-feature/index.test.js +96 -0
- package/dist/esm/color-picker/index.js +1 -1
- package/dist/esm/confirm-dialog/index.js +26 -9
- package/dist/esm/confirm-dialog/index.test.js +176 -0
- package/dist/esm/dataviews/index.js +11 -2
- package/dist/esm/divider/index.js +14 -0
- package/dist/esm/drawer/action.js +68 -0
- package/dist/esm/drawer/body.js +70 -0
- package/dist/esm/drawer/close-icon.js +37 -0
- package/dist/esm/drawer/content.js +63 -0
- package/dist/esm/drawer/context.js +17 -0
- package/dist/esm/drawer/context.test.js +40 -0
- package/dist/esm/drawer/divider.js +16 -0
- package/dist/esm/drawer/footer.js +18 -0
- package/dist/esm/drawer/header.js +18 -0
- package/dist/esm/drawer/index.js +35 -0
- package/dist/esm/drawer/index.test.js +1285 -0
- package/dist/esm/drawer/root.js +342 -0
- package/dist/esm/drawer/slots.test.js +422 -0
- package/dist/esm/drawer/style.scss +205 -0
- package/dist/esm/drawer/title.js +46 -0
- package/dist/esm/drawer/types.js +1 -0
- package/dist/esm/drawer/use-exit-animation.js +52 -0
- package/dist/esm/hooks/use-confirm-dialog.js +15 -3
- package/dist/esm/hooks/use-unsaved-changes-dialog.js +20 -5
- package/dist/esm/index.js +4 -0
- package/dist/esm/page/index.js +1 -1
- package/dist/esm/page-control/index.js +82 -0
- package/dist/esm/plugin-settings/SettingsSection.js +8 -2
- package/dist/esm/section-header/index.js +5 -2
- package/dist/esm/section-header/style.scss +25 -12
- package/dist/esm/table-card/index.js +60 -0
- package/dist/esm/table-card/index.test.js +150 -0
- package/dist/esm/with-wizard-screen/style.scss +8 -0
- package/dist/esm/wizard/index.js +104 -38
- package/package.json +2 -2
- package/src/autocomplete-tokenfield/style.scss +4 -2
- package/src/breadcrumbs/format-count.js +27 -0
- package/src/breadcrumbs/index.js +68 -8
- package/src/breadcrumbs/index.test.js +57 -0
- package/src/breadcrumbs/style.scss +6 -0
- package/src/button/index.test.js +143 -0
- package/src/button/index.tsx +32 -7
- package/src/card/core-card.js +15 -2
- package/src/card/core-card.test.js +19 -0
- package/src/card/index.js +3 -0
- package/src/card/style-core.scss +59 -0
- package/src/card-feature/README.md +4 -0
- package/src/card-feature/index.test.js +65 -0
- package/src/card-feature/index.tsx +13 -2
- package/src/color-picker/index.js +1 -1
- package/src/confirm-dialog/README.md +3 -2
- package/src/confirm-dialog/index.test.js +146 -0
- package/src/confirm-dialog/index.tsx +23 -9
- package/src/dataviews/index.tsx +11 -2
- package/src/divider/index.js +13 -0
- package/src/drawer/README.md +454 -0
- package/src/drawer/action.tsx +67 -0
- package/src/drawer/body.tsx +60 -0
- package/src/drawer/close-icon.tsx +37 -0
- package/src/drawer/content.tsx +54 -0
- package/src/drawer/context.test.js +31 -0
- package/src/drawer/context.ts +25 -0
- package/src/drawer/divider.tsx +13 -0
- package/src/drawer/footer.tsx +15 -0
- package/src/drawer/header.tsx +15 -0
- package/src/drawer/index.test.js +1012 -0
- package/src/drawer/index.tsx +33 -0
- package/src/drawer/root.tsx +328 -0
- package/src/drawer/slots.test.js +314 -0
- package/src/drawer/style.scss +205 -0
- package/src/drawer/title.tsx +40 -0
- package/src/drawer/types.ts +79 -0
- package/src/drawer/use-exit-animation.ts +46 -0
- package/src/hooks/use-confirm-dialog.tsx +14 -3
- package/src/hooks/use-unsaved-changes-dialog.tsx +17 -5
- package/src/index.js +4 -0
- package/src/page/index.js +1 -1
- package/src/page-control/index.js +63 -0
- package/src/plugin-settings/SettingsSection.js +8 -2
- package/src/section-header/index.js +5 -2
- package/src/section-header/style.scss +25 -12
- package/src/table-card/README.md +100 -0
- package/src/table-card/index.test.js +112 -0
- package/src/table-card/index.tsx +56 -0
- package/src/with-wizard-screen/style.scss +8 -0
- package/src/wizard/index.js +95 -34
|
@@ -65,6 +65,34 @@ describe('ConfirmDialog navigation blocking', function () {
|
|
|
65
65
|
expect(replaceSpy).toHaveBeenCalledTimes(1);
|
|
66
66
|
expect(history.location.pathname).toBe('/');
|
|
67
67
|
});
|
|
68
|
+
|
|
69
|
+
// v5 keeps a single prompt slot, so an unprompted re-sync would be caught by
|
|
70
|
+
// whichever dialog on the screen did install a blocker.
|
|
71
|
+
it('cancels an unprompted dialog without tripping another blocker', function () {
|
|
72
|
+
var historyRef = {
|
|
73
|
+
current: null
|
|
74
|
+
};
|
|
75
|
+
render(/*#__PURE__*/_jsxs(MemoryRouter, {
|
|
76
|
+
children: [/*#__PURE__*/_jsx(HistoryGrabber, {
|
|
77
|
+
historyRef: historyRef
|
|
78
|
+
}), /*#__PURE__*/_jsx(ConfirmDialog, {
|
|
79
|
+
when: true,
|
|
80
|
+
confirmButtonText: "Leave",
|
|
81
|
+
cancelButtonText: "Stay",
|
|
82
|
+
children: "Wizard guard"
|
|
83
|
+
}), /*#__PURE__*/_jsx(ConfirmDialog, {
|
|
84
|
+
isOpen: true,
|
|
85
|
+
confirmButtonText: "Discard changes",
|
|
86
|
+
cancelButtonText: "Keep editing",
|
|
87
|
+
children: "Unsaved changes"
|
|
88
|
+
})]
|
|
89
|
+
}));
|
|
90
|
+
var replaceSpy = jest.spyOn(historyRef.current, 'replace');
|
|
91
|
+
cancelNavigation();
|
|
92
|
+
expect(dialog()).not.toBeInTheDocument();
|
|
93
|
+
expect(screen.queryByText('Wizard guard')).not.toBeInTheDocument();
|
|
94
|
+
expect(replaceSpy).not.toHaveBeenCalled();
|
|
95
|
+
});
|
|
68
96
|
it('replays a blocked push on confirm: re-sync replace first, then push, and re-arms the blocker', function () {
|
|
69
97
|
var history = renderWithHistory();
|
|
70
98
|
var replaceSpy = jest.spyOn(history, 'replace');
|
|
@@ -170,4 +198,152 @@ describe('ConfirmDialog navigation blocking', function () {
|
|
|
170
198
|
expect(history.location.pathname).toBe('/');
|
|
171
199
|
expect(dialog()).toBeInTheDocument();
|
|
172
200
|
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// `useHistory` returns undefined outside a wizard, where `newspack-multibranded-site`
|
|
204
|
+
// runs react-router v6 against this package's v5.
|
|
205
|
+
describe('ConfirmDialog without a router', function () {
|
|
206
|
+
var renderUnrouted = function renderUnrouted() {
|
|
207
|
+
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
208
|
+
return render(/*#__PURE__*/_jsx(ConfirmDialog, _objectSpread(_objectSpread({
|
|
209
|
+
isOpen: true,
|
|
210
|
+
confirmButtonText: "Discard changes",
|
|
211
|
+
cancelButtonText: "Keep editing"
|
|
212
|
+
}, props), {}, {
|
|
213
|
+
children: "Unsaved changes"
|
|
214
|
+
})));
|
|
215
|
+
};
|
|
216
|
+
it('cancels with no history to re-sync', function () {
|
|
217
|
+
var onCancel = jest.fn();
|
|
218
|
+
renderUnrouted({
|
|
219
|
+
onCancel: onCancel
|
|
220
|
+
});
|
|
221
|
+
expect(dialog()).toBeInTheDocument();
|
|
222
|
+
cancelNavigation();
|
|
223
|
+
expect(onCancel).toHaveBeenCalled();
|
|
224
|
+
expect(dialog()).not.toBeInTheDocument();
|
|
225
|
+
});
|
|
226
|
+
it('confirms with no history to re-sync', function () {
|
|
227
|
+
var onConfirm = jest.fn();
|
|
228
|
+
renderUnrouted({
|
|
229
|
+
onConfirm: onConfirm
|
|
230
|
+
});
|
|
231
|
+
confirmNavigation();
|
|
232
|
+
expect(onConfirm).toHaveBeenCalled();
|
|
233
|
+
expect(dialog()).not.toBeInTheDocument();
|
|
234
|
+
});
|
|
235
|
+
it('installs no blocker when asked to block without a router', function () {
|
|
236
|
+
expect(function () {
|
|
237
|
+
return renderUnrouted({
|
|
238
|
+
when: true
|
|
239
|
+
});
|
|
240
|
+
}).not.toThrow();
|
|
241
|
+
expect(dialog()).toBeInTheDocument();
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
describe('ConfirmDialog controlled by isOpen', function () {
|
|
245
|
+
var tree = function tree(isOpen) {
|
|
246
|
+
return /*#__PURE__*/_jsx(ConfirmDialog, {
|
|
247
|
+
isOpen: isOpen,
|
|
248
|
+
confirmButtonText: "Discard changes",
|
|
249
|
+
cancelButtonText: "Keep editing",
|
|
250
|
+
children: "Unsaved changes"
|
|
251
|
+
});
|
|
252
|
+
};
|
|
253
|
+
it('closes when isOpen goes false', function () {
|
|
254
|
+
var _render = render(tree(true)),
|
|
255
|
+
rerender = _render.rerender;
|
|
256
|
+
expect(dialog()).toBeInTheDocument();
|
|
257
|
+
rerender(tree(false));
|
|
258
|
+
expect(dialog()).not.toBeInTheDocument();
|
|
259
|
+
});
|
|
260
|
+
it('opens again afterwards', function () {
|
|
261
|
+
var _render2 = render(tree(true)),
|
|
262
|
+
rerender = _render2.rerender;
|
|
263
|
+
rerender(tree(false));
|
|
264
|
+
rerender(tree(true));
|
|
265
|
+
expect(dialog()).toBeInTheDocument();
|
|
266
|
+
});
|
|
267
|
+
it('leaves a blocked navigation prompt up', function () {
|
|
268
|
+
var historyRef = {
|
|
269
|
+
current: null
|
|
270
|
+
};
|
|
271
|
+
render(/*#__PURE__*/_jsxs(MemoryRouter, {
|
|
272
|
+
children: [/*#__PURE__*/_jsx(HistoryGrabber, {
|
|
273
|
+
historyRef: historyRef
|
|
274
|
+
}), /*#__PURE__*/_jsx(ConfirmDialog, {
|
|
275
|
+
when: true,
|
|
276
|
+
isOpen: false,
|
|
277
|
+
confirmButtonText: "Discard changes",
|
|
278
|
+
cancelButtonText: "Keep editing",
|
|
279
|
+
children: "Unsaved changes"
|
|
280
|
+
})]
|
|
281
|
+
}));
|
|
282
|
+
act(function () {
|
|
283
|
+
return historyRef.current.push('/next');
|
|
284
|
+
});
|
|
285
|
+
expect(dialog()).toBeInTheDocument();
|
|
286
|
+
});
|
|
287
|
+
it('withdraws a blocked navigation prompt when isOpen goes false', function () {
|
|
288
|
+
var historyRef = {
|
|
289
|
+
current: null
|
|
290
|
+
};
|
|
291
|
+
var withOpen = function withOpen(isOpen) {
|
|
292
|
+
return /*#__PURE__*/_jsxs(MemoryRouter, {
|
|
293
|
+
children: [/*#__PURE__*/_jsx(HistoryGrabber, {
|
|
294
|
+
historyRef: historyRef
|
|
295
|
+
}), /*#__PURE__*/_jsx(ConfirmDialog, {
|
|
296
|
+
when: true,
|
|
297
|
+
isOpen: isOpen,
|
|
298
|
+
confirmButtonText: "Discard changes",
|
|
299
|
+
cancelButtonText: "Keep editing",
|
|
300
|
+
children: "Unsaved changes"
|
|
301
|
+
})]
|
|
302
|
+
});
|
|
303
|
+
};
|
|
304
|
+
var _render3 = render(withOpen(true)),
|
|
305
|
+
rerender = _render3.rerender;
|
|
306
|
+
act(function () {
|
|
307
|
+
return historyRef.current.push('/next');
|
|
308
|
+
});
|
|
309
|
+
expect(dialog()).toBeInTheDocument();
|
|
310
|
+
rerender(withOpen(false));
|
|
311
|
+
expect(dialog()).not.toBeInTheDocument();
|
|
312
|
+
expect(historyRef.current.location.pathname).toBe('/');
|
|
313
|
+
|
|
314
|
+
// Confirming the next prompt must not replay the abandoned destination.
|
|
315
|
+
rerender(withOpen(true));
|
|
316
|
+
confirmNavigation();
|
|
317
|
+
expect(historyRef.current.location.pathname).toBe('/');
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
describe('ConfirmDialog named by a hidden title', function () {
|
|
321
|
+
it('takes its accessible name from the title while hiding the header', function () {
|
|
322
|
+
render(/*#__PURE__*/_jsx(ConfirmDialog, {
|
|
323
|
+
isOpen: true,
|
|
324
|
+
hideTitle: true,
|
|
325
|
+
title: "Unsaved changes",
|
|
326
|
+
confirmButtonText: "Discard changes",
|
|
327
|
+
children: "You have unsaved changes that will be lost. Discard changes?"
|
|
328
|
+
}));
|
|
329
|
+
var frame = screen.getByRole('dialog', {
|
|
330
|
+
name: 'Unsaved changes'
|
|
331
|
+
});
|
|
332
|
+
expect(frame).toHaveClass('newspack-modal--hide-title');
|
|
333
|
+
expect(frame.querySelector('.components-modal__header')).toContainElement(screen.getByRole('heading', {
|
|
334
|
+
name: 'Unsaved changes'
|
|
335
|
+
}));
|
|
336
|
+
});
|
|
337
|
+
it('has no accessible name without a title', function () {
|
|
338
|
+
render(/*#__PURE__*/_jsx(ConfirmDialog, {
|
|
339
|
+
isOpen: true,
|
|
340
|
+
hideTitle: true,
|
|
341
|
+
confirmButtonText: "Discard changes",
|
|
342
|
+
children: "You have unsaved changes that will be lost. Discard changes?"
|
|
343
|
+
}));
|
|
344
|
+
expect(screen.queryByRole('dialog', {
|
|
345
|
+
name: 'Unsaved changes'
|
|
346
|
+
})).not.toBeInTheDocument();
|
|
347
|
+
expect(screen.getByRole('dialog')).not.toHaveAttribute('aria-labelledby');
|
|
348
|
+
});
|
|
173
349
|
});
|
|
@@ -24,11 +24,20 @@ import classnames from 'classnames';
|
|
|
24
24
|
*/
|
|
25
25
|
import './style.scss';
|
|
26
26
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
27
|
-
|
|
27
|
+
function DataViews(_ref) {
|
|
28
28
|
var className = _ref.className,
|
|
29
29
|
props = _objectWithoutProperties(_ref, _excluded);
|
|
30
30
|
return /*#__PURE__*/_jsx("div", {
|
|
31
31
|
className: classnames('newspack-dataviews', className),
|
|
32
32
|
children: /*#__PURE__*/_jsx(BaseDataViews, _objectSpread({}, props))
|
|
33
33
|
});
|
|
34
|
-
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// The implementation is typed against the `unknown` instantiation because
|
|
37
|
+
// BaseDataViews' conditionally-optional `getItemId` prop cannot be related
|
|
38
|
+
// to a JSX spread while `Item` is an unresolved type parameter. The export
|
|
39
|
+
// re-types it with the generic signature so call sites get item-aware
|
|
40
|
+
// typing for `data`, `fields`, `actions`, `getItemId`, etc. The assertion
|
|
41
|
+
// is verified by the compiler: the generic signature is assignable to the
|
|
42
|
+
// implementation's concrete one.
|
|
43
|
+
export default DataViews;
|
|
@@ -16,6 +16,20 @@ import './style.scss';
|
|
|
16
16
|
* External dependencies
|
|
17
17
|
*/
|
|
18
18
|
import classNames from 'classnames';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Divider component.
|
|
22
|
+
*
|
|
23
|
+
* Every prop beyond the documented ones is forwarded to the `hr` element.
|
|
24
|
+
*
|
|
25
|
+
* @param {import('react').ComponentPropsWithoutRef<'hr'> & {
|
|
26
|
+
* alignment?: string,
|
|
27
|
+
* marginBottom?: number|string,
|
|
28
|
+
* marginTop?: number|string,
|
|
29
|
+
* variant?: string,
|
|
30
|
+
* }} props - Component props: the documented options plus any `hr` attribute.
|
|
31
|
+
* @return {JSX.Element} Divider component.
|
|
32
|
+
*/
|
|
19
33
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
20
34
|
var Divider = function Divider(_ref) {
|
|
21
35
|
var _ref$alignment = _ref.alignment,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
|
+
var _excluded = ["ariaLabel", "closes", "onClick", "href", "children"];
|
|
4
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
5
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies.
|
|
8
|
+
*/
|
|
9
|
+
import { useContext, useEffect } from '@wordpress/element';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies.
|
|
13
|
+
*/
|
|
14
|
+
import Button from '../button';
|
|
15
|
+
import { DrawerContext } from './context';
|
|
16
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
17
|
+
var Action = function Action(_ref) {
|
|
18
|
+
var ariaLabel = _ref.ariaLabel,
|
|
19
|
+
_ref$closes = _ref.closes,
|
|
20
|
+
closes = _ref$closes === void 0 ? false : _ref$closes,
|
|
21
|
+
onClick = _ref.onClick,
|
|
22
|
+
href = _ref.href,
|
|
23
|
+
children = _ref.children,
|
|
24
|
+
buttonProps = _objectWithoutProperties(_ref, _excluded);
|
|
25
|
+
// Not useDrawerContext: only `closes` requires the context.
|
|
26
|
+
var context = useContext(DrawerContext);
|
|
27
|
+
|
|
28
|
+
// `href=""` is still an anchor; a null from a JS caller means no link at all.
|
|
29
|
+
var hasHref = 'string' === typeof href;
|
|
30
|
+
useEffect(function () {
|
|
31
|
+
if ('production' === process.env.NODE_ENV || !hasHref) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (onClick || closes) {
|
|
35
|
+
// eslint-disable-next-line no-console
|
|
36
|
+
console.warn('Drawer: an action with `href` cannot also take `onClick` or `closes`, and they are ignored. ' + 'A link that ran the close funnel would navigate away while the unsaved-changes confirmation was still opening.');
|
|
37
|
+
}
|
|
38
|
+
}, [hasHref, onClick, closes]);
|
|
39
|
+
useEffect(function () {
|
|
40
|
+
if ('production' === process.env.NODE_ENV) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (typeof children === 'string' && ariaLabel && !ariaLabel.toLowerCase().includes(children.toLowerCase())) {
|
|
44
|
+
// eslint-disable-next-line no-console
|
|
45
|
+
console.warn("Drawer: action ariaLabel \"".concat(ariaLabel, "\" does not contain its visible label \"").concat(children, "\". ") + 'Voice control matches spoken commands against the accessible name (WCAG 2.5.3, Label in Name), ' + 'so ariaLabel must extend the visible text rather than replace it.');
|
|
46
|
+
}
|
|
47
|
+
}, [ariaLabel, children]);
|
|
48
|
+
var handleClick = function handleClick(event) {
|
|
49
|
+
onClick === null || onClick === void 0 || onClick(event);
|
|
50
|
+
if (closes) {
|
|
51
|
+
if (!context) {
|
|
52
|
+
throw new Error('Drawer subcomponents must be rendered inside Drawer.Root.');
|
|
53
|
+
}
|
|
54
|
+
context.requestClose();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Button swallows `href` whenever an onClick is present, so a link supplies none.
|
|
59
|
+
var handlesClick = !hasHref && (!!onClick || closes);
|
|
60
|
+
return /*#__PURE__*/_jsx(Button, _objectSpread(_objectSpread({
|
|
61
|
+
"aria-label": ariaLabel,
|
|
62
|
+
href: hasHref ? href : undefined,
|
|
63
|
+
onClick: handlesClick ? handleClick : undefined
|
|
64
|
+
}, buttonProps), {}, {
|
|
65
|
+
children: children
|
|
66
|
+
}));
|
|
67
|
+
};
|
|
68
|
+
export default Action;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
/**
|
|
3
|
+
* WordPress dependencies.
|
|
4
|
+
*/
|
|
5
|
+
import { __experimentalVStack as VStack } from '@wordpress/components'; // eslint-disable-line @wordpress/no-unsafe-wp-apis
|
|
6
|
+
import { useEffect, useState } from '@wordpress/element';
|
|
7
|
+
import { __ } from '@wordpress/i18n';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Internal dependencies.
|
|
11
|
+
*/
|
|
12
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
+
// Core tab-stops its own container once it overflows; the drawer scrolls here.
|
|
14
|
+
var Body = function Body(_ref) {
|
|
15
|
+
var children = _ref.children;
|
|
16
|
+
var _useState = useState(null),
|
|
17
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
18
|
+
node = _useState2[0],
|
|
19
|
+
setNode = _useState2[1];
|
|
20
|
+
var _useState3 = useState(false),
|
|
21
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
22
|
+
isScrollable = _useState4[0],
|
|
23
|
+
setIsScrollable = _useState4[1];
|
|
24
|
+
useEffect(function () {
|
|
25
|
+
if (!node) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
var measure = function measure() {
|
|
29
|
+
return setIsScrollable(node.scrollHeight > node.clientHeight);
|
|
30
|
+
};
|
|
31
|
+
measure();
|
|
32
|
+
if (!window.ResizeObserver) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
var resize = new ResizeObserver(measure);
|
|
36
|
+
var observeChildren = function observeChildren() {
|
|
37
|
+
resize.disconnect();
|
|
38
|
+
resize.observe(node);
|
|
39
|
+
// Border box: a section's padding rides on an inline custom property.
|
|
40
|
+
Array.from(node.children).forEach(function (child) {
|
|
41
|
+
return resize.observe(child, {
|
|
42
|
+
box: 'border-box'
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
measure();
|
|
46
|
+
};
|
|
47
|
+
observeChildren();
|
|
48
|
+
var mutation = new MutationObserver(observeChildren);
|
|
49
|
+
mutation.observe(node, {
|
|
50
|
+
childList: true
|
|
51
|
+
});
|
|
52
|
+
return function () {
|
|
53
|
+
resize.disconnect();
|
|
54
|
+
mutation.disconnect();
|
|
55
|
+
};
|
|
56
|
+
}, [node]);
|
|
57
|
+
return /*#__PURE__*/_jsx(VStack, {
|
|
58
|
+
ref: setNode,
|
|
59
|
+
className: "newspack-drawer__body",
|
|
60
|
+
spacing: 0,
|
|
61
|
+
justify: "flex-start"
|
|
62
|
+
// A bare div is role=generic, where ARIA prohibits an author name.
|
|
63
|
+
,
|
|
64
|
+
role: isScrollable ? 'group' : undefined,
|
|
65
|
+
tabIndex: isScrollable ? 0 : undefined,
|
|
66
|
+
"aria-label": isScrollable ? __('Scrollable section', 'newspack-plugin') : undefined,
|
|
67
|
+
children: children
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
export default Body;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies.
|
|
3
|
+
*/
|
|
4
|
+
import { close } from '@wordpress/icons';
|
|
5
|
+
import { __, sprintf } from '@wordpress/i18n';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* External dependencies.
|
|
9
|
+
*/
|
|
10
|
+
import classnames from 'classnames';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Internal dependencies.
|
|
14
|
+
*/
|
|
15
|
+
import Button from '../button';
|
|
16
|
+
import { useDrawerContext } from './context';
|
|
17
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
18
|
+
var CloseIcon = function CloseIcon(_ref) {
|
|
19
|
+
var label = _ref.label,
|
|
20
|
+
_ref$icon = _ref.icon,
|
|
21
|
+
icon = _ref$icon === void 0 ? close : _ref$icon,
|
|
22
|
+
className = _ref.className;
|
|
23
|
+
var _useDrawerContext = useDrawerContext(),
|
|
24
|
+
requestClose = _useDrawerContext.requestClose,
|
|
25
|
+
title = _useDrawerContext.title;
|
|
26
|
+
var defaultLabel = title !== null && title !== void 0 && title.text ?
|
|
27
|
+
// translators: %s: the drawer's title.
|
|
28
|
+
sprintf(__('Close %s', 'newspack-plugin'), title.text) : __('Close', 'newspack-plugin');
|
|
29
|
+
return /*#__PURE__*/_jsx(Button, {
|
|
30
|
+
className: classnames('newspack-drawer__dismiss', className),
|
|
31
|
+
icon: icon,
|
|
32
|
+
size: "small",
|
|
33
|
+
label: label || defaultLabel,
|
|
34
|
+
onClick: requestClose
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
export default CloseIcon;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies.
|
|
3
|
+
*/
|
|
4
|
+
import { __experimentalVStack as VStack } from '@wordpress/components'; // eslint-disable-line @wordpress/no-unsafe-wp-apis
|
|
5
|
+
import { Children, isValidElement } from '@wordpress/element';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* External dependencies.
|
|
9
|
+
*/
|
|
10
|
+
import classnames from 'classnames';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Internal dependencies.
|
|
14
|
+
*/
|
|
15
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
16
|
+
// VStack drops non-elements, a lone string aside. A run shares one wrapper so
|
|
17
|
+
// `Edited by { name }` is one row, not three.
|
|
18
|
+
var asRows = function asRows(children) {
|
|
19
|
+
if ('string' === typeof children) {
|
|
20
|
+
return children;
|
|
21
|
+
}
|
|
22
|
+
var rows = [];
|
|
23
|
+
var text = [];
|
|
24
|
+
var flushText = function flushText() {
|
|
25
|
+
if (text.some(function (part) {
|
|
26
|
+
return '' !== String(part).trim();
|
|
27
|
+
})) {
|
|
28
|
+
rows.push(/*#__PURE__*/_jsx("div", {
|
|
29
|
+
children: text
|
|
30
|
+
}, "text-".concat(rows.length)));
|
|
31
|
+
}
|
|
32
|
+
text = [];
|
|
33
|
+
};
|
|
34
|
+
Children.toArray(children).forEach(function (child) {
|
|
35
|
+
if (isValidElement(child)) {
|
|
36
|
+
flushText();
|
|
37
|
+
rows.push(child);
|
|
38
|
+
} else {
|
|
39
|
+
text.push(child);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
flushText();
|
|
43
|
+
return rows;
|
|
44
|
+
};
|
|
45
|
+
var Content = function Content(_ref) {
|
|
46
|
+
var _ref$padding = _ref.padding,
|
|
47
|
+
padding = _ref$padding === void 0 ? 6 : _ref$padding,
|
|
48
|
+
_ref$gap = _ref.gap,
|
|
49
|
+
gap = _ref$gap === void 0 ? 4 : _ref$gap,
|
|
50
|
+
className = _ref.className,
|
|
51
|
+
children = _ref.children;
|
|
52
|
+
return /*#__PURE__*/_jsx(VStack, {
|
|
53
|
+
className: classnames('newspack-drawer__content', className),
|
|
54
|
+
spacing: gap
|
|
55
|
+
// A custom property, not inline padding, so the stylesheet's seam rules win.
|
|
56
|
+
,
|
|
57
|
+
style: {
|
|
58
|
+
'--newspack-drawer-content-padding': "".concat(padding * 4, "px")
|
|
59
|
+
},
|
|
60
|
+
children: asRows(children)
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
export default Content;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies.
|
|
3
|
+
*/
|
|
4
|
+
import { createContext, useContext } from '@wordpress/element';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export var DrawerContext = createContext(null);
|
|
11
|
+
export var useDrawerContext = function useDrawerContext() {
|
|
12
|
+
var context = useContext(DrawerContext);
|
|
13
|
+
if (!context) {
|
|
14
|
+
throw new Error('Drawer subcomponents must be rendered inside Drawer.Root.');
|
|
15
|
+
}
|
|
16
|
+
return context;
|
|
17
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies.
|
|
3
|
+
*/
|
|
4
|
+
import { DrawerContext, useDrawerContext } from './context';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* External dependencies.
|
|
8
|
+
*/
|
|
9
|
+
import { render, screen } from '@testing-library/react';
|
|
10
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
+
var Probe = function Probe() {
|
|
12
|
+
var _useDrawerContext = useDrawerContext(),
|
|
13
|
+
title = _useDrawerContext.title;
|
|
14
|
+
return /*#__PURE__*/_jsx("span", {
|
|
15
|
+
children: title ? title.text : 'no title'
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
describe('useDrawerContext', function () {
|
|
19
|
+
it('returns the provided value', function () {
|
|
20
|
+
render(/*#__PURE__*/_jsx(DrawerContext.Provider, {
|
|
21
|
+
value: {
|
|
22
|
+
requestClose: function requestClose() {},
|
|
23
|
+
title: {
|
|
24
|
+
id: 't1',
|
|
25
|
+
text: 'Hello'
|
|
26
|
+
},
|
|
27
|
+
setTitle: function setTitle() {}
|
|
28
|
+
},
|
|
29
|
+
children: /*#__PURE__*/_jsx(Probe, {})
|
|
30
|
+
}));
|
|
31
|
+
expect(screen.getByText('Hello')).toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
it('throws outside Drawer.Root', function () {
|
|
34
|
+
var consoleError = jest.spyOn(console, 'error').mockImplementation(function () {});
|
|
35
|
+
expect(function () {
|
|
36
|
+
return render(/*#__PURE__*/_jsx(Probe, {}));
|
|
37
|
+
}).toThrow('Drawer subcomponents must be rendered inside Drawer.Root.');
|
|
38
|
+
consoleError.mockRestore();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies.
|
|
3
|
+
*/
|
|
4
|
+
import classnames from 'classnames';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies.
|
|
8
|
+
*/
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
var Divider = function Divider(_ref) {
|
|
11
|
+
var className = _ref.className;
|
|
12
|
+
return /*#__PURE__*/_jsx("hr", {
|
|
13
|
+
className: classnames('newspack-drawer__divider', className)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
export default Divider;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies.
|
|
3
|
+
*/
|
|
4
|
+
import classnames from 'classnames';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies.
|
|
8
|
+
*/
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
var Footer = function Footer(_ref) {
|
|
11
|
+
var className = _ref.className,
|
|
12
|
+
children = _ref.children;
|
|
13
|
+
return /*#__PURE__*/_jsx("div", {
|
|
14
|
+
className: classnames('newspack-drawer__footer', className),
|
|
15
|
+
children: children
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
export default Footer;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies.
|
|
3
|
+
*/
|
|
4
|
+
import classnames from 'classnames';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies.
|
|
8
|
+
*/
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
var Header = function Header(_ref) {
|
|
11
|
+
var className = _ref.className,
|
|
12
|
+
children = _ref.children;
|
|
13
|
+
return /*#__PURE__*/_jsx("div", {
|
|
14
|
+
className: classnames('newspack-drawer__header', className),
|
|
15
|
+
children: children
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
export default Header;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
/**
|
|
3
|
+
* Drawer
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies.
|
|
8
|
+
*/
|
|
9
|
+
import Action from './action';
|
|
10
|
+
import CloseIcon from './close-icon';
|
|
11
|
+
import Content from './content';
|
|
12
|
+
import Footer from './footer';
|
|
13
|
+
import Header from './header';
|
|
14
|
+
import Root from './root';
|
|
15
|
+
import Divider from './divider';
|
|
16
|
+
import Title from './title';
|
|
17
|
+
|
|
18
|
+
// A namespace object, not this package's usual flat exports. See the README.
|
|
19
|
+
export var Drawer = {
|
|
20
|
+
Root: Root,
|
|
21
|
+
Header: Header,
|
|
22
|
+
Title: Title,
|
|
23
|
+
CloseIcon: CloseIcon,
|
|
24
|
+
Content: Content,
|
|
25
|
+
Divider: Divider,
|
|
26
|
+
Footer: Footer,
|
|
27
|
+
Action: Action
|
|
28
|
+
};
|
|
29
|
+
Object.entries(Drawer).forEach(function (_ref) {
|
|
30
|
+
var _ref2 = _slicedToArray(_ref, 2),
|
|
31
|
+
name = _ref2[0],
|
|
32
|
+
part = _ref2[1];
|
|
33
|
+
part.displayName = "Drawer.".concat(name);
|
|
34
|
+
});
|
|
35
|
+
export default Drawer;
|