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

Plugin: Implement block registering API #289

Merged
merged 24 commits into from
Mar 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8b8488c
Plugin: Implement block registering API
youknowriad Mar 17, 2017
203c2ba
Changes per review
youknowriad Mar 17, 2017
dfa146d
Remove namespace from PHP `register_block` stub
nylen Mar 17, 2017
e09b932
Add .nvmrc
nylen Mar 17, 2017
f81c876
Ignore yarn.lock
nylen Mar 17, 2017
9306751
Tweak docblock
nylen Mar 17, 2017
99b5fc3
Move block name validation to a separate function
nylen Mar 17, 2017
dd6e008
Add babel-plugin-transform-runtime
nylen Mar 17, 2017
43f241e
Add Mocha unit tests
nylen Mar 17, 2017
1fc3651
Bring back "modules": false to .babelrc
nylen Mar 17, 2017
3879318
Whitespace fix
nylen Mar 17, 2017
6e9986e
Rename "name" to "slug"
nylen Mar 17, 2017
086f993
- Using the env to config webpack for tests
youknowriad Mar 20, 2017
a7c6660
Fix tests after rebase
youknowriad Mar 20, 2017
59d3ea0
Add each submodule as modules root
aduth Mar 20, 2017
ee2b07f
Merge branch 'master' into update/implement-block-registering
youknowriad Mar 21, 2017
7482ebc
Plugin: Fix test runner. Use babel-register instead of mocha-webpack
youknowriad Mar 21, 2017
bce66e9
Remove unused dependency
aduth Mar 21, 2017
0f1ea2a
Avoid assigning globals
aduth Mar 21, 2017
a512271
Add `unregisterBlock` and resulting improvements:
nylen Mar 21, 2017
3f3469e
Remove .tmp from .gitignore
nylen Mar 21, 2017
65066e1
Exclude node modules from Webpack JS rules
aduth Mar 22, 2017
b95c775
Fix lint issues and correctly setup the `npm test` command
youknowriad Mar 22, 2017
e5f72ca
Fix registering the example text block
youknowriad Mar 22, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,13 @@
"modules": false
}
} ]
]
],
"plugins": [
"transform-runtime"
],
"env": {
"test": {
"presets": [ "latest" ]
}
}
}
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
modules/*/build
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"extends": "wordpress",
"env": {
"browser": true,
"node": true
"node": true,
"mocha": true
},
"parserOptions": {
"sourceType": "module"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
build
*.log
yarn.lock
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v6.10.0
7 changes: 7 additions & 0 deletions bootstrap-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* External dependencies
*/
import chai from 'chai';
import dirtyChai from 'dirty-chai';

chai.use( dirtyChai );
12 changes: 6 additions & 6 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ function the_gutenberg_project() {
/**
* Registers a block.
*
* @param string $namespace Block grouping unique to package or plugin.
* @param string $block Block name.
* @param array $args Optional. Array of settings for the block. Default empty array.
* @return bool True on success, false on error.
* @param string $name Block name including namespace.
* @param array $args Optional. Array of settings for the block. Default
* empty array.
* @return bool True on success, false on error.
*/
function register_block( $namespace, $block, $args = array() ) {

function register_block( $name, $args = array() ) {
// Not implemented yet.
}
56 changes: 45 additions & 11 deletions modules/blocks/index.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,66 @@
export { default as Editable } from './components/editable';

/**
* Block settings keyed by block slug.
*
* @var {Object} blocks
*/
const blocks = {};

/**
* Registers a block.
*
* @param {string} namespace Block grouping unique to package or plugin
* @param {string} block Block name
* @param {Object} settings Block settings
* @param {string} slug Block slug
* @param {Object} settings Block settings
*/
export function registerBlock( namespace, block, settings ) {
export function registerBlock( slug, settings ) {
if ( typeof slug !== 'string' ) {
throw new Error(
'Block slugs must be strings.'
);
}
if ( ! /^[a-z0-9-]+\/[a-z0-9-]+$/.test( slug ) ) {
throw new Error(
'Block slugs must contain a namespace prefix. Example: my-plugin/my-custom-block'
);
}
if ( blocks[ slug ] ) {
throw new Error(
'Block "' + slug + '" is already registered.'
);
}
blocks[ slug ] = Object.assign( { slug }, settings );
}

/**
* Unregisters a block.
*
* @param {string} slug Block slug
*/
export function unregisterBlock( slug ) {
if ( ! blocks[ slug ] ) {
throw new Error(
'Block "' + slug + '" is not registered.'
);
}
delete blocks[ slug ];
}

/**
* Returns settings associated with a block.
*
* @param {string} namespace Block grouping unique to package or plugin
* @param {string} block Block name
* @return {?Object} Block settings
* @param {string} slug Block slug
* @return {?Object} Block settings
*/
export function getBlockSettings( namespace, block ) {

export function getBlockSettings( slug ) {
return blocks[ slug ];
}

/**
* Returns all registered blocks.
*
* @return {Object} Block settings keyed by block name
* @return {Array} Block settings
*/
export function getBlocks() {

return Object.values( blocks );
}
112 changes: 112 additions & 0 deletions modules/blocks/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* External dependencies
*/
import { expect } from 'chai';

/**
* Internal dependencies
*/
import * as blocks from '../';

describe( 'blocks API', () => {
// Reset block state before each test.
beforeEach( () => {
blocks.getBlocks().forEach( block => {
blocks.unregisterBlock( block.slug );
} );
} );

describe( 'registerBlock', () => {
it( 'should reject numbers', () => {
expect(
() => blocks.registerBlock( 999 )
).to.throw( 'Block slugs must be strings.' );
} );

it( 'should reject blocks without a namespace', () => {
expect(
() => blocks.registerBlock( 'doing-it-wrong' )
).to.throw( /^Block slugs must contain a namespace prefix/ );
} );

it( 'should reject blocks with invalid characters', () => {
expect(
() => blocks.registerBlock( 'still/_doing_it_wrong' )
).to.throw( /^Block slugs must contain a namespace prefix/ );
} );

it( 'should accept valid block names', () => {
expect(
() => blocks.registerBlock( 'my-plugin/fancy-block-4' )
).not.to.throw();
} );

it( 'should prohibit registering the same block twice', () => {
blocks.registerBlock( 'core/test-block' );
expect(
() => blocks.registerBlock( 'core/test-block' )
).to.throw( 'Block "core/test-block" is already registered.' );
} );

it( 'should store a copy of block settings', () => {
const blockSettings = { settingName: 'settingValue' };
blocks.registerBlock( 'core/test-block-with-settings', blockSettings );
blockSettings.mutated = true;
expect( blocks.getBlockSettings( 'core/test-block-with-settings' ) ).to.eql( {
slug: 'core/test-block-with-settings',
settingName: 'settingValue',
} );
} );
} );

describe( 'unregisterBlock', () => {
it( 'should fail if a block is not registered', () => {
expect(
() => blocks.unregisterBlock( 'core/test-block' )
).to.throw( 'Block "core/test-block" is not registered.' );
} );

it( 'should unregister existing blocks', () => {
blocks.registerBlock( 'core/test-block' );
expect( blocks.getBlocks() ).to.eql( [
{ slug: 'core/test-block' },
] );
blocks.unregisterBlock( 'core/test-block' );
expect( blocks.getBlocks() ).to.eql( [] );
} );
} );

describe( 'getBlockSettings', () => {
it( 'should return { slug } for blocks with no settings', () => {
blocks.registerBlock( 'core/test-block' );
expect( blocks.getBlockSettings( 'core/test-block' ) ).to.eql( {
slug: 'core/test-block',
} );
} );

it( 'should return all block settings', () => {
const blockSettings = { settingName: 'settingValue' };
blocks.registerBlock( 'core/test-block-with-settings', blockSettings );
expect( blocks.getBlockSettings( 'core/test-block-with-settings' ) ).to.eql( {
slug: 'core/test-block-with-settings',
settingName: 'settingValue',
} );
} );
} );

describe( 'getBlocks', () => {
it( 'should return an empty array at first', () => {
expect( blocks.getBlocks() ).to.eql( [] );
} );

it( 'should return all registered blocks', () => {
blocks.registerBlock( 'core/test-block' );
const blockSettings = { settingName: 'settingValue' };
blocks.registerBlock( 'core/test-block-with-settings', blockSettings );
expect( blocks.getBlocks() ).to.eql( [
{ slug: 'core/test-block' },
{ slug: 'core/test-block-with-settings', settingName: 'settingValue' },
] );
} );
} );
} );
2 changes: 1 addition & 1 deletion modules/editor/blocks/text-block/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
wp.blocks.registerBlock( 'wp', 'Text', {
wp.blocks.registerBlock( 'wp/text', {
edit( state, onChange ) {
return wp.element.createElement( wp.blocks.Editable, {
value: state.value,
Expand Down
10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,25 @@
"editor"
],
"scripts": {
"test-unit": "cross-env NODE_ENV=test mocha modules/**/test/*.js --compilers js:babel-register --recursive --require ./bootstrap-test.js",
"build": "cross-env NODE_ENV=production webpack",
"lint": "eslint editor",
"test": "npm run lint"
"lint": "eslint modules",
"test": "npm run lint && npm run test-unit"
},
"devDependencies": {
"autoprefixer": "^6.7.7",
"babel-core": "^6.24.0",
"babel-loader": "^6.4.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-latest": "^6.24.0",
"babel-register": "^6.24.0",
"chai": "^3.5.0",
"cross-env": "^3.2.4",
"css-loader": "^0.27.3",
"dirty-chai": "^1.2.2",
"eslint": "^3.17.1",
"eslint-config-wordpress": "^1.1.0",
"mocha": "^3.2.0",
"node-sass": "^4.5.0",
"postcss-loader": "^1.3.3",
"sass-loader": "^6.0.3",
Expand Down
15 changes: 12 additions & 3 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const entry = fs.readdirSync( BASE_PATH ).reduce( ( memo, filename ) => {
return memo;
}, {} );

const config = module.exports = {
const config = {
entry: entry,
output: {
filename: '[name]/build/index.js',
Expand All @@ -39,15 +39,17 @@ const config = module.exports = {
},
resolve: {
modules: [
'editor',
'external',
...Object.keys( entry ).map( ( filename ) => {
return path.join( BASE_PATH, filename );
} ),
'node_modules'
]
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
Expand All @@ -62,6 +64,11 @@ const config = module.exports = {
]
},
plugins: [
new webpack.DefinePlugin( {
'process.env': {
NODE_ENV: JSON.stringify( process.env.NODE_ENV )
}
} ),
new webpack.LoaderOptionsPlugin( {
minimize: process.env.NODE_ENV === 'production',
debug: process.env.NODE_ENV !== 'production',
Expand All @@ -79,3 +86,5 @@ if ( 'production' === process.env.NODE_ENV ) {
} else {
config.devtool = 'source-map';
}

module.exports = config;