@plone/volto 19.0.0-alpha.10 → 19.0.0-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +15 -8
- package/locales/ca/LC_MESSAGES/volto.po +10 -0
- package/locales/de/LC_MESSAGES/volto.po +10 -0
- package/locales/en/LC_MESSAGES/volto.po +10 -0
- package/locales/es/LC_MESSAGES/volto.po +10 -0
- package/locales/eu/LC_MESSAGES/volto.po +10 -0
- package/locales/fi/LC_MESSAGES/volto.po +10 -0
- package/locales/fr/LC_MESSAGES/volto.po +10 -0
- package/locales/hi/LC_MESSAGES/volto.po +10 -0
- package/locales/it/LC_MESSAGES/volto.po +10 -0
- package/locales/ja/LC_MESSAGES/volto.po +10 -0
- package/locales/nl/LC_MESSAGES/volto.po +10 -0
- package/locales/pt/LC_MESSAGES/volto.po +10 -0
- package/locales/pt_BR/LC_MESSAGES/volto.po +10 -0
- package/locales/ro/LC_MESSAGES/volto.po +10 -0
- package/locales/ru/LC_MESSAGES/volto.po +10 -0
- package/locales/ta.json +1 -1
- package/locales/volto.pot +11 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +10 -0
- package/package.json +6 -5
- package/src/components/manage/Controlpanels/Relations/Relations.jsx +1 -1
- package/src/components/manage/Widgets/AlignWidget.stories.jsx +9 -0
- package/src/components/manage/Widgets/AlignWidget.test.tsx +95 -0
- package/src/components/manage/Widgets/{AlignWidget.jsx → AlignWidget.tsx} +23 -7
- package/src/components/manage/Widgets/BlockAlignment.stories.tsx +104 -0
- package/src/components/manage/Widgets/BlockAlignment.test.tsx +104 -0
- package/src/components/manage/Widgets/BlockAlignment.tsx +88 -0
- package/src/components/manage/Widgets/BlockWidth.stories.tsx +69 -0
- package/src/components/manage/Widgets/BlockWidth.test.tsx +62 -0
- package/src/components/manage/Widgets/BlockWidth.tsx +101 -0
- package/src/components/manage/Widgets/ButtonsWidget.stories.jsx +61 -0
- package/src/components/manage/Widgets/ButtonsWidget.test.tsx +138 -0
- package/src/components/manage/Widgets/ButtonsWidget.tsx +176 -0
- package/src/components/manage/Widgets/Size.stories.tsx +69 -0
- package/src/components/manage/Widgets/Size.test.tsx +59 -0
- package/src/components/manage/Widgets/Size.tsx +78 -0
- package/src/components/manage/Widgets/index.tsx +21 -0
- package/src/components/theme/App/App.jsx +2 -0
- package/src/components/theme/InjectPloneComponentsCSS/InjectPloneComponentsCSS.tsx +7 -0
- package/src/config/Widgets.jsx +7 -0
- package/src/config/slots.js +19 -0
- package/theme/themes/pastanaga/extras/widgets.less +45 -0
- package/types/components/manage/Widgets/AlignWidget.d.ts +8 -10
- package/types/components/manage/Widgets/AlignWidget.stories.d.ts +1 -0
- package/types/components/manage/Widgets/BlockAlignment.d.ts +7 -0
- package/types/components/manage/Widgets/BlockAlignment.stories.d.ts +8 -0
- package/types/components/manage/Widgets/BlockWidth.d.ts +7 -0
- package/types/components/manage/Widgets/BlockWidth.stories.d.ts +6 -0
- package/types/components/manage/Widgets/ButtonsWidget.d.ts +48 -1
- package/types/components/manage/Widgets/ButtonsWidget.stories.d.ts +3 -0
- package/types/components/manage/Widgets/Size.d.ts +7 -0
- package/types/components/manage/Widgets/Size.stories.d.ts +6 -0
- package/types/components/manage/Widgets/index.d.ts +7 -2
- package/types/components/theme/InjectPloneComponentsCSS/InjectPloneComponentsCSS.d.ts +3 -0
- package/types/config/Widgets.d.ts +6 -0
- package/types/config/slots.d.ts +7 -0
- package/src/components/manage/Widgets/AlignWidget.test.jsx +0 -59
- package/src/components/manage/Widgets/ButtonsWidget.jsx +0 -41
- package/src/components/manage/Widgets/ButtonsWidget.test.jsx +0 -70
package/locales/volto.pot
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
msgid ""
|
|
2
2
|
msgstr ""
|
|
3
3
|
"Project-Id-Version: Plone\n"
|
|
4
|
-
"POT-Creation-Date: 2025-10-
|
|
4
|
+
"POT-Creation-Date: 2025-10-28T09:22:24.223Z\n"
|
|
5
5
|
"Last-Translator: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
|
|
6
6
|
"Language-Team: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
|
|
7
7
|
"Content-Type: text/plain; charset=utf-8\n"
|
|
@@ -601,6 +601,7 @@ msgstr ""
|
|
|
601
601
|
#: components/manage/Blocks/Maps/Edit
|
|
602
602
|
#: components/manage/Sidebar/AlignBlock
|
|
603
603
|
#: components/manage/Widgets/AlignWidget
|
|
604
|
+
#: components/manage/Widgets/BlockAlignment
|
|
604
605
|
msgid "Center"
|
|
605
606
|
msgstr ""
|
|
606
607
|
|
|
@@ -1010,6 +1011,7 @@ msgstr ""
|
|
|
1010
1011
|
#: components/manage/Controlpanels/UndoControlpanel
|
|
1011
1012
|
#: components/manage/Preferences/ChangePassword
|
|
1012
1013
|
#: components/manage/Preferences/PersonalPreferences
|
|
1014
|
+
#: components/manage/Widgets/BlockWidth
|
|
1013
1015
|
#: components/manage/Widgets/SchemaWidget
|
|
1014
1016
|
#: components/manage/Widgets/SelectWidget
|
|
1015
1017
|
#: components/theme/Comments/CommentEditModal
|
|
@@ -1663,6 +1665,7 @@ msgstr ""
|
|
|
1663
1665
|
#: components/manage/Blocks/Maps/Edit
|
|
1664
1666
|
#: components/manage/Sidebar/AlignBlock
|
|
1665
1667
|
#: components/manage/Widgets/AlignWidget
|
|
1668
|
+
#: components/manage/Widgets/BlockWidth
|
|
1666
1669
|
msgid "Full"
|
|
1667
1670
|
msgstr ""
|
|
1668
1671
|
|
|
@@ -2048,6 +2051,7 @@ msgstr ""
|
|
|
2048
2051
|
|
|
2049
2052
|
#. Default: "Large"
|
|
2050
2053
|
#: components/manage/Widgets/ImageSizeWidget
|
|
2054
|
+
#: components/manage/Widgets/Size
|
|
2051
2055
|
msgid "Large"
|
|
2052
2056
|
msgstr ""
|
|
2053
2057
|
|
|
@@ -2078,6 +2082,7 @@ msgstr ""
|
|
|
2078
2082
|
|
|
2079
2083
|
#. Default: "Layout"
|
|
2080
2084
|
#: components/manage/Controlpanels/ContentTypesActions
|
|
2085
|
+
#: components/manage/Widgets/BlockWidth
|
|
2081
2086
|
msgid "Layout"
|
|
2082
2087
|
msgstr ""
|
|
2083
2088
|
|
|
@@ -2090,6 +2095,7 @@ msgstr ""
|
|
|
2090
2095
|
#: components/manage/Blocks/Maps/Edit
|
|
2091
2096
|
#: components/manage/Sidebar/AlignBlock
|
|
2092
2097
|
#: components/manage/Widgets/AlignWidget
|
|
2098
|
+
#: components/manage/Widgets/BlockAlignment
|
|
2093
2099
|
msgid "Left"
|
|
2094
2100
|
msgstr ""
|
|
2095
2101
|
|
|
@@ -2288,6 +2294,7 @@ msgstr ""
|
|
|
2288
2294
|
|
|
2289
2295
|
#. Default: "Medium"
|
|
2290
2296
|
#: components/manage/Widgets/ImageSizeWidget
|
|
2297
|
+
#: components/manage/Widgets/Size
|
|
2291
2298
|
msgid "Medium"
|
|
2292
2299
|
msgstr ""
|
|
2293
2300
|
|
|
@@ -2400,6 +2407,7 @@ msgstr ""
|
|
|
2400
2407
|
|
|
2401
2408
|
#. Default: "Narrow"
|
|
2402
2409
|
#: components/manage/Widgets/AlignWidget
|
|
2410
|
+
#: components/manage/Widgets/BlockWidth
|
|
2403
2411
|
msgid "Narrow"
|
|
2404
2412
|
msgstr ""
|
|
2405
2413
|
|
|
@@ -3116,6 +3124,7 @@ msgstr ""
|
|
|
3116
3124
|
#: components/manage/Blocks/Maps/Edit
|
|
3117
3125
|
#: components/manage/Sidebar/AlignBlock
|
|
3118
3126
|
#: components/manage/Widgets/AlignWidget
|
|
3127
|
+
#: components/manage/Widgets/BlockAlignment
|
|
3119
3128
|
msgid "Right"
|
|
3120
3129
|
msgstr ""
|
|
3121
3130
|
|
|
@@ -3512,6 +3521,7 @@ msgstr ""
|
|
|
3512
3521
|
|
|
3513
3522
|
#. Default: "Small"
|
|
3514
3523
|
#: components/manage/Widgets/ImageSizeWidget
|
|
3524
|
+
#: components/manage/Widgets/Size
|
|
3515
3525
|
msgid "Small"
|
|
3516
3526
|
msgstr ""
|
|
3517
3527
|
|
|
@@ -605,6 +605,7 @@ msgstr "单元格"
|
|
|
605
605
|
#: components/manage/Blocks/Maps/Edit
|
|
606
606
|
#: components/manage/Sidebar/AlignBlock
|
|
607
607
|
#: components/manage/Widgets/AlignWidget
|
|
608
|
+
#: components/manage/Widgets/BlockAlignment
|
|
608
609
|
msgid "Center"
|
|
609
610
|
msgstr "中间"
|
|
610
611
|
|
|
@@ -1014,6 +1015,7 @@ msgstr "日期(最新在前)"
|
|
|
1014
1015
|
#: components/manage/Controlpanels/UndoControlpanel
|
|
1015
1016
|
#: components/manage/Preferences/ChangePassword
|
|
1016
1017
|
#: components/manage/Preferences/PersonalPreferences
|
|
1018
|
+
#: components/manage/Widgets/BlockWidth
|
|
1017
1019
|
#: components/manage/Widgets/SchemaWidget
|
|
1018
1020
|
#: components/manage/Widgets/SelectWidget
|
|
1019
1021
|
#: components/theme/Comments/CommentEditModal
|
|
@@ -1667,6 +1669,7 @@ msgstr ""
|
|
|
1667
1669
|
#: components/manage/Blocks/Maps/Edit
|
|
1668
1670
|
#: components/manage/Sidebar/AlignBlock
|
|
1669
1671
|
#: components/manage/Widgets/AlignWidget
|
|
1672
|
+
#: components/manage/Widgets/BlockWidth
|
|
1670
1673
|
msgid "Full"
|
|
1671
1674
|
msgstr ""
|
|
1672
1675
|
|
|
@@ -2052,6 +2055,7 @@ msgstr ""
|
|
|
2052
2055
|
|
|
2053
2056
|
#. Default: "Large"
|
|
2054
2057
|
#: components/manage/Widgets/ImageSizeWidget
|
|
2058
|
+
#: components/manage/Widgets/Size
|
|
2055
2059
|
msgid "Large"
|
|
2056
2060
|
msgstr "大"
|
|
2057
2061
|
|
|
@@ -2082,6 +2086,7 @@ msgstr "最新版本"
|
|
|
2082
2086
|
|
|
2083
2087
|
#. Default: "Layout"
|
|
2084
2088
|
#: components/manage/Controlpanels/ContentTypesActions
|
|
2089
|
+
#: components/manage/Widgets/BlockWidth
|
|
2085
2090
|
msgid "Layout"
|
|
2086
2091
|
msgstr "布局"
|
|
2087
2092
|
|
|
@@ -2094,6 +2099,7 @@ msgstr "首图"
|
|
|
2094
2099
|
#: components/manage/Blocks/Maps/Edit
|
|
2095
2100
|
#: components/manage/Sidebar/AlignBlock
|
|
2096
2101
|
#: components/manage/Widgets/AlignWidget
|
|
2102
|
+
#: components/manage/Widgets/BlockAlignment
|
|
2097
2103
|
msgid "Left"
|
|
2098
2104
|
msgstr ""
|
|
2099
2105
|
|
|
@@ -2292,6 +2298,7 @@ msgstr "最大值为{len}。"
|
|
|
2292
2298
|
|
|
2293
2299
|
#. Default: "Medium"
|
|
2294
2300
|
#: components/manage/Widgets/ImageSizeWidget
|
|
2301
|
+
#: components/manage/Widgets/Size
|
|
2295
2302
|
msgid "Medium"
|
|
2296
2303
|
msgstr "中等"
|
|
2297
2304
|
|
|
@@ -2404,6 +2411,7 @@ msgstr "名字"
|
|
|
2404
2411
|
|
|
2405
2412
|
#. Default: "Narrow"
|
|
2406
2413
|
#: components/manage/Widgets/AlignWidget
|
|
2414
|
+
#: components/manage/Widgets/BlockWidth
|
|
2407
2415
|
msgid "Narrow"
|
|
2408
2416
|
msgstr ""
|
|
2409
2417
|
|
|
@@ -3120,6 +3128,7 @@ msgstr "富文本"
|
|
|
3120
3128
|
#: components/manage/Blocks/Maps/Edit
|
|
3121
3129
|
#: components/manage/Sidebar/AlignBlock
|
|
3122
3130
|
#: components/manage/Widgets/AlignWidget
|
|
3131
|
+
#: components/manage/Widgets/BlockAlignment
|
|
3123
3132
|
msgid "Right"
|
|
3124
3133
|
msgstr ""
|
|
3125
3134
|
|
|
@@ -3516,6 +3525,7 @@ msgstr "大小:{size}"
|
|
|
3516
3525
|
|
|
3517
3526
|
#. Default: "Small"
|
|
3518
3527
|
#: components/manage/Widgets/ImageSizeWidget
|
|
3528
|
+
#: components/manage/Widgets/Size
|
|
3519
3529
|
msgid "Small"
|
|
3520
3530
|
msgstr "小"
|
|
3521
3531
|
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
}
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"version": "19.0.0-alpha.
|
|
12
|
+
"version": "19.0.0-alpha.12",
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
15
|
"url": "git@github.com:plone/volto.git"
|
|
@@ -240,9 +240,10 @@
|
|
|
240
240
|
"url": "^0.11.3",
|
|
241
241
|
"use-deep-compare-effect": "1.8.1",
|
|
242
242
|
"uuid": "^8.3.2",
|
|
243
|
-
"@plone/
|
|
244
|
-
"@plone/registry": "3.0.0-alpha.
|
|
245
|
-
"@plone/volto-slate": "19.0.0-alpha.
|
|
243
|
+
"@plone/components": "4.0.0-alpha.1",
|
|
244
|
+
"@plone/registry": "3.0.0-alpha.8",
|
|
245
|
+
"@plone/volto-slate": "19.0.0-alpha.7",
|
|
246
|
+
"@plone/scripts": "4.0.0-alpha.3"
|
|
246
247
|
},
|
|
247
248
|
"devDependencies": {
|
|
248
249
|
"@babel/core": "^7.0.0",
|
|
@@ -366,7 +367,7 @@
|
|
|
366
367
|
"webpack-bundle-analyzer": "4.10.1",
|
|
367
368
|
"webpack-dev-server": "4.11.1",
|
|
368
369
|
"webpack-node-externals": "3.0.0",
|
|
369
|
-
"@plone/types": "2.0.0-alpha.
|
|
370
|
+
"@plone/types": "2.0.0-alpha.10",
|
|
370
371
|
"@plone/volto-coresandbox": "1.0.0"
|
|
371
372
|
},
|
|
372
373
|
"volta": {
|
|
@@ -7,6 +7,15 @@ export const Align = WidgetStory.bind({
|
|
|
7
7
|
widget: AlignWidget,
|
|
8
8
|
});
|
|
9
9
|
|
|
10
|
+
export const AlignDefaultLeft = WidgetStory.bind({
|
|
11
|
+
props: {
|
|
12
|
+
id: 'align-default-left',
|
|
13
|
+
title: 'Align (default left)',
|
|
14
|
+
default: 'left',
|
|
15
|
+
},
|
|
16
|
+
widget: AlignWidget,
|
|
17
|
+
});
|
|
18
|
+
|
|
10
19
|
export default {
|
|
11
20
|
title: 'Edit Widgets/Align',
|
|
12
21
|
component: AlignWidget,
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
import configureStore from 'redux-mock-store';
|
|
5
|
+
import { Provider } from 'react-intl-redux';
|
|
6
|
+
import imageFullSVG from '@plone/volto/icons/image-full.svg';
|
|
7
|
+
|
|
8
|
+
import AlignWidget from './AlignWidget';
|
|
9
|
+
import type { ButtonsWidgetProps } from './ButtonsWidget';
|
|
10
|
+
|
|
11
|
+
const mockStore = configureStore();
|
|
12
|
+
|
|
13
|
+
const renderAlignWidget = (
|
|
14
|
+
props: Partial<ButtonsWidgetProps> = {},
|
|
15
|
+
): {
|
|
16
|
+
onChange: ButtonsWidgetProps['onChange'];
|
|
17
|
+
asFragment: () => DocumentFragment;
|
|
18
|
+
getByRole: ReturnType<typeof render>['getByRole'];
|
|
19
|
+
} => {
|
|
20
|
+
const { onChange: providedOnChange, ...restProps } = props;
|
|
21
|
+
const onChange = providedOnChange ?? vi.fn();
|
|
22
|
+
const store = mockStore({
|
|
23
|
+
intl: {
|
|
24
|
+
locale: 'en',
|
|
25
|
+
messages: {},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const widgetProps: ButtonsWidgetProps = {
|
|
30
|
+
id: 'align',
|
|
31
|
+
title: 'Alignment',
|
|
32
|
+
fieldSet: 'default',
|
|
33
|
+
onChange,
|
|
34
|
+
value: undefined,
|
|
35
|
+
default: undefined,
|
|
36
|
+
disabled: false,
|
|
37
|
+
isDisabled: false,
|
|
38
|
+
...restProps,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const rendered = render(
|
|
42
|
+
<Provider store={store}>
|
|
43
|
+
<AlignWidget {...widgetProps} />
|
|
44
|
+
</Provider>,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
onChange,
|
|
49
|
+
asFragment: rendered.asFragment,
|
|
50
|
+
getByRole: rendered.getByRole,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
describe('AlignWidget', () => {
|
|
55
|
+
it('renders with default actions', () => {
|
|
56
|
+
const { asFragment } = renderAlignWidget();
|
|
57
|
+
expect(asFragment()).toMatchSnapshot();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('renders with custom actions', () => {
|
|
61
|
+
const { asFragment } = renderAlignWidget({
|
|
62
|
+
actions: ['additional'],
|
|
63
|
+
actionsInfoMap: {
|
|
64
|
+
additional: [imageFullSVG, 'Additional action title'],
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(asFragment()).toMatchSnapshot();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('selects the defaultAction when value is missing', () => {
|
|
72
|
+
const { getByRole } = renderAlignWidget({
|
|
73
|
+
defaultAction: 'left',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(getByRole('radio', { name: 'Left' })).toBeChecked();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('selects the default when value is missing', () => {
|
|
80
|
+
const { getByRole } = renderAlignWidget({
|
|
81
|
+
default: 'left',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
expect(getByRole('radio', { name: 'Left' })).toBeChecked();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('does not override provided value with default', () => {
|
|
88
|
+
const { onChange } = renderAlignWidget({
|
|
89
|
+
default: 'left',
|
|
90
|
+
value: 'center',
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
expect(onChange).not.toHaveBeenCalledWith('align', 'left');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { defineMessages, useIntl } from 'react-intl';
|
|
3
|
-
import ButtonsWidget
|
|
3
|
+
import ButtonsWidget, {
|
|
4
|
+
type ActionInfo,
|
|
5
|
+
type ButtonsWidgetProps,
|
|
6
|
+
} from './ButtonsWidget';
|
|
4
7
|
import imageLeftSVG from '@plone/volto/icons/image-left.svg';
|
|
5
8
|
import imageRightSVG from '@plone/volto/icons/image-right.svg';
|
|
6
9
|
import imageFitSVG from '@plone/volto/icons/image-fit.svg';
|
|
7
10
|
import imageNarrowSVG from '@plone/volto/icons/image-narrow.svg';
|
|
8
11
|
import imageWideSVG from '@plone/volto/icons/image-wide.svg';
|
|
9
12
|
import imageFullSVG from '@plone/volto/icons/image-full.svg';
|
|
13
|
+
import type { IntlShape } from 'react-intl';
|
|
10
14
|
|
|
11
15
|
const messages = defineMessages({
|
|
12
16
|
left: {
|
|
@@ -35,20 +39,32 @@ const messages = defineMessages({
|
|
|
35
39
|
},
|
|
36
40
|
});
|
|
37
41
|
|
|
38
|
-
export const defaultActionsInfo = ({
|
|
42
|
+
export const defaultActionsInfo = ({
|
|
43
|
+
intl,
|
|
44
|
+
}: {
|
|
45
|
+
intl: IntlShape;
|
|
46
|
+
}): Record<string, ActionInfo> => ({
|
|
39
47
|
left: [imageLeftSVG, intl.formatMessage(messages.left)],
|
|
40
|
-
right: [imageRightSVG, intl.formatMessage(messages.right)],
|
|
41
48
|
center: [imageFitSVG, intl.formatMessage(messages.center)],
|
|
49
|
+
right: [imageRightSVG, intl.formatMessage(messages.right)],
|
|
42
50
|
narrow: [imageNarrowSVG, intl.formatMessage(messages.narrow)],
|
|
43
51
|
wide: [imageWideSVG, intl.formatMessage(messages.wide)],
|
|
44
52
|
full: [imageFullSVG, intl.formatMessage(messages.full)],
|
|
45
53
|
});
|
|
46
54
|
|
|
47
|
-
|
|
55
|
+
type AlignWidgetProps = ButtonsWidgetProps & {
|
|
56
|
+
defaultAction?: string;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const AlignWidget = (props: AlignWidgetProps) => {
|
|
48
60
|
const intl = useIntl();
|
|
49
61
|
|
|
50
|
-
const {
|
|
51
|
-
|
|
62
|
+
const {
|
|
63
|
+
actions = ['left', 'center', 'right', 'full'],
|
|
64
|
+
actionsInfoMap,
|
|
65
|
+
default: defaultValue,
|
|
66
|
+
defaultAction,
|
|
67
|
+
} = props;
|
|
52
68
|
|
|
53
69
|
const actionsInfo = {
|
|
54
70
|
...defaultActionsInfo({ intl }),
|
|
@@ -60,7 +76,7 @@ const AlignWidget = (props) => {
|
|
|
60
76
|
{...props}
|
|
61
77
|
actions={actions}
|
|
62
78
|
actionsInfoMap={actionsInfo}
|
|
63
|
-
|
|
79
|
+
default={defaultValue ?? defaultAction ?? 'center'}
|
|
64
80
|
/>
|
|
65
81
|
);
|
|
66
82
|
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Meta, StoryFn, StoryObj } from '@storybook/react';
|
|
3
|
+
|
|
4
|
+
import BlockAlignment from './BlockAlignment';
|
|
5
|
+
import type { ButtonsWidgetProps } from './ButtonsWidget';
|
|
6
|
+
import {
|
|
7
|
+
RealStoreWrapper as Wrapper,
|
|
8
|
+
FormUndoWrapper,
|
|
9
|
+
} from '@plone/volto/storybook';
|
|
10
|
+
|
|
11
|
+
const meta: Meta<typeof BlockAlignment> = {
|
|
12
|
+
title: 'Edit Widgets/BlockAlignment',
|
|
13
|
+
component: BlockAlignment,
|
|
14
|
+
decorators: [
|
|
15
|
+
(Story) => (
|
|
16
|
+
<div className="ui segment form attached" style={{ width: '400px' }}>
|
|
17
|
+
<h4>Block alignment widget</h4>
|
|
18
|
+
<Story />
|
|
19
|
+
</div>
|
|
20
|
+
),
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default meta;
|
|
25
|
+
|
|
26
|
+
type Story = StoryObj<typeof BlockAlignment>;
|
|
27
|
+
|
|
28
|
+
type TemplateParameters = {
|
|
29
|
+
initialValue?: ButtonsWidgetProps['value'];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const Template: StoryFn<typeof BlockAlignment> = (args, context) => {
|
|
33
|
+
const { initialValue } =
|
|
34
|
+
(context.parameters as TemplateParameters | undefined) ?? {};
|
|
35
|
+
const { value: _ignoredValue, onChange: argsOnChange, ...restArgs } = args;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Wrapper location={{ pathname: '/folder2/folder21/doc212' }}>
|
|
39
|
+
<FormUndoWrapper
|
|
40
|
+
initialState={{ value: initialValue ?? _ignoredValue }}
|
|
41
|
+
showControls
|
|
42
|
+
>
|
|
43
|
+
{({ state, onChange }) => (
|
|
44
|
+
<div className="ui segment form attached" style={{ width: '400px' }}>
|
|
45
|
+
<BlockAlignment
|
|
46
|
+
{...(restArgs as ButtonsWidgetProps)}
|
|
47
|
+
value={state.value}
|
|
48
|
+
onChange={(id, nextValue) => {
|
|
49
|
+
argsOnChange?.(id, nextValue);
|
|
50
|
+
onChange({ value: nextValue });
|
|
51
|
+
}}
|
|
52
|
+
/>
|
|
53
|
+
<pre>Value: {JSON.stringify(state.value, null, 2)}</pre>
|
|
54
|
+
</div>
|
|
55
|
+
)}
|
|
56
|
+
</FormUndoWrapper>
|
|
57
|
+
</Wrapper>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const DefaultAlignment: Story = {
|
|
62
|
+
render: Template,
|
|
63
|
+
args: {
|
|
64
|
+
id: 'alignment',
|
|
65
|
+
title: 'Block alignment',
|
|
66
|
+
block: 'block',
|
|
67
|
+
fieldSet: 'default',
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const CustomActions: Story = {
|
|
72
|
+
render: Template,
|
|
73
|
+
args: {
|
|
74
|
+
...(DefaultAlignment.args ?? {}),
|
|
75
|
+
actions: [
|
|
76
|
+
{
|
|
77
|
+
name: 'wide',
|
|
78
|
+
label: 'Wide',
|
|
79
|
+
style: {
|
|
80
|
+
'--block-alignment': 'var(--align-wide)',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'full',
|
|
85
|
+
label: 'Full',
|
|
86
|
+
style: {
|
|
87
|
+
'--block-alignment': 'var(--align-full)',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
actionsInfoMap: {
|
|
92
|
+
wide: ['W', 'Wide alignment'],
|
|
93
|
+
full: ['F', 'Full width alignment'],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const FilteredActions: Story = {
|
|
99
|
+
render: Template,
|
|
100
|
+
args: {
|
|
101
|
+
...(DefaultAlignment.args ?? {}),
|
|
102
|
+
filterActions: ['center'],
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
4
|
+
import configureStore from 'redux-mock-store';
|
|
5
|
+
import { Provider } from 'react-intl-redux';
|
|
6
|
+
|
|
7
|
+
import BlockAlignment from './BlockAlignment';
|
|
8
|
+
import type { ButtonsWidgetProps } from './ButtonsWidget';
|
|
9
|
+
|
|
10
|
+
const mockStore = configureStore();
|
|
11
|
+
|
|
12
|
+
const renderWidget = (props: Partial<ButtonsWidgetProps> = {}) => {
|
|
13
|
+
const { onChange: providedOnChange, ...restProps } = props;
|
|
14
|
+
const onChange = providedOnChange ?? vi.fn();
|
|
15
|
+
const widgetProps: ButtonsWidgetProps = {
|
|
16
|
+
id: 'alignment',
|
|
17
|
+
title: 'Block alignment',
|
|
18
|
+
fieldSet: 'default',
|
|
19
|
+
onChange,
|
|
20
|
+
value: undefined,
|
|
21
|
+
default: undefined,
|
|
22
|
+
disabled: false,
|
|
23
|
+
isDisabled: false,
|
|
24
|
+
...restProps,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
onChange,
|
|
29
|
+
...render(
|
|
30
|
+
<Provider
|
|
31
|
+
store={mockStore({
|
|
32
|
+
intl: {
|
|
33
|
+
locale: 'en',
|
|
34
|
+
messages: {},
|
|
35
|
+
},
|
|
36
|
+
})}
|
|
37
|
+
>
|
|
38
|
+
<BlockAlignment {...widgetProps} />
|
|
39
|
+
</Provider>,
|
|
40
|
+
),
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
describe('BlockAlignment', () => {
|
|
45
|
+
it('renders default alignment buttons', () => {
|
|
46
|
+
renderWidget();
|
|
47
|
+
|
|
48
|
+
expect(screen.getByRole('radio', { name: 'Left' })).toBeInTheDocument();
|
|
49
|
+
expect(screen.getByRole('radio', { name: 'Center' })).toBeInTheDocument();
|
|
50
|
+
expect(screen.getByRole('radio', { name: 'Right' })).toBeInTheDocument();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('filters actions when filterActions is provided', () => {
|
|
54
|
+
renderWidget({ filterActions: ['center'] });
|
|
55
|
+
|
|
56
|
+
expect(screen.getByRole('radio', { name: 'Center' })).toBeInTheDocument();
|
|
57
|
+
expect(
|
|
58
|
+
screen.queryByRole('button', { name: 'Left' }),
|
|
59
|
+
).not.toBeInTheDocument();
|
|
60
|
+
expect(
|
|
61
|
+
screen.queryByRole('button', { name: 'Right' }),
|
|
62
|
+
).not.toBeInTheDocument();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('merges custom actions info map entries', () => {
|
|
66
|
+
renderWidget({
|
|
67
|
+
actions: ['left', 'custom'],
|
|
68
|
+
actionsInfoMap: {
|
|
69
|
+
custom: ['Custom', 'Custom alignment'],
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(screen.getByRole('radio', { name: 'Left' })).toBeInTheDocument();
|
|
74
|
+
expect(
|
|
75
|
+
screen.getByRole('radio', { name: 'Custom alignment' }),
|
|
76
|
+
).toBeInTheDocument();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('renders when style definitions are provided as actions', () => {
|
|
80
|
+
renderWidget({
|
|
81
|
+
actions: [
|
|
82
|
+
{
|
|
83
|
+
name: 'wide',
|
|
84
|
+
label: 'Wide',
|
|
85
|
+
style: {
|
|
86
|
+
'--block-alignment': 'var(--align-wide)',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(screen.getByRole('radio', { name: 'wide' })).toBeInTheDocument();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('invokes onChange with styles when default actions are used', () => {
|
|
96
|
+
const { onChange } = renderWidget();
|
|
97
|
+
|
|
98
|
+
fireEvent.click(screen.getByRole('radio', { name: 'Center' }));
|
|
99
|
+
|
|
100
|
+
expect(onChange).toHaveBeenCalledWith('alignment', {
|
|
101
|
+
'--block-alignment': 'var(--align-center)',
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
3
|
+
import ButtonsWidget, {
|
|
4
|
+
type ActionInfo,
|
|
5
|
+
type ButtonsWidgetProps,
|
|
6
|
+
} from './ButtonsWidget';
|
|
7
|
+
import imageFitSVG from '@plone/volto/icons/image-fit.svg';
|
|
8
|
+
import imageLeftSVG from '@plone/volto/icons/image-left.svg';
|
|
9
|
+
import imageRightSVG from '@plone/volto/icons/image-right.svg';
|
|
10
|
+
import type { IntlShape } from 'react-intl';
|
|
11
|
+
import type { StyleDefinition } from '@plone/types';
|
|
12
|
+
|
|
13
|
+
const messages = defineMessages({
|
|
14
|
+
left: {
|
|
15
|
+
id: 'Left',
|
|
16
|
+
defaultMessage: 'Left',
|
|
17
|
+
},
|
|
18
|
+
right: {
|
|
19
|
+
id: 'Right',
|
|
20
|
+
defaultMessage: 'Right',
|
|
21
|
+
},
|
|
22
|
+
center: {
|
|
23
|
+
id: 'Center',
|
|
24
|
+
defaultMessage: 'Center',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const defaultActionsInfo = ({
|
|
29
|
+
intl,
|
|
30
|
+
}: {
|
|
31
|
+
intl: IntlShape;
|
|
32
|
+
}): Record<string, ActionInfo> => ({
|
|
33
|
+
left: [imageLeftSVG, intl.formatMessage(messages.left)],
|
|
34
|
+
right: [imageRightSVG, intl.formatMessage(messages.right)],
|
|
35
|
+
center: [imageFitSVG, intl.formatMessage(messages.center)],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const DEFAULT_ACTIONS: StyleDefinition[] = [
|
|
39
|
+
{
|
|
40
|
+
style: {
|
|
41
|
+
'--block-alignment': 'var(--align-left)',
|
|
42
|
+
},
|
|
43
|
+
name: 'left',
|
|
44
|
+
label: 'Left',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
style: {
|
|
48
|
+
'--block-alignment': 'var(--align-center)',
|
|
49
|
+
},
|
|
50
|
+
name: 'center',
|
|
51
|
+
label: 'Center',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
style: {
|
|
55
|
+
'--block-alignment': 'var(--align-right)',
|
|
56
|
+
},
|
|
57
|
+
name: 'right',
|
|
58
|
+
label: 'Right',
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const BlockAlignmentWidget = (props: ButtonsWidgetProps) => {
|
|
63
|
+
const intl = useIntl();
|
|
64
|
+
|
|
65
|
+
const { actions = DEFAULT_ACTIONS, actionsInfoMap, filterActions } = props;
|
|
66
|
+
const filteredActions =
|
|
67
|
+
filterActions && filterActions.length > 0
|
|
68
|
+
? actions.filter((action) => {
|
|
69
|
+
const actionName = typeof action === 'string' ? action : action.name;
|
|
70
|
+
return filterActions.includes(actionName);
|
|
71
|
+
})
|
|
72
|
+
: actions;
|
|
73
|
+
|
|
74
|
+
const actionsInfo = {
|
|
75
|
+
...defaultActionsInfo({ intl }),
|
|
76
|
+
...actionsInfoMap,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<ButtonsWidget
|
|
81
|
+
{...props}
|
|
82
|
+
actions={filteredActions}
|
|
83
|
+
actionsInfoMap={actionsInfo}
|
|
84
|
+
/>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export default BlockAlignmentWidget;
|