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

Plugins: Add scoping functionality to the Plugins API #27438

Merged
merged 5 commits into from
Feb 26, 2021

Conversation

senadir
Copy link
Contributor

@senadir senadir commented Dec 2, 2020

Description

Currently, when you register a plugin with Plugins API from the @wordpress/plugins package, that component is rendered on all pages that use the PluginArea component. In addition to that, if you render PluginArea multiple times, then the same plugin will get rendered multiple times on the same page. The solution for that issue is the concept of scopes. This way, you can have more fine-grained control of where your plugin is going to be displayed.

There is now a new scope field added to the settings object passed as a second param to the registerPlugin function. There is also a scope prop introduced to the PluginArea component. A named PluginArea would only render plugins registered for the matching scope passed to the registerPlugin call. If the scope is not provided, then all plugins without a scope will render as before. It makes this approach fully backward compatible.

import { PluginArea, registerPlugin } from '@wordpress/plugins';
import MyPlugin from './my-plugin';

registerPlugin( 'plugin', {
    render() {
        return <MyPlugin >Renders in the default area</MyPlugin>;
    },
} );

registerPlugin( 'plugin-scoped', {
    render() {
        return <MyPlugin>Renders in the scoped area</MyPlugin>;
    },
    scope: 'named',
} );

const App = () => {
    return (
        <>
            <PluginArea />
            <PluginArea scope="named" />
        </>
    );
};

This PR combines work from #21908 proposed by @psealock.

Testing

The existing functionality should work as before.

npm run test-e2e should still pass.

New unit tests were added that cover new functionality:

npm run test-unit should pass.

Type

This PR adds new functionality to public APi in @wordpress/plugins package.

@senadir senadir self-assigned this Dec 2, 2020
@senadir senadir added [Feature] Plugins API Extending the Gutenberg project with plugins via the Plugins API [Package] Plugins /packages/plugins labels Dec 2, 2020
@github-actions
Copy link

github-actions bot commented Dec 2, 2020

Size Change: +62 B (0%)

Total Size: 1.38 MB

Filename Size Change
build/plugins/index.js 2.61 kB +62 B (+2%)
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.14 kB 0 B
build/annotations/index.js 3.78 kB 0 B
build/api-fetch/index.js 3.4 kB 0 B
build/autop/index.js 2.84 kB 0 B
build/blob/index.js 665 B 0 B
build/block-directory/index.js 9.1 kB 0 B
build/block-directory/style-rtl.css 1.01 kB 0 B
build/block-directory/style.css 1.01 kB 0 B
build/block-editor/index.js 124 kB 0 B
build/block-editor/style-rtl.css 12.1 kB 0 B
build/block-editor/style.css 12.1 kB 0 B
build/block-library/blocks/archives/editor-rtl.css 61 B 0 B
build/block-library/blocks/archives/editor.css 60 B 0 B
build/block-library/blocks/audio/editor-rtl.css 58 B 0 B
build/block-library/blocks/audio/editor.css 58 B 0 B
build/block-library/blocks/audio/style-rtl.css 103 B 0 B
build/block-library/blocks/audio/style.css 103 B 0 B
build/block-library/blocks/block/editor-rtl.css 161 B 0 B
build/block-library/blocks/block/editor.css 161 B 0 B
build/block-library/blocks/button/editor-rtl.css 475 B 0 B
build/block-library/blocks/button/editor.css 474 B 0 B
build/block-library/blocks/button/style-rtl.css 460 B 0 B
build/block-library/blocks/button/style.css 460 B 0 B
build/block-library/blocks/buttons/editor-rtl.css 233 B 0 B
build/block-library/blocks/buttons/editor.css 233 B 0 B
build/block-library/blocks/buttons/style-rtl.css 303 B 0 B
build/block-library/blocks/buttons/style.css 303 B 0 B
build/block-library/blocks/calendar/style-rtl.css 208 B 0 B
build/block-library/blocks/calendar/style.css 208 B 0 B
build/block-library/blocks/categories/editor-rtl.css 84 B 0 B
build/block-library/blocks/categories/editor.css 83 B 0 B
build/block-library/blocks/categories/style-rtl.css 79 B 0 B
build/block-library/blocks/categories/style.css 79 B 0 B
build/block-library/blocks/code/style-rtl.css 90 B 0 B
build/block-library/blocks/code/style.css 90 B 0 B
build/block-library/blocks/columns/editor-rtl.css 190 B 0 B
build/block-library/blocks/columns/editor.css 190 B 0 B
build/block-library/blocks/columns/style-rtl.css 421 B 0 B
build/block-library/blocks/columns/style.css 421 B 0 B
build/block-library/blocks/cover/editor-rtl.css 390 B 0 B
build/block-library/blocks/cover/editor.css 389 B 0 B
build/block-library/blocks/cover/style-rtl.css 1.25 kB 0 B
build/block-library/blocks/cover/style.css 1.25 kB 0 B
build/block-library/blocks/embed/editor-rtl.css 486 B 0 B
build/block-library/blocks/embed/editor.css 486 B 0 B
build/block-library/blocks/embed/style-rtl.css 396 B 0 B
build/block-library/blocks/embed/style.css 395 B 0 B
build/block-library/blocks/file/editor-rtl.css 199 B 0 B
build/block-library/blocks/file/editor.css 198 B 0 B
build/block-library/blocks/file/style-rtl.css 248 B 0 B
build/block-library/blocks/file/style.css 248 B 0 B
build/block-library/blocks/freeform/editor-rtl.css 2.45 kB 0 B
build/block-library/blocks/freeform/editor.css 2.45 kB 0 B
build/block-library/blocks/gallery/editor-rtl.css 689 B 0 B
build/block-library/blocks/gallery/editor.css 690 B 0 B
build/block-library/blocks/gallery/style-rtl.css 1.07 kB 0 B
build/block-library/blocks/gallery/style.css 1.06 kB 0 B
build/block-library/blocks/group/editor-rtl.css 318 B 0 B
build/block-library/blocks/group/editor.css 317 B 0 B
build/block-library/blocks/group/style-rtl.css 57 B 0 B
build/block-library/blocks/group/style.css 57 B 0 B
build/block-library/blocks/heading/editor-rtl.css 129 B 0 B
build/block-library/blocks/heading/editor.css 129 B 0 B
build/block-library/blocks/heading/style-rtl.css 76 B 0 B
build/block-library/blocks/heading/style.css 76 B 0 B
build/block-library/blocks/html/editor-rtl.css 281 B 0 B
build/block-library/blocks/html/editor.css 281 B 0 B
build/block-library/blocks/image/editor-rtl.css 717 B 0 B
build/block-library/blocks/image/editor.css 716 B 0 B
build/block-library/blocks/image/style-rtl.css 477 B 0 B
build/block-library/blocks/image/style.css 478 B 0 B
build/block-library/blocks/latest-comments/editor-rtl.css 159 B 0 B
build/block-library/blocks/latest-comments/editor.css 158 B 0 B
build/block-library/blocks/latest-comments/style-rtl.css 269 B 0 B
build/block-library/blocks/latest-comments/style.css 269 B 0 B
build/block-library/blocks/latest-posts/editor-rtl.css 137 B 0 B
build/block-library/blocks/latest-posts/editor.css 137 B 0 B
build/block-library/blocks/latest-posts/style-rtl.css 523 B 0 B
build/block-library/blocks/latest-posts/style.css 522 B 0 B
build/block-library/blocks/list/editor-rtl.css 65 B 0 B
build/block-library/blocks/list/editor.css 65 B 0 B
build/block-library/blocks/list/style-rtl.css 63 B 0 B
build/block-library/blocks/list/style.css 63 B 0 B
build/block-library/blocks/media-text/editor-rtl.css 191 B 0 B
build/block-library/blocks/media-text/editor.css 191 B 0 B
build/block-library/blocks/media-text/style-rtl.css 535 B 0 B
build/block-library/blocks/media-text/style.css 532 B 0 B
build/block-library/blocks/more/editor-rtl.css 434 B 0 B
build/block-library/blocks/more/editor.css 434 B 0 B
build/block-library/blocks/navigation-link/editor-rtl.css 395 B 0 B
build/block-library/blocks/navigation-link/editor.css 397 B 0 B
build/block-library/blocks/navigation-link/style-rtl.css 704 B 0 B
build/block-library/blocks/navigation-link/style.css 702 B 0 B
build/block-library/blocks/navigation/editor-rtl.css 1.34 kB 0 B
build/block-library/blocks/navigation/editor.css 1.34 kB 0 B
build/block-library/blocks/navigation/style-rtl.css 195 B 0 B
build/block-library/blocks/navigation/style.css 195 B 0 B
build/block-library/blocks/nextpage/editor-rtl.css 395 B 0 B
build/block-library/blocks/nextpage/editor.css 395 B 0 B
build/block-library/blocks/page-list/editor-rtl.css 214 B 0 B
build/block-library/blocks/page-list/editor.css 214 B 0 B
build/block-library/blocks/page-list/style-rtl.css 527 B 0 B
build/block-library/blocks/page-list/style.css 526 B 0 B
build/block-library/blocks/paragraph/editor-rtl.css 109 B 0 B
build/block-library/blocks/paragraph/editor.css 109 B 0 B
build/block-library/blocks/paragraph/style-rtl.css 273 B 0 B
build/block-library/blocks/paragraph/style.css 273 B 0 B
build/block-library/blocks/post-author/editor-rtl.css 209 B 0 B
build/block-library/blocks/post-author/editor.css 209 B 0 B
build/block-library/blocks/post-author/style-rtl.css 183 B 0 B
build/block-library/blocks/post-author/style.css 184 B 0 B
build/block-library/blocks/post-comments-form/style-rtl.css 250 B 0 B
build/block-library/blocks/post-comments-form/style.css 250 B 0 B
build/block-library/blocks/post-content/editor-rtl.css 139 B 0 B
build/block-library/blocks/post-content/editor.css 139 B 0 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B 0 B
build/block-library/blocks/post-excerpt/editor.css 73 B 0 B
build/block-library/blocks/post-featured-image/editor-rtl.css 338 B 0 B
build/block-library/blocks/post-featured-image/editor.css 338 B 0 B
build/block-library/blocks/post-featured-image/style-rtl.css 100 B 0 B
build/block-library/blocks/post-featured-image/style.css 100 B 0 B
build/block-library/blocks/preformatted/style-rtl.css 63 B 0 B
build/block-library/blocks/preformatted/style.css 63 B 0 B
build/block-library/blocks/pullquote/editor-rtl.css 183 B 0 B
build/block-library/blocks/pullquote/editor.css 183 B 0 B
build/block-library/blocks/pullquote/style-rtl.css 316 B 0 B
build/block-library/blocks/pullquote/style.css 316 B 0 B
build/block-library/blocks/query-loop/editor-rtl.css 90 B 0 B
build/block-library/blocks/query-loop/editor.css 89 B 0 B
build/block-library/blocks/query-loop/style-rtl.css 315 B 0 B
build/block-library/blocks/query-loop/style.css 317 B 0 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B 0 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B 0 B
build/block-library/blocks/query-pagination/editor-rtl.css 270 B 0 B
build/block-library/blocks/query-pagination/editor.css 262 B 0 B
build/block-library/blocks/query-pagination/style-rtl.css 168 B 0 B
build/block-library/blocks/query-pagination/style.css 168 B 0 B
build/block-library/blocks/query/editor-rtl.css 159 B 0 B
build/block-library/blocks/query/editor.css 160 B 0 B
build/block-library/blocks/quote/editor-rtl.css 61 B 0 B
build/block-library/blocks/quote/editor.css 61 B 0 B
build/block-library/blocks/quote/style-rtl.css 169 B 0 B
build/block-library/blocks/quote/style.css 169 B 0 B
build/block-library/blocks/rss/editor-rtl.css 201 B 0 B
build/block-library/blocks/rss/editor.css 202 B 0 B
build/block-library/blocks/rss/style-rtl.css 290 B 0 B
build/block-library/blocks/rss/style.css 290 B 0 B
build/block-library/blocks/search/editor-rtl.css 165 B 0 B
build/block-library/blocks/search/editor.css 165 B 0 B
build/block-library/blocks/search/style-rtl.css 342 B 0 B
build/block-library/blocks/search/style.css 344 B 0 B
build/block-library/blocks/separator/editor-rtl.css 99 B 0 B
build/block-library/blocks/separator/editor.css 99 B 0 B
build/block-library/blocks/separator/style-rtl.css 236 B 0 B
build/block-library/blocks/separator/style.css 236 B 0 B
build/block-library/blocks/shortcode/editor-rtl.css 504 B 0 B
build/block-library/blocks/shortcode/editor.css 504 B 0 B
build/block-library/blocks/site-logo/editor-rtl.css 201 B 0 B
build/block-library/blocks/site-logo/editor.css 201 B 0 B
build/block-library/blocks/site-logo/style-rtl.css 117 B 0 B
build/block-library/blocks/site-logo/style.css 117 B 0 B
build/block-library/blocks/social-link/editor-rtl.css 164 B 0 B
build/block-library/blocks/social-link/editor.css 165 B 0 B
build/block-library/blocks/social-links/editor-rtl.css 696 B 0 B
build/block-library/blocks/social-links/editor.css 696 B 0 B
build/block-library/blocks/social-links/style-rtl.css 1.36 kB 0 B
build/block-library/blocks/social-links/style.css 1.36 kB 0 B
build/block-library/blocks/spacer/editor-rtl.css 302 B 0 B
build/block-library/blocks/spacer/editor.css 302 B 0 B
build/block-library/blocks/spacer/style-rtl.css 48 B 0 B
build/block-library/blocks/spacer/style.css 48 B 0 B
build/block-library/blocks/subhead/editor-rtl.css 99 B 0 B
build/block-library/blocks/subhead/editor.css 99 B 0 B
build/block-library/blocks/subhead/style-rtl.css 80 B 0 B
build/block-library/blocks/subhead/style.css 80 B 0 B
build/block-library/blocks/table/editor-rtl.css 489 B 0 B
build/block-library/blocks/table/editor.css 489 B 0 B
build/block-library/blocks/table/style-rtl.css 386 B 0 B
build/block-library/blocks/table/style.css 386 B 0 B
build/block-library/blocks/tag-cloud/editor-rtl.css 118 B 0 B
build/block-library/blocks/tag-cloud/editor.css 118 B 0 B
build/block-library/blocks/tag-cloud/style-rtl.css 94 B 0 B
build/block-library/blocks/tag-cloud/style.css 94 B 0 B
build/block-library/blocks/template-part/editor-rtl.css 557 B 0 B
build/block-library/blocks/template-part/editor.css 556 B 0 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B 0 B
build/block-library/blocks/text-columns/editor.css 95 B 0 B
build/block-library/blocks/text-columns/style-rtl.css 166 B 0 B
build/block-library/blocks/text-columns/style.css 166 B 0 B
build/block-library/blocks/verse/editor-rtl.css 62 B 0 B
build/block-library/blocks/verse/editor.css 62 B 0 B
build/block-library/blocks/verse/style-rtl.css 87 B 0 B
build/block-library/blocks/verse/style.css 87 B 0 B
build/block-library/blocks/video/editor-rtl.css 504 B 0 B
build/block-library/blocks/video/editor.css 503 B 0 B
build/block-library/blocks/video/style-rtl.css 193 B 0 B
build/block-library/blocks/video/style.css 193 B 0 B
build/block-library/common-rtl.css 1.08 kB 0 B
build/block-library/common.css 1.08 kB 0 B
build/block-library/editor-rtl.css 9.05 kB 0 B
build/block-library/editor.css 9.04 kB 0 B
build/block-library/index.js 145 kB 0 B
build/block-library/style-rtl.css 8.81 kB 0 B
build/block-library/style.css 8.82 kB 0 B
build/block-library/theme-rtl.css 748 B 0 B
build/block-library/theme.css 748 B 0 B
build/block-serialization-default-parser/index.js 1.88 kB 0 B
build/block-serialization-spec-parser/index.js 3.06 kB 0 B
build/blocks/index.js 48.3 kB 0 B
build/components/index.js 272 kB 0 B
build/components/style-rtl.css 15.5 kB 0 B
build/components/style.css 15.5 kB 0 B
build/compose/index.js 11.1 kB 0 B
build/core-data/index.js 16.7 kB 0 B
build/customize-widgets/index.js 4.08 kB 0 B
build/customize-widgets/style-rtl.css 168 B 0 B
build/customize-widgets/style.css 168 B 0 B
build/data-controls/index.js 830 B 0 B
build/data/index.js 8.86 kB 0 B
build/date/index.js 31.8 kB 0 B
build/deprecated/index.js 768 B 0 B
build/dom-ready/index.js 576 B 0 B
build/dom/index.js 4.94 kB 0 B
build/edit-navigation/index.js 11 kB 0 B
build/edit-navigation/style-rtl.css 1.26 kB 0 B
build/edit-navigation/style.css 1.25 kB 0 B
build/edit-post/index.js 307 kB 0 B
build/edit-post/style-rtl.css 6.81 kB 0 B
build/edit-post/style.css 6.8 kB 0 B
build/edit-site/index.js 26.1 kB 0 B
build/edit-site/style-rtl.css 4.41 kB 0 B
build/edit-site/style.css 4.41 kB 0 B
build/edit-widgets/index.js 20 kB 0 B
build/edit-widgets/style-rtl.css 3.2 kB 0 B
build/edit-widgets/style.css 3.2 kB 0 B
build/editor/editor-styles-rtl.css 543 B 0 B
build/editor/editor-styles.css 545 B 0 B
build/editor/index.js 42.1 kB 0 B
build/editor/style-rtl.css 3.89 kB 0 B
build/editor/style.css 3.89 kB 0 B
build/element/index.js 4.61 kB 0 B
build/escape-html/index.js 735 B 0 B
build/format-library/index.js 6.77 kB 0 B
build/format-library/style-rtl.css 637 B 0 B
build/format-library/style.css 639 B 0 B
build/hooks/index.js 2.28 kB 0 B
build/html-entities/index.js 622 B 0 B
build/i18n/index.js 4.01 kB 0 B
build/is-shallow-equal/index.js 698 B 0 B
build/keyboard-shortcuts/index.js 2.54 kB 0 B
build/keycodes/index.js 1.95 kB 0 B
build/list-reusable-blocks/index.js 3.14 kB 0 B
build/list-reusable-blocks/style-rtl.css 629 B 0 B
build/list-reusable-blocks/style.css 628 B 0 B
build/media-utils/index.js 5.35 kB 0 B
build/notices/index.js 1.85 kB 0 B
build/nux/index.js 3.41 kB 0 B
build/nux/style-rtl.css 731 B 0 B
build/nux/style.css 727 B 0 B
build/primitives/index.js 1.42 kB 0 B
build/priority-queue/index.js 790 B 0 B
build/react-i18n/index.js 1.45 kB 0 B
build/redux-routine/index.js 2.83 kB 0 B
build/reusable-blocks/index.js 3.77 kB 0 B
build/reusable-blocks/style-rtl.css 225 B 0 B
build/reusable-blocks/style.css 225 B 0 B
build/rich-text/index.js 13.4 kB 0 B
build/server-side-render/index.js 2.76 kB 0 B
build/shortcode/index.js 1.7 kB 0 B
build/token-list/index.js 1.27 kB 0 B
build/url/index.js 3.01 kB 0 B
build/viewport/index.js 1.85 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.22 kB 0 B

compressed-size-action

@gziolo
Copy link
Member

gziolo commented Dec 2, 2020

I think, there was a similar proposal in the past. Did you check if there are any previous issues and PRs related to this topic?

@gziolo gziolo requested a review from mcsf December 2, 2020 11:04
@senadir
Copy link
Contributor Author

senadir commented Dec 2, 2020

I didn't, I will take a look.

@senadir
Copy link
Contributor Author

senadir commented Dec 2, 2020

Ah it seems @psealock introduced something similar in #21908 but that PR didn't progress beyond some initial questions.

I still think merging this is worthwhile, both solutions are backward compatible, #21908 needs a simple refactor (to move scope to settings maybe) but already has some tests in it.

@gziolo
Copy link
Member

gziolo commented Dec 2, 2020

Cool, should we wait for @psealock and decide what to do next?

I have a related question, what would be the handling if the scope isn't defined by the plugin but it's enforced inside PluginArea. Would it render only those plugins that opted in for this particular scope? What happens the other way around, when the PluginArea accepts all but the plugin author expect to see it in a given scope only.

@gziolo gziolo mentioned this pull request Dec 2, 2020
9 tasks
@senadir
Copy link
Contributor Author

senadir commented Dec 2, 2020

what would be the handling if the scope isn't defined by the plugin but it's enforced inside PluginArea

That PluginArea won't render that plugin.

when the PluginArea accepts all but the plugin author expect to see it in a given scope only.

When a PluginArea doesn't define a scope, it would only accept unscoped plugins.

@senadir
Copy link
Contributor Author

senadir commented Dec 2, 2020

The current code treats undefined as a scope on its own (same with Paul's PR) to undefined plugins would get rendered in undefined areas.

@gziolo
Copy link
Member

gziolo commented Dec 2, 2020

Cool, all clear now. Let's make it happen here on in another PR referenced. It's up to you two now. I can help with review 👍

@gziolo gziolo changed the title Add named plugin areas Plugins: Add named plugin areas Dec 2, 2020
@psealock
Copy link
Contributor

psealock commented Dec 4, 2020

Cool, glad to see this idea get traction!

One correction on this question:

what would be the handling if the scope isn't defined by the plugin but it's enforced inside PluginArea. Would it render only those plugins that opted in for this particular scope?

In #21908 I had an undefined area behave as it currently does now. That is, all items would be rendered instead of defining a new scope. I chose this behaviour for purposes of backwards compatibility and I think would be a good addition to this PR.

@senadir if you'd like to port over the unit tests I had as well, I'm cool to just go forward with this PR and close mine out.

@gziolo
Copy link
Member

gziolo commented Dec 4, 2020

That is, all items would be rendered instead of defining a new scope. I chose this behaviour for purposes of backwards compatibility and I think would be a good addition to this PR.

There was no scope concept before, so I personally wouldn't mind having:

  • <PluginArea /> rendering only unscoped plugins
  • <PluginArea scope="core/edit-post" /> rendering only scoped plugins that target core/edit-post

This way you ensure that using the scope is predictable. The edit post page would have for backward compatibility reasons would have to render two plugin areas, but other pages could decide themselves whether an unscoped plugin area makes sense for them.

@gziolo
Copy link
Member

gziolo commented Dec 4, 2020

I might also have a counterargument to my previous statement 🤣

We have scope prop on some SlotFills:

I think @jorgefilipecosta will know better how those two concepts could fit together. It might be also that they duplicate each other.

Anyway, the point is that it suddenly doesn't feel that clear whether having an unscoped plugin area should render only unscoped plugins. Sorry for bringing more confusion than clarity, but I thought it's an important detail that I completely forgot about. It's also possible that we discussed the scope setting introduction in the settings of the plugin in the past and we didn't proceed further because of this duality.

@senadir
Copy link
Contributor Author

senadir commented Dec 4, 2020

Anyway, the point is that it suddenly doesn't feel that clear whether having an unscoped plugin area should render only unscoped plugins.

The point of having a scope is to avoid accidental rendering of unwanted stuff, taking my case, for example, I only want people who are explicitly registering to my pluginArea to render, people who don't have scope are people not registering to me and therefore prefer to risk having them show up in a wrong context that might be missing dependencies and having them breaking the area.

@gziolo
Copy link
Member

gziolo commented Dec 4, 2020

The point of having a scope is to avoid accidental rendering of unwanted stuff, taking my case, for example, I only want people who are explicitly registering to my pluginArea to render, people who don't have scope are people not registering to me and therefore prefer to risk having them show up in a wrong context that might be missing dependencies and having them breaking the area.

For the sake of argument, I can tell that you could also achieve the same effect by adding scopes to the slots you include on the page and ensure that only fills that define the expected scope would render. The question here is what would be more convenient from the perspective of the developers using both sides of this API. Honestly speaking, I have no idea. @mcsf, do you remember if there was a prior discussion on the same topic?

@jorgefilipecosta
Copy link
Member

For the sake of argument, I can tell that you could also achieve the same effect by adding scopes to the slots you include on the page and ensure that only fills that define the expected scope would render. The question here is what would be more convenient from the perspective of the developers using both sides of this API. Honestly speaking, I have no idea. @mcsf, do you remember if there was a prior discussion on the same topic?

Given that we already have a scoped slot & fills https://github.com/WordPress/gutenberg/tree/master/packages/interface/src/components/complementary-area?rgh-link-date=2020-12-04T16%3A10%3A03Z#scope

I'm not sure having stopped plugin areas provides an added benefit. It seems we are going to the path of slot&fills having the scope, In that case, I guess we don't need scopes in the plugin areas. A third party plugin that wants to implement extensible screens/areas can expose scoped slots?

@senadir
Copy link
Contributor Author

senadir commented Dec 8, 2020

scoping for SlotFills seems to solve a very specific need for Gutenberg but doesn't work well for other cases.
You're treating that slotFill as a scoped PluginArea and that makes sense if I have a single slotFill, but my code contains several slotFills. getting a developer to define a scope for each and every slotFill he writes is extremely redundant because slots already serve a 1:1 relationship.

All of that aside, this doesn't solve the fact that having several parents extensions (Gutenberg, WooCommerce...) owning a pluginArea in the same page would cause clash and increase the risk of having thing render in the wrong place when 3PD tries to integrate into them.
Sure, a SlotFill ensures you're only rendering in the right tree, but this doesn't reduce the risk of developers writing code outside the slotFill (fetching, preps, handlers...) to be used inside the slotFill, that code is going to be executed on all PluginAreas.

@gziolo
Copy link
Member

gziolo commented Dec 8, 2020

@senadir, thanks for sharing more details and interesting use cases I haven't considered myself. That's a valid point that since render is a React component you can do many things that would be difficult to prevent without what you proposed. If feels like those two concepts might coexist and play different purposes. The nice part about scoped Slots is that you can have multiple instances on one page so that's another difference.

The biggest challenge is how to name and advertise those two scoping concepts.

@senadir
Copy link
Contributor Author

senadir commented Dec 8, 2020

We're also going to use scoped Slots in our code, I'm yet to figure out some details but the this is my case, I can use to break down the different:

  • Our Plugin is a different context from Gutenberg, while it runs inside Gutenberg, it also runs in other views (React om frontend).
  • For that reason, we can't safely share the same PluginArea instance.
  • We're going to use scoped PluginArea to distinguish our area from the one included in GB (or any other plugin that allow people to integrate into it).
  • Like ComplementaryArea, we have a shared view Sidebar that is shared between two views (blocks) and those are Cart and Checkout.
  • The sidebar component is the same between them to some degree, and so the same Slots exist on Cart and Checkout.
  • We're going to scoped Slots in this place to distinguish between those two areas, so we would have a slot like this <TotalsItem scope="checkout"> and <TotalsItem scope="cart">.

In this sense, scope for PluginArea is to separate different global views that could co-exist in the same view and it targets parent views (the editor, the checkout, the navigation...).
Scope for Slots is to offer contextual rendering, a developer can reuse the same Slot with the same name, but in a different context, and people can extend stuff in that context only.
This allows code reuse across the codebase.

Each plugin would probably define one scoped PluginArea and several scoped Slots.

A PluginArea is the entry point for any code to render, and a Slot is what decides where should that code render.
The separating line between slots and plugins is a bit confusing and hard to see, it sure took me some time understand.

@gziolo
Copy link
Member

gziolo commented Dec 9, 2020

I think this discussion helped me to better picture how all the different concepts could fit together. Thank you @senadir. I must admit it was extremely challenging to process all that since I wanted to make sure we not only introduce another prop or setting but we know how to communicate it to those consuming API. I'm on-board with landing this proposal after PR gets updated with:

  • area becomes scope since it's how we referred to it most often
  • unit tests are ported over from PR opened by @psealock
  • documentation includes the new prop/setting and there is a detailed example showing how to use it that we can post as a dev note for WordPress 5.7
  • @wordpress/plugins package includes a note in the CHANGELOG file.

Separately (another PR it seems), we can discuss how to scope other sites than the edit post one: edit site, edit widgets, etc. Should we have two areas on edit post page? 🤔

What do you think?

@mcsf
Copy link
Contributor

mcsf commented Dec 11, 2020

👋 I just wanted to echo the following from Grzegorz's latest comment:

I must admit it was extremely challenging to process all that since I wanted to make sure we not only introduce another prop or setting but we know how to communicate it to those consuming API. I'm on-board with landing this proposal after PR gets updated with:

  • documentation includes the new prop/setting and there is a detailed example showing how to use it that we can post as a dev note for WordPress 5.7

@gziolo gziolo self-assigned this Feb 22, 2021
@gziolo gziolo force-pushed the add/named-plugin-areas branch from 2ea8af5 to 5db5854 Compare February 22, 2021 15:18
@gziolo
Copy link
Member

gziolo commented Feb 22, 2021

I rebased this branch and included changes proposed in #21908 by @psealock. It should be ready for the final review.

I still need to compile a dev note and update documentation.

@gziolo gziolo added the [Type] New API New API to be used by plugin developers or package users. label Feb 22, 2021
@gziolo gziolo changed the title Plugins: Add named plugin areas Plugins: Add the scoping functionality to the Plugins API Feb 22, 2021
@gziolo gziolo changed the title Plugins: Add the scoping functionality to the Plugins API Plugins: Add scoping functionality to the Plugins API Feb 22, 2021
@gziolo gziolo force-pushed the add/named-plugin-areas branch from 17e6593 to 43da253 Compare February 22, 2021 15:38
@gziolo gziolo requested a review from psealock February 22, 2021 15:40
Copy link
Contributor

@psealock psealock left a comment

Choose a reason for hiding this comment

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

Code changes look good, backwards compatibility remains intact, and this achieves the goal of only having relevant components added to certain pages.

@gziolo
Copy link
Member

gziolo commented Feb 24, 2021

I still need to compile a dev note for WordPress 5.8 before I land this change.

@gziolo gziolo added the Needs Dev Note Requires a developer note for a major WordPress release cycle label Feb 26, 2021
@gziolo
Copy link
Member

gziolo commented Feb 26, 2021

Dev note for WordPress 5.8:

Scopes in the block editor Plugins API

Currently, when you register a plugin with Plugins API from the @wordpress/plugins package, that component is rendered on all pages that use the PluginArea component. In addition to that, if you render PluginArea multiple times, then the same plugin will get rendered multiple times on the same page. The solution for that issue is the concept of scopes. This way, you can have more fine-grained control of where your plugin is going to be displayed.

There is now a new scope field added to the settings object passed as a second param to the registerPlugin function. There is also a scope prop introduced to the PluginArea component. A named PluginArea would only render plugins registered for the matching scope passed to the registerPlugin call. If the scope is not provided, then all plugins without a scope will render as before. It makes this approach fully backward compatible.

import { PluginArea, registerPlugin } from '@wordpress/plugins';
import MyPlugin from './my-plugin';

registerPlugin( 'plugin', {
    render() {
        return <MyPlugin >Renders in the default area</MyPlugin>;
    },
} );

registerPlugin( 'plugin-scoped', {
    render() {
        return <MyPlugin>Renders in the scoped area</MyPlugin>;
    },
    scope: 'named',
} );

const App = () => {
    return (
        <>
            <PluginArea />
            <PluginArea scope="named" />
        </>
    );
};

Props to @psealock and @senadir for help.

@gziolo gziolo merged commit 645224d into master Feb 26, 2021
@gziolo gziolo deleted the add/named-plugin-areas branch February 26, 2021 09:41
@github-actions github-actions bot added this to the Gutenberg 10.2 milestone Feb 26, 2021
@youknowriad youknowriad removed the Needs Dev Note Requires a developer note for a major WordPress release cycle label Jun 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Plugins API Extending the Gutenberg project with plugins via the Plugins API [Package] Plugins /packages/plugins [Type] New API New API to be used by plugin developers or package users.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants