@wordpress/admin-ui 1.10.0 → 1.11.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 +10 -3
- package/README.md +23 -2
- package/build/breadcrumbs/index.cjs +15 -10
- package/build/breadcrumbs/index.cjs.map +3 -3
- package/build/breadcrumbs/types.cjs.map +1 -1
- package/build/page/index.cjs +1 -1
- package/build/page/index.cjs.map +2 -2
- package/build-module/breadcrumbs/index.mjs +16 -11
- package/build-module/breadcrumbs/index.mjs.map +2 -2
- package/build-module/page/index.mjs +1 -1
- package/build-module/page/index.mjs.map +2 -2
- package/build-types/breadcrumbs/index.d.ts +22 -0
- package/build-types/breadcrumbs/index.d.ts.map +1 -1
- package/build-types/breadcrumbs/types.d.ts +5 -2
- package/build-types/breadcrumbs/types.d.ts.map +1 -1
- package/build-types/page/stories/index.story.d.ts +5 -0
- package/build-types/page/stories/index.story.d.ts.map +1 -1
- package/package.json +13 -10
- package/src/breadcrumbs/index.tsx +48 -28
- package/src/breadcrumbs/test/index.test.tsx +169 -0
- package/src/breadcrumbs/types.ts +5 -2
- package/src/page/index.tsx +1 -1
- package/src/page/stories/index.story.tsx +84 -1
- package/src/page/style.scss +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,21 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 1.11.0 (2026-04-01)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- `Breadcrumbs`: throw a runtime error when non-last items are missing a `to` prop [#76493](https://github.com/WordPress/gutenberg/pull/76493/)
|
|
10
|
+
- Fix Page Header not rendering when only `actions` prop is provided. [#76695](https://github.com/WordPress/gutenberg/pull/76695)
|
|
11
|
+
|
|
5
12
|
## 1.10.0 (2026-03-18)
|
|
6
13
|
|
|
7
|
-
-
|
|
14
|
+
- Update Title and Breadcrumbs font sizes. [#76452](https://github.com/WordPress/gutenberg/pull/76452)
|
|
8
15
|
|
|
9
16
|
## 1.9.0 (2026-03-04)
|
|
10
17
|
|
|
11
18
|
### Bug Fixes
|
|
12
19
|
|
|
13
|
-
-
|
|
20
|
+
- Fix type mismatch between Page `title` (ReactNode) and NavigableRegion `ariaLabel` (string) by adding an optional `ariaLabel` prop to Page that falls back to `title` when it is a string. [#75899](https://github.com/WordPress/gutenberg/pull/75899/)
|
|
14
21
|
|
|
15
22
|
## 1.8.0 (2026-02-18)
|
|
16
23
|
|
|
17
24
|
### Enhancements
|
|
18
25
|
|
|
19
|
-
-
|
|
26
|
+
- Apply `text-wrap: pretty` for more balanced text in Page component [#74907](https://github.com/WordPress/gutenberg/pull/74907)
|
|
20
27
|
|
|
21
28
|
## 1.7.0 (2026-01-29)
|
|
22
29
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Admin UI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
UI components for building consistent admin page layouts.
|
|
4
|
+
|
|
5
|
+
While `@wordpress/ui` provides low-level, generic UI components that can be composed in flexible arrangements for building admin features, the purpose of this package is to guarantee consistency in the common page structure of an admin page layout. This includes high-level abstractions for a page, its sidebar, header, navigation, and other standardized page layout elements. The goal of standardizing these layouts is to provide a cohesive and predictable experience for users.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -16,7 +18,26 @@ npm install @wordpress/admin-ui --save
|
|
|
16
18
|
|
|
17
19
|
### Breadcrumbs
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
Renders a breadcrumb navigation trail.
|
|
22
|
+
|
|
23
|
+
All items except the last one must provide a `to` prop for navigation. In development mode, an error is thrown when a non-last item is missing `to`. The last item represents the current page and its `to` prop is optional. Only the last item (when it has no `to` prop) is rendered as an `h1`.
|
|
24
|
+
|
|
25
|
+
_Usage_
|
|
26
|
+
|
|
27
|
+
```jsx
|
|
28
|
+
<Breadcrumbs
|
|
29
|
+
items={ [
|
|
30
|
+
{ label: 'Home', to: '/' },
|
|
31
|
+
{ label: 'Settings', to: '/settings' },
|
|
32
|
+
{ label: 'General' },
|
|
33
|
+
] }
|
|
34
|
+
/>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
_Parameters_
|
|
38
|
+
|
|
39
|
+
- _props_ `BreadcrumbsProps`:
|
|
40
|
+
- _props.items_ `BreadcrumbsProps[ 'items' ]`: The breadcrumb items to display.
|
|
20
41
|
|
|
21
42
|
### NavigableRegion
|
|
22
43
|
|
|
@@ -28,19 +28,21 @@ var import_route = require("@wordpress/route");
|
|
|
28
28
|
var import_i18n = require("@wordpress/i18n");
|
|
29
29
|
var import_components = require("@wordpress/components");
|
|
30
30
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
31
|
-
var BreadcrumbItem = ({
|
|
32
|
-
item: { label, to }
|
|
33
|
-
}) => {
|
|
34
|
-
if (!to) {
|
|
35
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.__experimentalHeading, { level: 1, truncate: true, children: label }) });
|
|
36
|
-
}
|
|
37
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_route.Link, { to, children: label }) });
|
|
38
|
-
};
|
|
39
31
|
var Breadcrumbs = ({ items }) => {
|
|
40
32
|
if (!items.length) {
|
|
41
33
|
return null;
|
|
42
34
|
}
|
|
43
|
-
|
|
35
|
+
const precedingItems = items.slice(0, -1);
|
|
36
|
+
const lastItem = items[items.length - 1];
|
|
37
|
+
if (process.env.NODE_ENV !== "production") {
|
|
38
|
+
const invalidItem = precedingItems.find((item) => !item.to);
|
|
39
|
+
if (invalidItem) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Breadcrumbs: item "${invalidItem.label}" is missing a \`to\` prop. All items except the last one must have a \`to\` prop.`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", { "aria-label": (0, import_i18n.__)("Breadcrumbs"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
44
46
|
import_components.__experimentalHStack,
|
|
45
47
|
{
|
|
46
48
|
as: "ul",
|
|
@@ -48,7 +50,10 @@ var Breadcrumbs = ({ items }) => {
|
|
|
48
50
|
spacing: 0,
|
|
49
51
|
justify: "flex-start",
|
|
50
52
|
alignment: "center",
|
|
51
|
-
children:
|
|
53
|
+
children: [
|
|
54
|
+
precedingItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_route.Link, { to: item.to, children: item.label }) }, index)),
|
|
55
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: lastItem.to ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_route.Link, { to: lastItem.to, children: lastItem.label }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.__experimentalHeading, { level: 1, truncate: true, children: lastItem.label }) })
|
|
56
|
+
]
|
|
52
57
|
}
|
|
53
58
|
) });
|
|
54
59
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/breadcrumbs/index.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { Link } from '@wordpress/route';\nimport { __ } from '@wordpress/i18n';\nimport {\n\t__experimentalHeading as Heading,\n\t__experimentalHStack as HStack,\n} from '@wordpress/components';\n\n/**\n * Internal dependencies\n */\nimport type {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,mBAAqB;AACrB,kBAAmB;AACnB,wBAGO;
|
|
6
|
-
"names": ["
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { Link } from '@wordpress/route';\nimport { __ } from '@wordpress/i18n';\nimport {\n\t__experimentalHeading as Heading,\n\t__experimentalHStack as HStack,\n} from '@wordpress/components';\n\n/**\n * Internal dependencies\n */\nimport type { BreadcrumbsProps } from './types';\n\n/**\n * Renders a breadcrumb navigation trail.\n *\n * All items except the last one must provide a `to` prop for navigation.\n * In development mode, an error is thrown when a non-last item is missing `to`.\n * The last item represents the current page and its `to` prop is optional.\n * Only the last item (when it has no `to` prop) is rendered as an `h1`.\n *\n * @param props\n * @param props.items The breadcrumb items to display.\n *\n * @example\n * ```jsx\n * <Breadcrumbs\n * items={ [\n * { label: 'Home', to: '/' },\n * { label: 'Settings', to: '/settings' },\n * { label: 'General' },\n * ] }\n * />\n * ```\n */\nexport const Breadcrumbs = ( { items }: BreadcrumbsProps ) => {\n\tif ( ! items.length ) {\n\t\treturn null;\n\t}\n\n\tconst precedingItems = items.slice( 0, -1 );\n\tconst lastItem = items[ items.length - 1 ];\n\n\tif ( process.env.NODE_ENV !== 'production' ) {\n\t\tconst invalidItem = precedingItems.find( ( item ) => ! item.to );\n\t\tif ( invalidItem ) {\n\t\t\tthrow new Error(\n\t\t\t\t`Breadcrumbs: item \"${ invalidItem.label }\" is missing a \\`to\\` prop. All items except the last one must have a \\`to\\` prop.`\n\t\t\t);\n\t\t}\n\t}\n\n\treturn (\n\t\t<nav aria-label={ __( 'Breadcrumbs' ) }>\n\t\t\t<HStack\n\t\t\t\tas=\"ul\"\n\t\t\t\tclassName=\"admin-ui-breadcrumbs__list\"\n\t\t\t\tspacing={ 0 }\n\t\t\t\tjustify=\"flex-start\"\n\t\t\t\talignment=\"center\"\n\t\t\t>\n\t\t\t\t{ precedingItems.map( ( item, index ) => (\n\t\t\t\t\t<li key={ index }>\n\t\t\t\t\t\t<Link to={ item.to }>{ item.label }</Link>\n\t\t\t\t\t</li>\n\t\t\t\t) ) }\n\t\t\t\t<li>\n\t\t\t\t\t{ lastItem.to ? (\n\t\t\t\t\t\t<Link to={ lastItem.to }>{ lastItem.label }</Link>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Heading level={ 1 } truncate>\n\t\t\t\t\t\t\t{ lastItem.label }\n\t\t\t\t\t\t</Heading>\n\t\t\t\t\t) }\n\t\t\t\t</li>\n\t\t\t</HStack>\n\t\t</nav>\n\t);\n};\n\nexport default Breadcrumbs;\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,mBAAqB;AACrB,kBAAmB;AACnB,wBAGO;AAgDJ;AAnBI,IAAM,cAAc,CAAE,EAAE,MAAM,MAAyB;AAC7D,MAAK,CAAE,MAAM,QAAS;AACrB,WAAO;AAAA,EACR;AAEA,QAAM,iBAAiB,MAAM,MAAO,GAAG,EAAG;AAC1C,QAAM,WAAW,MAAO,MAAM,SAAS,CAAE;AAEzC,MAAK,QAAQ,IAAI,aAAa,cAAe;AAC5C,UAAM,cAAc,eAAe,KAAM,CAAE,SAAU,CAAE,KAAK,EAAG;AAC/D,QAAK,aAAc;AAClB,YAAM,IAAI;AAAA,QACT,sBAAuB,YAAY,KAAM;AAAA,MAC1C;AAAA,IACD;AAAA,EACD;AAEA,SACC,4CAAC,SAAI,kBAAa,gBAAI,aAAc,GACnC;AAAA,IAAC,kBAAAA;AAAA,IAAA;AAAA,MACA,IAAG;AAAA,MACH,WAAU;AAAA,MACV,SAAU;AAAA,MACV,SAAQ;AAAA,MACR,WAAU;AAAA,MAER;AAAA,uBAAe,IAAK,CAAE,MAAM,UAC7B,4CAAC,QACA,sDAAC,qBAAK,IAAK,KAAK,IAAO,eAAK,OAAO,KAD1B,KAEV,CACC;AAAA,QACF,4CAAC,QACE,mBAAS,KACV,4CAAC,qBAAK,IAAK,SAAS,IAAO,mBAAS,OAAO,IAE3C,4CAAC,kBAAAC,uBAAA,EAAQ,OAAQ,GAAI,UAAQ,MAC1B,mBAAS,OACZ,GAEF;AAAA;AAAA;AAAA,EACD,GACD;AAEF;AAEA,IAAO,sBAAQ;",
|
|
6
|
+
"names": ["HStack", "Heading"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/breadcrumbs/types.ts"],
|
|
4
|
-
"sourcesContent": ["export interface BreadcrumbItem {\n\t/**\n\t * The label text for the breadcrumb item.\n\t */\n\tlabel: string;\n\n\t/**\n\t * The router path that the breadcrumb item should link to.\n\t * It is optional
|
|
4
|
+
"sourcesContent": ["export interface BreadcrumbItem {\n\t/**\n\t * The label text for the breadcrumb item.\n\t */\n\tlabel: string;\n\n\t/**\n\t * The router path that the breadcrumb item should link to.\n\t * It is optional for the last item (the current page).\n\t * All preceding items should provide a `to` prop.\n\t */\n\tto?: string;\n}\n\nexport interface BreadcrumbsProps extends React.HTMLAttributes< HTMLElement > {\n\t/**\n\t * An array of items to display in the breadcrumb trail.\n\t * The last item is considered the current item and has an optional `to` prop.\n\t * All preceding items must have a `to` prop \u2014 in development mode,\n\t * an error is thrown when this requirement is not met.\n\t */\n\titems: BreadcrumbItem[];\n\t/**\n\t * A boolean to show/hide the current item in the trail.\n\t * Note that when `false` the current item is only visually hidden.\n\t */\n\tshowCurrentItem?: boolean;\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/build/page/index.cjs
CHANGED
|
@@ -54,7 +54,7 @@ function Page({
|
|
|
54
54
|
const classes = (0, import_clsx.default)("admin-ui-page", className);
|
|
55
55
|
const effectiveAriaLabel = ariaLabel ?? (typeof title === "string" ? title : "");
|
|
56
56
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_navigable_region.default, { className: classes, ariaLabel: effectiveAriaLabel, children: [
|
|
57
|
-
(title || breadcrumbs || badges) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
57
|
+
(title || breadcrumbs || badges || actions) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
58
58
|
import_header.default,
|
|
59
59
|
{
|
|
60
60
|
headingLevel,
|
package/build/page/index.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/page/index.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * External dependencies\n */\nimport clsx from 'clsx';\n\n/**\n * Internal dependencies\n */\nimport Header from './header';\nimport NavigableRegion from '../navigable-region';\nimport { SidebarToggleFill } from './sidebar-toggle-slot';\n\nfunction Page( {\n\theadingLevel,\n\tbreadcrumbs,\n\tbadges,\n\ttitle,\n\tsubTitle,\n\tchildren,\n\tclassName,\n\tactions,\n\tariaLabel,\n\thasPadding = false,\n\tshowSidebarToggle = true,\n}: {\n\theadingLevel?: 1 | 2 | 3 | 4 | 5 | 6;\n\tbreadcrumbs?: React.ReactNode;\n\tbadges?: React.ReactNode;\n\ttitle?: React.ReactNode;\n\tsubTitle?: React.ReactNode;\n\tchildren: React.ReactNode;\n\tclassName?: string;\n\tactions?: React.ReactNode;\n\tariaLabel?: string;\n\thasPadding?: boolean;\n\tshowSidebarToggle?: boolean;\n} ) {\n\tconst classes = clsx( 'admin-ui-page', className );\n\tconst effectiveAriaLabel =\n\t\tariaLabel ?? ( typeof title === 'string' ? title : '' );\n\n\treturn (\n\t\t<NavigableRegion className={ classes } ariaLabel={ effectiveAriaLabel }>\n\t\t\t{ ( title || breadcrumbs || badges ) && (\n\t\t\t\t<Header\n\t\t\t\t\theadingLevel={ headingLevel }\n\t\t\t\t\tbreadcrumbs={ breadcrumbs }\n\t\t\t\t\tbadges={ badges }\n\t\t\t\t\ttitle={ title }\n\t\t\t\t\tsubTitle={ subTitle }\n\t\t\t\t\tactions={ actions }\n\t\t\t\t\tshowSidebarToggle={ showSidebarToggle }\n\t\t\t\t/>\n\t\t\t) }\n\t\t\t{ hasPadding ? (\n\t\t\t\t<div className=\"admin-ui-page__content has-padding\">\n\t\t\t\t\t{ children }\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\tchildren\n\t\t\t) }\n\t\t</NavigableRegion>\n\t);\n}\n\nPage.SidebarToggleFill = SidebarToggleFill;\n\nexport default Page;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAAiB;AAKjB,oBAAmB;AACnB,8BAA4B;AAC5B,iCAAkC;AAgChC;AA9BF,SAAS,KAAM;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,oBAAoB;AACrB,GAYI;AACH,QAAM,cAAU,YAAAA,SAAM,iBAAiB,SAAU;AACjD,QAAM,qBACL,cAAe,OAAO,UAAU,WAAW,QAAQ;AAEpD,SACC,6CAAC,wBAAAC,SAAA,EAAgB,WAAY,SAAU,WAAY,oBAC9C;AAAA,cAAS,eAAe,
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport clsx from 'clsx';\n\n/**\n * Internal dependencies\n */\nimport Header from './header';\nimport NavigableRegion from '../navigable-region';\nimport { SidebarToggleFill } from './sidebar-toggle-slot';\n\nfunction Page( {\n\theadingLevel,\n\tbreadcrumbs,\n\tbadges,\n\ttitle,\n\tsubTitle,\n\tchildren,\n\tclassName,\n\tactions,\n\tariaLabel,\n\thasPadding = false,\n\tshowSidebarToggle = true,\n}: {\n\theadingLevel?: 1 | 2 | 3 | 4 | 5 | 6;\n\tbreadcrumbs?: React.ReactNode;\n\tbadges?: React.ReactNode;\n\ttitle?: React.ReactNode;\n\tsubTitle?: React.ReactNode;\n\tchildren: React.ReactNode;\n\tclassName?: string;\n\tactions?: React.ReactNode;\n\tariaLabel?: string;\n\thasPadding?: boolean;\n\tshowSidebarToggle?: boolean;\n} ) {\n\tconst classes = clsx( 'admin-ui-page', className );\n\tconst effectiveAriaLabel =\n\t\tariaLabel ?? ( typeof title === 'string' ? title : '' );\n\n\treturn (\n\t\t<NavigableRegion className={ classes } ariaLabel={ effectiveAriaLabel }>\n\t\t\t{ ( title || breadcrumbs || badges || actions ) && (\n\t\t\t\t<Header\n\t\t\t\t\theadingLevel={ headingLevel }\n\t\t\t\t\tbreadcrumbs={ breadcrumbs }\n\t\t\t\t\tbadges={ badges }\n\t\t\t\t\ttitle={ title }\n\t\t\t\t\tsubTitle={ subTitle }\n\t\t\t\t\tactions={ actions }\n\t\t\t\t\tshowSidebarToggle={ showSidebarToggle }\n\t\t\t\t/>\n\t\t\t) }\n\t\t\t{ hasPadding ? (\n\t\t\t\t<div className=\"admin-ui-page__content has-padding\">\n\t\t\t\t\t{ children }\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\tchildren\n\t\t\t) }\n\t\t</NavigableRegion>\n\t);\n}\n\nPage.SidebarToggleFill = SidebarToggleFill;\n\nexport default Page;\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAAiB;AAKjB,oBAAmB;AACnB,8BAA4B;AAC5B,iCAAkC;AAgChC;AA9BF,SAAS,KAAM;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,oBAAoB;AACrB,GAYI;AACH,QAAM,cAAU,YAAAA,SAAM,iBAAiB,SAAU;AACjD,QAAM,qBACL,cAAe,OAAO,UAAU,WAAW,QAAQ;AAEpD,SACC,6CAAC,wBAAAC,SAAA,EAAgB,WAAY,SAAU,WAAY,oBAC9C;AAAA,cAAS,eAAe,UAAU,YACrC;AAAA,MAAC,cAAAC;AAAA,MAAA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACD;AAAA,IAEC,aACD,4CAAC,SAAI,WAAU,sCACZ,UACH,IAEA;AAAA,KAEF;AAEF;AAEA,KAAK,oBAAoB;AAEzB,IAAO,eAAQ;",
|
|
6
6
|
"names": ["clsx", "NavigableRegion", "Header"]
|
|
7
7
|
}
|
|
@@ -5,20 +5,22 @@ import {
|
|
|
5
5
|
__experimentalHeading as Heading,
|
|
6
6
|
__experimentalHStack as HStack
|
|
7
7
|
} from "@wordpress/components";
|
|
8
|
-
import { jsx } from "react/jsx-runtime";
|
|
9
|
-
var BreadcrumbItem = ({
|
|
10
|
-
item: { label, to }
|
|
11
|
-
}) => {
|
|
12
|
-
if (!to) {
|
|
13
|
-
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Heading, { level: 1, truncate: true, children: label }) });
|
|
14
|
-
}
|
|
15
|
-
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link, { to, children: label }) });
|
|
16
|
-
};
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
17
9
|
var Breadcrumbs = ({ items }) => {
|
|
18
10
|
if (!items.length) {
|
|
19
11
|
return null;
|
|
20
12
|
}
|
|
21
|
-
|
|
13
|
+
const precedingItems = items.slice(0, -1);
|
|
14
|
+
const lastItem = items[items.length - 1];
|
|
15
|
+
if (process.env.NODE_ENV !== "production") {
|
|
16
|
+
const invalidItem = precedingItems.find((item) => !item.to);
|
|
17
|
+
if (invalidItem) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`Breadcrumbs: item "${invalidItem.label}" is missing a \`to\` prop. All items except the last one must have a \`to\` prop.`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return /* @__PURE__ */ jsx("nav", { "aria-label": __("Breadcrumbs"), children: /* @__PURE__ */ jsxs(
|
|
22
24
|
HStack,
|
|
23
25
|
{
|
|
24
26
|
as: "ul",
|
|
@@ -26,7 +28,10 @@ var Breadcrumbs = ({ items }) => {
|
|
|
26
28
|
spacing: 0,
|
|
27
29
|
justify: "flex-start",
|
|
28
30
|
alignment: "center",
|
|
29
|
-
children:
|
|
31
|
+
children: [
|
|
32
|
+
precedingItems.map((item, index) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link, { to: item.to, children: item.label }) }, index)),
|
|
33
|
+
/* @__PURE__ */ jsx("li", { children: lastItem.to ? /* @__PURE__ */ jsx(Link, { to: lastItem.to, children: lastItem.label }) : /* @__PURE__ */ jsx(Heading, { level: 1, truncate: true, children: lastItem.label }) })
|
|
34
|
+
]
|
|
30
35
|
}
|
|
31
36
|
) });
|
|
32
37
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/breadcrumbs/index.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { Link } from '@wordpress/route';\nimport { __ } from '@wordpress/i18n';\nimport {\n\t__experimentalHeading as Heading,\n\t__experimentalHStack as HStack,\n} from '@wordpress/components';\n\n/**\n * Internal dependencies\n */\nimport type {
|
|
5
|
-
"mappings": ";AAGA,SAAS,YAAY;AACrB,SAAS,UAAU;AACnB;AAAA,EACC,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,OAClB;
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { Link } from '@wordpress/route';\nimport { __ } from '@wordpress/i18n';\nimport {\n\t__experimentalHeading as Heading,\n\t__experimentalHStack as HStack,\n} from '@wordpress/components';\n\n/**\n * Internal dependencies\n */\nimport type { BreadcrumbsProps } from './types';\n\n/**\n * Renders a breadcrumb navigation trail.\n *\n * All items except the last one must provide a `to` prop for navigation.\n * In development mode, an error is thrown when a non-last item is missing `to`.\n * The last item represents the current page and its `to` prop is optional.\n * Only the last item (when it has no `to` prop) is rendered as an `h1`.\n *\n * @param props\n * @param props.items The breadcrumb items to display.\n *\n * @example\n * ```jsx\n * <Breadcrumbs\n * items={ [\n * { label: 'Home', to: '/' },\n * { label: 'Settings', to: '/settings' },\n * { label: 'General' },\n * ] }\n * />\n * ```\n */\nexport const Breadcrumbs = ( { items }: BreadcrumbsProps ) => {\n\tif ( ! items.length ) {\n\t\treturn null;\n\t}\n\n\tconst precedingItems = items.slice( 0, -1 );\n\tconst lastItem = items[ items.length - 1 ];\n\n\tif ( process.env.NODE_ENV !== 'production' ) {\n\t\tconst invalidItem = precedingItems.find( ( item ) => ! item.to );\n\t\tif ( invalidItem ) {\n\t\t\tthrow new Error(\n\t\t\t\t`Breadcrumbs: item \"${ invalidItem.label }\" is missing a \\`to\\` prop. All items except the last one must have a \\`to\\` prop.`\n\t\t\t);\n\t\t}\n\t}\n\n\treturn (\n\t\t<nav aria-label={ __( 'Breadcrumbs' ) }>\n\t\t\t<HStack\n\t\t\t\tas=\"ul\"\n\t\t\t\tclassName=\"admin-ui-breadcrumbs__list\"\n\t\t\t\tspacing={ 0 }\n\t\t\t\tjustify=\"flex-start\"\n\t\t\t\talignment=\"center\"\n\t\t\t>\n\t\t\t\t{ precedingItems.map( ( item, index ) => (\n\t\t\t\t\t<li key={ index }>\n\t\t\t\t\t\t<Link to={ item.to }>{ item.label }</Link>\n\t\t\t\t\t</li>\n\t\t\t\t) ) }\n\t\t\t\t<li>\n\t\t\t\t\t{ lastItem.to ? (\n\t\t\t\t\t\t<Link to={ lastItem.to }>{ lastItem.label }</Link>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Heading level={ 1 } truncate>\n\t\t\t\t\t\t\t{ lastItem.label }\n\t\t\t\t\t\t</Heading>\n\t\t\t\t\t) }\n\t\t\t\t</li>\n\t\t\t</HStack>\n\t\t</nav>\n\t);\n};\n\nexport default Breadcrumbs;\n"],
|
|
5
|
+
"mappings": ";AAGA,SAAS,YAAY;AACrB,SAAS,UAAU;AACnB;AAAA,EACC,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,OAClB;AAgDJ,SASG,KATH;AAnBI,IAAM,cAAc,CAAE,EAAE,MAAM,MAAyB;AAC7D,MAAK,CAAE,MAAM,QAAS;AACrB,WAAO;AAAA,EACR;AAEA,QAAM,iBAAiB,MAAM,MAAO,GAAG,EAAG;AAC1C,QAAM,WAAW,MAAO,MAAM,SAAS,CAAE;AAEzC,MAAK,QAAQ,IAAI,aAAa,cAAe;AAC5C,UAAM,cAAc,eAAe,KAAM,CAAE,SAAU,CAAE,KAAK,EAAG;AAC/D,QAAK,aAAc;AAClB,YAAM,IAAI;AAAA,QACT,sBAAuB,YAAY,KAAM;AAAA,MAC1C;AAAA,IACD;AAAA,EACD;AAEA,SACC,oBAAC,SAAI,cAAa,GAAI,aAAc,GACnC;AAAA,IAAC;AAAA;AAAA,MACA,IAAG;AAAA,MACH,WAAU;AAAA,MACV,SAAU;AAAA,MACV,SAAQ;AAAA,MACR,WAAU;AAAA,MAER;AAAA,uBAAe,IAAK,CAAE,MAAM,UAC7B,oBAAC,QACA,8BAAC,QAAK,IAAK,KAAK,IAAO,eAAK,OAAO,KAD1B,KAEV,CACC;AAAA,QACF,oBAAC,QACE,mBAAS,KACV,oBAAC,QAAK,IAAK,SAAS,IAAO,mBAAS,OAAO,IAE3C,oBAAC,WAAQ,OAAQ,GAAI,UAAQ,MAC1B,mBAAS,OACZ,GAEF;AAAA;AAAA;AAAA,EACD,GACD;AAEF;AAEA,IAAO,sBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -20,7 +20,7 @@ function Page({
|
|
|
20
20
|
const classes = clsx("admin-ui-page", className);
|
|
21
21
|
const effectiveAriaLabel = ariaLabel ?? (typeof title === "string" ? title : "");
|
|
22
22
|
return /* @__PURE__ */ jsxs(NavigableRegion, { className: classes, ariaLabel: effectiveAriaLabel, children: [
|
|
23
|
-
(title || breadcrumbs || badges) && /* @__PURE__ */ jsx(
|
|
23
|
+
(title || breadcrumbs || badges || actions) && /* @__PURE__ */ jsx(
|
|
24
24
|
Header,
|
|
25
25
|
{
|
|
26
26
|
headingLevel,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/page/index.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * External dependencies\n */\nimport clsx from 'clsx';\n\n/**\n * Internal dependencies\n */\nimport Header from './header';\nimport NavigableRegion from '../navigable-region';\nimport { SidebarToggleFill } from './sidebar-toggle-slot';\n\nfunction Page( {\n\theadingLevel,\n\tbreadcrumbs,\n\tbadges,\n\ttitle,\n\tsubTitle,\n\tchildren,\n\tclassName,\n\tactions,\n\tariaLabel,\n\thasPadding = false,\n\tshowSidebarToggle = true,\n}: {\n\theadingLevel?: 1 | 2 | 3 | 4 | 5 | 6;\n\tbreadcrumbs?: React.ReactNode;\n\tbadges?: React.ReactNode;\n\ttitle?: React.ReactNode;\n\tsubTitle?: React.ReactNode;\n\tchildren: React.ReactNode;\n\tclassName?: string;\n\tactions?: React.ReactNode;\n\tariaLabel?: string;\n\thasPadding?: boolean;\n\tshowSidebarToggle?: boolean;\n} ) {\n\tconst classes = clsx( 'admin-ui-page', className );\n\tconst effectiveAriaLabel =\n\t\tariaLabel ?? ( typeof title === 'string' ? title : '' );\n\n\treturn (\n\t\t<NavigableRegion className={ classes } ariaLabel={ effectiveAriaLabel }>\n\t\t\t{ ( title || breadcrumbs || badges ) && (\n\t\t\t\t<Header\n\t\t\t\t\theadingLevel={ headingLevel }\n\t\t\t\t\tbreadcrumbs={ breadcrumbs }\n\t\t\t\t\tbadges={ badges }\n\t\t\t\t\ttitle={ title }\n\t\t\t\t\tsubTitle={ subTitle }\n\t\t\t\t\tactions={ actions }\n\t\t\t\t\tshowSidebarToggle={ showSidebarToggle }\n\t\t\t\t/>\n\t\t\t) }\n\t\t\t{ hasPadding ? (\n\t\t\t\t<div className=\"admin-ui-page__content has-padding\">\n\t\t\t\t\t{ children }\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\tchildren\n\t\t\t) }\n\t\t</NavigableRegion>\n\t);\n}\n\nPage.SidebarToggleFill = SidebarToggleFill;\n\nexport default Page;\n"],
|
|
5
|
-
"mappings": ";AAGA,OAAO,UAAU;AAKjB,OAAO,YAAY;AACnB,OAAO,qBAAqB;AAC5B,SAAS,yBAAyB;AAgChC,SAEE,KAFF;AA9BF,SAAS,KAAM;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,oBAAoB;AACrB,GAYI;AACH,QAAM,UAAU,KAAM,iBAAiB,SAAU;AACjD,QAAM,qBACL,cAAe,OAAO,UAAU,WAAW,QAAQ;AAEpD,SACC,qBAAC,mBAAgB,WAAY,SAAU,WAAY,oBAC9C;AAAA,cAAS,eAAe,
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport clsx from 'clsx';\n\n/**\n * Internal dependencies\n */\nimport Header from './header';\nimport NavigableRegion from '../navigable-region';\nimport { SidebarToggleFill } from './sidebar-toggle-slot';\n\nfunction Page( {\n\theadingLevel,\n\tbreadcrumbs,\n\tbadges,\n\ttitle,\n\tsubTitle,\n\tchildren,\n\tclassName,\n\tactions,\n\tariaLabel,\n\thasPadding = false,\n\tshowSidebarToggle = true,\n}: {\n\theadingLevel?: 1 | 2 | 3 | 4 | 5 | 6;\n\tbreadcrumbs?: React.ReactNode;\n\tbadges?: React.ReactNode;\n\ttitle?: React.ReactNode;\n\tsubTitle?: React.ReactNode;\n\tchildren: React.ReactNode;\n\tclassName?: string;\n\tactions?: React.ReactNode;\n\tariaLabel?: string;\n\thasPadding?: boolean;\n\tshowSidebarToggle?: boolean;\n} ) {\n\tconst classes = clsx( 'admin-ui-page', className );\n\tconst effectiveAriaLabel =\n\t\tariaLabel ?? ( typeof title === 'string' ? title : '' );\n\n\treturn (\n\t\t<NavigableRegion className={ classes } ariaLabel={ effectiveAriaLabel }>\n\t\t\t{ ( title || breadcrumbs || badges || actions ) && (\n\t\t\t\t<Header\n\t\t\t\t\theadingLevel={ headingLevel }\n\t\t\t\t\tbreadcrumbs={ breadcrumbs }\n\t\t\t\t\tbadges={ badges }\n\t\t\t\t\ttitle={ title }\n\t\t\t\t\tsubTitle={ subTitle }\n\t\t\t\t\tactions={ actions }\n\t\t\t\t\tshowSidebarToggle={ showSidebarToggle }\n\t\t\t\t/>\n\t\t\t) }\n\t\t\t{ hasPadding ? (\n\t\t\t\t<div className=\"admin-ui-page__content has-padding\">\n\t\t\t\t\t{ children }\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\tchildren\n\t\t\t) }\n\t\t</NavigableRegion>\n\t);\n}\n\nPage.SidebarToggleFill = SidebarToggleFill;\n\nexport default Page;\n"],
|
|
5
|
+
"mappings": ";AAGA,OAAO,UAAU;AAKjB,OAAO,YAAY;AACnB,OAAO,qBAAqB;AAC5B,SAAS,yBAAyB;AAgChC,SAEE,KAFF;AA9BF,SAAS,KAAM;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,oBAAoB;AACrB,GAYI;AACH,QAAM,UAAU,KAAM,iBAAiB,SAAU;AACjD,QAAM,qBACL,cAAe,OAAO,UAAU,WAAW,QAAQ;AAEpD,SACC,qBAAC,mBAAgB,WAAY,SAAU,WAAY,oBAC9C;AAAA,cAAS,eAAe,UAAU,YACrC;AAAA,MAAC;AAAA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACD;AAAA,IAEC,aACD,oBAAC,SAAI,WAAU,sCACZ,UACH,IAEA;AAAA,KAEF;AAEF;AAEA,KAAK,oBAAoB;AAEzB,IAAO,eAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
* Internal dependencies
|
|
3
3
|
*/
|
|
4
4
|
import type { BreadcrumbsProps } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Renders a breadcrumb navigation trail.
|
|
7
|
+
*
|
|
8
|
+
* All items except the last one must provide a `to` prop for navigation.
|
|
9
|
+
* In development mode, an error is thrown when a non-last item is missing `to`.
|
|
10
|
+
* The last item represents the current page and its `to` prop is optional.
|
|
11
|
+
* Only the last item (when it has no `to` prop) is rendered as an `h1`.
|
|
12
|
+
*
|
|
13
|
+
* @param props
|
|
14
|
+
* @param props.items The breadcrumb items to display.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```jsx
|
|
18
|
+
* <Breadcrumbs
|
|
19
|
+
* items={ [
|
|
20
|
+
* { label: 'Home', to: '/' },
|
|
21
|
+
* { label: 'Settings', to: '/settings' },
|
|
22
|
+
* { label: 'General' },
|
|
23
|
+
* ] }
|
|
24
|
+
* />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
5
27
|
export declare const Breadcrumbs: ({ items }: BreadcrumbsProps) => import("react").JSX.Element | null;
|
|
6
28
|
export default Breadcrumbs;
|
|
7
29
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/breadcrumbs/index.tsx"],"names":[],"mappings":"AAUA;;GAEG;AACH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/breadcrumbs/index.tsx"],"names":[],"mappings":"AAUA;;GAEG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,WAAW,GAAK,WAAW,gBAAgB,uCA2CvD,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -5,14 +5,17 @@ export interface BreadcrumbItem {
|
|
|
5
5
|
label: string;
|
|
6
6
|
/**
|
|
7
7
|
* The router path that the breadcrumb item should link to.
|
|
8
|
-
* It is optional
|
|
8
|
+
* It is optional for the last item (the current page).
|
|
9
|
+
* All preceding items should provide a `to` prop.
|
|
9
10
|
*/
|
|
10
11
|
to?: string;
|
|
11
12
|
}
|
|
12
13
|
export interface BreadcrumbsProps extends React.HTMLAttributes<HTMLElement> {
|
|
13
14
|
/**
|
|
14
15
|
* An array of items to display in the breadcrumb trail.
|
|
15
|
-
* The last item is considered the current item.
|
|
16
|
+
* The last item is considered the current item and has an optional `to` prop.
|
|
17
|
+
* All preceding items must have a `to` prop — in development mode,
|
|
18
|
+
* an error is thrown when this requirement is not met.
|
|
16
19
|
*/
|
|
17
20
|
items: BreadcrumbItem[];
|
|
18
21
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/breadcrumbs/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/breadcrumbs/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAiB,SAAQ,KAAK,CAAC,cAAc,CAAE,WAAW,CAAE;IAC5E;;;;;OAKG;IACH,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B"}
|
|
@@ -13,4 +13,9 @@ export declare const Default: Story;
|
|
|
13
13
|
export declare const WithSubtitle: Story;
|
|
14
14
|
export declare const WithBreadcrumbs: Story;
|
|
15
15
|
export declare const WithBreadcrumbsAndSubtitle: Story;
|
|
16
|
+
export declare const WithoutHeader: Story;
|
|
17
|
+
export declare const WithTitleAndBadges: Story;
|
|
18
|
+
export declare const WithBreadcrumbsAndBadges: Story;
|
|
19
|
+
export declare const WithActions: Story;
|
|
20
|
+
export declare const FullHeader: Story;
|
|
16
21
|
//# sourceMappingURL=index.story.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.story.d.ts","sourceRoot":"","sources":["../../../src/page/stories/index.story.tsx"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.story.d.ts","sourceRoot":"","sources":["../../../src/page/stories/index.story.tsx"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAI5D;;GAEG;AACH,OAAO,IAAI,MAAM,IAAI,CAAC;AAItB,QAAA,MAAM,IAAI,EAAE,IAAI,CAAE,OAAO,IAAI,CAc5B,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAE,OAAO,IAAI,CAAE,CAAC;AAErC,eAAO,MAAM,OAAO,EAAE,KAOrB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAQ1B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAc7B,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,KAexC,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAM3B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,KAQhC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,KAetC,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAiBzB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KA0BxB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/admin-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "Generic components to be used in the Admin UI.",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -43,15 +43,18 @@
|
|
|
43
43
|
},
|
|
44
44
|
"react-native": "src/index",
|
|
45
45
|
"types": "build-types",
|
|
46
|
-
"sideEffects":
|
|
46
|
+
"sideEffects": [
|
|
47
|
+
"build-style/**",
|
|
48
|
+
"src/**/*.scss"
|
|
49
|
+
],
|
|
47
50
|
"dependencies": {
|
|
48
|
-
"@wordpress/base-styles": "^6.
|
|
49
|
-
"@wordpress/components": "^32.
|
|
50
|
-
"@wordpress/element": "^6.
|
|
51
|
-
"@wordpress/i18n": "^6.
|
|
52
|
-
"@wordpress/private-apis": "^1.
|
|
53
|
-
"@wordpress/route": "^0.
|
|
54
|
-
"@wordpress/ui": "^0.
|
|
51
|
+
"@wordpress/base-styles": "^6.19.0",
|
|
52
|
+
"@wordpress/components": "^32.5.0",
|
|
53
|
+
"@wordpress/element": "^6.43.0",
|
|
54
|
+
"@wordpress/i18n": "^6.16.0",
|
|
55
|
+
"@wordpress/private-apis": "^1.43.0",
|
|
56
|
+
"@wordpress/route": "^0.9.0",
|
|
57
|
+
"@wordpress/ui": "^0.10.0",
|
|
55
58
|
"clsx": "^2.1.1"
|
|
56
59
|
},
|
|
57
60
|
"peerDependencies": {
|
|
@@ -60,5 +63,5 @@
|
|
|
60
63
|
"publishConfig": {
|
|
61
64
|
"access": "public"
|
|
62
65
|
},
|
|
63
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "2cea90674d11aa521ec3f71652fb3a6a4c383969"
|
|
64
67
|
}
|
|
@@ -11,38 +11,47 @@ import {
|
|
|
11
11
|
/**
|
|
12
12
|
* Internal dependencies
|
|
13
13
|
*/
|
|
14
|
-
import type {
|
|
15
|
-
BreadcrumbsProps,
|
|
16
|
-
BreadcrumbItem as BreadcrumbItemType,
|
|
17
|
-
} from './types';
|
|
18
|
-
|
|
19
|
-
const BreadcrumbItem = ( {
|
|
20
|
-
item: { label, to },
|
|
21
|
-
}: {
|
|
22
|
-
item: BreadcrumbItemType;
|
|
23
|
-
} ) => {
|
|
24
|
-
if ( ! to ) {
|
|
25
|
-
return (
|
|
26
|
-
<li>
|
|
27
|
-
<Heading level={ 1 } truncate>
|
|
28
|
-
{ label }
|
|
29
|
-
</Heading>
|
|
30
|
-
</li>
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<li>
|
|
36
|
-
<Link to={ to }>{ label }</Link>
|
|
37
|
-
</li>
|
|
38
|
-
);
|
|
39
|
-
};
|
|
14
|
+
import type { BreadcrumbsProps } from './types';
|
|
40
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Renders a breadcrumb navigation trail.
|
|
18
|
+
*
|
|
19
|
+
* All items except the last one must provide a `to` prop for navigation.
|
|
20
|
+
* In development mode, an error is thrown when a non-last item is missing `to`.
|
|
21
|
+
* The last item represents the current page and its `to` prop is optional.
|
|
22
|
+
* Only the last item (when it has no `to` prop) is rendered as an `h1`.
|
|
23
|
+
*
|
|
24
|
+
* @param props
|
|
25
|
+
* @param props.items The breadcrumb items to display.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```jsx
|
|
29
|
+
* <Breadcrumbs
|
|
30
|
+
* items={ [
|
|
31
|
+
* { label: 'Home', to: '/' },
|
|
32
|
+
* { label: 'Settings', to: '/settings' },
|
|
33
|
+
* { label: 'General' },
|
|
34
|
+
* ] }
|
|
35
|
+
* />
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
41
38
|
export const Breadcrumbs = ( { items }: BreadcrumbsProps ) => {
|
|
42
39
|
if ( ! items.length ) {
|
|
43
40
|
return null;
|
|
44
41
|
}
|
|
45
42
|
|
|
43
|
+
const precedingItems = items.slice( 0, -1 );
|
|
44
|
+
const lastItem = items[ items.length - 1 ];
|
|
45
|
+
|
|
46
|
+
if ( process.env.NODE_ENV !== 'production' ) {
|
|
47
|
+
const invalidItem = precedingItems.find( ( item ) => ! item.to );
|
|
48
|
+
if ( invalidItem ) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Breadcrumbs: item "${ invalidItem.label }" is missing a \`to\` prop. All items except the last one must have a \`to\` prop.`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
46
55
|
return (
|
|
47
56
|
<nav aria-label={ __( 'Breadcrumbs' ) }>
|
|
48
57
|
<HStack
|
|
@@ -52,9 +61,20 @@ export const Breadcrumbs = ( { items }: BreadcrumbsProps ) => {
|
|
|
52
61
|
justify="flex-start"
|
|
53
62
|
alignment="center"
|
|
54
63
|
>
|
|
55
|
-
{
|
|
56
|
-
<
|
|
64
|
+
{ precedingItems.map( ( item, index ) => (
|
|
65
|
+
<li key={ index }>
|
|
66
|
+
<Link to={ item.to }>{ item.label }</Link>
|
|
67
|
+
</li>
|
|
57
68
|
) ) }
|
|
69
|
+
<li>
|
|
70
|
+
{ lastItem.to ? (
|
|
71
|
+
<Link to={ lastItem.to }>{ lastItem.label }</Link>
|
|
72
|
+
) : (
|
|
73
|
+
<Heading level={ 1 } truncate>
|
|
74
|
+
{ lastItem.label }
|
|
75
|
+
</Heading>
|
|
76
|
+
) }
|
|
77
|
+
</li>
|
|
58
78
|
</HStack>
|
|
59
79
|
</nav>
|
|
60
80
|
);
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { Breadcrumbs } from '..';
|
|
10
|
+
|
|
11
|
+
jest.mock( '@wordpress/route', () => ( {
|
|
12
|
+
Link: ( { to, children }: { to: string; children: React.ReactNode } ) => (
|
|
13
|
+
<a href={ to }>{ children }</a>
|
|
14
|
+
),
|
|
15
|
+
} ) );
|
|
16
|
+
|
|
17
|
+
describe( 'Breadcrumbs', () => {
|
|
18
|
+
describe( 'validation', () => {
|
|
19
|
+
it( 'should throw when a preceding item is missing `to`', () => {
|
|
20
|
+
expect( () =>
|
|
21
|
+
render(
|
|
22
|
+
<Breadcrumbs
|
|
23
|
+
items={ [
|
|
24
|
+
{ label: 'Home' },
|
|
25
|
+
{ label: 'Settings', to: '/settings' },
|
|
26
|
+
{ label: 'General' },
|
|
27
|
+
] }
|
|
28
|
+
/>
|
|
29
|
+
)
|
|
30
|
+
).toThrow( /item "Home" is missing a `to` prop/ );
|
|
31
|
+
expect( console ).toHaveErrored();
|
|
32
|
+
} );
|
|
33
|
+
|
|
34
|
+
it( 'should throw for the first preceding item missing `to`', () => {
|
|
35
|
+
expect( () =>
|
|
36
|
+
render(
|
|
37
|
+
<Breadcrumbs
|
|
38
|
+
items={ [
|
|
39
|
+
{ label: 'Home' },
|
|
40
|
+
{ label: 'Settings' },
|
|
41
|
+
{ label: 'General' },
|
|
42
|
+
] }
|
|
43
|
+
/>
|
|
44
|
+
)
|
|
45
|
+
).toThrow( /item "Home" is missing a `to` prop/ );
|
|
46
|
+
expect( console ).toHaveErrored();
|
|
47
|
+
} );
|
|
48
|
+
|
|
49
|
+
it( 'should not throw when all preceding items have `to`', () => {
|
|
50
|
+
expect( () =>
|
|
51
|
+
render(
|
|
52
|
+
<Breadcrumbs
|
|
53
|
+
items={ [
|
|
54
|
+
{ label: 'Home', to: '/' },
|
|
55
|
+
{ label: 'Settings', to: '/settings' },
|
|
56
|
+
{ label: 'General' },
|
|
57
|
+
] }
|
|
58
|
+
/>
|
|
59
|
+
)
|
|
60
|
+
).not.toThrow();
|
|
61
|
+
} );
|
|
62
|
+
|
|
63
|
+
it( 'should not throw when there is only one item without `to`', () => {
|
|
64
|
+
expect( () =>
|
|
65
|
+
render( <Breadcrumbs items={ [ { label: 'Dashboard' } ] } /> )
|
|
66
|
+
).not.toThrow();
|
|
67
|
+
} );
|
|
68
|
+
|
|
69
|
+
it( 'should not throw when items is empty', () => {
|
|
70
|
+
expect( () =>
|
|
71
|
+
render( <Breadcrumbs items={ [] } /> )
|
|
72
|
+
).not.toThrow();
|
|
73
|
+
} );
|
|
74
|
+
} );
|
|
75
|
+
|
|
76
|
+
describe( 'rendering', () => {
|
|
77
|
+
it( 'should render nothing when items is empty', () => {
|
|
78
|
+
const { container } = render( <Breadcrumbs items={ [] } /> );
|
|
79
|
+
expect( container ).toBeEmptyDOMElement();
|
|
80
|
+
} );
|
|
81
|
+
|
|
82
|
+
it( 'should render the last item as an h1 when it has no `to`', () => {
|
|
83
|
+
render(
|
|
84
|
+
<Breadcrumbs
|
|
85
|
+
items={ [
|
|
86
|
+
{ label: 'Home', to: '/' },
|
|
87
|
+
{ label: 'Current Page' },
|
|
88
|
+
] }
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
expect(
|
|
93
|
+
screen.getByRole( 'heading', { level: 1 } )
|
|
94
|
+
).toHaveTextContent( 'Current Page' );
|
|
95
|
+
} );
|
|
96
|
+
|
|
97
|
+
it( 'should render the last item as a link when it has `to`', () => {
|
|
98
|
+
render(
|
|
99
|
+
<Breadcrumbs
|
|
100
|
+
items={ [
|
|
101
|
+
{ label: 'Home', to: '/' },
|
|
102
|
+
{ label: 'Settings', to: '/settings' },
|
|
103
|
+
] }
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
expect(
|
|
108
|
+
screen.queryByRole( 'heading', { level: 1 } )
|
|
109
|
+
).not.toBeInTheDocument();
|
|
110
|
+
|
|
111
|
+
const links = screen.getAllByRole( 'link' );
|
|
112
|
+
expect( links ).toHaveLength( 2 );
|
|
113
|
+
expect( links[ 1 ] ).toHaveTextContent( 'Settings' );
|
|
114
|
+
expect( links[ 1 ] ).toHaveAttribute( 'href', '/settings' );
|
|
115
|
+
} );
|
|
116
|
+
|
|
117
|
+
it( 'should render preceding items as links', () => {
|
|
118
|
+
render(
|
|
119
|
+
<Breadcrumbs
|
|
120
|
+
items={ [
|
|
121
|
+
{ label: 'Home', to: '/' },
|
|
122
|
+
{ label: 'Settings', to: '/settings' },
|
|
123
|
+
{ label: 'General' },
|
|
124
|
+
] }
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const links = screen.getAllByRole( 'link' );
|
|
129
|
+
expect( links ).toHaveLength( 2 );
|
|
130
|
+
expect( links[ 0 ] ).toHaveTextContent( 'Home' );
|
|
131
|
+
expect( links[ 0 ] ).toHaveAttribute( 'href', '/' );
|
|
132
|
+
expect( links[ 1 ] ).toHaveTextContent( 'Settings' );
|
|
133
|
+
expect( links[ 1 ] ).toHaveAttribute( 'href', '/settings' );
|
|
134
|
+
} );
|
|
135
|
+
|
|
136
|
+
it( 'should never render preceding items as headings', () => {
|
|
137
|
+
render(
|
|
138
|
+
<Breadcrumbs
|
|
139
|
+
items={ [
|
|
140
|
+
{ label: 'Home', to: '/' },
|
|
141
|
+
{ label: 'Settings', to: '/settings' },
|
|
142
|
+
{ label: 'General' },
|
|
143
|
+
] }
|
|
144
|
+
/>
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const headings = screen.getAllByRole( 'heading', { level: 1 } );
|
|
148
|
+
expect( headings ).toHaveLength( 1 );
|
|
149
|
+
expect( headings[ 0 ] ).toHaveTextContent( 'General' );
|
|
150
|
+
} );
|
|
151
|
+
|
|
152
|
+
it( 'should render a single item without `to` as an h1', () => {
|
|
153
|
+
render( <Breadcrumbs items={ [ { label: 'Dashboard' } ] } /> );
|
|
154
|
+
|
|
155
|
+
expect(
|
|
156
|
+
screen.getByRole( 'heading', { level: 1 } )
|
|
157
|
+
).toHaveTextContent( 'Dashboard' );
|
|
158
|
+
expect( screen.queryByRole( 'link' ) ).not.toBeInTheDocument();
|
|
159
|
+
} );
|
|
160
|
+
|
|
161
|
+
it( 'should render inside a nav with an accessible label', () => {
|
|
162
|
+
render( <Breadcrumbs items={ [ { label: 'Home', to: '/' } ] } /> );
|
|
163
|
+
|
|
164
|
+
expect(
|
|
165
|
+
screen.getByRole( 'navigation', { name: 'Breadcrumbs' } )
|
|
166
|
+
).toBeInTheDocument();
|
|
167
|
+
} );
|
|
168
|
+
} );
|
|
169
|
+
} );
|
package/src/breadcrumbs/types.ts
CHANGED
|
@@ -6,7 +6,8 @@ export interface BreadcrumbItem {
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* The router path that the breadcrumb item should link to.
|
|
9
|
-
* It is optional
|
|
9
|
+
* It is optional for the last item (the current page).
|
|
10
|
+
* All preceding items should provide a `to` prop.
|
|
10
11
|
*/
|
|
11
12
|
to?: string;
|
|
12
13
|
}
|
|
@@ -14,7 +15,9 @@ export interface BreadcrumbItem {
|
|
|
14
15
|
export interface BreadcrumbsProps extends React.HTMLAttributes< HTMLElement > {
|
|
15
16
|
/**
|
|
16
17
|
* An array of items to display in the breadcrumb trail.
|
|
17
|
-
* The last item is considered the current item.
|
|
18
|
+
* The last item is considered the current item and has an optional `to` prop.
|
|
19
|
+
* All preceding items must have a `to` prop — in development mode,
|
|
20
|
+
* an error is thrown when this requirement is not met.
|
|
18
21
|
*/
|
|
19
22
|
items: BreadcrumbItem[];
|
|
20
23
|
/**
|
package/src/page/index.tsx
CHANGED
|
@@ -41,7 +41,7 @@ function Page( {
|
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
43
|
<NavigableRegion className={ classes } ariaLabel={ effectiveAriaLabel }>
|
|
44
|
-
{ ( title || breadcrumbs || badges ) && (
|
|
44
|
+
{ ( title || breadcrumbs || badges || actions ) && (
|
|
45
45
|
<Header
|
|
46
46
|
headingLevel={ headingLevel }
|
|
47
47
|
breadcrumbs={ breadcrumbs }
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
5
|
-
|
|
5
|
+
// eslint-disable-next-line @wordpress/use-recommended-components -- admin-ui is a bundled package that depends on @wordpress/ui
|
|
6
|
+
import { Badge, Button, Text } from '@wordpress/ui';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Internal dependencies
|
|
@@ -82,3 +83,85 @@ export const WithBreadcrumbsAndSubtitle: Story = {
|
|
|
82
83
|
children: <Text>Page content here</Text>,
|
|
83
84
|
},
|
|
84
85
|
};
|
|
86
|
+
|
|
87
|
+
export const WithoutHeader: Story = {
|
|
88
|
+
args: {
|
|
89
|
+
showSidebarToggle: false,
|
|
90
|
+
hasPadding: true,
|
|
91
|
+
children: <Text>Page content here</Text>,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const WithTitleAndBadges: Story = {
|
|
96
|
+
args: {
|
|
97
|
+
title: 'Page title',
|
|
98
|
+
badges: <Badge intent="informational">Status</Badge>,
|
|
99
|
+
showSidebarToggle: false,
|
|
100
|
+
hasPadding: true,
|
|
101
|
+
children: <Text>Page content here</Text>,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const WithBreadcrumbsAndBadges: Story = {
|
|
106
|
+
args: {
|
|
107
|
+
showSidebarToggle: false,
|
|
108
|
+
breadcrumbs: (
|
|
109
|
+
<Breadcrumbs
|
|
110
|
+
items={ [
|
|
111
|
+
{ label: 'Root breadcrumb', to: '/connectors' },
|
|
112
|
+
{ label: 'Level 1 breadcrumb' },
|
|
113
|
+
] }
|
|
114
|
+
/>
|
|
115
|
+
),
|
|
116
|
+
badges: <Badge intent="none">Published</Badge>,
|
|
117
|
+
hasPadding: true,
|
|
118
|
+
children: <Text>Page content here</Text>,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const WithActions: Story = {
|
|
123
|
+
args: {
|
|
124
|
+
title: 'Page title',
|
|
125
|
+
actions: (
|
|
126
|
+
<>
|
|
127
|
+
<Button size="compact" variant="outline">
|
|
128
|
+
Cancel
|
|
129
|
+
</Button>
|
|
130
|
+
<Button size="compact" variant="solid">
|
|
131
|
+
Save
|
|
132
|
+
</Button>
|
|
133
|
+
</>
|
|
134
|
+
),
|
|
135
|
+
showSidebarToggle: false,
|
|
136
|
+
hasPadding: true,
|
|
137
|
+
children: <Text>Page content here</Text>,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const FullHeader: Story = {
|
|
142
|
+
args: {
|
|
143
|
+
subTitle: 'All of the subtitle text you need goes here.',
|
|
144
|
+
breadcrumbs: (
|
|
145
|
+
<Breadcrumbs
|
|
146
|
+
items={ [
|
|
147
|
+
{ label: 'Root breadcrumb', to: '/connectors' },
|
|
148
|
+
{ label: 'Level 1 breadcrumb' },
|
|
149
|
+
] }
|
|
150
|
+
/>
|
|
151
|
+
),
|
|
152
|
+
badges: <Badge intent="informational">Status</Badge>,
|
|
153
|
+
actions: (
|
|
154
|
+
<>
|
|
155
|
+
<Button size="compact" variant="outline">
|
|
156
|
+
Cancel
|
|
157
|
+
</Button>
|
|
158
|
+
<Button size="compact" variant="solid">
|
|
159
|
+
Save
|
|
160
|
+
</Button>
|
|
161
|
+
</>
|
|
162
|
+
),
|
|
163
|
+
showSidebarToggle: false,
|
|
164
|
+
hasPadding: true,
|
|
165
|
+
children: <Text>Page content here</Text>,
|
|
166
|
+
},
|
|
167
|
+
};
|
package/src/page/style.scss
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
.admin-ui-page {
|
|
2
2
|
display: flex;
|
|
3
3
|
height: 100%;
|
|
4
|
+
// @TODO: Revisit page background color. Consider using
|
|
5
|
+
// --wpds-color-bg-surface-neutral once existing pages (e.g. Styles)
|
|
6
|
+
// are updated to handle the contrast change.
|
|
7
|
+
// See https://github.com/WordPress/gutenberg/pull/76548
|
|
4
8
|
background-color: var(--wpds-color-bg-surface-neutral-strong);
|
|
5
9
|
color: var(--wpds-color-fg-content-neutral);
|
|
6
10
|
position: relative;
|