diff --git a/bin/api-docs/gen-components-docs/get-tags-from-storybook.mjs b/bin/api-docs/gen-components-docs/get-tags-from-storybook.mjs new file mode 100644 index 00000000000000..84d7beaf1e4076 --- /dev/null +++ b/bin/api-docs/gen-components-docs/get-tags-from-storybook.mjs @@ -0,0 +1,30 @@ +/** + * External dependencies + */ +import fs from 'node:fs/promises'; +import babel from '@babel/core'; + +/** + * Returns `meta.tags` from a Storybook file. + * + * @param {string} filePath + * @return {Promise} Array of tags. + */ +export async function getTagsFromStorybook( filePath ) { + const fileContent = await fs.readFile( filePath, 'utf8' ); + const parsedFile = babel.parse( fileContent, { + filename: filePath, + } ); + + const meta = parsedFile.program.body.find( + ( node ) => + node.type === 'VariableDeclaration' && + node.declarations[ 0 ].id.name === 'meta' + ); + + return ( + meta.declarations[ 0 ].init.properties + .find( ( node ) => node.key.name === 'tags' ) + ?.value.elements.map( ( node ) => node.value ) ?? [] + ); +} diff --git a/bin/api-docs/gen-components-docs/index.mjs b/bin/api-docs/gen-components-docs/index.mjs index c7109dc4982c36..30888acf851cab 100644 --- a/bin/api-docs/gen-components-docs/index.mjs +++ b/bin/api-docs/gen-components-docs/index.mjs @@ -11,6 +11,7 @@ import path from 'path'; */ import { generateMarkdownDocs } from './markdown/index.mjs'; import { getDescriptionsForSubcomponents } from './get-subcomponent-descriptions.mjs'; +import { getTagsFromStorybook } from './get-tags-from-storybook.mjs'; const MANIFEST_GLOB = 'packages/components/src/**/docs-manifest.json'; @@ -113,9 +114,17 @@ await Promise.all( } ) ?? [] ); + const tags = await getTagsFromStorybook( + path.resolve( + path.dirname( manifestPath ), + 'stories/index.story.tsx' + ) + ); + const docs = generateMarkdownDocs( { typeDocs, subcomponentTypeDocs, + tags, } ); const outputFile = path.resolve( path.dirname( manifestPath ), diff --git a/bin/api-docs/gen-components-docs/markdown/index.mjs b/bin/api-docs/gen-components-docs/markdown/index.mjs index b66aedaa144e7f..28e20dc3de12e0 100644 --- a/bin/api-docs/gen-components-docs/markdown/index.mjs +++ b/bin/api-docs/gen-components-docs/markdown/index.mjs @@ -18,10 +18,19 @@ json2md.converters.md = ( input ) => { return input?.trim() || ''; }; -export function generateMarkdownDocs( { typeDocs, subcomponentTypeDocs } ) { +export function generateMarkdownDocs( { + typeDocs, + subcomponentTypeDocs, + tags, +} ) { const mainDocsJson = [ { h1: typeDocs.displayName }, '', + tags.includes( 'status-private' ) && [ + { + p: '🔒 This component is locked as a [private API](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-private-apis/). We do not yet recommend using this outside of the Gutenberg project.', + }, + ], { p: `

See the WordPress Storybook for more detailed, interactive documentation.

`, }, diff --git a/packages/block-editor/src/components/dimensions-tool/stories/aspect-ratio-tool.story.js b/packages/block-editor/src/components/dimensions-tool/stories/aspect-ratio-tool.story.js index b853d780052942..aeb8a5f957425f 100644 --- a/packages/block-editor/src/components/dimensions-tool/stories/aspect-ratio-tool.story.js +++ b/packages/block-editor/src/components/dimensions-tool/stories/aspect-ratio-tool.story.js @@ -13,8 +13,9 @@ import { import AspectRatioTool from '../aspect-ratio-tool'; export default { - title: 'BlockEditor (Private APIs)/DimensionsTool/AspectRatioTool', + title: 'BlockEditor/DimensionsTool/AspectRatioTool', component: AspectRatioTool, + tags: [ 'status-private' ], argTypes: { panelId: { control: false }, onChange: { action: 'changed' }, diff --git a/packages/block-editor/src/components/dimensions-tool/stories/index.story.js b/packages/block-editor/src/components/dimensions-tool/stories/index.story.js index ebf08fba0c686b..0ccfba2b9e97a6 100644 --- a/packages/block-editor/src/components/dimensions-tool/stories/index.story.js +++ b/packages/block-editor/src/components/dimensions-tool/stories/index.story.js @@ -13,8 +13,9 @@ import { import DimensionsTool from '..'; export default { - title: 'BlockEditor (Private APIs)/DimensionsTool', + title: 'BlockEditor/DimensionsTool/DimensionsTool', component: DimensionsTool, + tags: [ 'status-private' ], argTypes: { panelId: { control: false }, onChange: { action: 'changed' }, diff --git a/packages/block-editor/src/components/dimensions-tool/stories/scale-tool.story.js b/packages/block-editor/src/components/dimensions-tool/stories/scale-tool.story.js index b485bf68a892d9..ea0a3ec194beed 100644 --- a/packages/block-editor/src/components/dimensions-tool/stories/scale-tool.story.js +++ b/packages/block-editor/src/components/dimensions-tool/stories/scale-tool.story.js @@ -13,8 +13,9 @@ import { import ScaleTool from '../scale-tool'; export default { - title: 'BlockEditor (Private APIs)/DimensionsTool/ScaleTool', + title: 'BlockEditor/DimensionsTool/ScaleTool', component: ScaleTool, + tags: [ 'status-private' ], argTypes: { panelId: { control: false }, onChange: { action: 'changed' }, diff --git a/packages/block-editor/src/components/dimensions-tool/stories/width-height-tool.story.js b/packages/block-editor/src/components/dimensions-tool/stories/width-height-tool.story.js index eed3cbc02f466e..86b3b4b22be60d 100644 --- a/packages/block-editor/src/components/dimensions-tool/stories/width-height-tool.story.js +++ b/packages/block-editor/src/components/dimensions-tool/stories/width-height-tool.story.js @@ -13,8 +13,9 @@ import { import WidthHeightTool from '../width-height-tool'; export default { - title: 'BlockEditor (Private APIs)/DimensionsTool/WidthHeightTool', + title: 'BlockEditor/DimensionsTool/WidthHeightTool', component: WidthHeightTool, + tags: [ 'status-private' ], argTypes: { panelId: { control: false }, onChange: { action: 'changed' }, diff --git a/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js b/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js index fd801779372aac..505785c87914d7 100644 --- a/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js +++ b/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js @@ -7,7 +7,7 @@ import fastDeepEqual from 'fast-deep-equal/es6'; * WordPress dependencies */ import { useRef, useLayoutEffect } from '@wordpress/element'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useRegistry } from '@wordpress/data'; import { synchronizeBlocksWithTemplate } from '@wordpress/blocks'; /** @@ -42,14 +42,7 @@ export default function useInnerBlockTemplateSync( ) { // Instead of adding a useSelect mapping here, please add to the useSelect // mapping in InnerBlocks! Every subscription impacts performance. - - const { - getBlocks, - getSelectedBlocksInitialCaretPosition, - isBlockSelected, - } = useSelect( blockEditorStore ); - const { replaceInnerBlocks, __unstableMarkNextChangeAsNotPersistent } = - useDispatch( blockEditorStore ); + const registry = useRegistry(); // Maintain a reference to the previous value so we can do a deep equality check. const existingTemplateRef = useRef( null ); @@ -57,6 +50,14 @@ export default function useInnerBlockTemplateSync( useLayoutEffect( () => { let isCancelled = false; + const { + getBlocks, + getSelectedBlocksInitialCaretPosition, + isBlockSelected, + } = registry.select( blockEditorStore ); + const { replaceInnerBlocks, __unstableMarkNextChangeAsNotPersistent } = + registry.dispatch( blockEditorStore ); + // There's an implicit dependency between useInnerBlockTemplateSync and useNestedSettingsUpdate // The former needs to happen after the latter and since the latter is using microtasks to batch updates (performance optimization), // we need to schedule this one in a microtask as well. @@ -110,5 +111,11 @@ export default function useInnerBlockTemplateSync( return () => { isCancelled = true; }; - }, [ template, templateLock, clientId ] ); + }, [ + template, + templateLock, + clientId, + registry, + templateInsertUpdatesSelection, + ] ); } diff --git a/packages/block-editor/src/components/resolution-tool/stories/index.story.js b/packages/block-editor/src/components/resolution-tool/stories/index.story.js index 75db0f90a568bc..531618b38224f9 100644 --- a/packages/block-editor/src/components/resolution-tool/stories/index.story.js +++ b/packages/block-editor/src/components/resolution-tool/stories/index.story.js @@ -13,8 +13,9 @@ import { import ResolutionTool from '..'; export default { - title: 'BlockEditor (Private APIs)/ResolutionControl', + title: 'BlockEditor/ResolutionControl', component: ResolutionTool, + tags: [ 'status-private' ], argTypes: { panelId: { control: false }, onChange: { action: 'changed' }, diff --git a/packages/block-editor/src/components/text-alignment-control/stories/index.story.js b/packages/block-editor/src/components/text-alignment-control/stories/index.story.js index fd97f9b60e6a90..076535ab330d69 100644 --- a/packages/block-editor/src/components/text-alignment-control/stories/index.story.js +++ b/packages/block-editor/src/components/text-alignment-control/stories/index.story.js @@ -11,6 +11,7 @@ import TextAlignmentControl from '../'; const meta = { title: 'BlockEditor/TextAlignmentControl', component: TextAlignmentControl, + tags: [ 'status-private' ], parameters: { docs: { canvas: { sourceState: 'shown' }, diff --git a/packages/components/src/badge/README.md b/packages/components/src/badge/README.md index 10767b2505039d..2100939684a856 100644 --- a/packages/components/src/badge/README.md +++ b/packages/components/src/badge/README.md @@ -2,6 +2,8 @@ +🔒 This component is locked as a [private API](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-private-apis/). We do not yet recommend using this outside of the Gutenberg project. +

See the WordPress Storybook for more detailed, interactive documentation.

## Props diff --git a/packages/components/src/badge/stories/index.story.tsx b/packages/components/src/badge/stories/index.story.tsx index 7f827d3bfabf5a..bbe0bef2a79472 100644 --- a/packages/components/src/badge/stories/index.story.tsx +++ b/packages/components/src/badge/stories/index.story.tsx @@ -8,12 +8,12 @@ import type { Meta, StoryObj } from '@storybook/react'; */ import Badge from '..'; -const meta = { +const meta: Meta< typeof Badge > = { component: Badge, title: 'Components/Containers/Badge', id: 'components-badge', tags: [ 'status-private' ], -} satisfies Meta< typeof Badge >; +}; export default meta; diff --git a/packages/core-data/src/private-selectors.ts b/packages/core-data/src/private-selectors.ts index 0d4a28ad174a19..fb0401509694ef 100644 --- a/packages/core-data/src/private-selectors.ts +++ b/packages/core-data/src/private-selectors.ts @@ -6,7 +6,12 @@ import { createSelector, createRegistrySelector } from '@wordpress/data'; /** * Internal dependencies */ -import { getDefaultTemplateId, getEntityRecord, type State } from './selectors'; +import { + canUser, + getDefaultTemplateId, + getEntityRecord, + type State, +} from './selectors'; import { STORE_NAME } from './name'; import { unlock } from './lock-unlock'; @@ -134,6 +139,13 @@ interface SiteData { export const getHomePage = createRegistrySelector( ( select ) => createSelector( () => { + const canReadSiteData = select( STORE_NAME ).canUser( 'read', { + kind: 'root', + name: 'site', + } ); + if ( ! canReadSiteData ) { + return null; + } const siteData = select( STORE_NAME ).getEntityRecord( 'root', 'site' @@ -156,7 +168,10 @@ export const getHomePage = createRegistrySelector( ( select ) => return { postType: 'wp_template', postId: frontPageTemplateId }; }, ( state ) => [ - getEntityRecord( state, 'root', 'site' ), + canUser( state, 'read', { + kind: 'root', + name: 'site', + } ) && getEntityRecord( state, 'root', 'site' ), getDefaultTemplateId( state, { slug: 'front-page', } ), @@ -165,6 +180,13 @@ export const getHomePage = createRegistrySelector( ( select ) => ); export const getPostsPageId = createRegistrySelector( ( select ) => () => { + const canReadSiteData = select( STORE_NAME ).canUser( 'read', { + kind: 'root', + name: 'site', + } ); + if ( ! canReadSiteData ) { + return null; + } const siteData = select( STORE_NAME ).getEntityRecord( 'root', 'site' ) as | SiteData | undefined; diff --git a/packages/editor/src/components/document-bar/index.js b/packages/editor/src/components/document-bar/index.js index f5ca65dfe18ed7..544b5024d88a89 100644 --- a/packages/editor/src/components/document-bar/index.js +++ b/packages/editor/src/components/document-bar/index.js @@ -22,6 +22,7 @@ import { store as commandsStore } from '@wordpress/commands'; import { useRef, useEffect } from '@wordpress/element'; import { useReducedMotion } from '@wordpress/compose'; import { decodeEntities } from '@wordpress/html-entities'; +import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; /** * Internal dependencies @@ -200,7 +201,7 @@ export default function DocumentBar( props ) { { title - ? decodeEntities( title ) + ? stripHTML( title ) : __( 'No title' ) } { pageTypeBadge && ( diff --git a/packages/editor/src/components/post-card-panel/index.js b/packages/editor/src/components/post-card-panel/index.js index 78f9522ba5f444..895545cb007f00 100644 --- a/packages/editor/src/components/post-card-panel/index.js +++ b/packages/editor/src/components/post-card-panel/index.js @@ -12,7 +12,7 @@ import { store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; -import { decodeEntities } from '@wordpress/html-entities'; +import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; /** * Internal dependencies @@ -94,7 +94,7 @@ export default function PostCardPanel( { labels?.name ); } else if ( postTitle ) { - title = decodeEntities( postTitle ); + title = stripHTML( postTitle ); } return ( diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index abdab284cfd138..1259eae623de93 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -163,6 +163,7 @@ export const ExperimentalEditorProvider = withRegistryProvider( BlockEditorProviderComponent = ExperimentalBlockEditorProvider, __unstableTemplate: template, } ) => { + const hasTemplate = !! template; const { editorSettings, selection, @@ -195,7 +196,9 @@ export const ExperimentalEditorProvider = withRegistryProvider( isReady: __unstableIsEditorReady(), mode: getRenderingMode(), defaultMode: - postTypeObject?.default_rendering_mode ?? 'post-only', + hasTemplate && postTypeObject?.default_rendering_mode + ? postTypeObject?.default_rendering_mode + : 'post-only', selection: getEditorSelection(), postTypeEntities: post.type === 'wp_template' @@ -203,7 +206,7 @@ export const ExperimentalEditorProvider = withRegistryProvider( : null, }; }, - [ post.type ] + [ post.type, hasTemplate ] ); const shouldRenderTemplate = !! template && mode !== 'post-only';