newspack-components 4.7.0 → 4.8.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.
- package/CHANGELOG.md +20 -0
- package/DEVELOPMENT.md +42 -21
- package/dist/cjs/_mixins.scss +13 -0
- package/dist/cjs/_variables.scss +12 -0
- package/dist/cjs/action-card/index.js +18 -10
- package/dist/cjs/action-card/index.test.js +91 -0
- package/dist/cjs/action-card/style.scss +2 -40
- package/dist/cjs/button/index.js +8 -3
- package/dist/cjs/button/index.test.js +61 -5
- package/dist/cjs/card/core-card.js +0 -2
- package/dist/cjs/card/style-core.scss +7 -5
- package/dist/cjs/card-feature/index.js +75 -61
- package/dist/cjs/card-feature/index.test.js +396 -54
- package/dist/cjs/card-feature/style.scss +20 -31
- package/dist/cjs/card-form/index.js +5 -5
- package/dist/cjs/card-sortable-list/index.js +5 -3
- package/dist/cjs/card-sortable-list/index.test.js +50 -0
- package/dist/cjs/collapsible-group/context.js +19 -0
- package/dist/cjs/collapsible-group/index.js +24 -0
- package/dist/cjs/collapsible-group/index.test.js +215 -0
- package/dist/cjs/collapsible-group/item.js +61 -0
- package/dist/cjs/collapsible-group/root.js +62 -0
- package/dist/cjs/collapsible-group/style.scss +47 -0
- package/dist/cjs/collapsible-group/types.js +5 -0
- package/dist/cjs/empty-state/actions.js +45 -0
- package/dist/cjs/empty-state/context.js +41 -0
- package/dist/cjs/empty-state/header.js +66 -0
- package/dist/cjs/empty-state/index.js +31 -0
- package/dist/cjs/empty-state/index.test.js +303 -0
- package/dist/cjs/empty-state/root.js +56 -0
- package/dist/cjs/empty-state/style.scss +51 -0
- package/dist/cjs/empty-state/types.js +5 -0
- package/dist/cjs/index.js +34 -20
- package/dist/cjs/info-button/index.js +59 -43
- package/dist/cjs/info-button/index.test.js +199 -0
- package/dist/cjs/info-button/style.scss +41 -9
- package/dist/cjs/modal/style.scss +12 -12
- package/dist/cjs/newspack-icon/style.scss +4 -4
- package/dist/cjs/notice/style.scss +3 -3
- package/dist/cjs/page/style.scss +16 -16
- package/dist/cjs/plugin-installer/index.js +5 -9
- package/dist/cjs/plugin-installer/style.scss +3 -14
- package/dist/cjs/section-header/index.js +31 -18
- package/dist/cjs/section-header/index.test.js +40 -0
- package/dist/cjs/section-header/style.scss +22 -6
- package/dist/cjs/settings/DateRangeSetting.js +309 -0
- package/dist/cjs/settings/SettingsSection.js +19 -5
- package/dist/cjs/settings/SettingsSection.test.js +49 -0
- package/dist/cjs/settings/index.js +3 -1
- package/dist/cjs/settings/style.scss +40 -7
- package/dist/cjs/stat-card/body.js +41 -0
- package/dist/cjs/stat-card/constants.js +11 -0
- package/dist/cjs/stat-card/context.js +54 -0
- package/dist/cjs/stat-card/delta.js +91 -0
- package/dist/cjs/stat-card/footer.js +72 -0
- package/dist/cjs/stat-card/index.js +47 -0
- package/dist/cjs/stat-card/index.test.js +938 -0
- package/dist/cjs/stat-card/label.js +66 -0
- package/dist/cjs/stat-card/root.js +69 -0
- package/dist/cjs/stat-card/secondary.js +38 -0
- package/dist/cjs/stat-card/style.scss +89 -0
- package/dist/cjs/stat-card/types.js +5 -0
- package/dist/cjs/stat-card/value.js +78 -0
- package/dist/cjs/status-indicator/index.js +66 -0
- package/dist/cjs/status-indicator/index.test.js +101 -0
- package/dist/cjs/status-indicator/statuses.js +39 -0
- package/dist/cjs/status-indicator/style.scss +9 -0
- package/dist/cjs/status-indicator/types.js +5 -0
- package/dist/cjs/style.scss +9 -9
- package/dist/cjs/tabbed-navigation/style.scss +6 -4
- package/dist/cjs/types.js +5 -0
- package/dist/cjs/web-preview/style.scss +11 -11
- package/dist/cjs/with-wizard/style.scss +10 -10
- package/dist/cjs/with-wizard-screen/style.scss +9 -9
- package/dist/cjs/wizard/index.js +33 -4
- package/dist/cjs/wizard/store/index.js +23 -1
- package/dist/esm/_mixins.scss +13 -0
- package/dist/esm/_variables.scss +12 -0
- package/dist/esm/action-card/index.js +18 -10
- package/dist/esm/action-card/index.test.js +89 -0
- package/dist/esm/action-card/style.scss +2 -40
- package/dist/esm/button/index.js +9 -4
- package/dist/esm/button/index.test.js +63 -5
- package/dist/esm/card/core-card.js +0 -2
- package/dist/esm/card/style-core.scss +7 -5
- package/dist/esm/card-feature/index.js +78 -62
- package/dist/esm/card-feature/index.test.js +397 -55
- package/dist/esm/card-feature/style.scss +20 -31
- package/dist/esm/card-form/index.js +5 -5
- package/dist/esm/card-sortable-list/index.js +6 -4
- package/dist/esm/card-sortable-list/index.test.js +46 -0
- package/dist/esm/collapsible-group/context.js +13 -0
- package/dist/esm/collapsible-group/index.js +17 -0
- package/dist/esm/collapsible-group/index.test.js +215 -0
- package/dist/esm/collapsible-group/item.js +53 -0
- package/dist/esm/collapsible-group/root.js +54 -0
- package/dist/esm/collapsible-group/style.scss +47 -0
- package/dist/esm/collapsible-group/types.js +1 -0
- package/dist/esm/empty-state/actions.js +37 -0
- package/dist/esm/empty-state/context.js +35 -0
- package/dist/esm/empty-state/header.js +58 -0
- package/dist/esm/empty-state/index.js +23 -0
- package/dist/esm/empty-state/index.test.js +299 -0
- package/dist/esm/empty-state/root.js +56 -0
- package/dist/esm/empty-state/style.scss +51 -0
- package/dist/esm/empty-state/types.js +1 -0
- package/dist/esm/index.js +4 -2
- package/dist/esm/info-button/index.js +54 -43
- package/dist/esm/info-button/index.test.js +195 -0
- package/dist/esm/info-button/style.scss +41 -9
- package/dist/esm/modal/style.scss +12 -12
- package/dist/esm/newspack-icon/style.scss +4 -4
- package/dist/esm/notice/style.scss +3 -3
- package/dist/esm/page/style.scss +16 -16
- package/dist/esm/plugin-installer/index.js +5 -9
- package/dist/esm/plugin-installer/style.scss +3 -14
- package/dist/esm/section-header/index.js +27 -14
- package/dist/esm/section-header/index.test.js +36 -0
- package/dist/esm/section-header/style.scss +22 -6
- package/dist/esm/settings/DateRangeSetting.js +304 -0
- package/dist/esm/settings/SettingsSection.js +16 -3
- package/dist/esm/settings/SettingsSection.test.js +45 -0
- package/dist/esm/settings/index.js +3 -1
- package/dist/esm/settings/style.scss +40 -7
- package/dist/esm/stat-card/body.js +36 -0
- package/dist/esm/stat-card/constants.js +5 -0
- package/dist/esm/stat-card/context.js +48 -0
- package/dist/esm/stat-card/delta.js +86 -0
- package/dist/esm/stat-card/footer.js +67 -0
- package/dist/esm/stat-card/index.js +33 -0
- package/dist/esm/stat-card/index.test.js +939 -0
- package/dist/esm/stat-card/label.js +61 -0
- package/dist/esm/stat-card/root.js +64 -0
- package/dist/esm/stat-card/secondary.js +33 -0
- package/dist/esm/stat-card/style.scss +89 -0
- package/dist/esm/stat-card/types.js +1 -0
- package/dist/esm/stat-card/value.js +73 -0
- package/dist/esm/status-indicator/index.js +52 -0
- package/dist/esm/status-indicator/index.test.js +96 -0
- package/dist/esm/status-indicator/statuses.js +33 -0
- package/dist/esm/status-indicator/style.scss +9 -0
- package/dist/esm/status-indicator/types.js +1 -0
- package/dist/esm/style.scss +9 -9
- package/dist/esm/tabbed-navigation/style.scss +6 -4
- package/dist/esm/types.js +1 -0
- package/dist/esm/web-preview/style.scss +11 -11
- package/dist/esm/with-wizard/style.scss +10 -10
- package/dist/esm/with-wizard-screen/style.scss +9 -9
- package/dist/esm/wizard/index.js +38 -7
- package/dist/esm/wizard/store/index.js +23 -1
- package/package.json +1 -1
- package/src/_mixins.scss +13 -0
- package/src/_variables.scss +12 -0
- package/src/action-card/action-card.d.ts +3 -2
- package/src/action-card/index.js +12 -12
- package/src/action-card/index.test.js +56 -0
- package/src/action-card/style.scss +2 -40
- package/src/button/index.test.js +52 -5
- package/src/button/index.tsx +9 -4
- package/src/card/core-card.js +0 -2
- package/src/card/style-core.scss +7 -5
- package/src/card-feature/README.md +68 -27
- package/src/card-feature/index.test.js +242 -27
- package/src/card-feature/index.tsx +77 -67
- package/src/card-feature/style.scss +20 -31
- package/src/card-form/README.md +10 -10
- package/src/card-form/index.tsx +4 -8
- package/src/card-sortable-list/README.md +4 -5
- package/src/card-sortable-list/index.test.js +29 -0
- package/src/card-sortable-list/index.tsx +7 -4
- package/src/collapsible-group/README.md +50 -0
- package/src/collapsible-group/context.ts +13 -0
- package/src/collapsible-group/index.test.js +168 -0
- package/src/collapsible-group/index.tsx +17 -0
- package/src/collapsible-group/item.tsx +42 -0
- package/src/collapsible-group/root.tsx +42 -0
- package/src/collapsible-group/style.scss +47 -0
- package/src/collapsible-group/types.ts +22 -0
- package/src/drawer/README.md +2 -2
- package/src/empty-state/README.md +165 -0
- package/src/empty-state/actions.tsx +37 -0
- package/src/empty-state/context.ts +40 -0
- package/src/empty-state/header.tsx +48 -0
- package/src/empty-state/index.test.js +247 -0
- package/src/empty-state/index.tsx +22 -0
- package/src/empty-state/root.tsx +39 -0
- package/src/empty-state/style.scss +51 -0
- package/src/empty-state/types.ts +33 -0
- package/src/index.js +4 -2
- package/src/info-button/README.md +115 -0
- package/src/info-button/index.test.js +163 -0
- package/src/info-button/index.tsx +64 -0
- package/src/info-button/style.scss +41 -9
- package/src/modal/style.scss +12 -12
- package/src/newspack-icon/style.scss +4 -4
- package/src/notice/style.scss +3 -3
- package/src/page/style.scss +16 -16
- package/src/plugin-installer/index.js +3 -9
- package/src/plugin-installer/style.scss +3 -14
- package/src/section-header/index.js +27 -14
- package/src/section-header/index.test.js +28 -0
- package/src/section-header/style.scss +22 -6
- package/src/settings/DateRangeSetting.js +266 -0
- package/src/settings/SettingsSection.js +24 -4
- package/src/settings/SettingsSection.test.js +45 -0
- package/src/settings/index.js +2 -0
- package/src/settings/style.scss +40 -7
- package/src/stat-card/README.md +434 -0
- package/src/stat-card/body.tsx +28 -0
- package/src/stat-card/constants.ts +5 -0
- package/src/stat-card/context.ts +53 -0
- package/src/stat-card/delta.tsx +88 -0
- package/src/stat-card/footer.tsx +68 -0
- package/src/stat-card/index.test.js +818 -0
- package/src/stat-card/index.tsx +48 -0
- package/src/stat-card/label.tsx +61 -0
- package/src/stat-card/root.tsx +40 -0
- package/src/stat-card/secondary.tsx +27 -0
- package/src/stat-card/style.scss +89 -0
- package/src/stat-card/types.ts +84 -0
- package/src/stat-card/value.tsx +72 -0
- package/src/status-indicator/README.md +123 -0
- package/src/status-indicator/index.test.js +70 -0
- package/src/status-indicator/index.tsx +39 -0
- package/src/status-indicator/statuses.ts +35 -0
- package/src/status-indicator/style.scss +9 -0
- package/src/status-indicator/types.ts +28 -0
- package/src/style.scss +9 -9
- package/src/tabbed-navigation/style.scss +6 -4
- package/src/types.ts +25 -0
- package/src/web-preview/style.scss +11 -11
- package/src/with-wizard/style.scss +10 -10
- package/src/with-wizard-screen/style.scss +9 -9
- package/src/wizard/index.js +49 -7
- package/src/wizard/store/index.js +26 -1
- package/dist/cjs/accordion/index.js +0 -72
- package/dist/cjs/accordion/index.test.js +0 -80
- package/dist/cjs/accordion/style.scss +0 -35
- package/dist/cjs/badge/index.js +0 -32
- package/dist/cjs/badge/style.scss +0 -36
- package/dist/esm/accordion/index.js +0 -63
- package/dist/esm/accordion/index.test.js +0 -79
- package/dist/esm/accordion/style.scss +0 -35
- package/dist/esm/badge/index.js +0 -24
- package/dist/esm/badge/style.scss +0 -36
- package/src/accordion/index.js +0 -46
- package/src/accordion/index.test.js +0 -62
- package/src/accordion/style.scss +0 -35
- package/src/badge/index.tsx +0 -26
- package/src/badge/style.scss +0 -36
- package/src/info-button/index.js +0 -38
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
4
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
5
|
+
var _excluded = ["start", "end", "onChange", "label"];
|
|
6
|
+
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; }
|
|
7
|
+
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; }
|
|
8
|
+
/**
|
|
9
|
+
* WordPress dependencies.
|
|
10
|
+
*/
|
|
11
|
+
import { Fragment, useEffect, useState } from '@wordpress/element';
|
|
12
|
+
import { __, _n, sprintf } from '@wordpress/i18n';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Internal dependencies.
|
|
16
|
+
*/
|
|
17
|
+
import { SelectControl, TextControl } from '../';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A bound is stored as { type: 'absolute', date } or { type: 'relative', days },
|
|
21
|
+
* where days is negative for the past and positive for the future. The UI splits
|
|
22
|
+
* the relative case in two so a publisher never has to type a minus sign.
|
|
23
|
+
*/
|
|
24
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
25
|
+
var BOUND_TYPES = [{
|
|
26
|
+
label: __('Any', 'newspack-plugin'),
|
|
27
|
+
value: ''
|
|
28
|
+
}, {
|
|
29
|
+
label: __('Date', 'newspack-plugin'),
|
|
30
|
+
value: 'absolute'
|
|
31
|
+
}, {
|
|
32
|
+
label: __('Days ago', 'newspack-plugin'),
|
|
33
|
+
value: 'past'
|
|
34
|
+
}, {
|
|
35
|
+
label: __('Days from now', 'newspack-plugin'),
|
|
36
|
+
value: 'future'
|
|
37
|
+
}];
|
|
38
|
+
|
|
39
|
+
// Upper magnitude for a relative bound: ~273 years, far inside Date's range,
|
|
40
|
+
// and it keeps the resolved year four digits so the matcher's lexicographic
|
|
41
|
+
// compare stays valid. Import and REST bypass this input, so the matcher still
|
|
42
|
+
// fails closed on larger offsets — this just turns a wild typo into
|
|
43
|
+
// browser-level validation instead of a segment that matches nobody.
|
|
44
|
+
var MAX_RELATIVE_DAYS = 100000;
|
|
45
|
+
|
|
46
|
+
// Today as YYYY-MM-DD in local time. toISOString() converts to UTC and would give
|
|
47
|
+
// the wrong day for anyone west of Greenwich.
|
|
48
|
+
var today = function today() {
|
|
49
|
+
var date = new Date();
|
|
50
|
+
var month = String(date.getMonth() + 1).padStart(2, '0');
|
|
51
|
+
var day = String(date.getDate()).padStart(2, '0');
|
|
52
|
+
return "".concat(date.getFullYear(), "-").concat(month, "-").concat(day);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Zero days is the same day either way, and normalizes to "Days ago".
|
|
56
|
+
var boundTypeOf = function boundTypeOf(bound) {
|
|
57
|
+
if (!bound || !bound.type) {
|
|
58
|
+
return '';
|
|
59
|
+
}
|
|
60
|
+
if ('absolute' === bound.type) {
|
|
61
|
+
return 'absolute';
|
|
62
|
+
}
|
|
63
|
+
return bound.days > 0 ? 'future' : 'past';
|
|
64
|
+
};
|
|
65
|
+
var boundValueOf = function boundValueOf(bound) {
|
|
66
|
+
if (!bound) {
|
|
67
|
+
return '';
|
|
68
|
+
}
|
|
69
|
+
return 'absolute' === bound.type ? bound.date || '' : String(Math.abs(bound.days || 0));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// `seed` is set only when the publisher picks a bound type, so a newly chosen
|
|
73
|
+
// bound starts on a usable value instead of being half-filled. It must stay off
|
|
74
|
+
// for edits to the value itself: a date input reports '' both when cleared and
|
|
75
|
+
// transiently while its value is being retyped, and substituting today() there
|
|
76
|
+
// would silently move a saved window out from under the publisher.
|
|
77
|
+
var makeBound = function makeBound(type, rawValue) {
|
|
78
|
+
var seed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
79
|
+
if ('absolute' === type) {
|
|
80
|
+
var date = rawValue || (seed ? today() : '');
|
|
81
|
+
// An absolute bound with no date is not a bound: emitted and saved, it
|
|
82
|
+
// would reach the matcher as a bound it can only reject, silently
|
|
83
|
+
// shrinking the segment to nobody. The row itself stays open — the
|
|
84
|
+
// control falls back to the chosen bound type while the bound is absent.
|
|
85
|
+
return date ? {
|
|
86
|
+
type: 'absolute',
|
|
87
|
+
date: date
|
|
88
|
+
} : undefined;
|
|
89
|
+
}
|
|
90
|
+
// A cleared magnitude mirrors a cleared date: no bound at all, not a
|
|
91
|
+
// zero-day bound — { days: 0 } would quietly move the window's edge to
|
|
92
|
+
// today and snap the emptied input back to '0' mid-retype. An explicit
|
|
93
|
+
// '0' is falsy-but-present, so the check is on emptiness, not truthiness.
|
|
94
|
+
var raw = '' === rawValue && seed ? '0' : rawValue;
|
|
95
|
+
if ('' === raw) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
// Number() rather than parseInt(): a number input accepts scientific
|
|
99
|
+
// notation, and parseInt( '1e2' ) reads it as 1 instead of 100.
|
|
100
|
+
var days = Math.abs(Math.trunc(Number(raw)) || 0);
|
|
101
|
+
return {
|
|
102
|
+
type: 'relative',
|
|
103
|
+
days: 'future' === type ? days : -days
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
var DateRangeBound = function DateRangeBound(_ref) {
|
|
107
|
+
var label = _ref.label,
|
|
108
|
+
testId = _ref.testId,
|
|
109
|
+
bound = _ref.bound,
|
|
110
|
+
_onChange = _ref.onChange;
|
|
111
|
+
// The bound type is normally derived from the stored value, but a zero-magnitude
|
|
112
|
+
// relative bound (`days: 0`) is the same day whether the publisher chose "ago" or
|
|
113
|
+
// "from now" — the sign can't tell them apart. `chosenType` records the direction
|
|
114
|
+
// to fall back on while the bound stays ambiguous, and starts out `null` — no
|
|
115
|
+
// direction known yet — since the bound this control receives can arrive
|
|
116
|
+
// asynchronously (e.g. an existing segment fetched after first mount).
|
|
117
|
+
var _useState = useState(null),
|
|
118
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
119
|
+
chosenType = _useState2[0],
|
|
120
|
+
setChosenType = _useState2[1];
|
|
121
|
+
var derivedType = boundTypeOf(bound);
|
|
122
|
+
var isAmbiguous = 'relative' === (bound === null || bound === void 0 ? void 0 : bound.type) && 0 === bound.days;
|
|
123
|
+
|
|
124
|
+
// Keep chosenType in step with any unambiguous bound — freshly chosen by the
|
|
125
|
+
// publisher in this control, or loaded from storage — so the direction survives
|
|
126
|
+
// if the value is later cleared down to an ambiguous zero. A bound of '' (no
|
|
127
|
+
// bound at all, e.g. before an async load resolves) doesn't establish a
|
|
128
|
+
// direction; only a real, unambiguous one does.
|
|
129
|
+
useEffect(function () {
|
|
130
|
+
if ('' !== derivedType && !isAmbiguous) {
|
|
131
|
+
setChosenType(derivedType);
|
|
132
|
+
}
|
|
133
|
+
}, [derivedType, isAmbiguous]);
|
|
134
|
+
|
|
135
|
+
// Also fall back while there is no bound at all: a cleared date emits no
|
|
136
|
+
// bound (see makeBound), and deriving from the absent value would collapse
|
|
137
|
+
// the row to "Any" mid-edit instead of leaving the empty input for retyping.
|
|
138
|
+
var type = null !== chosenType && (isAmbiguous || !bound) ? chosenType : derivedType;
|
|
139
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
140
|
+
className: "newspack-settings__date-range-bound",
|
|
141
|
+
children: [/*#__PURE__*/_jsx(SelectControl, {
|
|
142
|
+
label: label
|
|
143
|
+
// Without this the select renders at 32px while the sibling input is
|
|
144
|
+
// 40px, so the two boxes in a row don't match.
|
|
145
|
+
,
|
|
146
|
+
__next40pxDefaultSize: true,
|
|
147
|
+
value: type,
|
|
148
|
+
options: BOUND_TYPES,
|
|
149
|
+
onChange: function onChange(nextType) {
|
|
150
|
+
setChosenType(nextType);
|
|
151
|
+
_onChange(nextType ? makeBound(nextType, '', true) : undefined);
|
|
152
|
+
}
|
|
153
|
+
}), '' !== type && /*#__PURE__*/_jsx(TextControl, {
|
|
154
|
+
"data-testid": testId
|
|
155
|
+
// The visible label sits on the sibling select, so without one here
|
|
156
|
+
// the input has no accessible name at all. Composed with the row
|
|
157
|
+
// label so the two bound rows' inputs stay distinguishable — two
|
|
158
|
+
// bare "Days" spin buttons announce identically.
|
|
159
|
+
,
|
|
160
|
+
label: 'absolute' === type ?
|
|
161
|
+
// translators: %s: the bound row label (From/To).
|
|
162
|
+
sprintf(__('%s date', 'newspack-plugin'), label) :
|
|
163
|
+
// translators: %s: the bound row label (From/To).
|
|
164
|
+
sprintf(__('%s days', 'newspack-plugin'), label),
|
|
165
|
+
hideLabelFromVision: true,
|
|
166
|
+
type: 'absolute' === type ? 'date' : 'number',
|
|
167
|
+
min: 'absolute' === type ? undefined : 0,
|
|
168
|
+
max: 'absolute' === type ? undefined : MAX_RELATIVE_DAYS,
|
|
169
|
+
value: boundValueOf(bound),
|
|
170
|
+
onChange: function onChange(nextValue) {
|
|
171
|
+
return _onChange(makeBound(type, nextValue));
|
|
172
|
+
}
|
|
173
|
+
})]
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Rolling windows offered as one-click presets. Each is the trailing window
|
|
179
|
+
* ending today, i.e. `start: -N days`, `end: today` — the same shape the custom
|
|
180
|
+
* rows produce, so a preset is a shortcut rather than a separate stored format.
|
|
181
|
+
*/
|
|
182
|
+
var PRESET_DAYS = [7, 30, 365];
|
|
183
|
+
var PRESETS = [{
|
|
184
|
+
label: __('Any', 'newspack-plugin'),
|
|
185
|
+
value: ''
|
|
186
|
+
}].concat(_toConsumableArray(PRESET_DAYS.map(function (days) {
|
|
187
|
+
return {
|
|
188
|
+
label: sprintf(_n('Last %d day', 'Last %d days', days, 'newspack-plugin'), days),
|
|
189
|
+
value: String(days)
|
|
190
|
+
};
|
|
191
|
+
})), [{
|
|
192
|
+
label: __('Custom', 'newspack-plugin'),
|
|
193
|
+
value: 'custom'
|
|
194
|
+
}]);
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Which preset a stored range corresponds to.
|
|
198
|
+
*
|
|
199
|
+
* @param {*} start The start bound.
|
|
200
|
+
* @param {*} end The end bound.
|
|
201
|
+
* @return {string} A PRESETS value: '' when unbounded, the day count when the
|
|
202
|
+
* range is exactly one of the presets, otherwise 'custom'.
|
|
203
|
+
*/
|
|
204
|
+
var presetOf = function presetOf(start, end) {
|
|
205
|
+
if (!start && !end) {
|
|
206
|
+
return '';
|
|
207
|
+
}
|
|
208
|
+
if (start && end && 'relative' === start.type && 'relative' === end.type && 0 === end.days && start.days < 0 && PRESET_DAYS.includes(Math.abs(start.days))) {
|
|
209
|
+
return String(Math.abs(start.days));
|
|
210
|
+
}
|
|
211
|
+
return 'custom';
|
|
212
|
+
};
|
|
213
|
+
var DateRangeSetting = function DateRangeSetting(_ref2) {
|
|
214
|
+
var start = _ref2.start,
|
|
215
|
+
end = _ref2.end,
|
|
216
|
+
onChange = _ref2.onChange,
|
|
217
|
+
label = _ref2.label,
|
|
218
|
+
props = _objectWithoutProperties(_ref2, _excluded);
|
|
219
|
+
var derivedPreset = presetOf(start, end);
|
|
220
|
+
// A range that matches a preset is indistinguishable from the same range built
|
|
221
|
+
// by hand, and an empty one is indistinguishable from "Custom, nothing filled
|
|
222
|
+
// in yet" — so a deliberate Custom choice has to be remembered rather than
|
|
223
|
+
// re-derived. Starts null: nothing chosen yet, so the stored value decides.
|
|
224
|
+
var _useState3 = useState(null),
|
|
225
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
226
|
+
chosenPreset = _useState4[0],
|
|
227
|
+
setChosenPreset = _useState4[1];
|
|
228
|
+
|
|
229
|
+
// Custom explains any range, so it is never overridden by what the value looks
|
|
230
|
+
// like. Every other selection stays in step with a range that arrived from
|
|
231
|
+
// storage or changed elsewhere.
|
|
232
|
+
useEffect(function () {
|
|
233
|
+
setChosenPreset(function (chosen) {
|
|
234
|
+
return 'custom' === chosen ? chosen : derivedPreset;
|
|
235
|
+
});
|
|
236
|
+
}, [derivedPreset]);
|
|
237
|
+
var preset = null === chosenPreset ? derivedPreset : chosenPreset;
|
|
238
|
+
|
|
239
|
+
// Both keys are always emitted: the consumer merges the value into the stored
|
|
240
|
+
// criterion, so omitting one would leave a stale bound behind. An empty range
|
|
241
|
+
// emits nothing at all, which drops the criterion — an empty `{}` would reach
|
|
242
|
+
// the matcher as "no bounds to check" and match every reader with a date.
|
|
243
|
+
var changeRange = function changeRange(nextStart, nextEnd) {
|
|
244
|
+
return onChange(nextStart || nextEnd ? {
|
|
245
|
+
start: nextStart,
|
|
246
|
+
end: nextEnd
|
|
247
|
+
} : undefined);
|
|
248
|
+
};
|
|
249
|
+
var changePreset = function changePreset(next) {
|
|
250
|
+
setChosenPreset(next);
|
|
251
|
+
if ('custom' === next) {
|
|
252
|
+
// Keep the current range so the rows open on what the preset meant.
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if ('' === next) {
|
|
256
|
+
onChange(undefined);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
changeRange({
|
|
260
|
+
type: 'relative',
|
|
261
|
+
days: -Number(next)
|
|
262
|
+
}, {
|
|
263
|
+
type: 'relative',
|
|
264
|
+
days: 0
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
return /*#__PURE__*/_jsxs("div", _objectSpread(_objectSpread({}, props), {}, {
|
|
268
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
269
|
+
className: "newspack-settings__date-range-preset",
|
|
270
|
+
children: /*#__PURE__*/_jsx(SelectControl
|
|
271
|
+
// The criterion's own name is the visible heading of the section
|
|
272
|
+
// this renders into, so a visible label here would just repeat it —
|
|
273
|
+
// but the heading is a bare span with no label association, so the
|
|
274
|
+
// consumer-passed name is what keeps several date criteria's preset
|
|
275
|
+
// selectors distinguishable to a screen reader.
|
|
276
|
+
, {
|
|
277
|
+
label: label || __('Date range', 'newspack-plugin'),
|
|
278
|
+
hideLabelFromVision: true,
|
|
279
|
+
__next40pxDefaultSize: true,
|
|
280
|
+
"data-testid": "date-range-preset",
|
|
281
|
+
value: preset,
|
|
282
|
+
options: PRESETS,
|
|
283
|
+
onChange: changePreset
|
|
284
|
+
})
|
|
285
|
+
}), 'custom' === preset && /*#__PURE__*/_jsxs(Fragment, {
|
|
286
|
+
children: [/*#__PURE__*/_jsx(DateRangeBound, {
|
|
287
|
+
label: __('From', 'newspack-plugin'),
|
|
288
|
+
testId: "date-range-start-value",
|
|
289
|
+
bound: start,
|
|
290
|
+
onChange: function onChange(nextStart) {
|
|
291
|
+
return changeRange(nextStart, end);
|
|
292
|
+
}
|
|
293
|
+
}), /*#__PURE__*/_jsx(DateRangeBound, {
|
|
294
|
+
label: __('To', 'newspack-plugin'),
|
|
295
|
+
testId: "date-range-end-value",
|
|
296
|
+
bound: end,
|
|
297
|
+
onChange: function onChange(nextEnd) {
|
|
298
|
+
return changeRange(start, nextEnd);
|
|
299
|
+
}
|
|
300
|
+
})]
|
|
301
|
+
})]
|
|
302
|
+
}));
|
|
303
|
+
};
|
|
304
|
+
export default DateRangeSetting;
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies.
|
|
3
|
+
*/
|
|
4
|
+
import { __, sprintf } from '@wordpress/i18n';
|
|
5
|
+
import { Stack } from '@wordpress/ui';
|
|
6
|
+
|
|
1
7
|
/**
|
|
2
8
|
* Internal dependencies.
|
|
3
9
|
*/
|
|
4
|
-
import
|
|
10
|
+
import Grid from '../grid';
|
|
11
|
+
import InfoButton from '../info-button';
|
|
5
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
6
13
|
var SettingSection = function SettingSection(_ref) {
|
|
7
14
|
var title = _ref.title,
|
|
@@ -11,12 +18,18 @@ var SettingSection = function SettingSection(_ref) {
|
|
|
11
18
|
columns: 1,
|
|
12
19
|
gutter: 8,
|
|
13
20
|
className: "newspack-settings__section",
|
|
14
|
-
children: [/*#__PURE__*/_jsxs(
|
|
21
|
+
children: [/*#__PURE__*/_jsxs(Stack, {
|
|
22
|
+
direction: "row",
|
|
23
|
+
align: "flex-start",
|
|
24
|
+
gap: "xs",
|
|
15
25
|
className: "newspack-settings__section__title",
|
|
16
26
|
children: [/*#__PURE__*/_jsx("span", {
|
|
17
27
|
children: title
|
|
18
28
|
}), description && /*#__PURE__*/_jsx(InfoButton, {
|
|
19
|
-
|
|
29
|
+
description: description,
|
|
30
|
+
triggerLabel: typeof title === 'string' ? sprintf(
|
|
31
|
+
// translators: %s is the name of the setting being explained.
|
|
32
|
+
__('More information about %s', 'newspack-plugin'), title) : __('More information', 'newspack-plugin')
|
|
20
33
|
})]
|
|
21
34
|
}), /*#__PURE__*/_jsx("div", {
|
|
22
35
|
className: "newspack-settings__section__content",
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies.
|
|
3
|
+
*/
|
|
4
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies.
|
|
8
|
+
*/
|
|
9
|
+
import SettingsSection from './SettingsSection';
|
|
10
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
+
var DESCRIPTION = 'Number of articles read in the last 30 day period.';
|
|
12
|
+
describe('SettingsSection', function () {
|
|
13
|
+
it('reveals the description from the info trigger', function () {
|
|
14
|
+
render(/*#__PURE__*/_jsx(SettingsSection, {
|
|
15
|
+
title: "Articles read",
|
|
16
|
+
description: DESCRIPTION,
|
|
17
|
+
children: /*#__PURE__*/_jsx("input", {})
|
|
18
|
+
}));
|
|
19
|
+
expect(screen.queryByText(DESCRIPTION)).not.toBeInTheDocument();
|
|
20
|
+
fireEvent.click(screen.getByRole('button', {
|
|
21
|
+
name: 'More information about Articles read'
|
|
22
|
+
}));
|
|
23
|
+
expect(screen.getByText(DESCRIPTION)).toBeInTheDocument();
|
|
24
|
+
});
|
|
25
|
+
it('falls back to a plain trigger name when the title is not a string', function () {
|
|
26
|
+
render(/*#__PURE__*/_jsx(SettingsSection, {
|
|
27
|
+
title: /*#__PURE__*/_jsx("em", {
|
|
28
|
+
children: "Articles read"
|
|
29
|
+
}),
|
|
30
|
+
description: DESCRIPTION,
|
|
31
|
+
children: /*#__PURE__*/_jsx("input", {})
|
|
32
|
+
}));
|
|
33
|
+
expect(screen.getByRole('button', {
|
|
34
|
+
name: 'More information'
|
|
35
|
+
})).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
it('renders no info trigger without a description', function () {
|
|
38
|
+
render(/*#__PURE__*/_jsx(SettingsSection, {
|
|
39
|
+
title: "Articles read",
|
|
40
|
+
children: /*#__PURE__*/_jsx("input", {})
|
|
41
|
+
}));
|
|
42
|
+
expect(screen.getByText('Articles read')).toBeInTheDocument();
|
|
43
|
+
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import SettingsCard from './SettingsCard';
|
|
2
2
|
import SettingsSection from './SettingsSection';
|
|
3
3
|
import MinMaxSetting from './MinMaxSetting';
|
|
4
|
+
import DateRangeSetting from './DateRangeSetting';
|
|
4
5
|
import './style.scss';
|
|
5
6
|
export default {
|
|
6
7
|
SettingsCard: SettingsCard,
|
|
7
8
|
SettingsSection: SettingsSection,
|
|
8
|
-
MinMaxSetting: MinMaxSetting
|
|
9
|
+
MinMaxSetting: MinMaxSetting,
|
|
10
|
+
DateRangeSetting: DateRangeSetting
|
|
9
11
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@use "~@wordpress/base-styles/variables" as wp-vars;
|
|
2
|
+
|
|
1
3
|
.newspack-settings {
|
|
2
4
|
&__card {
|
|
3
5
|
&.newspack-card.newspack-action-card {
|
|
@@ -28,17 +30,18 @@
|
|
|
28
30
|
|
|
29
31
|
.newspack-settings__section {
|
|
30
32
|
&__title {
|
|
31
|
-
align-items: center;
|
|
32
|
-
display: flex;
|
|
33
|
-
margin: 0;
|
|
34
|
-
|
|
35
33
|
span {
|
|
36
|
-
line-height:
|
|
34
|
+
line-height: wp-vars.$font-line-height-small;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.newspack-info-button {
|
|
38
|
+
margin-block: calc((#{wp-vars.$font-line-height-small} - #{wp-vars.$button-size-small}) / 2);
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
&__content {
|
|
40
42
|
> div,
|
|
41
|
-
.newspack-settings__min-max
|
|
43
|
+
.newspack-settings__min-max,
|
|
44
|
+
.newspack-settings__date-range-bound {
|
|
42
45
|
margin: 0;
|
|
43
46
|
display: flex;
|
|
44
47
|
align-items: center;
|
|
@@ -49,7 +52,19 @@
|
|
|
49
52
|
}
|
|
50
53
|
}
|
|
51
54
|
.newspack-autocomplete-tokenfield,
|
|
52
|
-
.newspack-settings__min-max
|
|
55
|
+
.newspack-settings__min-max,
|
|
56
|
+
.newspack-settings__date-range-bound {
|
|
57
|
+
width: 100%;
|
|
58
|
+
}
|
|
59
|
+
.newspack-settings__date-range-bound {
|
|
60
|
+
// Overrides the `center` above, which has to be beaten at this
|
|
61
|
+
// nesting level. The select carries the visible From/To label and
|
|
62
|
+
// the value input's is hidden, so the two controls differ in
|
|
63
|
+
// overall height; centering floats the input up against the label,
|
|
64
|
+
// while bottom-aligning lines the two boxes up with each other.
|
|
65
|
+
align-items: flex-end;
|
|
66
|
+
}
|
|
67
|
+
.newspack-settings__date-range-preset {
|
|
53
68
|
width: 100%;
|
|
54
69
|
}
|
|
55
70
|
.newspack-autocomplete-tokenfield {
|
|
@@ -90,6 +105,24 @@
|
|
|
90
105
|
margin-top: 16px !important;
|
|
91
106
|
}
|
|
92
107
|
}
|
|
108
|
+
.newspack-settings__date-range-preset + .newspack-settings__date-range-bound {
|
|
109
|
+
margin-top: 16px !important;
|
|
110
|
+
}
|
|
111
|
+
.newspack-settings__date-range-bound {
|
|
112
|
+
gap: 8px;
|
|
113
|
+
|
|
114
|
+
.newspack-select-control {
|
|
115
|
+
flex: 0 0 auto;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.newspack-text-control {
|
|
119
|
+
flex: 1 1 auto;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
+ .newspack-settings__date-range-bound {
|
|
123
|
+
margin-top: 16px !important;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
93
126
|
}
|
|
94
127
|
|
|
95
128
|
&__no-border {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
|
+
var _excluded = ["className", "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 { forwardRef } from '@wordpress/element';
|
|
10
|
+
import { Stack } from '@wordpress/ui';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* External dependencies.
|
|
14
|
+
*/
|
|
15
|
+
import classnames from 'classnames';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Internal dependencies.
|
|
19
|
+
*/
|
|
20
|
+
import { useStatCardContext } from './context';
|
|
21
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
22
|
+
var Body = forwardRef(function Body(_ref, ref) {
|
|
23
|
+
var className = _ref.className,
|
|
24
|
+
children = _ref.children,
|
|
25
|
+
props = _objectWithoutProperties(_ref, _excluded);
|
|
26
|
+
useStatCardContext();
|
|
27
|
+
return /*#__PURE__*/_jsx(Stack, _objectSpread(_objectSpread({
|
|
28
|
+
ref: ref,
|
|
29
|
+
direction: "column",
|
|
30
|
+
gap: "xs",
|
|
31
|
+
className: classnames('newspack-stat-card__body', className)
|
|
32
|
+
}, props), {}, {
|
|
33
|
+
children: children
|
|
34
|
+
}));
|
|
35
|
+
});
|
|
36
|
+
export default Body;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies.
|
|
3
|
+
*/
|
|
4
|
+
import { createContext, useContext, useEffect } from '@wordpress/element';
|
|
5
|
+
import { _x } from '@wordpress/i18n';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
var ORPHAN_MESSAGE = 'StatCard subcomponents must be rendered inside StatCard.Root.';
|
|
12
|
+
|
|
13
|
+
// Resolved per call rather than once at module load, so a locale switched after
|
|
14
|
+
// the bundle evaluates still reaches the defaults. A blank override is a missing
|
|
15
|
+
// one: the glyph and the arrow must never be left announcing nothing.
|
|
16
|
+
export var resolveStatCardLabels = function resolveStatCardLabels(labels) {
|
|
17
|
+
var _labels$notApplicable, _labels$up, _labels$down;
|
|
18
|
+
return {
|
|
19
|
+
notApplicable: (labels === null || labels === void 0 || (_labels$notApplicable = labels.notApplicable) === null || _labels$notApplicable === void 0 ? void 0 : _labels$notApplicable.trim()) || _x('Not applicable', 'a statistic with no number to show', 'newspack-plugin'),
|
|
20
|
+
up: (labels === null || labels === void 0 || (_labels$up = labels.up) === null || _labels$up === void 0 ? void 0 : _labels$up.trim()) || _x('Up', 'a statistic that has increased', 'newspack-plugin'),
|
|
21
|
+
down: (labels === null || labels === void 0 || (_labels$down = labels.down) === null || _labels$down === void 0 ? void 0 : _labels$down.trim()) || _x('Down', 'a statistic that has decreased', 'newspack-plugin')
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export var StatCardContext = createContext(null);
|
|
25
|
+
export var useStatCardContext = function useStatCardContext() {
|
|
26
|
+
var context = useContext(StatCardContext);
|
|
27
|
+
var isOrphan = !context;
|
|
28
|
+
useEffect(function () {
|
|
29
|
+
if (!isOrphan) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.warn(ORPHAN_MESSAGE);
|
|
34
|
+
}, [isOrphan]);
|
|
35
|
+
if (!context) {
|
|
36
|
+
// A loose slot sizes its figure against the wrong container, which is
|
|
37
|
+
// cosmetic. Nothing above these cards catches an error, so throwing in
|
|
38
|
+
// production would blank an admin screen over it.
|
|
39
|
+
if ('production' !== process.env.NODE_ENV) {
|
|
40
|
+
throw new Error(ORPHAN_MESSAGE);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
heading: 3,
|
|
44
|
+
labels: resolveStatCardLabels()
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return context;
|
|
48
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
|
+
var _excluded = ["direction", "tone", "directionLabel", "label", "className", "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 { forwardRef, useEffect } from '@wordpress/element';
|
|
10
|
+
import { VisuallyHidden } from '@wordpress/ui';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* External dependencies.
|
|
14
|
+
*/
|
|
15
|
+
import classnames from 'classnames';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Internal dependencies.
|
|
19
|
+
*/
|
|
20
|
+
import { useStatCardContext } from './context';
|
|
21
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
22
|
+
var glyphs = {
|
|
23
|
+
up: '↑',
|
|
24
|
+
down: '↓'
|
|
25
|
+
};
|
|
26
|
+
var tones = ['positive', 'negative', 'neutral'];
|
|
27
|
+
var Delta = forwardRef(function Delta(_ref, ref) {
|
|
28
|
+
var direction = _ref.direction,
|
|
29
|
+
_ref$tone = _ref.tone,
|
|
30
|
+
tone = _ref$tone === void 0 ? 'neutral' : _ref$tone,
|
|
31
|
+
directionLabel = _ref.directionLabel,
|
|
32
|
+
label = _ref.label,
|
|
33
|
+
className = _ref.className,
|
|
34
|
+
children = _ref.children,
|
|
35
|
+
props = _objectWithoutProperties(_ref, _excluded);
|
|
36
|
+
var _useStatCardContext = useStatCardContext(),
|
|
37
|
+
labels = _useStatCardContext.labels;
|
|
38
|
+
useEffect(function () {
|
|
39
|
+
if ('production' === process.env.NODE_ENV) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!glyphs[direction]) {
|
|
43
|
+
// eslint-disable-next-line no-console
|
|
44
|
+
console.warn("StatCard.Delta: unknown direction \"".concat(direction, "\". Use one of ").concat(Object.keys(glyphs).join(', '), "."));
|
|
45
|
+
}
|
|
46
|
+
if (!tones.includes(tone)) {
|
|
47
|
+
// eslint-disable-next-line no-console
|
|
48
|
+
console.warn("StatCard.Delta: unknown tone \"".concat(tone, "\", falling back to neutral. Use one of ").concat(tones.join(', '), "."));
|
|
49
|
+
}
|
|
50
|
+
}, [direction, tone]);
|
|
51
|
+
var directions = {
|
|
52
|
+
up: labels.up,
|
|
53
|
+
down: labels.down
|
|
54
|
+
};
|
|
55
|
+
var glyph = glyphs[direction];
|
|
56
|
+
// Trimmed, and `||` not `??`: a blank label is a missing one. The fallback
|
|
57
|
+
// is keyed on the arrow's own direction, so an unrecognised one goes
|
|
58
|
+
// unspoken rather than announced as its opposite; words the caller wrote
|
|
59
|
+
// are honoured either way.
|
|
60
|
+
var named = label === null || label === void 0 ? void 0 : label.trim();
|
|
61
|
+
var spoken = (directionLabel === null || directionLabel === void 0 ? void 0 : directionLabel.trim()) || directions[direction];
|
|
62
|
+
var classes = classnames('newspack-stat-card__delta', 'neutral' !== tone && tones.includes(tone) && "newspack-stat-card__delta--".concat(tone), className);
|
|
63
|
+
return /*#__PURE__*/_jsx("span", _objectSpread(_objectSpread({
|
|
64
|
+
ref: ref,
|
|
65
|
+
className: classes
|
|
66
|
+
}, props), {}, {
|
|
67
|
+
children: named ? /*#__PURE__*/_jsxs(_Fragment, {
|
|
68
|
+
children: [/*#__PURE__*/_jsxs("span", {
|
|
69
|
+
"aria-hidden": "true",
|
|
70
|
+
children: [glyph, children]
|
|
71
|
+
}), /*#__PURE__*/_jsx(VisuallyHidden, {
|
|
72
|
+
render: /*#__PURE__*/_jsx("span", {}),
|
|
73
|
+
children: named
|
|
74
|
+
})]
|
|
75
|
+
}) : /*#__PURE__*/_jsxs(_Fragment, {
|
|
76
|
+
children: [glyph && /*#__PURE__*/_jsx("span", {
|
|
77
|
+
"aria-hidden": "true",
|
|
78
|
+
children: glyph
|
|
79
|
+
}), spoken && /*#__PURE__*/_jsx(VisuallyHidden, {
|
|
80
|
+
render: /*#__PURE__*/_jsx("span", {}),
|
|
81
|
+
children: "".concat(spoken, " ")
|
|
82
|
+
}), children]
|
|
83
|
+
})
|
|
84
|
+
}));
|
|
85
|
+
});
|
|
86
|
+
export default Delta;
|