@eeacms/volto-eea-website-theme 2.2.2 → 2.4.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.
Files changed (31) hide show
  1. package/CHANGELOG.md +26 -22
  2. package/package.json +3 -1
  3. package/src/components/manage/Blocks/ContextNavigation/ContextNavigationEdit.jsx +45 -0
  4. package/src/components/manage/Blocks/ContextNavigation/ContextNavigationEdit.test.jsx +88 -0
  5. package/src/components/manage/Blocks/ContextNavigation/ContextNavigationView.jsx +14 -0
  6. package/src/components/manage/Blocks/ContextNavigation/ContextNavigationView.test.jsx +71 -0
  7. package/src/components/manage/Blocks/ContextNavigation/index.js +30 -0
  8. package/src/components/manage/Blocks/ContextNavigation/schema.js +88 -0
  9. package/src/components/manage/Blocks/ContextNavigation/variations/Accordion.jsx +179 -0
  10. package/src/components/manage/Blocks/ContextNavigation/variations/Default.jsx +9 -0
  11. package/src/components/manage/Blocks/ContextNavigation/variations/index.js +18 -0
  12. package/src/components/manage/Blocks/Title/Edit.jsx +7 -4
  13. package/src/components/manage/Blocks/Title/View.jsx +14 -24
  14. package/src/components/manage/Blocks/Title/index.js +52 -0
  15. package/src/components/manage/Blocks/Title/variations/Default.jsx +43 -0
  16. package/src/components/manage/Blocks/Title/variations/WebReport.jsx +69 -0
  17. package/src/components/manage/Blocks/Title/variations/WebReportPage.jsx +59 -0
  18. package/src/components/manage/Blocks/Title/variations/styles.less +28 -0
  19. package/src/components/theme/Banner/View.jsx +5 -1
  20. package/src/components/theme/SubsiteClass.jsx +3 -1
  21. package/src/components/theme/WebReport/WebReportSectionView.jsx +49 -0
  22. package/src/components/theme/Widgets/DateWidget.jsx +32 -0
  23. package/src/components/theme/Widgets/DateWidget.test.js +67 -0
  24. package/src/components/theme/Widgets/DatetimeWidget.jsx +45 -0
  25. package/src/components/theme/Widgets/DatetimeWidget.test.js +63 -0
  26. package/src/customizations/volto/components/theme/Breadcrumbs/Breadcrumbs.jsx +20 -2
  27. package/src/customizations/volto/components/theme/Header/Header.jsx +3 -3
  28. package/src/customizations/volto/components/theme/View/DefaultView.jsx +190 -0
  29. package/src/hocs/withDeviceSize.test.jsx +79 -0
  30. package/src/index.js +37 -14
  31. package/src/index.test.js +2 -0
@@ -0,0 +1,179 @@
1
+ import cx from 'classnames';
2
+ import PropTypes from 'prop-types';
3
+ import React from 'react';
4
+ import { defineMessages, useIntl } from 'react-intl';
5
+ import { withRouter } from 'react-router';
6
+ import { compose } from 'redux';
7
+ import { Accordion } from 'semantic-ui-react';
8
+
9
+ import Slugger from 'github-slugger';
10
+
11
+ import { Icon, UniversalLink } from '@plone/volto/components';
12
+ import { withContentNavigation } from '@plone/volto/components/theme/Navigation/withContentNavigation';
13
+ import withEEASideMenu from '@eeacms/volto-block-toc/hocs/withEEASideMenu';
14
+ import { flattenToAppURL } from '@plone/volto/helpers';
15
+
16
+ import downIcon from '@plone/volto/icons/down-key.svg';
17
+ import upIcon from '@plone/volto/icons/up-key.svg';
18
+
19
+ const messages = defineMessages({
20
+ navigation: {
21
+ id: 'Navigation',
22
+ defaultMessage: 'Navigation',
23
+ },
24
+ });
25
+
26
+ const AccordionNavigation = ({
27
+ navigation = {},
28
+ device,
29
+ isMenuOpenOnOutsideClick,
30
+ }) => {
31
+ const { items = [], title, has_custom_name } = navigation;
32
+ const intl = useIntl();
33
+ const navOpen = ['mobile', 'tablet'].includes(device) ? false : true;
34
+ const [isNavOpen, setIsNavOpen] = React.useState(navOpen);
35
+ const [activeItems, setActiveItems] = React.useState({});
36
+
37
+ const onClickSummary = React.useCallback((e) => {
38
+ e.preventDefault();
39
+ setIsNavOpen((prev) => !prev);
40
+ }, []);
41
+
42
+ React.useEffect(() => {
43
+ if (isMenuOpenOnOutsideClick === false) setIsNavOpen(false);
44
+ }, [isMenuOpenOnOutsideClick]);
45
+
46
+ const onKeyDownSummary = React.useCallback(
47
+ (e) => {
48
+ if (e.keyCode === 13 || e.keyCode === 32) {
49
+ e.preventDefault();
50
+ onClickSummary(e);
51
+ }
52
+ },
53
+ [onClickSummary],
54
+ );
55
+
56
+ const renderItems = ({ item, level = 0 }) => {
57
+ const {
58
+ title,
59
+ href,
60
+ is_current,
61
+ is_in_path,
62
+ items: childItems,
63
+ type,
64
+ } = item;
65
+ const hasChildItems = childItems && childItems.length > 0;
66
+ const normalizedTitle = Slugger.slug(title);
67
+
68
+ const checkIfActive = () => {
69
+ return activeItems[href] !== undefined ? activeItems[href] : is_in_path;
70
+ };
71
+
72
+ const isActive = checkIfActive();
73
+
74
+ const handleTitleClick = () => {
75
+ setActiveItems((prev) => ({ ...prev, [href]: !isActive }));
76
+ };
77
+
78
+ return (
79
+ <li
80
+ className={cx({
81
+ is_in_path,
82
+ 'accordion-list-title': !hasChildItems,
83
+ 'accordion-list-item': hasChildItems,
84
+ })}
85
+ key={href}
86
+ >
87
+ {hasChildItems ? (
88
+ <Accordion className="default">
89
+ <Accordion.Title
90
+ active={isActive}
91
+ as={'button'}
92
+ aria-expanded={isActive}
93
+ onClick={handleTitleClick}
94
+ aria-controls={`accordion-content-${normalizedTitle}`}
95
+ id={`accordion-title-${normalizedTitle}`}
96
+ >
97
+ <span className="title-text">{title}</span>
98
+ <Icon name={isActive ? upIcon : downIcon} size="32px" />
99
+ </Accordion.Title>
100
+ <Accordion.Content
101
+ active={isActive}
102
+ id={`accordion-content-${normalizedTitle}`}
103
+ aria-labelledby={`accordion-title-${normalizedTitle}`}
104
+ role="region"
105
+ >
106
+ <ul className="accordion-list">
107
+ {childItems.map((child) =>
108
+ renderItems({ item: child, level: level + 1 }),
109
+ )}
110
+ </ul>
111
+ </Accordion.Content>
112
+ </Accordion>
113
+ ) : (
114
+ <UniversalLink
115
+ href={flattenToAppURL(href)}
116
+ className={cx(`title-link contenttype-${type}`, {
117
+ current: is_current,
118
+ in_path: is_in_path,
119
+ })}
120
+ >
121
+ {title}
122
+ </UniversalLink>
123
+ )}
124
+ </li>
125
+ );
126
+ };
127
+
128
+ return items.length ? (
129
+ <>
130
+ <nav className="context-navigation">
131
+ <details open={isNavOpen}>
132
+ {/* eslint-disable-next-line */}
133
+ <summary
134
+ className="context-navigation-header accordion-header"
135
+ onClick={onClickSummary}
136
+ onKeyDown={onKeyDownSummary}
137
+ >
138
+ {has_custom_name ? title : intl.formatMessage(messages.navigation)}
139
+ <Icon name={isNavOpen ? upIcon : downIcon} size="40px" />
140
+ </summary>
141
+ <ul className="context-navigation-list accordion-list">
142
+ {items.map((item) => renderItems({ item }))}
143
+ </ul>
144
+ </details>
145
+ </nav>
146
+ </>
147
+ ) : null;
148
+ };
149
+
150
+ AccordionNavigation.propTypes = {
151
+ /**
152
+ * Navigation tree returned from @contextnavigation restapi endpoint
153
+ */
154
+ navigation: PropTypes.shape({
155
+ items: PropTypes.arrayOf(
156
+ PropTypes.shape({
157
+ title: PropTypes.string,
158
+ url: PropTypes.string,
159
+ href: PropTypes.string,
160
+ is_current: PropTypes.bool,
161
+ is_in_path: PropTypes.bool,
162
+ items: PropTypes.array,
163
+ type: PropTypes.string,
164
+ }),
165
+ ),
166
+ has_custom_name: PropTypes.bool,
167
+ title: PropTypes.string,
168
+ }),
169
+ };
170
+
171
+ export default compose(
172
+ withRouter,
173
+ withContentNavigation,
174
+ (WrappedComponent) => (props) =>
175
+ withEEASideMenu(WrappedComponent)({
176
+ ...props,
177
+ shouldRender: props.navigation?.items?.length > 0,
178
+ }),
179
+ )(AccordionNavigation);
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import ConnectedContextNavigation from '@plone/volto/components/theme/Navigation/ContextNavigation';
3
+
4
+ const Default = (props) => {
5
+ const { params } = props;
6
+ return <ConnectedContextNavigation params={params} />;
7
+ };
8
+
9
+ export default Default;
@@ -0,0 +1,18 @@
1
+ import Accordion from './Accordion';
2
+ import Default from './Default';
3
+
4
+ const contextBlockVariations = [
5
+ {
6
+ id: 'default',
7
+ title: 'Listing (default)',
8
+ view: Default,
9
+ isDefault: true,
10
+ },
11
+ {
12
+ id: 'accordion',
13
+ title: 'Accordion',
14
+ view: Accordion,
15
+ },
16
+ ];
17
+
18
+ export default contextBlockVariations;
@@ -11,8 +11,8 @@ import { ReactEditor, Editable, Slate, withReact } from 'slate-react';
11
11
  import config from '@plone/volto/registry';
12
12
  import { SidebarPortal } from '@plone/volto/components';
13
13
  import { BodyClass } from '@plone/volto/helpers';
14
- import InlineForm from '@plone/volto/components/manage/Form/InlineForm';
15
- import BannerView from '@eeacms/volto-eea-website-theme/components/theme/Banner/View';
14
+ import View from '@eeacms/volto-eea-website-theme/components/manage/Blocks/Title/View';
15
+ import BlockDataForm from '@plone/volto/components/manage/Form/BlockDataForm';
16
16
  import schema from './schema';
17
17
 
18
18
  const messages = defineMessages({
@@ -165,8 +165,9 @@ export const TitleBlockEdit = (props) => {
165
165
  return (
166
166
  <React.Fragment>
167
167
  <BodyClass className="with-title-block" />
168
- <BannerView
168
+ <View
169
169
  {...props}
170
+ isEditMode={true}
170
171
  banner={{
171
172
  title: {
172
173
  view: (
@@ -185,7 +186,7 @@ export const TitleBlockEdit = (props) => {
185
186
  fluid
186
187
  />
187
188
  <SidebarPortal selected={props.selected}>
188
- <InlineForm
189
+ <BlockDataForm
189
190
  schema={schema}
190
191
  title={schema.title}
191
192
  onChangeField={(id, value) => {
@@ -194,7 +195,9 @@ export const TitleBlockEdit = (props) => {
194
195
  [id]: value,
195
196
  });
196
197
  }}
198
+ onChangeBlock={props.onChangeBlock}
197
199
  formData={props.data}
200
+ block={props.block}
198
201
  />
199
202
  </SidebarPortal>
200
203
  </React.Fragment>
@@ -4,36 +4,26 @@
4
4
  */
5
5
 
6
6
  import React from 'react';
7
- import { Portal } from 'react-portal';
8
- import PropTypes from 'prop-types';
9
- import { BodyClass } from '@plone/volto/helpers';
10
-
11
- import BannerView from '@eeacms/volto-eea-website-theme/components/theme/Banner/View';
12
7
 
13
- function IsomorphicPortal({ children }) {
14
- const [isClient, setIsClient] = React.useState();
15
- React.useEffect(() => setIsClient(true), []);
16
-
17
- return isClient ? (
18
- <Portal node={document.getElementById('page-header')}>{children}</Portal>
19
- ) : (
20
- children
21
- );
22
- }
8
+ import PropTypes from 'prop-types';
9
+ import { withBlockExtensions, BodyClass } from '@plone/volto/helpers';
10
+ import DefaultTemplate from './variations/Default';
23
11
 
24
12
  /**
25
13
  * View title block class.
26
14
  * @class View
27
15
  * @extends Component
28
16
  */
29
- const View = (props) => (
30
- <React.Fragment>
31
- <BodyClass className="with-title-block" />
32
- <IsomorphicPortal>
33
- <BannerView {...props} />
34
- </IsomorphicPortal>
35
- </React.Fragment>
36
- );
17
+ const View = (props = {}) => {
18
+ const { variation } = props;
19
+ const Renderer = variation?.view ?? DefaultTemplate;
20
+ return (
21
+ <>
22
+ <BodyClass className="with-title-block" />
23
+ <Renderer {...props} />
24
+ </>
25
+ );
26
+ };
37
27
 
38
28
  /**
39
29
  * Property types.
@@ -44,4 +34,4 @@ View.propTypes = {
44
34
  properties: PropTypes.objectOf(PropTypes.any).isRequired,
45
35
  };
46
36
 
47
- export default View;
37
+ export default withBlockExtensions(View);
@@ -1,11 +1,63 @@
1
1
  import Edit from './Edit';
2
2
  import View from './View';
3
+ import DefaultTemplate from './variations/Default';
4
+ import WebReport from './variations/WebReport';
5
+ import WebReportPage from './variations/WebReportPage';
6
+ import './variations/styles.less';
3
7
 
4
8
  const applyConfig = (config) => {
5
9
  config.blocks.blocksConfig.title = {
6
10
  ...config.blocks.blocksConfig.title,
7
11
  edit: Edit,
8
12
  view: View,
13
+ variations: [
14
+ {
15
+ id: 'default',
16
+ title: 'Default',
17
+ view: DefaultTemplate,
18
+ isDefault: true,
19
+ },
20
+ {
21
+ id: 'web_report',
22
+ title: 'Web Report',
23
+ view: WebReport,
24
+ schemaEnhancer: ({ schema }) => {
25
+ const fields = schema.fieldsets[0].fields;
26
+ schema.fieldsets[0].fields = [
27
+ ...fields,
28
+ 'content_type',
29
+ 'hero_header',
30
+ ];
31
+
32
+ schema.properties.content_type = {
33
+ title: 'Content type name',
34
+ description:
35
+ 'Add a custom content-type name, leave empty for default',
36
+ };
37
+ schema.properties.hero_header = {
38
+ title: 'Hero header size',
39
+ type: 'boolean',
40
+ };
41
+ return schema;
42
+ },
43
+ },
44
+ {
45
+ id: 'web_report_page',
46
+ title: 'Web Report Page',
47
+ view: WebReportPage,
48
+ schemaEnhancer: ({ schema }) => {
49
+ const fields = schema.fieldsets[0].fields;
50
+ schema.fieldsets[0].fields = [...fields, 'content_type'];
51
+
52
+ schema.properties.content_type = {
53
+ title: 'Content type name',
54
+ description:
55
+ 'Add a custom content-type name, leave empty for default',
56
+ };
57
+ return schema;
58
+ },
59
+ },
60
+ ],
9
61
  copyrightPrefix: 'Image',
10
62
  sidebarTab: 1,
11
63
  };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * View title block.
3
+ * @module components/manage/Blocks/Title/View
4
+ */
5
+
6
+ import React from 'react';
7
+ import { Portal } from 'react-portal';
8
+ import PropTypes from 'prop-types';
9
+
10
+ import BannerView from '@eeacms/volto-eea-website-theme/components/theme/Banner/View';
11
+
12
+ function IsomorphicPortal({ children }) {
13
+ const [isClient, setIsClient] = React.useState();
14
+ React.useEffect(() => setIsClient(true), []);
15
+
16
+ return isClient ? (
17
+ <Portal node={document.getElementById('page-header')}>{children}</Portal>
18
+ ) : (
19
+ children
20
+ );
21
+ }
22
+
23
+ const DefaultTemplate = (props) =>
24
+ props.isEditMode ? (
25
+ <BannerView {...props} />
26
+ ) : (
27
+ <React.Fragment>
28
+ <IsomorphicPortal>
29
+ <BannerView {...props} />
30
+ </IsomorphicPortal>
31
+ </React.Fragment>
32
+ );
33
+
34
+ /**
35
+ * Property types.
36
+ * @property {Object} propTypes Property types.
37
+ * @static
38
+ */
39
+ DefaultTemplate.propTypes = {
40
+ properties: PropTypes.objectOf(PropTypes.any).isRequired,
41
+ };
42
+
43
+ export default DefaultTemplate;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Web Report title block variation.
3
+ * @module components/manage/Blocks/Title/variations/WebReport
4
+ */
5
+
6
+ import React from 'react';
7
+ import { Portal } from 'react-portal';
8
+ import PropTypes from 'prop-types';
9
+
10
+ import { MaybeWrap } from '@plone/volto/components';
11
+ import BannerView from '@eeacms/volto-eea-website-theme/components/theme/Banner/View';
12
+ import Banner from '@eeacms/volto-eea-design-system/ui/Banner/Banner';
13
+ import clsx from 'clsx';
14
+
15
+ import { BodyClass } from '@plone/volto/helpers';
16
+
17
+ function IsomorphicPortal({ children }) {
18
+ const [isClient, setIsClient] = React.useState();
19
+ React.useEffect(() => setIsClient(true), []);
20
+
21
+ return isClient ? (
22
+ <Portal node={document.querySelector('.eea.header')}>{children}</Portal>
23
+ ) : (
24
+ children
25
+ );
26
+ }
27
+
28
+ const WebReport = (props) => {
29
+ return (
30
+ <MaybeWrap condition={!props.isEditMode} as={IsomorphicPortal}>
31
+ <BodyClass
32
+ className={clsx(
33
+ 'homepage-inverse homepage-header light-header',
34
+ props.data.hero_header ? 'hero-header' : '',
35
+ )}
36
+ />
37
+ <BannerView
38
+ {...props}
39
+ data={{
40
+ ...props.data,
41
+ hideContentType: true,
42
+ aboveTitle: (
43
+ <div className="content-type">
44
+ {props.data.content_type || props.properties.type_title}
45
+ </div>
46
+ ),
47
+ belowTitle: (
48
+ <>
49
+ <Banner.Subtitle>
50
+ <span className="subtitle-light">{props.data.subtitle}</span>
51
+ </Banner.Subtitle>
52
+ </>
53
+ ),
54
+ }}
55
+ />
56
+ </MaybeWrap>
57
+ );
58
+ };
59
+
60
+ /**
61
+ * Property types.
62
+ * @property {Object} propTypes Property types.
63
+ * @static
64
+ */
65
+ WebReport.propTypes = {
66
+ properties: PropTypes.objectOf(PropTypes.any).isRequired,
67
+ };
68
+
69
+ export default WebReport;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Web Report Page title block variation.
3
+ * @module components/manage/Title/variations/WebReport
4
+ */
5
+
6
+ import React from 'react';
7
+ import { Portal } from 'react-portal';
8
+ import PropTypes from 'prop-types';
9
+
10
+ import { MaybeWrap } from '@plone/volto/components';
11
+ import BannerView from '@eeacms/volto-eea-website-theme/components/theme/Banner/View';
12
+ import clsx from 'clsx';
13
+
14
+ import { BodyClass } from '@plone/volto/helpers';
15
+
16
+ function IsomorphicPortal({ children }) {
17
+ const [isClient, setIsClient] = React.useState();
18
+ React.useEffect(() => setIsClient(true), []);
19
+
20
+ return isClient ? (
21
+ <Portal node={document.querySelector('.eea.header')}>{children}</Portal>
22
+ ) : (
23
+ children
24
+ );
25
+ }
26
+
27
+ const WebReportPage = (props) => {
28
+ return (
29
+ <MaybeWrap condition={!props.isEditMode} as={IsomorphicPortal}>
30
+ <BodyClass className={clsx('homepage-inverse light-header')} />
31
+ <BannerView
32
+ {...props}
33
+ data={{
34
+ ...props.data,
35
+ hideContentType: true,
36
+ aboveTitle: (
37
+ <>
38
+ <div className="content-type">
39
+ {props.data.content_type || props.properties.type_title}
40
+ </div>
41
+ <div className="subtitle">{props.data.subtitle}</div>
42
+ </>
43
+ ),
44
+ }}
45
+ />
46
+ </MaybeWrap>
47
+ );
48
+ };
49
+
50
+ /**
51
+ * Property types.
52
+ * @property {Object} propTypes Property types.
53
+ * @static
54
+ */
55
+ WebReportPage.propTypes = {
56
+ properties: PropTypes.objectOf(PropTypes.any).isRequired,
57
+ };
58
+
59
+ export default WebReportPage;
@@ -0,0 +1,28 @@
1
+ .view-viewview.light-header .main.bar {
2
+ position: relative;
3
+ z-index: 1;
4
+ width: 100%;
5
+ margin-bottom: -160px;
6
+ }
7
+ //Gradient styles for web report
8
+ .light-header .gradient {
9
+ background: linear-gradient(
10
+ 0deg,
11
+ #ffffff,
12
+ rgba(255, 255, 255, 0.9) 30%,
13
+ rgba(46, 82, 114, 0.7) 70%,
14
+ rgba(14, 21, 26, 0.8) 100%
15
+ ) !important;
16
+ }
17
+
18
+ .ui.block.title .eea.banner .content {
19
+ padding-right: 1rem;
20
+ padding-left: 1rem;
21
+ }
22
+
23
+ .share-popup {
24
+ .actions {
25
+ display: flex;
26
+ flex-flow: row;
27
+ }
28
+ }
@@ -291,8 +291,12 @@ const View = (props) => {
291
291
  </>
292
292
  }
293
293
  >
294
- {subtitle && <Banner.Subtitle>{subtitle}</Banner.Subtitle>}
294
+ {!props.data.aboveTitle && subtitle && (
295
+ <Banner.Subtitle>{subtitle}</Banner.Subtitle>
296
+ )}
297
+ {props.data.aboveTitle}
295
298
  <Title config={banner.title} properties={metadata} />
299
+ {props.data.belowTitle}
296
300
  <Banner.Metadata>
297
301
  <Banner.MetadataField
298
302
  type="type"
@@ -14,8 +14,10 @@ const SubsiteClass = () => {
14
14
 
15
15
  return (
16
16
  <BodyClass
17
- className={cx('subsite', `subsite-${subsite.subsite_css_class?.token}`, {
17
+ className={cx('subsite', {
18
18
  'subsite-root': isSubsiteRoot(location.pathname, subsite),
19
+ [`subsite-${subsite.subsite_css_class?.token}`]:
20
+ subsite.subsite_css_class?.token,
19
21
  })}
20
22
  />
21
23
  );
@@ -0,0 +1,49 @@
1
+ import React, { useEffect } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useHistory } from 'react-router-dom';
4
+ import { isInternalURL, flattenToAppURL } from '@plone/volto/helpers';
5
+ import { DefaultView } from '@plone/volto/components/';
6
+ import { Redirect } from 'react-router-dom';
7
+
8
+ const WebReportSectionView = (props) => {
9
+ const { content, token } = props;
10
+ const history = useHistory();
11
+ const redirectUrl = React.useMemo(() => {
12
+ if (content) {
13
+ const items = content.items;
14
+ const firstItem = items?.[0];
15
+ return firstItem?.['@id'];
16
+ }
17
+ }, [content]);
18
+
19
+ useEffect(() => {
20
+ if (!token) {
21
+ if (isInternalURL(redirectUrl)) {
22
+ history.replace(flattenToAppURL(redirectUrl));
23
+ } else if (!__SERVER__ && redirectUrl) {
24
+ window.location.href = flattenToAppURL(redirectUrl);
25
+ }
26
+ }
27
+ }, [history, content, redirectUrl, token]);
28
+
29
+ if (__SERVER__ && redirectUrl && !token) {
30
+ return <Redirect to={redirectUrl} />;
31
+ }
32
+ return <DefaultView {...props} />;
33
+ };
34
+
35
+ WebReportSectionView.propTypes = {
36
+ content: PropTypes.shape({
37
+ items: PropTypes.arrayOf(
38
+ PropTypes.shape({
39
+ '@id': PropTypes.string,
40
+ }),
41
+ ),
42
+ }),
43
+ };
44
+
45
+ WebReportSectionView.defaultProps = {
46
+ content: null,
47
+ };
48
+
49
+ export default WebReportSectionView;
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import { useSelector } from 'react-redux';
4
+ import { toBackendLang } from '@plone/volto/helpers';
5
+ import { formatDate } from '@plone/volto/helpers/Utils/Date';
6
+ import config from '@plone/volto/registry';
7
+
8
+ export const DateWidget = ({ value, children, className }) => {
9
+ const lang = useSelector((state) => state.intl.locale);
10
+ const backendLang = toBackendLang(lang);
11
+ const locale =
12
+ backendLang === 'en' ? config.settings.dateLocale : backendLang;
13
+ const formatOptions = {
14
+ date: value,
15
+ format: {
16
+ year: 'numeric',
17
+ month: 'short',
18
+ day: '2-digit',
19
+ },
20
+ locale,
21
+ };
22
+
23
+ return value ? (
24
+ <span className={cx(className, 'date', 'widget')}>
25
+ {children
26
+ ? children(formatDate(formatOptions))
27
+ : formatDate(formatOptions)}
28
+ </span>
29
+ ) : (
30
+ ''
31
+ );
32
+ };