diff --git a/packages/js/components/changelog/add-39499 b/packages/js/components/changelog/add-39499 new file mode 100644 index 00000000000..886126605a4 --- /dev/null +++ b/packages/js/components/changelog/add-39499 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add Tooltip to each list item when need it diff --git a/packages/js/components/src/experimental-select-control/menu-item.tsx b/packages/js/components/src/experimental-select-control/menu-item.tsx index ef3dbbd465b..745ec825f70 100644 --- a/packages/js/components/src/experimental-select-control/menu-item.tsx +++ b/packages/js/components/src/experimental-select-control/menu-item.tsx @@ -1,6 +1,7 @@ /** * External dependencies */ +import { Tooltip } from '@wordpress/components'; import { createElement, CSSProperties, ReactElement } from 'react'; /** @@ -15,6 +16,7 @@ export type MenuItemProps< ItemType > = { children: ReactElement | string; getItemProps: getItemPropsType< ItemType >; activeStyle?: CSSProperties; + tooltipText?: string; }; export const MenuItem = < ItemType, >( { @@ -24,14 +26,27 @@ export const MenuItem = < ItemType, >( { isActive, activeStyle = { backgroundColor: '#bde4ff' }, item, + tooltipText, }: MenuItemProps< ItemType > ) => { - return ( -
  • - { children } -
  • - ); + function renderListItem() { + return ( +
  • + { children } +
  • + ); + } + + if ( tooltipText ) { + return ( + + { renderListItem() } + + ); + } + + return renderListItem(); }; diff --git a/packages/js/product-editor/changelog/add-39499 b/packages/js/product-editor/changelog/add-39499 new file mode 100644 index 00000000000..50c00ac8088 --- /dev/null +++ b/packages/js/product-editor/changelog/add-39499 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Disable attributes used in different sections diff --git a/packages/js/product-editor/src/blocks/variation-options/edit.tsx b/packages/js/product-editor/src/blocks/variation-options/edit.tsx index a1c63afcd27..481c0ea908f 100644 --- a/packages/js/product-editor/src/blocks/variation-options/edit.tsx +++ b/packages/js/product-editor/src/blocks/variation-options/edit.tsx @@ -139,6 +139,9 @@ export function Edit() { product_block_variable_options_notice_dismissed: 'yes', } ) } + disabledAttributeIds={ entityAttributes + .filter( ( attr ) => ! attr.variation ) + .map( ( attr ) => attr.id ) } uiStrings={ { notice, globalAttributeHelperMessage: '', diff --git a/packages/js/product-editor/src/blocks/variations/edit.tsx b/packages/js/product-editor/src/blocks/variations/edit.tsx index 5272e252af7..9e6ba9a42f3 100644 --- a/packages/js/product-editor/src/blocks/variations/edit.tsx +++ b/packages/js/product-editor/src/blocks/variations/edit.tsx @@ -158,6 +158,9 @@ export function Edit( { selectedAttributeIds={ variationOptions.map( ( attr ) => attr.id ) } + disabledAttributeIds={ productAttributes + .filter( ( attr ) => ! attr.variation ) + .map( ( attr ) => attr.id ) } /> ) } diff --git a/packages/js/product-editor/src/components/attribute-control/attribute-control.tsx b/packages/js/product-editor/src/components/attribute-control/attribute-control.tsx index daa65dac132..4a937d83332 100644 --- a/packages/js/product-editor/src/components/attribute-control/attribute-control.tsx +++ b/packages/js/product-editor/src/components/attribute-control/attribute-control.tsx @@ -48,6 +48,7 @@ type AttributeControlProps = { onNoticeDismiss?: () => void; createNewAttributesAsGlobal?: boolean; useRemoveConfirmationModal?: boolean; + disabledAttributeIds?: number[]; uiStrings?: { notice?: string | React.ReactElement; emptyStateSubtitle?: string; @@ -59,7 +60,8 @@ type AttributeControlProps = { attributeRemoveLabel?: string; attributeRemoveConfirmationMessage?: string; attributeRemoveConfirmationModalMessage?: string; - globalAttributeHelperMessage: string; + globalAttributeHelperMessage?: string; + disabledAttributeMessage?: string; }; }; @@ -80,6 +82,7 @@ export const AttributeControl: React.FC< AttributeControlProps > = ( { uiStrings, createNewAttributesAsGlobal = false, useRemoveConfirmationModal = false, + disabledAttributeIds = [], } ) => { uiStrings = { newAttributeListItemLabel: __( 'Add new', 'woocommerce' ), @@ -279,6 +282,10 @@ export const AttributeControl: React.FC< AttributeControlProps > = ( { onAdd={ handleAdd } selectedAttributeIds={ value.map( ( attr ) => attr.id ) } createNewAttributesAsGlobal={ createNewAttributesAsGlobal } + disabledAttributeIds={ disabledAttributeIds } + disabledAttributeMessage={ + uiStrings.disabledAttributeMessage + } /> ) } @@ -292,22 +299,26 @@ export const AttributeControl: React.FC< AttributeControlProps > = ( { customAttributeHelperMessage={ uiStrings.customAttributeHelperMessage } - globalAttributeHelperMessage={ createInterpolateElement( - uiStrings.globalAttributeHelperMessage, - { - link: ( - - <> - - ), - } - ) } + globalAttributeHelperMessage={ + uiStrings.globalAttributeHelperMessage + ? createInterpolateElement( + uiStrings.globalAttributeHelperMessage, + { + link: ( + + <> + + ), + } + ) + : undefined + } onCancel={ () => { closeEditModal( currentAttribute ); onEditModalCancel( currentAttribute ); diff --git a/packages/js/product-editor/src/components/attribute-control/new-attribute-modal.tsx b/packages/js/product-editor/src/components/attribute-control/new-attribute-modal.tsx index 5dc682b23f5..9c7a36adc51 100644 --- a/packages/js/product-editor/src/components/attribute-control/new-attribute-modal.tsx +++ b/packages/js/product-editor/src/components/attribute-control/new-attribute-modal.tsx @@ -55,6 +55,8 @@ type NewAttributeModalProps = { onAdd: ( newCategories: EnhancedProductAttribute[] ) => void; selectedAttributeIds?: number[]; createNewAttributesAsGlobal?: boolean; + disabledAttributeIds?: number[]; + disabledAttributeMessage?: string; }; type AttributeForm = { @@ -88,6 +90,11 @@ export const NewAttributeModal: React.FC< NewAttributeModalProps > = ( { onAdd, selectedAttributeIds = [], createNewAttributesAsGlobal = false, + disabledAttributeIds = [], + disabledAttributeMessage = __( + 'Already used in Attributes', + 'woocommerce' + ), } ) => { const scrollAttributeIntoView = ( index: number ) => { setTimeout( () => { @@ -317,6 +324,12 @@ export const NewAttributeModal: React.FC< NewAttributeModalProps > = ( { createNewAttributesAsGlobal={ createNewAttributesAsGlobal } + disabledAttributeIds={ + disabledAttributeIds + } + disabledAttributeMessage={ + disabledAttributeMessage + } /> diff --git a/packages/js/product-editor/src/components/attribute-input-field/attribute-input-field.scss b/packages/js/product-editor/src/components/attribute-input-field/attribute-input-field.scss index cf1b23b60f0..0c83f6af4cd 100644 --- a/packages/js/product-editor/src/components/attribute-input-field/attribute-input-field.scss +++ b/packages/js/product-editor/src/components/attribute-input-field/attribute-input-field.scss @@ -8,3 +8,13 @@ margin-right: $gap-small; } } + +.woocommerce-experimental-select-control__popover-menu-container { + .woocommerce-experimental-select-control__menu-item[disabled] { + pointer-events: none; + color: $gray-600; + } + .disabled-element-wrapper { + cursor: not-allowed; + } +} diff --git a/packages/js/product-editor/src/components/attribute-input-field/attribute-input-field.tsx b/packages/js/product-editor/src/components/attribute-input-field/attribute-input-field.tsx index 3ff391d8d85..c47ed08e806 100644 --- a/packages/js/product-editor/src/components/attribute-input-field/attribute-input-field.tsx +++ b/packages/js/product-editor/src/components/attribute-input-field/attribute-input-field.tsx @@ -5,7 +5,7 @@ import { sprintf, __ } from '@wordpress/i18n'; import { useDispatch, useSelect } from '@wordpress/data'; import { Spinner, Icon } from '@wordpress/components'; import { plus } from '@wordpress/icons'; -import { createElement } from '@wordpress/element'; +import { createElement, useMemo } from '@wordpress/element'; import { EXPERIMENTAL_PRODUCT_ATTRIBUTES_STORE_NAME, QueryProductAttribute, @@ -27,7 +27,9 @@ import { import { EnhancedProductAttribute } from '../../hooks/use-product-attributes'; import { TRACKS_SOURCE } from '../../constants'; -type NarrowedQueryAttribute = Pick< QueryProductAttribute, 'id' | 'name' >; +type NarrowedQueryAttribute = Pick< QueryProductAttribute, 'id' | 'name' > & { + isDisabled?: boolean; +}; type AttributeInputFieldProps = { value?: EnhancedProductAttribute | null; @@ -39,6 +41,8 @@ type AttributeInputFieldProps = { label?: string; placeholder?: string; disabled?: boolean; + disabledAttributeIds?: number[]; + disabledAttributeMessage?: string; ignoredAttributeIds?: number[]; createNewAttributesAsGlobal?: boolean; }; @@ -53,6 +57,8 @@ export const AttributeInputField: React.FC< AttributeInputFieldProps > = ( { placeholder, label, disabled, + disabledAttributeIds = [], + disabledAttributeMessage, ignoredAttributeIds = [], createNewAttributesAsGlobal = false, } ) => { @@ -72,6 +78,18 @@ export const AttributeInputField: React.FC< AttributeInputFieldProps > = ( { }; } ); + const markedAttributes = useMemo( + function setDisabledAttribute() { + return ( + attributes?.map( ( attribute ) => ( { + ...attribute, + isDisabled: disabledAttributeIds.includes( attribute.id ), + } ) ) ?? [] + ); + }, + [ attributes, disabledAttributeIds ] + ); + const getFilteredItems = ( allItems: NarrowedQueryAttribute[], inputValue: string @@ -139,7 +157,7 @@ export const AttributeInputField: React.FC< AttributeInputFieldProps > = ( { return ( className="woocommerce-attribute-input-field" - items={ attributes || [] } + items={ markedAttributes || [] } label={ label || '' } disabled={ disabled } getFilteredItems={ getFilteredItems } @@ -179,7 +197,15 @@ export const AttributeInputField: React.FC< AttributeInputFieldProps > = ( { index={ index } isActive={ highlightedIndex === index } item={ item } - getItemProps={ getItemProps } + getItemProps={ ( options ) => ( { + ...getItemProps( options ), + disabled: item.isDisabled || undefined, + } ) } + tooltipText={ + item.isDisabled + ? disabledAttributeMessage + : undefined + } > { isNewAttributeListItem( item ) ? (
    diff --git a/packages/js/product-editor/src/components/attributes/attributes.tsx b/packages/js/product-editor/src/components/attributes/attributes.tsx index efc8c5315e5..50b5c83240c 100644 --- a/packages/js/product-editor/src/components/attributes/attributes.tsx +++ b/packages/js/product-editor/src/components/attributes/attributes.tsx @@ -3,6 +3,7 @@ */ import { createElement } from '@wordpress/element'; import { ProductAttribute } from '@woocommerce/data'; +import { __ } from '@wordpress/i18n'; import { recordEvent } from '@woocommerce/tracks'; /** @@ -31,6 +32,15 @@ export const Attributes: React.FC< AttributesProps > = ( { return ( !! attr.variation ) + .map( ( attr ) => attr.id ) } + uiStrings={ { + disabledAttributeMessage: __( + 'Already used in Variations', + 'woocommerce' + ), + } } onAdd={ () => { recordEvent( 'product_add_attributes_modal_add_button_click' ); } }