Skip to content

Commit

Permalink
Patterns: Fix bug with pattern categories not saving sometimes (#54676)
Browse files Browse the repository at this point in the history

---------

Co-authored-by: Kai Hao <kai@kaihao.dev>
  • Loading branch information
glendaviesnz and kevin940726 authored Sep 21, 2023
1 parent ea24e06 commit fec52d0
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 54 deletions.
52 changes: 7 additions & 45 deletions packages/patterns/src/components/category-selector.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { __ } from '@wordpress/i18n';
import { useMemo, useState } from '@wordpress/element';
import { FormTokenField } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { useDebounce } from '@wordpress/compose';
import { decodeEntities } from '@wordpress/html-entities';
Expand All @@ -13,35 +13,26 @@ const unescapeString = ( arg ) => {
return decodeEntities( arg );
};

const unescapeTerm = ( term ) => {
return {
...term,
name: unescapeString( term.name ),
};
};

const EMPTY_ARRAY = [];
const MAX_TERMS_SUGGESTIONS = 20;
const DEFAULT_QUERY = {
per_page: MAX_TERMS_SUGGESTIONS,
_fields: 'id,name',
context: 'view',
};
const slug = 'wp_pattern_category';
export const CATEGORY_SLUG = 'wp_pattern_category';

export default function CategorySelector( { onCategorySelection } ) {
const [ values, setValues ] = useState( [] );
export default function CategorySelector( { values, onChange } ) {
const [ search, setSearch ] = useState( '' );
const debouncedSearch = useDebounce( setSearch, 500 );
const { invalidateResolution } = useDispatch( coreStore );

const { searchResults } = useSelect(
( select ) => {
const { getEntityRecords } = select( coreStore );

return {
searchResults: !! search
? getEntityRecords( 'taxonomy', slug, {
? getEntityRecords( 'taxonomy', CATEGORY_SLUG, {
...DEFAULT_QUERY,
search,
} )
Expand All @@ -57,28 +48,7 @@ export default function CategorySelector( { onCategorySelection } ) {
);
}, [ searchResults ] );

const { saveEntityRecord } = useDispatch( coreStore );

async function findOrCreateTerm( term ) {
try {
const newTerm = await saveEntityRecord( 'taxonomy', slug, term, {
throwOnError: true,
} );
invalidateResolution( 'getUserPatternCategories' );
return unescapeTerm( newTerm );
} catch ( error ) {
if ( error.code !== 'term_exists' ) {
throw error;
}

return {
id: error.data.term_id,
name: term.name,
};
}
}

function onChange( termNames ) {
function handleChange( termNames ) {
const uniqueTerms = termNames.reduce( ( terms, newTerm ) => {
if (
! terms.some(
Expand All @@ -90,15 +60,7 @@ export default function CategorySelector( { onCategorySelection } ) {
return terms;
}, [] );

setValues( uniqueTerms );

Promise.all(
uniqueTerms.map( ( termName ) =>
findOrCreateTerm( { name: termName } )
)
).then( ( newTerms ) => {
onCategorySelection( newTerms );
} );
onChange( uniqueTerms );
}

return (
Expand All @@ -107,7 +69,7 @@ export default function CategorySelector( { onCategorySelection } ) {
className="patterns-menu-items__convert-modal-categories"
value={ values }
suggestions={ suggestions }
onChange={ onChange }
onChange={ handleChange }
onInputChange={ debouncedSearch }
maxSuggestions={ MAX_TERMS_SUGGESTIONS }
label={ __( 'Categories' ) }
Expand Down
58 changes: 49 additions & 9 deletions packages/patterns/src/components/create-pattern-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';
import { store as noticesStore } from '@wordpress/notices';
import { store as coreStore } from '@wordpress/core-data';

/**
* Internal dependencies
Expand All @@ -23,7 +24,7 @@ import { PATTERN_DEFAULT_CATEGORY, PATTERN_SYNC_TYPES } from '../constants';
* Internal dependencies
*/
import { store as patternsStore } from '../store';
import CategorySelector from './category-selector';
import CategorySelector, { CATEGORY_SLUG } from './category-selector';
import { unlock } from '../lock-unlock';

export default function CreatePatternModal( {
Expand All @@ -34,13 +35,24 @@ export default function CreatePatternModal( {
className = 'patterns-menu-items__convert-modal',
} ) {
const [ syncType, setSyncType ] = useState( PATTERN_SYNC_TYPES.full );
const [ categories, setCategories ] = useState( [] );
const [ categoryTerms, setCategoryTerms ] = useState( [] );
const [ title, setTitle ] = useState( '' );
const [ isSaving, setIsSaving ] = useState( false );
const { createPattern } = unlock( useDispatch( patternsStore ) );

const { saveEntityRecord, invalidateResolution } = useDispatch( coreStore );
const { createErrorNotice } = useDispatch( noticesStore );

async function onCreate( patternTitle, sync ) {
if ( isSaving ) return;

try {
setIsSaving( true );
const categories = await Promise.all(
categoryTerms.map( ( termName ) =>
findOrCreateTerm( termName )
)
);

const newPattern = await createPattern(
patternTitle,
sync,
Expand All @@ -57,12 +69,35 @@ export default function CreatePatternModal( {
id: 'convert-to-pattern-error',
} );
onError();
} finally {
setIsSaving( false );
setCategoryTerms( [] );
setTitle( '' );
}
}

const handleCategorySelection = ( selectedCategories ) => {
setCategories( selectedCategories.map( ( cat ) => cat.id ) );
};
/**
* @param {string} term
* @return {Promise<number>} The pattern category id.
*/
async function findOrCreateTerm( term ) {
try {
const newTerm = await saveEntityRecord(
'taxonomy',
CATEGORY_SLUG,
{ name: term },
{ throwOnError: true }
);
invalidateResolution( 'getUserPatternCategories' );
return newTerm.id;
} catch ( error ) {
if ( error.code !== 'term_exists' ) {
throw error;
}

return error.data.term_id;
}
}

return (
<Modal
Expand All @@ -77,7 +112,6 @@ export default function CreatePatternModal( {
onSubmit={ ( event ) => {
event.preventDefault();
onCreate( title, syncType );
setTitle( '' );
} }
>
<VStack spacing="5">
Expand All @@ -90,7 +124,8 @@ export default function CreatePatternModal( {
className="patterns-create-modal__name-input"
/>
<CategorySelector
onCategorySelection={ handleCategorySelection }
values={ categoryTerms }
onChange={ setCategoryTerms }
/>
<ToggleControl
label={ __( 'Synced' ) }
Expand All @@ -117,7 +152,12 @@ export default function CreatePatternModal( {
{ __( 'Cancel' ) }
</Button>

<Button variant="primary" type="submit">
<Button
variant="primary"
type="submit"
aria-disabled={ isSaving }
isBusy={ isSaving }
>
{ __( 'Create' ) }
</Button>
</HStack>
Expand Down

1 comment on commit fec52d0

@github-actions
Copy link

Choose a reason for hiding this comment

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

Flaky tests detected in fec52d0.
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/6268313760
📝 Reported issues:

Please sign in to comment.