Merged in feature/117-dev-dev01 (pull request #8)
auto-patch 117-dev-dev01-2023-12-15T16_09_06 * auto-patch 117-dev-dev01-2023-12-15T16_09_06
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
import { useEffect } from '@wordpress/element';
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Button, Disabled, Tooltip } from '@wordpress/components';
|
||||
import { Disabled, Tooltip } from '@wordpress/components';
|
||||
import { Skeleton } from '@woocommerce/base-components/skeleton';
|
||||
import { BlockEditProps } from '@wordpress/blocks';
|
||||
|
||||
@@ -37,31 +37,28 @@ const Edit = ( props: BlockEditProps< Attributes > ) => {
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<Tooltip
|
||||
text="Customer will see product add-to-cart options in this space, dependend on the product type. "
|
||||
text="Customer will see product add-to-cart options in this space, dependent on the product type. "
|
||||
position="bottom right"
|
||||
>
|
||||
<div className="wc-block-editor-container">
|
||||
<div className="wc-block-editor-add-to-cart-form-container">
|
||||
<Skeleton numberOfLines={ 3 } />
|
||||
<Disabled>
|
||||
<input
|
||||
type={ 'number' }
|
||||
value={ '1' }
|
||||
className={
|
||||
'wc-block-editor-add-to-cart-form__quantity'
|
||||
}
|
||||
readOnly
|
||||
/>
|
||||
<Button
|
||||
variant={ 'primary' }
|
||||
className={
|
||||
'wc-block-editor-add-to-cart-form__button'
|
||||
}
|
||||
<div className="quantity">
|
||||
<input
|
||||
type={ 'number' }
|
||||
value={ '1' }
|
||||
className={ 'input-text qty text' }
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className={ `single_add_to_cart_button button alt wp-element-button` }
|
||||
>
|
||||
{ __(
|
||||
'Add to cart',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</Button>
|
||||
</button>
|
||||
</Disabled>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
@@ -1,30 +1,4 @@
|
||||
.wc-block-editor-add-to-cart-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: $default-block-margin;
|
||||
}
|
||||
|
||||
input.wc-block-editor-add-to-cart-form__quantity[type="number"] {
|
||||
max-width: 50px;
|
||||
min-height: 23px;
|
||||
float: left;
|
||||
padding: 6px 6px 6px 12px;
|
||||
margin-right: 10px;
|
||||
font-size: 13px;
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
input[type="number"]::-webkit-inner-spin-button {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
button.components-button.wc-block-add-to-cart-form__button {
|
||||
float: left;
|
||||
padding: 20px 30px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.wc-block-editor-container {
|
||||
.wc-block-editor-add-to-cart-form-container {
|
||||
cursor: help;
|
||||
gap: 10px;
|
||||
display: flex;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.wp-block-add-to-cart-form {
|
||||
.wc-block-add-to-cart-form {
|
||||
width: unset;
|
||||
/**
|
||||
* This is a base style for the input text element in WooCommerce that prevents inputs from appearing too small.
|
||||
@@ -9,4 +9,17 @@
|
||||
font-size: var(--wp--preset--font-size--small);
|
||||
padding: 0.9rem 1.1rem;
|
||||
}
|
||||
|
||||
.quantity {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
margin-right: 4px;
|
||||
vertical-align: middle;
|
||||
|
||||
.qty {
|
||||
margin-right: 0.5rem;
|
||||
width: 3.631em;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { SelectControl } from 'wordpress-components';
|
||||
import type { SelectControl as SelectControlType } from '@wordpress/components';
|
||||
import { useEffect } from '@wordpress/element';
|
||||
import classnames from 'classnames';
|
||||
import { ValidationInputError } from '@woocommerce/blocks-checkout';
|
||||
import { ValidationInputError } from '@woocommerce/blocks-components';
|
||||
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
// Set button font size and padding so it inherits from parent.
|
||||
padding: 0.5em 1em;
|
||||
// Set button font size so it inherits from parent.
|
||||
font-size: 1em;
|
||||
|
||||
&.loading {
|
||||
@@ -25,6 +24,7 @@
|
||||
font-family: WooCommerce; /* stylelint-disable-line */
|
||||
content: "\e031";
|
||||
animation: spin 2s linear infinite;
|
||||
margin-right: 0;
|
||||
margin-left: 0.5em;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
@@ -70,8 +70,9 @@
|
||||
justify-content: center;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
width: 150px;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
line-height: inherit;
|
||||
|
||||
span {
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import {
|
||||
BlockConfiguration,
|
||||
registerBlockType,
|
||||
unregisterBlockType,
|
||||
registerBlockVariation,
|
||||
unregisterBlockVariation,
|
||||
BlockVariation,
|
||||
BlockAttributes,
|
||||
} from '@wordpress/blocks';
|
||||
|
||||
export interface BlockRegistrationStrategy {
|
||||
register(
|
||||
blockNameOrMetadata: string | Partial< BlockConfiguration >,
|
||||
blockSettings: Partial< BlockConfiguration >
|
||||
): boolean;
|
||||
unregister( blockName: string, variationName?: string ): boolean;
|
||||
}
|
||||
|
||||
export class BlockTypeStrategy implements BlockRegistrationStrategy {
|
||||
register(
|
||||
blockNameOrMetadata: string | Partial< BlockConfiguration >,
|
||||
blockSettings: Partial< BlockConfiguration >
|
||||
): boolean {
|
||||
return Boolean(
|
||||
// @ts-expect-error: `registerBlockType` is typed in WordPress core
|
||||
registerBlockType( blockNameOrMetadata, blockSettings )
|
||||
);
|
||||
}
|
||||
|
||||
unregister( blockName: string ): boolean {
|
||||
return Boolean( unregisterBlockType( blockName ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy for BlockVariation
|
||||
export class BlockVariationStrategy implements BlockRegistrationStrategy {
|
||||
register(
|
||||
blockName: string,
|
||||
blockSettings: Partial< BlockConfiguration >
|
||||
): boolean {
|
||||
return Boolean(
|
||||
registerBlockVariation(
|
||||
blockName,
|
||||
blockSettings as BlockVariation< BlockAttributes >
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
unregister( blockName: string, variationName: string ): boolean {
|
||||
return Boolean( unregisterBlockVariation( blockName, variationName ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { getBlockType } from '@wordpress/blocks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
TemplateChangeDetector,
|
||||
TemplateChangeDetectorObserver,
|
||||
} from './template-change-detector';
|
||||
import {
|
||||
BlockRegistrationStrategy,
|
||||
BlockTypeStrategy,
|
||||
BlockVariationStrategy,
|
||||
} from './block-registration-strategy';
|
||||
import { BLOCKS_WITH_RESTRICTION } from './blocks-with-restriction';
|
||||
|
||||
/**
|
||||
* Manages the registration and unregistration of blocks based on template or page restrictions.
|
||||
*
|
||||
* This class implements the TemplateChangeDetectorObserver interface and is responsible for managing the registration and unregistration of blocks based on the restrictions defined in the BLOCKS_WITH_RESTRICTION constant.
|
||||
*
|
||||
* The class maintains a list of unregistered blocks and uses a block registration strategy to register and unregister blocks as needed. The strategy used depends on whether the block is a variation block or a regular block.
|
||||
*
|
||||
* The `run` method is the main entry point for the class. It is called with a TemplateChangeDetector object and registers and unregisters blocks based on the current template and whether the editor is in post or page mode.
|
||||
*/
|
||||
export class BlockRegistrationManager
|
||||
implements TemplateChangeDetectorObserver
|
||||
{
|
||||
private unregisteredBlocks: string[] = [];
|
||||
private blockRegistrationStrategy: BlockRegistrationStrategy;
|
||||
|
||||
constructor() {
|
||||
this.blockRegistrationStrategy = new BlockTypeStrategy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a block should be registered based on the current template or page.
|
||||
*
|
||||
* This method checks whether a block with restrictions should be registered based on the current template ID and
|
||||
* whether the editor is in post or page mode. It checks whether the current template ID starts with any of the
|
||||
* allowed templates or template parts for the block, and whether the block is available in the post or page editor.
|
||||
*
|
||||
* @param {Object} params - The parameters for the method.
|
||||
* @param {string} params.blockWithRestrictionName - The name of the block with restrictions.
|
||||
* @param {string} params.currentTemplateId - The ID of the current template.
|
||||
* @param {boolean} params.isPostOrPage - Whether the editor is in a post or page.
|
||||
* @return {boolean} True if the block should be registered, false otherwise.
|
||||
*/
|
||||
private shouldBlockBeRegistered( {
|
||||
blockWithRestrictionName,
|
||||
currentTemplateId,
|
||||
isPostOrPage,
|
||||
}: {
|
||||
blockWithRestrictionName: string;
|
||||
currentTemplateId: string;
|
||||
isPostOrPage: boolean;
|
||||
} ) {
|
||||
const {
|
||||
allowedTemplates,
|
||||
allowedTemplateParts,
|
||||
availableInPostOrPageEditor,
|
||||
} = BLOCKS_WITH_RESTRICTION[ blockWithRestrictionName ];
|
||||
const shouldBeAvailableOnTemplate = Object.keys(
|
||||
allowedTemplates
|
||||
).some( ( allowedTemplate ) =>
|
||||
currentTemplateId.startsWith( allowedTemplate )
|
||||
);
|
||||
const shouldBeAvailableOnTemplatePart = Object.keys(
|
||||
allowedTemplateParts
|
||||
).some( ( allowedTemplate ) =>
|
||||
currentTemplateId.startsWith( allowedTemplate )
|
||||
);
|
||||
const shouldBeAvailableOnPostOrPageEditor =
|
||||
isPostOrPage && availableInPostOrPageEditor;
|
||||
|
||||
return (
|
||||
shouldBeAvailableOnTemplate ||
|
||||
shouldBeAvailableOnTemplatePart ||
|
||||
shouldBeAvailableOnPostOrPageEditor
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters blocks before entering a restricted area based on the current template or page/post.
|
||||
*
|
||||
* This method iterates over all blocks with restrictions and unregisters them if they should not be registered
|
||||
* based on the current template ID and whether the editor is in a post or page. It uses a block registration
|
||||
* strategy to unregister the blocks, which depends on whether the block is a variation block or a regular block.
|
||||
*
|
||||
* @param {Object} params - The parameters for the method.
|
||||
* @param {string} params.currentTemplateId - The ID of the current template.
|
||||
* @param {boolean} params.isPostOrPage - Whether the editor is in post or page mode.
|
||||
*/
|
||||
unregisterBlocksBeforeEnteringRestrictedArea( {
|
||||
currentTemplateId,
|
||||
isPostOrPage,
|
||||
}: {
|
||||
currentTemplateId: string;
|
||||
isPostOrPage: boolean;
|
||||
} ) {
|
||||
for ( const blockWithRestrictionName of Object.keys(
|
||||
BLOCKS_WITH_RESTRICTION
|
||||
) ) {
|
||||
if (
|
||||
this.shouldBlockBeRegistered( {
|
||||
blockWithRestrictionName,
|
||||
currentTemplateId,
|
||||
isPostOrPage,
|
||||
} )
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! getBlockType( blockWithRestrictionName ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.blockRegistrationStrategy = BLOCKS_WITH_RESTRICTION[
|
||||
blockWithRestrictionName
|
||||
].isVariationBlock
|
||||
? new BlockVariationStrategy()
|
||||
: new BlockTypeStrategy();
|
||||
|
||||
this.blockRegistrationStrategy.unregister(
|
||||
blockWithRestrictionName
|
||||
);
|
||||
this.unregisteredBlocks.push( blockWithRestrictionName );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers blocks after leaving a restricted area.
|
||||
*
|
||||
* This method iterates over all unregistered blocks and registers them if they are not restricted in the current context.
|
||||
* It uses a block registration strategy to register the blocks, which depends on whether the block is a variation block or a regular block.
|
||||
* If the block is successfully registered, it is removed from the list of unregistered blocks.
|
||||
*/
|
||||
registerBlocksAfterLeavingRestrictedArea() {
|
||||
for ( const unregisteredBlockName of this.unregisteredBlocks ) {
|
||||
if ( ! getBlockType( unregisteredBlockName ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const restrictedBlockData =
|
||||
BLOCKS_WITH_RESTRICTION[ unregisteredBlockName ];
|
||||
this.blockRegistrationStrategy = BLOCKS_WITH_RESTRICTION[
|
||||
unregisteredBlockName
|
||||
].isVariationBlock
|
||||
? new BlockVariationStrategy()
|
||||
: new BlockTypeStrategy();
|
||||
const isBlockRegistered = this.blockRegistrationStrategy.register(
|
||||
restrictedBlockData.blockMetadata,
|
||||
restrictedBlockData.blockSettings
|
||||
);
|
||||
this.unregisteredBlocks = isBlockRegistered
|
||||
? this.unregisteredBlocks.filter(
|
||||
( blockName ) => blockName !== unregisteredBlockName
|
||||
)
|
||||
: this.unregisteredBlocks;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the block registration manager.
|
||||
*
|
||||
* This method is the main entry point for the block registration manager. It is called with a TemplateChangeDetector object,
|
||||
* and registers and unregisters blocks based on the current template and whether the editor is in a post or page.
|
||||
*
|
||||
* @param {TemplateChangeDetector} templateChangeDetector - The template change detector object.
|
||||
*/
|
||||
run( templateChangeDetector: TemplateChangeDetector ) {
|
||||
this.registerBlocksAfterLeavingRestrictedArea();
|
||||
this.unregisterBlocksBeforeEnteringRestrictedArea( {
|
||||
currentTemplateId:
|
||||
templateChangeDetector.getCurrentTemplateId() || '',
|
||||
isPostOrPage: templateChangeDetector.getIsPostOrPage(),
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { BlockConfiguration } from '@wordpress/blocks';
|
||||
import { ProductGalleryBlockSettings } from '@woocommerce/blocks/product-gallery/settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import productGalleryBlockMetadata from '../../../blocks/product-gallery/block.json';
|
||||
|
||||
export interface BlocksWithRestriction {
|
||||
[ key: string ]: {
|
||||
blockMetadata: Partial< BlockConfiguration >;
|
||||
blockSettings: Partial< BlockConfiguration >;
|
||||
allowedTemplates: {
|
||||
[ key: string ]: boolean;
|
||||
};
|
||||
allowedTemplateParts: {
|
||||
[ key: string ]: boolean;
|
||||
};
|
||||
availableInPostOrPageEditor: boolean;
|
||||
isVariationBlock: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error: `metadata` currently does not have a type definition in WordPress core
|
||||
export const BLOCKS_WITH_RESTRICTION: BlocksWithRestriction = {
|
||||
[ productGalleryBlockMetadata.name ]: {
|
||||
blockMetadata: productGalleryBlockMetadata,
|
||||
blockSettings: ProductGalleryBlockSettings,
|
||||
allowedTemplates: {
|
||||
'single-product': true,
|
||||
},
|
||||
allowedTemplateParts: {
|
||||
'product-gallery': true,
|
||||
},
|
||||
availableInPostOrPageEditor: false,
|
||||
isVariationBlock: false,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import domReady from '@wordpress/dom-ready';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { BlockRegistrationManager } from './blocks-registration-manager';
|
||||
import { TemplateChangeDetector } from './template-change-detector';
|
||||
|
||||
domReady( () => {
|
||||
const templateChangeDetector = new TemplateChangeDetector();
|
||||
const blockRegistrationManager = new BlockRegistrationManager();
|
||||
templateChangeDetector.add( blockRegistrationManager );
|
||||
} );
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { subscribe, select } from '@wordpress/data';
|
||||
import { isNumber } from '@woocommerce/types';
|
||||
|
||||
interface TemplateChangeDetectorSubject {
|
||||
add( observer: TemplateChangeDetectorObserver ): void;
|
||||
getPreviousTemplateId(): string | undefined;
|
||||
getCurrentTemplateId(): string | undefined;
|
||||
notify(): void;
|
||||
}
|
||||
|
||||
export interface TemplateChangeDetectorObserver {
|
||||
run( subject: TemplateChangeDetectorSubject ): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements the TemplateChangeDetectorSubject interface and is responsible for detecting changes in the
|
||||
* current template or page and notifying any observers of these changes. It maintains a list of observers and provides methods
|
||||
* to add observers and notify them of changes.
|
||||
*
|
||||
* The class also provides methods to get the previous and current template IDs and whether the editor is in a post or page.
|
||||
*
|
||||
* The `checkIfTemplateHasChangedAndNotifySubscribers` method is the main method of the class. It checks if the current
|
||||
* template has changed and, if so, notifies all observers.
|
||||
*/
|
||||
export class TemplateChangeDetector implements TemplateChangeDetectorSubject {
|
||||
private previousTemplateId: string | undefined;
|
||||
private currentTemplateId: string | undefined;
|
||||
private isPostOrPage: boolean;
|
||||
|
||||
private observers: TemplateChangeDetectorObserver[] = [];
|
||||
|
||||
constructor() {
|
||||
this.isPostOrPage = false;
|
||||
subscribe( () => {
|
||||
this.checkIfTemplateHasChangedAndNotifySubscribers();
|
||||
}, 'core/edit-site' );
|
||||
}
|
||||
|
||||
public add( observer: TemplateChangeDetectorObserver ): void {
|
||||
this.observers.push( observer );
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger an update in each subscriber.
|
||||
*/
|
||||
public notify(): void {
|
||||
for ( const observer of this.observers ) {
|
||||
observer.run( this );
|
||||
}
|
||||
}
|
||||
|
||||
public getPreviousTemplateId() {
|
||||
return this.previousTemplateId;
|
||||
}
|
||||
|
||||
public getCurrentTemplateId() {
|
||||
return this.currentTemplateId;
|
||||
}
|
||||
|
||||
public getIsPostOrPage() {
|
||||
return this.isPostOrPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the template ID.
|
||||
*
|
||||
* This method takes a template or a post ID and returns it parsed in the expected format.
|
||||
*
|
||||
* @param {string | number | undefined} templateId - The template ID to parse.
|
||||
* @return {string | undefined} The parsed template ID.
|
||||
*/
|
||||
private parseTemplateId(
|
||||
templateId: string | number | undefined
|
||||
): string | undefined {
|
||||
if ( isNumber( templateId ) ) {
|
||||
return String( templateId );
|
||||
}
|
||||
return templateId?.split( '//' )[ 1 ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current template or page has changed and notifies subscribers.
|
||||
*
|
||||
* If the current template ID has changed and is not undefined (which means that it is not a page, post or template), it notifies all subscribers.
|
||||
*/
|
||||
public checkIfTemplateHasChangedAndNotifySubscribers(): void {
|
||||
this.previousTemplateId = this.currentTemplateId;
|
||||
|
||||
const postOrPageId = select( 'core/editor' )?.getCurrentPostId<
|
||||
string | number | undefined
|
||||
>();
|
||||
|
||||
this.isPostOrPage = Boolean( postOrPageId );
|
||||
|
||||
const editedPostId =
|
||||
postOrPageId ||
|
||||
select( 'core/edit-site' )?.getEditedPostId<
|
||||
string | number | undefined
|
||||
>();
|
||||
this.currentTemplateId = this.parseTemplateId( editedPostId );
|
||||
|
||||
const hasChangedTemplate =
|
||||
this.previousTemplateId !== this.currentTemplateId;
|
||||
const hasTemplateId = Boolean( this.currentTemplateId );
|
||||
|
||||
if ( ! hasChangedTemplate || ! hasTemplateId ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export const registerBlockSingleProductTemplate = ( {
|
||||
|
||||
// With GB 16.3.0 the return type can be a number: https://github.com/WordPress/gutenberg/issues/53230
|
||||
currentTemplateId = parseTemplateId(
|
||||
store?.getEditedPostId() as string | number | undefined
|
||||
store?.getEditedPostId< string | number | undefined >()
|
||||
);
|
||||
const hasChangedTemplate = previousTemplateId !== currentTemplateId;
|
||||
const hasTemplateId = Boolean( currentTemplateId );
|
||||
|
||||
Reference in New Issue
Block a user