-
Notifications
You must be signed in to change notification settings - Fork 10.7k
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
Changes from all commits
b58159e
979d331
0b68513
ada5c04
3ea761f
0b5c263
054e8f1
d06de61
2ceb876
d58f79b
b6eb079
0d9e3da
c706812
1f5c7c5
f1aef26
92837fc
0e90a85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
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": { | ||
"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 | ||
} | ||
} |
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems odd to base the There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Creating a new var, at least for now, sounds good for you? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ad88986 Here we are using the |
||
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; | ||
} | ||
} |
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; | ||
}; |
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 >; |
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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