@kitconcept/volto-light-theme 8.0.0-alpha.3 → 8.0.0-alpha.30

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 (200) hide show
  1. package/.changelog.draft +6 -9
  2. package/CHANGELOG.md +310 -0
  3. package/locales/af/LC_MESSAGES/volto.po +645 -0
  4. package/locales/ar/LC_MESSAGES/volto.po +645 -0
  5. package/locales/bg/LC_MESSAGES/volto.po +645 -0
  6. package/locales/bn/LC_MESSAGES/volto.po +645 -0
  7. package/locales/ca/LC_MESSAGES/volto.po +645 -0
  8. package/locales/cs/LC_MESSAGES/volto.po +645 -0
  9. package/locales/cy/LC_MESSAGES/volto.po +645 -0
  10. package/locales/da/LC_MESSAGES/volto.po +645 -0
  11. package/locales/de/LC_MESSAGES/volto.po +83 -167
  12. package/locales/el/LC_MESSAGES/volto.po +645 -0
  13. package/locales/en/LC_MESSAGES/volto.po +30 -115
  14. package/locales/en_AU/LC_MESSAGES/volto.po +645 -0
  15. package/locales/en_GB/LC_MESSAGES/volto.po +645 -0
  16. package/locales/eo/LC_MESSAGES/volto.po +645 -0
  17. package/locales/es/LC_MESSAGES/volto.po +75 -160
  18. package/locales/et/LC_MESSAGES/volto.po +645 -0
  19. package/locales/eu/LC_MESSAGES/volto.po +59 -125
  20. package/locales/fa/LC_MESSAGES/volto.po +645 -0
  21. package/locales/fi/LC_MESSAGES/volto.po +645 -0
  22. package/locales/fr/LC_MESSAGES/volto.po +645 -0
  23. package/locales/fu/LC_MESSAGES/volto.po +645 -0
  24. package/locales/ga/LC_MESSAGES/volto.po +645 -0
  25. package/locales/gl/LC_MESSAGES/volto.po +645 -0
  26. package/locales/he/LC_MESSAGES/volto.po +645 -0
  27. package/locales/hi/LC_MESSAGES/volto.po +645 -0
  28. package/locales/hr/LC_MESSAGES/volto.po +645 -0
  29. package/locales/hu/LC_MESSAGES/volto.po +645 -0
  30. package/locales/hy/LC_MESSAGES/volto.po +645 -0
  31. package/locales/id/LC_MESSAGES/volto.po +645 -0
  32. package/locales/it/LC_MESSAGES/volto.po +645 -0
  33. package/locales/ja/LC_MESSAGES/volto.po +645 -0
  34. package/locales/ka/LC_MESSAGES/volto.po +645 -0
  35. package/locales/kn/LC_MESSAGES/volto.po +645 -0
  36. package/locales/ko/LC_MESSAGES/volto.po +645 -0
  37. package/locales/lt/LC_MESSAGES/volto.po +645 -0
  38. package/locales/lv/LC_MESSAGES/volto.po +645 -0
  39. package/locales/mi/LC_MESSAGES/volto.po +645 -0
  40. package/locales/mk_MK/LC_MESSAGES/volto.po +645 -0
  41. package/locales/ms/LC_MESSAGES/volto.po +645 -0
  42. package/locales/mt/LC_MESSAGES/volto.po +645 -0
  43. package/locales/my/LC_MESSAGES/volto.po +645 -0
  44. package/locales/nl/LC_MESSAGES/volto.po +645 -0
  45. package/locales/nl_BE/LC_MESSAGES/volto.po +645 -0
  46. package/locales/nn/LC_MESSAGES/volto.po +645 -0
  47. package/locales/no/LC_MESSAGES/volto.po +645 -0
  48. package/locales/pl/LC_MESSAGES/volto.po +645 -0
  49. package/locales/pt/LC_MESSAGES/volto.po +645 -0
  50. package/locales/pt_BR/LC_MESSAGES/volto.po +38 -123
  51. package/locales/rm/LC_MESSAGES/volto.po +645 -0
  52. package/locales/ro/LC_MESSAGES/volto.po +645 -0
  53. package/locales/ru/LC_MESSAGES/volto.po +645 -0
  54. package/locales/sk/LC_MESSAGES/volto.po +645 -0
  55. package/locales/sl/LC_MESSAGES/volto.po +645 -0
  56. package/locales/sm/LC_MESSAGES/volto.po +645 -0
  57. package/locales/sq/LC_MESSAGES/volto.po +645 -0
  58. package/locales/sr/LC_MESSAGES/volto.po +645 -0
  59. package/locales/sr_Cyrl/LC_MESSAGES/volto.po +645 -0
  60. package/locales/sr_Latn/LC_MESSAGES/volto.po +645 -0
  61. package/locales/sv/LC_MESSAGES/volto.po +645 -0
  62. package/locales/sw/LC_MESSAGES/volto.po +645 -0
  63. package/locales/ta/LC_MESSAGES/volto.po +645 -0
  64. package/locales/te/LC_MESSAGES/volto.po +645 -0
  65. package/locales/th/LC_MESSAGES/volto.po +645 -0
  66. package/locales/tl/LC_MESSAGES/volto.po +645 -0
  67. package/locales/to/LC_MESSAGES/volto.po +645 -0
  68. package/locales/tr/LC_MESSAGES/volto.po +645 -0
  69. package/locales/uk/LC_MESSAGES/volto.po +645 -0
  70. package/locales/vi/LC_MESSAGES/volto.po +645 -0
  71. package/locales/volto.pot +31 -116
  72. package/locales/zh_CN/LC_MESSAGES/volto.po +645 -0
  73. package/locales/zh_HK/LC_MESSAGES/volto.po +645 -0
  74. package/locales/zh_TW/LC_MESSAGES/volto.po +645 -0
  75. package/package.json +7 -4
  76. package/src/__mocks__/semantic-ui-react.ts +31 -0
  77. package/src/components/Blocks/Block/EditBlockWrapper.jsx +9 -3
  78. package/src/components/Blocks/Button/schema.js +12 -0
  79. package/src/components/Blocks/EventCalendar/Search/components/EventTemplate.tsx +1 -1
  80. package/src/components/Blocks/Image/Edit.jsx +9 -32
  81. package/src/components/Blocks/Image/View.jsx +9 -26
  82. package/src/components/Blocks/Image/adapter.js +28 -14
  83. package/src/components/Blocks/Image/adapter.test.js +156 -0
  84. package/src/components/Blocks/Image/schema.js +21 -7
  85. package/src/components/Blocks/Listing/DefaultTemplate.jsx +12 -6
  86. package/src/components/Blocks/Listing/GridTemplate.jsx +17 -7
  87. package/src/components/Blocks/Listing/ListingBody.jsx +4 -1
  88. package/src/components/Blocks/Listing/SummaryTemplate.jsx +17 -7
  89. package/src/components/Blocks/Maps/MapsSidebar.jsx +68 -0
  90. package/src/components/Blocks/Maps/View.jsx +37 -0
  91. package/src/components/Blocks/Maps/adapter.js +27 -0
  92. package/src/components/Blocks/Maps/adapter.test.js +63 -0
  93. package/src/components/Blocks/Maps/schema.js +42 -2
  94. package/src/components/Blocks/Separator/schema.js +12 -0
  95. package/src/components/Blocks/Teaser/DefaultBody.tsx +35 -6
  96. package/src/components/Blocks/Video/VideoSidebar.jsx +68 -0
  97. package/src/components/Blocks/Video/View.jsx +38 -0
  98. package/src/components/Blocks/Video/adapter.js +28 -0
  99. package/src/components/Blocks/Video/adapter.test.js +63 -0
  100. package/src/components/Blocks/Video/schema.js +42 -2
  101. package/src/components/Blocks/schema.ts +69 -0
  102. package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +128 -0
  103. package/src/components/Breadcrumbs/Breadcrumbs.tsx +117 -0
  104. package/src/components/Caption/Caption.test.tsx +31 -0
  105. package/src/components/Caption/{Caption.jsx → Caption.tsx} +14 -21
  106. package/src/components/Footer/ColumnLinks.tsx +2 -2
  107. package/src/components/Footer/Footer.tsx +2 -2
  108. package/src/components/Footer/slots/Colophon.tsx +13 -1
  109. package/src/components/Footer/slots/CoreFooter.tsx +4 -2
  110. package/src/components/Footer/slots/FollowUsLogoAndLinks.tsx +12 -23
  111. package/src/components/Header/Header.tsx +3 -3
  112. package/src/components/LanguageSelector/LanguageSelector.tsx +91 -0
  113. package/src/components/MobileNavigation/MobileNavigation.jsx +11 -9
  114. package/src/components/Navigation/Navigation.test.tsx +176 -0
  115. package/src/components/Navigation/{Navigation.jsx → Navigation.tsx} +89 -42
  116. package/src/components/StickyMenu/MobileCarouselArrowButton.tsx +81 -0
  117. package/src/components/StickyMenu/MobileStickyMenu.tsx +76 -0
  118. package/src/components/Summary/DefaultSummary.tsx +10 -3
  119. package/src/components/Summary/EventSummary.tsx +10 -3
  120. package/src/components/Summary/FileSummary.tsx +10 -3
  121. package/src/components/Summary/NewsItemSummary.tsx +10 -3
  122. package/src/components/Summary/PersonSummary.tsx +10 -3
  123. package/src/components/Summary/Summary.stories.tsx +46 -30
  124. package/src/components/Tags/Tags.test.tsx +71 -0
  125. package/src/components/Tags/{Tags.jsx → Tags.tsx} +9 -25
  126. package/src/components/Theme/EventView.jsx +4 -4
  127. package/src/components/Theme/ImageView.jsx +8 -1
  128. package/src/components/Theme/NewsItemView.jsx +4 -4
  129. package/src/components/Theme/RenderBlocksV2.jsx +38 -15
  130. package/src/components/Widgets/ColorSwatch.stories.tsx +197 -0
  131. package/src/components/Widgets/ColorSwatch.test.tsx +188 -0
  132. package/src/components/Widgets/ColorSwatch.tsx +77 -39
  133. package/src/components/Widgets/ObjectList.tsx +37 -27
  134. package/src/components/Widgets/SoftTextWidget.tsx +129 -0
  135. package/src/components/Widgets/SoftTextareaWidget.tsx +118 -0
  136. package/src/components/Widgets/ThemeColorSwatch.tsx +5 -9
  137. package/src/config/blocks.tsx +83 -28
  138. package/src/config/classExtenders.ts +11 -10
  139. package/src/config/settings.ts +6 -0
  140. package/src/config/slots.ts +7 -0
  141. package/src/config/widgets.ts +5 -9
  142. package/src/customizations/volto/components/manage/Blocks/Maps/MapsSidebar.jsx +10 -0
  143. package/src/customizations/volto/components/manage/Blocks/Maps/View.jsx +10 -0
  144. package/src/customizations/volto/components/manage/Blocks/Video/VideoSidebar.jsx +10 -0
  145. package/src/customizations/volto/components/manage/Blocks/Video/View.jsx +10 -0
  146. package/src/customizations/volto/components/manage/DragDropList/DragDropList.jsx +263 -0
  147. package/src/customizations/volto/components/theme/LanguageSelector/LanguageSelector.tsx +10 -0
  148. package/src/helpers/styleDefinitions.test.tsx +30 -0
  149. package/src/helpers/styleDefinitions.ts +49 -0
  150. package/src/helpers/useLiveData.ts +7 -2
  151. package/src/index.ts +15 -0
  152. package/src/internalChecks.test.ts +94 -0
  153. package/src/primitives/Card/Card.stories.tsx +4 -1
  154. package/src/primitives/Card/Card.test.tsx +11 -33
  155. package/src/primitives/Card/Card.tsx +37 -44
  156. package/src/primitives/IconLinkList.tsx +53 -52
  157. package/src/primitives/LinkIconButton.tsx +52 -0
  158. package/src/reducers/errorContext.ts +14 -0
  159. package/src/reducers/index.ts +7 -0
  160. package/src/theme/_bgcolor-blocks-layout.scss +48 -46
  161. package/src/theme/_content.scss +12 -13
  162. package/src/theme/_export_import.scss +94 -0
  163. package/src/theme/_footer.scss +131 -64
  164. package/src/theme/_header.scss +25 -5
  165. package/src/theme/_insets.scss +1 -1
  166. package/src/theme/_layout.scss +41 -77
  167. package/src/theme/_mobile-sticky-menu.scss +92 -0
  168. package/src/theme/_search-page.scss +250 -0
  169. package/src/theme/_typo-custom.scss +24 -8
  170. package/src/theme/_variables.scss +40 -4
  171. package/src/theme/_widgets.scss +6 -17
  172. package/src/theme/blocks/_accordion.scss +11 -4
  173. package/src/theme/blocks/_form.scss +350 -0
  174. package/src/theme/blocks/_grid.scss +10 -77
  175. package/src/theme/blocks/_highlight.scss +10 -7
  176. package/src/theme/blocks/_image.scss +99 -184
  177. package/src/theme/blocks/_listing.scss +61 -128
  178. package/src/theme/blocks/_maps.scss +60 -34
  179. package/src/theme/blocks/_search.scss +3 -4
  180. package/src/theme/blocks/_table.scss +1 -0
  181. package/src/theme/blocks/_teaser.scss +7 -117
  182. package/src/theme/blocks/_toc.scss +2 -1
  183. package/src/theme/card.scss +136 -69
  184. package/src/theme/main.scss +4 -0
  185. package/src/theme/notfound.scss +2 -0
  186. package/src/theme/person.scss +7 -1
  187. package/src/theme/sticky-menu.scss +7 -5
  188. package/src/transforms/to6.ts +5 -49
  189. package/src/transforms/to8.test.js +201 -0
  190. package/src/transforms/to8.ts +109 -0
  191. package/src/types.d.ts +1 -0
  192. package/vitest.config.mjs +28 -3
  193. package/razzle.extend.js +0 -38
  194. package/src/components/Blocks/schema.js +0 -44
  195. package/src/components/Breadcrumbs/Breadcrumbs.jsx +0 -118
  196. package/src/components/Widgets/AlignWidget.tsx +0 -84
  197. package/src/components/Widgets/BlockAlignment.tsx +0 -88
  198. package/src/components/Widgets/BlockWidth.tsx +0 -101
  199. package/src/components/Widgets/Buttons.tsx +0 -167
  200. package/src/components/Widgets/Size.tsx +0 -78
@@ -1,10 +1,17 @@
1
+ import * as React from 'react';
1
2
  import { parseDateFromCatalog } from '@kitconcept/volto-light-theme/helpers/dates';
2
3
  import FormattedDate from '@plone/volto/components/theme/FormattedDate/FormattedDate';
3
4
  import type { DefaultSummaryProps } from './DefaultSummary';
4
5
  import { smartTextRenderer } from '../../helpers/smartText';
5
6
 
6
7
  const NewsItemSummary = (props: DefaultSummaryProps) => {
7
- const { item, HeadingTag = 'h3', a11yLabelId, hide_description } = props;
8
+ const {
9
+ item,
10
+ LinkToItem = React.Fragment,
11
+ HeadingTag = 'div',
12
+ a11yLabelId,
13
+ hide_description,
14
+ } = props;
8
15
 
9
16
  const effective = parseDateFromCatalog(item.effective);
10
17
  const headline = [
@@ -31,9 +38,9 @@ const NewsItemSummary = (props: DefaultSummaryProps) => {
31
38
  <>
32
39
  {headline.length ? <div className="headline">{headline}</div> : null}
33
40
  <HeadingTag className="title" id={a11yLabelId}>
34
- {item.title ? item.title : item.id}
41
+ <LinkToItem>{item.title ? item.title : item.id}</LinkToItem>
35
42
  </HeadingTag>
36
- {!hide_description && (
43
+ {!hide_description && item?.description !== '' && (
37
44
  <p className="description">{smartTextRenderer(item.description)}</p>
38
45
  )}
39
46
  </>
@@ -1,3 +1,4 @@
1
+ import * as React from 'react';
1
2
  import Icon from '@plone/volto/components/theme/Icon/Icon';
2
3
  import mailSVG from '@plone/volto/icons/email.svg';
3
4
  import locationSVG from '@plone/volto/icons/map.svg';
@@ -22,16 +23,22 @@ const messages = defineMessages({
22
23
  });
23
24
 
24
25
  const PersonSummary = (props: DefaultSummaryProps) => {
25
- const { item, HeadingTag = 'h3', a11yLabelId, hide_description } = props;
26
+ const {
27
+ item,
28
+ LinkToItem = React.Fragment,
29
+ HeadingTag = 'div',
30
+ a11yLabelId,
31
+ hide_description,
32
+ } = props;
26
33
  const intl = useIntl();
27
34
 
28
35
  return (
29
36
  <>
30
37
  {item?.head_title && <div className="headline">{item.head_title}</div>}
31
38
  <HeadingTag className="title" id={a11yLabelId}>
32
- {item.title ? item.title : item.id}
39
+ <LinkToItem>{item.title ? item.title : item.id}</LinkToItem>
33
40
  </HeadingTag>
34
- {!hide_description && (
41
+ {!hide_description && item?.description !== '' && (
35
42
  <p className="description">{smartTextRenderer(item.description)}</p>
36
43
  )}
37
44
 
@@ -22,15 +22,17 @@ type Story = StoryObj<typeof meta>;
22
22
 
23
23
  export const Summary: Story = {
24
24
  render: (args) => (
25
- <div style={{ width: '300px' }}>
26
- <Wrapper>
27
- <DefaultSummary {...args} />
28
- </Wrapper>
25
+ <div className="card" style={{ width: '300px' }}>
26
+ <div className="card-summary">
27
+ <Wrapper>
28
+ <DefaultSummary {...args} />
29
+ </Wrapper>
30
+ </div>
29
31
  </div>
30
32
  ),
31
33
  args: {
32
34
  item: {
33
- title: 'Simple Card with strings',
35
+ title: 'Summary',
34
36
  description:
35
37
  'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea.',
36
38
  head_title: 'Simple Card',
@@ -40,15 +42,17 @@ export const Summary: Story = {
40
42
 
41
43
  export const SummaryWithLink: Story = {
42
44
  render: (args) => (
43
- <div style={{ width: '300px' }}>
44
- <Wrapper>
45
- <DefaultSummary {...args} />
46
- </Wrapper>
45
+ <div className="card" style={{ width: '300px' }}>
46
+ <div className="card-summary">
47
+ <Wrapper>
48
+ <DefaultSummary {...args} />
49
+ </Wrapper>
50
+ </div>
47
51
  </div>
48
52
  ),
49
53
  args: {
50
54
  item: {
51
- title: 'Simple Card with strings',
55
+ title: 'Summary',
52
56
  description:
53
57
  '[Lorem ipsum](https://example.com) dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea.',
54
58
  head_title: 'Simple Card',
@@ -58,15 +62,17 @@ export const SummaryWithLink: Story = {
58
62
 
59
63
  export const SummaryHideDescription: Story = {
60
64
  render: (args) => (
61
- <div style={{ width: '300px' }}>
62
- <Wrapper>
63
- <DefaultSummary {...args} />
64
- </Wrapper>
65
+ <div className="card" style={{ width: '300px' }}>
66
+ <div className="card-summary">
67
+ <Wrapper>
68
+ <DefaultSummary {...args} />
69
+ </Wrapper>
70
+ </div>
65
71
  </div>
66
72
  ),
67
73
  args: {
68
74
  item: {
69
- title: 'Simple Card with strings',
75
+ title: 'Summary',
70
76
  description:
71
77
  'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea.',
72
78
  head_title: 'Simple Card',
@@ -77,15 +83,17 @@ export const SummaryHideDescription: Story = {
77
83
 
78
84
  export const NewsItemSummary: Story = {
79
85
  render: (args) => (
80
- <div style={{ width: '300px' }}>
81
- <Wrapper>
82
- <NewsItemSummaryComponent {...args} />
83
- </Wrapper>
86
+ <div className="card" style={{ width: '300px' }}>
87
+ <div className="card-summary">
88
+ <Wrapper>
89
+ <NewsItemSummaryComponent {...args} />
90
+ </Wrapper>
91
+ </div>
84
92
  </div>
85
93
  ),
86
94
  args: {
87
95
  item: {
88
- title: 'Simple Card with strings',
96
+ title: 'Summary',
89
97
  description:
90
98
  'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea.',
91
99
  head_title: 'Simple Card',
@@ -98,15 +106,17 @@ export const NewsItemSummary: Story = {
98
106
 
99
107
  export const EventSummary: Story = {
100
108
  render: (args) => (
101
- <div style={{ width: '300px' }}>
102
- <Wrapper>
103
- <EventSummaryComponent {...args} />
104
- </Wrapper>
109
+ <div className="card" style={{ width: '300px' }}>
110
+ <div className="card-summary">
111
+ <Wrapper>
112
+ <EventSummaryComponent {...args} />
113
+ </Wrapper>
114
+ </div>
105
115
  </div>
106
116
  ),
107
117
  args: {
108
118
  item: {
109
- title: 'Simple Card with strings',
119
+ title: 'Summary',
110
120
  description:
111
121
  'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea.',
112
122
  head_title: 'Simple Card',
@@ -119,17 +129,23 @@ export const EventSummary: Story = {
119
129
 
120
130
  export const FileSummary: Story = {
121
131
  render: (args) => (
122
- <div style={{ width: '300px' }}>
123
- <Wrapper>
124
- <FileSummaryComponent {...args} />
125
- </Wrapper>
132
+ <div className="card" style={{ width: '300px' }}>
133
+ <div className="card-summary">
134
+ <Wrapper>
135
+ <div className="file-teaser">
136
+ <FileSummaryComponent {...args} />
137
+ </div>
138
+ </Wrapper>
139
+ </div>
126
140
  </div>
127
141
  ),
128
142
  args: {
129
143
  item: {
130
- title: 'Simple Card with strings',
144
+ title: 'Summary',
131
145
  description:
132
146
  'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea.',
147
+ getObjSize: '72.7 KB',
148
+ mime_type: 'application/pdf',
133
149
  head_title: 'Simple Card',
134
150
  start: '2023-01-01T11:00:00+00:00',
135
151
  end: '2023-12-31T12:00:00+00:00',
@@ -0,0 +1,71 @@
1
+ import React from 'react';
2
+ import { describe, it, beforeEach, expect, vi } from 'vitest';
3
+ import { render, screen } from '@testing-library/react';
4
+ import { MemoryRouter } from 'react-router-dom';
5
+
6
+ import Tags from './Tags';
7
+ import type { Content } from '@plone/types';
8
+
9
+ const mockConfig = vi.hoisted(() => ({
10
+ getComponent: vi.fn(() => ({
11
+ component: ({ children }: { children: React.ReactNode }) => (
12
+ <div data-testid="container">{children}</div>
13
+ ),
14
+ })),
15
+ settings: {
16
+ showTags: true,
17
+ },
18
+ }));
19
+
20
+ vi.mock('@plone/registry', () => ({
21
+ __esModule: true,
22
+ default: mockConfig,
23
+ }));
24
+
25
+ const mockUseLiveData = vi.fn();
26
+
27
+ vi.mock('@kitconcept/volto-light-theme/helpers/useLiveData', () => ({
28
+ useLiveData: (content: unknown, behavior: unknown, field: string) =>
29
+ mockUseLiveData(content, behavior, field),
30
+ }));
31
+
32
+ describe('Tags', () => {
33
+ beforeEach(() => {
34
+ vi.clearAllMocks();
35
+ mockConfig.settings.showTags = true;
36
+ });
37
+
38
+ it('renders tag links when enabled', () => {
39
+ mockUseLiveData.mockReturnValue(['React', 'Volto']);
40
+ render(
41
+ <MemoryRouter>
42
+ <Tags content={{ subjects: ['React', 'Volto'] } as Content} />
43
+ </MemoryRouter>,
44
+ );
45
+
46
+ const links = screen.getAllByRole('link');
47
+ expect(links).toHaveLength(2);
48
+ expect(links[0]).toHaveAttribute('href', '/search?Subject=React');
49
+ expect(links[1]).toHaveTextContent('Volto');
50
+ });
51
+
52
+ it('returns null when tags are hidden or empty', () => {
53
+ mockConfig.settings.showTags = false;
54
+ mockUseLiveData.mockReturnValue(['React']);
55
+ const { container: hidden } = render(
56
+ <MemoryRouter>
57
+ <Tags />
58
+ </MemoryRouter>,
59
+ );
60
+ expect(hidden.firstChild).toBeNull();
61
+
62
+ mockConfig.settings.showTags = true;
63
+ mockUseLiveData.mockReturnValue([]);
64
+ const { container: empty } = render(
65
+ <MemoryRouter>
66
+ <Tags />
67
+ </MemoryRouter>,
68
+ );
69
+ expect(empty.firstChild).toBeNull();
70
+ });
71
+ });
@@ -1,11 +1,17 @@
1
+ import React from 'react';
1
2
  import { Link } from 'react-router-dom';
2
- import PropTypes from 'prop-types';
3
3
  import { Container as SemanticContainer } from 'semantic-ui-react';
4
4
  import config from '@plone/registry';
5
5
  import { useLiveData } from '@kitconcept/volto-light-theme/helpers/useLiveData';
6
+ import type { Content } from '@plone/types';
6
7
 
7
- const Tags = ({ content }) => {
8
- const tags = useLiveData(content, undefined, 'subjects') || [];
8
+ type TagsProps = {
9
+ content?: Content;
10
+ };
11
+
12
+ const Tags: React.FC<TagsProps> = ({ content }) => {
13
+ const safeContent = content ?? ({ subjects: [] } as Content);
14
+ const tags = useLiveData<string[]>(safeContent, undefined, 'subjects') || [];
9
15
  const Container =
10
16
  config.getComponent({ name: 'Container' }).component || SemanticContainer;
11
17
 
@@ -22,26 +28,4 @@ const Tags = ({ content }) => {
22
28
  );
23
29
  };
24
30
 
25
- /**
26
- * Property types.
27
- * @property {Object} propTypes Property types.
28
- * @static
29
- */
30
- Tags.propTypes = {
31
- content: PropTypes.shape({
32
- subjects: PropTypes.arrayOf(PropTypes.string),
33
- }),
34
- };
35
-
36
- /**
37
- * Default properties.
38
- * @property {Object} defaultProps Default properties.
39
- * @static
40
- */
41
- Tags.defaultProps = {
42
- content: {
43
- subjects: [],
44
- },
45
- };
46
-
47
31
  export default Tags;
@@ -73,14 +73,14 @@ const EventView = (props) => {
73
73
  return (
74
74
  <Container id="page-document" className="view-wrapper event-view">
75
75
  <div className="dates">
76
- {content?.head_title && (
77
- <span className="head-title"> {content?.head_title}</span>
78
- )}{' '}
79
76
  {formattedDate ? (
80
77
  <span className="day" suppressHydrationWarning>
81
78
  {formattedDate}
82
79
  </span>
83
- ) : null}
80
+ ) : null}{' '}
81
+ {content?.head_title && (
82
+ <span className="head-title"> {content?.head_title}</span>
83
+ )}
84
84
  </div>
85
85
  {hasBlocksData(content) ? (
86
86
  <>
@@ -23,6 +23,7 @@ const ImageView = ({ content }) => {
23
23
  const Image = config.getComponent('Image').component;
24
24
  const Container =
25
25
  config.getComponent({ name: 'Container' }).component || SemanticContainer;
26
+ const width = config.settings.layout.defaultContainerWidth;
26
27
 
27
28
  return (
28
29
  <Container id="page-document" className="view-wrapper image-view">
@@ -30,7 +31,13 @@ const ImageView = ({ content }) => {
30
31
  <h1 className="documentFirstHeading">{content.title}</h1>
31
32
  {content?.image?.download && (
32
33
  <figure>
33
- <Image item={content} imageField="image" alt="" responsive={true} />
34
+ <Image
35
+ item={content}
36
+ imageField="image"
37
+ alt=""
38
+ responsive={true}
39
+ sizes={`(max-width: ${width}px) 100vw, ${width}px`}
40
+ />
34
41
  <Caption
35
42
  title={content.title}
36
43
  description={content.description}
@@ -21,9 +21,6 @@ const NewsItemView = ({ content }) => {
21
21
  return (
22
22
  <Container id="page-document" className="view-wrapper newsitem-view">
23
23
  <div className="dates">
24
- {content?.head_title && (
25
- <span className="head-title"> {content?.head_title}</span>
26
- )}{' '}
27
24
  {content.effective ? (
28
25
  <FormattedDate
29
26
  key="day"
@@ -35,7 +32,10 @@ const NewsItemView = ({ content }) => {
35
32
  }}
36
33
  className="day"
37
34
  />
38
- ) : null}
35
+ ) : null}{' '}
36
+ {content?.head_title && (
37
+ <span className="head-title"> {content?.head_title}</span>
38
+ )}
39
39
  </div>
40
40
  <RenderBlocks content={content} />
41
41
  </Container>
@@ -16,6 +16,7 @@ import RenderEmptyBlock from '@plone/volto/components/theme/View/RenderEmptyBloc
16
16
  import cx from 'classnames';
17
17
  import { groupByBGColor } from '../../helpers/grouping';
18
18
  import ErrorBoundary from '../Blocks/Block/ErrorBoundary';
19
+ import StyleWrapperV3 from './StyleWrapperV3';
19
20
 
20
21
  const messages = defineMessages({
21
22
  unknownBlock: {
@@ -68,6 +69,9 @@ const RenderBlocks = (props) => {
68
69
  }
69
70
  >
70
71
  {group.map((block) => {
72
+ const currentBlockModel =
73
+ blocksConfig[content[blocksFieldname]?.[block]?.['@type']]
74
+ ?.blockModel;
71
75
  const Block =
72
76
  blocksConfig[content[blocksFieldname]?.[block]?.['@type']]
73
77
  ?.view || ViewDefaultBlock;
@@ -104,23 +108,42 @@ const RenderBlocks = (props) => {
104
108
  condition={blockWrapperTag}
105
109
  as={blockWrapperTag}
106
110
  >
107
- <StyleWrapper
108
- key={block}
109
- {...props}
110
- id={block}
111
- block={block}
112
- data={blockData}
113
- isContainer={isContainer}
114
- >
115
- <Block
116
- id={block}
117
- metadata={metadata}
118
- properties={content}
111
+ {currentBlockModel === 3 ? (
112
+ <StyleWrapperV3
113
+ block={block}
114
+ content={content}
119
115
  data={blockData}
120
- path={getBaseUrl(location?.pathname || '')}
121
116
  blocksConfig={blocksConfig}
122
- />
123
- </StyleWrapper>
117
+ isContainer={isContainer}
118
+ >
119
+ <Block
120
+ id={block}
121
+ metadata={metadata}
122
+ properties={content}
123
+ data={blockData}
124
+ path={getBaseUrl(location?.pathname || '')}
125
+ blocksConfig={blocksConfig}
126
+ />
127
+ </StyleWrapperV3>
128
+ ) : (
129
+ <StyleWrapper
130
+ key={block}
131
+ {...props}
132
+ id={block}
133
+ block={block}
134
+ data={blockData}
135
+ isContainer={isContainer}
136
+ >
137
+ <Block
138
+ id={block}
139
+ metadata={metadata}
140
+ properties={content}
141
+ data={blockData}
142
+ path={getBaseUrl(location?.pathname || '')}
143
+ blocksConfig={blocksConfig}
144
+ />
145
+ </StyleWrapper>
146
+ )}
124
147
  </MaybeWrap>
125
148
  </ErrorBoundary>
126
149
  );
@@ -0,0 +1,197 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import Wrapper from '@plone/volto/storybook';
4
+ import type { StyleDefinition } from '@plone/types';
5
+
6
+ import ColorSwatch, { type ColorSwatchProps } from './ColorSwatch';
7
+
8
+ const palettes: StyleDefinition[] = [
9
+ {
10
+ name: 'default',
11
+ label: 'Default',
12
+ style: {
13
+ '--theme-color': '#ffffff',
14
+ '--theme-foreground-color': '#1b1c1d',
15
+ '--theme-low-contrast-foreground-color': '#585858',
16
+ },
17
+ },
18
+ {
19
+ name: 'warm',
20
+ label: 'Warm',
21
+ style: {
22
+ '--theme-color': '#fff5ed',
23
+ '--theme-foreground-color': '#6b2c1f',
24
+ '--theme-low-contrast-foreground-color': '#b06a54',
25
+ },
26
+ },
27
+ {
28
+ name: 'unstyled',
29
+ label: 'No Inline Style',
30
+ style: undefined,
31
+ },
32
+ ];
33
+
34
+ const themePalettes: StyleDefinition[] = [
35
+ {
36
+ name: 'ocean',
37
+ label: 'Ocean',
38
+ style: {
39
+ '--theme-color': '#dff5ff',
40
+ '--theme-foreground-color': '#003d5b',
41
+ '--theme-low-contrast-foreground-color': '#5c6b73',
42
+ },
43
+ },
44
+ {
45
+ name: 'forest',
46
+ label: 'Forest',
47
+ style: {
48
+ '--theme-color': '#e6f4ea',
49
+ '--theme-foreground-color': '#1e4d2b',
50
+ '--theme-low-contrast-foreground-color': '#4b7754',
51
+ },
52
+ },
53
+ ];
54
+
55
+ const palettesWithoutDefault: StyleDefinition[] = [
56
+ {
57
+ name: 'primary',
58
+ label: 'Primary',
59
+ style: {
60
+ '--theme-color': '#333333',
61
+ '--theme-foreground-color': '#f2f2f2',
62
+ '--theme-low-contrast-foreground-color': '#d9d9d9',
63
+ },
64
+ },
65
+ {
66
+ name: 'secondary',
67
+ label: 'Secondary',
68
+ style: {
69
+ '--theme-color': '#666666',
70
+ '--theme-foreground-color': '#ffffff',
71
+ '--theme-low-contrast-foreground-color': '#f0f0f0',
72
+ },
73
+ },
74
+ ];
75
+
76
+ const meta = {
77
+ title: 'Widgets/ColorSwatch',
78
+ component: ColorSwatch,
79
+ parameters: {
80
+ layout: 'centered',
81
+ },
82
+ decorators: [
83
+ (Story) => (
84
+ <Wrapper>
85
+ <div style={{ width: 320 }}>
86
+ <Story />
87
+ </div>
88
+ </Wrapper>
89
+ ),
90
+ ],
91
+ tags: ['autodocs'],
92
+ } satisfies Meta<typeof ColorSwatch>;
93
+
94
+ export default meta;
95
+ type Story = StoryObj<typeof meta>;
96
+
97
+ const resolveSelectedColorName = (
98
+ args: ColorSwatchProps,
99
+ value: string | undefined,
100
+ ) => {
101
+ const colors = args.themes || args.colors || [];
102
+ const selectedColorName = colors.find(({ name }) => name === value)?.name;
103
+ const defaultSelectedColorName =
104
+ !selectedColorName && typeof args.default === 'string'
105
+ ? colors.find(({ name }) => name === args.default)?.name
106
+ : undefined;
107
+
108
+ if (selectedColorName || defaultSelectedColorName) {
109
+ return selectedColorName ?? defaultSelectedColorName;
110
+ }
111
+
112
+ return colors.find(({ name }) => name === 'default')?.name ?? colors[0]?.name;
113
+ };
114
+
115
+ const InteractiveColorSwatch = (args: ColorSwatchProps) => {
116
+ const [value, setValue] = React.useState(args.value);
117
+ const selectedValue = resolveSelectedColorName(args, value);
118
+
119
+ return (
120
+ <>
121
+ <ColorSwatch
122
+ {...args}
123
+ value={value}
124
+ onChange={(id, selectedValue) => {
125
+ setValue(selectedValue);
126
+ args.onChange?.(id, selectedValue);
127
+ }}
128
+ />
129
+ <div style={{ marginTop: '10px' }}>
130
+ The selected value is: <strong>{selectedValue || 'none'}</strong>
131
+ </div>
132
+ </>
133
+ );
134
+ };
135
+
136
+ export const NoPalettes: Story = {
137
+ render: (args) => <InteractiveColorSwatch {...args} />,
138
+ args: {
139
+ id: 'color',
140
+ label: 'Theme',
141
+ title: 'Theme',
142
+ themes: [],
143
+ },
144
+ };
145
+
146
+ export const DefaultPalette: Story = {
147
+ render: (args) => <InteractiveColorSwatch {...args} />,
148
+ args: {
149
+ id: 'color',
150
+ label: 'Color palette',
151
+ title: 'Color palette',
152
+ value: 'default',
153
+ colors: palettes,
154
+ },
155
+ };
156
+
157
+ export const ThemePalette: Story = {
158
+ render: (args) => <InteractiveColorSwatch {...args} />,
159
+ args: {
160
+ id: 'theme',
161
+ label: 'Theme palette',
162
+ title: 'Theme palette',
163
+ value: 'ocean',
164
+ themes: themePalettes,
165
+ },
166
+ };
167
+
168
+ export const ColorPaletteWithDefault: Story = {
169
+ render: (args) => <InteractiveColorSwatch {...args} />,
170
+ args: {
171
+ id: 'color',
172
+ label: 'Color palette',
173
+ title: 'Color palette',
174
+ colors: palettes,
175
+ default: 'warm',
176
+ },
177
+ };
178
+
179
+ export const ColorPaletteNoValueNoDefault: Story = {
180
+ render: (args) => <InteractiveColorSwatch {...args} />,
181
+ args: {
182
+ id: 'color',
183
+ label: 'Color palette',
184
+ title: 'Color palette',
185
+ colors: palettes,
186
+ },
187
+ };
188
+
189
+ export const ColorPaletteNoValueNoDefaultWithFallbackToFirst: Story = {
190
+ render: (args) => <InteractiveColorSwatch {...args} />,
191
+ args: {
192
+ id: 'color',
193
+ label: 'Color palette',
194
+ title: 'Color palette',
195
+ colors: palettesWithoutDefault,
196
+ },
197
+ };