Product image getting removed in the new product editor after replacing cover image (#50181)

* Fix image gallery state conflict with external consumer state

* Fix the image gallery removal conflict now that the image gallery is stateless

* Add changelog files

* Ignore the media if it is replaced by itseft
This commit is contained in:
Maikel Perez 2024-08-02 08:29:28 -04:00 committed by GitHub
parent 8849f54491
commit 67ab34aab2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 99 additions and 136 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: enhancement
Fix image gallery state conflict with external consumer state

View File

@ -1,13 +1,14 @@
/** /**
* External dependencies * External dependencies
*/ */
import type { DragEventHandler } from 'react';
import { import {
Children,
createElement, createElement,
cloneElement, cloneElement,
useState, useState,
useEffect, useMemo,
} from '@wordpress/element'; } from '@wordpress/element';
import { DragEventHandler } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { MediaItem, MediaUpload } from '@wordpress/media-utils'; import { MediaItem, MediaUpload } from '@wordpress/media-utils';
@ -15,10 +16,9 @@ import { MediaItem, MediaUpload } from '@wordpress/media-utils';
* Internal dependencies * Internal dependencies
*/ */
import { moveIndex } from '../sortable'; import { moveIndex } from '../sortable';
import { ImageGalleryToolbar } from './index';
import { ImageGalleryChild, MediaUploadComponentType } from './types';
import { removeItem, replaceItem } from './utils';
import { ImageGalleryWrapper } from './image-gallery-wrapper'; import { ImageGalleryWrapper } from './image-gallery-wrapper';
import { ImageGalleryToolbar } from './index';
import type { ImageGalleryChild, MediaUploadComponentType } from './types';
export type ImageGalleryProps = { export type ImageGalleryProps = {
children: ImageGalleryChild | ImageGalleryChild[]; children: ImageGalleryChild | ImageGalleryChild[];
@ -57,26 +57,86 @@ export const ImageGallery: React.FC< ImageGalleryProps > = ( {
null null
); );
const [ isDragging, setIsDragging ] = useState< boolean >( false ); const [ isDragging, setIsDragging ] = useState< boolean >( false );
const [ orderedChildren, setOrderedChildren ] = useState< const childElements = useMemo(
ImageGalleryChild[] () => Children.toArray( children ) as JSX.Element[],
>( [] ); [ children ]
);
useEffect( () => { function cloneChild( child: JSX.Element, childIndex: number ) {
if ( ! children ) { const key = child.key || String( childIndex );
const isToolbarVisible = key === activeToolbarKey;
return cloneElement(
child,
{
key,
isDraggable: allowDragging && ! child.props.isCover,
className: classnames( {
'is-toolbar-visible': isToolbarVisible,
} ),
onClick() {
setActiveToolbarKey( isToolbarVisible ? null : key );
},
onBlur( event: React.FocusEvent< HTMLDivElement > ) {
if (
isDragging ||
event.currentTarget.contains( event.relatedTarget ) ||
( event.relatedTarget &&
( event.relatedTarget as Element ).closest(
'.media-modal, .components-modal__frame'
) ) ||
( event.relatedTarget &&
// Check if not a button within the toolbar is clicked, to prevent hiding the toolbar.
( event.relatedTarget as Element ).closest(
'.woocommerce-image-gallery__toolbar'
) ) ||
( event.relatedTarget &&
// Prevent toolbar from hiding if the dropdown is clicked within the toolbar.
( event.relatedTarget as Element ).closest(
'.woocommerce-image-gallery__toolbar-dropdown-popover'
) )
) {
return; return;
} }
setOrderedChildren( setActiveToolbarKey( null );
( Array.isArray( children ) ? children : [ children ] ).map( },
( child, index ) => },
cloneElement( child, { key: child.key || String( index ) } ) isToolbarVisible && (
<ImageGalleryToolbar
value={ child.props.id }
allowDragging={ allowDragging }
childIndex={ childIndex }
lastChild={ childIndex === childElements.length - 1 }
moveItem={ ( fromIndex: number, toIndex: number ) => {
onOrderChange(
moveIndex< ImageGalleryChild >(
fromIndex,
toIndex,
childElements
) )
); );
}, [ children ] ); } }
removeItem={ ( removeIndex: number ) => {
const updateOrderedChildren = ( items: ImageGalleryChild[] ) => { onRemove( {
setOrderedChildren( items ); removeIndex,
onOrderChange( items ); removedItem: childElements[ removeIndex ],
}; } );
} }
replaceItem={ (
replaceIndex: number,
media: { id: number } & MediaItem
) => {
onReplace( { replaceIndex, media } );
} }
setToolBarItem={ ( toolBarItem ) => {
onSelectAsCover( activeToolbarKey );
setActiveToolbarKey( toolBarItem );
} }
MediaUploadComponent={ MediaUploadComponent }
/>
)
);
}
return ( return (
<div <div
@ -87,7 +147,7 @@ export const ImageGallery: React.FC< ImageGalleryProps > = ( {
> >
<ImageGalleryWrapper <ImageGalleryWrapper
allowDragging={ allowDragging } allowDragging={ allowDragging }
updateOrderedChildren={ updateOrderedChildren } updateOrderedChildren={ onOrderChange }
onDragStart={ ( event ) => { onDragStart={ ( event ) => {
setIsDragging( true ); setIsDragging( true );
onDragStart( event ); onDragStart( event );
@ -98,114 +158,7 @@ export const ImageGallery: React.FC< ImageGalleryProps > = ( {
} } } }
onDragOver={ onDragOver } onDragOver={ onDragOver }
> >
{ orderedChildren.map( ( child, childIndex ) => { { childElements.map( cloneChild ) }
const isToolbarVisible = child.key === activeToolbarKey;
return cloneElement(
child,
{
isDraggable: allowDragging && ! child.props.isCover,
className: classnames( {
'is-toolbar-visible': isToolbarVisible,
} ),
onClick: () => {
setActiveToolbarKey(
isToolbarVisible
? null
: ( child.key as string )
);
},
onBlur: (
event: React.FocusEvent< HTMLDivElement >
) => {
if (
isDragging ||
event.currentTarget.contains(
event.relatedTarget
) ||
( event.relatedTarget &&
(
event.relatedTarget as Element
).closest(
'.media-modal, .components-modal__frame'
) ) ||
( event.relatedTarget &&
// Check if not a button within the toolbar is clicked, to prevent hiding the toolbar.
(
event.relatedTarget as Element
).closest(
'.woocommerce-image-gallery__toolbar'
) ) ||
( event.relatedTarget &&
// Prevent toolbar from hiding if the dropdown is clicked within the toolbar.
(
event.relatedTarget as Element
).closest(
'.woocommerce-image-gallery__toolbar-dropdown-popover'
) )
) {
return;
}
setActiveToolbarKey( null );
},
},
isToolbarVisible ? (
<ImageGalleryToolbar
value={ child.props.id }
allowDragging={ allowDragging }
childIndex={ childIndex }
lastChild={
childIndex === orderedChildren.length - 1
}
moveItem={ (
fromIndex: number,
toIndex: number
) => {
updateOrderedChildren(
moveIndex< ImageGalleryChild >(
fromIndex,
toIndex,
orderedChildren
)
);
} }
removeItem={ ( removeIndex: number ) => {
onRemove( {
removeIndex,
removedItem:
orderedChildren[ removeIndex ],
} );
updateOrderedChildren(
removeItem(
orderedChildren,
removeIndex
)
);
} }
replaceItem={ (
replaceIndex: number,
media: { id: number } & MediaItem
) => {
onReplace( { replaceIndex, media } );
setOrderedChildren(
replaceItem< {
src: string;
alt: string;
} >( orderedChildren, replaceIndex, {
src: media.url as string,
alt: media.alt as string,
} )
);
} }
setToolBarItem={ ( toolBarItem ) => {
onSelectAsCover( activeToolbarKey );
setActiveToolbarKey( toolBarItem );
} }
MediaUploadComponent={ MediaUploadComponent }
/>
) : null
);
} ) }
</ImageGalleryWrapper> </ImageGalleryWrapper>
</div> </div>
); );

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Fix the image gallery removal conflict now that the image gallery is stateless

View File

@ -162,10 +162,12 @@ export function ImageBlockEdit( {
} ) { } ) {
recordEvent( 'product_images_replace_image_button_click' ); recordEvent( 'product_images_replace_image_button_click' );
if ( if ( Array.isArray( propertyValue ) ) {
Array.isArray( propertyValue ) && // Ignore the media if it is replaced by itseft.
! propertyValue.some( ( img ) => media.id === img.id ) if ( propertyValue.some( ( img ) => media.id === img.id ) ) {
) { return;
}
const image = mapUploadImageToImage( media ); const image = mapUploadImageToImage( media );
if ( image ) { if ( image ) {
const newImages = [ ...propertyValue ]; const newImages = [ ...propertyValue ];
@ -182,7 +184,7 @@ export function ImageBlockEdit( {
if ( Array.isArray( propertyValue ) ) { if ( Array.isArray( propertyValue ) ) {
const remainingImages = propertyValue.filter( const remainingImages = propertyValue.filter(
( image ) => image.id === removedItem.props.id ( image ) => String( image.id ) !== removedItem.props.id
); );
setPropertyValue( remainingImages ); setPropertyValue( remainingImages );
} else { } else {