-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Plugins: Add pinning support for sidebar plugins #6442
Conversation
edit-post/components/header/index.js
Outdated
aria-expanded={ isEditorSidebarOpened } | ||
/> | ||
<MoreMenu key="more-menu" /> | ||
<PinnedPlugins> |
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.
Question is if we should also convert Document Settings
into a plugin. I put this fill in here as a temporary solution.
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.
Out of scope for this PR.
It would be nice if the end-user had the ability to remove these pins, as it would become quite cluttered (especially on mobile) after installing a few plugins. Almost to the point of being reminiscent of Internet Explorer toolbars that you cannot get rid of. Personally I'd prefer them to be opt-in and not forced by default. |
Yep, this has been an ongoing topic for a while, and there are more details on the native extensibility ticket. The current plan is opt out. That is, a plugin can choose to pin an icon as soon as the plugin is activated. The user can then opt out of that pinned icon by clicking it and removing the star. Every plugin has a menu item that opens the same as the pinned icon does. A previous plan had this be opt in, as in you had to open the plugin from the more menu first, and then explicitly pin it by checking the star. |
Worth explicitly stating, in case there's any doubt: a plugin doesn't have to register a pinned icon. In that way this is similar to top level admin menus. |
80b02c8
to
8dc4fa2
Compare
I have an initial implementation ready to review.
Every plugin item (sidebar, takeover screen, etc) - can opt out from being pinnable by using
I started with the easier implementation or more straightforward - everything is unpinned by default. Once the user pins (and unpins afterward), this state is persisted to be used during the next visits.
I will look into it tomorrow. However, I need to find a way how to prevent displaying the icon for the plugin item that opted out from being pinnable. |
Code-wise this looks great 👍 The only bug I'm noticing is that a pinned item can become stuck if the plugin is updated:
|
Not entirely sure how to test this yet, but from your process and the screenshots, this looks right on the money. Thanks for working on this. I think it's important that we try and get this in as soon as we possibly can — not to add stress :) — it would be good to start testing the current plan which is to allow extensions to pin themselves by default and requiring the user to unpin them if they would prefer them in the More Menu only. |
Taking into account what @noisysocks found out, I think I need to refactor code in a way which will allow to make the proposed flow to be the default one. I have a hunch that it can be achieved by moving Fill for pins next to the sidebar rather than having it bundled together with the menu item. Initially, it seemed to be an easier solution because all the logic for both the menu item and pin are the same.
The easiest way to test it is to copy this file and paste it in the JS console. It will enable the sidebar. I also added a second variation with the |
Awesome, thank you for the code. Yes absolutely address Roberts' comments, didn't mean to add stress. I'm just excited to see this, as it's like a bow on our extensibility efforts. |
4427b83
to
fef693b
Compare
@noisysocks - it should work properly with the recent changes introduced. |
edit-post/README.md
Outdated
|
||
- Type: `String` | `Element` | ||
- Required: No | ||
- Default: `admin-plugins` |
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.
@jasmussen - should I pick something else as default? I assumed the more generic icon, the higher probability it gets replaced by the plugin author :)
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.
Code looks good. Tested locally and everything seems peachy. If Joen is happy, I'm happy.
const { Fill: PinnedPlugins, Slot } = createSlotFill( 'PinnedPlugins' ); | ||
|
||
PinnedPlugins.Slot = ( { fillProps } ) => ( | ||
<Slot fillProps={ fillProps }> |
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.
minor: We can make this more future-proof by passing every prop to <Slot>
. Then, if the API for Slot
ever changes, PinnedPlugins.Slot
will change with it.
PinnedPlugins.Slot = ( props ) => (
<Slot { ...props }>
icon = 'admin-plugins', | ||
isActive, | ||
isPinned, | ||
pinnable = true, |
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.
minor: Would isPinnable
be more consistent with e.g. isActive
and isPinned
?
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.
Yes, makes sense.
label={ title } | ||
onClick={ toggleSidebar } | ||
isToggled={ isActive } | ||
aria-expanded={ isActive } |
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.
irrelevant: Should Button
handle setting aria-expanded
iff isToggled
is set?
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.
Good idea, I will double check all occurrences and update IconButton and if it is applicable I will fix in a follow-up PR.
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.
By https://www.w3.org/WAI/GL/wiki/Using_aria-expanded_to_indicate_the_state_of_a_collapsible_element:
When authors use collapsible content, for example, to hide navigation menus or lists of content, the triggering link or button should indicate to screen reader users whether the collapsable content below is in the expanded or in the collapsed state. The aria-expanded attribute is used for this purpose.
It looks like it isn't always the case that both those props should exist. See:
https://github.com/WordPress/gutenberg/blob/master/components/date-time/time.js#L145-L158
In this case, buttons can be toggled. However, they don't control any collapsible content so aria-expanded
is not used.
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.
Makes sense. Thanks for looking into it.
fef693b
to
2c4b4c7
Compare
- Required: No | ||
- Default: `true` | ||
|
||
##### icon |
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.
Should this be a property of registerPlugin
rather than a prop of PluginSidebar
?
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.
As I had a similar dilemma. I will explore how much work is required to move it to registerPlugin
.
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 mean, even just as a default, it seems like many / most plugins would have a single icon to represent themselves throughout the entire experience.
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.
Yes, I like it. It should work. I’ll update PR on Monday 👍
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 added an option to provide an icon
property as part of plugin's registration with 15a51cc. It should give it more flexibility because I left the capability to override this icon for the individual components.
edit-post/store/reducer.js
Outdated
if ( action.type === 'TOGGLE_PINNED_PLUGIN_ITEM' ) { | ||
return { | ||
...state, | ||
[ action.pluginName ]: isUndefined( state[ action.pluginName ] ) ? |
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.
Should we want to use Object#hasOwnProperty
here so we're not dealing with oddities with my aptly-named "constructor" plugin?
$ n_
n_ > var state = {};
undefined
n_ > var action = { pluginName: 'constructor' };
undefined
n_ > _.isUndefined( state[ action.pluginName ] );
false
edit-post/store/selectors.js
Outdated
const defaultValue = true; | ||
const pinnedPluginItems = getPreference( state, 'pinnedPluginItems', {} ); | ||
|
||
return isUndefined( pinnedPluginItems[ pluginName ] ) ? |
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 leverage get
and it's defaultValue
argument?
return get( getPreference( state, 'pinnedPluginItems', {} ), [ pluginName ], true );
edit-post/store/selectors.js
Outdated
|
||
return isUndefined( pinnedPluginItems[ pluginName ] ) ? | ||
defaultValue : | ||
Boolean( pinnedPluginItems[ pluginName ] ); |
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.
When are we expecting the value to be anything other than a boolean here to warrant the coercion?
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 don’t think so.
I also agree that get from lodash will be easier to read in both places. I forgot about the fact that it checks if value is defined 😎
2c4b4c7
to
86cb16b
Compare
@aduth, I addressed your feedback, should be ready for another check. |
I think this would be good to get in sooner rather than later. 👍 👍 |
86cb16b
to
7a31494
Compare
Fixed a regression introduced in |
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 great 👍
import Sidebar from '../'; | ||
import SidebarHeader from '../sidebar-header'; | ||
|
||
/** | ||
* Renders the plugin sidebar component. | ||
* | ||
* @param {Object} props Element props. |
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.
Nit: Blank comment line between @param
and @return
.
@@ -22,6 +22,10 @@ | |||
display: none; | |||
margin-left: auto; | |||
|
|||
~ .components-icon-button { | |||
margin-left: 0; |
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.
Added 4px margin after recommendation from @jasmussen:
* When the value is not set it defaults to true. | ||
* | ||
* @param {Object} state Global application state. | ||
* @param {string} pluginName Plugin item 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.
Similar nit: Documentation standards recommend @param
and @return
to be separate groups, no need to align between the two.
plugins/api/index.js
Outdated
@@ -23,6 +23,7 @@ const plugins = {}; | |||
* @param {string} name The name of the plugin. | |||
* @param {Object} settings The settings for this plugin. | |||
* @param {Function} settings.render The function that renders the plugin. | |||
* @param {string} settings.icon An icon to be shown in the UI. |
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.
Can be element as well?
Includes also JSDoc updates.
This a great final touch for the Plugins API. Great work @gziolo ! |
Description
This PR adds pinning support for sidebar plugins as described by @jasmussen in #4287 (comment):
and also in #3330 (comment):
How has this been tested?
It was tested manually using a test plugin. The easiest way to apply the plugin is to copy ES5 version from this gist: https://gist.github.com/gziolo/e2954aa83aa1f823b2b05ca1660c2223.
Screenshots
Version
1 - always present 2 - unpinned by default3 - pinned by defaultTypes of changes
New feature (non-breaking change which adds functionality).
Checklist: