-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Migrate site editor to use new preferences package #39158
Conversation
Near the end of my day and I haven't tested this yet, and also tests are likely to fail, so keeping this a draft until I can finish it off tomorrow. |
Size Change: +3.11 kB (0%) Total Size: 1.16 MB
ℹ️ View Unchanged
|
3e23219
to
6088be3
Compare
/** | ||
* Helper for getting a preference from the preferences store. | ||
* | ||
* This is only present so that `getSettings` doesn't need to be made a | ||
* registry selector. | ||
* | ||
* It's unstable because the selector needs to be exported and so part of the | ||
* public API to work. | ||
*/ | ||
export const __unstableGetPreference = createRegistrySelector( | ||
( select ) => ( state, name ) => | ||
select( preferencesStore ).get( 'core/edit-site', name ) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the part I'm most unhappy with. Ideally any helper is internal only, but the registry selector needs to be exported to work.
The separate selector is also needed for the memoized getSettings
selector.
If there's a different way to do this that anyone knows of, that'd be great.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we may just have to inline this function so that
isFeatureActive
,getSettings
are registry selectors that callselect( preferencesStore ).get
directly.
If you insist on having a getPreference
for code re-use (I'd argue it's maybe not worth it since it's only one line) then getPreference
could accept select
as an argument.
const getPreference = ( select, name ) =>
select( preferencesStore ).get( 'core/edit-site', name );
export const isFeatureActive = createRegistrySelector(
( select ) => ( state, featureName ) => {
return !! getPreference( select, featureName );
}
);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing that makes it difficult is the second argument to createSelector
(which is used for memoizing getSettings
) is one of the places __unstableGetPreference
needs to be used. That has no way to access select
from what I can see:
gutenberg/packages/edit-site/src/store/selectors.js
Lines 105 to 151 in c366a96
/** | |
* Returns the settings, taking into account active features and permissions. | |
* | |
* @param {Object} state Global application state. | |
* @param {Function} setIsInserterOpen Setter for the open state of the global inserter. | |
* | |
* @return {Object} Settings. | |
*/ | |
export const getSettings = createSelector( | |
( state, setIsInserterOpen ) => { | |
const settings = { | |
...state.settings, | |
outlineMode: true, | |
focusMode: !! __unstableGetPreference( state, 'focusMode' ), | |
hasFixedToolbar: !! __unstableGetPreference( | |
state, | |
'fixedToolbar' | |
), | |
__experimentalSetIsInserterOpened: setIsInserterOpen, | |
__experimentalReusableBlocks: getReusableBlocks( state ), | |
__experimentalPreferPatternsOnRoot: | |
'wp_template' === getEditedPostType( state ), | |
}; | |
const canUserCreateMedia = getCanUserCreateMedia( state ); | |
if ( ! canUserCreateMedia ) { | |
return settings; | |
} | |
settings.mediaUpload = ( { onError, ...rest } ) => { | |
uploadMedia( { | |
wpAllowedMimeTypes: state.settings.allowedMimeTypes, | |
onError: ( { message } ) => onError( message ), | |
...rest, | |
} ); | |
}; | |
return settings; | |
}, | |
( state ) => [ | |
getCanUserCreateMedia( state ), | |
state.settings, | |
__unstableGetPreference( state, 'focusMode' ), | |
__unstableGetPreference( state, 'fixedToolbar' ), | |
getReusableBlocks( state ), | |
getEditedPostType( state ), | |
] | |
); |
I suppose it would be possible to import select
from the data store for this purpose. I don't know whether that's an ok practice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid there are only two options available:
- export the
__unstableGetPreference
selector as part of the public API. The only reason we need to do that is that the code that initializes the public selectors assigns to theselector.registry
field. - wrap all the selectors that use
__unstableGetPreference
withcreateRegistrySelector
and inline the helper.
import
select
from the data store for this purpose
That would mean that the store can be ever registered only with the default registry and never any other one. Because such an import would hardcode a reference to the default registry to the selector.
createRegistrySelector
will already fail when you try to register its store with two registries -- because they will fight over the selector.registry
field, both assigning to them. We'll need to rethink the entire concept at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing that makes it difficult is the second argument to
createSelector
Ah nuts 🥜
We'll need to rethink the entire concept at some point.
Yeah. I wonder if we can do something similar to thunks where selectors (and the second argument of createSelector
) can return a function which is passed an object containing registry
, select
, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would mean that the store can be ever registered only with the default registry and never any other one. Because such an import would hardcode a reference to the default registry to the selector.
I see. I remember there being a reason for avoiding this practice, but couldn't remember what it was.
Seems like making it __unstable
is probably the best trade-off for now. Thanks for all the feedback.
eeb7311
to
14a27e9
Compare
14a27e9
to
c366a96
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works a treat 👍 and look at all that red! 📉
* Internal dependencies | ||
*/ | ||
import { store as editSiteStore } from '../../../store'; | ||
import { store as preferencesStore } from '@wordpress/preferences'; | ||
|
||
export default function WelcomeGuideMenuItem() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't we use a PreferenceToggleMenuItem
instead of WelcomeGuideMenuItem
? Because we don't want to show a tick ✔️?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, it would also have the wrong aria role (menuitemcheckbox
vs plain menuitem
)
@@ -15,11 +15,7 @@ export const storeConfig = { | |||
reducer, | |||
actions, | |||
selectors, | |||
persist: [ 'preferences' ], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
@@ -42,17 +42,6 @@ function createRegistryWithStores() { | |||
} | |||
|
|||
describe( 'actions', () => { | |||
describe( 'toggleFeature', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's worth keeping a unit test for toggleFeature
around? I'm not sure. On the one hand, it would be a very dumb test that barely tests anything. On the other hand, we can't ever delete toggleFeature
and so it's all that we would have to ensure that there aren't any typos inside toggleFeature
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I was just happy to delete stuff. 😄
I'll add a commit that brings it back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 94eef52.
I've only added back the action test as its easier to test a registry selector there, but I've expanded the test so that it does more testing of the selector.
getCanUserCreateMedia.registry = { | ||
select: jest.fn( () => ( { canUser } ) ), | ||
}; | ||
getReusableBlocks.registry = { | ||
select: jest.fn( () => ( { getEntityRecords } ) ), | ||
}; | ||
|
||
describe( 'isFeatureActive', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same again here.
Description
Addresses some of #31965
Migrates the site editor to use the new preferences package (introduced in #38873). Previously this package had its own preferences implementation, but now it uses a common package, which should speed up the process of implementing new preferences.
The main goal here is that nothing is different from a user perspective.
Testing Instructions
trunk
open the site editor and dismiss the welcome guideChecklist:
*.native.js
files for terms that need renaming or removal).