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

Add reusable blocks data layer #3017

Merged
merged 4 commits into from
Nov 7, 2017
Merged

Add reusable blocks data layer #3017

merged 4 commits into from
Nov 7, 2017

Conversation

noisysocks
Copy link
Member

@noisysocks noisysocks commented Oct 13, 2017

Description

Adds the actions, selectors and reducers necessary for supporting reusable blocks. There are 5 actions:

  • FETCH_REUSABLE_BLOCKS—Loads reusable blocks from the API and inserts them into the store.
  • UPDATE_REUSABLE_BLOCK—Inserts and updates a reusable block that is in the store.
  • SAVE_REUSABLE_BLOCK—Persists a reusable block that's in the store to the API.
  • MAKE_BLOCK_STATIC—Transforms a reusable block on the page into a regular block.
  • MAKE_BLOCK_REUSABLE—Transforms a regular block on the page into a reusable block.

#1516 describes the feature and #2081 (#2081 (comment), in particular) describes the technical approach I'm taking.

This PR is a subset of #2659, which was too large. Check it out though if you'd like to play with the complete feature!

How Has This Been Tested?

Unit tests are included for the new actions, selectors and reducers.

I changed the inserter to ignore blocks that are marked with isPrivate: true, so it's worth testing the inserter to make sure that there are no regressions.


cc. @mtias @aduth @youknowriad

❤️

@noisysocks noisysocks mentioned this pull request Oct 13, 2017
Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

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

Left some small comments, but this is in a good shape. Nice work

* @param {string} id If given, only a single reusable block with this ID will be fetched
* @return {Object} Action object
*/
export function fetchReusableBlocks( id = null ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: do we really need the default value?

Copy link
Member Author

Choose a reason for hiding this comment

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

I suppose not! Removed in 7762cb73a84b08f60938bc004cfd1fa20bccf36a.

...newState,
[ reusableBlock.id ]: {
...reusableBlock,
isSaving: false,
Copy link
Contributor

@youknowriad youknowriad Oct 13, 2017

Choose a reason for hiding this comment

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

Why are we merging the isSaving flag with the reusable block object? My first reaction here is: "Should this be in a separate reducer (or subReducer)?

Copy link
Member Author

Choose a reason for hiding this comment

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

No reason other than that it's convenient to pass the block's data and metadata (isSaving) to UI components all in one go.

I'd be happy to move this to a different reducer. Perhaps the shape of the redux tree could be something like:

reusableBlocks: {
    data: {
        'f5ca58b0-aad0-4777-bb10-f3d0fa08a163': /* the block */,
    },
    isSaving: {
        'f5ca58b0-aad0-4777-bb10-f3d0fa08a163': false,
    },
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps the shape of the redux tree could be something like

I like this proposal. Request flags could be generic, it makes sense to me to separate them from the data.

Copy link
Member Author

Choose a reason for hiding this comment

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

👌 done in fb9d4c70a898908eddcf9b71028d16262d171791.

Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

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

Nice work 👍

return reduce( action.reusableBlocks, ( newState, reusableBlock ) => ( {
...newState,
[ reusableBlock.id ]: {
...reusableBlock,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason we're still cloning the object?

const { id } = action;
isSaving( state = {}, action ) {
switch ( action.type ) {
case 'SAVE_REUSABLE_BLOCK': {
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess the scoping ({) is not useful anymore

@@ -17,6 +17,7 @@ const categories = [
{ slug: 'layout', title: __( 'Layout Blocks' ) },
{ slug: 'widgets', title: __( 'Widgets' ) },
{ slug: 'embed', title: __( 'Embed' ) },
{ slug: 'reusable-blocks', title: __( 'My Reusable Blocks' ) },
Copy link
Member

Choose a reason for hiding this comment

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

These should probably be allocated in another "tab" (like recent, blocks, embeds) rather than a category. The names should probably be "Saved". cc @jasmussen

Adds the actions, selectors and reducers necessary for supporting reusable
blocks. There are 5 actions:

- FETCH_REUSABLE_BLOCKS: Loads reusable blocks from the API and inserts
  them into the store.
- UPDATE_REUSABLE_BLOCK: Inserts of updates a reusable block that is in
  the store.
- SAVE_REUSABLE_BLOCK: Persists a reusable block that's in the store to
  the API.
- MAKE_BLOCK_STATIC: Transforms a reusable block on the page into a
  regular block.
- MAKE_BLOCK_REUSABLE: Transforms a regular block on the page into a
  reusable block.
Re-shape the Redux state tree so that metadata like `isSaving` is kept
seperately from each reusable block's actual data.
@codecov
Copy link

codecov bot commented Nov 7, 2017

Codecov Report

Merging #3017 into master will increase coverage by 0.24%.
The diff coverage is 96.55%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master   #3017      +/-   ##
=========================================
+ Coverage   31.06%   31.3%   +0.24%     
=========================================
  Files         245     246       +1     
  Lines        6716    6741      +25     
  Branches     1205    1209       +4     
=========================================
+ Hits         2086    2110      +24     
- Misses       3895    3896       +1     
  Partials      735     735
Impacted Files Coverage Δ
blocks/api/categories.js 100% <ø> (ø) ⬆️
editor/reducer.js 90.76% <100%> (+0.64%) ⬆️
editor/inserter/menu.js 49.6% <100%> (+0.4%) ⬆️
editor/actions.js 44.23% <100%> (+5.93%) ⬆️
editor/selectors.js 93.47% <100%> (+0.1%) ⬆️
blocks/api/factory.js 90.62% <100%> (+0.3%) ⬆️
blocks/library/reusable-block/index.js 66.66% <66.66%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e7bdfee...d6e0cf2. Read the comment docs.

@youknowriad youknowriad merged commit fa59bf0 into WordPress:master Nov 7, 2017
@aduth aduth added the [Feature] Synced Patterns Related to synced patterns (formerly reusable blocks) label Dec 19, 2017
*/
export function createReusableBlock( type, attributes ) {
return {
id: uuid(),
Copy link
Member

@aduth aduth Jan 29, 2018

Choose a reason for hiding this comment

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

The apparent overlap yet disconnect between a reusable block's id property and a standard block's uid property is unfortunate. I can see why we might want to call this one id, since it aligns well with how it is modeled on the server (the post's ID), but it makes me wonder whether we should then align the standard block property to id as well. I'm not strongly attached to uid as a property name.

Do you have any thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd be into aligning them so long as we're explicit when writing code that interacts with both types of ID, e.g:

const blockId = block.id;
const reusableBlockId = block.attributes.ref;

This would be a breaking change though, since there might be third party code that references block.uid.

export function createReusableBlock( type, attributes ) {
return {
id: uuid(),
name: __( 'Untitled block' ),
Copy link
Member

@aduth aduth Jan 29, 2018

Choose a reason for hiding this comment

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

Would there ever be a case where we want to create a reusable block and assign its name immediately? A bit awkward to achieve now, since name is not accepted as an argument of the function.

Copy link
Member Author

Choose a reason for hiding this comment

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

No such case was in our design so I figured you aren't gonna need it. We could add an optional argument in the future if it comes up.

Copy link
Member

Choose a reason for hiding this comment

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

I think we should support this and have UI that focus on the name input before saving. cc @jasmussen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Synced Patterns Related to synced patterns (formerly reusable blocks)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants