diff --git a/blocks/api/categories.js b/blocks/api/categories.js index 2a76e170cb378..f3f44f3c19fd7 100644 --- a/blocks/api/categories.js +++ b/blocks/api/categories.js @@ -1,3 +1,5 @@ +/* eslint no-console: [ 'error', { allow: [ 'error' ] } ] */ + /** * WordPress dependencies */ @@ -20,6 +22,14 @@ const categories = [ { slug: 'reusable-blocks', title: __( 'Saved Blocks' ) }, ]; +/** + * @type {RegExp} + * @const + * + * Category names must be a combination of lower-case letters, numbers, and hypens + */ +const categoryNamePattern = /^[a-z0-9-]+$/; + /** * Returns all the block categories. * @@ -28,3 +38,48 @@ const categories = [ export function getCategories() { return categories; } + +/** + * Register a new block Category. + * + * @param {Array} category e.g {slug: 'custom', title: __('Custom Blocks')} + * + * @return {Array} categories + */ +export function registerCategory( category ) { + if ( ! category ) { + console.error( + 'The block Category must be defined' + ); + return; + } + if ( ! category.slug ) { + console.error( + 'The block Category slug must be defined' + ); + return; + } + if ( ! categoryNamePattern.test( category.slug ) ) { + console.error( + 'The block Category slug must not contain characters which are invalid for urls' + ); + return; + } + + if ( categories.some( x => x.slug === category.slug ) ) { + console.error( + 'The block Category "' + category.slug + '" is already registered' + ); + return; + } + if ( ! category.title ) { + console.error( + 'The block Category title must be defined' + ); + return; + } + + categories.push( category ); + return categories; +} + diff --git a/blocks/api/index.js b/blocks/api/index.js index 7f0653a133c5b..32d95bec5a6b1 100644 --- a/blocks/api/index.js +++ b/blocks/api/index.js @@ -8,7 +8,10 @@ export { getSaveElement, } from './serializer'; export { isValidBlock } from './validation'; -export { getCategories } from './categories'; +export { + getCategories, + registerCategory, +} from './categories'; export { registerBlockType, unregisterBlockType, diff --git a/blocks/api/test/categories.js b/blocks/api/test/categories.js new file mode 100644 index 0000000000000..f4e947f507b29 --- /dev/null +++ b/blocks/api/test/categories.js @@ -0,0 +1,69 @@ +/* eslint-disable no-console */ + +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { + registerCategory, +} from '../categories'; + +describe( 'categories', () => { + const error = console.error; + + // Reset block state before each test. + beforeEach( () => { + console.error = jest.fn(); + } ); + + afterEach( () => { + console.error = error; + } ); + + describe( 'registerCategory()', () => { + it( 'should reject empty categories', () => { + const categories = registerCategory(); + expect( console.error ).toHaveBeenCalledWith( 'The block Category must be defined' ); + expect( categories ).toBeUndefined(); + } ); + + it( 'should reject categories with empty slug', () => { + const categories = registerCategory( { slug: '', title: __( 'Custom Blocks' ) } ); + expect( console.error ).toHaveBeenCalledWith( 'The block Category slug must be defined' ); + expect( categories ).toBeUndefined(); + } ); + + it( 'should reject categories with slug not defined', () => { + const categories = registerCategory( { title: __( 'Custom Blocks' ) } ); + expect( console.error ).toHaveBeenCalledWith( 'The block Category slug must be defined' ); + expect( categories ).toBeUndefined(); + } ); + + it( 'should reject categories with invalid slug', () => { + const categories = registerCategory( { slug: 'custom blocks', title: __( 'Custom Blocks' ) } ); + expect( console.error ).toHaveBeenCalledWith( 'The block Category slug must not contain characters which are invalid for urls' ); + expect( categories ).toBeUndefined(); + } ); + + it( 'should reject categories with empty title', () => { + const categories = registerCategory( { slug: 'custom-blocks', title: '' } ); + expect( console.error ).toHaveBeenCalledWith( 'The block Category title must be defined' ); + expect( categories ).toBeUndefined(); + } ); + + it( 'should store the new category', () => { + const categories = registerCategory( { slug: 'custom-blocks', title: 'Custom Blocks' } ); + expect( categories ).toEqual( jasmine.arrayContaining( [ { slug: 'custom-blocks', title: 'Custom Blocks' } ] ) ); + } ); + + it( 'should reject categories already registered', () => { + const categories = registerCategory( { slug: 'custom-blocks', title: 'Custom Blocks' } ); + expect( console.error ).toHaveBeenCalledWith( 'The block Category "custom-blocks" is already registered' ); + expect( categories ).toBeUndefined(); + } ); + } ); +} );