@plone/volto 14.7.1 → 14.8.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
@@ -1,5 +1,22 @@
1
1
  # Change Log
2
2
 
3
+ ## 14.8.0 (2022-02-03)
4
+
5
+ ### Feature
6
+
7
+ - Enable `components` property in Volto's config registry. Does not expose any direct feature but this will open the door to be able to override registered components using the config registry and avoid using shadowing explicitly. @sneridagh
8
+ - Add `resolve` and `register` helper methods for the Volto config. They retrieve and register new components in the registry. @tiberiuichim @sneridagh
9
+ - Add `Component` component, given a `name` of a component registered in the registry, it renders it, passing down the props. @tiberiuichim
10
+ - Syncronize the content language with the UI language in multilingual sites. So when you are accessing a content in a given language the rest of the interface literals follow along (it updates the language cookie). So the UI remains consistent. @sneridagh
11
+
12
+ ### Bugfix
13
+
14
+ - Fix the a11y violation of UrlWidget @iRohitSingh
15
+
16
+ ### Internal
17
+
18
+ - Update volta pins in package.json @fredvd
19
+
3
20
  ## 14.7.1 (2022-02-02)
4
21
 
5
22
  ### Internal
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "14.7.1",
12
+ "version": "14.8.0",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -434,7 +434,7 @@
434
434
  "ua-parser-js": "0.7.28"
435
435
  },
436
436
  "volta": {
437
- "node": "14.17.0",
438
- "yarn": "1.19.1"
437
+ "node": "16.13.2",
438
+ "yarn": "1.22.17"
439
439
  }
440
440
  }
@@ -101,6 +101,7 @@ export const UrlWidget = (props) => {
101
101
  <Button
102
102
  basic
103
103
  className="cancel"
104
+ aria-label="clearUrlBrowser"
104
105
  onClick={(e) => {
105
106
  e.preventDefault();
106
107
  e.stopPropagation();
@@ -115,6 +116,7 @@ export const UrlWidget = (props) => {
115
116
  <Button
116
117
  basic
117
118
  icon
119
+ aria-label="openUrlBrowser"
118
120
  onClick={(e) => {
119
121
  e.preventDefault();
120
122
  e.stopPropagation();
@@ -146,7 +146,10 @@ class App extends Component {
146
146
  <SkipLinks />
147
147
  <Header pathname={path} />
148
148
  <Breadcrumbs pathname={path} />
149
- <MultilingualRedirector pathname={this.props.pathname}>
149
+ <MultilingualRedirector
150
+ pathname={this.props.pathname}
151
+ contentLanguage={this.props.content?.language?.token}
152
+ >
150
153
  <Segment basic className="content-area">
151
154
  <main>
152
155
  <OutdatedBrowser />
@@ -0,0 +1,19 @@
1
+ import registry from '@plone/volto/registry';
2
+
3
+ /**
4
+ * A component that can autommatically look up its implementation from the
5
+ * registry based on the provided component name
6
+ */
7
+ const Component = ({ name, ...rest }) => {
8
+ const Component = registry.resolve(name)?.component;
9
+
10
+ if (!Component) {
11
+ // eslint-disable-next-line no-console
12
+ console.warn(`Component not found in registry: ${name}`);
13
+ return null;
14
+ }
15
+
16
+ return <Component {...rest} />;
17
+ };
18
+
19
+ export default Component;
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import '@testing-library/jest-dom/extend-expect';
3
+ import { render } from '@testing-library/react';
4
+ import Component from './Component';
5
+ import config from '@plone/volto/registry';
6
+
7
+ config.set('components', {
8
+ Toolbar: { component: (props) => <div>this is the Toolbar component</div> },
9
+ 'Toolbar.Types': {
10
+ component: ({ teststring }) => (
11
+ <div>this is the Toolbar component with a prop {teststring} in it</div>
12
+ ),
13
+ },
14
+ });
15
+
16
+ describe('Component component :P', () => {
17
+ it('Render a Component in the registry', () => {
18
+ const { container } = render(<Component name="Toolbar" />);
19
+ expect(container).toMatchSnapshot();
20
+ });
21
+ it('Renders a Fallback Component that does not exists in the registry', () => {
22
+ const { container } = render(<Component name="Toolbar.Foo" />);
23
+ expect(container).toMatchSnapshot();
24
+ });
25
+ it('Renders a Component in the registry - passes props correctly', () => {
26
+ const { container } = render(
27
+ <Component name="Toolbar.Types" teststring="Hi!" />,
28
+ );
29
+ expect(container).toMatchSnapshot();
30
+ });
31
+ });
@@ -8,7 +8,7 @@ import { normalizeLanguageName } from '@plone/volto/helpers';
8
8
 
9
9
  const MultilingualRedirector = (props) => {
10
10
  const { settings } = config;
11
- const { pathname, children } = props;
11
+ const { pathname, contentLanguage, children } = props;
12
12
  const currentLanguage =
13
13
  cookie.load('I18N_LANGUAGE') || settings.defaultLanguage;
14
14
  const redirectToLanguage = settings.supportedLanguages.includes(
@@ -18,6 +18,33 @@ const MultilingualRedirector = (props) => {
18
18
  : settings.defaultLanguage;
19
19
  const dispatch = useDispatch();
20
20
 
21
+ React.useEffect(() => {
22
+ // This ensures the current content language and the current active language in the
23
+ // UI are the same. Otherwise, there are inconsistencies between the UI main elements
24
+ // eg. Home link in breadcrumbs, other i18n dependant literals from the main UI and
25
+ // the current content language.
26
+ if (
27
+ contentLanguage &&
28
+ currentLanguage !== contentLanguage &&
29
+ pathname &&
30
+ // We don't want to trigger it in Babel View, since Babel view already takes care
31
+ // of it
32
+ !pathname.endsWith('/add') &&
33
+ settings.isMultilingual
34
+ ) {
35
+ const langFileName = normalizeLanguageName(contentLanguage);
36
+ import('~/../locales/' + langFileName + '.json').then((locale) => {
37
+ dispatch(changeLanguage(contentLanguage, locale.default));
38
+ });
39
+ }
40
+ }, [
41
+ pathname,
42
+ dispatch,
43
+ currentLanguage,
44
+ contentLanguage,
45
+ settings.isMultilingual,
46
+ ]);
47
+
21
48
  React.useEffect(() => {
22
49
  // ToDo: Add means to support language negotiation (with config)
23
50
  // const detectedLang = (navigator.language || navigator.userLanguage).substring(0, 2);
@@ -192,3 +192,4 @@ ConfigRegistry.widgets = config.widgets;
192
192
  ConfigRegistry.addonRoutes = config.addonRoutes;
193
193
  ConfigRegistry.addonReducers = config.addonReducers;
194
194
  ConfigRegistry.appExtras = config.appExtras;
195
+ ConfigRegistry.components = config.components;
package/src/registry.js CHANGED
@@ -79,6 +79,22 @@ class Config {
79
79
  set slots(slots) {
80
80
  this._data.slots = slots;
81
81
  }
82
+
83
+ get components() {
84
+ return this._data.components;
85
+ }
86
+
87
+ set components(components) {
88
+ this._data.components = components;
89
+ }
90
+
91
+ resolve(component) {
92
+ return this._data.components[component] || {};
93
+ }
94
+
95
+ register(name, component) {
96
+ this._data.components[name] = component;
97
+ }
82
98
  }
83
99
 
84
100
  const instance = new Config();
@@ -0,0 +1,28 @@
1
+ import config from './registry';
2
+
3
+ config.set('components', {
4
+ Toolbar: { component: 'this is the Toolbar component' },
5
+ 'Toolbar.Types': { component: 'this is the Types component' },
6
+ });
7
+
8
+ describe('registry', () => {
9
+ it('resolve components', () => {
10
+ expect(config.resolve('Toolbar').component).toEqual(
11
+ 'this is the Toolbar component',
12
+ );
13
+ });
14
+ it('resolve components with dots', () => {
15
+ expect(config.resolve('Toolbar.Types').component).toEqual(
16
+ 'this is the Types component',
17
+ );
18
+ });
19
+ it('resolve unexistent component', () => {
20
+ expect(config.resolve('Toolbar.Doh').component).toEqual(undefined);
21
+ });
22
+ it('register component by name', () => {
23
+ config.register('Toolbar.Bar', { component: 'this is a Bar component' });
24
+ expect(config.resolve('Toolbar.Bar').component).toEqual(
25
+ 'this is a Bar component',
26
+ );
27
+ });
28
+ });