@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,9 +1,6 @@
1
1
  import type { BlocksFormData } from '@plone/types';
2
- import { findStyleByName } from '@plone/volto/helpers/Blocks/Blocks';
3
2
 
4
- import config from '@plone/volto/registry';
5
-
6
- function* visitBlocks(blocks) {
3
+ function* visitBlocks(blocks: any): Generator<any> {
7
4
  for (const key in blocks) {
8
5
  if (blocks.hasOwnProperty(key)) {
9
6
  const block = blocks[key];
@@ -16,17 +13,6 @@ function* visitBlocks(blocks) {
16
13
  }
17
14
 
18
15
  export function migrateToVLT6ColorAndWidthModel(data: BlocksFormData) {
19
- const NORMALIZED_WIDTHS = [
20
- ...config.blocks.widths,
21
- {
22
- style: {
23
- '--block-width': 'var(--default-container-width)',
24
- },
25
- name: 'wide',
26
- label: 'Default',
27
- },
28
- ];
29
-
30
16
  for (const block of visitBlocks(data.blocks)) {
31
17
  if (block?.styles?.backgroundColor) {
32
18
  if (block.styles.backgroundColor === 'transparent') {
@@ -41,10 +27,7 @@ export function migrateToVLT6ColorAndWidthModel(data: BlocksFormData) {
41
27
  ...block.styles,
42
28
  'blockWidth:noprefix':
43
29
  block.styles?.['blockWidth:noprefix'] ??
44
- findStyleByName(
45
- NORMALIZED_WIDTHS,
46
- `${block.styles?.buttonAlign === 'wide' ? 'default' : 'narrow'}`,
47
- ),
30
+ (block.styles?.buttonAlign === 'wide' ? 'default' : 'narrow'),
48
31
  };
49
32
 
50
33
  delete block.styles.buttonAlign;
@@ -54,41 +37,14 @@ export function migrateToVLT6ColorAndWidthModel(data: BlocksFormData) {
54
37
  block.styles = {
55
38
  ...block.styles,
56
39
  shortLine:
57
- block.styles?.['shortLine'] ?? block?.styles?.align === 'left'
58
- ? true
59
- : false,
60
- 'align:noprefix': block.styles?.['align:noprefix'] ?? {
61
- '--block-alignment': 'var(--align-left)',
62
- },
40
+ block.styles?.['shortLine'] ?? block?.styles?.align === 'left',
41
+ 'align:noprefix': block.styles?.['align:noprefix'] ?? 'left',
63
42
  'blockWidth:noprefix':
64
43
  block.styles?.['blockWidth:noprefix'] ??
65
- findStyleByName(
66
- NORMALIZED_WIDTHS,
67
- `${block.styles?.align === 'full' ? 'default' : 'narrow'}`,
68
- ),
44
+ (block.styles?.align === 'full' ? 'default' : 'narrow'),
69
45
  };
70
46
 
71
47
  delete block.styles.align;
72
48
  }
73
-
74
- if (
75
- block['@type'] === 'image' &&
76
- !['left', 'right'].includes(block?.align)
77
- ) {
78
- block.styles = {
79
- ...block.styles,
80
- 'blockWidth:noprefix':
81
- block.styles?.['blockWidth:noprefix'] ??
82
- findStyleByName(
83
- NORMALIZED_WIDTHS,
84
- block?.align === 'wide'
85
- ? 'default'
86
- : block?.align === 'center'
87
- ? 'narrow'
88
- : block.align,
89
- ),
90
- };
91
- block.align = 'center';
92
- }
93
49
  }
94
50
  }
@@ -0,0 +1,201 @@
1
+ import { describe, it, expect, beforeAll } from 'vitest';
2
+ import config from '@plone/volto/registry';
3
+ import { migrateToVLT8FloatingBlocks } from './to8';
4
+
5
+ const alignLeft = { 'align:noprefix': 'left' };
6
+ const alignRight = { 'align:noprefix': 'right' };
7
+ const alignCenter = { 'align:noprefix': 'center' };
8
+ const narrowWidth = { 'blockWidth:noprefix': 'narrow' };
9
+ const defaultWidth = { 'blockWidth:noprefix': 'default' };
10
+ const fullWidth = { 'blockWidth:noprefix': 'full' };
11
+
12
+ // Wraps a single block in the `data` shape the transform expects and runs it.
13
+ const migrate = (block) => {
14
+ const data = { blocks: { 'block-1': block } };
15
+ migrateToVLT8FloatingBlocks(data);
16
+ return data.blocks['block-1'];
17
+ };
18
+
19
+ beforeAll(() => {
20
+ config.blocks.widths = [
21
+ {
22
+ name: 'narrow',
23
+ style: { '--block-width': 'var(--narrow-container-width)' },
24
+ },
25
+ {
26
+ name: 'default',
27
+ style: { '--block-width': 'var(--default-container-width)' },
28
+ },
29
+ {
30
+ name: 'layout',
31
+ style: { '--block-width': 'var(--layout-container-width)' },
32
+ },
33
+ { name: 'full', style: { '--block-width': '100%' } },
34
+ ];
35
+ config.blocks.alignments = [
36
+ { name: 'left', style: { '--block-alignment': 'var(--align-left)' } },
37
+ { name: 'center', style: { '--block-alignment': 'var(--align-center)' } },
38
+ { name: 'right', style: { '--block-alignment': 'var(--align-right)' } },
39
+ ];
40
+ });
41
+
42
+ describe('migrateToVLT8FloatingBlocks', () => {
43
+ it('centers a non-floating image and applies the narrow width', () => {
44
+ expect(migrate({ '@type': 'image', align: 'center' })).toEqual({
45
+ '@type': 'image',
46
+ styles: { ...alignCenter, ...narrowWidth },
47
+ });
48
+ });
49
+
50
+ it('applies the default width to a wide image', () => {
51
+ expect(migrate({ '@type': 'image', align: 'wide' })).toEqual({
52
+ '@type': 'image',
53
+ styles: { ...alignCenter, ...defaultWidth },
54
+ });
55
+ });
56
+
57
+ it('applies the full width to a full image', () => {
58
+ expect(migrate({ '@type': 'image', align: 'full' })).toEqual({
59
+ '@type': 'image',
60
+ styles: { ...alignCenter, ...fullWidth },
61
+ });
62
+ });
63
+
64
+ it('applies the narrow width to a small floating image', () => {
65
+ expect(migrate({ '@type': 'image', align: 'left', size: 'm' })).toEqual({
66
+ '@type': 'image',
67
+ size: 'm',
68
+ styles: { ...alignLeft, ...narrowWidth },
69
+ });
70
+ });
71
+
72
+ it('applies the default width to a floating image that is large by size', () => {
73
+ expect(migrate({ '@type': 'image', align: 'right', size: 'l' })).toEqual({
74
+ '@type': 'image',
75
+ size: 'l',
76
+ styles: { ...alignRight, ...defaultWidth },
77
+ });
78
+ });
79
+
80
+ it('applies the default width to a floating image that is large by style', () => {
81
+ expect(
82
+ migrate({
83
+ '@type': 'image',
84
+ align: 'left',
85
+ styles: { 'size:noprefix': 'var(--size-large)' },
86
+ }),
87
+ ).toEqual({
88
+ '@type': 'image',
89
+ styles: {
90
+ 'size:noprefix': 'var(--size-large)',
91
+ ...alignLeft,
92
+ ...defaultWidth,
93
+ },
94
+ });
95
+ });
96
+
97
+ it('applies the default width to a floating video', () => {
98
+ expect(migrate({ '@type': 'video', align: 'left' })).toEqual({
99
+ '@type': 'video',
100
+ styles: { ...alignLeft, ...defaultWidth },
101
+ });
102
+ });
103
+
104
+ it('applies the narrow width to a non-floating video', () => {
105
+ expect(migrate({ '@type': 'video', align: 'center' })).toEqual({
106
+ '@type': 'video',
107
+ styles: { ...alignCenter, ...narrowWidth },
108
+ });
109
+ });
110
+
111
+ it('applies the default width to a floating map', () => {
112
+ expect(migrate({ '@type': 'maps', align: 'right' })).toEqual({
113
+ '@type': 'maps',
114
+ styles: { ...alignRight, ...defaultWidth },
115
+ });
116
+ });
117
+
118
+ it('applies the full width to a full map', () => {
119
+ expect(migrate({ '@type': 'maps', align: 'full' })).toEqual({
120
+ '@type': 'maps',
121
+ styles: { ...alignCenter, ...fullWidth },
122
+ });
123
+ });
124
+
125
+ it('migrates the button inneralign to the align literal', () => {
126
+ expect(migrate({ '@type': '__button', inneralign: 'right' })).toEqual({
127
+ '@type': '__button',
128
+ styles: { ...alignRight },
129
+ });
130
+ });
131
+
132
+ it('keeps an existing button alignment over the legacy inneralign', () => {
133
+ expect(
134
+ migrate({
135
+ '@type': '__button',
136
+ inneralign: 'right',
137
+ styles: { ...alignLeft },
138
+ }),
139
+ ).toEqual({
140
+ '@type': '__button',
141
+ styles: { ...alignLeft },
142
+ });
143
+ });
144
+
145
+ it('drops the button inneralign property when it has no value', () => {
146
+ expect(migrate({ '@type': '__button', inneralign: '' })).toEqual({
147
+ '@type': '__button',
148
+ });
149
+ });
150
+
151
+ it('leaves an already migrated block untouched', () => {
152
+ // No legacy `align`, alignment and width already stored as literals.
153
+ expect(
154
+ migrate({ '@type': 'image', styles: { ...alignLeft, ...defaultWidth } }),
155
+ ).toEqual({
156
+ '@type': 'image',
157
+ styles: { ...alignLeft, ...defaultWidth },
158
+ });
159
+ });
160
+
161
+ it('keeps a pre-existing width for a non-floating block', () => {
162
+ expect(
163
+ migrate({ '@type': 'image', align: 'center', styles: { ...fullWidth } }),
164
+ ).toEqual({
165
+ '@type': 'image',
166
+ styles: { ...fullWidth, ...alignCenter },
167
+ });
168
+ });
169
+
170
+ it('ignores blocks of other types', () => {
171
+ expect(migrate({ '@type': 'slate', align: 'left' })).toEqual({
172
+ '@type': 'slate',
173
+ align: 'left',
174
+ });
175
+ });
176
+
177
+ it('migrates nested blocks recursively', () => {
178
+ const data = {
179
+ blocks: {
180
+ 'block-1': {
181
+ '@type': 'image',
182
+ align: 'left',
183
+ blocks: {
184
+ 'block-2': { '@type': 'video', align: 'right' },
185
+ },
186
+ },
187
+ },
188
+ };
189
+
190
+ migrateToVLT8FloatingBlocks(data);
191
+
192
+ expect(data.blocks['block-1']).toMatchObject({
193
+ '@type': 'image',
194
+ styles: { ...alignLeft, ...narrowWidth },
195
+ });
196
+ expect(data.blocks['block-1'].blocks['block-2']).toEqual({
197
+ '@type': 'video',
198
+ styles: { ...alignRight, ...defaultWidth },
199
+ });
200
+ });
201
+ });
@@ -0,0 +1,109 @@
1
+ import type { BlocksFormData, StyleDefinition } from '@plone/types';
2
+ import config from '@plone/volto/registry';
3
+
4
+ function* visitBlocks(blocks: any): Generator<any> {
5
+ for (const key in blocks) {
6
+ if (blocks.hasOwnProperty(key)) {
7
+ const block = blocks[key];
8
+ yield block;
9
+ if (block.blocks) {
10
+ yield* visitBlocks(block.blocks);
11
+ }
12
+ }
13
+ }
14
+ }
15
+
16
+ function alignmentLiteral(align: string | undefined): string {
17
+ if (align === 'left') return 'left';
18
+ if (align === 'right') return 'right';
19
+ return 'center';
20
+ }
21
+
22
+ // The block alignments can be overridden per block, falling back to the global
23
+ // definitions, the same way themes work.
24
+ function alignmentsFor(block: any): StyleDefinition[] {
25
+ return (
26
+ config.blocks.blocksConfig?.[block['@type']]?.alignments ||
27
+ config.blocks.alignments ||
28
+ []
29
+ );
30
+ }
31
+
32
+ // Pre-VLT8 the resolved CSS object was stored in the style field
33
+ // (e.g. `{ '--block-width': 'var(--narrow-container-width)' }`). VLT8 stores
34
+ // only the token literal (`narrow`) and resolves it at runtime through the
35
+ // `styleFieldDefinition` utility.
36
+ function normalizeStyleField(
37
+ block: any,
38
+ fieldName: string,
39
+ definitions: StyleDefinition[],
40
+ ) {
41
+ const value = block?.styles?.[fieldName];
42
+ if (!value || typeof value !== 'object') return;
43
+
44
+ const match = definitions.find((definition) =>
45
+ Object.entries(definition.style ?? {}).every(
46
+ ([key, css]) => value[key] === css,
47
+ ),
48
+ );
49
+ if (match) {
50
+ block.styles[fieldName] = match.name;
51
+ }
52
+ }
53
+
54
+ // Migrates the legacy `align` field of a media block to the `align:noprefix` /
55
+ // `blockWidth:noprefix` style literals. `sizeAware` images keep a narrow width
56
+ // while floating unless they are large; videos and maps always go to default.
57
+ function migrateMediaBlock(block: any, sizeAware: boolean) {
58
+ const align = block?.align;
59
+ const isFloating = align === 'left' || align === 'right';
60
+ const isLarge =
61
+ block?.styles?.['size:noprefix'] === 'var(--size-large)' ||
62
+ block?.size === 'l';
63
+
64
+ const isWide = align === 'wide' || (isFloating && (!sizeAware || isLarge));
65
+ const width = isWide ? 'default' : align === 'full' ? 'full' : 'narrow';
66
+
67
+ block.styles = {
68
+ ...block?.styles,
69
+ 'align:noprefix':
70
+ block?.styles?.['align:noprefix'] ?? alignmentLiteral(align),
71
+ // While floating, the width is driven by the adapter, so it always wins;
72
+ // otherwise an explicit width already on the block is kept.
73
+ 'blockWidth:noprefix': isFloating
74
+ ? width
75
+ : block?.styles?.['blockWidth:noprefix'] ?? width,
76
+ };
77
+
78
+ delete block.align;
79
+ }
80
+
81
+ export function migrateToVLT8FloatingBlocks(data: BlocksFormData) {
82
+ const widths = config.blocks.widths || [];
83
+
84
+ for (const block of visitBlocks(data.blocks)) {
85
+ // Rewrite any CSS object left over from before VLT8 to its token literal.
86
+ normalizeStyleField(block, 'align:noprefix', alignmentsFor(block));
87
+ normalizeStyleField(block, 'blockWidth:noprefix', widths);
88
+
89
+ if (block['@type'] === 'image') {
90
+ migrateMediaBlock(block, true);
91
+ }
92
+
93
+ if (block['@type'] === 'video' || block['@type'] === 'maps') {
94
+ migrateMediaBlock(block, false);
95
+ }
96
+
97
+ // The button's legacy `inneralign` becomes the `align:noprefix` literal.
98
+ if (block['@type'] === '__button') {
99
+ if (block.inneralign) {
100
+ block.styles = {
101
+ ...block.styles,
102
+ 'align:noprefix':
103
+ block.styles?.['align:noprefix'] ?? block.inneralign,
104
+ };
105
+ }
106
+ delete block.inneralign;
107
+ }
108
+ }
109
+ }
package/src/types.d.ts CHANGED
@@ -81,6 +81,7 @@ export type StickyMenuSettings = {
81
81
  sticky_menu: Array<iconLink>;
82
82
  sticky_menu_color: string;
83
83
  sticky_menu_foreground_color: string;
84
+ enable_mobile_sticky_menu: boolean;
84
85
  };
85
86
 
86
87
  export type PloneGobrSocialMediaSettings = {
package/vitest.config.mjs CHANGED
@@ -1,17 +1,42 @@
1
1
  import { defineConfig } from 'vitest/config';
2
2
  import voltoVitestConfig from '@plone/volto/vitest.config.mjs';
3
3
  import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ const localAliases = {
10
+ // Alias for absolute imports within this addon
11
+ '@kitconcept/volto-light-theme': path.resolve(__dirname, './src'),
12
+ '@kitconcept/volto-light-theme/': path.resolve(__dirname, './src/'),
13
+ 'semantic-ui-react': path.resolve(
14
+ __dirname,
15
+ './src/__mocks__/semantic-ui-react.ts',
16
+ ),
17
+ };
4
18
 
5
19
  export default defineConfig({
6
20
  ...voltoVitestConfig,
7
21
  resolve: {
8
22
  alias: {
9
23
  ...voltoVitestConfig.resolve.alias,
10
- // Alias for absolute imports
11
- '@kitconcept/volto-light-theme': path.resolve(__dirname, './src'),
12
- '@kitconcept/volto-light-theme/': path.resolve(__dirname, './src/'),
24
+ ...localAliases,
13
25
  },
14
26
  },
27
+ test: {
28
+ ...voltoVitestConfig.test,
29
+ projects: voltoVitestConfig.test.projects.map((project) => ({
30
+ ...project,
31
+ resolve: {
32
+ ...project.resolve,
33
+ alias: {
34
+ ...project.resolve?.alias,
35
+ ...localAliases,
36
+ },
37
+ },
38
+ })),
39
+ },
15
40
  server: {
16
41
  fs: {
17
42
  allow: [
package/razzle.extend.js DELETED
@@ -1,38 +0,0 @@
1
- const plugins = (defaultPlugins) => {
2
- const newPlugins = defaultPlugins.filter((plugin) => plugin !== 'scss');
3
- newPlugins.push({
4
- name: 'scss',
5
- options: {
6
- sass: {
7
- dev: {
8
- sassOptions: {
9
- includePaths: ['node_modules'],
10
- outputStyle: 'expanded',
11
- sourceMap: true,
12
- silenceDeprecations: ['legacy-js-api', 'mixed-decls', 'import'],
13
- quietDeps: true,
14
- },
15
- },
16
- prod: {
17
- sassOptions: {
18
- includePaths: ['node_modules'],
19
- outputStyle: 'expanded',
20
- sourceMap: true,
21
- silenceDeprecations: ['legacy-js-api', 'mixed-decls', 'import'],
22
- quietDeps: true,
23
- },
24
- },
25
- },
26
- },
27
- });
28
- return newPlugins;
29
- };
30
-
31
- const modify = (config, { target, dev }, webpack) => {
32
- return config;
33
- };
34
-
35
- module.exports = {
36
- plugins,
37
- modify,
38
- };
@@ -1,44 +0,0 @@
1
- import { addStyling } from '@plone/volto/helpers/Extensions/withBlockSchemaEnhancer';
2
- import { defineMessages } from 'react-intl';
3
- import config from '@plone/volto/registry';
4
-
5
- const messages = defineMessages({
6
- backgroundColor: {
7
- id: 'Background color',
8
- defaultMessage: 'Background color',
9
- },
10
- });
11
-
12
- export const defaultStylingSchema = ({ schema, formData, intl }) => {
13
- const themes =
14
- config.blocks?.blocksConfig?.[formData['@type']]?.themes ||
15
- config.blocks.themes;
16
-
17
- const defaultTheme =
18
- config.blocks?.blocksConfig?.[formData['@type']]?.defaultTheme ||
19
- // The default color is the first color in the themes list
20
- config.blocks.themes?.[0].name;
21
-
22
- addStyling({ schema, intl });
23
-
24
- const stylingIndex = schema.fieldsets.findIndex(
25
- (item) => item.id === 'styling',
26
- );
27
- schema.fieldsets[stylingIndex].fields = [
28
- ...schema.fieldsets[stylingIndex].fields,
29
- 'theme',
30
- ];
31
- schema.properties.theme = {
32
- widget: 'color_picker',
33
- title: intl.formatMessage(messages.backgroundColor),
34
- themes,
35
- default: defaultTheme,
36
- };
37
-
38
- return schema;
39
- };
40
-
41
- export const removeStylingSchema = ({ schema, formData, intl }) => {
42
- schema.fieldsets = schema.fieldsets.filter((item) => item.id !== 'styling');
43
- return schema;
44
- };
@@ -1,118 +0,0 @@
1
- // SemanticUI-free pre-@plone/components
2
- import React, { Component } from 'react';
3
- import PropTypes from 'prop-types';
4
- import { connect } from 'react-redux';
5
- import { compose } from 'redux';
6
- import { Link } from 'react-router-dom';
7
- import { defineMessages, injectIntl } from 'react-intl';
8
- import { Container } from '@plone/components';
9
-
10
- import Icon from '@plone/volto/components/theme/Icon/Icon';
11
- import { getBreadcrumbs } from '@plone/volto/actions/breadcrumbs/breadcrumbs';
12
- import { getBaseUrl } from '@plone/volto/helpers/Url/Url';
13
- import { hasApiExpander } from '@plone/volto/helpers/Utils/Utils';
14
-
15
- import homeSVG from '@plone/volto/icons/home.svg';
16
-
17
- const messages = defineMessages({
18
- home: {
19
- id: 'Home',
20
- defaultMessage: 'Home',
21
- },
22
- breadcrumbs: {
23
- id: 'Breadcrumbs',
24
- defaultMessage: 'Breadcrumbs',
25
- },
26
- });
27
-
28
- /**
29
- * Breadcrumbs container class.
30
- */
31
- export class BreadcrumbsComponent extends Component {
32
- /**
33
- * Property types.
34
- * @property {Object} propTypes Property types.
35
- * @static
36
- */
37
- static propTypes = {
38
- getBreadcrumbs: PropTypes.func.isRequired,
39
- pathname: PropTypes.string.isRequired,
40
- root: PropTypes.string,
41
- items: PropTypes.arrayOf(
42
- PropTypes.shape({
43
- title: PropTypes.string,
44
- url: PropTypes.string,
45
- }),
46
- ).isRequired,
47
- };
48
-
49
- componentDidMount() {
50
- if (!hasApiExpander('breadcrumbs', getBaseUrl(this.props.pathname))) {
51
- this.props.getBreadcrumbs(getBaseUrl(this.props.pathname));
52
- }
53
- }
54
-
55
- /**
56
- * Component will receive props
57
- * @method componentWillReceiveProps
58
- * @param {Object} nextProps Next properties
59
- * @returns {undefined}
60
- */
61
- UNSAFE_componentWillReceiveProps(nextProps) {
62
- if (nextProps.pathname !== this.props.pathname) {
63
- if (!hasApiExpander('breadcrumbs', getBaseUrl(this.props.pathname))) {
64
- this.props.getBreadcrumbs(getBaseUrl(nextProps.pathname));
65
- }
66
- }
67
- }
68
-
69
- /**
70
- * Render method.
71
- * @method render
72
- * @returns {string} Markup for the component.
73
- */
74
- render() {
75
- return (
76
- <div
77
- role="navigation"
78
- aria-label={this.props.intl.formatMessage(messages.breadcrumbs)}
79
- className="breadcrumbs"
80
- >
81
- <Container layout>
82
- <div className="breadcrumb">
83
- <Link
84
- to={this.props.root || '/'}
85
- className="home"
86
- title={this.props.intl.formatMessage(messages.home)}
87
- >
88
- <Icon name={homeSVG} size="25px" />
89
- </Link>
90
- {this.props.items.map((item, index, items) => [
91
- <div className="divider" key={`divider-${item.url}`} />,
92
- index < items.length - 1 ? (
93
- <Link key={item.url} to={item.url} className="section">
94
- {item.title}
95
- </Link>
96
- ) : (
97
- <div className="section active" key={item.url}>
98
- {item.title}
99
- </div>
100
- ),
101
- ])}
102
- </div>
103
- </Container>
104
- </div>
105
- );
106
- }
107
- }
108
-
109
- export default compose(
110
- injectIntl,
111
- connect(
112
- (state) => ({
113
- items: state.breadcrumbs.items,
114
- root: state.breadcrumbs.root,
115
- }),
116
- { getBreadcrumbs },
117
- ),
118
- )(BreadcrumbsComponent);