Skip to content

Commit

Permalink
[Block Library - Query Loop]: Fetch terms suggestions dynamically
Browse files Browse the repository at this point in the history
  • Loading branch information
ntsekouras committed Dec 19, 2022
1 parent a556b69 commit 89e573a
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 49 deletions.
7 changes: 0 additions & 7 deletions packages/block-library/src/query/constants.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@
import { FormTokenField } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { useState, useEffect } from '@wordpress/element';
import { useDebounce } from '@wordpress/compose';

/**
* Internal dependencies
*/
import { useTaxonomies } from '../../utils';
import { MAX_FETCHED_TERMS } from '../../constants';

const EMPTY_ARRAY = [];
const BASE_QUERY = {
order: 'asc',
_fields: 'id,name',
context: 'view',
};

// Helper function to get the term id based on user input in terms `FormTokenField`.
const getTermIdByTermValue = ( terms, termValue ) => {
Expand All @@ -35,20 +43,6 @@ const getTermIdByTermValue = ( terms, termValue ) => {
)?.id;
};

const useTaxonomyTerms = ( slug ) => {
return useSelect(
( select ) => {
const terms = select( coreStore ).getEntityRecords(
'taxonomy',
slug,
{ context: 'view', per_page: MAX_FETCHED_TERMS }
);
return { terms };
},
[ slug ]
);
};

export function TaxonomyControls( { onChange, query } ) {
const { postType, taxQuery } = query;

Expand All @@ -73,49 +67,105 @@ export function TaxonomyControls( { onChange, query } ) {
<TaxonomyItem
key={ taxonomy.slug }
taxonomy={ taxonomy }
value={ value }
terms={ value }
onChange={ handleChange }
/>
);
} ) }
</>
);
}
function TaxonomyItem( { taxonomy, value, onChange } ) {
const { terms } = useTaxonomyTerms( taxonomy.slug );
if ( ! terms?.length ) {
return null;
}

function TaxonomyItem( { taxonomy, terms, onChange } ) {
const [ search, setSearch ] = useState( '' );
const [ value, setValue ] = useState( EMPTY_ARRAY );
const [ suggestions, setSuggestions ] = useState( EMPTY_ARRAY );
const debouncedSearch = useDebounce( setSearch, 250 );
const { searchResults, searchHasResolved } = useSelect(
( select ) => {
if ( ! search ) {
return { searchResults: EMPTY_ARRAY, searchHasResolved: true };
}
const { getEntityRecords, hasFinishedResolution } =
select( coreStore );
const selectorArgs = [
'taxonomy',
taxonomy.slug,
{
...BASE_QUERY,
search,
orderby: 'name',
exclude: terms,
per_page: 20,
},
];
return {
searchResults: getEntityRecords( ...selectorArgs ),
searchHasResolved: hasFinishedResolution(
'getEntityRecords',
selectorArgs
),
};
},
[ search, terms ]
);
const currentTerms = useSelect(
( select ) => {
if ( ! terms?.length ) return EMPTY_ARRAY;
const { getEntityRecords } = select( coreStore );
return getEntityRecords( 'taxonomy', taxonomy.slug, {
...BASE_QUERY,
include: terms,
per_page: terms.length,
} );
},
[ terms ]
);
// Update the `value` state only after the selectors are resolved
// to avoid emptying the input when we're changing terms.
useEffect( () => {
if ( ! terms?.length ) {
setValue( EMPTY_ARRAY );
}
if ( ! currentTerms?.length ) return;
// Returns only the existing entity ids. This prevents the component
// from crashing in the editor, when non existing ids are provided.
const sanitizedValue = terms.reduce( ( accumulator, id ) => {
const entity = currentTerms.find( ( term ) => term.id === id );
if ( entity ) {
accumulator.push( {
id,
value: entity.name,
} );
}
return accumulator;
}, [] );
setValue( sanitizedValue );
}, [ terms, currentTerms ] );
// Update suggestions only when the query has resolved.
useEffect( () => {
if ( ! searchHasResolved ) return;
setSuggestions( searchResults.map( ( result ) => result.name ) );
}, [ searchResults, searchHasResolved ] );
const onTermsChange = ( newTermValues ) => {
const termIds = new Set();
for ( const termValue of newTermValues ) {
const termId = getTermIdByTermValue( terms, termValue );
const termId = getTermIdByTermValue( searchResults, termValue );
if ( termId ) {
termIds.add( termId );
}
}

setSuggestions( EMPTY_ARRAY );
onChange( Array.from( termIds ) );
};

// Selects only the existing term ids in proper format to be
// used in `FormTokenField`. This prevents the component from
// crashing in the editor, when non existing term ids were provided.
const taxQueryValue = value
.map( ( termId ) => terms.find( ( t ) => t.id === termId ) )
.filter( Boolean )
.map( ( term ) => ( { id: term.id, value: term.name } ) );

return (
<div className="block-library-query-inspector__taxonomy-control">
<FormTokenField
label={ taxonomy.name }
value={ taxQueryValue }
suggestions={ terms.map( ( t ) => t.name ) }
onChange={ onTermsChange }
__experimentalShowHowTo={ false }
/>
</div>
<FormTokenField
label={ taxonomy.name }
value={ value }
onInputChange={ debouncedSearch }
suggestions={ suggestions }
onChange={ onTermsChange }
__experimentalShowHowTo={ false }
/>
);
}
3 changes: 2 additions & 1 deletion packages/block-library/src/query/edit/query-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import { __ } from '@wordpress/i18n';
*/
import QueryToolbar from './query-toolbar';
import QueryInspectorControls from './inspector-controls';
import { DEFAULTS_POSTS_PER_PAGE } from '../constants';

const DEFAULTS_POSTS_PER_PAGE = 3;

const TEMPLATE = [ [ 'core/post-template' ] ];
export default function QueryContent( {
Expand Down

1 comment on commit 89e573a

@github-actions
Copy link

@github-actions github-actions bot commented on 89e573a Dec 19, 2022

Choose a reason for hiding this comment

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

Flaky tests detected.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/3731218714
📝 Reported issues:

Please sign in to comment.