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

Editor: Added slug selector #4802

Merged
merged 4 commits into from
Feb 2, 2018
Merged

Editor: Added slug selector #4802

merged 4 commits into from
Feb 2, 2018

Conversation

abotteram
Copy link
Contributor

@abotteram abotteram commented Feb 1, 2018

Description

Adds selector for slug.

How Has This Been Tested?

Added unit tests.
Ran wp.data.select( "core/editor", "getCurrentPostSlug" ); in the console.

Types of changes

Addition to the selector API.

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code has proper inline documentation.

@atimmer
Copy link
Member

atimmer commented Feb 1, 2018

Will this work together with #3418? We might have to use getEditedPostAttribute.

@abotteram
Copy link
Contributor Author

abotteram commented Feb 1, 2018

@atimmer Makes sense, refactored.

Copy link
Member

@atimmer atimmer left a comment

Choose a reason for hiding this comment

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

It still needs to be added here: https://github.com/WordPress/gutenberg/blob/master/editor/store/index.js#L29.

Otherwise, it looks good.

@@ -200,7 +211,7 @@ export function getCurrentPostLastRevisionId( state ) {
* @returns {Object} Object of key value pairs comprising unsaved edits.
*/
export function getPostEdits( state ) {
return state.editor.present.edits;
return get( state, [ 'editor', 'present', 'edits' ], {} );
Copy link
Member

Choose a reason for hiding this comment

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

Just noting that you can use also string as 2nd param:

return get( state, 'editor.present.edits', {} );

See 3rd example in here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@gziolo I know, this is a small performance consideration because lodash won't have to parse the string.

Copy link
Member

Choose a reason for hiding this comment

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

Hehe, more keystrokes to save some CPU time. They should include lodash into JS engine by default and optimize that for us or teach Babel to do the transformation from string to array!

Copy link
Member

Choose a reason for hiding this comment

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

Just noting that you can use also string as 2nd param:

We seem to be encouraging the opposite thing, as I tend to leave the inverse as a suggestion whenever possible 😉 Beside the performance advantage, I find it trivially more legible by virtue of greater separation between the properties.

Copy link
Member

Choose a reason for hiding this comment

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

Aside: Why would state.editor.present.edits ever be undefined?

(I hope the answer is not "because tests")

Copy link
Member

Choose a reason for hiding this comment

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

Further, if this triggers the default, since it's not a shared reference, two subsequent invocations of this function would not be strictly equal and therefore trigger re-renders on any connected components relying on the result of this function:

const state = {};
getPostEdits( state ) !== getPostEdits( state );

Copy link
Member

Choose a reason for hiding this comment

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

That's a good catch about having {} as the default value. The same would apply to [].

Copy link
Member

Choose a reason for hiding this comment

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

One example where I worked around this was in getBlockOrder:

/**
* Shared reference to an empty array used as the default block order return
* value when the state value is not explicitly assigned, since we want to
* avoid returning a new array reference on every invocation.
*
* @type {Array}
*/
const DEFAULT_BLOCK_ORDER = [];

/**
* Returns an array containing all block unique IDs of the post being edited,
* in the order they appear in the post. Optionally accepts a root UID of the
* block list for which the order should be returned, defaulting to the top-
* level block order.
*
* @param {Object} state Global application state.
* @param {?string} rootUID Optional root UID of block list.
*
* @return {Array} Ordered unique IDs of post blocks.
*/
export function getBlockOrder( state, rootUID ) {
return state.editor.present.blockOrder[ rootUID || '' ] || DEFAULT_BLOCK_ORDER;
}

I'd not love if we start having many constants at the top of this file, but we also should try to avoid these fallbacks whenever possible (instead relying on the value reference living in state as the result of the reducer).

Copy link
Member

Choose a reason for hiding this comment

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

There could be 2 constants, one for an empty array and one for an empty object. However I second your opinion that reducers should handle it.

@@ -214,9 +225,10 @@ export function getPostEdits( state ) {
* @returns {*} Post attribute value.
*/
export function getEditedPostAttribute( state, attributeName ) {
return state.editor.present.edits[ attributeName ] === undefined ?
const edits = getPostEdits( state );
Copy link
Member

Choose a reason for hiding this comment

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

Nice 👍

@@ -375,6 +376,31 @@ describe( 'selectors', () => {
} );
} );

describe( 'getCurrentPostSlug', () => {
it( 'should return the current post\'s slug is no edits have been made', () => {
Copy link
Member

@gziolo gziolo Feb 2, 2018

Choose a reason for hiding this comment

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

typo: slug *is* no edits -> slug if no edits

@gziolo
Copy link
Member

gziolo commented Feb 2, 2018

Looks good, nice work on this one. I would wait for @youknowriad to perform a final check. I'm still learning how this should be exposed to make it scalable. We might want also to document all exposed selectors in our docs.

@gziolo gziolo added the [Feature] Extensibility The ability to extend blocks or the editing experience label Feb 2, 2018
@youknowriad
Copy link
Contributor

👍 LGTM Also @aduth proposed that we expose all our selectors (which forces us to ensure backwards compatibilty but maybe it's fine)

@gziolo gziolo changed the title Added slug selector Data: Added slug selector Feb 2, 2018
@gziolo gziolo changed the title Data: Added slug selector Editor: Added slug selector Feb 2, 2018
@gziolo gziolo merged commit ef95c64 into WordPress:master Feb 2, 2018
@atimmer atimmer deleted the add/slug-selector branch February 2, 2018 14:25
Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

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

Why do we need a dedicated selector for slug, when getEditedPostAttribute should work just as well?

I'd like to avoid excessive proliferation of selectors, since as implemented with these changes, there's no reason we shouldn't have selectors for the 23 other properties of the edited post (I'm arguing this as a bad thing).

@@ -200,7 +211,7 @@ export function getCurrentPostLastRevisionId( state ) {
* @returns {Object} Object of key value pairs comprising unsaved edits.
*/
export function getPostEdits( state ) {
return state.editor.present.edits;
return get( state, [ 'editor', 'present', 'edits' ], {} );
Copy link
Member

Choose a reason for hiding this comment

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

Just noting that you can use also string as 2nd param:

We seem to be encouraging the opposite thing, as I tend to leave the inverse as a suggestion whenever possible 😉 Beside the performance advantage, I find it trivially more legible by virtue of greater separation between the properties.

@@ -200,7 +211,7 @@ export function getCurrentPostLastRevisionId( state ) {
* @returns {Object} Object of key value pairs comprising unsaved edits.
*/
export function getPostEdits( state ) {
return state.editor.present.edits;
return get( state, [ 'editor', 'present', 'edits' ], {} );
Copy link
Member

Choose a reason for hiding this comment

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

Aside: Why would state.editor.present.edits ever be undefined?

(I hope the answer is not "because tests")

@@ -200,7 +211,7 @@ export function getCurrentPostLastRevisionId( state ) {
* @returns {Object} Object of key value pairs comprising unsaved edits.
*/
export function getPostEdits( state ) {
return state.editor.present.edits;
return get( state, [ 'editor', 'present', 'edits' ], {} );
Copy link
Member

Choose a reason for hiding this comment

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

Further, if this triggers the default, since it's not a shared reference, two subsequent invocations of this function would not be strictly equal and therefore trigger re-renders on any connected components relying on the result of this function:

const state = {};
getPostEdits( state ) !== getPostEdits( state );

@abotteram
Copy link
Contributor Author

@aduth I'm starting work on addressing your concerns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Extensibility The ability to extend blocks or the editing experience
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants