Add an empty state to the variations table when there are no options added yet (#44163)
* Create variation empty state when no variable attributes are asigned to the product * Add changelog file * Remove the Add new button when a custom empty state is set * Conditionally invalidate resolution before requesting a new variations page
This commit is contained in:
parent
7e7b78f943
commit
26bcbb0417
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Create variation empty state when no variable attributes are asigned to the product
|
|
@ -22,11 +22,13 @@ import { useEntityId, useEntityProp } from '@wordpress/core-data';
|
|||
*/
|
||||
import { VariationsTable } from '../../../components/variations-table';
|
||||
import { useValidation } from '../../../contexts/validation-context';
|
||||
import useProductEntityProp from '../../../hooks/use-product-entity-prop';
|
||||
import { VariationOptionsBlockAttributes } from './types';
|
||||
import { VariableProductTour } from './variable-product-tour';
|
||||
import { TRACKS_SOURCE } from '../../../constants';
|
||||
import { handlePrompt } from '../../../utils/handle-prompt';
|
||||
import { ProductEditorBlockEditProps } from '../../../types';
|
||||
import { EmptyState } from './empty-state';
|
||||
|
||||
export function Edit( {
|
||||
attributes,
|
||||
|
@ -47,6 +49,17 @@ export function Edit( {
|
|||
'product',
|
||||
'status'
|
||||
);
|
||||
const [ productAttributes ] =
|
||||
useProductEntityProp< Product[ 'attributes' ] >( 'attributes' );
|
||||
|
||||
const hasVariationOptions = useMemo(
|
||||
function hasAttributesUsedForVariations() {
|
||||
return productAttributes?.some(
|
||||
( productAttribute ) => productAttribute.variation
|
||||
);
|
||||
},
|
||||
[ productAttributes ]
|
||||
);
|
||||
|
||||
const totalCountWithoutPriceRequestParams = useMemo(
|
||||
() => ( {
|
||||
|
@ -162,6 +175,10 @@ export function Edit( {
|
|||
)
|
||||
: '';
|
||||
|
||||
if ( ! hasVariationOptions ) {
|
||||
return <EmptyState />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<VariationsTable
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
@import "empty-state/style.scss";
|
||||
|
||||
.wp-block-woocommerce-product-variation-items-field {
|
||||
@media ( min-width: #{ ($break-medium) } ) {
|
||||
min-height: 420px;
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement } from '@wordpress/element';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
||||
export function EmptyState(
|
||||
props: React.DetailedHTMLProps<
|
||||
React.HTMLAttributes< HTMLDivElement >,
|
||||
HTMLDivElement
|
||||
>
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
{ ...props }
|
||||
role="none"
|
||||
className="wp-block-woocommerce-product-variation-items-field__empty-state"
|
||||
>
|
||||
<div className="wp-block-woocommerce-product-variation-items-field__empty-state-row">
|
||||
<div>{ __( 'Variation', 'woocommerce' ) }</div>
|
||||
<div>
|
||||
<div className="wp-block-woocommerce-product-variation-items-field__empty-state-name" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="wp-block-woocommerce-product-variation-items-field__empty-state-actions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="wp-block-woocommerce-product-variation-items-field__empty-state-row">
|
||||
<div>{ __( 'Colors', 'woocommerce' ) }</div>
|
||||
<div>
|
||||
<div className="wp-block-woocommerce-product-variation-items-field__empty-state-name" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="wp-block-woocommerce-product-variation-items-field__empty-state-actions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="wp-block-woocommerce-product-variation-items-field__empty-state-row">
|
||||
<div>{ __( 'Sizes', 'woocommerce' ) }</div>
|
||||
<div>
|
||||
<div className="wp-block-woocommerce-product-variation-items-field__empty-state-name" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="wp-block-woocommerce-product-variation-items-field__empty-state-actions" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './empty-state';
|
|
@ -0,0 +1,59 @@
|
|||
.wp-block-woocommerce-product-variation-items-field__empty-state {
|
||||
@mixin skeleton {
|
||||
@include placeholder();
|
||||
background-color: $gray-200;
|
||||
border-radius: $grid-unit-05;
|
||||
width: $grid-unit-30;
|
||||
height: $grid-unit;
|
||||
}
|
||||
|
||||
border: 1px dashed $gray-400;
|
||||
padding: 0 $grid-unit-30;
|
||||
border-radius: 2px;
|
||||
|
||||
&-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1.5fr 1fr 0.5fr;
|
||||
height: $grid-unit-80;
|
||||
align-items: center;
|
||||
border-top: 1px solid $gray-100;
|
||||
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
|
||||
.wp-block-woocommerce-product-variation-items-field__empty-state-name {
|
||||
width: 140px;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
opacity: 0.7;
|
||||
|
||||
.wp-block-woocommerce-product-variation-items-field__empty-state-name {
|
||||
width: 75px;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
opacity: 0.5;
|
||||
|
||||
.wp-block-woocommerce-product-variation-items-field__empty-state-name {
|
||||
width: 114px;
|
||||
}
|
||||
}
|
||||
|
||||
:last-child {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
&-name {
|
||||
@include skeleton();
|
||||
}
|
||||
|
||||
&-actions {
|
||||
@include skeleton();
|
||||
width: $grid-unit-60;
|
||||
}
|
||||
}
|
|
@ -53,7 +53,7 @@ export const AttributeControl: React.FC< AttributeControlProps > = ( {
|
|||
onRemove = () => {},
|
||||
onRemoveCancel = () => {},
|
||||
onNoticeDismiss = () => {},
|
||||
renderCustomEmptyState = () => <AttributeEmptyStateSkeleton />,
|
||||
renderCustomEmptyState,
|
||||
uiStrings,
|
||||
createNewAttributesAsGlobal = false,
|
||||
useRemoveConfirmationModal = false,
|
||||
|
@ -203,19 +203,43 @@ export const AttributeControl: React.FC< AttributeControlProps > = ( {
|
|||
|
||||
const isMobileViewport = useViewportMatch( 'medium', '<' );
|
||||
|
||||
function renderEmptyState() {
|
||||
if ( isMobileViewport || value.length ) return null;
|
||||
|
||||
if ( renderCustomEmptyState ) {
|
||||
return renderCustomEmptyState( {
|
||||
addAttribute( search ) {
|
||||
setDefaultAttributeSearch( search );
|
||||
openNewModal();
|
||||
},
|
||||
} );
|
||||
}
|
||||
|
||||
return <AttributeEmptyStateSkeleton />;
|
||||
}
|
||||
|
||||
function renderSectionActions() {
|
||||
if ( renderCustomEmptyState && value.length === 0 ) return null;
|
||||
|
||||
return (
|
||||
<SectionActions>
|
||||
{ uiStrings?.newAttributeListItemLabel && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="woocommerce-add-attribute-list-item__add-button"
|
||||
onClick={ openNewModal }
|
||||
>
|
||||
{ uiStrings.newAttributeListItemLabel }
|
||||
</Button>
|
||||
) }
|
||||
</SectionActions>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="woocommerce-attribute-field">
|
||||
<SectionActions>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="woocommerce-add-attribute-list-item__add-button"
|
||||
onClick={ () => {
|
||||
openNewModal();
|
||||
} }
|
||||
>
|
||||
{ uiStrings.newAttributeListItemLabel }
|
||||
</Button>
|
||||
</SectionActions>
|
||||
{ renderSectionActions() }
|
||||
|
||||
{ uiStrings.notice && (
|
||||
<Notice
|
||||
isDismissible={ true }
|
||||
|
@ -345,14 +369,7 @@ export const AttributeControl: React.FC< AttributeControlProps > = ( {
|
|||
} }
|
||||
/>
|
||||
) }
|
||||
{ ! isMobileViewport &&
|
||||
value.length === 0 &&
|
||||
renderCustomEmptyState( {
|
||||
addAttribute( search ) {
|
||||
setDefaultAttributeSearch( search );
|
||||
openNewModal();
|
||||
},
|
||||
} ) }
|
||||
{ renderEmptyState() }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -37,7 +37,10 @@ export function useVariations( { productId }: UseVariationsProps ) {
|
|||
const [ filters, setFilters ] = useState< AttributeFilters[] >( [] );
|
||||
const perPageRef = useRef( DEFAULT_VARIATION_PER_PAGE_OPTION );
|
||||
|
||||
async function getCurrentVariationsPage( params: GetVariationsRequest ) {
|
||||
async function getCurrentVariationsPage(
|
||||
params: GetVariationsRequest,
|
||||
invalidateResolutionBeforeRequest = false
|
||||
) {
|
||||
const requestParams: GetVariationsRequest = {
|
||||
page: 1,
|
||||
per_page: perPageRef.current,
|
||||
|
@ -48,6 +51,19 @@ export function useVariations( { productId }: UseVariationsProps ) {
|
|||
};
|
||||
|
||||
try {
|
||||
const { invalidateResolution } = dispatch(
|
||||
EXPERIMENTAL_PRODUCT_VARIATIONS_STORE_NAME
|
||||
);
|
||||
|
||||
if ( invalidateResolutionBeforeRequest ) {
|
||||
await invalidateResolution( 'getProductVariations', [
|
||||
requestParams,
|
||||
] );
|
||||
await invalidateResolution( 'getProductVariationsTotalCount', [
|
||||
requestParams,
|
||||
] );
|
||||
}
|
||||
|
||||
const { getProductVariations, getProductVariationsTotalCount } =
|
||||
resolveSelect( EXPERIMENTAL_PRODUCT_VARIATIONS_STORE_NAME );
|
||||
|
||||
|
@ -455,29 +471,28 @@ export function useVariations( { productId }: UseVariationsProps ) {
|
|||
|
||||
const wasGenerating = useRef( false );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! isGenerating ) {
|
||||
getCurrentVariationsPage( { product_id: productId } );
|
||||
}
|
||||
}, [ productId, isGenerating ] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( isGenerating ) {
|
||||
clearFilters();
|
||||
onClearSelection();
|
||||
}
|
||||
|
||||
const didMount =
|
||||
wasGenerating.current === false && isGenerating === false;
|
||||
const didGenerate =
|
||||
wasGenerating.current === true && isGenerating === false;
|
||||
|
||||
if ( didGenerate ) {
|
||||
getCurrentVariationsPage( {
|
||||
product_id: productId,
|
||||
} );
|
||||
if ( didMount || didGenerate ) {
|
||||
getCurrentVariationsPage(
|
||||
{
|
||||
product_id: productId,
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
wasGenerating.current = Boolean( isGenerating );
|
||||
}, [ isGenerating ] );
|
||||
}, [ productId, isGenerating ] );
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
|
|
Loading…
Reference in New Issue