From 74d0b39bb06deb1f8724155b48edc389cba0a7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Mon, 29 Jan 2024 17:14:17 -0300 Subject: [PATCH] [Product Block Editor]: introduce TextArea field block (#44104) * introduce TextAreaBlockEdit block * reglister text area block * use text-area block in the summary block * changelog * changelog * fix whitespace lint issue * remove unused types * intrduce placehholder attribute * property is the only required attribute * tidy block attributes * use useProductEntityProp() * define fallback value * remove unused CSS class * organize attrs * do not implement tooltip yet * fix eslint error * update text-area keywords --- .../update-introduce-text-area-block | 4 + .../src/blocks/generic/text-area/block.json | 63 ++++++++++ .../src/blocks/generic/text-area/edit.tsx | 111 ++++++++++++++++++ .../src/blocks/generic/text-area/editor.scss | 32 +++++ .../src/blocks/generic/text-area/index.ts | 28 +++++ .../toolbar-button-alignment/index.tsx | 52 ++++++++ .../toolbar/toolbar-button-rtl/index.tsx | 32 +++++ .../toolbar/toolbar-button-rtl/types.ts | 18 +++ .../src/blocks/generic/text-area/types.ts | 34 ++++++ .../js/product-editor/src/blocks/index.ts | 1 + .../js/product-editor/src/blocks/style.scss | 1 + .../update-introduce-text-area-block | 4 + .../ProductBlockEditor/BlockRegistry.php | 1 + .../SimpleProductTemplate.php | 8 +- 14 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 packages/js/product-editor/changelog/update-introduce-text-area-block create mode 100644 packages/js/product-editor/src/blocks/generic/text-area/block.json create mode 100644 packages/js/product-editor/src/blocks/generic/text-area/edit.tsx create mode 100644 packages/js/product-editor/src/blocks/generic/text-area/editor.scss create mode 100644 packages/js/product-editor/src/blocks/generic/text-area/index.ts create mode 100644 packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-alignment/index.tsx create mode 100644 packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-rtl/index.tsx create mode 100644 packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-rtl/types.ts create mode 100644 packages/js/product-editor/src/blocks/generic/text-area/types.ts create mode 100644 plugins/woocommerce/changelog/update-introduce-text-area-block diff --git a/packages/js/product-editor/changelog/update-introduce-text-area-block b/packages/js/product-editor/changelog/update-introduce-text-area-block new file mode 100644 index 00000000000..9fc61b7477f --- /dev/null +++ b/packages/js/product-editor/changelog/update-introduce-text-area-block @@ -0,0 +1,4 @@ +Significance: patch +Type: add + +[Product Block Editor]: introduce TextArea field block diff --git a/packages/js/product-editor/src/blocks/generic/text-area/block.json b/packages/js/product-editor/src/blocks/generic/text-area/block.json new file mode 100644 index 00000000000..0ae0c745bb2 --- /dev/null +++ b/packages/js/product-editor/src/blocks/generic/text-area/block.json @@ -0,0 +1,63 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "woocommerce/product-text-area-field", + "title": "Product textarea block", + "category": "woocommerce", + "description": "A text-area field for use in the product editor.", + "keywords": [ "textarea", "rich-text" ], + "textdomain": "default", + "attributes": { + "property": { + "type": "string" + }, + "label": { + "type": "string", + "__experimentalRole": "content" + }, + "placeholder": { + "type": "string" + }, + "help": { + "type": "string" + }, + "required": { + "type": "string" + }, + "disabled": { + "type": "boolean" + }, + "align": { + "type": "string", + "enum": [ "left", "center", "right", "justify" ] + }, + "allowedFormats": { + "type": "array", + "default": [ + "core/bold", + "core/code", + "core/italic", + "core/link", + "core/strikethrough", + "core/underline", + "core/text-color", + "core/subscript", + "core/superscript", + "core/unknown" + ] + }, + "direction": { + "type": "string", + "enum": [ "ltr", "rtl" ] + } + }, + "supports": { + "align": false, + "html": false, + "multiple": true, + "reusable": false, + "inserter": false, + "lock": false, + "__experimentalToolbar": true + } +} diff --git a/packages/js/product-editor/src/blocks/generic/text-area/edit.tsx b/packages/js/product-editor/src/blocks/generic/text-area/edit.tsx new file mode 100644 index 00000000000..6167b0ff3d1 --- /dev/null +++ b/packages/js/product-editor/src/blocks/generic/text-area/edit.tsx @@ -0,0 +1,111 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useWooBlockProps } from '@woocommerce/block-templates'; +import { createElement } from '@wordpress/element'; +import { BaseControl } from '@wordpress/components'; +import { useInstanceId } from '@wordpress/compose'; +import { BlockControls, RichText } from '@wordpress/block-editor'; +import classNames from 'classnames'; + +/** + * Internal dependencies + */ +import { RTLToolbarButton } from './toolbar/toolbar-button-rtl'; +import type { + TextAreaBlockEditAttributes, + TextAreaBlockEditProps, +} from './types'; +import AligmentToolbarButton from './toolbar/toolbar-button-alignment'; +import useProductEntityProp from '../../../hooks/use-product-entity-prop'; + +export function TextAreaBlockEdit( { + attributes, + setAttributes, + context: { postType }, +}: TextAreaBlockEditProps ) { + const { + property, + label, + placeholder, + help, + required, + disabled, + align, + allowedFormats, + direction, + } = attributes; + const blockProps = useWooBlockProps( attributes, { + style: { direction }, + } ); + + const contentId = useInstanceId( + TextAreaBlockEdit, + 'wp-block-woocommerce-product-content-field__content' + ); + + // `property` attribute is required. + if ( ! property ) { + throw new Error( + __( 'Property attribute is required.', 'woocommerce' ) + ); + } + + const [ content, setContent ] = useProductEntityProp< string >( property, { + postType, + } ); + + function setAlignment( value: TextAreaBlockEditAttributes[ 'align' ] ) { + setAttributes( { align: value } ); + } + + function changeDirection( + value: TextAreaBlockEditAttributes[ 'direction' ] + ) { + setAttributes( { direction: value } ); + } + + const blockControlsProps = { group: 'block' }; + + return ( +
+ + + + + + + +
+ +
+
+
+ ); +} diff --git a/packages/js/product-editor/src/blocks/generic/text-area/editor.scss b/packages/js/product-editor/src/blocks/generic/text-area/editor.scss new file mode 100644 index 00000000000..0a452eeb821 --- /dev/null +++ b/packages/js/product-editor/src/blocks/generic/text-area/editor.scss @@ -0,0 +1,32 @@ +.wp-block-woocommerce-product-text-area-field { + .rich-text { + width: 100%; + min-height: calc($gap-larger * 3); + background-color: $white; + box-sizing: border-box; + border: 1px solid #757575; + border-radius: 2px; + padding: $gap-smaller; + margin: 0; + appearance: textarea; + resize: vertical; + overflow: hidden; + + &.rich-text [data-rich-text-placeholder]:after { + color: $gray-700; + opacity: 1; + } + + &:focus { + box-shadow: inset 0 0 0 1px var(--wp-admin-theme-color-darker-10, --wp-admin-theme-color); + border-color: var(--wp-admin-theme-color-darker-10, --wp-admin-theme-color); + } + } + + // This alignment class does not exists in + // https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/common.scss + .has-text-align-justify { + /*rtl:ignore*/ + text-align: justify; + } +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/text-area/index.ts b/packages/js/product-editor/src/blocks/generic/text-area/index.ts new file mode 100644 index 00000000000..58f17cde62c --- /dev/null +++ b/packages/js/product-editor/src/blocks/generic/text-area/index.ts @@ -0,0 +1,28 @@ +/** + * External dependencies + */ +import { postContent } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import blockConfiguration from './block.json'; +import { TextAreaBlockEdit } from './edit'; +import { registerProductEditorBlockType } from '../../../utils'; + +const { name, ...metadata } = blockConfiguration; + +export { metadata, name }; + +export const settings = { + example: {}, + edit: TextAreaBlockEdit, + icon: postContent, +}; + +export const init = () => + registerProductEditorBlockType( { + name, + metadata: metadata as never, + settings: settings as never, + } ); diff --git a/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-alignment/index.tsx b/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-alignment/index.tsx new file mode 100644 index 00000000000..9b0e48d6c94 --- /dev/null +++ b/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-alignment/index.tsx @@ -0,0 +1,52 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { + alignCenter, + alignJustify, + alignLeft, + alignRight, +} from '@wordpress/icons'; +import { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore No types for this exist yet. + AlignmentControl, +} from '@wordpress/block-editor'; + +export const ALIGNMENT_CONTROLS = [ + { + icon: alignLeft, + title: __( 'Align text left', 'woocommerce' ), + align: 'left', + }, + { + icon: alignCenter, + title: __( 'Align text center', 'woocommerce' ), + align: 'center', + }, + { + icon: alignRight, + title: __( 'Align text right', 'woocommerce' ), + align: 'right', + }, + { + icon: alignJustify, + title: __( 'Align text justify', 'woocommerce' ), + align: 'justify', + }, +]; + +export default function AligmentToolbarButton( { + align, + setAlignment, +}: AlignmentControl ) { + return ( + + ); +} diff --git a/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-rtl/index.tsx b/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-rtl/index.tsx new file mode 100644 index 00000000000..21574adeada --- /dev/null +++ b/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-rtl/index.tsx @@ -0,0 +1,32 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { ToolbarButton } from '@wordpress/components'; +import { _x, isRTL } from '@wordpress/i18n'; +import { formatLtr } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import type { RTLToolbarButtonProps } from './types'; + +export function RTLToolbarButton( { + direction, + onChange, +}: RTLToolbarButtonProps ) { + if ( ! isRTL() ) { + return null; + } + + return ( + + onChange?.( direction === 'ltr' ? undefined : 'ltr' ) + } + /> + ); +} diff --git a/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-rtl/types.ts b/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-rtl/types.ts new file mode 100644 index 00000000000..5c19b64eae2 --- /dev/null +++ b/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-rtl/types.ts @@ -0,0 +1,18 @@ +/** + * Internal dependencies + */ +import type { TextAreaBlockEditAttributes } from '../../types'; + +type DirectionProp = TextAreaBlockEditAttributes[ 'direction' ]; + +export type RTLToolbarButtonProps = { + /** + * Current direction. + */ + direction: DirectionProp; + + /** + * Callback to update the direction. + */ + onChange( direction?: DirectionProp ): void; +}; diff --git a/packages/js/product-editor/src/blocks/generic/text-area/types.ts b/packages/js/product-editor/src/blocks/generic/text-area/types.ts new file mode 100644 index 00000000000..43422e85827 --- /dev/null +++ b/packages/js/product-editor/src/blocks/generic/text-area/types.ts @@ -0,0 +1,34 @@ +/** + * Internal dependencies + */ +import { + ProductEditorBlockAttributes, + ProductEditorBlockEditProps, +} from '../../../types'; + +type AllowedFormat = + | 'core/bold' + | 'core/code' + | 'core/italic' + | 'core/link' + | 'core/strikethrough' + | 'core/underline' + | 'core/text-color' + | 'core/subscript' + | 'core/superscript' + | 'core/unknown'; + +export type TextAreaBlockEditAttributes = ProductEditorBlockAttributes & { + property: string; + label?: string; + placeholder?: string; + help?: string; + required?: boolean; + disabled?: boolean; + align?: 'left' | 'center' | 'right' | 'justify'; + allowedFormats?: AllowedFormat[]; + direction?: 'ltr' | 'rtl'; +}; + +export type TextAreaBlockEditProps = + ProductEditorBlockEditProps< TextAreaBlockEditAttributes >; diff --git a/packages/js/product-editor/src/blocks/index.ts b/packages/js/product-editor/src/blocks/index.ts index 9093c55defe..61176ee33da 100644 --- a/packages/js/product-editor/src/blocks/index.ts +++ b/packages/js/product-editor/src/blocks/index.ts @@ -35,3 +35,4 @@ export { init as initTaxonomy } from './generic/taxonomy'; export { init as initText } from './generic/text'; export { init as initNumber } from './generic/number'; export { init as initLinkedProductList } from './generic/linked-product-list'; +export { init as initTextArea } from './generic/text-area'; diff --git a/packages/js/product-editor/src/blocks/style.scss b/packages/js/product-editor/src/blocks/style.scss index e0791f1d9e3..44d7c17cba1 100644 --- a/packages/js/product-editor/src/blocks/style.scss +++ b/packages/js/product-editor/src/blocks/style.scss @@ -25,3 +25,4 @@ @import "generic/toggle/editor.scss"; @import "generic/number/editor.scss"; @import "generic/linked-product-list/editor.scss"; +@import "generic/text-area/editor.scss"; diff --git a/plugins/woocommerce/changelog/update-introduce-text-area-block b/plugins/woocommerce/changelog/update-introduce-text-area-block new file mode 100644 index 00000000000..1a085367ead --- /dev/null +++ b/plugins/woocommerce/changelog/update-introduce-text-area-block @@ -0,0 +1,4 @@ +Significance: patch +Type: add + +Use the new text area block in the summary field diff --git a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/BlockRegistry.php b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/BlockRegistry.php index 738b1a58316..bf71de088cb 100644 --- a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/BlockRegistry.php +++ b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/BlockRegistry.php @@ -36,6 +36,7 @@ class BlockRegistry { 'woocommerce/product-toggle-field', 'woocommerce/product-taxonomy-field', 'woocommerce/product-text-field', + 'woocommerce/product-text-area-field', 'woocommerce/product-number-field', 'woocommerce/product-linked-list-field', ); diff --git a/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php b/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php index 00858a1186c..1370cd0dd38 100644 --- a/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php +++ b/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php @@ -215,12 +215,18 @@ class SimpleProductTemplate extends AbstractProductFormTemplate implements Produ ), ) ); + $basic_details->add_block( array( 'id' => 'product-summary', - 'blockName' => 'woocommerce/product-summary-field', + 'blockName' => 'woocommerce/product-text-area-field', 'order' => 20, 'attributes' => array( + 'label' => __( 'Summary', 'woocommerce' ), + 'help' => __( + "Summarize this product in 1-2 short sentences. We'll show it at the top of the page.", + 'woocommerce' + ), 'property' => 'short_description', ), )