This repository has been archived by the owner on Feb 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 219
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Product Gallery block: Restrict block to be available only on the Sin…
…gle Product template or the Product Gallery template part (#11664) * WIP: experimenting with strategy pattern for block registration * Add TemplateChangeDetector to BlocksRegistrationManager * Handle blocks registration * Fix issue causing blocks to be registered multiple times * Allow register/unregister blocks when on pages or posts * Add BlockRegistrationStrategy logic * Fix import error * Add doc comments for BlockRegistrationManager class * Add doc comments to TemplateChangeDetector class * Fix eslint errors * Import domReady from @wordpress/dom-ready * Prevent error when using blockName for registerBlockType function * Add e2e tests to check for block availability in different contexts * Add e2e tests to cover block availability on different contexts
- Loading branch information
1 parent
879b1d2
commit 83e5b64
Showing
11 changed files
with
462 additions
and
9 deletions.
There are no files selected for viewing
55 changes: 55 additions & 0 deletions
55
assets/js/atomic/utils/blocks-registration-manager/block-registration-strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ) ); | ||
} | ||
} |
168 changes: 168 additions & 0 deletions
168
assets/js/atomic/utils/blocks-registration-manager/blocks-registration-manager.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
/** | ||
* 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; | ||
} | ||
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 ) { | ||
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(), | ||
} ); | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
assets/js/atomic/utils/blocks-registration-manager/blocks-with-restriction.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
}, | ||
}; |
16 changes: 16 additions & 0 deletions
16
assets/js/atomic/utils/blocks-registration-manager/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ); | ||
} ); |
Oops, something went wrong.