@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
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+
3
+ type GenericProps = {
4
+ children?: React.ReactNode;
5
+ } & Record<string, unknown>;
6
+
7
+ const createComponent =
8
+ (element: keyof JSX.IntrinsicElements = 'div') =>
9
+ ({ children, ...rest }: GenericProps) =>
10
+ React.createElement(element, rest, children);
11
+
12
+ export const Container = createComponent('div');
13
+ export const Segment = createComponent('section');
14
+ export const Button = createComponent('button');
15
+ export const Form = createComponent('form');
16
+ export const Input = createComponent('input');
17
+ export const Pagination = createComponent('div');
18
+ export const Dimmer = createComponent('div');
19
+ export const Loader = createComponent('div');
20
+
21
+ // eslint-disable-next-line import/no-anonymous-default-export
22
+ export default {
23
+ Container,
24
+ Segment,
25
+ Button,
26
+ Form,
27
+ Input,
28
+ Pagination,
29
+ Dimmer,
30
+ Loader,
31
+ };
@@ -84,7 +84,15 @@ const EditBlockWrapper = (props) => {
84
84
  style: { ...style, ...draginfo.draggableProps.style },
85
85
  };
86
86
 
87
- return blocksConfig?.[type]?.blockModel !== 3 ? (
87
+ if (blocksConfig?.[type]?.blockModel === 3) {
88
+ // We still need to attach the draggable props/ref so RBD stays happy,
89
+ // but we skip rendering the drag handle and friends
90
+ // consider removing the whole EditBlockWrapper for BlockModelv3 blocks in the future
91
+ // or moving the handlers here.
92
+ return <>{children}</>;
93
+ }
94
+
95
+ return (
88
96
  <div
89
97
  ref={draginfo.innerRef}
90
98
  {...styleMergedWithDragProps}
@@ -160,8 +168,6 @@ const EditBlockWrapper = (props) => {
160
168
  </div>
161
169
  </div>
162
170
  </div>
163
- ) : (
164
- <>{children}</>
165
171
  );
166
172
  };
167
173
 
@@ -1,7 +1,19 @@
1
1
  import { defaultStylingSchema } from '../schema';
2
+ import config from '@plone/volto/registry';
2
3
 
3
4
  export const ButtonStylingSchema = ({ schema, formData, intl }) => {
4
5
  defaultStylingSchema({ schema, formData, intl });
6
+ const styleProps = schema.properties.styles?.schema?.properties;
7
+ if (styleProps?.['align:noprefix']) {
8
+ delete styleProps['align:noprefix'].filterActions;
9
+ styleProps['align:noprefix'].actions = config.blocks.alignments.map(
10
+ (alignment) => alignment.name,
11
+ );
12
+ }
13
+ if (styleProps?.['blockWidth:noprefix']) {
14
+ delete styleProps['blockWidth:noprefix'].filterActions;
15
+ styleProps['blockWidth:noprefix'].actions = ['narrow', 'default'];
16
+ }
5
17
 
6
18
  return schema;
7
19
  };
@@ -49,7 +49,7 @@ const EventItem = ({ item, lang, isEditMode }) => {
49
49
  <span className="event-title">{item.head_title}</span>
50
50
  )}
51
51
  </div>
52
- <DefaultSummary item={item} HeadingTag="h2" />
52
+ <DefaultSummary item={item} />
53
53
  </Card.Summary>
54
54
  </Card>
55
55
  </div>
@@ -13,6 +13,7 @@ import Caption from '../../Caption/Caption';
13
13
  function Edit(props) {
14
14
  const { data } = props;
15
15
  const Image = config.getComponent({ name: 'Image' }).component;
16
+
16
17
  const onSelectItem = React.useCallback(
17
18
  (url, item) => {
18
19
  const dataAdapter = props.blocksConfig[props.data['@type']].dataAdapter;
@@ -49,42 +50,17 @@ function Edit(props) {
49
50
 
50
51
  return (
51
52
  <>
52
- <div
53
- className={cx(
54
- 'block image align',
55
- {
56
- center: !Boolean(data.align),
57
- },
58
- data.align,
59
- )}
60
- >
53
+ <div className={cx('block image')}>
61
54
  {data.url ? (
62
55
  <figure
63
- className={cx(
64
- 'figure',
65
- {
66
- center: !Boolean(data.align),
67
- },
68
- data.align,
69
- {
70
- // START CUSTOMIZATION
71
- // 'full-width': data.align === 'full',
72
- // END CUSTOMIZATION
73
- large: data.size === 'l',
74
- medium: data.size === 'm' || !data.size,
75
- small: data.size === 's',
76
- },
77
- )}
56
+ // START CUSTOMIZATION
57
+ className={cx({
58
+ large: data.size === 'l' || !data.size,
59
+ medium: data.size === 'm',
60
+ small: data.size === 's',
61
+ })}
78
62
  >
79
63
  <Image
80
- // START CUSTOMIZATION - Moved to the figure
81
- // className={cx({
82
- // 'full-width': data.align === 'full',
83
- // large: data.size === 'l',
84
- // medium: data.size === 'm',
85
- // small: data.size === 's',
86
- // })}
87
- // END CUSTOMIZATION
88
64
  item={
89
65
  data.image_scales
90
66
  ? {
@@ -94,6 +70,7 @@ function Edit(props) {
94
70
  }
95
71
  : undefined
96
72
  }
73
+ // END CUSTOMIZATION
97
74
  src={
98
75
  data.image_scales
99
76
  ? undefined
@@ -37,12 +37,10 @@ export const ImageView = ({ className, data, detached, properties, style }) => {
37
37
  return (
38
38
  <div
39
39
  className={cx(
40
- 'block image align',
40
+ 'block image',
41
41
  {
42
- center: !Boolean(data.align),
43
42
  detached,
44
43
  },
45
- data.align,
46
44
  className,
47
45
  )}
48
46
  style={style}
@@ -52,31 +50,16 @@ export const ImageView = ({ className, data, detached, properties, style }) => {
52
50
  {(() => {
53
51
  const image = (
54
52
  <figure
55
- className={cx(
56
- 'figure',
57
- {
58
- center: !Boolean(data.align),
59
- detached,
60
- },
61
- data.align,
62
- {
63
- // START CUSTOMIZATION
64
- // 'full-width': data.align === 'full',
65
- // END CUSTOMIZATION
66
- large: data.size === 'l',
67
- medium: data.size === 'm' || !data.size,
68
- small: data.size === 's',
69
- },
70
- )}
53
+ // START CUSTOMIZATION
54
+ className={cx({
55
+ detached,
56
+ large: data.size === 'l' || !data.size,
57
+ medium: data.size === 'm',
58
+ small: data.size === 's',
59
+ })}
60
+ // END CUSTOMIZATION
71
61
  >
72
62
  <Image
73
- // Removed for now
74
- // className={cx({
75
- // 'full-width': data.align === 'full',
76
- // large: data.size === 'l',
77
- // medium: data.size === 'm',
78
- // small: data.size === 's',
79
- // })}
80
63
  item={
81
64
  data.image_scales
82
65
  ? {
@@ -17,25 +17,39 @@ export const ImageBlockDataAdapter = ({
17
17
  [id]: value,
18
18
  };
19
19
 
20
- if (id === 'align' && !(value === 'left' || value === 'right')) {
21
- if (data.size !== 'l') {
22
- dataSaved = {
23
- ...dataSaved,
24
- size: 'l',
25
- styles: {
26
- ...dataSaved.styles,
27
- 'size:noprefix': SIZEMAP['l'],
28
- },
29
- };
30
- }
31
- }
20
+ const align = dataSaved.styles?.['align:noprefix'];
21
+ const isFloating = align === 'left' || align === 'right';
22
+ const isLarge =
23
+ dataSaved.size === 'l' ||
24
+ dataSaved.styles?.['size:noprefix'] === 'var(--size-large)';
32
25
 
33
- if (id === 'size') {
26
+ if (!isFloating) {
27
+ dataSaved = {
28
+ ...dataSaved,
29
+ size: 'l',
30
+ styles: {
31
+ ...dataSaved.styles,
32
+ 'size:noprefix': SIZEMAP['l'],
33
+ },
34
+ };
35
+ }
36
+ if (!isLarge && isFloating) {
37
+ dataSaved = {
38
+ ...dataSaved,
39
+ styles: {
40
+ ...dataSaved.styles,
41
+ 'size:noprefix': SIZEMAP[dataSaved.size] || 'large',
42
+ 'blockWidth:noprefix': 'narrow',
43
+ },
44
+ };
45
+ }
46
+ if (isLarge && isFloating) {
34
47
  dataSaved = {
35
48
  ...dataSaved,
36
49
  styles: {
37
50
  ...dataSaved.styles,
38
- 'size:noprefix': SIZEMAP[value],
51
+ 'size:noprefix': SIZEMAP['l'],
52
+ 'blockWidth:noprefix': 'default',
39
53
  },
40
54
  };
41
55
  }
@@ -0,0 +1,156 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { ImageBlockDataAdapter } from './adapter';
3
+
4
+ const alignLeft = { 'align:noprefix': 'left' };
5
+ const alignRight = { 'align:noprefix': 'right' };
6
+
7
+ describe('ImageBlockDataAdapter', () => {
8
+ it('forces a large size for non-floating images', () => {
9
+ const onChangeBlock = vi.fn();
10
+
11
+ ImageBlockDataAdapter({
12
+ block: 'block-1',
13
+ data: { '@type': 'image' },
14
+ id: 'align',
15
+ onChangeBlock,
16
+ value: 'center',
17
+ });
18
+
19
+ expect(onChangeBlock).toHaveBeenCalledWith('block-1', {
20
+ '@type': 'image',
21
+ align: 'center',
22
+ size: 'l',
23
+ styles: { 'size:noprefix': 'large' },
24
+ });
25
+ });
26
+
27
+ it('maps the current size and applies a narrow width for a floating, non-large image', () => {
28
+ const onChangeBlock = vi.fn();
29
+
30
+ ImageBlockDataAdapter({
31
+ block: 'block-1',
32
+ data: { '@type': 'image', size: 'm', styles: { ...alignLeft } },
33
+ id: 'align',
34
+ onChangeBlock,
35
+ value: 'left',
36
+ });
37
+
38
+ expect(onChangeBlock).toHaveBeenCalledWith('block-1', {
39
+ '@type': 'image',
40
+ align: 'left',
41
+ size: 'm',
42
+ styles: {
43
+ ...alignLeft,
44
+ 'size:noprefix': 'medium',
45
+ 'blockWidth:noprefix': 'narrow',
46
+ },
47
+ });
48
+ });
49
+
50
+ it('applies the default width for a floating image that is large by size', () => {
51
+ const onChangeBlock = vi.fn();
52
+
53
+ ImageBlockDataAdapter({
54
+ block: 'block-1',
55
+ data: { '@type': 'image', size: 'l', styles: { ...alignRight } },
56
+ id: 'align',
57
+ onChangeBlock,
58
+ value: 'right',
59
+ });
60
+
61
+ expect(onChangeBlock).toHaveBeenCalledWith('block-1', {
62
+ '@type': 'image',
63
+ align: 'right',
64
+ size: 'l',
65
+ styles: {
66
+ ...alignRight,
67
+ 'size:noprefix': 'large',
68
+ 'blockWidth:noprefix': 'default',
69
+ },
70
+ });
71
+ });
72
+
73
+ it('applies the default width for a floating image that is large by style', () => {
74
+ const onChangeBlock = vi.fn();
75
+
76
+ ImageBlockDataAdapter({
77
+ block: 'block-1',
78
+ data: {
79
+ '@type': 'image',
80
+ styles: { ...alignLeft, 'size:noprefix': 'var(--size-large)' },
81
+ },
82
+ id: 'align',
83
+ onChangeBlock,
84
+ value: 'left',
85
+ });
86
+
87
+ expect(onChangeBlock).toHaveBeenCalledWith('block-1', {
88
+ '@type': 'image',
89
+ align: 'left',
90
+ styles: {
91
+ ...alignLeft,
92
+ 'size:noprefix': 'large',
93
+ 'blockWidth:noprefix': 'default',
94
+ },
95
+ });
96
+ });
97
+
98
+ it('copies the item metadata when a url value is set', () => {
99
+ const onChangeBlock = vi.fn();
100
+ const item = {
101
+ credit: 'A photographer',
102
+ Description: 'A description',
103
+ Title: 'A title',
104
+ image_field: 'image',
105
+ image_scales: { image: [{ download: 'image.png' }] },
106
+ };
107
+
108
+ ImageBlockDataAdapter({
109
+ block: 'block-1',
110
+ data: { '@type': 'image' },
111
+ id: 'url',
112
+ item,
113
+ onChangeBlock,
114
+ value: '/an-image',
115
+ });
116
+
117
+ expect(onChangeBlock).toHaveBeenCalledWith('block-1', {
118
+ '@type': 'image',
119
+ url: '/an-image',
120
+ size: 'l',
121
+ styles: { 'size:noprefix': 'large' },
122
+ credit: { data: 'A photographer' },
123
+ description: 'A description',
124
+ title: 'A title',
125
+ image_field: 'image',
126
+ image_scales: { image: [{ download: 'image.png' }] },
127
+ });
128
+ });
129
+
130
+ it('clears the item metadata when the url value is removed', () => {
131
+ const onChangeBlock = vi.fn();
132
+
133
+ ImageBlockDataAdapter({
134
+ block: 'block-1',
135
+ data: {
136
+ '@type': 'image',
137
+ alt: 'alt text',
138
+ credit: { data: 'credit' },
139
+ description: 'description',
140
+ image_scales: {},
141
+ image_field: 'image',
142
+ title: 'title',
143
+ },
144
+ id: 'url',
145
+ onChangeBlock,
146
+ value: '',
147
+ });
148
+
149
+ expect(onChangeBlock).toHaveBeenCalledWith('block-1', {
150
+ '@type': 'image',
151
+ url: '',
152
+ size: 'l',
153
+ styles: { 'size:noprefix': 'large' },
154
+ });
155
+ });
156
+ });
@@ -16,6 +16,10 @@ const messages = defineMessages({
16
16
  id: 'Block Width',
17
17
  defaultMessage: 'Block Width',
18
18
  },
19
+ Alignment: {
20
+ id: 'Alignment',
21
+ defaultMessage: 'Alignment',
22
+ },
19
23
  });
20
24
 
21
25
  export const imageBlockSchemaEnhancer = ({ formData, schema, intl }) => {
@@ -49,28 +53,38 @@ export const standAloneImageBlockSchemaEnhancer = ({
49
53
  intl,
50
54
  }) => {
51
55
  if (formData.url) {
52
- schema.properties.align.default = 'center';
53
- schema.properties.align.actions = ['left', 'right', 'center'];
56
+ schema.fieldsets[0].fields = schema.fieldsets[0].fields.filter(
57
+ (f) => f !== 'align',
58
+ );
59
+
60
+ const align = formData.styles?.['align:noprefix'];
61
+ const isFloating = align === 'left' || align === 'right';
54
62
 
55
63
  schema.properties.size.default = 'l';
56
- schema.properties.size.disabled = formData.align === 'center';
57
64
  schema.properties.styles.schema.fieldsets[0].fields = [
65
+ 'align:noprefix',
58
66
  'blockWidth:noprefix',
59
67
  ...schema.properties.styles.schema.fieldsets[0].fields,
60
68
  ];
61
69
 
70
+ schema.properties.styles.schema.properties['align:noprefix'] = {
71
+ widget: 'blockAlignment',
72
+ title: intl.formatMessage(messages.Alignment),
73
+ default: 'center',
74
+ actions: config.blocks.alignments.map((alignment) => alignment.name),
75
+ };
76
+
62
77
  schema.properties.styles.schema.properties['blockWidth:noprefix'] = {
63
78
  widget: 'blockWidth',
64
79
  title: intl.formatMessage(messages.BlockWidth),
65
80
  default: 'default',
66
- filterActions: ['narrow', 'default', 'layout', 'full'],
67
- actions: config.blocks.widths,
81
+ actions: config.blocks.widths.map((width) => width.name),
68
82
  };
69
83
 
70
84
  schema.properties.styles.schema.properties['blockWidth:noprefix'].disabled =
71
- formData.align === 'left' || formData.align === 'right';
85
+ isFloating;
86
+ schema.properties.size.disabled = !isFloating;
72
87
  }
73
-
74
88
  return schema;
75
89
  };
76
90
 
@@ -1,4 +1,5 @@
1
1
  // See Customization for more info
2
+ import { useSelector } from 'react-redux';
2
3
  import PropTypes from 'prop-types';
3
4
  import ConditionalLink from '@plone/volto/components/manage/ConditionalLink/ConditionalLink';
4
5
  import Card from '../../../primitives/Card/Card';
@@ -11,6 +12,8 @@ import cx from 'classnames';
11
12
  const DefaultTemplate = ({ items, linkTitle, linkHref, isEditMode }) => {
12
13
  let link = null;
13
14
  let href = linkHref?.[0]?.['@id'] || '';
15
+ const site = useSelector((state) => state.site?.data);
16
+ const hideProfileLinks = site?.['kitconcept.disable_profile_links'];
14
17
 
15
18
  if (isInternalURL(href)) {
16
19
  link = (
@@ -24,16 +27,19 @@ const DefaultTemplate = ({ items, linkTitle, linkHref, isEditMode }) => {
24
27
 
25
28
  return (
26
29
  <>
27
- <div className="items">
30
+ <ul className="items">
28
31
  {items.map((item) => {
29
32
  const Summary =
30
33
  config.getComponent({
31
34
  name: 'Summary',
32
35
  dependencies: [item['@type']],
33
36
  }).component || DefaultSummary;
34
- const showLink = !Summary.hideLink && !isEditMode;
37
+ let showLink = !Summary.hideLink && !isEditMode;
38
+ if (item['@type'] === 'Person' && hideProfileLinks !== undefined) {
39
+ showLink = !hideProfileLinks && !isEditMode;
40
+ }
35
41
  return (
36
- <div
42
+ <li
37
43
  className={cx('listing-item', {
38
44
  [`${item['@type']?.toLowerCase()}-listing`]: item['@type'],
39
45
  })}
@@ -41,13 +47,13 @@ const DefaultTemplate = ({ items, linkTitle, linkHref, isEditMode }) => {
41
47
  >
42
48
  <Card item={showLink ? item : null}>
43
49
  <Card.Summary>
44
- <Summary item={item} HeadingTag="h2" />
50
+ <Summary item={item} />
45
51
  </Card.Summary>
46
52
  </Card>
47
- </div>
53
+ </li>
48
54
  );
49
55
  })}
50
- </div>
56
+ </ul>
51
57
 
52
58
  {link && <div className="footer">{link}</div>}
53
59
  </>
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import { useSelector } from 'react-redux';
3
4
  import ConditionalLink from '@plone/volto/components/manage/ConditionalLink/ConditionalLink';
4
5
  import Card from '../../../primitives/Card/Card';
5
6
  import { flattenToAppURL, isInternalURL } from '@plone/volto/helpers/Url/Url';
@@ -11,6 +12,8 @@ const GridTemplate = ({ items, linkTitle, linkHref, isEditMode }) => {
11
12
  let link = null;
12
13
  let href = linkHref?.[0]?.['@id'] || '';
13
14
  const PreviewImageComponent = config.getComponent('PreviewImage').component;
15
+ const site = useSelector((state) => state.site?.data);
16
+ const hideProfileLinks = site?.['kitconcept.disable_profile_links'];
14
17
 
15
18
  if (isInternalURL(href)) {
16
19
  link = (
@@ -24,7 +27,7 @@ const GridTemplate = ({ items, linkTitle, linkHref, isEditMode }) => {
24
27
 
25
28
  return (
26
29
  <>
27
- <div className="items">
30
+ <ul className="items">
28
31
  {items.map((item) => {
29
32
  const CustomItemBodyTemplate = config.getComponent({
30
33
  name: 'GridListingItemTemplate',
@@ -35,7 +38,10 @@ const GridTemplate = ({ items, linkTitle, linkHref, isEditMode }) => {
35
38
  name: 'Summary',
36
39
  dependencies: [item['@type']],
37
40
  }).component || DefaultSummary;
38
- const showLink = !Summary.hideLink && !isEditMode;
41
+ let showLink = !Summary.hideLink && !isEditMode;
42
+ if (item['@type'] === 'Person' && hideProfileLinks !== undefined) {
43
+ showLink = !hideProfileLinks && !isEditMode;
44
+ }
39
45
 
40
46
  const ItemBodyTemplate = (props) =>
41
47
  CustomItemBodyTemplate ? (
@@ -47,16 +53,20 @@ const GridTemplate = ({ items, linkTitle, linkHref, isEditMode }) => {
47
53
  className="item-image"
48
54
  item={item}
49
55
  imageComponent={PreviewImageComponent}
56
+ sizes={`(max-width: ${config.settings.layout.tabletBreakpoint}px) 100vw, ${Math.trunc(config.settings.layout.defaultContainerWidth / 2)}px`}
50
57
  />
51
58
  )}
52
- <Card.Summary a11yLabelId={props.a11yLabelId}>
53
- <Summary item={item} HeadingTag="h2" />
59
+ <Card.Summary
60
+ a11yLabelId={props.a11yLabelId}
61
+ LinkToItem={props.LinkToItem}
62
+ >
63
+ <Summary item={item} />
54
64
  </Card.Summary>
55
65
  </>
56
66
  );
57
67
 
58
68
  return (
59
- <div
69
+ <li
60
70
  className={cx('listing-item', {
61
71
  [`${item['@type']?.toLowerCase()}-listing`]: item['@type'],
62
72
  })}
@@ -65,10 +75,10 @@ const GridTemplate = ({ items, linkTitle, linkHref, isEditMode }) => {
65
75
  <Card item={showLink ? item : null}>
66
76
  <ItemBodyTemplate item={item} />
67
77
  </Card>
68
- </div>
78
+ </li>
69
79
  );
70
80
  })}
71
- </div>
81
+ </ul>
72
82
 
73
83
  {link && <div className="footer">{link}</div>}
74
84
  </>
@@ -6,6 +6,7 @@ import Slugger from 'github-slugger';
6
6
  import { renderLinkElement } from '@plone/volto-slate/editor/render';
7
7
  import config from '@plone/volto/registry';
8
8
  import withQuerystringResults from '@plone/volto/components/manage/Blocks/Listing/withQuerystringResults';
9
+ import SlotRenderer from '@plone/volto/components/theme/SlotRenderer/SlotRenderer';
9
10
 
10
11
  const messages = defineMessages({
11
12
  PaginationNavigationFor: {
@@ -38,7 +39,7 @@ const Headline = ({
38
39
  mode={!isEditMode && 'view'}
39
40
  children={data.headline}
40
41
  attributes={attr}
41
- className={cx('headline', {
42
+ className={cx('headline block-title', {
42
43
  emptyListing: !listingItems?.length > 0,
43
44
  })}
44
45
  style={style}
@@ -63,6 +64,7 @@ export const ListingBody = (props) => {
63
64
  id,
64
65
  total,
65
66
  properties,
67
+ content,
66
68
  } = props;
67
69
 
68
70
  let ListingBodyTemplate;
@@ -99,6 +101,7 @@ export const ListingBody = (props) => {
99
101
  isEditMode={isEditMode}
100
102
  />
101
103
  )}
104
+ <SlotRenderer name="aboveListingItems" content={content} data={data} />
102
105
  {listingItems?.length > 0 ? (
103
106
  <div ref={listingRef}>
104
107
  <ListingBodyTemplate