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 test name block with support for entity data store #37132

Merged
merged 9 commits into from Mar 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/js/product-editor/changelog/add-name-block-37007
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Adding name field block to product editor.
13 changes: 9 additions & 4 deletions packages/js/product-editor/package.json
Expand Up @@ -38,16 +38,18 @@
"@woocommerce/tracks": "workspace:^1.3.0",
"@wordpress/block-editor": "^9.8.0",
"@wordpress/blocks": "^12.3.0",
"@wordpress/data": "wp-6.0",
"@wordpress/interface": "wp-6.0",
"@wordpress/keyboard-shortcuts": "wp-6.0",
"@wordpress/media-utils": "wp-6.0",
"@wordpress/components": "wp-6.0",
"@wordpress/compose": "wp-6.0",
"@wordpress/core-data": "wp-6.0",
"@wordpress/data": "wp-6.0",
"@wordpress/editor": "wp-6.0",
"@wordpress/element": "wp-6.0",
"@wordpress/html-entities": "wp-6.0",
"@wordpress/i18n": "wp-6.0",
"@wordpress/icons": "wp-6.0",
"@wordpress/interface": "wp-6.0",
"@wordpress/keyboard-shortcuts": "wp-6.0",
"@wordpress/media-utils": "wp-6.0",
"@wordpress/url": "wp-6.0",
"classnames": "^2.3.1",
"lodash": "^4.17.21",
Expand All @@ -57,9 +59,12 @@
"@testing-library/react": "^12.1.3",
"@types/react": "^17.0.2",
"@types/wordpress__block-editor": "^7.0.0",
"@types/wordpress__block-library": "^2.6.1",
"@types/wordpress__blocks": "^11.0.7",
"@types/wordpress__components": "^19.10.3",
"@types/wordpress__core-data": "^2.4.5",
"@types/wordpress__data": "^6.0.2",
"@types/wordpress__editor": "^13.0.0",
"@types/wordpress__media-utils": "^3.0.0",
"@woocommerce/eslint-plugin": "workspace:*",
"@woocommerce/internal-style-build": "workspace:*",
Expand Down
@@ -1,10 +1,10 @@
/**
* External dependencies
*/
import { BlockInstance } from '@wordpress/blocks';
import { createElement, useState, useMemo } from '@wordpress/element';
import { Template } from '@wordpress/blocks';
import { createElement, useMemo, useLayoutEffect } from '@wordpress/element';
import { Product } from '@woocommerce/data';
import { useSelect, select as WPSelect } from '@wordpress/data';
import { useSelect, select as WPSelect, useDispatch } from '@wordpress/data';
import { uploadMedia } from '@wordpress/media-utils';
import {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
Expand All @@ -22,6 +22,13 @@ import {
WritingFlow,
ObserveTyping,
} from '@wordpress/block-editor';
// It doesn't seem to notice the External dependency block whn @ts-ignore is added.
// eslint-disable-next-line @woocommerce/dependency-group
import {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore store should be included.
useEntityBlockEditor,
} from '@wordpress/core-data';

/**
* Internal dependencies
Expand All @@ -30,18 +37,25 @@ import { Sidebar } from '../sidebar';

type BlockEditorProps = {
product: Partial< Product >;
settings: Partial< EditorSettings & EditorBlockListSettings > | undefined;
settings:
| ( Partial< EditorSettings & EditorBlockListSettings > & {
template?: Template[];
} )
| undefined;
};

export function BlockEditor( { settings: _settings }: BlockEditorProps ) {
const [ blocks, updateBlocks ] = useState< BlockInstance[] >();
export function BlockEditor( {
settings: _settings,
product,
}: BlockEditorProps ) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore __experimentalTearDownEditor is not yet included in types package.
const { setupEditor, __experimentalTearDownEditor } =
useDispatch( 'core/editor' );

const canUserCreateMedia = useSelect( ( select: typeof WPSelect ) => {
const { canUser } = select( 'core' ) as Record<
string,
( ...args: string[] ) => boolean
>;
return canUser( 'create', 'media' ) !== false;
const { canUser } = select( 'core' );
return canUser( 'create', 'media', '' ) !== false;
}, [] );

const settings = useMemo( () => {
Expand All @@ -68,28 +82,30 @@ export function BlockEditor( { settings: _settings }: BlockEditorProps ) {
};
}, [ canUserCreateMedia, _settings ] );

/**
* Wrapper for updating blocks. Required as `onInput` callback passed to
* `BlockEditorProvider` is now called with more than 1 argument. Therefore
* attempting to setState directly via `updateBlocks` will trigger an error
* in React.
*
* @param _blocks
*/
function handleUpdateBlocks( _blocks: BlockInstance[] ) {
updateBlocks( _blocks );
}
useLayoutEffect( () => {
setupEditor( product, {}, _settings?.template );

return () => {
__experimentalTearDownEditor();
};
}, [] );

const [ blocks, onInput, onChange ] = useEntityBlockEditor(
'postType',
'product',
{ id: product.id }
);

function handlePersistBlocks( newBlocks: BlockInstance[] ) {
updateBlocks( newBlocks );
if ( ! blocks ) {
return null;
}

return (
<div className="woocommerce-product-block-editor">
<BlockEditorProvider
value={ blocks }
onInput={ handleUpdateBlocks }
onChange={ handlePersistBlocks }
onInput={ onInput }
onChange={ onChange }
settings={ settings }
>
<BlockBreadcrumb />
Expand Down
@@ -0,0 +1,23 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "woocommerce/product-name",
"title": "Product name",
"category": "widgets",
"description": "The product name.",
"keywords": [ "products", "name", "title" ],
"textdomain": "default",
"attributes": {
"name": {
"type": "string"
}
},
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false,
"lock": false
}
}
@@ -0,0 +1,38 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { createElement } from '@wordpress/element';
import interpolateComponents from '@automattic/interpolate-components';
import { TextControl } from '@woocommerce/components';
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 } from '@wordpress/core-data';

export function Edit() {
const blockProps = useBlockProps();
const [ name, setName ] = useEntityProp( 'postType', 'product', 'name' );

return (
<div { ...blockProps }>
<TextControl
label={ interpolateComponents( {
mixedString: __( 'Name {{required/}}', 'woocommerce' ),
components: {
required: (
<span className="woocommerce-product-form__optional-input">
{ __( '(required)', 'woocommerce' ) }
</span>
),
},
} ) }
name={ 'woocommerce-product-name' }
placeholder={ __( 'e.g. 12 oz Coffee Mug', 'woocommerce' ) }
onChange={ setName }
value={ name || '' }
/>
</div>
);
}
@@ -0,0 +1,17 @@
/**
* 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, settings } );
37 changes: 22 additions & 15 deletions packages/js/product-editor/src/components/editor/editor.tsx
Expand Up @@ -8,6 +8,11 @@ import {
} from '@wordpress/block-editor';
import { SlotFillProvider } from '@wordpress/components';
import { Product } from '@woocommerce/data';
// 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 { EntityProvider } from '@wordpress/core-data';

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore No types for this exist yet.
// eslint-disable-next-line @woocommerce/dependency-group
Expand Down Expand Up @@ -37,21 +42,23 @@ type EditorProps = {
export function Editor( { product, settings }: EditorProps ) {
return (
<StrictMode>
<ShortcutProvider>
<FullscreenMode isActive={ false } />
<SlotFillProvider>
<InterfaceSkeleton
header={ <Header title={ product.name } /> }
sidebar={ <Sidebar /> }
content={
<BlockEditor
settings={ settings }
product={ product }
/>
}
/>
</SlotFillProvider>
</ShortcutProvider>
<EntityProvider kind="postType" type="product" id={ product.id }>
<ShortcutProvider>
<FullscreenMode isActive={ false } />
<SlotFillProvider>
<InterfaceSkeleton
header={ <Header title={ product.name } /> }
sidebar={ <Sidebar /> }
content={
<BlockEditor
settings={ settings }
product={ product }
/>
}
/>
</SlotFillProvider>
</ShortcutProvider>
</EntityProvider>
</StrictMode>
);
}
@@ -1,5 +1,8 @@
/**
* Internal dependencies
*/
import { init as initName } from '../details-name-block';

export const initBlocks = () => {};
export const initBlocks = () => {
initName();
};
1 change: 1 addition & 0 deletions packages/js/product-editor/src/utils/index.ts
Expand Up @@ -19,6 +19,7 @@ import { preventLeavingProductForm } from './prevent-leaving-product-form';

export * from './create-ordered-children';
export * from './sort-fills-by-order';
export * from './init-blocks';

export {
AUTO_DRAFT_NAME,
Expand Down
26 changes: 26 additions & 0 deletions packages/js/product-editor/src/utils/init-blocks.ts
@@ -0,0 +1,26 @@
/**
* External dependencies
*/
import { BlockConfiguration, registerBlockType } from '@wordpress/blocks';

interface BlockRepresentation {
name: string;
metadata: BlockConfiguration;
settings: Partial< BlockConfiguration >;
}

/**
* Function to register an individual block.
*
* @param {Object} block The block to be registered.
*
* @return {?WPBlockType} The block, if it has been successfully registered;
* otherwise `undefined`.
*/
export const initBlock = ( block: BlockRepresentation ) => {
if ( ! block ) {
return;
}
const { metadata, settings, name } = block;
return registerBlockType( { name, ...metadata }, settings );
};
7 changes: 6 additions & 1 deletion packages/js/product-editor/tsconfig-cjs.json
@@ -1,6 +1,11 @@
{
"extends": "../tsconfig-cjs",
"include": [
"**/*",
"src/**/*.json"
],
"compilerOptions": {
"outDir": "build"
"outDir": "build",
"resolveJsonModule": true,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Need to tweak TS configuration to support importing JSON files, as there are benefits to using the standard block.json file to provide metadata.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1

}
}
7 changes: 6 additions & 1 deletion packages/js/product-editor/tsconfig.json
@@ -1,10 +1,15 @@
{
"extends": "../tsconfig",
"include": [
"**/*",
"src/**/*.json"
],
"compilerOptions": {
"rootDir": "src",
"outDir": "build-module",
"declaration": true,
"declarationMap": true,
"declarationDir": "./build-types"
"declarationDir": "./build-types",
"resolveJsonModule": true,
}
}
Expand Up @@ -23,8 +23,8 @@ const onDraftCES = jest.fn().mockResolvedValue( {} );

jest.mock( '@wordpress/plugins', () => ( { registerPlugin: jest.fn() } ) );

jest.mock( '@woocommerce/data', () => ( {
...jest.requireActual( '@woocommerce/data' ),
jest.mock( '@wordpress/data', () => ( {
...jest.requireActual( '@wordpress/data' ),
useDispatch: jest.fn().mockReturnValue( { updateOptions: jest.fn() } ),
useSelect: jest.fn().mockReturnValue( { productCESAction: 'hide' } ),
} ) );
Expand Down
Expand Up @@ -32,6 +32,12 @@ jest.mock( '../task-headers', () => ( {
required: () => <div>required_header</div>,
completed: () => <div>completed_header</div>,
} ) );
jest.mock( '@woocommerce/data', () => ( {
...jest.requireActual( '@woocommerce/data' ),
useUserPreferences: jest.fn().mockReturnValue( {
updateUserPreferences: jest.fn(),
} ),
} ) );

const tasks: { [ key: string ]: TaskType[] } = {
setup: [
Expand Down
4 changes: 4 additions & 0 deletions plugins/woocommerce/changelog/add-name-block-37007
@@ -0,0 +1,4 @@
Significance: minor
Type: update

Update template of product type to include product name block.
4 changes: 2 additions & 2 deletions plugins/woocommerce/includes/class-wc-post-types.php
Expand Up @@ -368,9 +368,9 @@ public static function register_post_types() {
'rest_namespace' => 'wp/v3',
'template' => array(
array(
'core/paragraph',
'woocommerce/product-name',
array(
'placeholder' => 'Product description',
'name' => 'Product name',
),
),
),
Expand Down