Skip to content

Commit

Permalink
[Product Block Editor] Remove additional create term modal (woocommer…
Browse files Browse the repository at this point in the history
…ce#39610)

* Remove additional create term modal

# Conflicts:
#	packages/js/product-editor/src/components/attribute-term-input-field/attribute-term-input-field.tsx

# Conflicts:
#	packages/js/product-editor/src/style.scss

* Add changelog

* Fix list refresh and add loading state

* Remove duplicated "invalidateResolutionForStoreSelector"

* Add modal and `autoCreateOnSelect`

* Update packages/js/product-editor/src/components/attribute-term-input-field/attribute-term-input-field.tsx

Co-authored-by: louwie17 <lourensschep@gmail.com>

* Add Attributes `invalidateResolution`

* Improve error handling

* Add term_exists error

* Fix lint errors

* Fix isOpen variable

* Fix lint

---------

Co-authored-by: louwie17 <lourensschep@gmail.com>
  • Loading branch information
octaedro and louwie17 authored Aug 10, 2023
1 parent 106e15f commit 91fadfd
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: dev

[Product Block Editor] Remove additional create attribute term modal
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
createElement,
Fragment,
} from '@wordpress/element';
import { recordEvent } from '@woocommerce/tracks';
import { useDebounce } from '@wordpress/compose';
import { plus } from '@wordpress/icons';
import {
Expand All @@ -24,11 +25,13 @@ import {
__experimentalSelectControlMenu as Menu,
__experimentalSelectControlMenuItem as MenuItem,
} from '@woocommerce/components';
import { cleanForSlug } from '@wordpress/url';

/**
* Internal dependencies
*/
import { CreateAttributeTermModal } from './create-attribute-term-modal';
import { TRACKS_SOURCE } from '../../constants';

type AttributeTermInputFieldProps = {
value?: ProductAttributeTerm[];
Expand All @@ -37,8 +40,14 @@ type AttributeTermInputFieldProps = {
placeholder?: string;
disabled?: boolean;
label?: string;
autoCreateOnSelect?: boolean;
};

interface customError extends Error {
code: string;
message: string;
}

let uniqueId = 0;

export const AttributeTermInputField: React.FC<
Expand All @@ -50,19 +59,21 @@ export const AttributeTermInputField: React.FC<
disabled,
attributeId,
label = '',
autoCreateOnSelect = true,
} ) => {
const { invalidateResolutionForStoreSelector } = useDispatch(
EXPERIMENTAL_PRODUCT_ATTRIBUTE_TERMS_STORE_NAME
);
const attributeTermInputId = useRef(
`woocommerce-attribute-term-field-${ ++uniqueId }`
);
const [ fetchedItems, setFetchedItems ] = useState<
ProductAttributeTerm[]
>( [] );
const [ isFetching, setIsFetching ] = useState( false );
const [ isCreatingTerm, setIsCreatingTerm ] = useState( false );
const [ addNewAttributeTermName, setAddNewAttributeTermName ] =
useState< string >();
const { createNotice } = useDispatch( 'core/notices' );
const { createProductAttributeTerm, invalidateResolutionForStoreSelector } =
useDispatch( EXPERIMENTAL_PRODUCT_ATTRIBUTE_TERMS_STORE_NAME );

const fetchItems = useCallback(
( searchString?: string | undefined ) => {
Expand Down Expand Up @@ -105,20 +116,6 @@ export const AttributeTermInputField: React.FC<
onChange( value.filter( ( opt ) => opt.slug !== item.slug ) );
};

const onSelect = ( item: ProductAttributeTerm ) => {
// Add new item.
if ( item.id === -99 ) {
setAddNewAttributeTermName( item.name );
return;
}
const isSelected = value.find( ( i ) => i.slug === item.slug );
if ( isSelected ) {
onRemove( item );
return;
}
onChange( [ ...value, item ] );
};

const focusSelectControl = () => {
const selectControlInputField: HTMLInputElement | null =
document.querySelector(
Expand All @@ -133,6 +130,78 @@ export const AttributeTermInputField: React.FC<
}
};

const createAttributeTerm = async (
attribute: Partial< ProductAttributeTerm >
) => {
recordEvent( 'product_attribute_term_add', {
source: TRACKS_SOURCE,
} );
setIsCreatingTerm( true );
try {
const newAttribute: ProductAttributeTerm =
await createProductAttributeTerm( {
...attribute,
attribute_id: attributeId,
} );
recordEvent( 'product_attribute_term_add_success', {
source: TRACKS_SOURCE,
} );
onChange( [ ...value, newAttribute ] );
invalidateResolutionForStoreSelector( 'getProductAttributes' );
invalidateResolutionForStoreSelector( 'getProductAttributeTerms' );
setIsCreatingTerm( false );
} catch ( err: unknown ) {
let error = {
source: TRACKS_SOURCE,
code: 'Unknown error.',
message: 'An unknown error occurred.',
};
let noticeMessage = __(
'Failed to create attribute term.',
'woocommerce'
);
const errorResponse = err as customError;
if ( errorResponse?.code && errorResponse?.message ) {
error = {
...error,
code: errorResponse.code,
message: errorResponse.message,
};
if ( errorResponse.code === 'term_exists' ) {
noticeMessage = __(
'Attribute term already exists.',
'woocommerce'
);
}
}
recordEvent( 'product_attribute_term_add_failed', error );
createNotice( 'error', noticeMessage );
setIsCreatingTerm( false );
}
};

const onSelect = ( item: ProductAttributeTerm ) => {
// Add new item.
if ( item.id === -99 ) {
if ( autoCreateOnSelect ) {
createAttributeTerm( {
name: item.name,
slug: cleanForSlug( item.name ),
} );
focusSelectControl();
} else {
setAddNewAttributeTermName( item.name );
}
return;
}
const isSelected = value.find( ( i ) => i.slug === item.slug );
if ( isSelected ) {
onRemove( item );
return;
}
onChange( [ ...value, item ] );
};

const selectedTermSlugs = ( value || [] ).map( ( term ) => term.slug );

return (
Expand Down Expand Up @@ -169,8 +238,12 @@ export const AttributeTermInputField: React.FC<
const { changes, type } = actionAndChanges;
switch ( type ) {
case selectControlStateChangeTypes.ControlledPropUpdatedSelectedItem:
const listIsOpen = isCreatingTerm
? { isOpen: isCreatingTerm }
: {};
return {
...changes,
...listIsOpen,
inputValue: state.inputValue,
};
case selectControlStateChangeTypes.ItemClick:
Expand Down Expand Up @@ -209,7 +282,7 @@ export const AttributeTermInputField: React.FC<
return (
<Menu isOpen={ isOpen } getMenuProps={ getMenuProps }>
{ [
isFetching ? (
isFetching || isCreatingTerm ? (
<div
key="loading-spinner"
className="woocommerce-attribute-term-field__loading-spinner"
Expand Down Expand Up @@ -278,24 +351,26 @@ export const AttributeTermInputField: React.FC<
);
} }
</SelectControl>
{ addNewAttributeTermName && attributeId !== undefined && (
<CreateAttributeTermModal
initialAttributeTermName={ addNewAttributeTermName }
onCancel={ () => {
setAddNewAttributeTermName( undefined );
focusSelectControl();
} }
attributeId={ attributeId }
onCreated={ ( newAttribute ) => {
onSelect( newAttribute );
setAddNewAttributeTermName( undefined );
invalidateResolutionForStoreSelector(
'getProductAttributeTerms'
);
focusSelectControl();
} }
/>
) }
{ ! autoCreateOnSelect &&
addNewAttributeTermName &&
attributeId !== undefined && (
<CreateAttributeTermModal
initialAttributeTermName={ addNewAttributeTermName }
onCancel={ () => {
setAddNewAttributeTermName( undefined );
focusSelectControl();
} }
attributeId={ attributeId }
onCreated={ ( newAttribute ) => {
onSelect( newAttribute );
setAddNewAttributeTermName( undefined );
invalidateResolutionForStoreSelector(
'getProductAttributeTerms'
);
focusSelectControl();
} }
/>
) }
</>
);
};
1 change: 0 additions & 1 deletion packages/js/product-editor/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
@import 'components/attribute-input-field/attribute-input-field.scss';
@import 'components/attribute-list-item/attribute-list-item.scss';
@import 'components/attribute-term-input-field/attribute-term-input-field.scss';
@import 'components/attribute-term-input-field/create-attribute-term-modal.scss';
@import 'components/variations-table/styles.scss';

/* Field Blocks */
Expand Down

0 comments on commit 91fadfd

Please sign in to comment.