Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding attributes block to product block editor. #38051

Merged
merged 12 commits into from May 3, 2023
Merged
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Adding attributes components, block and styles.
3 changes: 2 additions & 1 deletion packages/js/product-editor/package.json
Expand Up @@ -35,7 +35,7 @@
"@woocommerce/components": "workspace:*",
"@woocommerce/currency": "workspace:*",
"@woocommerce/customer-effort-score": "workspace:*",
"@woocommerce/data": "workspace:^4.1.0",
"@woocommerce/data": "workspace:*",
Copy link
Contributor

Choose a reason for hiding this comment

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

🎉

"@woocommerce/experimental": "workspace:*",
"@woocommerce/navigation": "workspace:^8.1.0",
"@woocommerce/number": "workspace:*",
Expand Down Expand Up @@ -89,6 +89,7 @@
"@types/wordpress__keycodes": "^2.3.1",
"@types/wordpress__media-utils": "^3.0.0",
"@types/wordpress__plugins": "^3.0.0",
"@types/wordpress__rich-text": "^3.4.6",
"@woocommerce/eslint-plugin": "workspace:*",
"@woocommerce/internal-js-tests": "workspace:*",
"@woocommerce/internal-style-build": "workspace:*",
Expand Down
26 changes: 26 additions & 0 deletions packages/js/product-editor/src/blocks/attributes/block.json
@@ -0,0 +1,26 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "woocommerce/product-attributes-field",
"title": "Product attributes",
"category": "widgets",
"description": "The product attributes.",
"keywords": [ "products", "attributes" ],
"textdomain": "default",
"attributes": {
"name": {
"type": "string",
"__experimentalRole": "content"
}
},
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false,
"lock": false,
"__experimentalToolbar": false
},
"editorStyle": "file:./editor.css"
}
35 changes: 35 additions & 0 deletions packages/js/product-editor/src/blocks/attributes/edit.tsx
@@ -0,0 +1,35 @@
/**
* External dependencies
*/
import { createElement } from '@wordpress/element';
import { ProductAttribute } from '@woocommerce/data';
import { useBlockProps } from '@wordpress/block-editor';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore No types for this exist yet.
// eslint-disable-next-line @woocommerce/dependency-group
import { useEntityProp, useEntityId } from '@wordpress/core-data';

/**
* Internal dependencies
*/
import { Attributes as AttributesContainer } from '../../components/attributes/attributes';

export function Edit() {
const [ entityAttributes, setEntityAttributes ] = useEntityProp<
ProductAttribute[]
>( 'postType', 'product', 'attributes' );

const productId = useEntityId( 'postType', 'product' );

const blockProps = useBlockProps();

return (
<div { ...blockProps }>
<AttributesContainer
productId={ productId }
value={ entityAttributes }
onChange={ setEntityAttributes }
/>
</div>
);
}
21 changes: 21 additions & 0 deletions packages/js/product-editor/src/blocks/attributes/editor.scss
@@ -0,0 +1,21 @@
.wp-block-woocommerce-product-images-field {
.woocommerce-image-gallery {
margin-top: $gap-largest;
}
.woocommerce-media-uploader {
text-align: left;
}
.woocommerce-media-uploader__label {
display: none;
}
.woocommerce-sortable {
margin-top: 0;
padding: 0;
}

&:not(.has-images) {
.woocommerce-sortable {
display: none;
}
}
}
22 changes: 22 additions & 0 deletions packages/js/product-editor/src/blocks/attributes/index.ts
@@ -0,0 +1,22 @@
/**
* Internal dependencies
*/
import { initBlock } from '../../utils';
import metadata from './block.json';
import { Edit } from './edit';

const { name } = metadata;

export { metadata, name };

export const settings = {
example: {},
edit: Edit,
};

export const init = () =>
initBlock( {
name,
metadata: metadata as never,
settings,
} );
1 change: 1 addition & 0 deletions packages/js/product-editor/src/blocks/index.ts
Expand Up @@ -17,3 +17,4 @@ export { init as initSummary } from './summary';
export { init as initTab } from './tab';
export { init as initInventoryQuantity } from './inventory-quantity';
export { init as initToggle } from './toggle';
export { init as attributesInit } from './attributes';
Expand Up @@ -2,32 +2,32 @@
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import {
useState,
createElement,
Fragment,
createInterpolateElement,
} from '@wordpress/element';
import { Button } from '@wordpress/components';
import { ProductAttribute } from '@woocommerce/data';
import {
Sortable,
__experimentalSelectControlMenuSlot as SelectControlMenuSlot,
Link,
} from '@woocommerce/components';
import interpolateComponents from '@automattic/interpolate-components';
import { getAdminLink } from '@woocommerce/settings';

/**
* Internal dependencies
*/
import './attribute-field.scss';
import { EditAttributeModal } from './edit-attribute-modal';
import { EnhancedProductAttribute } from '~/products/hooks/use-product-attributes';
import { EnhancedProductAttribute } from '../../hooks/use-product-attributes';
import {
getAttributeId,
getAttributeKey,
reorderSortableProductAttributePositions,
} from './utils';
import { AttributeEmptyState } from '../attribute-empty-state';
import {
AttributeListItem,
NewAttributeListItem,
} from '../attribute-list-item';
import { AttributeListItem } from '../attribute-list-item';
import { NewAttributeModal } from './new-attribute-modal';

type AttributeControlProps = {
Expand Down Expand Up @@ -67,9 +67,9 @@ export const AttributeControl: React.FC< AttributeControlProps > = ( {
uiStrings = {
newAttributeModalTitle: undefined,
emptyStateSubtitle: undefined,
newAttributeListItemLabel: undefined,
newAttributeListItemLabel: __( 'Add attributes', 'woocommerce' ),
globalAttributeHelperMessage: __(
`You can change the attribute's name in {{link}}Attributes{{/link}}.`,
`You can change the attribute's name in <link>Attributes</link>.`,
'woocommerce'
),
},
Expand Down Expand Up @@ -160,30 +160,6 @@ export const AttributeControl: React.FC< AttributeControlProps > = ( {
closeEditModal( updatedAttribute );
};

if ( ! value.length ) {
return (
<>
<AttributeEmptyState
addNewLabel={ uiStrings.newAttributeModalTitle }
onNewClick={ () => openNewModal() }
subtitle={ uiStrings.emptyStateSubtitle }
/>
{ isNewModalVisible && (
<NewAttributeModal
onCancel={ () => {
closeNewModal();
onNewModalCancel();
} }
onAdd={ handleAdd }
selectedAttributeIds={ [] }
title={ uiStrings.newAttributeModalTitle }
/>
) }
<SelectControlMenuSlot />
</>
);
}

const sortedAttributes = value.sort( ( a, b ) => a.position - b.position );

const attributeKeyValues = value.reduce(
Expand All @@ -203,37 +179,44 @@ export const AttributeControl: React.FC< AttributeControlProps > = ( {

return (
<div className="woocommerce-attribute-field">
<Sortable
onOrderChange={ ( items ) => {
const itemPositions = items.reduce(
( positions, { props }, index ) => {
positions[ getAttributeKey( props.attribute ) ] =
index;
return positions;
},
{} as Record< number | string, number >
);
onChange(
reorderSortableProductAttributePositions(
itemPositions,
attributeKeyValues
)
);
} }
<Button
variant="secondary"
className="woocommerce-add-attribute-list-item__add-button"
onClick={ openNewModal }
>
{ sortedAttributes.map( ( attr ) => (
<AttributeListItem
attribute={ attr }
key={ getAttributeId( attr ) }
onEditClick={ () => openEditModal( attr ) }
onRemoveClick={ () => handleRemove( attr ) }
/>
) ) }
</Sortable>
<NewAttributeListItem
label={ uiStrings.newAttributeListItemLabel }
onClick={ () => openNewModal() }
/>
{ uiStrings.newAttributeListItemLabel }
</Button>
{ Boolean( value.length ) && (
<Sortable
onOrderChange={ ( items ) => {
const itemPositions = items.reduce(
( positions, { props }, index ) => {
positions[
getAttributeKey( props.attribute )
] = index;
return positions;
},
{} as Record< number | string, number >
);
onChange(
reorderSortableProductAttributePositions(
itemPositions,
attributeKeyValues
)
);
} }
>
{ sortedAttributes.map( ( attr ) => (
<AttributeListItem
attribute={ attr }
key={ getAttributeId( attr ) }
onEditClick={ () => openEditModal( attr ) }
onRemoveClick={ () => handleRemove( attr ) }
/>
) ) }
</Sortable>
) }

{ isNewModalVisible && (
<NewAttributeModal
title={ uiStrings.newAttributeModalTitle }
Expand All @@ -253,9 +236,9 @@ export const AttributeControl: React.FC< AttributeControlProps > = ( {
__( 'Edit %s', 'woocommerce' ),
currentAttribute.name
) }
globalAttributeHelperMessage={ interpolateComponents( {
mixedString: uiStrings.globalAttributeHelperMessage,
components: {
globalAttributeHelperMessage={ createInterpolateElement(
uiStrings.globalAttributeHelperMessage,
{
link: (
<Link
href={ getAdminLink(
Expand All @@ -267,8 +250,8 @@ export const AttributeControl: React.FC< AttributeControlProps > = ( {
<></>
</Link>
),
},
} ) }
}
) }
onCancel={ () => {
closeEditModal( currentAttribute );
onEditModalCancel( currentAttribute );
Expand Down
@@ -1,5 +1,6 @@
.woocommerce-attribute-field {
width: 100%;
font-size: 13px;

.woocommerce-sortable {
margin: 0;
Expand All @@ -12,4 +13,23 @@
background: none;
border-top: 0;
}

.woocommerce-add-attribute-list-item__add-button {
margin-bottom: $gap;
}

}

.wp-block-woocommerce-product-attributes-field {

.woocommerce-sortable {
padding: 0;
}

.woocommerce-list-item {
background: none;
border: none;
border-bottom: 1px solid $gray-200;
padding-left: 0;
}
}
Expand Up @@ -8,7 +8,7 @@ import {
CheckboxControl,
TextControl,
} from '@wordpress/components';
import { useState } from '@wordpress/element';
import { useState, createElement } from '@wordpress/element';
import { __experimentalTooltip as Tooltip } from '@woocommerce/components';

/**
Expand All @@ -20,8 +20,6 @@ import {
} from '../attribute-term-input-field';
import { EnhancedProductAttribute } from '../../hooks/use-product-attributes';

import './edit-attribute-modal.scss';

type EditAttributeModalProps = {
title?: string;
nameLabel?: string;
Expand Down
Expand Up @@ -2,7 +2,7 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { useState, createElement, Fragment } from '@wordpress/element';
import { trash } from '@wordpress/icons';
import {
Form,
Expand All @@ -20,13 +20,12 @@ import {
/**
* Internal dependencies
*/
import './new-attribute-modal.scss';
import { AttributeInputField } from '../attribute-input-field';
import {
AttributeTermInputField,
CustomAttributeTermInputField,
} from '../attribute-term-input-field';
import { EnhancedProductAttribute } from '~/products/hooks/use-product-attributes';
import { EnhancedProductAttribute } from '../../hooks/use-product-attributes';
import { getProductAttributeObject } from './utils';

type NewAttributeModalProps = {
Expand Down