newspack-components 4.5.0 → 4.6.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 +40 -0
- package/DEVELOPMENT.md +1 -1
- package/dist/cjs/accordion/index.js +43 -26
- package/dist/cjs/accordion/index.test.js +80 -0
- package/dist/cjs/accordion/style.scss +25 -21
- package/dist/cjs/action-card/style.scss +0 -22
- package/dist/cjs/breadcrumbs/index.js +80 -0
- package/dist/cjs/breadcrumbs/index.test.js +109 -0
- package/dist/cjs/breadcrumbs/style.scss +41 -0
- package/dist/cjs/card/core-card.js +8 -4
- package/dist/cjs/card/index.js +1 -1
- package/dist/cjs/card/style-core.scss +14 -0
- package/dist/cjs/card/style.scss +0 -9
- package/dist/cjs/card-feature/index.js +25 -13
- package/dist/cjs/card-settings-group/index.js +3 -0
- package/dist/cjs/confirm-dialog/index.js +34 -8
- package/dist/cjs/confirm-dialog/index.test.js +173 -0
- package/dist/cjs/divider/style.scss +4 -2
- package/dist/cjs/grid/style.scss +5 -0
- package/dist/cjs/handoff/index.js +8 -5
- package/dist/cjs/index.js +34 -1
- package/dist/cjs/integration-icon/index.js +40 -0
- package/dist/cjs/integration-icon/style.scss +42 -0
- package/dist/cjs/integration-icons/index.js +21 -2
- package/dist/cjs/modal/style.scss +4 -0
- package/dist/cjs/page/index.js +121 -0
- package/dist/cjs/page/index.test.js +69 -0
- package/dist/cjs/page/style.scss +86 -0
- package/dist/cjs/route-match.js +59 -0
- package/dist/cjs/route-match.test.js +77 -0
- package/dist/cjs/section-header/index.js +8 -1
- package/dist/cjs/section-header/style.scss +11 -11
- package/dist/cjs/select-control/GroupedSelectControl.js +1 -1
- package/dist/cjs/select-control/style.scss +4 -0
- package/dist/cjs/style.scss +1 -4
- package/dist/cjs/tabbed-navigation/index.js +188 -40
- package/dist/cjs/tabbed-navigation/index.test.js +299 -0
- package/dist/cjs/tabbed-navigation/style.scss +77 -57
- package/dist/cjs/with-wizard/index.js +34 -45
- package/dist/cjs/with-wizard-screen/index.js +82 -41
- package/dist/cjs/with-wizard-screen/index.test.js +50 -0
- package/dist/cjs/with-wizard-screen/style.scss +15 -118
- package/dist/cjs/wizard/breadcrumbs-select.js +70 -0
- package/dist/cjs/wizard/breadcrumbs-select.test.js +200 -0
- package/dist/cjs/wizard/components/WizardError.js +8 -4
- package/dist/cjs/wizard/index.js +162 -141
- package/dist/esm/accordion/index.js +42 -26
- package/dist/esm/accordion/index.test.js +79 -0
- package/dist/esm/accordion/style.scss +25 -21
- package/dist/esm/action-card/style.scss +0 -22
- package/dist/esm/breadcrumbs/index.js +75 -0
- package/dist/esm/breadcrumbs/index.test.js +105 -0
- package/dist/esm/breadcrumbs/style.scss +41 -0
- package/dist/esm/card/core-card.js +8 -4
- package/dist/esm/card/index.js +1 -1
- package/dist/esm/card/style-core.scss +14 -0
- package/dist/esm/card/style.scss +0 -9
- package/dist/esm/card-feature/index.js +25 -13
- package/dist/esm/card-settings-group/index.js +3 -0
- package/dist/esm/confirm-dialog/index.js +34 -8
- package/dist/esm/confirm-dialog/index.test.js +173 -0
- package/dist/esm/divider/style.scss +4 -2
- package/dist/esm/grid/style.scss +5 -0
- package/dist/esm/handoff/index.js +7 -5
- package/dist/esm/index.js +5 -1
- package/dist/esm/integration-icon/index.js +34 -0
- package/dist/esm/integration-icon/style.scss +42 -0
- package/dist/esm/integration-icons/index.js +22 -1
- package/dist/esm/modal/style.scss +4 -0
- package/dist/esm/page/index.js +113 -0
- package/dist/esm/page/index.test.js +65 -0
- package/dist/esm/page/style.scss +86 -0
- package/dist/esm/route-match.js +51 -0
- package/dist/esm/route-match.test.js +74 -0
- package/dist/esm/section-header/index.js +8 -1
- package/dist/esm/section-header/style.scss +11 -11
- package/dist/esm/select-control/GroupedSelectControl.js +1 -1
- package/dist/esm/select-control/style.scss +4 -0
- package/dist/esm/style.scss +1 -4
- package/dist/esm/tabbed-navigation/index.js +192 -38
- package/dist/esm/tabbed-navigation/index.test.js +296 -0
- package/dist/esm/tabbed-navigation/style.scss +77 -57
- package/dist/esm/with-wizard/index.js +33 -45
- package/dist/esm/with-wizard-screen/index.js +78 -41
- package/dist/esm/with-wizard-screen/index.test.js +46 -0
- package/dist/esm/with-wizard-screen/style.scss +15 -118
- package/dist/esm/wizard/breadcrumbs-select.js +63 -0
- package/dist/esm/wizard/breadcrumbs-select.test.js +197 -0
- package/dist/esm/wizard/components/WizardError.js +7 -5
- package/dist/esm/wizard/index.js +165 -144
- package/package.json +10 -2
- package/src/accordion/index.js +27 -15
- package/src/accordion/index.test.js +62 -0
- package/src/accordion/style.scss +25 -21
- package/src/action-card/style.scss +0 -22
- package/src/breadcrumbs/index.js +63 -0
- package/src/breadcrumbs/index.test.js +59 -0
- package/src/breadcrumbs/style.scss +41 -0
- package/src/card/core-card.js +11 -6
- package/src/card/index.js +0 -1
- package/src/card/style-core.scss +14 -0
- package/src/card/style.scss +0 -9
- package/src/card-feature/README.md +3 -3
- package/src/card-feature/index.tsx +37 -21
- package/src/card-settings-group/index.tsx +4 -0
- package/src/card-sortable-list/index.tsx +1 -1
- package/src/confirm-dialog/index.test.js +142 -0
- package/src/confirm-dialog/index.tsx +34 -7
- package/src/divider/style.scss +4 -2
- package/src/grid/style.scss +5 -0
- package/src/handoff/index.js +4 -3
- package/src/index.js +5 -1
- package/src/integration-icon/index.tsx +35 -0
- package/src/integration-icon/style.scss +42 -0
- package/src/integration-icons/index.js +23 -0
- package/src/modal/style.scss +4 -0
- package/src/page/index.js +89 -0
- package/src/page/index.test.js +43 -0
- package/src/page/style.scss +86 -0
- package/src/route-match.js +47 -0
- package/src/route-match.test.js +50 -0
- package/src/section-header/index.js +7 -3
- package/src/section-header/style.scss +11 -11
- package/src/select-control/GroupedSelectControl.js +1 -1
- package/src/select-control/style.scss +4 -0
- package/src/style.scss +1 -4
- package/src/tabbed-navigation/index.js +181 -34
- package/src/tabbed-navigation/index.test.js +228 -0
- package/src/tabbed-navigation/style.scss +77 -57
- package/src/with-wizard/index.js +18 -39
- package/src/with-wizard-screen/index.js +70 -35
- package/src/with-wizard-screen/index.test.js +36 -0
- package/src/with-wizard-screen/style.scss +15 -118
- package/src/wizard/breadcrumbs-select.js +51 -0
- package/src/wizard/breadcrumbs-select.test.js +124 -0
- package/src/wizard/components/WizardError.js +4 -3
- package/src/wizard/index.js +160 -146
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
|
+
var _excluded = ["initialEntries"];
|
|
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
|
+
* External dependencies.
|
|
8
|
+
*/
|
|
9
|
+
import { act, fireEvent, render, screen } from '@testing-library/react';
|
|
10
|
+
import { MemoryRouter, Redirect, useHistory } from 'react-router-dom';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Internal dependencies.
|
|
14
|
+
*/
|
|
15
|
+
import TabbedNavigation, { isItemActive } from './index';
|
|
16
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
17
|
+
var HistoryGrabber = function HistoryGrabber(_ref) {
|
|
18
|
+
var historyRef = _ref.historyRef;
|
|
19
|
+
historyRef.current = useHistory();
|
|
20
|
+
return null;
|
|
21
|
+
};
|
|
22
|
+
var ITEMS = [{
|
|
23
|
+
label: 'Stories',
|
|
24
|
+
path: '/stories'
|
|
25
|
+
}, {
|
|
26
|
+
label: 'Budgets',
|
|
27
|
+
path: '/budgets'
|
|
28
|
+
}, {
|
|
29
|
+
label: 'Sites',
|
|
30
|
+
path: '/sites'
|
|
31
|
+
}];
|
|
32
|
+
var renderTabs = function renderTabs() {
|
|
33
|
+
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
34
|
+
_ref2$initialEntries = _ref2.initialEntries,
|
|
35
|
+
initialEntries = _ref2$initialEntries === void 0 ? ['/stories'] : _ref2$initialEntries,
|
|
36
|
+
props = _objectWithoutProperties(_ref2, _excluded);
|
|
37
|
+
var historyRef = {
|
|
38
|
+
current: null
|
|
39
|
+
};
|
|
40
|
+
render(/*#__PURE__*/_jsxs(MemoryRouter, {
|
|
41
|
+
initialEntries: initialEntries,
|
|
42
|
+
children: [/*#__PURE__*/_jsx(HistoryGrabber, {
|
|
43
|
+
historyRef: historyRef
|
|
44
|
+
}), /*#__PURE__*/_jsx(TabbedNavigation, _objectSpread({
|
|
45
|
+
items: ITEMS,
|
|
46
|
+
content: /*#__PURE__*/_jsx("div", {
|
|
47
|
+
children: "Routed content"
|
|
48
|
+
})
|
|
49
|
+
}, props))]
|
|
50
|
+
}));
|
|
51
|
+
return historyRef.current;
|
|
52
|
+
};
|
|
53
|
+
var getTab = function getTab(name) {
|
|
54
|
+
return screen.getByRole('tab', {
|
|
55
|
+
name: name
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
describe('isItemActive', function () {
|
|
59
|
+
it('treats an explicitly selected item as active regardless of pathname', function () {
|
|
60
|
+
expect(isItemActive({
|
|
61
|
+
selected: true,
|
|
62
|
+
path: '/other'
|
|
63
|
+
}, '/current')).toBe(true);
|
|
64
|
+
expect(isItemActive({
|
|
65
|
+
selected: true
|
|
66
|
+
}, null)).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
describe('outside a router (pathname is null)', function () {
|
|
69
|
+
afterEach(function () {
|
|
70
|
+
delete window.location;
|
|
71
|
+
window.location = new URL('http://localhost/');
|
|
72
|
+
});
|
|
73
|
+
it('is active when the href matches the current URL', function () {
|
|
74
|
+
delete window.location;
|
|
75
|
+
window.location = new URL('http://example.com/wp-admin/admin.php?page=ads');
|
|
76
|
+
expect(isItemActive({
|
|
77
|
+
href: 'http://example.com/wp-admin/admin.php?page=ads'
|
|
78
|
+
}, null)).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
it('is inactive when the href does not match', function () {
|
|
81
|
+
delete window.location;
|
|
82
|
+
window.location = new URL('http://example.com/wp-admin/admin.php?page=ads');
|
|
83
|
+
expect(isItemActive({
|
|
84
|
+
href: 'http://example.com/wp-admin/admin.php?page=other'
|
|
85
|
+
}, null)).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
it('is inactive when the item has no href', function () {
|
|
88
|
+
expect(isItemActive({
|
|
89
|
+
path: '/ads'
|
|
90
|
+
}, null)).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('inside a router (delegates to the shared route matcher)', function () {
|
|
94
|
+
it('matches a path as a prefix by default', function () {
|
|
95
|
+
expect(isItemActive({
|
|
96
|
+
path: '/stories'
|
|
97
|
+
}, '/stories')).toBe(true);
|
|
98
|
+
expect(isItemActive({
|
|
99
|
+
path: '/stories'
|
|
100
|
+
}, '/stories/new')).toBe(true);
|
|
101
|
+
expect(isItemActive({
|
|
102
|
+
path: '/stories'
|
|
103
|
+
}, '/budgets')).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
it('matches exactly when the item opts in via exact', function () {
|
|
106
|
+
expect(isItemActive({
|
|
107
|
+
path: '/stories',
|
|
108
|
+
exact: true
|
|
109
|
+
}, '/stories/new')).toBe(false);
|
|
110
|
+
});
|
|
111
|
+
it('keeps the parent tab active on a hidden subpage via wildcard', function () {
|
|
112
|
+
var item = {
|
|
113
|
+
path: '/additional-brands',
|
|
114
|
+
activeTabPaths: ['/additional-brands/*']
|
|
115
|
+
};
|
|
116
|
+
expect(isItemActive(item, '/additional-brands/new')).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
describe('TabbedNavigation with routed items', function () {
|
|
121
|
+
it('renders the routed content inside the active tab panel', function () {
|
|
122
|
+
renderTabs({
|
|
123
|
+
initialEntries: ['/budgets']
|
|
124
|
+
});
|
|
125
|
+
expect(getTab('Budgets')).toHaveAttribute('aria-selected', 'true');
|
|
126
|
+
var content = screen.getByText('Routed content');
|
|
127
|
+
var panel = content.closest('[role="tabpanel"]');
|
|
128
|
+
expect(panel).not.toBeNull();
|
|
129
|
+
expect(getTab('Budgets')).toHaveAttribute('aria-controls', panel.id);
|
|
130
|
+
});
|
|
131
|
+
it('keeps the tab active and the content in its panel on a nested route', function () {
|
|
132
|
+
renderTabs({
|
|
133
|
+
initialEntries: ['/stories/new']
|
|
134
|
+
});
|
|
135
|
+
expect(getTab('Stories')).toHaveAttribute('aria-selected', 'true');
|
|
136
|
+
expect(screen.getByText('Routed content').closest('[role="tabpanel"]')).not.toBeNull();
|
|
137
|
+
});
|
|
138
|
+
it('prefers the most specific tab when paths nest', function () {
|
|
139
|
+
render(/*#__PURE__*/_jsx(MemoryRouter, {
|
|
140
|
+
initialEntries: ['/stories/new'],
|
|
141
|
+
children: /*#__PURE__*/_jsx(TabbedNavigation, {
|
|
142
|
+
items: [{
|
|
143
|
+
label: 'Stories',
|
|
144
|
+
path: '/stories'
|
|
145
|
+
}, {
|
|
146
|
+
label: 'New story',
|
|
147
|
+
path: '/stories/new'
|
|
148
|
+
}]
|
|
149
|
+
})
|
|
150
|
+
}));
|
|
151
|
+
expect(getTab('New story')).toHaveAttribute('aria-selected', 'true');
|
|
152
|
+
expect(getTab('Stories')).toHaveAttribute('aria-selected', 'false');
|
|
153
|
+
});
|
|
154
|
+
it('navigates through history.push on click', function () {
|
|
155
|
+
var history = renderTabs();
|
|
156
|
+
fireEvent.click(getTab('Sites'));
|
|
157
|
+
expect(history.location.pathname).toBe('/sites');
|
|
158
|
+
expect(getTab('Sites')).toHaveAttribute('aria-selected', 'true');
|
|
159
|
+
});
|
|
160
|
+
it('leaves modified clicks to the browser', function () {
|
|
161
|
+
var history = renderTabs();
|
|
162
|
+
fireEvent.click(getTab('Sites'), {
|
|
163
|
+
metaKey: true
|
|
164
|
+
});
|
|
165
|
+
expect(history.location.pathname).toBe('/stories');
|
|
166
|
+
});
|
|
167
|
+
it('consults history.block guards before navigating', function () {
|
|
168
|
+
var history = renderTabs();
|
|
169
|
+
var unblock = history.block(function () {
|
|
170
|
+
return false;
|
|
171
|
+
});
|
|
172
|
+
fireEvent.click(getTab('Sites'));
|
|
173
|
+
expect(history.location.pathname).toBe('/stories');
|
|
174
|
+
expect(getTab('Stories')).toHaveAttribute('aria-selected', 'true');
|
|
175
|
+
unblock();
|
|
176
|
+
});
|
|
177
|
+
it('disables tabs after the active one with disableUpcoming', function () {
|
|
178
|
+
renderTabs({
|
|
179
|
+
initialEntries: ['/budgets'],
|
|
180
|
+
disableUpcoming: true
|
|
181
|
+
});
|
|
182
|
+
expect(getTab('Stories')).toHaveAttribute('href');
|
|
183
|
+
expect(getTab('Sites')).not.toHaveAttribute('href');
|
|
184
|
+
expect(getTab('Sites')).toHaveAttribute('aria-disabled', 'true');
|
|
185
|
+
fireEvent.click(getTab('Sites'));
|
|
186
|
+
expect(getTab('Budgets')).toHaveAttribute('aria-selected', 'true');
|
|
187
|
+
});
|
|
188
|
+
it('disables every tab with disableUpcoming when no route matches', function () {
|
|
189
|
+
renderTabs({
|
|
190
|
+
initialEntries: ['/unknown'],
|
|
191
|
+
disableUpcoming: true
|
|
192
|
+
});
|
|
193
|
+
ITEMS.forEach(function (_ref3) {
|
|
194
|
+
var label = _ref3.label;
|
|
195
|
+
expect(getTab(label)).toHaveAttribute('aria-disabled', 'true');
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
it('renders the content outside the panels when no tab owns the route', function () {
|
|
199
|
+
// A route no tab matches — an edit screen registered as hidden, or a stale
|
|
200
|
+
// URL. The content must still mount, or the page body is blank.
|
|
201
|
+
renderTabs({
|
|
202
|
+
initialEntries: ['/edit/123']
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Assert positively: with nothing selected the panels unmount entirely, so
|
|
206
|
+
// a `closest( '[role=tabpanel]' )` check alone would pass even if the
|
|
207
|
+
// content rendered somewhere wrong. Exactly one copy also guards the mutual
|
|
208
|
+
// exclusivity with the in-panel render.
|
|
209
|
+
var matches = screen.getAllByText('Routed content');
|
|
210
|
+
expect(matches).toHaveLength(1);
|
|
211
|
+
expect(matches[0].closest('.newspack-tabbed-navigation__root')).not.toBeNull();
|
|
212
|
+
expect(matches[0].closest('[role="tabpanel"]')).toBeNull();
|
|
213
|
+
ITEMS.forEach(function (_ref4) {
|
|
214
|
+
var label = _ref4.label;
|
|
215
|
+
expect(getTab(label)).toHaveAttribute('aria-selected', 'false');
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
it('renders the content when the only matching item is hidden from the bar', function () {
|
|
219
|
+
// The wizard routes hidden sections but filters them out of the bar, so the
|
|
220
|
+
// item exists and yet can never own the route.
|
|
221
|
+
render(/*#__PURE__*/_jsx(MemoryRouter, {
|
|
222
|
+
initialEntries: ['/edit/123'],
|
|
223
|
+
children: /*#__PURE__*/_jsx(TabbedNavigation, {
|
|
224
|
+
items: [].concat(ITEMS, [{
|
|
225
|
+
label: 'Edit',
|
|
226
|
+
path: '/edit/:id',
|
|
227
|
+
isHiddenInTabbedNavigation: true
|
|
228
|
+
}]),
|
|
229
|
+
content: /*#__PURE__*/_jsx("div", {
|
|
230
|
+
children: "Routed content"
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
}));
|
|
234
|
+
expect(screen.getAllByText('Routed content')).toHaveLength(1);
|
|
235
|
+
expect(screen.queryByRole('tab', {
|
|
236
|
+
name: 'Edit'
|
|
237
|
+
})).not.toBeInTheDocument();
|
|
238
|
+
});
|
|
239
|
+
it('renders unowned content even when disableUpcoming disables every tab', function () {
|
|
240
|
+
// The setup wizard's Welcome and Completed screens are hidden from the bar
|
|
241
|
+
// AND use disableUpcoming, so `index > activeIndex` disables every tab. The
|
|
242
|
+
// content still has to render — this is the case that left onboarding blank.
|
|
243
|
+
renderTabs({
|
|
244
|
+
initialEntries: ['/unowned'],
|
|
245
|
+
disableUpcoming: true
|
|
246
|
+
});
|
|
247
|
+
expect(screen.getAllByText('Routed content')).toHaveLength(1);
|
|
248
|
+
ITEMS.forEach(function (_ref5) {
|
|
249
|
+
var label = _ref5.label;
|
|
250
|
+
expect(getTab(label)).toHaveAttribute('aria-disabled', 'true');
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
it('mounts unowned content so a router fallback inside it still runs', function () {
|
|
254
|
+
// The wizard's trailing <Redirect> lives inside this content, so dropping
|
|
255
|
+
// it would strand the route instead of correcting it.
|
|
256
|
+
renderTabs({
|
|
257
|
+
initialEntries: ['/unknown'],
|
|
258
|
+
content: /*#__PURE__*/_jsx(Redirect, {
|
|
259
|
+
to: "/stories"
|
|
260
|
+
})
|
|
261
|
+
});
|
|
262
|
+
expect(getTab('Stories')).toHaveAttribute('aria-selected', 'true');
|
|
263
|
+
});
|
|
264
|
+
it('navigates when the active tab changes so panels follow the route', function () {
|
|
265
|
+
var history = renderTabs();
|
|
266
|
+
act(function () {
|
|
267
|
+
return history.push('/budgets');
|
|
268
|
+
});
|
|
269
|
+
expect(getTab('Budgets')).toHaveAttribute('aria-selected', 'true');
|
|
270
|
+
expect(screen.getByText('Routed content').closest('[role="tabpanel"]').id).toBe(getTab('Budgets').getAttribute('aria-controls'));
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
describe('TabbedNavigation with href-only items', function () {
|
|
274
|
+
var LINK_ITEMS = [{
|
|
275
|
+
label: 'Newsletters',
|
|
276
|
+
href: 'http://example.com/wp-admin/admin.php?page=newsletters',
|
|
277
|
+
selected: true
|
|
278
|
+
}, {
|
|
279
|
+
label: 'Ads',
|
|
280
|
+
href: 'http://example.com/wp-admin/admin.php?page=ads'
|
|
281
|
+
}];
|
|
282
|
+
it('renders plain navigation links instead of a tabs widget', function () {
|
|
283
|
+
render(/*#__PURE__*/_jsx(TabbedNavigation, {
|
|
284
|
+
items: LINK_ITEMS
|
|
285
|
+
}));
|
|
286
|
+
expect(screen.queryByRole('tab')).not.toBeInTheDocument();
|
|
287
|
+
expect(screen.getByRole('navigation')).toBeInTheDocument();
|
|
288
|
+
var active = screen.getByRole('link', {
|
|
289
|
+
name: 'Newsletters'
|
|
290
|
+
});
|
|
291
|
+
expect(active).toHaveAttribute('aria-current', 'page');
|
|
292
|
+
expect(screen.getByRole('link', {
|
|
293
|
+
name: 'Ads'
|
|
294
|
+
})).not.toHaveAttribute('aria-current');
|
|
295
|
+
});
|
|
296
|
+
});
|
|
@@ -2,81 +2,101 @@
|
|
|
2
2
|
* Tabbed Navigation
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
@use "../../../colors/colors.module" as colors;
|
|
5
6
|
@use "~@wordpress/base-styles/colors" as wp-colors;
|
|
7
|
+
@use "~@wordpress/base-styles/variables" as wp-vars;
|
|
8
|
+
|
|
9
|
+
// The Tabs.Root wrapper spans the tab bar and the tab panels so the panels can
|
|
10
|
+
// contain the routed content; it plays no layout role of its own.
|
|
11
|
+
.newspack-tabbed-navigation__root {
|
|
12
|
+
display: contents;
|
|
13
|
+
}
|
|
6
14
|
|
|
7
15
|
.newspack-tabbed-navigation {
|
|
8
|
-
background: white;
|
|
16
|
+
background: wp-colors.$white;
|
|
17
|
+
border-bottom: var( --wpds-border-width-xs, #{wp-vars.$border-width} ) solid var( --wpds-color-stroke-surface-neutral-weak, #{wp-colors.$gray-100} );
|
|
9
18
|
width: 100%;
|
|
10
19
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
top: 46px;
|
|
14
|
-
z-index: 3;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
@media only screen and ( min-width: 783px ) {
|
|
18
|
-
top: 32px;
|
|
19
|
-
}
|
|
20
|
+
/* stylelint-disable-next-line plugin-wpds/no-setting-wpds-custom-properties -- deliberate Newspack brand override of the indicator token. */
|
|
21
|
+
--wpds-color-stroke-interactive-neutral-strong: #{colors.$primary-600};
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
display: flex;
|
|
24
|
-
flex-wrap: wrap;
|
|
25
|
-
margin: 0;
|
|
26
|
-
}
|
|
23
|
+
a[role="tab"] {
|
|
24
|
+
text-decoration: none;
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
// wp-admin's unlayered `a { color }` beats every rule inside the design
|
|
27
|
+
// system's @layer wp-ui, so anchor tabs need their colors restated here:
|
|
28
|
+
// gray-900 for enabled tabs, and the DS disabled token for disabled ones.
|
|
29
|
+
&:not([data-disabled]):not([aria-disabled="true"]) {
|
|
30
|
+
color: wp-colors.$gray-900;
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
&:hover,
|
|
33
|
+
&:active,
|
|
34
|
+
&[aria-selected="true"] {
|
|
35
|
+
color: wp-colors.$gray-900;
|
|
36
|
+
}
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
a
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
// wp-admin's common.css and the wizard shell both decorate a:focus with a
|
|
40
|
+
// box-shadow ring, a 2px border-radius, and a transparent outline. Reset
|
|
41
|
+
// all three so the component's own :focus-visible ring (an inset outline
|
|
42
|
+
// on its ::after, shipped by @wordpress/ui) is the only focus treatment.
|
|
43
|
+
&:focus {
|
|
44
|
+
border-radius: 0;
|
|
45
|
+
box-shadow: none;
|
|
42
46
|
outline: none;
|
|
43
|
-
|
|
44
|
-
text-decoration: none;
|
|
47
|
+
}
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
// Square the component's focus ring corners.
|
|
50
|
+
&::after {
|
|
51
|
+
border-radius: 0;
|
|
52
|
+
}
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
&[data-disabled],
|
|
55
|
+
&[aria-disabled="true"] {
|
|
56
|
+
color: var( --wpds-color-foreground-interactive-neutral-disabled, #{wp-colors.$gray-600} );
|
|
57
|
+
pointer-events: none;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
// Link-driven variant: plain navigation styled to match the tabs widget.
|
|
62
|
+
&--links {
|
|
63
|
+
align-items: stretch;
|
|
64
|
+
display: flex;
|
|
65
|
+
overflow-x: auto;
|
|
66
|
+
}
|
|
61
67
|
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
.newspack-tabbed-navigation__link {
|
|
69
|
+
align-items: center;
|
|
70
|
+
color: wp-colors.$gray-900;
|
|
71
|
+
display: flex;
|
|
72
|
+
font-size: var( --wpds-typography-font-size-md, #{wp-vars.$default-font-size} );
|
|
73
|
+
height: wp-vars.$grid-unit-60; // Matches @wordpress/ui's horizontal tab height.
|
|
74
|
+
line-height: 1.2;
|
|
75
|
+
padding-inline: var( --wpds-dimension-padding-lg, #{wp-vars.$grid-unit-20} );
|
|
76
|
+
text-decoration: none;
|
|
77
|
+
white-space: nowrap;
|
|
64
78
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
&:hover,
|
|
80
|
+
&:active,
|
|
81
|
+
&:focus {
|
|
82
|
+
color: wp-colors.$gray-900;
|
|
83
|
+
}
|
|
70
84
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
85
|
+
// Mirror the tabs widget's focus ring (an inset outline) with plain
|
|
86
|
+
// outline + offset, replacing wp-admin's box-shadow focus decoration.
|
|
87
|
+
&:focus {
|
|
88
|
+
border-radius: 0;
|
|
89
|
+
box-shadow: none;
|
|
90
|
+
outline: none;
|
|
91
|
+
}
|
|
75
92
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
93
|
+
&:focus-visible {
|
|
94
|
+
outline: var( --wpds-border-width-focus, var( --wp-admin-border-width-focus, #{wp-vars.$border-width-focus-fallback} ) ) solid var( --wpds-color-stroke-focus, var( --wp-admin-theme-color ) );
|
|
95
|
+
outline-offset: calc( var( --wpds-dimension-padding-md, #{wp-vars.$grid-unit-15} ) * -1 );
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
&[aria-current="page"] {
|
|
99
|
+
box-shadow: inset 0 calc( var( --wpds-border-width-focus, #{wp-vars.$border-width-focus-fallback} ) * -1 ) 0 var( --wpds-color-stroke-interactive-neutral-strong, #{colors.$primary-600} );
|
|
80
100
|
}
|
|
81
101
|
}
|
|
82
102
|
}
|
|
@@ -14,12 +14,12 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
|
|
|
14
14
|
import { Component, createRef, Fragment } from '@wordpress/element';
|
|
15
15
|
import { __ } from '@wordpress/i18n';
|
|
16
16
|
import apiFetch from '@wordpress/api-fetch';
|
|
17
|
-
import {
|
|
17
|
+
import { __experimentalHStack as HStack } from '@wordpress/components'; // eslint-disable-line @wordpress/no-unsafe-wp-apis
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Internal dependencies.
|
|
21
21
|
*/
|
|
22
|
-
import { Button,
|
|
22
|
+
import { Button, Modal, Notice, Page, PluginInstaller } from '../';
|
|
23
23
|
import Router from '../proxied-imports/router';
|
|
24
24
|
import Footer from '../footer';
|
|
25
25
|
import './style.scss';
|
|
@@ -111,10 +111,11 @@ export default function withWizard(WrappedComponent, requiredPlugins) {
|
|
|
111
111
|
noticeText: message,
|
|
112
112
|
isError: true,
|
|
113
113
|
rawHTML: true
|
|
114
|
-
}), /*#__PURE__*/_jsx(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
}), /*#__PURE__*/_jsx(HStack, {
|
|
115
|
+
justify: "flex-end",
|
|
116
|
+
spacing: 4,
|
|
117
|
+
wrap: true,
|
|
118
|
+
className: "newspack-modal__footer",
|
|
118
119
|
children: /*#__PURE__*/_jsx(Button, {
|
|
119
120
|
isPrimary: true,
|
|
120
121
|
href: fallbackURL,
|
|
@@ -226,43 +227,29 @@ export default function withWizard(WrappedComponent, requiredPlugins) {
|
|
|
226
227
|
to: "/"
|
|
227
228
|
});
|
|
228
229
|
}
|
|
230
|
+
var headerText = requiredPlugins.length > 1 ? __('Required plugins', 'newspack-plugin') : __('Required plugin', 'newspack-plugin');
|
|
231
|
+
var content = /*#__PURE__*/_jsx("div", {
|
|
232
|
+
className: "newspack-wizard newspack-wizard__content",
|
|
233
|
+
children: /*#__PURE__*/_jsx(PluginInstaller, {
|
|
234
|
+
plugins: requiredPlugins,
|
|
235
|
+
onStatus: function onStatus(status) {
|
|
236
|
+
return _this.pluginInstallationStatus(status);
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
});
|
|
229
240
|
return /*#__PURE__*/_jsx(Route, {
|
|
230
241
|
path: "/",
|
|
231
242
|
render: function render() {
|
|
232
|
-
return
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
label: __('Return to Dashboard', 'newspack-plugin'),
|
|
243
|
-
showTooltip: true,
|
|
244
|
-
icon: category,
|
|
245
|
-
iconSize: 36,
|
|
246
|
-
children: /*#__PURE__*/_jsx(NewspackIcon, {
|
|
247
|
-
size: 36
|
|
248
|
-
})
|
|
249
|
-
}), /*#__PURE__*/_jsx("div", {
|
|
250
|
-
children: /*#__PURE__*/_jsx("h2", {
|
|
251
|
-
children: requiredPlugins.length > 1 ? __('Required plugins', 'newspack-plugin') : __('Required plugin', 'newspack-plugin')
|
|
252
|
-
})
|
|
253
|
-
})]
|
|
254
|
-
})
|
|
255
|
-
})
|
|
256
|
-
}), /*#__PURE__*/_jsx("div", {
|
|
257
|
-
className: "newspack-wizard newspack-wizard__content",
|
|
258
|
-
children: /*#__PURE__*/_jsx(PluginInstaller, {
|
|
259
|
-
plugins: requiredPlugins,
|
|
260
|
-
onStatus: function onStatus(status) {
|
|
261
|
-
return _this.pluginInstallationStatus(status);
|
|
262
|
-
}
|
|
263
|
-
})
|
|
264
|
-
})]
|
|
265
|
-
});
|
|
243
|
+
return (
|
|
244
|
+
// While `complete` is still null the requirements are being
|
|
245
|
+
// determined; render content without a header (matches prior behavior).
|
|
246
|
+
complete === null ? content : /*#__PURE__*/_jsx(Page, {
|
|
247
|
+
breadcrumbItems: [{
|
|
248
|
+
label: headerText
|
|
249
|
+
}],
|
|
250
|
+
children: content
|
|
251
|
+
})
|
|
252
|
+
);
|
|
266
253
|
}
|
|
267
254
|
});
|
|
268
255
|
});
|
|
@@ -280,7 +267,7 @@ export default function withWizard(WrappedComponent, requiredPlugins) {
|
|
|
280
267
|
_defineProperty(_this, "confirmAction", function (options) {
|
|
281
268
|
var modalOptions = _objectSpread({
|
|
282
269
|
title: null,
|
|
283
|
-
message: __('Are you sure?', '
|
|
270
|
+
message: __('Are you sure?', 'newspack-plugin'),
|
|
284
271
|
confirmText: __('OK', 'newspack-plugin'),
|
|
285
272
|
cancelText: __('Cancel', 'newspack-plugin'),
|
|
286
273
|
callback: null
|
|
@@ -316,10 +303,11 @@ export default function withWizard(WrappedComponent, requiredPlugins) {
|
|
|
316
303
|
},
|
|
317
304
|
children: [/*#__PURE__*/_jsx("p", {
|
|
318
305
|
children: message
|
|
319
|
-
}), /*#__PURE__*/_jsxs(
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
306
|
+
}), /*#__PURE__*/_jsxs(HStack, {
|
|
307
|
+
justify: "flex-end",
|
|
308
|
+
spacing: 4,
|
|
309
|
+
wrap: true,
|
|
310
|
+
className: "newspack-modal__footer",
|
|
323
311
|
children: [/*#__PURE__*/_jsx(Button, {
|
|
324
312
|
variant: "secondary",
|
|
325
313
|
onClick: function onClick() {
|