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

[Product Block Editor]: introduce TextArea field block #44104

Merged
merged 17 commits into from
Jan 29, 2024
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: add

[Product Block Editor]: introduce TextArea field block
63 changes: 63 additions & 0 deletions packages/js/product-editor/src/blocks/generic/text-area/block.json
Original file line number Diff line number Diff line change
@@ -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": {
Copy link
Contributor

Choose a reason for hiding this comment

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

placeholder isn't listed here, though it is supported.

We should also support (same as woocommerce/product-text-field):

  • tooltip
  • required
  • disabled

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added the attributes and types, but probably we need to keep improving these attributes. Can we do it in a follow-up?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm cool with a follow-up, but we shouldn't consider the (sorely missing any details 😞) issue closed/fixed until they are added.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have updated the pull request so that it doesnt close the issue when merged

"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
}
}
111 changes: 111 additions & 0 deletions packages/js/product-editor/src/blocks/generic/text-area/edit.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className={ 'wp-block-woocommerce-product-text-area-field' }>
<BlockControls { ...blockControlsProps }>
<AligmentToolbarButton
align={ align }
setAlignment={ setAlignment }
/>
Comment on lines +74 to +77
Copy link
Contributor

Choose a reason for hiding this comment

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

We should have a way to turn off the alignment toolbar, as for a plain text textarea, it isn't necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need to define this functionality a little bit. It isn't clear to me when/how the block should support rich text or plain text areas.

  • Is it the same block that changes its behavior depending on a parameter? (block attribute).
  • Could this block support both modes? It could be switchable from the block toolbar.
  • Or, should we create two different blocks? (text-area and rich-text, for instance)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could we address this issue in a follow-up, Matt? I want to avoid increasing the PR size.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm good if this is addressed in a follow-up. I think we should allow block attributes that allow us to pick between using a plain textarea vs one with rich text.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree that it should be switchable between rich text and plain text via a block attribute. I don't think, for our purposes, it makes sense to have it be switchable from the block toolbar.

Copy link
Contributor Author

Choose a reason for hiding this comment

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


<RTLToolbarButton
direction={ direction }
onChange={ changeDirection }
/>
</BlockControls>

<BaseControl
id={ contentId.toString() }
label={ label }
help={ help }
>
<div { ...blockProps }>
<RichText
id={ contentId.toString() }
identifier="content"
tagName="p"
value={ content || '' }
onChange={ setContent }
data-empty={ Boolean( content ) }
className={ classNames( {
[ `has-text-align-${ align }` ]: align,
} ) }
dir={ direction }
allowedFormats={ allowedFormats }
placeholder={ placeholder }
required={ required }
disabled={ disabled }
/>
</div>
</BaseControl>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.wp-block-woocommerce-product-text-area-field {
.rich-text {
width: 100%;
min-height: calc($gap-larger * 3);
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems odd to base the min-height on the gap instead of something related to the font size.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How do you think we should define the minimum height? Maybe we can use a different var?

background-color: $white;
box-sizing: border-box;
border: 1px solid #757575;
Copy link
Contributor

Choose a reason for hiding this comment

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

Hardcoded color. It's a shame we don't have a variable for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Creating a new var, at least for now, sounds good for you?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ad88986 Here we are using the $gray-700 var.

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;
}
}
28 changes: 28 additions & 0 deletions packages/js/product-editor/src/blocks/generic/text-area/index.ts
Original file line number Diff line number Diff line change
@@ -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,
} );
Original file line number Diff line number Diff line change
@@ -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 (
<AlignmentControl
alignmentControls={ ALIGNMENT_CONTROLS }
value={ align }
onChange={ setAlignment }
/>
);
}
Original file line number Diff line number Diff line change
@@ -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 (
<ToolbarButton
icon={ formatLtr }
title={ _x( 'Left to right', 'editor button', 'woocommerce' ) }
isActive={ direction === 'ltr' }
onClick={ () =>
onChange?.( direction === 'ltr' ? undefined : 'ltr' )
}
/>
);
}
Original file line number Diff line number Diff line change
@@ -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;
};
34 changes: 34 additions & 0 deletions packages/js/product-editor/src/blocks/generic/text-area/types.ts
Original file line number Diff line number Diff line change
@@ -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 >;
1 change: 1 addition & 0 deletions packages/js/product-editor/src/blocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
1 change: 1 addition & 0 deletions packages/js/product-editor/src/blocks/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: add

Use the new text area block in the summary field