@wordpress/plugins 5.6.0 → 5.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 5.7.0 (2023-03-29)
6
+
5
7
  ## 5.6.0 (2023-03-15)
6
8
 
7
9
  ## 5.5.0 (2023-03-01)
package/README.md CHANGED
@@ -68,6 +68,12 @@ const Layout = () => (
68
68
  );
69
69
  ```
70
70
 
71
+ _Parameters_
72
+
73
+ - _props_ `Object`:
74
+ - _props.scope_ `string|undefined`:
75
+ - _props.onError_ `Function|undefined`:
76
+
71
77
  _Returns_
72
78
 
73
79
  - `WPComponent`: The component to be rendered.
@@ -13,6 +13,8 @@ var _memize = _interopRequireDefault(require("memize"));
13
13
 
14
14
  var _hooks = require("@wordpress/hooks");
15
15
 
16
+ var _isShallowEqual = _interopRequireDefault(require("@wordpress/is-shallow-equal"));
17
+
16
18
  var _pluginContext = require("../plugin-context");
17
19
 
18
20
  var _pluginErrorBoundary = require("../plugin-error-boundary");
@@ -30,10 +32,16 @@ var _api = require("../../api");
30
32
  /**
31
33
  * Internal dependencies
32
34
  */
33
-
35
+ const getPluginContext = (0, _memize.default)((icon, name) => ({
36
+ icon,
37
+ name
38
+ }));
34
39
  /**
35
40
  * A component that renders all plugin fills in a hidden div.
36
41
  *
42
+ * @param {Object} props
43
+ * @param {string|undefined} props.scope
44
+ * @param {Function|undefined} props.onError
37
45
  * @example
38
46
  * ```js
39
47
  * // Using ES5 syntax
@@ -65,69 +73,55 @@ var _api = require("../../api");
65
73
  *
66
74
  * @return {WPComponent} The component to be rendered.
67
75
  */
68
- class PluginArea extends _element.Component {
69
- constructor() {
70
- super(...arguments);
71
- this.setPlugins = this.setPlugins.bind(this);
72
- this.memoizedContext = (0, _memize.default)((name, icon) => {
73
- return {
74
- name,
75
- icon
76
- };
77
- });
78
- this.state = this.getCurrentPluginsState();
79
- }
80
-
81
- getCurrentPluginsState() {
76
+
77
+ function PluginArea(_ref) {
78
+ let {
79
+ scope,
80
+ onError
81
+ } = _ref;
82
+ const store = (0, _element.useMemo)(() => {
83
+ let lastValue;
82
84
  return {
83
- plugins: (0, _api.getPlugins)(this.props.scope).map(_ref => {
84
- let {
85
- icon,
86
- name,
87
- render
88
- } = _ref;
89
- return {
90
- Plugin: render,
91
- context: this.memoizedContext(name, icon)
85
+ subscribe(listener) {
86
+ (0, _hooks.addAction)('plugins.pluginRegistered', 'core/plugins/plugin-area/plugins-registered', listener);
87
+ (0, _hooks.addAction)('plugins.pluginUnregistered', 'core/plugins/plugin-area/plugins-unregistered', listener);
88
+ return () => {
89
+ (0, _hooks.removeAction)('plugins.pluginRegistered', 'core/plugins/plugin-area/plugins-registered');
90
+ (0, _hooks.removeAction)('plugins.pluginUnregistered', 'core/plugins/plugin-area/plugins-unregistered');
92
91
  };
93
- })
94
- };
95
- }
96
-
97
- componentDidMount() {
98
- (0, _hooks.addAction)('plugins.pluginRegistered', 'core/plugins/plugin-area/plugins-registered', this.setPlugins);
99
- (0, _hooks.addAction)('plugins.pluginUnregistered', 'core/plugins/plugin-area/plugins-unregistered', this.setPlugins);
100
- }
101
-
102
- componentWillUnmount() {
103
- (0, _hooks.removeAction)('plugins.pluginRegistered', 'core/plugins/plugin-area/plugins-registered');
104
- (0, _hooks.removeAction)('plugins.pluginUnregistered', 'core/plugins/plugin-area/plugins-unregistered');
105
- }
106
-
107
- setPlugins() {
108
- this.setState(this.getCurrentPluginsState);
109
- }
110
-
111
- render() {
112
- return (0, _element.createElement)("div", {
113
- style: {
114
- display: 'none'
92
+ },
93
+
94
+ getValue() {
95
+ const nextValue = (0, _api.getPlugins)(scope);
96
+
97
+ if (!(0, _isShallowEqual.default)(lastValue, nextValue)) {
98
+ lastValue = nextValue;
99
+ }
100
+
101
+ return lastValue;
115
102
  }
116
- }, this.state.plugins.map(_ref2 => {
117
- let {
118
- context,
119
- Plugin
120
- } = _ref2;
121
- return (0, _element.createElement)(_pluginContext.PluginContextProvider, {
122
- key: context.name,
123
- value: context
124
- }, (0, _element.createElement)(_pluginErrorBoundary.PluginErrorBoundary, {
125
- name: context.name,
126
- onError: this.props.onError
127
- }, (0, _element.createElement)(Plugin, null)));
128
- }));
129
- }
130
103
 
104
+ };
105
+ }, [scope]);
106
+ const plugins = (0, _element.useSyncExternalStore)(store.subscribe, store.getValue);
107
+ return (0, _element.createElement)("div", {
108
+ style: {
109
+ display: 'none'
110
+ }
111
+ }, plugins.map(_ref2 => {
112
+ let {
113
+ icon,
114
+ name,
115
+ render: Plugin
116
+ } = _ref2;
117
+ return (0, _element.createElement)(_pluginContext.PluginContextProvider, {
118
+ key: name,
119
+ value: getPluginContext(icon, name)
120
+ }, (0, _element.createElement)(_pluginErrorBoundary.PluginErrorBoundary, {
121
+ name: name,
122
+ onError: onError
123
+ }, (0, _element.createElement)(Plugin, null)));
124
+ }));
131
125
  }
132
126
 
133
127
  var _default = PluginArea;
@@ -1 +1 @@
1
- {"version":3,"sources":["@wordpress/plugins/src/components/plugin-area/index.js"],"names":["PluginArea","Component","constructor","arguments","setPlugins","bind","memoizedContext","name","icon","state","getCurrentPluginsState","plugins","props","scope","map","render","Plugin","context","componentDidMount","componentWillUnmount","setState","display","onError"],"mappings":";;;;;;;;;AAQA;;AALA;;AAMA;;AAKA;;AACA;;AACA;;AAhBA;AACA;AACA;;AAGA;AACA;AACA;;AAIA;AACA;AACA;;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMA,UAAN,SAAyBC,kBAAzB,CAAmC;AAClCC,EAAAA,WAAW,GAAG;AACb,UAAO,GAAGC,SAAV;AAEA,SAAKC,UAAL,GAAkB,KAAKA,UAAL,CAAgBC,IAAhB,CAAsB,IAAtB,CAAlB;AACA,SAAKC,eAAL,GAAuB,qBAAS,CAAEC,IAAF,EAAQC,IAAR,KAAkB;AACjD,aAAO;AACND,QAAAA,IADM;AAENC,QAAAA;AAFM,OAAP;AAIA,KALsB,CAAvB;AAMA,SAAKC,KAAL,GAAa,KAAKC,sBAAL,EAAb;AACA;;AAEDA,EAAAA,sBAAsB,GAAG;AACxB,WAAO;AACNC,MAAAA,OAAO,EAAE,qBAAY,KAAKC,KAAL,CAAWC,KAAvB,EAA+BC,GAA/B,CACR,QAA8B;AAAA,YAA5B;AAAEN,UAAAA,IAAF;AAAQD,UAAAA,IAAR;AAAcQ,UAAAA;AAAd,SAA4B;AAC7B,eAAO;AACNC,UAAAA,MAAM,EAAED,MADF;AAENE,UAAAA,OAAO,EAAE,KAAKX,eAAL,CAAsBC,IAAtB,EAA4BC,IAA5B;AAFH,SAAP;AAIA,OANO;AADH,KAAP;AAUA;;AAEDU,EAAAA,iBAAiB,GAAG;AACnB,0BACC,0BADD,EAEC,6CAFD,EAGC,KAAKd,UAHN;AAKA,0BACC,4BADD,EAEC,+CAFD,EAGC,KAAKA,UAHN;AAKA;;AAEDe,EAAAA,oBAAoB,GAAG;AACtB,6BACC,0BADD,EAEC,6CAFD;AAIA,6BACC,4BADD,EAEC,+CAFD;AAIA;;AAEDf,EAAAA,UAAU,GAAG;AACZ,SAAKgB,QAAL,CAAe,KAAKV,sBAApB;AACA;;AAEDK,EAAAA,MAAM,GAAG;AACR,WACC;AAAK,MAAA,KAAK,EAAG;AAAEM,QAAAA,OAAO,EAAE;AAAX;AAAb,OACG,KAAKZ,KAAL,CAAWE,OAAX,CAAmBG,GAAnB,CAAwB;AAAA,UAAE;AAAEG,QAAAA,OAAF;AAAWD,QAAAA;AAAX,OAAF;AAAA,aACzB,4BAAC,oCAAD;AACC,QAAA,GAAG,EAAGC,OAAO,CAACV,IADf;AAEC,QAAA,KAAK,EAAGU;AAFT,SAIC,4BAAC,wCAAD;AACC,QAAA,IAAI,EAAGA,OAAO,CAACV,IADhB;AAEC,QAAA,OAAO,EAAG,KAAKK,KAAL,CAAWU;AAFtB,SAIC,4BAAC,MAAD,OAJD,CAJD,CADyB;AAAA,KAAxB,CADH,CADD;AAiBA;;AAzEiC;;eA4EpBtB,U","sourcesContent":["/**\n * External dependencies\n */\nimport memoize from 'memize';\n\n/**\n * WordPress dependencies\n */\nimport { Component } from '@wordpress/element';\nimport { addAction, removeAction } from '@wordpress/hooks';\n\n/**\n * Internal dependencies\n */\nimport { PluginContextProvider } from '../plugin-context';\nimport { PluginErrorBoundary } from '../plugin-error-boundary';\nimport { getPlugins } from '../../api';\n\n/**\n * A component that renders all plugin fills in a hidden div.\n *\n * @example\n * ```js\n * // Using ES5 syntax\n * var el = wp.element.createElement;\n * var PluginArea = wp.plugins.PluginArea;\n *\n * function Layout() {\n * \treturn el(\n * \t\t'div',\n * \t\t{ scope: 'my-page' },\n * \t\t'Content of the page',\n * \t\tPluginArea\n * \t);\n * }\n * ```\n *\n * @example\n * ```js\n * // Using ESNext syntax\n * import { PluginArea } from '@wordpress/plugins';\n *\n * const Layout = () => (\n * \t<div>\n * \t\tContent of the page\n * \t\t<PluginArea scope=\"my-page\" />\n * \t</div>\n * );\n * ```\n *\n * @return {WPComponent} The component to be rendered.\n */\nclass PluginArea extends Component {\n\tconstructor() {\n\t\tsuper( ...arguments );\n\n\t\tthis.setPlugins = this.setPlugins.bind( this );\n\t\tthis.memoizedContext = memoize( ( name, icon ) => {\n\t\t\treturn {\n\t\t\t\tname,\n\t\t\t\ticon,\n\t\t\t};\n\t\t} );\n\t\tthis.state = this.getCurrentPluginsState();\n\t}\n\n\tgetCurrentPluginsState() {\n\t\treturn {\n\t\t\tplugins: getPlugins( this.props.scope ).map(\n\t\t\t\t( { icon, name, render } ) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tPlugin: render,\n\t\t\t\t\t\tcontext: this.memoizedContext( name, icon ),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t),\n\t\t};\n\t}\n\n\tcomponentDidMount() {\n\t\taddAction(\n\t\t\t'plugins.pluginRegistered',\n\t\t\t'core/plugins/plugin-area/plugins-registered',\n\t\t\tthis.setPlugins\n\t\t);\n\t\taddAction(\n\t\t\t'plugins.pluginUnregistered',\n\t\t\t'core/plugins/plugin-area/plugins-unregistered',\n\t\t\tthis.setPlugins\n\t\t);\n\t}\n\n\tcomponentWillUnmount() {\n\t\tremoveAction(\n\t\t\t'plugins.pluginRegistered',\n\t\t\t'core/plugins/plugin-area/plugins-registered'\n\t\t);\n\t\tremoveAction(\n\t\t\t'plugins.pluginUnregistered',\n\t\t\t'core/plugins/plugin-area/plugins-unregistered'\n\t\t);\n\t}\n\n\tsetPlugins() {\n\t\tthis.setState( this.getCurrentPluginsState );\n\t}\n\n\trender() {\n\t\treturn (\n\t\t\t<div style={ { display: 'none' } }>\n\t\t\t\t{ this.state.plugins.map( ( { context, Plugin } ) => (\n\t\t\t\t\t<PluginContextProvider\n\t\t\t\t\t\tkey={ context.name }\n\t\t\t\t\t\tvalue={ context }\n\t\t\t\t\t>\n\t\t\t\t\t\t<PluginErrorBoundary\n\t\t\t\t\t\t\tname={ context.name }\n\t\t\t\t\t\t\tonError={ this.props.onError }\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Plugin />\n\t\t\t\t\t\t</PluginErrorBoundary>\n\t\t\t\t\t</PluginContextProvider>\n\t\t\t\t) ) }\n\t\t\t</div>\n\t\t);\n\t}\n}\n\nexport default PluginArea;\n"]}
1
+ {"version":3,"sources":["@wordpress/plugins/src/components/plugin-area/index.js"],"names":["getPluginContext","icon","name","PluginArea","scope","onError","store","lastValue","subscribe","listener","getValue","nextValue","plugins","display","map","render","Plugin"],"mappings":";;;;;;;;;AAQA;;AALA;;AAMA;;AACA;;AAKA;;AACA;;AACA;;AAjBA;AACA;AACA;;AAGA;AACA;AACA;;AAKA;AACA;AACA;AAKA,MAAMA,gBAAgB,GAAG,qBAAS,CAAEC,IAAF,EAAQC,IAAR,MAAoB;AAAED,EAAAA,IAAF;AAAQC,EAAAA;AAAR,CAApB,CAAT,CAAzB;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA,SAASC,UAAT,OAA0C;AAAA,MAArB;AAAEC,IAAAA,KAAF;AAASC,IAAAA;AAAT,GAAqB;AACzC,QAAMC,KAAK,GAAG,sBAAS,MAAM;AAC5B,QAAIC,SAAJ;AAEA,WAAO;AACNC,MAAAA,SAAS,CAAEC,QAAF,EAAa;AACrB,8BACC,0BADD,EAEC,6CAFD,EAGCA,QAHD;AAKA,8BACC,4BADD,EAEC,+CAFD,EAGCA,QAHD;AAKA,eAAO,MAAM;AACZ,mCACC,0BADD,EAEC,6CAFD;AAIA,mCACC,4BADD,EAEC,+CAFD;AAIA,SATD;AAUA,OAtBK;;AAuBNC,MAAAA,QAAQ,GAAG;AACV,cAAMC,SAAS,GAAG,qBAAYP,KAAZ,CAAlB;;AAEA,YAAK,CAAE,6BAAgBG,SAAhB,EAA2BI,SAA3B,CAAP,EAAgD;AAC/CJ,UAAAA,SAAS,GAAGI,SAAZ;AACA;;AAED,eAAOJ,SAAP;AACA;;AA/BK,KAAP;AAiCA,GApCa,EAoCX,CAAEH,KAAF,CApCW,CAAd;AAsCA,QAAMQ,OAAO,GAAG,mCAAsBN,KAAK,CAACE,SAA5B,EAAuCF,KAAK,CAACI,QAA7C,CAAhB;AAEA,SACC;AAAK,IAAA,KAAK,EAAG;AAAEG,MAAAA,OAAO,EAAE;AAAX;AAAb,KACGD,OAAO,CAACE,GAAR,CAAa;AAAA,QAAE;AAAEb,MAAAA,IAAF;AAAQC,MAAAA,IAAR;AAAca,MAAAA,MAAM,EAAEC;AAAtB,KAAF;AAAA,WACd,4BAAC,oCAAD;AACC,MAAA,GAAG,EAAGd,IADP;AAEC,MAAA,KAAK,EAAGF,gBAAgB,CAAEC,IAAF,EAAQC,IAAR;AAFzB,OAIC,4BAAC,wCAAD;AAAqB,MAAA,IAAI,EAAGA,IAA5B;AAAmC,MAAA,OAAO,EAAGG;AAA7C,OACC,4BAAC,MAAD,OADD,CAJD,CADc;AAAA,GAAb,CADH,CADD;AAcA;;eAEcF,U","sourcesContent":["/**\n * External dependencies\n */\nimport memoize from 'memize';\n\n/**\n * WordPress dependencies\n */\nimport { useMemo, useSyncExternalStore } from '@wordpress/element';\nimport { addAction, removeAction } from '@wordpress/hooks';\nimport isShallowEqual from '@wordpress/is-shallow-equal';\n\n/**\n * Internal dependencies\n */\nimport { PluginContextProvider } from '../plugin-context';\nimport { PluginErrorBoundary } from '../plugin-error-boundary';\nimport { getPlugins } from '../../api';\n\nconst getPluginContext = memoize( ( icon, name ) => ( { icon, name } ) );\n\n/**\n * A component that renders all plugin fills in a hidden div.\n *\n * @param {Object} props\n * @param {string|undefined} props.scope\n * @param {Function|undefined} props.onError\n * @example\n * ```js\n * // Using ES5 syntax\n * var el = wp.element.createElement;\n * var PluginArea = wp.plugins.PluginArea;\n *\n * function Layout() {\n * \treturn el(\n * \t\t'div',\n * \t\t{ scope: 'my-page' },\n * \t\t'Content of the page',\n * \t\tPluginArea\n * \t);\n * }\n * ```\n *\n * @example\n * ```js\n * // Using ESNext syntax\n * import { PluginArea } from '@wordpress/plugins';\n *\n * const Layout = () => (\n * \t<div>\n * \t\tContent of the page\n * \t\t<PluginArea scope=\"my-page\" />\n * \t</div>\n * );\n * ```\n *\n * @return {WPComponent} The component to be rendered.\n */\nfunction PluginArea( { scope, onError } ) {\n\tconst store = useMemo( () => {\n\t\tlet lastValue;\n\n\t\treturn {\n\t\t\tsubscribe( listener ) {\n\t\t\t\taddAction(\n\t\t\t\t\t'plugins.pluginRegistered',\n\t\t\t\t\t'core/plugins/plugin-area/plugins-registered',\n\t\t\t\t\tlistener\n\t\t\t\t);\n\t\t\t\taddAction(\n\t\t\t\t\t'plugins.pluginUnregistered',\n\t\t\t\t\t'core/plugins/plugin-area/plugins-unregistered',\n\t\t\t\t\tlistener\n\t\t\t\t);\n\t\t\t\treturn () => {\n\t\t\t\t\tremoveAction(\n\t\t\t\t\t\t'plugins.pluginRegistered',\n\t\t\t\t\t\t'core/plugins/plugin-area/plugins-registered'\n\t\t\t\t\t);\n\t\t\t\t\tremoveAction(\n\t\t\t\t\t\t'plugins.pluginUnregistered',\n\t\t\t\t\t\t'core/plugins/plugin-area/plugins-unregistered'\n\t\t\t\t\t);\n\t\t\t\t};\n\t\t\t},\n\t\t\tgetValue() {\n\t\t\t\tconst nextValue = getPlugins( scope );\n\n\t\t\t\tif ( ! isShallowEqual( lastValue, nextValue ) ) {\n\t\t\t\t\tlastValue = nextValue;\n\t\t\t\t}\n\n\t\t\t\treturn lastValue;\n\t\t\t},\n\t\t};\n\t}, [ scope ] );\n\n\tconst plugins = useSyncExternalStore( store.subscribe, store.getValue );\n\n\treturn (\n\t\t<div style={ { display: 'none' } }>\n\t\t\t{ plugins.map( ( { icon, name, render: Plugin } ) => (\n\t\t\t\t<PluginContextProvider\n\t\t\t\t\tkey={ name }\n\t\t\t\t\tvalue={ getPluginContext( icon, name ) }\n\t\t\t\t>\n\t\t\t\t\t<PluginErrorBoundary name={ name } onError={ onError }>\n\t\t\t\t\t\t<Plugin />\n\t\t\t\t\t</PluginErrorBoundary>\n\t\t\t\t</PluginContextProvider>\n\t\t\t) ) }\n\t\t</div>\n\t);\n}\n\nexport default PluginArea;\n"]}
@@ -8,8 +8,9 @@ import memoize from 'memize';
8
8
  * WordPress dependencies
9
9
  */
10
10
 
11
- import { Component } from '@wordpress/element';
11
+ import { useMemo, useSyncExternalStore } from '@wordpress/element';
12
12
  import { addAction, removeAction } from '@wordpress/hooks';
13
+ import isShallowEqual from '@wordpress/is-shallow-equal';
13
14
  /**
14
15
  * Internal dependencies
15
16
  */
@@ -17,9 +18,16 @@ import { addAction, removeAction } from '@wordpress/hooks';
17
18
  import { PluginContextProvider } from '../plugin-context';
18
19
  import { PluginErrorBoundary } from '../plugin-error-boundary';
19
20
  import { getPlugins } from '../../api';
21
+ const getPluginContext = memoize((icon, name) => ({
22
+ icon,
23
+ name
24
+ }));
20
25
  /**
21
26
  * A component that renders all plugin fills in a hidden div.
22
27
  *
28
+ * @param {Object} props
29
+ * @param {string|undefined} props.scope
30
+ * @param {Function|undefined} props.onError
23
31
  * @example
24
32
  * ```js
25
33
  * // Using ES5 syntax
@@ -52,69 +60,54 @@ import { getPlugins } from '../../api';
52
60
  * @return {WPComponent} The component to be rendered.
53
61
  */
54
62
 
55
- class PluginArea extends Component {
56
- constructor() {
57
- super(...arguments);
58
- this.setPlugins = this.setPlugins.bind(this);
59
- this.memoizedContext = memoize((name, icon) => {
60
- return {
61
- name,
62
- icon
63
- };
64
- });
65
- this.state = this.getCurrentPluginsState();
66
- }
67
-
68
- getCurrentPluginsState() {
63
+ function PluginArea(_ref) {
64
+ let {
65
+ scope,
66
+ onError
67
+ } = _ref;
68
+ const store = useMemo(() => {
69
+ let lastValue;
69
70
  return {
70
- plugins: getPlugins(this.props.scope).map(_ref => {
71
- let {
72
- icon,
73
- name,
74
- render
75
- } = _ref;
76
- return {
77
- Plugin: render,
78
- context: this.memoizedContext(name, icon)
71
+ subscribe(listener) {
72
+ addAction('plugins.pluginRegistered', 'core/plugins/plugin-area/plugins-registered', listener);
73
+ addAction('plugins.pluginUnregistered', 'core/plugins/plugin-area/plugins-unregistered', listener);
74
+ return () => {
75
+ removeAction('plugins.pluginRegistered', 'core/plugins/plugin-area/plugins-registered');
76
+ removeAction('plugins.pluginUnregistered', 'core/plugins/plugin-area/plugins-unregistered');
79
77
  };
80
- })
81
- };
82
- }
78
+ },
83
79
 
84
- componentDidMount() {
85
- addAction('plugins.pluginRegistered', 'core/plugins/plugin-area/plugins-registered', this.setPlugins);
86
- addAction('plugins.pluginUnregistered', 'core/plugins/plugin-area/plugins-unregistered', this.setPlugins);
87
- }
80
+ getValue() {
81
+ const nextValue = getPlugins(scope);
88
82
 
89
- componentWillUnmount() {
90
- removeAction('plugins.pluginRegistered', 'core/plugins/plugin-area/plugins-registered');
91
- removeAction('plugins.pluginUnregistered', 'core/plugins/plugin-area/plugins-unregistered');
92
- }
83
+ if (!isShallowEqual(lastValue, nextValue)) {
84
+ lastValue = nextValue;
85
+ }
93
86
 
94
- setPlugins() {
95
- this.setState(this.getCurrentPluginsState);
96
- }
97
-
98
- render() {
99
- return createElement("div", {
100
- style: {
101
- display: 'none'
87
+ return lastValue;
102
88
  }
103
- }, this.state.plugins.map(_ref2 => {
104
- let {
105
- context,
106
- Plugin
107
- } = _ref2;
108
- return createElement(PluginContextProvider, {
109
- key: context.name,
110
- value: context
111
- }, createElement(PluginErrorBoundary, {
112
- name: context.name,
113
- onError: this.props.onError
114
- }, createElement(Plugin, null)));
115
- }));
116
- }
117
89
 
90
+ };
91
+ }, [scope]);
92
+ const plugins = useSyncExternalStore(store.subscribe, store.getValue);
93
+ return createElement("div", {
94
+ style: {
95
+ display: 'none'
96
+ }
97
+ }, plugins.map(_ref2 => {
98
+ let {
99
+ icon,
100
+ name,
101
+ render: Plugin
102
+ } = _ref2;
103
+ return createElement(PluginContextProvider, {
104
+ key: name,
105
+ value: getPluginContext(icon, name)
106
+ }, createElement(PluginErrorBoundary, {
107
+ name: name,
108
+ onError: onError
109
+ }, createElement(Plugin, null)));
110
+ }));
118
111
  }
119
112
 
120
113
  export default PluginArea;
@@ -1 +1 @@
1
- {"version":3,"sources":["@wordpress/plugins/src/components/plugin-area/index.js"],"names":["memoize","Component","addAction","removeAction","PluginContextProvider","PluginErrorBoundary","getPlugins","PluginArea","constructor","arguments","setPlugins","bind","memoizedContext","name","icon","state","getCurrentPluginsState","plugins","props","scope","map","render","Plugin","context","componentDidMount","componentWillUnmount","setState","display","onError"],"mappings":";;AAAA;AACA;AACA;AACA,OAAOA,OAAP,MAAoB,QAApB;AAEA;AACA;AACA;;AACA,SAASC,SAAT,QAA0B,oBAA1B;AACA,SAASC,SAAT,EAAoBC,YAApB,QAAwC,kBAAxC;AAEA;AACA;AACA;;AACA,SAASC,qBAAT,QAAsC,mBAAtC;AACA,SAASC,mBAAT,QAAoC,0BAApC;AACA,SAASC,UAAT,QAA2B,WAA3B;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA,MAAMC,UAAN,SAAyBN,SAAzB,CAAmC;AAClCO,EAAAA,WAAW,GAAG;AACb,UAAO,GAAGC,SAAV;AAEA,SAAKC,UAAL,GAAkB,KAAKA,UAAL,CAAgBC,IAAhB,CAAsB,IAAtB,CAAlB;AACA,SAAKC,eAAL,GAAuBZ,OAAO,CAAE,CAAEa,IAAF,EAAQC,IAAR,KAAkB;AACjD,aAAO;AACND,QAAAA,IADM;AAENC,QAAAA;AAFM,OAAP;AAIA,KAL6B,CAA9B;AAMA,SAAKC,KAAL,GAAa,KAAKC,sBAAL,EAAb;AACA;;AAEDA,EAAAA,sBAAsB,GAAG;AACxB,WAAO;AACNC,MAAAA,OAAO,EAAEX,UAAU,CAAE,KAAKY,KAAL,CAAWC,KAAb,CAAV,CAA+BC,GAA/B,CACR,QAA8B;AAAA,YAA5B;AAAEN,UAAAA,IAAF;AAAQD,UAAAA,IAAR;AAAcQ,UAAAA;AAAd,SAA4B;AAC7B,eAAO;AACNC,UAAAA,MAAM,EAAED,MADF;AAENE,UAAAA,OAAO,EAAE,KAAKX,eAAL,CAAsBC,IAAtB,EAA4BC,IAA5B;AAFH,SAAP;AAIA,OANO;AADH,KAAP;AAUA;;AAEDU,EAAAA,iBAAiB,GAAG;AACnBtB,IAAAA,SAAS,CACR,0BADQ,EAER,6CAFQ,EAGR,KAAKQ,UAHG,CAAT;AAKAR,IAAAA,SAAS,CACR,4BADQ,EAER,+CAFQ,EAGR,KAAKQ,UAHG,CAAT;AAKA;;AAEDe,EAAAA,oBAAoB,GAAG;AACtBtB,IAAAA,YAAY,CACX,0BADW,EAEX,6CAFW,CAAZ;AAIAA,IAAAA,YAAY,CACX,4BADW,EAEX,+CAFW,CAAZ;AAIA;;AAEDO,EAAAA,UAAU,GAAG;AACZ,SAAKgB,QAAL,CAAe,KAAKV,sBAApB;AACA;;AAEDK,EAAAA,MAAM,GAAG;AACR,WACC;AAAK,MAAA,KAAK,EAAG;AAAEM,QAAAA,OAAO,EAAE;AAAX;AAAb,OACG,KAAKZ,KAAL,CAAWE,OAAX,CAAmBG,GAAnB,CAAwB;AAAA,UAAE;AAAEG,QAAAA,OAAF;AAAWD,QAAAA;AAAX,OAAF;AAAA,aACzB,cAAC,qBAAD;AACC,QAAA,GAAG,EAAGC,OAAO,CAACV,IADf;AAEC,QAAA,KAAK,EAAGU;AAFT,SAIC,cAAC,mBAAD;AACC,QAAA,IAAI,EAAGA,OAAO,CAACV,IADhB;AAEC,QAAA,OAAO,EAAG,KAAKK,KAAL,CAAWU;AAFtB,SAIC,cAAC,MAAD,OAJD,CAJD,CADyB;AAAA,KAAxB,CADH,CADD;AAiBA;;AAzEiC;;AA4EnC,eAAerB,UAAf","sourcesContent":["/**\n * External dependencies\n */\nimport memoize from 'memize';\n\n/**\n * WordPress dependencies\n */\nimport { Component } from '@wordpress/element';\nimport { addAction, removeAction } from '@wordpress/hooks';\n\n/**\n * Internal dependencies\n */\nimport { PluginContextProvider } from '../plugin-context';\nimport { PluginErrorBoundary } from '../plugin-error-boundary';\nimport { getPlugins } from '../../api';\n\n/**\n * A component that renders all plugin fills in a hidden div.\n *\n * @example\n * ```js\n * // Using ES5 syntax\n * var el = wp.element.createElement;\n * var PluginArea = wp.plugins.PluginArea;\n *\n * function Layout() {\n * \treturn el(\n * \t\t'div',\n * \t\t{ scope: 'my-page' },\n * \t\t'Content of the page',\n * \t\tPluginArea\n * \t);\n * }\n * ```\n *\n * @example\n * ```js\n * // Using ESNext syntax\n * import { PluginArea } from '@wordpress/plugins';\n *\n * const Layout = () => (\n * \t<div>\n * \t\tContent of the page\n * \t\t<PluginArea scope=\"my-page\" />\n * \t</div>\n * );\n * ```\n *\n * @return {WPComponent} The component to be rendered.\n */\nclass PluginArea extends Component {\n\tconstructor() {\n\t\tsuper( ...arguments );\n\n\t\tthis.setPlugins = this.setPlugins.bind( this );\n\t\tthis.memoizedContext = memoize( ( name, icon ) => {\n\t\t\treturn {\n\t\t\t\tname,\n\t\t\t\ticon,\n\t\t\t};\n\t\t} );\n\t\tthis.state = this.getCurrentPluginsState();\n\t}\n\n\tgetCurrentPluginsState() {\n\t\treturn {\n\t\t\tplugins: getPlugins( this.props.scope ).map(\n\t\t\t\t( { icon, name, render } ) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tPlugin: render,\n\t\t\t\t\t\tcontext: this.memoizedContext( name, icon ),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t),\n\t\t};\n\t}\n\n\tcomponentDidMount() {\n\t\taddAction(\n\t\t\t'plugins.pluginRegistered',\n\t\t\t'core/plugins/plugin-area/plugins-registered',\n\t\t\tthis.setPlugins\n\t\t);\n\t\taddAction(\n\t\t\t'plugins.pluginUnregistered',\n\t\t\t'core/plugins/plugin-area/plugins-unregistered',\n\t\t\tthis.setPlugins\n\t\t);\n\t}\n\n\tcomponentWillUnmount() {\n\t\tremoveAction(\n\t\t\t'plugins.pluginRegistered',\n\t\t\t'core/plugins/plugin-area/plugins-registered'\n\t\t);\n\t\tremoveAction(\n\t\t\t'plugins.pluginUnregistered',\n\t\t\t'core/plugins/plugin-area/plugins-unregistered'\n\t\t);\n\t}\n\n\tsetPlugins() {\n\t\tthis.setState( this.getCurrentPluginsState );\n\t}\n\n\trender() {\n\t\treturn (\n\t\t\t<div style={ { display: 'none' } }>\n\t\t\t\t{ this.state.plugins.map( ( { context, Plugin } ) => (\n\t\t\t\t\t<PluginContextProvider\n\t\t\t\t\t\tkey={ context.name }\n\t\t\t\t\t\tvalue={ context }\n\t\t\t\t\t>\n\t\t\t\t\t\t<PluginErrorBoundary\n\t\t\t\t\t\t\tname={ context.name }\n\t\t\t\t\t\t\tonError={ this.props.onError }\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Plugin />\n\t\t\t\t\t\t</PluginErrorBoundary>\n\t\t\t\t\t</PluginContextProvider>\n\t\t\t\t) ) }\n\t\t\t</div>\n\t\t);\n\t}\n}\n\nexport default PluginArea;\n"]}
1
+ {"version":3,"sources":["@wordpress/plugins/src/components/plugin-area/index.js"],"names":["memoize","useMemo","useSyncExternalStore","addAction","removeAction","isShallowEqual","PluginContextProvider","PluginErrorBoundary","getPlugins","getPluginContext","icon","name","PluginArea","scope","onError","store","lastValue","subscribe","listener","getValue","nextValue","plugins","display","map","render","Plugin"],"mappings":";;AAAA;AACA;AACA;AACA,OAAOA,OAAP,MAAoB,QAApB;AAEA;AACA;AACA;;AACA,SAASC,OAAT,EAAkBC,oBAAlB,QAA8C,oBAA9C;AACA,SAASC,SAAT,EAAoBC,YAApB,QAAwC,kBAAxC;AACA,OAAOC,cAAP,MAA2B,6BAA3B;AAEA;AACA;AACA;;AACA,SAASC,qBAAT,QAAsC,mBAAtC;AACA,SAASC,mBAAT,QAAoC,0BAApC;AACA,SAASC,UAAT,QAA2B,WAA3B;AAEA,MAAMC,gBAAgB,GAAGT,OAAO,CAAE,CAAEU,IAAF,EAAQC,IAAR,MAAoB;AAAED,EAAAA,IAAF;AAAQC,EAAAA;AAAR,CAApB,CAAF,CAAhC;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA,SAASC,UAAT,OAA0C;AAAA,MAArB;AAAEC,IAAAA,KAAF;AAASC,IAAAA;AAAT,GAAqB;AACzC,QAAMC,KAAK,GAAGd,OAAO,CAAE,MAAM;AAC5B,QAAIe,SAAJ;AAEA,WAAO;AACNC,MAAAA,SAAS,CAAEC,QAAF,EAAa;AACrBf,QAAAA,SAAS,CACR,0BADQ,EAER,6CAFQ,EAGRe,QAHQ,CAAT;AAKAf,QAAAA,SAAS,CACR,4BADQ,EAER,+CAFQ,EAGRe,QAHQ,CAAT;AAKA,eAAO,MAAM;AACZd,UAAAA,YAAY,CACX,0BADW,EAEX,6CAFW,CAAZ;AAIAA,UAAAA,YAAY,CACX,4BADW,EAEX,+CAFW,CAAZ;AAIA,SATD;AAUA,OAtBK;;AAuBNe,MAAAA,QAAQ,GAAG;AACV,cAAMC,SAAS,GAAGZ,UAAU,CAAEK,KAAF,CAA5B;;AAEA,YAAK,CAAER,cAAc,CAAEW,SAAF,EAAaI,SAAb,CAArB,EAAgD;AAC/CJ,UAAAA,SAAS,GAAGI,SAAZ;AACA;;AAED,eAAOJ,SAAP;AACA;;AA/BK,KAAP;AAiCA,GApCoB,EAoClB,CAAEH,KAAF,CApCkB,CAArB;AAsCA,QAAMQ,OAAO,GAAGnB,oBAAoB,CAAEa,KAAK,CAACE,SAAR,EAAmBF,KAAK,CAACI,QAAzB,CAApC;AAEA,SACC;AAAK,IAAA,KAAK,EAAG;AAAEG,MAAAA,OAAO,EAAE;AAAX;AAAb,KACGD,OAAO,CAACE,GAAR,CAAa;AAAA,QAAE;AAAEb,MAAAA,IAAF;AAAQC,MAAAA,IAAR;AAAca,MAAAA,MAAM,EAAEC;AAAtB,KAAF;AAAA,WACd,cAAC,qBAAD;AACC,MAAA,GAAG,EAAGd,IADP;AAEC,MAAA,KAAK,EAAGF,gBAAgB,CAAEC,IAAF,EAAQC,IAAR;AAFzB,OAIC,cAAC,mBAAD;AAAqB,MAAA,IAAI,EAAGA,IAA5B;AAAmC,MAAA,OAAO,EAAGG;AAA7C,OACC,cAAC,MAAD,OADD,CAJD,CADc;AAAA,GAAb,CADH,CADD;AAcA;;AAED,eAAeF,UAAf","sourcesContent":["/**\n * External dependencies\n */\nimport memoize from 'memize';\n\n/**\n * WordPress dependencies\n */\nimport { useMemo, useSyncExternalStore } from '@wordpress/element';\nimport { addAction, removeAction } from '@wordpress/hooks';\nimport isShallowEqual from '@wordpress/is-shallow-equal';\n\n/**\n * Internal dependencies\n */\nimport { PluginContextProvider } from '../plugin-context';\nimport { PluginErrorBoundary } from '../plugin-error-boundary';\nimport { getPlugins } from '../../api';\n\nconst getPluginContext = memoize( ( icon, name ) => ( { icon, name } ) );\n\n/**\n * A component that renders all plugin fills in a hidden div.\n *\n * @param {Object} props\n * @param {string|undefined} props.scope\n * @param {Function|undefined} props.onError\n * @example\n * ```js\n * // Using ES5 syntax\n * var el = wp.element.createElement;\n * var PluginArea = wp.plugins.PluginArea;\n *\n * function Layout() {\n * \treturn el(\n * \t\t'div',\n * \t\t{ scope: 'my-page' },\n * \t\t'Content of the page',\n * \t\tPluginArea\n * \t);\n * }\n * ```\n *\n * @example\n * ```js\n * // Using ESNext syntax\n * import { PluginArea } from '@wordpress/plugins';\n *\n * const Layout = () => (\n * \t<div>\n * \t\tContent of the page\n * \t\t<PluginArea scope=\"my-page\" />\n * \t</div>\n * );\n * ```\n *\n * @return {WPComponent} The component to be rendered.\n */\nfunction PluginArea( { scope, onError } ) {\n\tconst store = useMemo( () => {\n\t\tlet lastValue;\n\n\t\treturn {\n\t\t\tsubscribe( listener ) {\n\t\t\t\taddAction(\n\t\t\t\t\t'plugins.pluginRegistered',\n\t\t\t\t\t'core/plugins/plugin-area/plugins-registered',\n\t\t\t\t\tlistener\n\t\t\t\t);\n\t\t\t\taddAction(\n\t\t\t\t\t'plugins.pluginUnregistered',\n\t\t\t\t\t'core/plugins/plugin-area/plugins-unregistered',\n\t\t\t\t\tlistener\n\t\t\t\t);\n\t\t\t\treturn () => {\n\t\t\t\t\tremoveAction(\n\t\t\t\t\t\t'plugins.pluginRegistered',\n\t\t\t\t\t\t'core/plugins/plugin-area/plugins-registered'\n\t\t\t\t\t);\n\t\t\t\t\tremoveAction(\n\t\t\t\t\t\t'plugins.pluginUnregistered',\n\t\t\t\t\t\t'core/plugins/plugin-area/plugins-unregistered'\n\t\t\t\t\t);\n\t\t\t\t};\n\t\t\t},\n\t\t\tgetValue() {\n\t\t\t\tconst nextValue = getPlugins( scope );\n\n\t\t\t\tif ( ! isShallowEqual( lastValue, nextValue ) ) {\n\t\t\t\t\tlastValue = nextValue;\n\t\t\t\t}\n\n\t\t\t\treturn lastValue;\n\t\t\t},\n\t\t};\n\t}, [ scope ] );\n\n\tconst plugins = useSyncExternalStore( store.subscribe, store.getValue );\n\n\treturn (\n\t\t<div style={ { display: 'none' } }>\n\t\t\t{ plugins.map( ( { icon, name, render: Plugin } ) => (\n\t\t\t\t<PluginContextProvider\n\t\t\t\t\tkey={ name }\n\t\t\t\t\tvalue={ getPluginContext( icon, name ) }\n\t\t\t\t>\n\t\t\t\t\t<PluginErrorBoundary name={ name } onError={ onError }>\n\t\t\t\t\t\t<Plugin />\n\t\t\t\t\t</PluginErrorBoundary>\n\t\t\t\t</PluginContextProvider>\n\t\t\t) ) }\n\t\t</div>\n\t);\n}\n\nexport default PluginArea;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/plugins",
3
- "version": "5.6.0",
3
+ "version": "5.7.0",
4
4
  "description": "Plugins module for WordPress.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -26,10 +26,11 @@
26
26
  "react-native": "src/index",
27
27
  "dependencies": {
28
28
  "@babel/runtime": "^7.16.0",
29
- "@wordpress/compose": "^6.6.0",
30
- "@wordpress/element": "^5.6.0",
31
- "@wordpress/hooks": "^3.29.0",
32
- "@wordpress/icons": "^9.20.0",
29
+ "@wordpress/compose": "^6.7.0",
30
+ "@wordpress/element": "^5.7.0",
31
+ "@wordpress/hooks": "^3.30.0",
32
+ "@wordpress/icons": "^9.21.0",
33
+ "@wordpress/is-shallow-equal": "^4.30.0",
33
34
  "memize": "^1.1.0"
34
35
  },
35
36
  "peerDependencies": {
@@ -38,5 +39,5 @@
38
39
  "publishConfig": {
39
40
  "access": "public"
40
41
  },
41
- "gitHead": "9534a7b3bbf07c1d40b94fdb7a3d091f297bfb06"
42
+ "gitHead": "d5c28a67b11e91e3e4b8e90346bfcb90909364d6"
42
43
  }
@@ -6,8 +6,9 @@ import memoize from 'memize';
6
6
  /**
7
7
  * WordPress dependencies
8
8
  */
9
- import { Component } from '@wordpress/element';
9
+ import { useMemo, useSyncExternalStore } from '@wordpress/element';
10
10
  import { addAction, removeAction } from '@wordpress/hooks';
11
+ import isShallowEqual from '@wordpress/is-shallow-equal';
11
12
 
12
13
  /**
13
14
  * Internal dependencies
@@ -16,9 +17,14 @@ import { PluginContextProvider } from '../plugin-context';
16
17
  import { PluginErrorBoundary } from '../plugin-error-boundary';
17
18
  import { getPlugins } from '../../api';
18
19
 
20
+ const getPluginContext = memoize( ( icon, name ) => ( { icon, name } ) );
21
+
19
22
  /**
20
23
  * A component that renders all plugin fills in a hidden div.
21
24
  *
25
+ * @param {Object} props
26
+ * @param {string|undefined} props.scope
27
+ * @param {Function|undefined} props.onError
22
28
  * @example
23
29
  * ```js
24
30
  * // Using ES5 syntax
@@ -50,80 +56,61 @@ import { getPlugins } from '../../api';
50
56
  *
51
57
  * @return {WPComponent} The component to be rendered.
52
58
  */
53
- class PluginArea extends Component {
54
- constructor() {
55
- super( ...arguments );
56
-
57
- this.setPlugins = this.setPlugins.bind( this );
58
- this.memoizedContext = memoize( ( name, icon ) => {
59
- return {
60
- name,
61
- icon,
62
- };
63
- } );
64
- this.state = this.getCurrentPluginsState();
65
- }
59
+ function PluginArea( { scope, onError } ) {
60
+ const store = useMemo( () => {
61
+ let lastValue;
66
62
 
67
- getCurrentPluginsState() {
68
63
  return {
69
- plugins: getPlugins( this.props.scope ).map(
70
- ( { icon, name, render } ) => {
71
- return {
72
- Plugin: render,
73
- context: this.memoizedContext( name, icon ),
74
- };
75
- }
76
- ),
77
- };
78
- }
64
+ subscribe( listener ) {
65
+ addAction(
66
+ 'plugins.pluginRegistered',
67
+ 'core/plugins/plugin-area/plugins-registered',
68
+ listener
69
+ );
70
+ addAction(
71
+ 'plugins.pluginUnregistered',
72
+ 'core/plugins/plugin-area/plugins-unregistered',
73
+ listener
74
+ );
75
+ return () => {
76
+ removeAction(
77
+ 'plugins.pluginRegistered',
78
+ 'core/plugins/plugin-area/plugins-registered'
79
+ );
80
+ removeAction(
81
+ 'plugins.pluginUnregistered',
82
+ 'core/plugins/plugin-area/plugins-unregistered'
83
+ );
84
+ };
85
+ },
86
+ getValue() {
87
+ const nextValue = getPlugins( scope );
79
88
 
80
- componentDidMount() {
81
- addAction(
82
- 'plugins.pluginRegistered',
83
- 'core/plugins/plugin-area/plugins-registered',
84
- this.setPlugins
85
- );
86
- addAction(
87
- 'plugins.pluginUnregistered',
88
- 'core/plugins/plugin-area/plugins-unregistered',
89
- this.setPlugins
90
- );
91
- }
89
+ if ( ! isShallowEqual( lastValue, nextValue ) ) {
90
+ lastValue = nextValue;
91
+ }
92
92
 
93
- componentWillUnmount() {
94
- removeAction(
95
- 'plugins.pluginRegistered',
96
- 'core/plugins/plugin-area/plugins-registered'
97
- );
98
- removeAction(
99
- 'plugins.pluginUnregistered',
100
- 'core/plugins/plugin-area/plugins-unregistered'
101
- );
102
- }
93
+ return lastValue;
94
+ },
95
+ };
96
+ }, [ scope ] );
103
97
 
104
- setPlugins() {
105
- this.setState( this.getCurrentPluginsState );
106
- }
98
+ const plugins = useSyncExternalStore( store.subscribe, store.getValue );
107
99
 
108
- render() {
109
- return (
110
- <div style={ { display: 'none' } }>
111
- { this.state.plugins.map( ( { context, Plugin } ) => (
112
- <PluginContextProvider
113
- key={ context.name }
114
- value={ context }
115
- >
116
- <PluginErrorBoundary
117
- name={ context.name }
118
- onError={ this.props.onError }
119
- >
120
- <Plugin />
121
- </PluginErrorBoundary>
122
- </PluginContextProvider>
123
- ) ) }
124
- </div>
125
- );
126
- }
100
+ return (
101
+ <div style={ { display: 'none' } }>
102
+ { plugins.map( ( { icon, name, render: Plugin } ) => (
103
+ <PluginContextProvider
104
+ key={ name }
105
+ value={ getPluginContext( icon, name ) }
106
+ >
107
+ <PluginErrorBoundary name={ name } onError={ onError }>
108
+ <Plugin />
109
+ </PluginErrorBoundary>
110
+ </PluginContextProvider>
111
+ ) ) }
112
+ </div>
113
+ );
127
114
  }
128
115
 
129
116
  export default PluginArea;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { act, render, cleanup } from '@testing-library/react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { getPlugins, unregisterPlugin, registerPlugin } from '../../api';
10
+ import PluginArea from '../plugin-area';
11
+
12
+ describe( 'PluginArea', () => {
13
+ afterEach( () => {
14
+ // Unmount components before unregistering the plugins.
15
+ // RTL uses top-level `afterEach` for cleanup, executed after this teardown.
16
+ cleanup();
17
+ getPlugins().forEach( ( plugin ) => {
18
+ unregisterPlugin( plugin.name );
19
+ } );
20
+ getPlugins( 'my-app' ).forEach( ( plugin ) => {
21
+ unregisterPlugin( plugin.name );
22
+ } );
23
+ } );
24
+
25
+ const TestComponent = ( { content } ) => {
26
+ return `plugin: ${ content }.`;
27
+ };
28
+
29
+ test( 'renders unscoped plugin', () => {
30
+ registerPlugin( 'unscoped', {
31
+ render: () => <TestComponent content="unscoped" />,
32
+ icon: 'smiley',
33
+ } );
34
+
35
+ const { container } = render( <PluginArea /> );
36
+
37
+ expect( container ).toHaveTextContent( 'plugin: unscoped.' );
38
+ } );
39
+
40
+ test( 'renders scoped plugin', () => {
41
+ registerPlugin( 'scoped', {
42
+ render: () => <TestComponent content="scoped" />,
43
+ icon: 'smiley',
44
+ scope: 'my-app',
45
+ } );
46
+
47
+ const { container } = render( <PluginArea scope="my-app" /> );
48
+
49
+ expect( container ).toHaveTextContent( 'plugin: scoped.' );
50
+ } );
51
+
52
+ test( 'rerenders when a new plugin is registered', () => {
53
+ registerPlugin( 'foo', {
54
+ render: () => <TestComponent content="foo" />,
55
+ icon: 'smiley',
56
+ scope: 'my-app',
57
+ } );
58
+
59
+ const { container } = render( <PluginArea scope="my-app" /> );
60
+
61
+ act( () => {
62
+ registerPlugin( 'bar', {
63
+ render: () => <TestComponent content="bar" />,
64
+ icon: 'smiley',
65
+ scope: 'my-app',
66
+ } );
67
+ } );
68
+
69
+ expect( container ).toHaveTextContent( 'plugin: bar.' );
70
+ } );
71
+
72
+ test( 'rerenders when a plugin is unregistered', () => {
73
+ registerPlugin( 'one', {
74
+ render: () => <TestComponent content="one" />,
75
+ icon: 'smiley',
76
+ scope: 'my-app',
77
+ } );
78
+ registerPlugin( 'two', {
79
+ render: () => <TestComponent content="two" />,
80
+ icon: 'smiley',
81
+ scope: 'my-app',
82
+ } );
83
+
84
+ const { container } = render( <PluginArea scope="my-app" /> );
85
+
86
+ expect( container ).toHaveTextContent( 'plugin: one.plugin: two.' );
87
+
88
+ act( () => {
89
+ unregisterPlugin( 'one' );
90
+ } );
91
+
92
+ expect( container ).toHaveTextContent( 'plugin: two.' );
93
+ } );
94
+
95
+ test( 'does not rerender when a plugin is added to a different scope', () => {
96
+ const ComponentSpy = jest.fn( ( { content } ) => {
97
+ return `plugin: ${ content }.`;
98
+ } );
99
+
100
+ registerPlugin( 'scoped', {
101
+ render: () => <ComponentSpy content="scoped" />,
102
+ icon: 'smiley',
103
+ scope: 'my-app',
104
+ } );
105
+
106
+ render( <PluginArea scope="my-app" /> );
107
+
108
+ act( () => {
109
+ registerPlugin( 'unscoped', {
110
+ render: () => <TestComponent content="unscoped" />,
111
+ icon: 'smiley',
112
+ } );
113
+ } );
114
+
115
+ expect( ComponentSpy ).toHaveBeenCalledTimes( 1 );
116
+ } );
117
+ } );