From 4b36a86eaa49a8bf3005dbdb8d4ed06c23f2fdb4 Mon Sep 17 00:00:00 2001 From: apeatling Date: Mon, 23 Jan 2023 16:18:53 -0800 Subject: [PATCH 1/8] Move the pattern selection modal in query block to a separate panel. Switch the modal to use the BlockPaternsList component to match other pattern selection UI across the editor. --- .../block-library/src/query/edit/index.js | 63 ++--------------- .../src/query/edit/pattern-selection-modal.js | 69 +++++++++++++++++++ .../src/query/edit/query-toolbar.js | 17 ++--- packages/block-library/src/query/editor.scss | 36 +++++----- packages/block-library/src/query/utils.js | 19 +++++ 5 files changed, 115 insertions(+), 89 deletions(-) create mode 100644 packages/block-library/src/query/edit/pattern-selection-modal.js diff --git a/packages/block-library/src/query/edit/index.js b/packages/block-library/src/query/edit/index.js index 5f5dc7815e0072..9a54b5976fe4bc 100644 --- a/packages/block-library/src/query/edit/index.js +++ b/packages/block-library/src/query/edit/index.js @@ -1,25 +1,16 @@ /** * WordPress dependencies */ -import { useSelect, useDispatch } from '@wordpress/data'; -import { useState, useMemo } from '@wordpress/element'; -import { - BlockContextProvider, - store as blockEditorStore, - __experimentalBlockPatternSetup as BlockPatternSetup, -} from '@wordpress/block-editor'; -import { Modal } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; +import { useState } from '@wordpress/element'; +import { store as blockEditorStore } from '@wordpress/block-editor'; /** * Internal dependencies */ import QueryContent from './query-content'; import QueryPlaceholder from './query-placeholder'; -import { - useBlockNameForPatterns, - getTransformedBlocksFromPattern, -} from '../utils'; +import PatternSelectionModal from './pattern-selection-modal'; const QueryEdit = ( props ) => { const { clientId, attributes } = props; @@ -52,50 +43,4 @@ const QueryEdit = ( props ) => { ); }; -function PatternSelectionModal( { - clientId, - attributes, - setIsPatternSelectionModalOpen, -} ) { - const { replaceBlock, selectBlock } = useDispatch( blockEditorStore ); - const onBlockPatternSelect = ( blocks ) => { - const { newBlocks, queryClientIds } = getTransformedBlocksFromPattern( - blocks, - attributes - ); - replaceBlock( clientId, newBlocks ); - if ( queryClientIds[ 0 ] ) { - selectBlock( queryClientIds[ 0 ] ); - } - }; - // When we preview Query Loop blocks we should prefer the current - // block's postType, which is passed through block context. - const blockPreviewContext = useMemo( - () => ( { - previewPostType: attributes.query.postType, - } ), - [ attributes.query.postType ] - ); - const blockNameForPatterns = useBlockNameForPatterns( - clientId, - attributes - ); - return ( - setIsPatternSelectionModalOpen( false ) } - > - - - - - ); -} - export default QueryEdit; diff --git a/packages/block-library/src/query/edit/pattern-selection-modal.js b/packages/block-library/src/query/edit/pattern-selection-modal.js new file mode 100644 index 00000000000000..346be46c3725a6 --- /dev/null +++ b/packages/block-library/src/query/edit/pattern-selection-modal.js @@ -0,0 +1,69 @@ +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; +import { useDispatch } from '@wordpress/data'; +import { Modal, SearchControl } from '@wordpress/components'; +import { + store as blockEditorStore, + __experimentalBlockPatternsList as BlockPatternsList, +} from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { + useBlockNameForPatterns, + getTransformedBlocksFromPattern, + usePatterns, +} from '../utils'; + +export default function PatternSelectionModal( { + clientId, + attributes, + setIsPatternSelectionModalOpen, +} ) { + const [ searchValue, setSearchValue ] = useState( '' ); + const { replaceBlock, selectBlock } = useDispatch( blockEditorStore ); + const onBlockPatternSelect = ( blocks ) => { + const { newBlocks, queryClientIds } = getTransformedBlocksFromPattern( + blocks, + attributes + ); + replaceBlock( clientId, newBlocks ); + if ( queryClientIds[ 0 ] ) { + selectBlock( queryClientIds[ 0 ] ); + } + }; + const blockNameForPatterns = useBlockNameForPatterns( + clientId, + attributes + ); + const blockPatterns = usePatterns( clientId, blockNameForPatterns ); + + return ( + setIsPatternSelectionModalOpen( false ) } + > +
+
+ +
+ +
+
+ ); +} diff --git a/packages/block-library/src/query/edit/query-toolbar.js b/packages/block-library/src/query/edit/query-toolbar.js index 4be92281799148..1d079eb399fb80 100644 --- a/packages/block-library/src/query/edit/query-toolbar.js +++ b/packages/block-library/src/query/edit/query-toolbar.js @@ -11,8 +11,11 @@ import { import { useInstanceId } from '@wordpress/compose'; import { __ } from '@wordpress/i18n'; import { settings, list, grid } from '@wordpress/icons'; -import { useSelect } from '@wordpress/data'; -import { store as blockEditorStore } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { usePatterns } from '../utils'; export default function QueryToolbar( { attributes: { query, displayLayout }, @@ -22,15 +25,7 @@ export default function QueryToolbar( { name, clientId, } ) { - const hasPatterns = useSelect( - ( select ) => { - const { getBlockRootClientId, getPatternsByBlockTypes } = - select( blockEditorStore ); - const rootClientId = getBlockRootClientId( clientId ); - return !! getPatternsByBlockTypes( name, rootClientId ).length; - }, - [ name, clientId ] - ); + const hasPatterns = !! usePatterns( clientId, name ).length; const maxPageInputId = useInstanceId( QueryToolbar, 'blocks-query-pagination-max-page-input' diff --git a/packages/block-library/src/query/editor.scss b/packages/block-library/src/query/editor.scss index f6de35bc04f69b..7a3584af1098e2 100644 --- a/packages/block-library/src/query/editor.scss +++ b/packages/block-library/src/query/editor.scss @@ -19,28 +19,26 @@ } } -.block-editor-query-pattern__selection-modal .components-modal__content { - padding: 0; - margin-bottom: $header-height; // $header-height to accommodate bottom toolbar - &::before { - margin-bottom: 0; - } -} - -.block-editor-query-pattern__selection-modal { +.block-library-query-pattern__selection-modal { // To keep modal dimensions consistent as subsections are navigated, width // and height are used instead of max-(width/height). - @include break-small() { - width: calc(100% - #{ $grid-unit-20 * 2 }); - height: calc(100% - #{ $header-height * 2 }); - } - @include break-medium() { - width: $break-medium - $grid-unit-20 * 2; + .components-modal__frame { + @include break-small() { + width: calc(100% - #{$grid-unit-20 * 2}); + height: calc(100% - #{$header-height * 2}); + } + + @include break-medium() { + width: $break-medium - $grid-unit-20 * 2; + } + + @include break-large() { + height: 70%; + } } - @include break-large() { - height: 80%; - width: 80%; - max-height: none; + + .block-library-query-pattern__selection-search { + margin-bottom: $grid-unit-30; } } diff --git a/packages/block-library/src/query/utils.js b/packages/block-library/src/query/utils.js index 060efed9c6a38b..0a1704dd6db4db 100644 --- a/packages/block-library/src/query/utils.js +++ b/packages/block-library/src/query/utils.js @@ -313,3 +313,22 @@ export function useScopedBlockVariations( attributes ) { }, [ activeVariationName, blockVariations ] ); return variations; } + +/** + * Hook that returns the block patterns for a specific block type. + * + * @param {string} clientId The block's client ID. + * @param {string} name The block type name. + * @return {Object[]} An array of valid block patterns. + */ +export const usePatterns = ( clientId, name ) => { + return useSelect( + ( select ) => { + const { getBlockRootClientId, getPatternsByBlockTypes } = + select( blockEditorStore ); + const rootClientId = getBlockRootClientId( clientId ); + return getPatternsByBlockTypes( name, rootClientId ); + }, + [ name, clientId ] + ); +}; From ec05701c8ee4cf5fc301f6d3324284bf62404687 Mon Sep 17 00:00:00 2001 From: apeatling Date: Tue, 24 Jan 2023 10:32:06 -0800 Subject: [PATCH 2/8] Fix selection of a new pattern and insertion of blocks. --- .../block-library/src/query/edit/pattern-selection-modal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/query/edit/pattern-selection-modal.js b/packages/block-library/src/query/edit/pattern-selection-modal.js index 346be46c3725a6..859c9f4f6d7aa6 100644 --- a/packages/block-library/src/query/edit/pattern-selection-modal.js +++ b/packages/block-library/src/query/edit/pattern-selection-modal.js @@ -26,7 +26,7 @@ export default function PatternSelectionModal( { } ) { const [ searchValue, setSearchValue ] = useState( '' ); const { replaceBlock, selectBlock } = useDispatch( blockEditorStore ); - const onBlockPatternSelect = ( blocks ) => { + const onBlockPatternSelect = ( pattern, blocks ) => { const { newBlocks, queryClientIds } = getTransformedBlocksFromPattern( blocks, attributes From 6da02bb6e536772e7929727538a68952f1b38c75 Mon Sep 17 00:00:00 2001 From: apeatling Date: Tue, 24 Jan 2023 10:55:48 -0800 Subject: [PATCH 3/8] Move pattern search to shared utils directory. Hook up pattern search in query block view. --- .../src/query/edit/pattern-selection-modal.js | 12 +++++++++--- .../src/template-part/edit/selection-modal.js | 2 +- .../utils/search.js => utils/search-patterns.js} | 0 3 files changed, 10 insertions(+), 4 deletions(-) rename packages/block-library/src/{template-part/edit/utils/search.js => utils/search-patterns.js} (100%) diff --git a/packages/block-library/src/query/edit/pattern-selection-modal.js b/packages/block-library/src/query/edit/pattern-selection-modal.js index 859c9f4f6d7aa6..8084f785b7b28c 100644 --- a/packages/block-library/src/query/edit/pattern-selection-modal.js +++ b/packages/block-library/src/query/edit/pattern-selection-modal.js @@ -1,9 +1,10 @@ /** * WordPress dependencies */ -import { useState } from '@wordpress/element'; +import { useState, useMemo } from '@wordpress/element'; import { useDispatch } from '@wordpress/data'; import { Modal, SearchControl } from '@wordpress/components'; +import { useAsyncList } from '@wordpress/compose'; import { store as blockEditorStore, __experimentalBlockPatternsList as BlockPatternsList, @@ -18,6 +19,7 @@ import { getTransformedBlocksFromPattern, usePatterns, } from '../utils'; +import { searchPatterns } from '../../utils/search-patterns'; export default function PatternSelectionModal( { clientId, @@ -41,6 +43,10 @@ export default function PatternSelectionModal( { attributes ); const blockPatterns = usePatterns( clientId, blockNameForPatterns ); + const filteredBlockPatterns = useMemo( () => { + return searchPatterns( blockPatterns, searchValue ); + }, [ blockPatterns, searchValue ] ); + const shownBlockPatterns = useAsyncList( filteredBlockPatterns ); return ( diff --git a/packages/block-library/src/template-part/edit/selection-modal.js b/packages/block-library/src/template-part/edit/selection-modal.js index 9a83914ddb4241..7987d5720cdada 100644 --- a/packages/block-library/src/template-part/edit/selection-modal.js +++ b/packages/block-library/src/template-part/edit/selection-modal.js @@ -22,7 +22,7 @@ import { useCreateTemplatePartFromBlocks, } from './utils/hooks'; import { createTemplatePartId } from './utils/create-template-part-id'; -import { searchPatterns } from './utils/search'; +import { searchPatterns } from '../../utils/search-patterns'; export default function TemplatePartSelectionModal( { setAttributes, diff --git a/packages/block-library/src/template-part/edit/utils/search.js b/packages/block-library/src/utils/search-patterns.js similarity index 100% rename from packages/block-library/src/template-part/edit/utils/search.js rename to packages/block-library/src/utils/search-patterns.js From 3ffe01366f568339152291ef7bc30d4f4ff890b2 Mon Sep 17 00:00:00 2001 From: apeatling Date: Tue, 24 Jan 2023 12:40:51 -0800 Subject: [PATCH 4/8] Add unit tests for pattern search functionality. --- .../src/utils/search-patterns.js | 4 +- .../src/utils/test/search-patterns.js | 58 +++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 packages/block-library/src/utils/test/search-patterns.js diff --git a/packages/block-library/src/utils/search-patterns.js b/packages/block-library/src/utils/search-patterns.js index 46db0a5ad14a72..8b63d31fb378d6 100644 --- a/packages/block-library/src/utils/search-patterns.js +++ b/packages/block-library/src/utils/search-patterns.js @@ -10,7 +10,7 @@ import removeAccents from 'remove-accents'; * * @return {string} The normalized search input. */ -function normalizeSearchInput( input = '' ) { +export function normalizeSearchInput( input = '' ) { // Disregard diacritics. input = removeAccents( input ); @@ -27,7 +27,7 @@ function normalizeSearchInput( input = '' ) { * @param {string} searchValue Search term * @return {number} A pattern search rank */ -function getPatternSearchRank( pattern, searchValue ) { +export function getPatternSearchRank( pattern, searchValue ) { const normalizedSearchValue = normalizeSearchInput( searchValue ); const normalizedTitle = normalizeSearchInput( pattern.title ); diff --git a/packages/block-library/src/utils/test/search-patterns.js b/packages/block-library/src/utils/test/search-patterns.js new file mode 100644 index 00000000000000..5ba4dd35e302ca --- /dev/null +++ b/packages/block-library/src/utils/test/search-patterns.js @@ -0,0 +1,58 @@ +/** + * Internal dependencies + */ +import { + normalizeSearchInput, + getPatternSearchRank, + searchPatterns, +} from '../search-patterns'; + +describe( 'normalizeSearchInput', () => { + it( 'should remove accents', () => { + expect( normalizeSearchInput( 'café' ) ).toBe( 'cafe' ); + } ); + + it( 'should trim and lowercase', () => { + expect( normalizeSearchInput( ' Foo ' ) ).toBe( 'foo' ); + } ); +} ); + +describe( 'getPatternSearchRank', () => { + it( 'should give a high rank to exact matches', () => { + const pattern = { title: 'Foo' }; + expect( getPatternSearchRank( pattern, 'Foo' ) ).toBe( 30 ); + } ); + + it( 'should give a high rank to prefix matches', () => { + const pattern = { title: 'Foo' }; + expect( getPatternSearchRank( pattern, 'Fo' ) ).toBe( 20 ); + } ); + + it( 'should give a low rank to no matches', () => { + const pattern = { title: 'Foo' }; + expect( getPatternSearchRank( pattern, 'Bar' ) ).toBe( 0 ); + } ); +} ); + +describe( 'searchPatterns', () => { + it( 'should return all patterns if no search term is provided', () => { + const patterns = [ { title: 'Foo' }, { title: 'Bar' } ]; + expect( searchPatterns( patterns, '' ) ).toEqual( patterns ); + } ); + + it( 'should return an empty array if no patterns are provided', () => { + expect( searchPatterns( [], 'Foo' ) ).toEqual( [] ); + } ); + + it( 'should return an empty array if no patterns match', () => { + const patterns = [ { title: 'Foo' }, { title: 'Bar' } ]; + expect( searchPatterns( patterns, 'Baz' ) ).toEqual( [] ); + } ); + + it( 'should return the matching patterns', () => { + const patterns = [ { title: 'Foo' }, { title: 'Bar' } ]; + expect( searchPatterns( patterns, 'Foo' ) ).toEqual( [ + patterns[ 0 ], + ] ); + } ); +} ); From 2201b04546988f41f88b30627a39bdfc6de28cf4 Mon Sep 17 00:00:00 2001 From: apeatling Date: Wed, 25 Jan 2023 10:15:31 -0800 Subject: [PATCH 5/8] Retain block preview context. --- .../src/query/edit/pattern-selection-modal.js | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/packages/block-library/src/query/edit/pattern-selection-modal.js b/packages/block-library/src/query/edit/pattern-selection-modal.js index 8084f785b7b28c..693fba9cf271ed 100644 --- a/packages/block-library/src/query/edit/pattern-selection-modal.js +++ b/packages/block-library/src/query/edit/pattern-selection-modal.js @@ -6,6 +6,7 @@ import { useDispatch } from '@wordpress/data'; import { Modal, SearchControl } from '@wordpress/components'; import { useAsyncList } from '@wordpress/compose'; import { + BlockContextProvider, store as blockEditorStore, __experimentalBlockPatternsList as BlockPatternsList, } from '@wordpress/block-editor'; @@ -38,6 +39,14 @@ export default function PatternSelectionModal( { selectBlock( queryClientIds[ 0 ] ); } }; + // When we preview Query Loop blocks we should prefer the current + // block's postType, which is passed through block context. + const blockPreviewContext = useMemo( + () => ( { + previewPostType: attributes.query.postType, + } ), + [ attributes.query.postType ] + ); const blockNameForPatterns = useBlockNameForPatterns( clientId, attributes @@ -54,22 +63,24 @@ export default function PatternSelectionModal( { title={ __( 'Choose a pattern' ) } onRequestClose={ () => setIsPatternSelectionModalOpen( false ) } > -
-
- +
+
+ +
+
- -
+ ); } From 494be8e2842825b8681a656f785952fdd22e1e5b Mon Sep 17 00:00:00 2001 From: apeatling Date: Fri, 27 Jan 2023 10:10:05 -0800 Subject: [PATCH 6/8] Update e2e-test for query block pattern selection. --- .../specs/editor/blocks/query.test.js | 64 +++---------------- 1 file changed, 10 insertions(+), 54 deletions(-) diff --git a/packages/e2e-tests/specs/editor/blocks/query.test.js b/packages/e2e-tests/specs/editor/blocks/query.test.js index e051783ec7ebc0..d99918a2534621 100644 --- a/packages/e2e-tests/specs/editor/blocks/query.test.js +++ b/packages/e2e-tests/specs/editor/blocks/query.test.js @@ -31,7 +31,7 @@ describe( 'Query block', () => { await trashAllPosts( 'page' ); } ); describe( 'Query block insertion', () => { - it( 'Carousel', async () => { + it( 'List', async () => { await insertBlock( 'Query' ); // Wait for the choose pattern button const choosePatternButton = await page.waitForSelector( @@ -40,64 +40,20 @@ describe( 'Query block', () => { await choosePatternButton.click(); // Wait for pattern blocks to be loaded. await page.waitForSelector( - '.block-editor-block-pattern-setup__container iframe[title="Editor canvas"]' + '.block-library-query-pattern__selection-content iframe[title="Editor canvas"]' ); - /** - * Ensure that carousel is working by checking slider css classes - * and navigating to the next pattern. - */ - await page.waitForSelector( - 'li.pattern-slide.active-slide[aria-label="Query Test 1"]' - ); - const nextPatternButton = await page.waitForSelector( - '.block-editor-block-pattern-setup__navigation button[aria-label="Next pattern"]' - ); - await nextPatternButton.click(); - await page.waitForSelector( - 'li.pattern-slide.active-slide[aria-label="Query Test 2"]' + // Choose the standard pattern. + const chosenPattern = await page.waitForSelector( + '.block-editor-block-patterns-list__item[aria-label="Standard"]' ); - // Choose the selected pattern. - const chooseButton = await page.waitForXPath( - '//div[contains(@class, "block-editor-block-pattern-setup__actions")]//button[text()="Choose"]' - ); - chooseButton.click(); + chosenPattern.click(); // Wait for pattern setup to go away. - await page.waitForSelector( '.block-editor-block-pattern-setup', { - hidden: true, - } ); - /** - * We can't use `getEditedPostContent` easily for now because - * `query` makes used of `instanceId` so it's not very reliable. - * This should be revisited. - */ - await page.waitForSelector( '.wp-block-post-date' ); - await page.waitForSelector( '.wp-block-post-title' ); - } ); - it( 'Grid view', async () => { - await insertBlock( 'Query' ); - // Wait for the choose pattern button - const choosePatternButton = await page.waitForSelector( - 'div[data-type="core/query"] button.is-primary' - ); - await choosePatternButton.click(); - // Wait for patterns setup to be loaded. await page.waitForSelector( - '.block-editor-block-pattern-setup__display-controls' + '.block-library-query-pattern__selection-content', + { + hidden: true, + } ); - // Click the Grid view button. - const gridViewButton = await page.waitForSelector( - '.block-editor-block-pattern-setup__display-controls button[aria-label="Grid view"]' - ); - await gridViewButton.click(); - // Wait for patterns to be loaded and click the wanted pattern. - const gridPattern = await page.waitForSelector( - '.block-editor-block-pattern-setup-list__list-item[aria-label="Query Test 2"]' - ); - await gridPattern.click(); - // Wait for pattern setup to go away. - await page.waitForSelector( '.block-editor-block-pattern-setup', { - hidden: true, - } ); /** * We can't use `getEditedPostContent` easily for now because * `query` makes used of `instanceId` so it's not very reliable. From 34aa1f28d610beb63e7be410264bd54d28e5c1d4 Mon Sep 17 00:00:00 2001 From: apeatling Date: Mon, 30 Jan 2023 10:24:20 -0800 Subject: [PATCH 7/8] Make the search box sticky. --- packages/base-styles/_z-index.scss | 1 + packages/block-library/src/query/editor.scss | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 0e3cf596a24c50..fe1f3e12b95196 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -14,6 +14,7 @@ $z-layers: ( // These next three share a stacking context ".block-library-template-part__selection-search": 2, // higher sticky element + ".block-library-query-pattern__selection-search": 2, // higher sticky element // These next two share a stacking context ".interface-complementary-area .components-panel" : 0, // lower scrolling content diff --git a/packages/block-library/src/query/editor.scss b/packages/block-library/src/query/editor.scss index 7a3584af1098e2..b0f36eb9b49337 100644 --- a/packages/block-library/src/query/editor.scss +++ b/packages/block-library/src/query/editor.scss @@ -38,7 +38,11 @@ } .block-library-query-pattern__selection-search { - margin-bottom: $grid-unit-30; + background: $white; + position: sticky; + top: 0; + padding: $grid-unit-20 0; + z-index: z-index(".block-library-query-pattern__selection-search"); } } From d8098f8bea0a86ac480a135816375b9d089db0e0 Mon Sep 17 00:00:00 2001 From: apeatling Date: Mon, 30 Jan 2023 10:29:36 -0800 Subject: [PATCH 8/8] Change wrap location. --- .../src/query/edit/pattern-selection-modal.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/block-library/src/query/edit/pattern-selection-modal.js b/packages/block-library/src/query/edit/pattern-selection-modal.js index 693fba9cf271ed..731740ace64bed 100644 --- a/packages/block-library/src/query/edit/pattern-selection-modal.js +++ b/packages/block-library/src/query/edit/pattern-selection-modal.js @@ -63,24 +63,24 @@ export default function PatternSelectionModal( { title={ __( 'Choose a pattern' ) } onRequestClose={ () => setIsPatternSelectionModalOpen( false ) } > - -
-
- -
+
+
+ +
+ -
- + +
); }