Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/admin-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@wordpress/components": "file:../components",
"@wordpress/element": "file:../element",
"@wordpress/i18n": "file:../i18n",
"@wordpress/private-apis": "file:../private-apis",
"@wordpress/route": "file:../route",
"@wordpress/ui": "file:../ui",
"clsx": "^2.1.1"
Expand Down
45 changes: 45 additions & 0 deletions packages/admin-ui/src/breadcrumbs/stories/index.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* External dependencies
*/
import type { Meta, StoryObj } from '@storybook/react-vite';

/**
* Internal dependencies
*/
import Breadcrumbs from '..';
import { withRouter } from '../../stories/with-router';

const meta: Meta< typeof Breadcrumbs > = {
component: Breadcrumbs,
title: 'Admin UI/Breadcrumbs',
decorators: [ withRouter ],
};

export default meta;

type Story = StoryObj< typeof meta >;

export const SingleItem: Story = {
args: {
items: [ { label: 'Root breadcrumb' } ],
},
};

export const TwoLevels: Story = {
args: {
items: [
{ label: 'Root breadcrumb', to: '/settings' },
{ label: 'Level 1 breadcrumb' },
],
},
};

export const ThreeLevels: Story = {
args: {
items: [
{ label: 'Root breadcrumb', to: '/settings' },
{ label: 'Level 1 breadcrumb', to: '/settings/connectors' },
{ label: 'Level 2 breadcrumb' },
],
},
};
10 changes: 10 additions & 0 deletions packages/admin-ui/src/lock-unlock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* WordPress dependencies
*/
import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis';

export const { lock, unlock } =
__dangerousOptInToUnstableAPIsOnlyForCoreModules(
'I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.',
'@wordpress/admin-ui'
);
88 changes: 88 additions & 0 deletions packages/admin-ui/src/page/stories/index.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* External dependencies
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import { __experimentalText as Text } from '@wordpress/components';

/**
* Internal dependencies
*/
import Page from '..';
import Breadcrumbs from '../../breadcrumbs';
import { withRouter } from '../../stories/with-router';

const meta: Meta< typeof Page > = {
component: Page,
title: 'Admin UI/Page',
parameters: {
layout: 'fullscreen',
},
decorators: [
( Story ) => (
<div style={ { minHeight: '400px' } }>
<Story />
</div>
),
withRouter,
],
};

export default meta;

type Story = StoryObj< typeof meta >;

export const Default: Story = {
args: {
title: 'Page title',
showSidebarToggle: false,
children: (
<Text style={ { padding: '24px 24px' } }>Page content here</Text>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<Text style={ { padding: '24px 24px' } }>Page content here</Text>
<Text>Page content here</Text>

Not a blocker, but I don't think that consumers of Page should have to pass a custom padding every time they use the component. Ideally, the main spacing should be provided by Page and the consumers should only worry about content + intra-content spacing

(here and everywhere else in the PR)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you on consumers not having to pass custom padding, but this is how it looks without the injected padding:
image

Do you know if we have some container for this purpose already? Should the Page component handle this so its children are already contained with paddings?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, we can leave it with or without the injected paddings while we figure out whose responsibility is the children's paddings.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

@ciampo ciampo Mar 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a look at the Page component, and it has a dedicated hasPadding prop, which serves exactly that purpose.

Now, we normally code stories so that they use as many default prop values as possible (and hasPadding is false by default), so I suggest that at least:

  • if we want to keep the padding on the default story, we change it to a shorthand (ie just 24px) and add a code comment explaining that, by default, Page doesn't add padding to its content
  • we could have a dedicated with padding story

Also curious to hear @jameskoster and @mirka 's thoughts on this one

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we ought to consider reusing the technique from Card. IE Content and FullBleed sub-components that handle padding (Content would have padding applied). This seems a more flexible solution compared to unilaterally saying the contents are padded or not.

),
},
};

export const WithSubtitle: Story = {
args: {
title: 'Page title',
subTitle: 'All of the subtitle text you need goes here.',
showSidebarToggle: false,
children: (
<Text style={ { padding: '24px 24px' } }>Page content here</Text>
),
},
};

export const WithBreadcrumbs: Story = {
args: {
showSidebarToggle: false,
breadcrumbs: (
<Breadcrumbs
items={ [
{ label: 'Root breadcrumb', to: '/connectors' },
{ label: 'Level 1 breadcrumb' },
] }
/>
),
children: (
<Text style={ { padding: '24px 24px' } }>Page content here</Text>
),
},
};

export const WithBreadcrumbsAndSubtitle: Story = {
args: {
showSidebarToggle: false,
subTitle: 'All of the subtitle text you need goes here.',
breadcrumbs: (
<Breadcrumbs
items={ [
{ label: 'Root breadcrumb', to: '/connectors' },
{ label: 'Level 1 breadcrumb' },
] }
/>
),
children: (
<Text style={ { padding: '24px 24px' } }>Page content here</Text>
),
},
};
29 changes: 29 additions & 0 deletions packages/admin-ui/src/stories/with-router.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* WordPress dependencies
*/
import { privateApis as routePrivateApis } from '@wordpress/route';

/**
* Internal dependencies
*/
import { unlock } from '../lock-unlock';

const { createMemoryHistory, createRootRoute, createRouter, RouterProvider } =
unlock( routePrivateApis );

/**
* Storybook decorator that provides a router context.
*
* Wraps stories in a minimal tanstack router (via @wordpress/route private
* APIs) so that components consuming `Link` from `@wordpress/route` can
* render without errors.
*/
export function withRouter( Story: React.ComponentType ) {
const rootRoute = createRootRoute( { component: Story } );
const router = createRouter( {
routeTree: rootRoute,
history: createMemoryHistory( { initialEntries: [ '/' ] } ),
defaultNotFoundComponent: () => null,
} );
return <RouterProvider router={ router } />;
}
1 change: 1 addition & 0 deletions packages/admin-ui/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
{ "path": "../components" },
{ "path": "../element" },
{ "path": "../i18n" },
{ "path": "../private-apis" },
{ "path": "../route" },
{ "path": "../ui" }
]
Expand Down
1 change: 1 addition & 0 deletions packages/private-apis/src/implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* The list of core modules allowed to opt-in to the private APIs.
*/
const CORE_MODULES_USING_PRIVATE_APIS = [
'@wordpress/admin-ui',
'@wordpress/block-directory',
'@wordpress/block-editor',
'@wordpress/block-library',
Expand Down
2 changes: 2 additions & 0 deletions packages/route/src/private-apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
createBrowserHistory,
createLazyRoute,
createLink,
createMemoryHistory,
createRootRoute,
createRoute,
createRouter,
Expand Down Expand Up @@ -38,6 +39,7 @@ lock( privateApis, {
// Router creation and setup
createBrowserHistory,
createLazyRoute,
createMemoryHistory,
createRouter,
createRootRoute,
createRoute,
Expand Down
1 change: 1 addition & 0 deletions storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const stories = [
'../packages/theme/src/**/stories/*.story.@(tsx|mdx)',
'../packages/ui/src/**/stories/*.mdx',
'../packages/ui/src/**/stories/*.story.@(ts|tsx)',
'../packages/admin-ui/src/**/stories/*.story.@(ts|tsx)',
].filter( Boolean );

const config: StorybookConfig = {
Expand Down
1 change: 1 addition & 0 deletions storybook/package-styles/admin-ui-ltr.lazy.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "../../packages/admin-ui/build-style/style.css";
1 change: 1 addition & 0 deletions storybook/package-styles/admin-ui-rtl.lazy.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "../../packages/admin-ui/build-style/style-rtl.css";
7 changes: 7 additions & 0 deletions storybook/package-styles/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import fieldsLtr from '../package-styles/fields-ltr.lazy.scss?inline';
import fieldsRtl from '../package-styles/fields-rtl.lazy.scss?inline';
import mediaFieldsLtr from '../package-styles/media-fields-ltr.lazy.scss?inline';
import mediaFieldsRtl from '../package-styles/media-fields-rtl.lazy.scss?inline';
import adminUiLtr from '../package-styles/admin-ui-ltr.lazy.scss?inline';
import adminUiRtl from '../package-styles/admin-ui-rtl.lazy.scss?inline';
import designTokens from '../package-styles/design-tokens.lazy.scss?inline';

/**
Expand Down Expand Up @@ -73,6 +75,11 @@ const CONFIG = [
ltr: [ componentsLtr, dataviewsLtr, fieldsLtr, mediaFieldsLtr ],
rtl: [ componentsRtl, dataviewsRtl, fieldsRtl, mediaFieldsRtl ],
},
{
componentIdMatcher: /^admin-ui-/,
ltr: [ designTokens, componentsLtr, adminUiLtr ],
rtl: [ designTokens, componentsRtl, adminUiRtl ],
},
{
componentIdMatcher: /^design-system-/,
ltr: [ designTokens ],
Expand Down
Loading