diff --git a/package-lock.json b/package-lock.json
index e831f80ec58887..9daff0e044507a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10256,7 +10256,6 @@
"@wordpress/is-shallow-equal": "file:packages/is-shallow-equal",
"@wordpress/keyboard-shortcuts": "file:packages/keyboard-shortcuts",
"@wordpress/keycodes": "file:packages/keycodes",
- "@wordpress/priority-queue": "file:packages/priority-queue",
"@wordpress/rich-text": "file:packages/rich-text",
"@wordpress/shortcode": "file:packages/shortcode",
"@wordpress/token-list": "file:packages/token-list",
@@ -10405,6 +10404,7 @@
"@babel/runtime": "^7.9.2",
"@wordpress/element": "file:packages/element",
"@wordpress/is-shallow-equal": "file:packages/is-shallow-equal",
+ "@wordpress/priority-queue": "file:packages/priority-queue",
"clipboard": "^2.0.1",
"lodash": "^4.17.15",
"mousetrap": "^1.6.5",
diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json
index 873921a8791098..93528e43f6db47 100644
--- a/packages/block-editor/package.json
+++ b/packages/block-editor/package.json
@@ -43,7 +43,6 @@
"@wordpress/is-shallow-equal": "file:../is-shallow-equal",
"@wordpress/keyboard-shortcuts": "file:../keyboard-shortcuts",
"@wordpress/keycodes": "file:../keycodes",
- "@wordpress/priority-queue": "file:../priority-queue",
"@wordpress/rich-text": "file:../rich-text",
"@wordpress/shortcode": "file:../shortcode",
"@wordpress/token-list": "file:../token-list",
diff --git a/packages/block-editor/src/components/inserter/block-patterns.js b/packages/block-editor/src/components/inserter/block-patterns.js
index 9fc7f67413469b..2da6a210fd0346 100644
--- a/packages/block-editor/src/components/inserter/block-patterns.js
+++ b/packages/block-editor/src/components/inserter/block-patterns.js
@@ -10,12 +10,12 @@ import { useMemo, useCallback } from '@wordpress/element';
import { parse } from '@wordpress/blocks';
import { ENTER, SPACE } from '@wordpress/keycodes';
import { __, _x } from '@wordpress/i18n';
+import { useAsyncList } from '@wordpress/compose';
/**
* Internal dependencies
*/
import BlockPreview from '../block-preview';
-import useAsyncList from './use-async-list';
import InserterPanel from './panel';
import { searchItems } from './search-items';
import InserterNoResults from './no-results';
diff --git a/packages/block-library/src/template-part/edit/placeholder.js b/packages/block-library/src/template-part/edit/placeholder.js
deleted file mode 100644
index f3b3f7bc43e48d..00000000000000
--- a/packages/block-library/src/template-part/edit/placeholder.js
+++ /dev/null
@@ -1,131 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { useEntityBlockEditor, EntityProvider } from '@wordpress/core-data';
-import { __ } from '@wordpress/i18n';
-import { BlockPreview } from '@wordpress/block-editor';
-import { useState, useCallback } from '@wordpress/element';
-import { useSelect, useDispatch } from '@wordpress/data';
-import { cleanForSlug } from '@wordpress/url';
-import { Placeholder, TextControl, Button } from '@wordpress/components';
-import { layout } from '@wordpress/icons';
-
-/**
- * Internal dependencies
- */
-import useTemplatePartPost from './use-template-part-post';
-
-function TemplatePartPreview() {
- const [ blocks ] = useEntityBlockEditor( 'postType', 'wp_template_part' );
- return (
-
-
- { __( 'Preview' ) }
-
-
-
- );
-}
-
-export default function TemplatePartPlaceholder( { setAttributes } ) {
- const [ slug, _setSlug ] = useState( '' );
- const [ theme, setTheme ] = useState( '' );
- const [ help, setHelp ] = useState();
-
- // Try to find an existing template part.
- const postId = useTemplatePartPost( null, slug, theme );
-
- // If found, get its preview.
- const preview = useSelect(
- ( select ) => {
- if ( ! postId ) {
- return;
- }
- const templatePart = select( 'core' ).getEntityRecord(
- 'postType',
- 'wp_template_part',
- postId
- );
- if ( templatePart ) {
- return (
-
-
-
- );
- }
- },
- [ postId ]
- );
-
- const setSlug = useCallback( ( nextSlug ) => {
- _setSlug( nextSlug );
- setHelp( cleanForSlug( nextSlug ) );
- }, [] );
-
- const { saveEntityRecord } = useDispatch( 'core' );
- const onChooseOrCreate = useCallback( async () => {
- const nextAttributes = { slug, theme };
- if ( postId !== undefined && postId !== null ) {
- // Existing template part found.
- nextAttributes.postId = postId;
- } else {
- // Create a new template part.
- try {
- const cleanSlug = cleanForSlug( slug );
- const templatePart = await saveEntityRecord(
- 'postType',
- 'wp_template_part',
- {
- title: cleanSlug,
- status: 'publish',
- slug: cleanSlug,
- meta: { theme },
- }
- );
- nextAttributes.postId = templatePart.id;
- } catch ( err ) {
- setHelp( __( 'Error adding template.' ) );
- }
- }
- setAttributes( nextAttributes );
- }, [ postId, slug, theme ] );
- return (
-
-
-
-
-
- { preview }
-
-
- );
-}
diff --git a/packages/block-library/src/template-part/edit/placeholder/index.js b/packages/block-library/src/template-part/edit/placeholder/index.js
new file mode 100644
index 00000000000000..1da611ae523378
--- /dev/null
+++ b/packages/block-library/src/template-part/edit/placeholder/index.js
@@ -0,0 +1,171 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { useState, useCallback } from '@wordpress/element';
+import { useDispatch } from '@wordpress/data';
+import { cleanForSlug } from '@wordpress/url';
+import {
+ Placeholder,
+ TextControl,
+ Button,
+ TabPanel,
+} from '@wordpress/components';
+import { layout } from '@wordpress/icons';
+
+/**
+ * Internal dependencies
+ */
+import useTemplatePartPost from '../use-template-part-post';
+import TemplatePartPreviews from './template-part-previews';
+
+const HELP_PHRASES = {
+ initial: __( 'Please add a name and theme for your new Template Part.' ),
+ unavailable: __(
+ 'Name and theme combination already in use, please try another.'
+ ),
+ available: __( 'This name is available!' ),
+ error: __( 'Error adding template.' ),
+};
+
+export default function TemplatePartPlaceholder( { setAttributes } ) {
+ const [ slug, _setSlug ] = useState( '' );
+ const [ theme, _setTheme ] = useState( '' );
+ const [ help, setHelp ] = useState( '' );
+
+ // Try to find an existing template part.
+ const postId = useTemplatePartPost( null, slug, theme );
+
+ const setSlug = useCallback(
+ ( nextSlug ) => {
+ _setSlug( nextSlug );
+ if ( help ) {
+ setHelp( '' );
+ }
+ },
+ [ help ]
+ );
+
+ const setTheme = useCallback(
+ ( nextTheme ) => {
+ _setTheme( nextTheme );
+ if ( help ) {
+ setHelp( '' );
+ }
+ },
+ [ help ]
+ );
+
+ const getHelpPhrase = () => {
+ if ( ! slug || ! theme ) {
+ return HELP_PHRASES.initial;
+ } else if ( postId ) {
+ return HELP_PHRASES.unavailable;
+ }
+
+ return HELP_PHRASES.available;
+ };
+
+ const { saveEntityRecord } = useDispatch( 'core' );
+ const onCreate = useCallback( async () => {
+ const nextAttributes = { slug, theme };
+ // Create a new template part.
+ try {
+ const cleanSlug = cleanForSlug( slug );
+ const templatePart = await saveEntityRecord(
+ 'postType',
+ 'wp_template_part',
+ {
+ title: cleanSlug,
+ status: 'publish',
+ slug: cleanSlug,
+ meta: { theme },
+ }
+ );
+ nextAttributes.postId = templatePart.id;
+ } catch ( err ) {
+ setHelp( HELP_PHRASES.error );
+ }
+ setAttributes( nextAttributes );
+ }, [ postId, slug, theme ] );
+
+ const [ filterValue, setFilterValue ] = useState( '' );
+
+ const createTab = (
+ <>
+
+
+
+
+
+ { help || getHelpPhrase() }
+
+
+ >
+ );
+
+ const selectTab = (
+ <>
+
+
+
+
+
+ >
+ );
+
+ return (
+
+
+ { ( tab ) => {
+ if ( tab.name === 'create' ) {
+ return createTab;
+ }
+ return selectTab;
+ } }
+
+
+ );
+}
diff --git a/packages/block-library/src/template-part/edit/placeholder/template-part-previews.js b/packages/block-library/src/template-part/edit/placeholder/template-part-previews.js
new file mode 100644
index 00000000000000..2d4d87801c0cd0
--- /dev/null
+++ b/packages/block-library/src/template-part/edit/placeholder/template-part-previews.js
@@ -0,0 +1,219 @@
+/**
+ * WordPress dependencies
+ */
+import { useSelect, useDispatch } from '@wordpress/data';
+import { parse } from '@wordpress/blocks';
+import { useMemo, useCallback } from '@wordpress/element';
+import { ENTER, SPACE } from '@wordpress/keycodes';
+import { __, sprintf } from '@wordpress/i18n';
+import { BlockPreview } from '@wordpress/block-editor';
+import { Icon } from '@wordpress/components';
+import { useAsyncList } from '@wordpress/compose';
+
+/**
+ * External dependencies
+ */
+import { groupBy, uniq, deburr } from 'lodash';
+
+function PreviewPlaceholder() {
+ return (
+
+ );
+}
+
+function TemplatePartItem( { templatePart, setAttributes } ) {
+ const { id, slug, theme } = templatePart;
+ // The 'raw' property is not defined for a brief period in the save cycle.
+ // The fallback prevents an error in the parse function while saving.
+ const content = templatePart.content.raw || '';
+ const blocks = useMemo( () => parse( content ), [ content ] );
+ const { createSuccessNotice } = useDispatch( 'core/notices' );
+
+ const onClick = useCallback( () => {
+ setAttributes( { postId: id, slug, theme } );
+ createSuccessNotice(
+ sprintf(
+ /* translators: %s: template part title. */
+ __( 'Template Part "%s" inserted.' ),
+ slug
+ ),
+ {
+ type: 'snackbar',
+ }
+ );
+ }, [ id, slug, theme ] );
+
+ return (
+ {
+ if ( ENTER === event.keyCode || SPACE === event.keyCode ) {
+ onClick();
+ }
+ } }
+ tabIndex={ 0 }
+ aria-label={ templatePart.slug }
+ >
+
+
+ { templatePart.slug }
+
+
+ );
+}
+
+function PanelGroup( { title, icon, children } ) {
+ return (
+ <>
+
+
+ { title }
+
+
+
+
+ { children }
+
+ >
+ );
+}
+
+function TemplatePartsByTheme( { templateParts, setAttributes } ) {
+ const templatePartsByTheme = useMemo( () => {
+ return Object.values( groupBy( templateParts, 'meta.theme' ) );
+ }, [ templateParts ] );
+ const currentShownTPs = useAsyncList( templateParts );
+
+ return templatePartsByTheme.map( ( templatePartList ) => (
+
+ { templatePartList.map( ( templatePart ) => {
+ return currentShownTPs.includes( templatePart ) ? (
+
+ ) : (
+
+ );
+ } ) }
+
+ ) );
+}
+
+function TemplatePartSearchResults( {
+ templateParts,
+ setAttributes,
+ filterValue,
+} ) {
+ const filteredTPs = useMemo( () => {
+ // Filter based on value.
+ // Remove diacritics and convert to lowercase to normalize.
+ const normalizedFilterValue = deburr( filterValue ).toLowerCase();
+ const searchResults = templateParts.filter(
+ ( { slug, meta: { theme } } ) =>
+ slug.toLowerCase().includes( normalizedFilterValue ) ||
+ // Since diacritics can be used in theme names, remove them for the comparison.
+ deburr( theme ).toLowerCase().includes( normalizedFilterValue )
+ );
+ // Order based on value location.
+ searchResults.sort( ( a, b ) => {
+ // First prioritize index found in slug.
+ const indexInSlugA = a.slug
+ .toLowerCase()
+ .indexOf( normalizedFilterValue );
+ const indexInSlugB = b.slug
+ .toLowerCase()
+ .indexOf( normalizedFilterValue );
+ if ( indexInSlugA !== -1 && indexInSlugB !== -1 ) {
+ return indexInSlugA - indexInSlugB;
+ } else if ( indexInSlugA !== -1 ) {
+ return -1;
+ } else if ( indexInSlugB !== -1 ) {
+ return 1;
+ }
+ // Second prioritize index found in theme.
+ // Since diacritics can be used in theme names, remove them for the comparison.
+ return (
+ deburr( a.meta.theme )
+ .toLowerCase()
+ .indexOf( normalizedFilterValue ) -
+ deburr( b.meta.theme )
+ .toLowerCase()
+ .indexOf( normalizedFilterValue )
+ );
+ } );
+ return searchResults;
+ }, [ filterValue, templateParts ] );
+
+ const currentShownTPs = useAsyncList( filteredTPs );
+
+ return filteredTPs.map( ( templatePart ) => (
+
+ { currentShownTPs.includes( templatePart ) ? (
+
+ ) : (
+
+ ) }
+
+ ) );
+}
+
+export default function TemplateParts( { setAttributes, filterValue } ) {
+ const templateParts = useSelect( ( select ) => {
+ const publishedTemplateParts = select( 'core' ).getEntityRecords(
+ 'postType',
+ 'wp_template_part',
+ {
+ status: [ 'publish' ],
+ per_page: -1,
+ }
+ );
+ const resolvedTemplateParts = select( 'core' ).getEntityRecords(
+ 'postType',
+ 'wp_template_part',
+ {
+ resolved: true,
+ per_page: -1,
+ }
+ );
+ const combinedTemplateParts = [];
+ if ( publishedTemplateParts ) {
+ combinedTemplateParts.push( ...publishedTemplateParts );
+ }
+ if ( resolvedTemplateParts ) {
+ combinedTemplateParts.push( ...resolvedTemplateParts );
+ }
+ return uniq( combinedTemplateParts );
+ }, [] );
+
+ if ( ! templateParts || ! templateParts.length ) {
+ return null;
+ }
+
+ if ( filterValue ) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+}
diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss
index 2b38d51f999fba..0b2712129a270d 100644
--- a/packages/block-library/src/template-part/editor.scss
+++ b/packages/block-library/src/template-part/editor.scss
@@ -1,24 +1,105 @@
-.wp-block-template-part__placeholder-input-container {
+
+.wp-block-template-part__placeholder-tabs {
display: flex;
- flex-wrap: wrap;
- width: 100%;
-}
+ flex-grow: 1;
+ flex-direction: column;
-.wp-block-template-part__placeholder-input {
- margin: 5px;
-}
+ .components-tab-panel__tabs {
+ border-bottom: $border-width solid $light-gray-500;
+
+ .components-tab-panel__tabs-item {
+ flex-grow: 1;
+ margin-bottom: -$border-width;
+ }
+ }
+
+ .components-tab-panel__tab-content {
+ margin-top: $grid-unit-20;
+ display: flex;
+ flex-grow: 1;
+ flex-direction: column;
+ position: relative;
+ }
-.wp-block-template-part__placeholder-preview {
- margin-bottom: 15px;
- width: 100%;
+ .wp-block-template-part__placeholder-input-container {
+ display: flex;
+ flex-wrap: wrap;
+ width: 100%;
- .block-editor-block-preview__container {
- padding: 1px;
+ .wp-block-template-part__placeholder-input {
+ margin: 5px;
+ }
+ }
+
+ .wp-block-template-part__placeholder-help-phrase {
+ padding: 0 $grid-unit-15 $grid-unit-15;
+ }
+
+ .wp-block-template-part__placeholder-create-button {
+ align-self: flex-start;
+ }
+
+ .wp-block-template-part__placeholder-preview-filter-input {
+ width: inherit;
}
}
-.wp-block-template-part__placeholder-preview-title {
- font-size: 15px;
- font-weight: 600;
- margin-bottom: 4px;
+
+.wp-block-template-part__placeholder-preview-container {
+ width: inherit;
+ max-height: 600px;
+ overflow-y: scroll;
+ background: $white;
+ border-radius: $radius-block-ui;
+ border: $border-width solid $light-gray-500;
+ top: $grid-unit-20;
+ left: calc(100% + #{$grid-unit-20});
+
+ .wp-block-template-part__placeholder-preview-item {
+ border-radius: $radius-block-ui;
+ cursor: pointer;
+ margin-top: $grid-unit-20;
+ transition: all 0.05s ease-in-out;
+ position: relative;
+ border: $border-width solid transparent;
+
+ &:hover {
+ border: $border-width solid $theme-color;
+ }
+
+ &:focus {
+ box-shadow: inset 0 0 0 1px $white, 0 0 0 $border-width-focus $theme-color;
+
+ // Windows High Contrast mode will show this outline, but not the box-shadow.
+ outline: 2px solid transparent;
+ }
+
+ &.is-placeholder {
+ min-height: 100px;
+ }
+ }
+
+ .wp-block-template-part__placeholder-preview-item-title {
+ padding: $grid-unit-05;
+ font-size: 12px;
+ text-align: center;
+ }
+
+ .wp-block-template-part__placeholder-panel-group-header {
+ display: inline-flex;
+ align-items: center;
+ padding: $grid-unit-20 $grid-unit-20 0;
+ }
+
+ .wp-block-template-part__placeholder-panel-group-content {
+ padding: 0 $grid-unit-20;
+ }
+
+ .wp-block-template-part__placeholder-panel-group-title {
+ color: $theme-color;
+ text-transform: uppercase;
+ font-size: 11px;
+ font-weight: 500;
+ margin-right: $grid-unit-10;
+ }
}
diff --git a/packages/compose/README.md b/packages/compose/README.md
index d8cc4678b1d5e1..bbee55eb75da4d 100644
--- a/packages/compose/README.md
+++ b/packages/compose/README.md
@@ -119,6 +119,19 @@ _Returns_
- `WPComponent`: Component class with generated display name assigned.
+# **useAsyncList**
+
+React hook returns an array which items get asynchronously appended from a source array.
+This behavior is useful if we want to render a list of items asynchronously for performance reasons.
+
+_Parameters_
+
+- _list_ `Array`: Source array.
+
+_Returns_
+
+- `Array`: Async array.
+
# **useCopyOnClick**
Copies the text to the clipboard when the element is clicked.
diff --git a/packages/compose/package.json b/packages/compose/package.json
index 1c2dc23f0acfda..e3606ef74435f7 100644
--- a/packages/compose/package.json
+++ b/packages/compose/package.json
@@ -26,6 +26,7 @@
"@babel/runtime": "^7.9.2",
"@wordpress/element": "file:../element",
"@wordpress/is-shallow-equal": "file:../is-shallow-equal",
+ "@wordpress/priority-queue": "file:../priority-queue",
"clipboard": "^2.0.1",
"lodash": "^4.17.15",
"mousetrap": "^1.6.5",
diff --git a/packages/block-editor/src/components/inserter/use-async-list.js b/packages/compose/src/hooks/use-async-list/index.js
similarity index 100%
rename from packages/block-editor/src/components/inserter/use-async-list.js
rename to packages/compose/src/hooks/use-async-list/index.js
diff --git a/packages/compose/src/index.js b/packages/compose/src/index.js
index 7acd1620ee8805..9c790f06227e8a 100644
--- a/packages/compose/src/index.js
+++ b/packages/compose/src/index.js
@@ -22,3 +22,4 @@ export { default as usePrevious } from './hooks/use-previous';
export { default as useReducedMotion } from './hooks/use-reduced-motion';
export { default as useViewportMatch } from './hooks/use-viewport-match';
export { default as useResizeObserver } from './hooks/use-resize-observer';
+export { default as useAsyncList } from './hooks/use-async-list';
diff --git a/packages/compose/src/index.native.js b/packages/compose/src/index.native.js
index d1129d4836e3dd..eb3181954b0252 100644
--- a/packages/compose/src/index.native.js
+++ b/packages/compose/src/index.native.js
@@ -19,6 +19,7 @@ export { default as useKeyboardShortcut } from './hooks/use-keyboard-shortcut';
export { default as useMediaQuery } from './hooks/use-media-query';
export { default as useReducedMotion } from './hooks/use-reduced-motion';
export { default as useViewportMatch } from './hooks/use-viewport-match';
+export { default as useAsyncList } from './hooks/use-async-list';
// Higher-order components
export { default as withPreferredColorScheme } from './higher-order/with-preferred-color-scheme';
diff --git a/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js b/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js
index d4dcfbd3eca8a9..11de0b508a7611 100644
--- a/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js
+++ b/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js
@@ -54,6 +54,11 @@ const createTemplatePart = async (
) => {
// Create new template part.
await insertBlock( 'Template Part' );
+ const [ createNewButton ] = await page.$x(
+ '//button[contains(text(), "Create new")]'
+ );
+ await createNewButton.click();
+ await page.keyboard.press( 'Tab' );
await page.keyboard.type( templatePartName );
await page.keyboard.press( 'Tab' );
await page.keyboard.type( themeName );
diff --git a/packages/e2e-tests/specs/experiments/multi-entity-saving.test.js b/packages/e2e-tests/specs/experiments/multi-entity-saving.test.js
index ffe457d7bad88d..5dd1c1f14d83c8 100644
--- a/packages/e2e-tests/specs/experiments/multi-entity-saving.test.js
+++ b/packages/e2e-tests/specs/experiments/multi-entity-saving.test.js
@@ -24,6 +24,7 @@ describe( 'Multi-entity save flow', () => {
const activatedTemplatePartSelector = `${ templatePartSelector } .block-editor-inner-blocks`;
const savePanelSelector = '.entities-saved-states__panel';
const closePanelButtonSelector = 'button[aria-label="Close panel"]';
+ const createNewButtonSelector = '//button[contains(text(), "Create new")]';
// Reusable assertions across Post/Site editors.
const assertAllBoxesChecked = async () => {
@@ -108,6 +109,11 @@ describe( 'Multi-entity save flow', () => {
it( 'Should trigger multi-entity save button once template part edited', async () => {
// Create new template part.
await insertBlock( 'Template Part' );
+ const [ createNewButton ] = await page.$x(
+ createNewButtonSelector
+ );
+ await createNewButton.click();
+ await page.keyboard.press( 'Tab' );
await page.keyboard.type( 'test-template-part' );
await page.keyboard.press( 'Tab' );
await page.keyboard.type( 'test-theme' );
diff --git a/packages/e2e-tests/specs/experiments/template-part.test.js b/packages/e2e-tests/specs/experiments/template-part.test.js
index f7b1b480d5605b..827585089e356f 100644
--- a/packages/e2e-tests/specs/experiments/template-part.test.js
+++ b/packages/e2e-tests/specs/experiments/template-part.test.js
@@ -85,72 +85,80 @@ describe( 'Template Part', () => {
const testContent = 'some words...';
// Selectors
- const chooseButtonSelector =
- '//div[contains(@class,"is-selected")]//button[text()="Choose"]';
const entitiesSaveSelector =
'.editor-entities-saved-states__save-button';
const savePostSelector = '.editor-post-publish-button__button';
const templatePartSelector = '*[data-type="core/template-part"]';
const activatedTemplatePartSelector = `${ templatePartSelector } .block-editor-inner-blocks`;
- const templatePartButtonSelector = `${ templatePartSelector } button`;
const testContentSelector = `//p[contains(., "${ testContent }")]`;
+ const createNewButtonSelector =
+ '//button[contains(text(), "Create new")]';
+ const disabledButtonSelector =
+ '.wp-block-template-part__placeholder-create-button[disabled]';
- it( 'Should prompt to create when no match found', async () => {
+ it( 'Should insert new template part on creation', async () => {
await createNewPost();
await disablePrePublishChecks();
// Create new template part.
await insertBlock( 'Template Part' );
+ const [ createNewButton ] = await page.$x(
+ createNewButtonSelector
+ );
+ await createNewButton.click();
+ await page.keyboard.press( 'Tab' );
await page.keyboard.type( testSlug );
await page.keyboard.press( 'Tab' );
await page.keyboard.type( testTheme );
- // Should say 'Create'
- const placeholderButton = await page.$(
- templatePartButtonSelector
- );
- const text = await page.evaluate(
- ( element ) => element.textContent,
- placeholderButton
+ await page.keyboard.press( 'Tab' );
+ await page.keyboard.press( 'Enter' );
+
+ const newTemplatePart = await page.waitForSelector(
+ activatedTemplatePartSelector
);
- expect( text ).toBe( 'Create' );
+ expect( newTemplatePart ).toBeTruthy();
// Finish creating template part, insert some text, and save.
- await page.keyboard.press( 'Tab' );
- await page.keyboard.press( 'Enter' );
- await page.waitForSelector( activatedTemplatePartSelector );
await page.click( templatePartSelector );
await page.keyboard.type( testContent );
await page.click( savePostSelector );
await page.click( entitiesSaveSelector );
} );
- it( 'Should prompt to Choose when match found', async () => {
+ it( 'Should preview newly added template part', async () => {
await createNewPost();
- await disablePrePublishChecks();
// Try to insert the template part we created.
await insertBlock( 'Template Part' );
- await page.keyboard.type( testSlug );
- await page.keyboard.press( 'Tab' );
- await page.keyboard.type( testTheme );
- // Should say 'Choose'
- const placeholderButton = await page.waitForXPath(
- chooseButtonSelector
- );
- expect( placeholderButton ).not.toBeNull();
- } );
-
- it( 'Should dispaly a preview when match is found', async () => {
- const [ preview ] = await page.$x( testContentSelector );
+ const preview = await page.waitForXPath( testContentSelector );
expect( preview ).toBeTruthy();
} );
- it( 'Should insert the desired template part', async () => {
- const [ placeholderButton ] = await page.$x( chooseButtonSelector );
- await placeholderButton.click();
+ it( 'Should insert template part when preview is selected', async () => {
+ const [ preview ] = await page.$x( testContentSelector );
+ await preview.click();
await page.waitForSelector( activatedTemplatePartSelector );
const templatePartContent = await page.waitForXPath(
testContentSelector
);
expect( templatePartContent ).toBeTruthy();
} );
+
+ it( 'Should disable create button for slug/theme combo', async () => {
+ await createNewPost();
+ // Create new template part.
+ await insertBlock( 'Template Part' );
+ const [ createNewButton ] = await page.$x(
+ createNewButtonSelector
+ );
+ await createNewButton.click();
+ await page.keyboard.press( 'Tab' );
+ await page.keyboard.type( testSlug );
+ await page.keyboard.press( 'Tab' );
+ await page.keyboard.type( testTheme );
+
+ const disabledButton = await page.waitForSelector(
+ disabledButtonSelector
+ );
+ expect( disabledButton ).toBeTruthy();
+ } );
} );
} );