newspack-components 4.5.0 → 4.6.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 (138) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/DEVELOPMENT.md +1 -1
  3. package/dist/cjs/accordion/index.js +43 -26
  4. package/dist/cjs/accordion/index.test.js +80 -0
  5. package/dist/cjs/accordion/style.scss +25 -21
  6. package/dist/cjs/action-card/style.scss +0 -22
  7. package/dist/cjs/breadcrumbs/index.js +80 -0
  8. package/dist/cjs/breadcrumbs/index.test.js +109 -0
  9. package/dist/cjs/breadcrumbs/style.scss +41 -0
  10. package/dist/cjs/card/core-card.js +8 -4
  11. package/dist/cjs/card/index.js +1 -1
  12. package/dist/cjs/card/style-core.scss +14 -0
  13. package/dist/cjs/card/style.scss +0 -9
  14. package/dist/cjs/card-feature/index.js +25 -13
  15. package/dist/cjs/card-settings-group/index.js +3 -0
  16. package/dist/cjs/confirm-dialog/index.js +34 -8
  17. package/dist/cjs/confirm-dialog/index.test.js +173 -0
  18. package/dist/cjs/divider/style.scss +4 -2
  19. package/dist/cjs/grid/style.scss +5 -0
  20. package/dist/cjs/handoff/index.js +8 -5
  21. package/dist/cjs/index.js +34 -1
  22. package/dist/cjs/integration-icon/index.js +40 -0
  23. package/dist/cjs/integration-icon/style.scss +42 -0
  24. package/dist/cjs/integration-icons/index.js +21 -2
  25. package/dist/cjs/modal/style.scss +4 -0
  26. package/dist/cjs/page/index.js +121 -0
  27. package/dist/cjs/page/index.test.js +69 -0
  28. package/dist/cjs/page/style.scss +86 -0
  29. package/dist/cjs/route-match.js +59 -0
  30. package/dist/cjs/route-match.test.js +77 -0
  31. package/dist/cjs/section-header/index.js +8 -1
  32. package/dist/cjs/section-header/style.scss +11 -11
  33. package/dist/cjs/select-control/GroupedSelectControl.js +1 -1
  34. package/dist/cjs/select-control/style.scss +4 -0
  35. package/dist/cjs/style.scss +1 -4
  36. package/dist/cjs/tabbed-navigation/index.js +188 -40
  37. package/dist/cjs/tabbed-navigation/index.test.js +299 -0
  38. package/dist/cjs/tabbed-navigation/style.scss +77 -57
  39. package/dist/cjs/with-wizard/index.js +34 -45
  40. package/dist/cjs/with-wizard-screen/index.js +82 -41
  41. package/dist/cjs/with-wizard-screen/index.test.js +50 -0
  42. package/dist/cjs/with-wizard-screen/style.scss +15 -118
  43. package/dist/cjs/wizard/breadcrumbs-select.js +70 -0
  44. package/dist/cjs/wizard/breadcrumbs-select.test.js +200 -0
  45. package/dist/cjs/wizard/components/WizardError.js +8 -4
  46. package/dist/cjs/wizard/index.js +162 -141
  47. package/dist/esm/accordion/index.js +42 -26
  48. package/dist/esm/accordion/index.test.js +79 -0
  49. package/dist/esm/accordion/style.scss +25 -21
  50. package/dist/esm/action-card/style.scss +0 -22
  51. package/dist/esm/breadcrumbs/index.js +75 -0
  52. package/dist/esm/breadcrumbs/index.test.js +105 -0
  53. package/dist/esm/breadcrumbs/style.scss +41 -0
  54. package/dist/esm/card/core-card.js +8 -4
  55. package/dist/esm/card/index.js +1 -1
  56. package/dist/esm/card/style-core.scss +14 -0
  57. package/dist/esm/card/style.scss +0 -9
  58. package/dist/esm/card-feature/index.js +25 -13
  59. package/dist/esm/card-settings-group/index.js +3 -0
  60. package/dist/esm/confirm-dialog/index.js +34 -8
  61. package/dist/esm/confirm-dialog/index.test.js +173 -0
  62. package/dist/esm/divider/style.scss +4 -2
  63. package/dist/esm/grid/style.scss +5 -0
  64. package/dist/esm/handoff/index.js +7 -5
  65. package/dist/esm/index.js +5 -1
  66. package/dist/esm/integration-icon/index.js +34 -0
  67. package/dist/esm/integration-icon/style.scss +42 -0
  68. package/dist/esm/integration-icons/index.js +22 -1
  69. package/dist/esm/modal/style.scss +4 -0
  70. package/dist/esm/page/index.js +113 -0
  71. package/dist/esm/page/index.test.js +65 -0
  72. package/dist/esm/page/style.scss +86 -0
  73. package/dist/esm/route-match.js +51 -0
  74. package/dist/esm/route-match.test.js +74 -0
  75. package/dist/esm/section-header/index.js +8 -1
  76. package/dist/esm/section-header/style.scss +11 -11
  77. package/dist/esm/select-control/GroupedSelectControl.js +1 -1
  78. package/dist/esm/select-control/style.scss +4 -0
  79. package/dist/esm/style.scss +1 -4
  80. package/dist/esm/tabbed-navigation/index.js +192 -38
  81. package/dist/esm/tabbed-navigation/index.test.js +296 -0
  82. package/dist/esm/tabbed-navigation/style.scss +77 -57
  83. package/dist/esm/with-wizard/index.js +33 -45
  84. package/dist/esm/with-wizard-screen/index.js +78 -41
  85. package/dist/esm/with-wizard-screen/index.test.js +46 -0
  86. package/dist/esm/with-wizard-screen/style.scss +15 -118
  87. package/dist/esm/wizard/breadcrumbs-select.js +63 -0
  88. package/dist/esm/wizard/breadcrumbs-select.test.js +197 -0
  89. package/dist/esm/wizard/components/WizardError.js +7 -5
  90. package/dist/esm/wizard/index.js +165 -144
  91. package/package.json +10 -2
  92. package/src/accordion/index.js +27 -15
  93. package/src/accordion/index.test.js +62 -0
  94. package/src/accordion/style.scss +25 -21
  95. package/src/action-card/style.scss +0 -22
  96. package/src/breadcrumbs/index.js +63 -0
  97. package/src/breadcrumbs/index.test.js +59 -0
  98. package/src/breadcrumbs/style.scss +41 -0
  99. package/src/card/core-card.js +11 -6
  100. package/src/card/index.js +0 -1
  101. package/src/card/style-core.scss +14 -0
  102. package/src/card/style.scss +0 -9
  103. package/src/card-feature/README.md +3 -3
  104. package/src/card-feature/index.tsx +37 -21
  105. package/src/card-settings-group/index.tsx +4 -0
  106. package/src/card-sortable-list/index.tsx +1 -1
  107. package/src/confirm-dialog/index.test.js +142 -0
  108. package/src/confirm-dialog/index.tsx +34 -7
  109. package/src/divider/style.scss +4 -2
  110. package/src/grid/style.scss +5 -0
  111. package/src/handoff/index.js +4 -3
  112. package/src/index.js +5 -1
  113. package/src/integration-icon/index.tsx +35 -0
  114. package/src/integration-icon/style.scss +42 -0
  115. package/src/integration-icons/index.js +23 -0
  116. package/src/modal/style.scss +4 -0
  117. package/src/page/index.js +89 -0
  118. package/src/page/index.test.js +43 -0
  119. package/src/page/style.scss +86 -0
  120. package/src/route-match.js +47 -0
  121. package/src/route-match.test.js +50 -0
  122. package/src/section-header/index.js +7 -3
  123. package/src/section-header/style.scss +11 -11
  124. package/src/select-control/GroupedSelectControl.js +1 -1
  125. package/src/select-control/style.scss +4 -0
  126. package/src/style.scss +1 -4
  127. package/src/tabbed-navigation/index.js +181 -34
  128. package/src/tabbed-navigation/index.test.js +228 -0
  129. package/src/tabbed-navigation/style.scss +77 -57
  130. package/src/with-wizard/index.js +18 -39
  131. package/src/with-wizard-screen/index.js +70 -35
  132. package/src/with-wizard-screen/index.test.js +36 -0
  133. package/src/with-wizard-screen/style.scss +15 -118
  134. package/src/wizard/breadcrumbs-select.js +51 -0
  135. package/src/wizard/breadcrumbs-select.test.js +124 -0
  136. package/src/wizard/components/WizardError.js +4 -3
  137. package/src/wizard/index.js +160 -146
  138. package/tsconfig.json +7 -0
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports["default"] = void 0;
8
+ var _adminUi = require("@wordpress/admin-ui");
9
+ var _components = require("@wordpress/components");
10
+ var _element = require("@wordpress/element");
11
+ var _icons = require("@wordpress/icons");
12
+ var _ui = require("@wordpress/ui");
13
+ var _classnames = _interopRequireDefault(require("classnames"));
14
+ var _newspackIcons = require("newspack-icons");
15
+ var _breadcrumbs = _interopRequireDefault(require("../breadcrumbs"));
16
+ require("./style.scss");
17
+ var _jsxRuntime = require("react/jsx-runtime");
18
+ /**
19
+ * WordPress dependencies.
20
+ */
21
+
22
+ // eslint-disable-line @wordpress/no-unsafe-wp-apis
23
+
24
+ /**
25
+ * External dependencies.
26
+ */
27
+
28
+ /**
29
+ * Internal dependencies.
30
+ */
31
+
32
+ /**
33
+ * Newspack page region: a sticky header block (breadcrumbs, actions, optional
34
+ * tabbed navigation) followed by the page content.
35
+ *
36
+ * This intentionally does not use `@wordpress/admin-ui`'s `Page`: that
37
+ * component has no slot for content rendered between the header and the body
38
+ * inside the sticky region, which is exactly where the tab bar must live. Until
39
+ * admin-ui grows such a slot, the header markup is assembled here from the same
40
+ * design-system primitives and tokens, so it can be swapped back later.
41
+ *
42
+ * @param {Object} props
43
+ * @param {Array} props.breadcrumbItems Trail items: `{ label, url? }`.
44
+ * @param {*} [props.badges]
45
+ * @param {*} [props.subTitle]
46
+ * @param {*} [props.actions]
47
+ * @param {*} [props.tabbedNavigation] A `TabbedNavigation` element; its bar renders inside
48
+ * the sticky header block and the page children render
49
+ * inside its active tab panel — or, when no visible tab
50
+ * owns the route, as a sibling of the panels.
51
+ * @param {string} [props.className]
52
+ * @param {*} props.children
53
+ * @return {JSX.Element} Page component.
54
+ */var Page = function Page(_ref) {
55
+ var _breadcrumbItems;
56
+ var _ref$breadcrumbItems = _ref.breadcrumbItems,
57
+ breadcrumbItems = _ref$breadcrumbItems === void 0 ? [] : _ref$breadcrumbItems,
58
+ badges = _ref.badges,
59
+ subTitle = _ref.subTitle,
60
+ actions = _ref.actions,
61
+ tabbedNavigation = _ref.tabbedNavigation,
62
+ className = _ref.className,
63
+ children = _ref.children;
64
+ var currentLabel = (_breadcrumbItems = breadcrumbItems[breadcrumbItems.length - 1]) === null || _breadcrumbItems === void 0 ? void 0 : _breadcrumbItems.label;
65
+ var renderShell = function renderShell(tabBar, content) {
66
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
67
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_ui.Stack, {
68
+ direction: "column",
69
+ className: "newspack-page__header-region",
70
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_ui.Stack, {
71
+ direction: "column",
72
+ className: "newspack-page__header",
73
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_ui.Stack, {
74
+ direction: "row",
75
+ gap: "sm",
76
+ justify: "space-between",
77
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_ui.Stack, {
78
+ direction: "row",
79
+ gap: "sm",
80
+ align: "center",
81
+ justify: "start",
82
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
83
+ className: "newspack-page__header-visual",
84
+ "aria-hidden": "true",
85
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.Icon, {
86
+ icon: _newspackIcons.newspack
87
+ })
88
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.__experimentalHStack, {
89
+ className: "newspack-page__breadcrumbs",
90
+ justify: "flex-start",
91
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_breadcrumbs["default"], {
92
+ items: breadcrumbItems
93
+ })
94
+ }), badges]
95
+ }), actions && /*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Stack, {
96
+ direction: "row",
97
+ gap: "sm",
98
+ align: "center",
99
+ className: "newspack-page__header-actions",
100
+ children: actions
101
+ })]
102
+ }), subTitle && /*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Text, {
103
+ render: /*#__PURE__*/(0, _jsxRuntime.jsx)("p", {}),
104
+ variant: "body-md",
105
+ className: "newspack-page__header-subtitle",
106
+ children: subTitle
107
+ })]
108
+ }), tabBar]
109
+ }), content]
110
+ });
111
+ };
112
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_adminUi.NavigableRegion, {
113
+ className: (0, _classnames["default"])('newspack-page', className),
114
+ ariaLabel: currentLabel,
115
+ children: (0, _element.isValidElement)(tabbedNavigation) ? (0, _element.cloneElement)(tabbedNavigation, {
116
+ renderShell: renderShell,
117
+ content: children
118
+ }) : renderShell(null, children)
119
+ });
120
+ };
121
+ var _default = exports["default"] = Page;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _react = require("@testing-library/react");
5
+ var _reactRouterDom = require("react-router-dom");
6
+ var _ = _interopRequireDefault(require("./"));
7
+ var _jsxRuntime = require("react/jsx-runtime");
8
+ /**
9
+ * External dependencies.
10
+ */
11
+
12
+ /**
13
+ * Internal dependencies.
14
+ */
15
+
16
+ var renderInRouter = function renderInRouter(ui) {
17
+ return (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactRouterDom.HashRouter, {
18
+ children: ui
19
+ }));
20
+ };
21
+ describe('Page', function () {
22
+ it('renders the breadcrumb current item as h1 and the content', function () {
23
+ renderInRouter(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
24
+ breadcrumbItems: [{
25
+ label: 'Audience',
26
+ url: '/'
27
+ }, {
28
+ label: 'Access control'
29
+ }],
30
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
31
+ children: "Body content"
32
+ })
33
+ }));
34
+ expect(_react.screen.getByRole('heading', {
35
+ level: 1
36
+ })).toHaveTextContent('Access control');
37
+ expect(_react.screen.getByText('Body content')).toBeInTheDocument();
38
+ });
39
+ it('renders actions and subtitle', function () {
40
+ renderInRouter(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
41
+ breadcrumbItems: [{
42
+ label: 'Audience'
43
+ }],
44
+ subTitle: "Manage readers",
45
+ actions: /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
46
+ children: "Add new"
47
+ }),
48
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {})
49
+ }));
50
+ expect(_react.screen.getByRole('button', {
51
+ name: 'Add new'
52
+ })).toBeInTheDocument();
53
+ expect(_react.screen.getByText('Manage readers')).toBeInTheDocument();
54
+ });
55
+ it('names the page region after the current breadcrumb item', function () {
56
+ renderInRouter(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
57
+ breadcrumbItems: [{
58
+ label: 'Audience',
59
+ url: '/'
60
+ }, {
61
+ label: 'Access control'
62
+ }],
63
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {})
64
+ }));
65
+ expect(_react.screen.getByRole('region', {
66
+ name: 'Access control'
67
+ })).toBeInTheDocument();
68
+ });
69
+ });
@@ -0,0 +1,86 @@
1
+ @use "../../../colors/colors.module" as colors;
2
+ @use "~@wordpress/base-styles/colors" as wp-colors;
3
+ @use "~@wordpress/base-styles/mixins" as wp-mixins;
4
+ @use "~@wordpress/base-styles/variables" as wp-vars;
5
+
6
+ // Design-system tokens ride with the Page header into any consuming bundle.
7
+ // Known tech debt: this reaches past the package's public exports entry
8
+ // (`@wordpress/theme/design-tokens.css`) into its internal layout, because
9
+ // postcss-import cannot resolve exports maps, and resolving the public
10
+ // specifier through sass/webpack leaks a bogus `wp-theme/design-tokens.css`
11
+ // script dependency into consumers' asset manifests, which silently blocks
12
+ // their enqueues. If upstream moves `src/prebuilt/`, this fails loudly at
13
+ // build time.
14
+ /* stylelint-disable-next-line no-invalid-position-at-import-rule -- sass @use must precede the CSS @import; postcss-import inlines it at build time. */
15
+ @import "@wordpress/theme/src/prebuilt/css/design-tokens.css";
16
+
17
+ // Newspack brand mapping: the wpds brand color tokens ship in WordPress
18
+ // blue; remap them onto the Newspack primary scale so every design-system
19
+ // component (focus rings, brand buttons, toggles) renders in brand colors
20
+ // without per-component overrides.
21
+ /* stylelint-disable plugin-wpds/no-setting-wpds-custom-properties -- deliberate Newspack brand override of the wpds brand tokens. */
22
+ :root {
23
+ --wpds-color-background-interactive-brand-strong: #{colors.$primary-600};
24
+ --wpds-color-background-interactive-brand-strong-active: #{colors.$primary-800};
25
+ --wpds-color-background-interactive-brand-weak-active: #{colors.$primary-000};
26
+ --wpds-color-background-surface-brand: #{colors.$primary-000};
27
+ --wpds-color-background-thumb-brand: #{colors.$primary-600};
28
+ --wpds-color-background-thumb-brand-active: #{colors.$primary-600};
29
+ --wpds-color-foreground-interactive-brand: #{colors.$primary-600};
30
+ --wpds-color-foreground-interactive-brand-active: #{colors.$primary-800};
31
+ --wpds-color-foreground-interactive-brand-disabled: #{colors.$primary-400};
32
+ --wpds-color-stroke-focus: #{colors.$primary-600};
33
+ --wpds-color-stroke-interactive-brand: #{colors.$primary-600};
34
+ --wpds-color-stroke-interactive-brand-active: #{colors.$primary-800};
35
+ --wpds-color-stroke-interactive-brand-disabled: #{colors.$primary-100};
36
+ --wpds-color-stroke-surface-brand: #{colors.$primary-000};
37
+ --wpds-color-stroke-surface-brand-strong: #{colors.$primary-600};
38
+ }
39
+ /* stylelint-enable plugin-wpds/no-setting-wpds-custom-properties */
40
+
41
+ [role="region"].newspack-page {
42
+ background-color: wp-colors.$white;
43
+ }
44
+
45
+ .newspack-page__header-region {
46
+ @include wp-mixins.break-medium {
47
+ position: sticky;
48
+ top: var(--wp-admin--admin-bar--height);
49
+ z-index: 3;
50
+ }
51
+ }
52
+
53
+ .newspack-page__header {
54
+ /* stylelint-disable-next-line plugin-wpds/no-unknown-ds-tokens -- the plugin's token list lags the prebuilt design-tokens.css names. */
55
+ background: var(--wpds-color-background-surface-neutral-strong);
56
+ border-block-end: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral-weak);
57
+ padding: var(--wpds-dimension-padding-lg) var(--wpds-dimension-padding-2xl);
58
+ }
59
+
60
+ .newspack-page__header-actions {
61
+ flex-shrink: 0;
62
+ }
63
+
64
+ .newspack-page__header-subtitle {
65
+ /* stylelint-disable-next-line plugin-wpds/no-unknown-ds-tokens -- the plugin's token list lags the prebuilt design-tokens.css names. */
66
+ color: var(--wpds-color-foreground-content-neutral-weak);
67
+ padding-block-end: var(--wpds-dimension-padding-xs);
68
+ }
69
+
70
+ .newspack-page__header-visual {
71
+ display: grid;
72
+ flex-shrink: 0;
73
+ height: 24px;
74
+ place-items: center;
75
+ width: 24px;
76
+
77
+ svg path {
78
+ transform: scale(1.1111);
79
+ transform-box: view-box;
80
+ transform-origin: center;
81
+ }
82
+ }
83
+
84
+ .newspack-page__breadcrumbs {
85
+ min-height: wp-vars.$button-size;
86
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.matchesRoute = void 0;
8
+ var _router = _interopRequireDefault(require("./proxied-imports/router"));
9
+ /**
10
+ * Internal dependencies.
11
+ */
12
+
13
+ var matchPath = _router["default"].matchPath;
14
+
15
+ /**
16
+ * Whether a wildcard pattern (`'/base/*'` or `'/base*'`) matches a pathname.
17
+ * The wildcard is anchored at a `/` boundary: `'/segments*'` matches
18
+ * `/segments` and `/segments/123`, never `/segments-old`.
19
+ *
20
+ * @param {string} pattern Pattern ending in `*`.
21
+ * @param {string} pathname Current router pathname.
22
+ * @return {boolean} Whether the pattern matches.
23
+ */
24
+ var matchesWildcard = function matchesWildcard(pattern, pathname) {
25
+ var base = pattern.slice(0, -1).replace(/\/$/, '');
26
+ return pathname === base || pathname.startsWith(base + '/');
27
+ };
28
+
29
+ /**
30
+ * Single route matcher shared by breadcrumb selection and tabbed navigation.
31
+ *
32
+ * An item matches when one of its `activeTabPaths` hits (exact string, or a
33
+ * `*` wildcard anchored at a `/` boundary), or — falling through on a miss —
34
+ * when its `path` matches the pathname. Path matching is prefix-based except
35
+ * for the root path and items opting in via `exact: true`, mirroring how the
36
+ * wizard registers its `<Route>`s (`exact={ section.exact ?? false }`).
37
+ *
38
+ * @param {Object} item Route item (`{ path?, exact?, activeTabPaths? }`).
39
+ * @param {string} pathname Current router pathname.
40
+ * @return {boolean} Whether the item matches the pathname.
41
+ */
42
+ var matchesRoute = exports.matchesRoute = function matchesRoute(item, pathname) {
43
+ if (Array.isArray(item.activeTabPaths)) {
44
+ var wildcardHit = item.activeTabPaths.some(function (path) {
45
+ return path.endsWith('*') ? matchesWildcard(path, pathname) : path === pathname;
46
+ });
47
+ if (wildcardHit) {
48
+ return true;
49
+ }
50
+ }
51
+ if (!item.path) {
52
+ return false;
53
+ }
54
+ var exact = '/' === item.path || item.exact === true;
55
+ return !!matchPath(pathname, {
56
+ path: item.path,
57
+ exact: exact
58
+ });
59
+ };
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+
3
+ var _routeMatch = require("./route-match");
4
+ /**
5
+ * Internal dependencies.
6
+ */
7
+
8
+ describe('matchesRoute', function () {
9
+ it('matches the root path exactly, never as a prefix', function () {
10
+ expect((0, _routeMatch.matchesRoute)({
11
+ path: '/'
12
+ }, '/')).toBe(true);
13
+ expect((0, _routeMatch.matchesRoute)({
14
+ path: '/'
15
+ }, '/segments')).toBe(false);
16
+ });
17
+ it('matches a path as a prefix by default, at segment boundaries', function () {
18
+ expect((0, _routeMatch.matchesRoute)({
19
+ path: '/segments'
20
+ }, '/segments')).toBe(true);
21
+ expect((0, _routeMatch.matchesRoute)({
22
+ path: '/segments'
23
+ }, '/segments/123')).toBe(true);
24
+ expect((0, _routeMatch.matchesRoute)({
25
+ path: '/segments'
26
+ }, '/segments-old')).toBe(false);
27
+ expect((0, _routeMatch.matchesRoute)({
28
+ path: '/segments'
29
+ }, '/donations')).toBe(false);
30
+ });
31
+ it('matches exactly when the item opts in via exact', function () {
32
+ expect((0, _routeMatch.matchesRoute)({
33
+ path: '/segments',
34
+ exact: true
35
+ }, '/segments')).toBe(true);
36
+ expect((0, _routeMatch.matchesRoute)({
37
+ path: '/segments',
38
+ exact: true
39
+ }, '/segments/123')).toBe(false);
40
+ });
41
+ it('matches an activeTabPaths entry exactly', function () {
42
+ var item = {
43
+ path: '/other',
44
+ activeTabPaths: ['/segments/new']
45
+ };
46
+ expect((0, _routeMatch.matchesRoute)(item, '/segments/new')).toBe(true);
47
+ expect((0, _routeMatch.matchesRoute)(item, '/segments/123')).toBe(false);
48
+ });
49
+ it('anchors an activeTabPaths wildcard at a segment boundary', function () {
50
+ var item = {
51
+ path: '/other',
52
+ activeTabPaths: ['/segments/*']
53
+ };
54
+ expect((0, _routeMatch.matchesRoute)(item, '/segments')).toBe(true);
55
+ expect((0, _routeMatch.matchesRoute)(item, '/segments/123')).toBe(true);
56
+ expect((0, _routeMatch.matchesRoute)(item, '/segments-old')).toBe(false);
57
+ var noSlash = {
58
+ path: '/other',
59
+ activeTabPaths: ['/segments*']
60
+ };
61
+ expect((0, _routeMatch.matchesRoute)(noSlash, '/segments')).toBe(true);
62
+ expect((0, _routeMatch.matchesRoute)(noSlash, '/segments/123')).toBe(true);
63
+ expect((0, _routeMatch.matchesRoute)(noSlash, '/segments-old')).toBe(false);
64
+ });
65
+ it('falls through to path matching when activeTabPaths misses', function () {
66
+ expect((0, _routeMatch.matchesRoute)({
67
+ path: '/segments',
68
+ activeTabPaths: ['/elsewhere']
69
+ }, '/segments/123')).toBe(true);
70
+ });
71
+ it('never matches an item without a path when activeTabPaths misses', function () {
72
+ expect((0, _routeMatch.matchesRoute)({}, '/segments')).toBe(false);
73
+ expect((0, _routeMatch.matchesRoute)({
74
+ activeTabPaths: ['/elsewhere']
75
+ }, '/segments')).toBe(false);
76
+ });
77
+ });
@@ -102,12 +102,18 @@ var _jsxRuntime = require("react/jsx-runtime");
102
102
  }
103
103
  }, []);
104
104
  var classes = (0, _classnames["default"])('newspack-section-header', centered && 'newspack-section-header--is-centered', isWhite && 'newspack-section-header--is-white', noMargin && 'newspack-section-header--no-margin', pageHeader && 'newspack-section-header--page-header');
105
- var HeadingTag = pageHeader ? 'h1' : "h".concat(heading);
105
+
106
+ // The breadcrumb `Page` owns the single page `<h1>`, so a `pageHeader` section
107
+ // is a secondary heading: its level follows `heading` (default 2). `pageHeader`
108
+ // controls only the enlarged, centered styling — not the tag. Pass `heading={ 1 }`
109
+ // on a headerless screen that needs the section header to be the page's h1.
110
+ var HeadingTag = "h".concat(heading);
106
111
  var titleContent = null;
107
112
  if (typeof title === 'string') {
108
113
  titleContent = /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
109
114
  className: "newspack-section-header__title-container",
110
115
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(HeadingTag, {
116
+ className: "newspack-section-header__title",
111
117
  children: [title, badges !== null && badges !== void 0 && badges.length ? badges.map(function (badge, i) {
112
118
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_.Badge, {
113
119
  text: badge.label,
@@ -142,6 +148,7 @@ var _jsxRuntime = require("react/jsx-runtime");
142
148
  });
143
149
  } else if (typeof title === 'function') {
144
150
  titleContent = /*#__PURE__*/(0, _jsxRuntime.jsx)(HeadingTag, {
151
+ className: "newspack-section-header__title",
145
152
  children: title()
146
153
  });
147
154
  }
@@ -4,6 +4,7 @@
4
4
 
5
5
  @use "~@wordpress/base-styles/colors" as wp-colors;
6
6
  @use "~@wordpress/base-styles/variables" as wp-vars;
7
+ @use "~@wordpress/base-styles/mixins" as wp-mixins;
7
8
  @use "../../../colors/colors.module" as colors;
8
9
 
9
10
  .newspack-section-header {
@@ -12,6 +13,9 @@
12
13
 
13
14
  &__container {
14
15
  position: relative;
16
+ p {
17
+ text-wrap: balance;
18
+ }
15
19
  &:not(:first-child) {
16
20
  margin-top: 48px;
17
21
  }
@@ -84,17 +88,18 @@
84
88
  color: wp-colors.$gray-700;
85
89
  }
86
90
 
91
+ // Title sizing keyed to the class, not the tag, so demoting the heading level
92
+ // (h1 -> h2 when the breadcrumb Page owns the page h1) never changes its look,
93
+ // and genuine h2s elsewhere in the header are unaffected.
94
+ &__title {
95
+ @include wp-mixins.heading-x-large();
96
+ }
97
+
87
98
  &__title-container {
88
99
  align-items: center;
89
100
  display: flex;
90
101
  gap: 8px;
91
102
 
92
- h2 {
93
- font-size: wp-vars.$font-size-x-large;
94
- font-weight: wp-vars.$font-weight-medium;
95
- line-height: wp-vars.$font-line-height-x-large;
96
- }
97
-
98
103
  .newspack-section-header__menu,
99
104
  .newspack-section-header__secondary-action,
100
105
  .newspack-badge {
@@ -161,10 +166,5 @@
161
166
  fill: var(--wp-admin-theme-color);
162
167
  }
163
168
  }
164
- h1 {
165
- font-size: wp-vars.$font-size-x-large;
166
- font-weight: wp-vars.$font-weight-medium;
167
- line-height: wp-vars.$font-line-height-x-large;
168
- }
169
169
  }
170
170
  }
@@ -54,7 +54,7 @@ function GroupedSelectControl(_ref) {
54
54
  help: help,
55
55
  className: (0, _classnames["default"])(className, 'components-select-control'),
56
56
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
57
- className: "relative",
57
+ className: "newspack-grouped-select-control__field",
58
58
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("select", _objectSpread(_objectSpread({
59
59
  id: id,
60
60
  className: "components-select-control__input",
@@ -43,6 +43,10 @@
43
43
  .newspack-grouped-select-control {
44
44
  position: relative;
45
45
 
46
+ &__field {
47
+ position: relative;
48
+ }
49
+
46
50
  .components-select-control {
47
51
  &__input {
48
52
  appearance: none;
@@ -35,12 +35,9 @@
35
35
  // Checkboxes
36
36
  .newspack-checkbox-control {
37
37
  margin-top: 0 !important;
38
- .components-checkbox-control__label {
39
- font-weight: 600;
40
- }
41
38
 
42
39
  .components-base-control__help {
43
- margin-left: 32px;
40
+ margin-left: calc(var(--checkbox-input-size) + var(--checkbox-input-margin));
44
41
  margin-top: 0;
45
42
  }
46
43
  }