newspack-components 4.10.0 → 4.12.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/DEVELOPMENT.md +80 -38
  3. package/dist/cjs/breadcrumbs/index.js +1 -0
  4. package/dist/cjs/breadcrumbs/style.scss +3 -10
  5. package/dist/cjs/card/style-core.scss +1 -1
  6. package/dist/cjs/card-sortable-list/index.js +23 -12
  7. package/dist/cjs/card-sortable-list/index.test.js +21 -0
  8. package/dist/cjs/card-sortable-list/style.scss +8 -0
  9. package/dist/cjs/drawer/body.js +4 -6
  10. package/dist/cjs/drawer/content.js +7 -37
  11. package/dist/cjs/drawer/footer.js +10 -1
  12. package/dist/cjs/drawer/header.js +9 -1
  13. package/dist/cjs/drawer/index.test.js +3 -4
  14. package/dist/cjs/drawer/slots.test.js +25 -67
  15. package/dist/cjs/drawer/style.scss +0 -18
  16. package/dist/cjs/index.js +14 -0
  17. package/dist/cjs/page/index.js +10 -2
  18. package/dist/cjs/page/style.scss +24 -2
  19. package/dist/cjs/reorder-modal/index.js +521 -0
  20. package/dist/cjs/reorder-modal/index.test.js +1223 -0
  21. package/dist/cjs/reorder-modal/style.scss +114 -0
  22. package/dist/cjs/tabbed-navigation/style.scss +5 -0
  23. package/dist/cjs/tooltip-badge/index.js +50 -0
  24. package/dist/cjs/tooltip-badge/index.test.js +30 -0
  25. package/dist/cjs/wizard/index.js +21 -8
  26. package/dist/cjs/wizard/index.test.js +78 -0
  27. package/dist/cjs/wizard/store/index.js +2 -1
  28. package/dist/esm/breadcrumbs/index.js +1 -0
  29. package/dist/esm/breadcrumbs/style.scss +3 -10
  30. package/dist/esm/card/style-core.scss +1 -1
  31. package/dist/esm/card-sortable-list/index.js +25 -13
  32. package/dist/esm/card-sortable-list/index.test.js +21 -0
  33. package/dist/esm/card-sortable-list/style.scss +8 -0
  34. package/dist/esm/drawer/body.js +4 -5
  35. package/dist/esm/drawer/content.js +7 -36
  36. package/dist/esm/drawer/footer.js +10 -1
  37. package/dist/esm/drawer/header.js +9 -1
  38. package/dist/esm/drawer/index.test.js +3 -4
  39. package/dist/esm/drawer/slots.test.js +25 -67
  40. package/dist/esm/drawer/style.scss +0 -18
  41. package/dist/esm/index.js +2 -0
  42. package/dist/esm/page/index.js +10 -2
  43. package/dist/esm/page/style.scss +24 -2
  44. package/dist/esm/reorder-modal/index.js +514 -0
  45. package/dist/esm/reorder-modal/index.test.js +1224 -0
  46. package/dist/esm/reorder-modal/style.scss +114 -0
  47. package/dist/esm/tabbed-navigation/style.scss +5 -0
  48. package/dist/esm/tooltip-badge/index.js +43 -0
  49. package/dist/esm/tooltip-badge/index.test.js +26 -0
  50. package/dist/esm/wizard/index.js +23 -12
  51. package/dist/esm/wizard/index.test.js +78 -0
  52. package/dist/esm/wizard/store/index.js +2 -1
  53. package/package.json +2 -2
  54. package/src/breadcrumbs/index.js +1 -1
  55. package/src/breadcrumbs/style.scss +3 -10
  56. package/src/card/style-core.scss +1 -1
  57. package/src/card-form/README.md +2 -2
  58. package/src/card-sortable-list/index.test.js +13 -0
  59. package/src/card-sortable-list/index.tsx +31 -12
  60. package/src/card-sortable-list/style.scss +8 -0
  61. package/src/drawer/README.md +10 -16
  62. package/src/drawer/body.tsx +4 -5
  63. package/src/drawer/content.tsx +7 -33
  64. package/src/drawer/footer.tsx +8 -1
  65. package/src/drawer/header.tsx +8 -1
  66. package/src/drawer/index.test.js +3 -4
  67. package/src/drawer/slots.test.js +14 -48
  68. package/src/drawer/style.scss +0 -18
  69. package/src/drawer/types.ts +8 -3
  70. package/src/index.js +2 -0
  71. package/src/page/index.js +7 -3
  72. package/src/page/style.scss +24 -2
  73. package/src/reorder-modal/index.js +484 -0
  74. package/src/reorder-modal/index.test.js +566 -0
  75. package/src/reorder-modal/style.scss +114 -0
  76. package/src/tabbed-navigation/style.scss +5 -0
  77. package/src/tooltip-badge/index.test.js +24 -0
  78. package/src/tooltip-badge/index.tsx +40 -0
  79. package/src/wizard/index.js +25 -14
  80. package/src/wizard/index.test.js +38 -0
  81. package/src/wizard/store/index.js +1 -0
@@ -0,0 +1,114 @@
1
+ @use "~@wordpress/base-styles/colors" as wp-colors;
2
+ @use "~@wordpress/base-styles/mixins" as wp-mixins;
3
+ @use "~@wordpress/base-styles/variables" as wp-vars;
4
+
5
+ .newspack-reorder-modal {
6
+ &__body {
7
+ display: flex;
8
+ flex-direction: column;
9
+ min-height: calc(#{wp-vars.$grid-unit} * 23);
10
+ }
11
+
12
+ &__loading {
13
+ align-items: center;
14
+ display: flex;
15
+ flex: 1;
16
+ justify-content: center;
17
+ }
18
+
19
+ &__notice {
20
+ margin: 0 0 wp-vars.$grid-unit-10;
21
+ }
22
+
23
+ &__list {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: wp-vars.$grid-unit-10;
27
+ list-style: none;
28
+ margin: 0;
29
+ padding: 0;
30
+ }
31
+
32
+ &__item {
33
+ --newspack-reorder-modal-divider: #{wp-colors.$gray-100};
34
+ --wp-ui-card-padding: #{wp-vars.$grid-unit-15};
35
+
36
+ align-items: stretch;
37
+ cursor: grab;
38
+ flex-direction: row;
39
+ margin: 0;
40
+
41
+ &:focus-within,
42
+ &:hover {
43
+ --newspack-reorder-modal-divider: #{wp-colors.$gray-300};
44
+
45
+ border-color: wp-colors.$gray-300;
46
+ }
47
+
48
+ &.is-dragging {
49
+ cursor: grabbing;
50
+ opacity: 0.5;
51
+ }
52
+ }
53
+
54
+ &__grip {
55
+ align-items: center;
56
+ color: wp-colors.$gray-700;
57
+ display: flex;
58
+ padding-left: wp-vars.$grid-unit-05;
59
+ }
60
+
61
+ &__move {
62
+ align-items: center;
63
+ border-right: 1px solid var(--newspack-reorder-modal-divider);
64
+ display: flex;
65
+ flex-direction: column;
66
+ padding: wp-vars.$grid-unit-05;
67
+ }
68
+
69
+ &__content {
70
+ align-items: center;
71
+ align-self: center;
72
+ display: flex;
73
+ flex: 1;
74
+ gap: wp-vars.$grid-unit-10;
75
+ min-width: 0;
76
+ padding-block-end: var(--wp-ui-card-padding);
77
+ }
78
+
79
+ &__title {
80
+ @include wp-mixins.heading-large();
81
+
82
+ min-width: 0;
83
+ overflow: hidden;
84
+ text-overflow: ellipsis;
85
+ white-space: nowrap;
86
+ }
87
+
88
+ &__badge {
89
+ display: flex;
90
+ flex-shrink: 0;
91
+ }
92
+
93
+ &__toggle {
94
+ align-items: center;
95
+ display: flex;
96
+ padding-inline-end: wp-vars.$grid-unit-15;
97
+ }
98
+
99
+ &__description {
100
+ margin: 0 0 wp-vars.$grid-unit-20;
101
+ }
102
+
103
+ &__item.is-off &__title {
104
+ color: wp-colors.$gray-700;
105
+ }
106
+
107
+ &__footer {
108
+ display: flex;
109
+ gap: wp-vars.$grid-unit-10;
110
+ justify-content: flex-end;
111
+ margin-top: auto;
112
+ padding-top: wp-vars.$grid-unit-30;
113
+ }
114
+ }
@@ -21,6 +21,11 @@
21
21
  /* stylelint-disable-next-line plugin-wpds/no-setting-wpds-custom-properties -- deliberate Newspack brand override of the indicator token. */
22
22
  --wpds-color-stroke-interactive-neutral-strong: #{np-colors.$primary-600};
23
23
 
24
+ [role="tablist"] {
25
+ max-width: 100%;
26
+ overflow-x: auto;
27
+ }
28
+
24
29
  a[role="tab"] {
25
30
  text-decoration: none;
26
31
 
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports["default"] = void 0;
7
+ var _compose = require("@wordpress/compose");
8
+ var _ui = require("@wordpress/ui");
9
+ var _jsxRuntime = require("react/jsx-runtime");
10
+ /**
11
+ * WordPress dependencies
12
+ */
13
+
14
+ /**
15
+ * Internal dependencies
16
+ */
17
+
18
+ /**
19
+ * A badge whose tooltip explains it. The tooltip serves mouse and keyboard users;
20
+ * screen readers get the same text as the trigger's description, which keeps it
21
+ * out of the accessible name of a heading the badge sits in.
22
+ */
23
+ var _TooltipBadge = function TooltipBadge(_ref) {
24
+ var label = _ref.label,
25
+ tooltip = _ref.tooltip,
26
+ _ref$intent = _ref.intent,
27
+ intent = _ref$intent === void 0 ? 'none' : _ref$intent;
28
+ var descriptionId = (0, _compose.useInstanceId)(_TooltipBadge, 'newspack-tooltip-badge');
29
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
30
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_ui.Tooltip.Root, {
31
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Tooltip.Trigger, {
32
+ render: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
33
+ tabIndex: 0,
34
+ "aria-describedby": descriptionId
35
+ }),
36
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Badge, {
37
+ intent: intent,
38
+ children: label
39
+ })
40
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Tooltip.Popup, {
41
+ children: tooltip
42
+ })]
43
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
44
+ id: descriptionId,
45
+ hidden: true,
46
+ children: tooltip
47
+ })]
48
+ });
49
+ };
50
+ var _default = exports["default"] = _TooltipBadge;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _react = require("@testing-library/react");
5
+ var _ = _interopRequireDefault(require("./"));
6
+ var _jsxRuntime = require("react/jsx-runtime");
7
+ /**
8
+ * External dependencies
9
+ */
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+
15
+ describe('TooltipBadge', function () {
16
+ it('describes its focusable trigger with the tooltip text, keeping it out of a surrounding heading name', function () {
17
+ (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsxs)("h3", {
18
+ children: ["Registration wall", /*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
19
+ label: "Overrides paid access",
20
+ tooltip: "This grants registered readers access.",
21
+ intent: "low"
22
+ })]
23
+ }));
24
+ expect(_react.screen.getByRole('heading', {
25
+ name: 'Registration wall Overrides paid access'
26
+ })).toBeInTheDocument();
27
+ var trigger = _react.screen.getByText('Overrides paid access').closest('[tabindex="0"]');
28
+ expect(trigger).toHaveAccessibleDescription('This grants registered readers access.');
29
+ });
30
+ });
@@ -9,6 +9,7 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
9
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
10
  var _classnames = _interopRequireDefault(require("classnames"));
11
11
  var _components = require("@wordpress/components");
12
+ var _ui = require("@wordpress/ui");
12
13
  var _data = require("@wordpress/data");
13
14
  var _element = require("@wordpress/element");
14
15
  var _i18n = require("@wordpress/i18n");
@@ -114,12 +115,13 @@ var ResetHeaderData = function ResetHeaderData() {
114
115
  * can read the current route and derive the active-tab breadcrumb.
115
116
  */
116
117
  var WizardHeaderRegion = function WizardHeaderRegion(_ref) {
117
- var _activeSection;
118
+ var _activeSection, _activeSection2;
118
119
  var hideHeader = _ref.hideHeader,
119
120
  headerText = _ref.headerText,
120
121
  sections = _ref.sections,
121
122
  sectionName = _ref.sectionName,
122
123
  subTitle = _ref.subTitle,
124
+ headerSubTitle = _ref.headerSubTitle,
123
125
  actions = _ref.actions,
124
126
  wizardTabbedNavigation = _ref.tabbedNavigation,
125
127
  children = _ref.children;
@@ -149,11 +151,19 @@ var WizardHeaderRegion = function WizardHeaderRegion(_ref) {
149
151
  break;
150
152
  }
151
153
  }
154
+
155
+ // WizardError normally mounts inside the tab bar, so a route without one renders it itself.
152
156
  } catch (err) {
153
157
  _iterator.e(err);
154
158
  } finally {
155
159
  _iterator.f();
156
160
  }
161
+ if ((_activeSection = (0, _breadcrumbsSelect.activeSection)(sections, pathname)) !== null && _activeSection !== void 0 && _activeSection.hideTabbedNavigation && tabbedNavigation) {
162
+ tabbedNavigation = null;
163
+ children = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
164
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_WizardError["default"], {}), children]
165
+ });
166
+ }
157
167
  if (hideHeader) {
158
168
  // Without the Page shell the tabs still own the content: it renders
159
169
  // inside the active tab's panel.
@@ -173,10 +183,10 @@ var WizardHeaderRegion = function WizardHeaderRegion(_ref) {
173
183
  // Append any render-time leaf crumb(s) the section supplied via
174
184
  // headerData.sectionName (deduped against the current trailing label).
175
185
  breadcrumbItems = (0, _breadcrumbsSelect.appendSectionName)(breadcrumbItems, sectionName);
176
- var sectionSubTitle = (_activeSection = (0, _breadcrumbsSelect.activeSection)(sections, pathname)) === null || _activeSection === void 0 ? void 0 : _activeSection.subHeaderText;
186
+ var sectionSubTitle = (_activeSection2 = (0, _breadcrumbsSelect.activeSection)(sections, pathname)) === null || _activeSection2 === void 0 ? void 0 : _activeSection2.subHeaderText;
177
187
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_.Page, {
178
188
  breadcrumbItems: breadcrumbItems,
179
- subTitle: sectionSubTitle !== null && sectionSubTitle !== void 0 ? sectionSubTitle : subTitle,
189
+ subTitle: headerSubTitle || (sectionSubTitle !== null && sectionSubTitle !== void 0 ? sectionSubTitle : subTitle),
180
190
  actions: actions,
181
191
  tabbedNavigation: tabbedNavigation,
182
192
  children: children
@@ -189,7 +199,7 @@ var WizardHeaderRegion = function WizardHeaderRegion(_ref) {
189
199
  * @property {string} [subHeaderText] The sub-header text, optional.
190
200
  * @property {string} [apiSlug] The API slug, optional.
191
201
  * @property {string} [className] CSS classes, optional.
192
- * @property {any[]} sections Array of sections. A section's own `subHeaderText` replaces the wizard's while it is active.
202
+ * @property {any[]} sections Array of sections. A section's own `subHeaderText` replaces the wizard's while it is active, and `hideTabbedNavigation` hides the tab bar on its route.
193
203
  * Its optional `tabbedNavigation( params )` returns the tab items shown while its route matches.
194
204
  * @property {boolean} [hasSimpleFooter] Indicates if a simple footer is used, optional.
195
205
  * @property {() => void} [renderAboveSections] Function to render content above sections, optional.
@@ -247,7 +257,8 @@ var Wizard = function Wizard(_ref2, ref) {
247
257
  sectionSize = headerData.sectionSize,
248
258
  sectionTitle = headerData.sectionTitle,
249
259
  sectionPrimaryAction = headerData.sectionPrimaryAction,
250
- sectionSecondaryAction = headerData.sectionSecondaryAction;
260
+ sectionSecondaryAction = headerData.sectionSecondaryAction,
261
+ headerSubTitle = headerData.subTitle;
251
262
  var mainActions = actions === null || actions === void 0 ? void 0 : actions.filter(function (action) {
252
263
  return action.type === 'primary' || action.type === 'secondary';
253
264
  });
@@ -310,9 +321,10 @@ var Wizard = function Wizard(_ref2, ref) {
310
321
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
311
322
  children: [!pluginRequirementsKnown && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
312
323
  className: "newspack-wizard__loader",
313
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.__experimentalVStack, {
314
- alignment: "center",
315
- spacing: 2,
324
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_ui.Stack, {
325
+ direction: "column",
326
+ align: "center",
327
+ gap: "sm",
316
328
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_.Waiting, {
317
329
  noMargin: true
318
330
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)("strong", {
@@ -466,6 +478,7 @@ var Wizard = function Wizard(_ref2, ref) {
466
478
  sections: routedSections,
467
479
  sectionName: sectionName,
468
480
  subTitle: subHeaderText,
481
+ headerSubTitle: headerSubTitle,
469
482
  actions: headerActions,
470
483
  tabbedNavigation: tabbedNavigation,
471
484
  children: content
@@ -548,4 +548,82 @@ describe('Wizard section subtitle', function () {
548
548
  }
549
549
  }, _callee1);
550
550
  })));
551
+ });
552
+ describe('Wizard tab bar on hidden routes', function () {
553
+ beforeEach(function () {
554
+ _apiFetch["default"].mockReset();
555
+ _apiFetch["default"].mockResolvedValue({});
556
+ });
557
+ it('hides the tab bar on a hideTabbedNavigation route and restores it on a tab route', /*#__PURE__*/(0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator().m(function _callee10() {
558
+ var _t10, _t11, _t12;
559
+ return _regenerator().w(function (_context10) {
560
+ while (1) switch (_context10.n) {
561
+ case 0:
562
+ window.location.hash = '#/list';
563
+ (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
564
+ headerText: "Test wizard",
565
+ sections: [{
566
+ label: 'List',
567
+ path: '/list',
568
+ exact: true,
569
+ render: function render() {
570
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
571
+ children: "List view"
572
+ });
573
+ }
574
+ }, {
575
+ label: 'Other',
576
+ path: '/other',
577
+ exact: true,
578
+ render: function render() {
579
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
580
+ children: "Other view"
581
+ });
582
+ }
583
+ }, {
584
+ path: '/list/:id',
585
+ isHidden: true,
586
+ hideTabbedNavigation: true,
587
+ render: function render() {
588
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
589
+ children: "Item view"
590
+ });
591
+ }
592
+ }]
593
+ }));
594
+ _t10 = expect;
595
+ _context10.n = 1;
596
+ return _react.screen.findByText('List view');
597
+ case 1:
598
+ _t10(_context10.v).toBeInTheDocument();
599
+ expect(_react.screen.getByRole('tab', {
600
+ name: 'Other'
601
+ })).toBeInTheDocument();
602
+ (0, _react.act)(function () {
603
+ window.location.hash = '#/list/esp';
604
+ window.dispatchEvent(new HashChangeEvent('hashchange'));
605
+ });
606
+ _t11 = expect;
607
+ _context10.n = 2;
608
+ return _react.screen.findByText('Item view');
609
+ case 2:
610
+ _t11(_context10.v).toBeInTheDocument();
611
+ expect(_react.screen.queryByRole('tab')).not.toBeInTheDocument();
612
+ (0, _react.act)(function () {
613
+ window.location.hash = '#/list';
614
+ window.dispatchEvent(new HashChangeEvent('hashchange'));
615
+ });
616
+ _t12 = expect;
617
+ _context10.n = 3;
618
+ return _react.screen.findByText('List view');
619
+ case 3:
620
+ _t12(_context10.v).toBeInTheDocument();
621
+ expect(_react.screen.getByRole('tab', {
622
+ name: 'Other'
623
+ })).toBeInTheDocument();
624
+ case 4:
625
+ return _context10.a(2);
626
+ }
627
+ }, _callee10);
628
+ })));
551
629
  });
@@ -31,7 +31,8 @@ var DEFAULT_STATE = {
31
31
  badges: [],
32
32
  sectionDescription: '',
33
33
  sectionName: '',
34
- sectionTitle: ''
34
+ sectionTitle: '',
35
+ subTitle: ''
35
36
  },
36
37
  isLoading: false,
37
38
  isQuietLoading: false,
@@ -92,6 +92,7 @@ var Breadcrumbs = function Breadcrumbs(_ref3) {
92
92
  render: /*#__PURE__*/_jsx("ul", {}),
93
93
  direction: "row",
94
94
  align: "center",
95
+ wrap: "wrap",
95
96
  className: "newspack-breadcrumbs__list",
96
97
  children: [preceding.map(function (item, index) {
97
98
  return /*#__PURE__*/_jsxs("li", {
@@ -7,8 +7,8 @@
7
7
  padding: 0;
8
8
 
9
9
  > li {
10
- flex-shrink: 0;
11
10
  margin: 0;
11
+ min-width: 0;
12
12
 
13
13
  a {
14
14
  color: wp-colors.$gray-900;
@@ -20,11 +20,6 @@
20
20
  }
21
21
  }
22
22
  }
23
-
24
- > li:last-child {
25
- flex-shrink: 1;
26
- min-width: 0;
27
- }
28
23
  }
29
24
 
30
25
  .newspack-breadcrumbs__count {
@@ -39,9 +34,7 @@
39
34
  }
40
35
 
41
36
  .newspack-breadcrumbs__list h1.newspack-breadcrumbs__current {
42
- overflow: hidden;
43
- text-overflow: ellipsis;
44
- white-space: nowrap;
37
+ overflow-wrap: break-word;
45
38
  padding: 0;
46
- line-height: normal;
39
+ line-height: var(--wpds-typography-line-height-md, #{wp-vars.$grid-unit-30});
47
40
  }
@@ -221,7 +221,7 @@
221
221
  color: inherit;
222
222
  text-decoration: none;
223
223
  &:hover {
224
- text-decoration: underline;
224
+ color: var(--wp-admin-theme-color);
225
225
  }
226
226
  }
227
227
  }
@@ -7,23 +7,24 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
7
7
  * Card - Sortable list component.
8
8
  */
9
9
 
10
+ /**
11
+ * External dependencies
12
+ */
13
+ import classNames from 'classnames';
14
+
10
15
  /**
11
16
  * WordPress dependencies.
12
17
  */
13
- import { Disabled, Draggable, __experimentalVStack as VStack } from '@wordpress/components'; // eslint-disable-line @wordpress/no-unsafe-wp-apis
18
+ import { Disabled, Draggable } from '@wordpress/components';
14
19
  import { useEffect, useLayoutEffect, useRef, useState } from '@wordpress/element';
15
- import { Badge } from '@wordpress/ui';
20
+ import { Badge, Stack } from '@wordpress/ui';
16
21
 
17
22
  /**
18
23
  * Internal dependencies
19
24
  */
20
- import { Card } from '../';
25
+ import Card from '../card';
26
+ import TooltipBadge from '../tooltip-badge';
21
27
  import './style.scss';
22
-
23
- /**
24
- * External dependencies
25
- */
26
- import classNames from 'classnames';
27
28
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
28
29
  var DROP_ANIMATION_DURATION = 400; // ms — must match $drop-duration in style.scss
29
30
  var BUTTON_MOVE_DURATION = 200; // ms — must match $shift-duration in style.scss
@@ -346,13 +347,14 @@ var CardSortableList = function CardSortableList(_ref) {
346
347
  return 0;
347
348
  };
348
349
  var isDragging = draggingIndex !== null;
349
- return /*#__PURE__*/_jsx(VStack, {
350
+ return /*#__PURE__*/_jsx(Stack, {
351
+ direction: "column",
352
+ gap: "lg",
350
353
  ref: listRef,
351
354
  className: classNames('newspack-card--core--sortable-list', disabled && 'newspack-card--core--sortable-list__is-disabled', isDragging && 'newspack-card--core--sortable-list__is-dragging'),
352
355
  style: measurements ? {
353
356
  height: measurements.lockedHeight
354
357
  } : undefined,
355
- spacing: 4,
356
358
  children: sortedItems.map(function (item, index) {
357
359
  var translateY = getTranslateY(index);
358
360
  return /*#__PURE__*/_jsx(Disabled, {
@@ -384,7 +386,7 @@ var CardSortableList = function CardSortableList(_ref) {
384
386
  onDragEnd: handleDragEnd,
385
387
  appendToOwnerDocument: true,
386
388
  children: function children(_ref2) {
387
- var _item$badge, _item$badge$intent;
389
+ var _item$badge, _item$badge$intent, _item$secondaryBadge, _item$secondaryBadge$;
388
390
  var onDraggableStart = _ref2.onDraggableStart,
389
391
  onDraggableEnd = _ref2.onDraggableEnd;
390
392
  return /*#__PURE__*/_jsx(Card, {
@@ -399,7 +401,14 @@ var CardSortableList = function CardSortableList(_ref) {
399
401
  children: [item.title, ((_item$badge = item.badge) === null || _item$badge === void 0 ? void 0 : _item$badge.label) && /*#__PURE__*/_jsx(Badge, {
400
402
  intent: (_item$badge$intent = item.badge.intent) !== null && _item$badge$intent !== void 0 ? _item$badge$intent : 'none',
401
403
  children: item.badge.label
402
- })]
404
+ }), ((_item$secondaryBadge = item.secondaryBadge) === null || _item$secondaryBadge === void 0 ? void 0 : _item$secondaryBadge.label) && (item.secondaryBadge.tooltip ? /*#__PURE__*/_jsx(TooltipBadge, {
405
+ label: item.secondaryBadge.label,
406
+ tooltip: item.secondaryBadge.tooltip,
407
+ intent: item.secondaryBadge.intent
408
+ }) : /*#__PURE__*/_jsx(Badge, {
409
+ intent: (_item$secondaryBadge$ = item.secondaryBadge.intent) !== null && _item$secondaryBadge$ !== void 0 ? _item$secondaryBadge$ : 'none',
410
+ children: item.secondaryBadge.label
411
+ }))]
403
412
  }), item.description && /*#__PURE__*/_jsx("p", {
404
413
  children: item.description
405
414
  })]
@@ -412,7 +421,10 @@ var CardSortableList = function CardSortableList(_ref) {
412
421
  }, item.onToggleChange !== undefined && {
413
422
  actionType: 'toggle',
414
423
  isActive: item.toggleChecked,
415
- onToggle: item.onToggleChange
424
+ onToggle: item.onToggleChange,
425
+ // Names the toggle for assistive tech. The card hides this
426
+ // label, since the header already shows the title.
427
+ title: item.title
416
428
  }), item.actions !== undefined && {
417
429
  actions: item.actions
418
430
  })
@@ -43,4 +43,25 @@ describe('CardSortableList', function () {
43
43
  // An unguarded Badge would still emit an empty span, so assert on element children.
44
44
  expect(container.querySelector('h3').children).toHaveLength(0);
45
45
  });
46
+ it('names each toggle after its item', function () {
47
+ render(/*#__PURE__*/_jsx(CardSortableList, {
48
+ items: [{
49
+ id: 1,
50
+ title: 'Homepage',
51
+ toggleChecked: true,
52
+ onToggleChange: function onToggleChange() {}
53
+ }, {
54
+ id: 2,
55
+ title: 'About',
56
+ toggleChecked: false,
57
+ onToggleChange: function onToggleChange() {}
58
+ }]
59
+ }));
60
+ expect(screen.getByRole('checkbox', {
61
+ name: 'Homepage'
62
+ })).toBeChecked();
63
+ expect(screen.getByRole('checkbox', {
64
+ name: 'About'
65
+ })).not.toBeChecked();
66
+ });
46
67
  });
@@ -6,6 +6,14 @@ $drop-duration: 400ms; /* must match DROP_ANIMATION_DURATION in index.tsx */
6
6
  &__item {
7
7
  cursor: grab;
8
8
 
9
+ /*
10
+ * Keep a card's title in the case it was written in. The block editor
11
+ * sidebar uppercases every h3 (`.interface-complementary-area h3`).
12
+ */
13
+ .newspack-card--core__header-content h3 {
14
+ text-transform: none;
15
+ }
16
+
9
17
  /*
10
18
  * Animate items sliding to fill/make space around the dragged item.
11
19
  */
@@ -2,9 +2,9 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
  /**
3
3
  * WordPress dependencies.
4
4
  */
5
- import { __experimentalVStack as VStack } from '@wordpress/components'; // eslint-disable-line @wordpress/no-unsafe-wp-apis
6
5
  import { useEffect, useState } from '@wordpress/element';
7
6
  import { __ } from '@wordpress/i18n';
7
+ import { Stack } from '@wordpress/ui';
8
8
 
9
9
  /**
10
10
  * Internal dependencies.
@@ -54,11 +54,10 @@ var Body = function Body(_ref) {
54
54
  mutation.disconnect();
55
55
  };
56
56
  }, [node]);
57
- return /*#__PURE__*/_jsx(VStack, {
57
+ return /*#__PURE__*/_jsx(Stack, {
58
58
  ref: setNode,
59
- className: "newspack-drawer__body",
60
- spacing: 0,
61
- justify: "flex-start"
59
+ direction: "column",
60
+ className: "newspack-drawer__body"
62
61
  // A bare div is role=generic, where ARIA prohibits an author name.
63
62
  ,
64
63
  role: isScrollable ? 'group' : undefined,
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies.
3
3
  */
4
- import { __experimentalVStack as VStack } from '@wordpress/components'; // eslint-disable-line @wordpress/no-unsafe-wp-apis
5
- import { Children, isValidElement } from '@wordpress/element';
4
+ import { Stack } from '@wordpress/ui';
6
5
 
7
6
  /**
8
7
  * External dependencies.
@@ -13,51 +12,23 @@ import classnames from 'classnames';
13
12
  * Internal dependencies.
14
13
  */
15
14
  import { jsx as _jsx } from "react/jsx-runtime";
16
- // VStack drops non-elements, a lone string aside. A run shares one wrapper so
17
- // `Edited by { name }` is one row, not three.
18
- var asRows = function asRows(children) {
19
- if ('string' === typeof children) {
20
- return children;
21
- }
22
- var rows = [];
23
- var text = [];
24
- var flushText = function flushText() {
25
- if (text.some(function (part) {
26
- return '' !== String(part).trim();
27
- })) {
28
- rows.push(/*#__PURE__*/_jsx("div", {
29
- children: text
30
- }, "text-".concat(rows.length)));
31
- }
32
- text = [];
33
- };
34
- Children.toArray(children).forEach(function (child) {
35
- if (isValidElement(child)) {
36
- flushText();
37
- rows.push(child);
38
- } else {
39
- text.push(child);
40
- }
41
- });
42
- flushText();
43
- return rows;
44
- };
45
15
  var Content = function Content(_ref) {
46
16
  var _ref$padding = _ref.padding,
47
17
  padding = _ref$padding === void 0 ? 6 : _ref$padding,
48
18
  _ref$gap = _ref.gap,
49
- gap = _ref$gap === void 0 ? 4 : _ref$gap,
19
+ gap = _ref$gap === void 0 ? 'lg' : _ref$gap,
50
20
  className = _ref.className,
51
21
  children = _ref.children;
52
- return /*#__PURE__*/_jsx(VStack, {
53
- className: classnames('newspack-drawer__content', className),
54
- spacing: gap
22
+ return /*#__PURE__*/_jsx(Stack, {
23
+ direction: "column",
24
+ gap: gap,
25
+ className: classnames('newspack-drawer__content', className)
55
26
  // A custom property, not inline padding, so the stylesheet's seam rules win.
56
27
  ,
57
28
  style: {
58
29
  '--newspack-drawer-content-padding': "".concat(padding * 4, "px")
59
30
  },
60
- children: asRows(children)
31
+ children: children
61
32
  });
62
33
  };
63
34
  export default Content;