newspack-components 4.6.3 → 4.7.0-alpha.2
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 +7 -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
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
/**
|
|
3
|
+
* WordPress dependencies.
|
|
4
|
+
*/
|
|
5
|
+
import { useReducedMotion } from '@wordpress/compose';
|
|
6
|
+
import { useEffect, useState } from '@wordpress/element';
|
|
7
|
+
|
|
8
|
+
// Covers an animationend that never arrives.
|
|
9
|
+
var EXIT_FALLBACK_MS = 400;
|
|
10
|
+
function useExitAnimation(isOpen, node) {
|
|
11
|
+
var _useState = useState(false),
|
|
12
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
13
|
+
isExiting = _useState2[0],
|
|
14
|
+
setIsExiting = _useState2[1];
|
|
15
|
+
var _useState3 = useState(isOpen),
|
|
16
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
17
|
+
wasOpen = _useState4[0],
|
|
18
|
+
setWasOpen = _useState4[1];
|
|
19
|
+
|
|
20
|
+
// The stylesheet zeroes the animation here, so animationend never fires and the
|
|
21
|
+
// fallback would hold a visible panel for 400ms.
|
|
22
|
+
var prefersReducedMotion = useReducedMotion();
|
|
23
|
+
|
|
24
|
+
// During render: a commit later the panel would leave the DOM and remount.
|
|
25
|
+
if (wasOpen !== isOpen) {
|
|
26
|
+
setWasOpen(isOpen);
|
|
27
|
+
setIsExiting(!isOpen && !prefersReducedMotion);
|
|
28
|
+
}
|
|
29
|
+
useEffect(function () {
|
|
30
|
+
if (!isExiting) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Keyed on the name: animationend bubbles from anything else animating.
|
|
34
|
+
var finish = function finish(event) {
|
|
35
|
+
if (event !== null && event !== void 0 && event.animationName && 'newspack-drawer-slide-out' !== event.animationName) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
setIsExiting(false);
|
|
39
|
+
};
|
|
40
|
+
var timer = setTimeout(finish, EXIT_FALLBACK_MS);
|
|
41
|
+
node === null || node === void 0 || node.addEventListener('animationend', finish);
|
|
42
|
+
return function () {
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
node === null || node === void 0 || node.removeEventListener('animationend', finish);
|
|
45
|
+
};
|
|
46
|
+
}, [isExiting, node]);
|
|
47
|
+
return {
|
|
48
|
+
isRendered: isOpen || isExiting,
|
|
49
|
+
isExiting: isExiting
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export default useExitAnimation;
|
|
@@ -29,6 +29,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
29
29
|
*
|
|
30
30
|
* The `confirmDialog` element must be rendered somewhere in the component's
|
|
31
31
|
* JSX for the dialog to appear.
|
|
32
|
+
*
|
|
33
|
+
* `cancelConfirm()` withdraws a pending request without invoking its callback,
|
|
34
|
+
* for when whatever raised the prompt goes away on its own.
|
|
32
35
|
*/
|
|
33
36
|
function useConfirmDialog(options) {
|
|
34
37
|
var message = options.message,
|
|
@@ -53,12 +56,20 @@ function useConfirmDialog(options) {
|
|
|
53
56
|
callback();
|
|
54
57
|
}
|
|
55
58
|
}, []);
|
|
59
|
+
var cancelConfirm = useCallback(function () {
|
|
60
|
+
return setPendingAction(null);
|
|
61
|
+
}, []);
|
|
56
62
|
var confirmDialog = /*#__PURE__*/_jsx(ConfirmDialog, _objectSpread(_objectSpread({}, dialogProps), {}, {
|
|
57
63
|
when: when,
|
|
58
64
|
isOpen: !!pendingAction,
|
|
59
65
|
onConfirm: function onConfirm() {
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
// Cleared either way: a throwing callback would otherwise leave the
|
|
67
|
+
// dialog permanently unraisable for this instance.
|
|
68
|
+
try {
|
|
69
|
+
pendingAction === null || pendingAction === void 0 || pendingAction();
|
|
70
|
+
} finally {
|
|
71
|
+
setPendingAction(null);
|
|
72
|
+
}
|
|
62
73
|
},
|
|
63
74
|
onCancel: function onCancel() {
|
|
64
75
|
return setPendingAction(null);
|
|
@@ -67,7 +78,8 @@ function useConfirmDialog(options) {
|
|
|
67
78
|
}));
|
|
68
79
|
return {
|
|
69
80
|
confirmDialog: confirmDialog,
|
|
70
|
-
requestConfirm: requestConfirm
|
|
81
|
+
requestConfirm: requestConfirm,
|
|
82
|
+
cancelConfirm: cancelConfirm
|
|
71
83
|
};
|
|
72
84
|
}
|
|
73
85
|
export default useConfirmDialog;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WordPress dependencies.
|
|
3
3
|
*/
|
|
4
|
-
import { useEffect, useRef } from '@wordpress/element';
|
|
4
|
+
import { useCallback, useEffect, useRef } from '@wordpress/element';
|
|
5
5
|
import { __ } from '@wordpress/i18n';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -34,7 +34,8 @@ function isSameOriginNavigation(link) {
|
|
|
34
34
|
* messaging, intercepts same-origin link clicks so the dialog fires instead
|
|
35
35
|
* of a silent navigation, and adds a `beforeunload` listener as the last-resort
|
|
36
36
|
* guard for refresh / tab-close (browser-native, cannot be styled). The
|
|
37
|
-
* returned `confirmDialog` element must be rendered in JSX
|
|
37
|
+
* returned `confirmDialog` element must be rendered in JSX, and `requestConfirm`
|
|
38
|
+
* and `cancelConfirm` behave as `useConfirmDialog` documents them.
|
|
38
39
|
*
|
|
39
40
|
* Single-consumer constraint: the click handler is attached at the document
|
|
40
41
|
* level in capture phase. Two simultaneously-active instances will both fire
|
|
@@ -45,11 +46,14 @@ function useUnsavedChangesDialog(_ref) {
|
|
|
45
46
|
var _useConfirmDialog = useConfirmDialog({
|
|
46
47
|
when: when,
|
|
47
48
|
message: __('You have unsaved changes that will be lost. Discard changes?', 'newspack-plugin'),
|
|
48
|
-
confirmButtonText: __('Discard
|
|
49
|
+
confirmButtonText: __('Discard Changes', 'newspack-plugin'),
|
|
50
|
+
// Hidden, but still the dialog's accessible name.
|
|
51
|
+
title: __('Unsaved changes', 'newspack-plugin'),
|
|
49
52
|
hideTitle: true
|
|
50
53
|
}),
|
|
51
54
|
confirmDialog = _useConfirmDialog.confirmDialog,
|
|
52
|
-
requestConfirm = _useConfirmDialog.requestConfirm
|
|
55
|
+
requestConfirm = _useConfirmDialog.requestConfirm,
|
|
56
|
+
cancelConfirm = _useConfirmDialog.cancelConfirm;
|
|
53
57
|
|
|
54
58
|
// Tracks navigation the user has already approved via our custom dialog so
|
|
55
59
|
// the beforeunload guard doesn't fire a second native prompt on top of it.
|
|
@@ -128,9 +132,20 @@ function useUnsavedChangesDialog(_ref) {
|
|
|
128
132
|
return window.removeEventListener('beforeunload', handler);
|
|
129
133
|
};
|
|
130
134
|
}, [when]);
|
|
135
|
+
|
|
136
|
+
// Escape hatch for an action that navigates on its own — a handoff that
|
|
137
|
+
// POSTs and then assigns `window.location` — once the dialog has already
|
|
138
|
+
// been confirmed. Left armed, the beforeunload guard would draw a second,
|
|
139
|
+
// native prompt on top of it. Call it immediately before navigating, so a
|
|
140
|
+
// failed action leaves the guard in place.
|
|
141
|
+
var allowNextUnload = useCallback(function () {
|
|
142
|
+
isNavigatingRef.current = true;
|
|
143
|
+
}, []);
|
|
131
144
|
return {
|
|
132
145
|
confirmDialog: confirmDialog,
|
|
133
|
-
requestConfirm: requestConfirm
|
|
146
|
+
requestConfirm: requestConfirm,
|
|
147
|
+
cancelConfirm: cancelConfirm,
|
|
148
|
+
allowNextUnload: allowNextUnload
|
|
134
149
|
};
|
|
135
150
|
}
|
|
136
151
|
export default useUnsavedChangesDialog;
|
package/dist/esm/index.js
CHANGED
|
@@ -19,6 +19,7 @@ export { default as ConfirmDialog } from './confirm-dialog';
|
|
|
19
19
|
export { default as CustomSelectControl } from './custom-select-control';
|
|
20
20
|
export { default as DataViews } from './dataviews';
|
|
21
21
|
export { default as Divider } from './divider';
|
|
22
|
+
export { default as Drawer } from './drawer';
|
|
22
23
|
export { default as FormTokenField } from './form-token-field';
|
|
23
24
|
export { default as Footer } from './footer';
|
|
24
25
|
export { default as Handoff } from './handoff';
|
|
@@ -31,6 +32,7 @@ export { default as Modal } from './modal';
|
|
|
31
32
|
export { default as NewspackIcon } from './newspack-icon';
|
|
32
33
|
export { default as Notice } from './notice';
|
|
33
34
|
export { default as Page } from './page';
|
|
35
|
+
export { default as PageControl } from './page-control';
|
|
34
36
|
export { default as PluginInstaller } from './plugin-installer';
|
|
35
37
|
export { default as PluginSettings } from './plugin-settings';
|
|
36
38
|
export { default as PluginToggle } from './plugin-toggle';
|
|
@@ -44,10 +46,12 @@ export { default as StepsList } from './steps-list';
|
|
|
44
46
|
export { default as StepsListItem } from './steps-list-item';
|
|
45
47
|
export { default as StyleCard } from './style-card';
|
|
46
48
|
export { default as TabbedNavigation } from './tabbed-navigation';
|
|
49
|
+
export { default as TableCard } from './table-card';
|
|
47
50
|
export { default as TextControl } from './text-control';
|
|
48
51
|
export { default as Waiting } from './waiting';
|
|
49
52
|
export { default as WebPreview } from './web-preview';
|
|
50
53
|
export { default as Wizard } from './wizard';
|
|
54
|
+
export { WizardBanner } from './wizard';
|
|
51
55
|
export { default as withWizard } from './with-wizard';
|
|
52
56
|
export { default as withWizardScreen } from './with-wizard-screen';
|
|
53
57
|
export { default as Router } from './proxied-imports/router';
|
package/dist/esm/page/index.js
CHANGED
|
@@ -39,7 +39,7 @@ import './style.scss';
|
|
|
39
39
|
* inside its active tab panel — or, when no visible tab
|
|
40
40
|
* owns the route, as a sibling of the panels.
|
|
41
41
|
* @param {string} [props.className]
|
|
42
|
-
* @param {*} props.children
|
|
42
|
+
* @param {*} [props.children]
|
|
43
43
|
* @return {JSX.Element} Page component.
|
|
44
44
|
*/
|
|
45
45
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
3
|
+
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; }
|
|
4
|
+
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; }
|
|
5
|
+
/**
|
|
6
|
+
* Page Control.
|
|
7
|
+
*
|
|
8
|
+
* A single-page selector backed by an autocomplete search, for settings that point at a
|
|
9
|
+
* page without enumerating every page the site has.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* WordPress dependencies
|
|
14
|
+
*/
|
|
15
|
+
import { useEffect, useState } from '@wordpress/element';
|
|
16
|
+
import { decodeEntities } from '@wordpress/html-entities';
|
|
17
|
+
import { __ } from '@wordpress/i18n';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Internal dependencies
|
|
21
|
+
*/
|
|
22
|
+
import { AutocompleteWithSuggestions } from '../';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Render a single-page selector.
|
|
26
|
+
*
|
|
27
|
+
* @param {Object} props Component props.
|
|
28
|
+
* @param {string} props.label Field label.
|
|
29
|
+
* @param {string} props.help Help text.
|
|
30
|
+
* @param {boolean} props.disabled Whether the field is disabled (e.g. while saving).
|
|
31
|
+
* @param {Object} props.selected The saved page as `{ label, value }`, if any.
|
|
32
|
+
* @param {Function} props.onChange Called with the selected page ID, or '' when cleared.
|
|
33
|
+
*/
|
|
34
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
35
|
+
var PageControl = function PageControl(_ref) {
|
|
36
|
+
var label = _ref.label,
|
|
37
|
+
help = _ref.help,
|
|
38
|
+
disabled = _ref.disabled,
|
|
39
|
+
selected = _ref.selected,
|
|
40
|
+
_onChange = _ref.onChange;
|
|
41
|
+
// Titles arrive HTML-encoded from the server, while the autocomplete decodes the
|
|
42
|
+
// suggestions it fetches. Decode here so the saved page and the search results below
|
|
43
|
+
// it render the same way.
|
|
44
|
+
var normalize = function normalize(item) {
|
|
45
|
+
return item ? _objectSpread(_objectSpread({}, item), {}, {
|
|
46
|
+
label: decodeEntities(item.label)
|
|
47
|
+
}) : null;
|
|
48
|
+
};
|
|
49
|
+
var _useState = useState(normalize(selected)),
|
|
50
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
51
|
+
selectedItem = _useState2[0],
|
|
52
|
+
setSelectedItem = _useState2[1];
|
|
53
|
+
// Settings are fetched after this control first renders, so adopt the saved page
|
|
54
|
+
// whenever it arrives or changes rather than only on mount.
|
|
55
|
+
useEffect(function () {
|
|
56
|
+
setSelectedItem(normalize(selected));
|
|
57
|
+
}, [selected === null || selected === void 0 ? void 0 : selected.value, selected === null || selected === void 0 ? void 0 : selected.label]);
|
|
58
|
+
return /*#__PURE__*/_jsx("fieldset", {
|
|
59
|
+
disabled: disabled,
|
|
60
|
+
className: "newspack-page-control",
|
|
61
|
+
children: /*#__PURE__*/_jsx(AutocompleteWithSuggestions, {
|
|
62
|
+
label: label,
|
|
63
|
+
help: help,
|
|
64
|
+
postTypes: [{
|
|
65
|
+
slug: 'page',
|
|
66
|
+
label: __('Page', 'newspack-plugin')
|
|
67
|
+
}],
|
|
68
|
+
postTypeLabel: __('page', 'newspack-plugin'),
|
|
69
|
+
postTypeLabelPlural: __('pages', 'newspack-plugin'),
|
|
70
|
+
selectedItems: selectedItem ? [selectedItem] : [],
|
|
71
|
+
onChange: function onChange(items) {
|
|
72
|
+
if (disabled) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
var item = items && items.length ? items[items.length - 1] : null;
|
|
76
|
+
setSelectedItem(normalize(item));
|
|
77
|
+
_onChange(item ? String(item.value) : '');
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
export default PageControl;
|
|
@@ -17,13 +17,17 @@ import { CheckboxControl } from '@wordpress/components';
|
|
|
17
17
|
/**
|
|
18
18
|
* Internal dependencies
|
|
19
19
|
*/
|
|
20
|
-
import { ActionCard, Button, Grid, Notice, SelectControl, TextControl } from '../';
|
|
20
|
+
import { ActionCard, Button, Grid, Notice, PageControl, SelectControl, TextControl } from '../';
|
|
21
21
|
import './style.scss';
|
|
22
22
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
23
23
|
var isSelectControl = function isSelectControl(setting) {
|
|
24
24
|
return Array.isArray(setting.options) && setting.options.length;
|
|
25
25
|
};
|
|
26
26
|
var getControlComponent = function getControlComponent(setting) {
|
|
27
|
+
// An explicit `control` overrides the control implied by the setting's data type.
|
|
28
|
+
if ('page' === setting.control) {
|
|
29
|
+
return PageControl;
|
|
30
|
+
}
|
|
27
31
|
if (isSelectControl(setting)) {
|
|
28
32
|
return SelectControl;
|
|
29
33
|
}
|
|
@@ -84,6 +88,8 @@ var SettingsSection = function SettingsSection(props) {
|
|
|
84
88
|
};
|
|
85
89
|
})) || null,
|
|
86
90
|
value: setting.value,
|
|
91
|
+
// Only page controls consume `selected`; keep it off every other control's props.
|
|
92
|
+
selected: 'page' === setting.control ? setting.selected || null : null,
|
|
87
93
|
multiple: isSelectControl(setting) && setting.multiple ? true : null,
|
|
88
94
|
checked: setting.type === 'boolean' ? !!setting.value : null,
|
|
89
95
|
onChange: function onChange(value) {
|
|
@@ -122,7 +128,7 @@ var SettingsSection = function SettingsSection(props) {
|
|
|
122
128
|
onUpdate();
|
|
123
129
|
setSaveDisabled(true);
|
|
124
130
|
},
|
|
125
|
-
children: __('Save Settings', 'newspack')
|
|
131
|
+
children: __('Save Settings', 'newspack-plugin')
|
|
126
132
|
})),
|
|
127
133
|
children: (active || active === null) && /*#__PURE__*/_jsxs(Fragment, {
|
|
128
134
|
children: [(error === null || error === void 0 ? void 0 : error.message) && /*#__PURE__*/_jsx(Notice, {
|
|
@@ -36,6 +36,7 @@ import classnames from 'classnames';
|
|
|
36
36
|
* @property {boolean} [isWhite=false] - Indicates if the header should use a white theme.
|
|
37
37
|
* @property {boolean} [noMargin=false] - Indicates if the header should have no margin.
|
|
38
38
|
* @property {boolean} [pageHeader=false] - Indicates if the header is used as a page header.
|
|
39
|
+
* @property {string} [size='default'] - Size variant, either 'default' or 'small'. Scales the title, and the icon with it, independently of `pageHeader`.
|
|
39
40
|
* @property {string} title - The title of the section.
|
|
40
41
|
* @property {?string} [id=null] - Optional ID for the header element.
|
|
41
42
|
* @property {?string|Function|*} [children=null] - Optional children to display in the header.
|
|
@@ -67,6 +68,8 @@ var SectionHeader = function SectionHeader(_ref) {
|
|
|
67
68
|
noMargin = _ref$noMargin === void 0 ? false : _ref$noMargin,
|
|
68
69
|
_ref$pageHeader = _ref.pageHeader,
|
|
69
70
|
pageHeader = _ref$pageHeader === void 0 ? false : _ref$pageHeader,
|
|
71
|
+
_ref$size = _ref.size,
|
|
72
|
+
size = _ref$size === void 0 ? 'default' : _ref$size,
|
|
70
73
|
title = _ref.title,
|
|
71
74
|
_ref$id = _ref.id,
|
|
72
75
|
id = _ref$id === void 0 ? null : _ref$id,
|
|
@@ -93,7 +96,7 @@ var SectionHeader = function SectionHeader(_ref) {
|
|
|
93
96
|
}, 250);
|
|
94
97
|
}
|
|
95
98
|
}, []);
|
|
96
|
-
var classes = classnames('newspack-section-header', centered && 'newspack-section-header--is-centered', isWhite && 'newspack-section-header--is-white', noMargin && 'newspack-section-header--no-margin', pageHeader && 'newspack-section-header--page-header');
|
|
99
|
+
var classes = classnames('newspack-section-header', centered && 'newspack-section-header--is-centered', isWhite && 'newspack-section-header--is-white', noMargin && 'newspack-section-header--no-margin', pageHeader && 'newspack-section-header--page-header', size === 'small' && 'newspack-section-header--small');
|
|
97
100
|
|
|
98
101
|
// The breadcrumb `Page` owns the single page `<h1>`, so a `pageHeader` section
|
|
99
102
|
// is a secondary heading: its level follows `heading` (default 2). `pageHeader`
|
|
@@ -156,7 +159,7 @@ var SectionHeader = function SectionHeader(_ref) {
|
|
|
156
159
|
className: "newspack-section-header__icon",
|
|
157
160
|
children: /*#__PURE__*/_jsx(Icon, {
|
|
158
161
|
icon: icon,
|
|
159
|
-
size: 48
|
|
162
|
+
size: size === 'small' ? 24 : 48
|
|
160
163
|
})
|
|
161
164
|
}), backNav ? /*#__PURE__*/_jsxs(HStack, {
|
|
162
165
|
alignment: "left",
|
|
@@ -95,6 +95,31 @@
|
|
|
95
95
|
@include wp-mixins.heading-x-large();
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
&#{&}--small {
|
|
99
|
+
.newspack-section-header__title {
|
|
100
|
+
@include wp-mixins.heading-large();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
&__icon {
|
|
105
|
+
background-color: var(--wp-admin-theme-color-lighter-10);
|
|
106
|
+
border-radius: 100%;
|
|
107
|
+
display: grid;
|
|
108
|
+
height: 80px;
|
|
109
|
+
margin-bottom: 8px !important;
|
|
110
|
+
width: 80px;
|
|
111
|
+
place-items: center;
|
|
112
|
+
svg {
|
|
113
|
+
fill: var(--wp-admin-theme-color);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
&#{&}--small .newspack-section-header__icon {
|
|
118
|
+
height: wp-vars.$grid-unit-50;
|
|
119
|
+
margin-bottom: wp-vars.$grid-unit-05 !important;
|
|
120
|
+
width: wp-vars.$grid-unit-50;
|
|
121
|
+
}
|
|
122
|
+
|
|
98
123
|
&__title-container {
|
|
99
124
|
align-items: center;
|
|
100
125
|
display: flex;
|
|
@@ -154,17 +179,5 @@
|
|
|
154
179
|
> p {
|
|
155
180
|
text-align: center;
|
|
156
181
|
}
|
|
157
|
-
.newspack-section-header__icon {
|
|
158
|
-
background-color: var(--wp-admin-theme-color-lighter-10);
|
|
159
|
-
border-radius: 100%;
|
|
160
|
-
display: grid;
|
|
161
|
-
height: 80px;
|
|
162
|
-
margin-bottom: 8px !important;
|
|
163
|
-
width: 80px;
|
|
164
|
-
place-items: center;
|
|
165
|
-
svg {
|
|
166
|
-
fill: var(--wp-admin-theme-color);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
182
|
}
|
|
170
183
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TableCard
|
|
3
|
+
*
|
|
4
|
+
* The DataViews-in-a-card composition from @wordpress/ui. Which card edges the
|
|
5
|
+
* table touches follows upstream first/last-child rules, so it changes with the
|
|
6
|
+
* slots present — the README spells the combinations out.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* WordPress dependencies
|
|
11
|
+
*/
|
|
12
|
+
import { Card, Stack } from '@wordpress/ui';
|
|
13
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
+
/** `0` is a renderable slot value; the falsy leftovers of `cond && <X />` are not. */
|
|
15
|
+
var isRenderable = function isRenderable(node) {
|
|
16
|
+
return node !== undefined && node !== null && node !== '' && typeof node !== 'boolean';
|
|
17
|
+
};
|
|
18
|
+
export default function TableCard(_ref) {
|
|
19
|
+
var title = _ref.title,
|
|
20
|
+
titleId = _ref.titleId,
|
|
21
|
+
_ref$heading = _ref.heading,
|
|
22
|
+
heading = _ref$heading === void 0 ? 3 : _ref$heading,
|
|
23
|
+
actions = _ref.actions,
|
|
24
|
+
before = _ref.before,
|
|
25
|
+
after = _ref.after,
|
|
26
|
+
className = _ref.className,
|
|
27
|
+
children = _ref.children;
|
|
28
|
+
var TitleTag = "h".concat(heading);
|
|
29
|
+
var hasTitle = isRenderable(title);
|
|
30
|
+
var hasActions = isRenderable(actions);
|
|
31
|
+
return /*#__PURE__*/_jsxs(Card.Root, {
|
|
32
|
+
className: className,
|
|
33
|
+
children: [(hasTitle || hasActions) && /*#__PURE__*/_jsxs(Card.Header, {
|
|
34
|
+
render: hasActions ? /*#__PURE__*/_jsx(Stack, {
|
|
35
|
+
direction: "row",
|
|
36
|
+
justify: hasTitle ? 'space-between' : 'end',
|
|
37
|
+
align: "center"
|
|
38
|
+
}) : undefined,
|
|
39
|
+
children: [hasTitle && /*#__PURE__*/_jsx(Card.Title, {
|
|
40
|
+
render: /*#__PURE__*/_jsx(TitleTag, {
|
|
41
|
+
id: titleId
|
|
42
|
+
}),
|
|
43
|
+
children: title
|
|
44
|
+
}), hasActions && /*#__PURE__*/_jsx(Stack, {
|
|
45
|
+
direction: "row",
|
|
46
|
+
gap: "sm",
|
|
47
|
+
align: "center",
|
|
48
|
+
children: actions
|
|
49
|
+
})]
|
|
50
|
+
}), /*#__PURE__*/_jsxs(Card.Content, {
|
|
51
|
+
render: /*#__PURE__*/_jsx(Stack, {
|
|
52
|
+
direction: "column",
|
|
53
|
+
gap: "lg"
|
|
54
|
+
}),
|
|
55
|
+
children: [before, /*#__PURE__*/_jsx(Card.FullBleed, {
|
|
56
|
+
children: children
|
|
57
|
+
}), after]
|
|
58
|
+
})]
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import TableCard from '.';
|
|
10
|
+
|
|
11
|
+
// @wordpress/ui injects no CSS under Jest, so the layout this component exists
|
|
12
|
+
// for (full bleed, edge bumps, the content gap) is invisible here; slot order
|
|
13
|
+
// and heading semantics are the assertable contract.
|
|
14
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
15
|
+
describe('TableCard', function () {
|
|
16
|
+
it('renders the table children inside the card', function () {
|
|
17
|
+
render(/*#__PURE__*/_jsx(TableCard, {
|
|
18
|
+
children: /*#__PURE__*/_jsx("table", {
|
|
19
|
+
"aria-label": "Prices"
|
|
20
|
+
})
|
|
21
|
+
}));
|
|
22
|
+
expect(screen.getByRole('table', {
|
|
23
|
+
name: 'Prices'
|
|
24
|
+
})).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
it('renders the title as a real heading carrying the given id', function () {
|
|
27
|
+
var _render = render(/*#__PURE__*/_jsx(TableCard, {
|
|
28
|
+
title: "Price schedule",
|
|
29
|
+
titleId: "tc-title",
|
|
30
|
+
children: /*#__PURE__*/_jsx("table", {
|
|
31
|
+
"aria-label": "Prices"
|
|
32
|
+
})
|
|
33
|
+
})),
|
|
34
|
+
rerender = _render.rerender;
|
|
35
|
+
expect(screen.getByRole('heading', {
|
|
36
|
+
level: 3,
|
|
37
|
+
name: 'Price schedule'
|
|
38
|
+
})).toHaveAttribute('id', 'tc-title');
|
|
39
|
+
rerender(/*#__PURE__*/_jsx(TableCard, {
|
|
40
|
+
title: "Price schedule",
|
|
41
|
+
heading: 2,
|
|
42
|
+
children: /*#__PURE__*/_jsx("table", {
|
|
43
|
+
"aria-label": "Prices"
|
|
44
|
+
})
|
|
45
|
+
}));
|
|
46
|
+
expect(screen.getByRole('heading', {
|
|
47
|
+
level: 2,
|
|
48
|
+
name: 'Price schedule'
|
|
49
|
+
})).toBeInTheDocument();
|
|
50
|
+
rerender(/*#__PURE__*/_jsx(TableCard, {
|
|
51
|
+
children: /*#__PURE__*/_jsx("table", {
|
|
52
|
+
"aria-label": "Prices"
|
|
53
|
+
})
|
|
54
|
+
}));
|
|
55
|
+
expect(screen.queryByRole('heading')).not.toBeInTheDocument();
|
|
56
|
+
});
|
|
57
|
+
it('treats 0 as a renderable title and the empty string as none', function () {
|
|
58
|
+
var _render2 = render(/*#__PURE__*/_jsx(TableCard, {
|
|
59
|
+
title: 0,
|
|
60
|
+
children: /*#__PURE__*/_jsx("table", {
|
|
61
|
+
"aria-label": "Prices"
|
|
62
|
+
})
|
|
63
|
+
})),
|
|
64
|
+
rerender = _render2.rerender;
|
|
65
|
+
expect(screen.getByRole('heading', {
|
|
66
|
+
level: 3,
|
|
67
|
+
name: '0'
|
|
68
|
+
})).toBeInTheDocument();
|
|
69
|
+
rerender(/*#__PURE__*/_jsx(TableCard, {
|
|
70
|
+
title: "",
|
|
71
|
+
children: /*#__PURE__*/_jsx("table", {
|
|
72
|
+
"aria-label": "Prices"
|
|
73
|
+
})
|
|
74
|
+
}));
|
|
75
|
+
expect(screen.queryByRole('heading')).not.toBeInTheDocument();
|
|
76
|
+
});
|
|
77
|
+
it('renders header actions alongside the title', function () {
|
|
78
|
+
render(/*#__PURE__*/_jsx(TableCard, {
|
|
79
|
+
title: "Price Schedule",
|
|
80
|
+
actions: /*#__PURE__*/_jsx("button", {
|
|
81
|
+
children: "Add Price"
|
|
82
|
+
}),
|
|
83
|
+
children: /*#__PURE__*/_jsx("table", {
|
|
84
|
+
"aria-label": "Prices"
|
|
85
|
+
})
|
|
86
|
+
}));
|
|
87
|
+
var heading = screen.getByRole('heading', {
|
|
88
|
+
level: 3,
|
|
89
|
+
name: 'Price Schedule'
|
|
90
|
+
});
|
|
91
|
+
var action = screen.getByRole('button', {
|
|
92
|
+
name: 'Add Price'
|
|
93
|
+
});
|
|
94
|
+
expect(heading.parentElement).toContainElement(action);
|
|
95
|
+
});
|
|
96
|
+
it('renders a header for actions alone, and none for boolean slot leftovers', function () {
|
|
97
|
+
var _render3 = render(/*#__PURE__*/_jsx(TableCard, {
|
|
98
|
+
actions: /*#__PURE__*/_jsx("button", {
|
|
99
|
+
children: "Add Price"
|
|
100
|
+
}),
|
|
101
|
+
children: /*#__PURE__*/_jsx("table", {
|
|
102
|
+
"aria-label": "Prices"
|
|
103
|
+
})
|
|
104
|
+
})),
|
|
105
|
+
rerender = _render3.rerender;
|
|
106
|
+
expect(screen.getByRole('button', {
|
|
107
|
+
name: 'Add Price'
|
|
108
|
+
})).toBeInTheDocument();
|
|
109
|
+
expect(screen.queryByRole('heading')).not.toBeInTheDocument();
|
|
110
|
+
rerender(/*#__PURE__*/_jsx(TableCard, {
|
|
111
|
+
title: false,
|
|
112
|
+
actions: false,
|
|
113
|
+
children: /*#__PURE__*/_jsx("table", {
|
|
114
|
+
"aria-label": "Prices"
|
|
115
|
+
})
|
|
116
|
+
}));
|
|
117
|
+
expect(screen.queryByRole('heading')).not.toBeInTheDocument();
|
|
118
|
+
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
|
119
|
+
});
|
|
120
|
+
it('orders before, table, after within the card body', function () {
|
|
121
|
+
render(/*#__PURE__*/_jsx(TableCard, {
|
|
122
|
+
before: /*#__PURE__*/_jsx("p", {
|
|
123
|
+
children: "Stats row"
|
|
124
|
+
}),
|
|
125
|
+
after: /*#__PURE__*/_jsx("button", {
|
|
126
|
+
children: "See More"
|
|
127
|
+
}),
|
|
128
|
+
children: /*#__PURE__*/_jsx("table", {
|
|
129
|
+
"aria-label": "Prices"
|
|
130
|
+
})
|
|
131
|
+
}));
|
|
132
|
+
var before = screen.getByText('Stats row');
|
|
133
|
+
var after = screen.getByRole('button', {
|
|
134
|
+
name: 'See More'
|
|
135
|
+
});
|
|
136
|
+
var table = screen.getByRole('table', {
|
|
137
|
+
name: 'Prices'
|
|
138
|
+
});
|
|
139
|
+
// Walk the table up to the card body's direct child, so extra upstream
|
|
140
|
+
// wrappers cannot silently repoint the comparison.
|
|
141
|
+
var body = before.parentElement;
|
|
142
|
+
var tableWrap = table;
|
|
143
|
+
while (tableWrap.parentElement !== body) {
|
|
144
|
+
tableWrap = tableWrap.parentElement;
|
|
145
|
+
}
|
|
146
|
+
var order = Array.from(body.children);
|
|
147
|
+
expect(order.indexOf(before)).toBeLessThan(order.indexOf(tableWrap));
|
|
148
|
+
expect(order.indexOf(tableWrap)).toBeLessThan(order.indexOf(after));
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -18,6 +18,14 @@
|
|
|
18
18
|
|
|
19
19
|
color: wp-colors.$gray-900;
|
|
20
20
|
|
|
21
|
+
// Full-width dividers span 100vw, which overspills the admin content area
|
|
22
|
+
// (admin menu offset + scrollbar) and triggers horizontal scrolling; clip
|
|
23
|
+
// (not hidden) so no scroll container is created. Scoped off __content,
|
|
24
|
+
// which can be a constrained centered column.
|
|
25
|
+
&:not(.newspack-wizard__content) {
|
|
26
|
+
overflow-x: clip;
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
// Headings
|
|
22
30
|
h1,
|
|
23
31
|
h2,
|