@plone/volto 18.9.1 → 18.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +0 -2
- package/locales/ca/LC_MESSAGES/volto.po +5 -0
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +5 -0
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +5 -0
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +5 -0
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +5 -0
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +5 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +5 -0
- package/locales/fr.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +5 -0
- package/locales/hi.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +5 -0
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +5 -0
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +5 -0
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +5 -0
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +5 -0
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +5 -0
- package/locales/ro.json +1 -1
- package/locales/volto.pot +6 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +5 -0
- package/locales/zh_CN.json +1 -1
- package/package.json +2 -2
- package/src/components/manage/Add/Add.jsx +5 -1
- package/src/components/manage/AnchorPlugin/components/LinkButton/AddLinkForm.jsx +2 -0
- package/src/components/manage/BlockChooser/BlockChooser.jsx +1 -0
- package/src/components/manage/BlockChooser/BlockChooserButton.jsx +1 -0
- package/src/components/manage/BlockChooser/BlockChooserSearch.jsx +1 -0
- package/src/components/manage/Blocks/Block/EditBlockWrapper.jsx +1 -0
- package/src/components/manage/Blocks/Block/StyleWrapper.jsx +8 -2
- package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +11 -1
- package/src/components/manage/Blocks/Container/NewBlockAddButton.jsx +1 -0
- package/src/components/manage/Blocks/Container/SimpleContainerToolbar.jsx +2 -0
- package/src/components/manage/Blocks/HTML/Edit.jsx +9 -1
- package/src/components/manage/Blocks/Image/ImageSidebar.jsx +1 -0
- package/src/components/manage/Blocks/Listing/ImageGallery.jsx +2 -0
- package/src/components/manage/Blocks/Maps/Edit.jsx +2 -0
- package/src/components/manage/Blocks/Search/components/Facets.jsx +1 -0
- package/src/components/manage/Blocks/Search/components/FilterList.jsx +1 -0
- package/src/components/manage/Blocks/Search/components/SearchInput.jsx +1 -0
- package/src/components/manage/Blocks/Search/components/SortOn.jsx +2 -0
- package/src/components/manage/Blocks/Teaser/Data.jsx +2 -0
- package/src/components/manage/Blocks/Video/Edit.jsx +2 -0
- package/src/components/manage/Delete/Delete.jsx +1 -0
- package/src/components/manage/Diff/Diff.jsx +1 -0
- package/src/components/manage/Edit/Edit.jsx +1 -0
- package/src/components/manage/Form/Form.jsx +1 -0
- package/src/components/manage/Form/ModalForm.jsx +1 -0
- package/src/components/manage/Form/UndoToolbar.jsx +2 -0
- package/src/components/manage/Sidebar/AlignBlock.jsx +1 -0
- package/src/components/manage/Sidebar/ObjectBrowserNav.jsx +1 -0
- package/src/components/manage/Sidebar/Sidebar.jsx +2 -0
- package/src/components/manage/TemplateChooser/TemplateChooser.jsx +1 -0
- package/src/components/manage/Widgets/CheckboxWidget.jsx +1 -0
- package/src/components/manage/Widgets/SelectWidget.jsx +1 -0
- package/src/components/theme/AlternateHrefLangs/AlternateHrefLangs.jsx +2 -1
- package/src/components/theme/AlternateHrefLangs/AlternateHrefLangs.test.jsx +67 -10
- package/src/components/theme/Image/Image.jsx +1 -2
- package/src/components/theme/Image/Image.test.jsx +8 -0
- package/src/components/theme/View/RenderBlocks.jsx +2 -1
- package/src/config/index.js +11 -1
- package/src/config/validation.ts +8 -0
- package/src/helpers/Blocks/Blocks.js +6 -2
- package/src/helpers/Blocks/Blocks.test.js +1 -1
- package/src/helpers/FormValidation/FormValidation.jsx +1 -0
- package/src/helpers/FormValidation/FormValidation.test.js +72 -4
- package/src/helpers/FormValidation/validators.ts +14 -0
- package/src/helpers/Html/Html.jsx +3 -0
- package/src/helpers/MessageLabels/MessageLabels.js +5 -0
- package/src/start-client.jsx +4 -0
- package/src/storybook.jsx +2 -0
- package/types/helpers/Blocks/Blocks.d.ts +1 -1
- package/types/helpers/FormValidation/validators.d.ts +1 -0
- package/types/helpers/MessageLabels/MessageLabels.d.ts +6 -0
|
@@ -39,6 +39,7 @@ const UndoToolbar = ({ state, onUndoRedo, maxUndoLevels, enableHotKeys }) => {
|
|
|
39
39
|
dependencies={[canUndo, canRedo]}
|
|
40
40
|
>
|
|
41
41
|
<Button
|
|
42
|
+
type="button"
|
|
42
43
|
className="undo"
|
|
43
44
|
onClick={() => doUndo()}
|
|
44
45
|
aria-label={intl.formatMessage(messages.undo)}
|
|
@@ -58,6 +59,7 @@ const UndoToolbar = ({ state, onUndoRedo, maxUndoLevels, enableHotKeys }) => {
|
|
|
58
59
|
dependencies={[canUndo, canRedo]}
|
|
59
60
|
>
|
|
60
61
|
<Button
|
|
62
|
+
type="button"
|
|
61
63
|
className="redo"
|
|
62
64
|
onClick={() => doRedo()}
|
|
63
65
|
aria-label={intl.formatMessage(messages.redo)}
|
|
@@ -113,6 +113,7 @@ const Sidebar = (props) => {
|
|
|
113
113
|
style={size > 0 ? { width: size } : null}
|
|
114
114
|
>
|
|
115
115
|
<Button
|
|
116
|
+
type="button"
|
|
116
117
|
aria-label={
|
|
117
118
|
expanded
|
|
118
119
|
? intl.formatMessage(messages.shrinkSidebar)
|
|
@@ -126,6 +127,7 @@ const Sidebar = (props) => {
|
|
|
126
127
|
onClick={onToggleExpanded}
|
|
127
128
|
/>
|
|
128
129
|
<Button
|
|
130
|
+
type="button"
|
|
129
131
|
className="full-size-sidenav-btn"
|
|
130
132
|
onClick={onToggleFullSize}
|
|
131
133
|
aria-label="full-screen-sidenav"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import config from '@plone/volto/registry';
|
|
2
2
|
import Helmet from '@plone/volto/helpers/Helmet/Helmet';
|
|
3
|
+
import { flattenToAppURL, toPublicURL } from '@plone/volto/helpers/Url/Url';
|
|
3
4
|
|
|
4
5
|
const AlternateHrefLangs = (props) => {
|
|
5
6
|
const { content } = props;
|
|
@@ -16,7 +17,7 @@ const AlternateHrefLangs = (props) => {
|
|
|
16
17
|
key={key}
|
|
17
18
|
rel="alternate"
|
|
18
19
|
hrefLang={item.language}
|
|
19
|
-
href={item['@id']}
|
|
20
|
+
href={toPublicURL(flattenToAppURL(item['@id']))}
|
|
20
21
|
/>
|
|
21
22
|
);
|
|
22
23
|
})}
|
|
@@ -35,16 +35,18 @@ describe('AlternateHrefLangs', () => {
|
|
|
35
35
|
const helmetLinks = Helmet.peek().linkTags;
|
|
36
36
|
expect(helmetLinks.length).toBe(0);
|
|
37
37
|
});
|
|
38
|
+
|
|
38
39
|
it('multilingual site, with some translations', () => {
|
|
40
|
+
config.settings.publicURL = 'https://plone.org';
|
|
39
41
|
config.settings.isMultilingual = true;
|
|
40
42
|
config.settings.supportedLanguages = ['en', 'es', 'eu'];
|
|
41
43
|
|
|
42
44
|
const content = {
|
|
43
|
-
'@id': '/en',
|
|
45
|
+
'@id': 'http://localhost:8080/Plone/en',
|
|
44
46
|
language: { token: 'en', title: 'English' },
|
|
45
47
|
'@components': {
|
|
46
48
|
translations: {
|
|
47
|
-
items: [{ '@id': '/es', language: 'es' }],
|
|
49
|
+
items: [{ '@id': 'http://localhost:8080/Plone/es', language: 'es' }],
|
|
48
50
|
},
|
|
49
51
|
},
|
|
50
52
|
};
|
|
@@ -71,17 +73,72 @@ describe('AlternateHrefLangs', () => {
|
|
|
71
73
|
|
|
72
74
|
expect(helmetLinks).toContainEqual({
|
|
73
75
|
rel: 'alternate',
|
|
74
|
-
href: '/es',
|
|
76
|
+
href: 'https://plone.org/es',
|
|
75
77
|
hrefLang: 'es',
|
|
76
78
|
});
|
|
77
79
|
expect(helmetLinks).toContainEqual({
|
|
78
80
|
rel: 'alternate',
|
|
79
|
-
href: '/en',
|
|
81
|
+
href: 'https://plone.org/en',
|
|
80
82
|
hrefLang: 'en',
|
|
81
83
|
});
|
|
82
84
|
});
|
|
83
85
|
|
|
84
86
|
it('multilingual site, with all available translations', () => {
|
|
87
|
+
config.settings.publicURL = 'https://plone.org';
|
|
88
|
+
config.settings.isMultilingual = true;
|
|
89
|
+
config.settings.supportedLanguages = ['en', 'es', 'eu'];
|
|
90
|
+
const store = mockStore({
|
|
91
|
+
intl: {
|
|
92
|
+
locale: 'en',
|
|
93
|
+
messages: {},
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const content = {
|
|
98
|
+
'@id': 'http://localhost:8080/Plone/en',
|
|
99
|
+
language: { token: 'en', title: 'English' },
|
|
100
|
+
'@components': {
|
|
101
|
+
translations: {
|
|
102
|
+
items: [
|
|
103
|
+
{ '@id': 'http://localhost:8080/Plone/eu', language: 'eu' },
|
|
104
|
+
{ '@id': 'http://localhost:8080/Plone/es', language: 'es' },
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// We need to force the component rendering
|
|
111
|
+
// to fill the Helmet
|
|
112
|
+
renderer.create(
|
|
113
|
+
<Provider store={store}>
|
|
114
|
+
<AlternateHrefLangs content={content} />
|
|
115
|
+
</Provider>,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const helmetLinks = Helmet.peek().linkTags;
|
|
119
|
+
|
|
120
|
+
// We expect having 3 links
|
|
121
|
+
expect(helmetLinks.length).toBe(3);
|
|
122
|
+
|
|
123
|
+
expect(helmetLinks).toContainEqual({
|
|
124
|
+
rel: 'alternate',
|
|
125
|
+
href: 'https://plone.org/eu',
|
|
126
|
+
hrefLang: 'eu',
|
|
127
|
+
});
|
|
128
|
+
expect(helmetLinks).toContainEqual({
|
|
129
|
+
rel: 'alternate',
|
|
130
|
+
href: 'https://plone.org/es',
|
|
131
|
+
hrefLang: 'es',
|
|
132
|
+
});
|
|
133
|
+
expect(helmetLinks).toContainEqual({
|
|
134
|
+
rel: 'alternate',
|
|
135
|
+
href: 'https://plone.org/en',
|
|
136
|
+
hrefLang: 'en',
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('multilingual site, with all available translations - with server URL', () => {
|
|
141
|
+
config.settings.publicURL = 'https://plone.org';
|
|
85
142
|
config.settings.isMultilingual = true;
|
|
86
143
|
config.settings.supportedLanguages = ['en', 'es', 'eu'];
|
|
87
144
|
const store = mockStore({
|
|
@@ -92,13 +149,13 @@ describe('AlternateHrefLangs', () => {
|
|
|
92
149
|
});
|
|
93
150
|
|
|
94
151
|
const content = {
|
|
95
|
-
'@id': '/en',
|
|
152
|
+
'@id': 'http://localhost:8080/Plone/en',
|
|
96
153
|
language: { token: 'en', title: 'English' },
|
|
97
154
|
'@components': {
|
|
98
155
|
translations: {
|
|
99
156
|
items: [
|
|
100
|
-
{ '@id': '/eu', language: 'eu' },
|
|
101
|
-
{ '@id': '/es', language: 'es' },
|
|
157
|
+
{ '@id': 'http://localhost:8080/Plone/eu', language: 'eu' },
|
|
158
|
+
{ '@id': 'http://localhost:8080/Plone/es', language: 'es' },
|
|
102
159
|
],
|
|
103
160
|
},
|
|
104
161
|
},
|
|
@@ -119,17 +176,17 @@ describe('AlternateHrefLangs', () => {
|
|
|
119
176
|
|
|
120
177
|
expect(helmetLinks).toContainEqual({
|
|
121
178
|
rel: 'alternate',
|
|
122
|
-
href: '/eu',
|
|
179
|
+
href: 'https://plone.org/eu',
|
|
123
180
|
hrefLang: 'eu',
|
|
124
181
|
});
|
|
125
182
|
expect(helmetLinks).toContainEqual({
|
|
126
183
|
rel: 'alternate',
|
|
127
|
-
href: '/es',
|
|
184
|
+
href: 'https://plone.org/es',
|
|
128
185
|
hrefLang: 'es',
|
|
129
186
|
});
|
|
130
187
|
expect(helmetLinks).toContainEqual({
|
|
131
188
|
rel: 'alternate',
|
|
132
|
-
href: '/en',
|
|
189
|
+
href: 'https://plone.org/en',
|
|
133
190
|
hrefLang: 'en',
|
|
134
191
|
});
|
|
135
192
|
});
|
|
@@ -27,10 +27,10 @@ export default function Image({
|
|
|
27
27
|
// TypeScript hints for editor autocomplete :)
|
|
28
28
|
/** @type {React.ImgHTMLAttributes<HTMLImageElement>} */
|
|
29
29
|
const attrs = {};
|
|
30
|
+
attrs.className = cx(className, { responsive }) || undefined;
|
|
30
31
|
|
|
31
32
|
if (!item && src) {
|
|
32
33
|
attrs.src = src;
|
|
33
|
-
attrs.className = cx(className, { responsive });
|
|
34
34
|
} else {
|
|
35
35
|
const isFromRealObject = !item.image_scales;
|
|
36
36
|
const imageFieldWithDefault = imageField || item.image_field || 'image';
|
|
@@ -51,7 +51,6 @@ export default function Image({
|
|
|
51
51
|
attrs.src = `${flattenToAppURL(basePath)}/${image.download}`;
|
|
52
52
|
attrs.width = image.width;
|
|
53
53
|
attrs.height = image.height;
|
|
54
|
-
attrs.className = cx(className, { responsive });
|
|
55
54
|
|
|
56
55
|
if (!isSvg && image.scales && Object.keys(image.scales).length > 0) {
|
|
57
56
|
const sortedScales = Object.values({
|
|
@@ -155,3 +155,11 @@ test('renders an image component from a string src', () => {
|
|
|
155
155
|
const json = component.toJSON();
|
|
156
156
|
expect(json).toMatchSnapshot();
|
|
157
157
|
});
|
|
158
|
+
|
|
159
|
+
test('should not render empty class attribute in img tag', () => {
|
|
160
|
+
const component = renderer.create(
|
|
161
|
+
<Image src="/image.png" alt="no class attribute" />,
|
|
162
|
+
);
|
|
163
|
+
const json = component.toJSON();
|
|
164
|
+
expect(json).toMatchSnapshot();
|
|
165
|
+
});
|
|
@@ -26,7 +26,7 @@ const messages = defineMessages({
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
const RenderBlocks = (props) => {
|
|
29
|
-
const { content, location,
|
|
29
|
+
const { blockWrapperTag, content, location, isContainer, metadata } = props;
|
|
30
30
|
const intl = useIntl();
|
|
31
31
|
const blocksFieldname = getBlocksFieldname(content);
|
|
32
32
|
const blocksLayoutFieldname = getBlocksLayoutFieldname(content);
|
|
@@ -72,6 +72,7 @@ const RenderBlocks = (props) => {
|
|
|
72
72
|
id={block}
|
|
73
73
|
block={block}
|
|
74
74
|
data={blockData}
|
|
75
|
+
isContainer={isContainer}
|
|
75
76
|
>
|
|
76
77
|
<Block
|
|
77
78
|
id={block}
|
package/src/config/index.js
CHANGED
|
@@ -113,7 +113,7 @@ let config = {
|
|
|
113
113
|
defaultPageSize: 25,
|
|
114
114
|
isMultilingual: false,
|
|
115
115
|
supportedLanguages: ['en'],
|
|
116
|
-
defaultLanguage: 'en',
|
|
116
|
+
defaultLanguage: process.env.SITE_DEFAULT_LANGUAGE || 'en',
|
|
117
117
|
navDepth: 1,
|
|
118
118
|
expressMiddleware: serverConfig.expressMiddleware, // BBB
|
|
119
119
|
defaultBlockType: 'slate',
|
|
@@ -232,6 +232,16 @@ Object.entries(slots).forEach(([slotName, components]) => {
|
|
|
232
232
|
});
|
|
233
233
|
});
|
|
234
234
|
|
|
235
|
+
// Make sure that process.env.SITE_DEFAULT_LANGUAGE is set in availableLanguages
|
|
236
|
+
if (
|
|
237
|
+
process.env.SITE_DEFAULT_LANGUAGE &&
|
|
238
|
+
!config.settings.supportedLanguages.includes(
|
|
239
|
+
process.env.SITE_DEFAULT_LANGUAGE,
|
|
240
|
+
)
|
|
241
|
+
) {
|
|
242
|
+
config.settings.supportedLanguages.push(process.env.SITE_DEFAULT_LANGUAGE);
|
|
243
|
+
}
|
|
244
|
+
|
|
235
245
|
registerValidators(ConfigRegistry);
|
|
236
246
|
installDefaultComponents(ConfigRegistry);
|
|
237
247
|
installDefaultWidgets(ConfigRegistry);
|
package/src/config/validation.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
startEventDateRangeValidator,
|
|
16
16
|
endEventDateRangeValidator,
|
|
17
17
|
patternValidator,
|
|
18
|
+
defaultLanguageControlPanelValidator,
|
|
18
19
|
} from '@plone/volto/helpers/FormValidation/validators';
|
|
19
20
|
|
|
20
21
|
const registerValidators = (config: ConfigType) => {
|
|
@@ -150,6 +151,13 @@ const registerValidators = (config: ConfigType) => {
|
|
|
150
151
|
dependencies: { behaviorName: 'plone.eventbasic', fieldName: 'end' },
|
|
151
152
|
method: endEventDateRangeValidator,
|
|
152
153
|
});
|
|
154
|
+
|
|
155
|
+
config.registerUtility({
|
|
156
|
+
name: 'default_language',
|
|
157
|
+
type: 'validator',
|
|
158
|
+
dependencies: { format: 'default_language' },
|
|
159
|
+
method: defaultLanguageControlPanelValidator,
|
|
160
|
+
});
|
|
153
161
|
};
|
|
154
162
|
|
|
155
163
|
export { registerValidators };
|
|
@@ -661,7 +661,11 @@ export const styleDataToStyleObject = (key, value, prefix = '') => {
|
|
|
661
661
|
* @param {string} prefix The prefix (could be dragged from a recursive call, initially empty)
|
|
662
662
|
* @return {Object} The style object ready to be passed as prop
|
|
663
663
|
*/
|
|
664
|
-
export const buildStyleObjectFromData = (
|
|
664
|
+
export const buildStyleObjectFromData = (
|
|
665
|
+
data = {},
|
|
666
|
+
prefix = '',
|
|
667
|
+
container = {},
|
|
668
|
+
) => {
|
|
665
669
|
// style wrapper object has the form:
|
|
666
670
|
// const styles = {
|
|
667
671
|
// color: 'red',
|
|
@@ -720,7 +724,7 @@ export const buildStyleObjectFromData = (data = {}, prefix = '') => {
|
|
|
720
724
|
enhancers.forEach(({ method }) => {
|
|
721
725
|
stylesFromObjectStyleEnhancers = {
|
|
722
726
|
...stylesFromObjectStyleEnhancers,
|
|
723
|
-
...method(data),
|
|
727
|
+
...method({ data, container }),
|
|
724
728
|
};
|
|
725
729
|
});
|
|
726
730
|
|
|
@@ -1151,7 +1151,7 @@ describe('Blocks', () => {
|
|
|
1151
1151
|
|
|
1152
1152
|
describe('buildStyleObjectFromData', () => {
|
|
1153
1153
|
beforeEach(() => {
|
|
1154
|
-
function blockThemesEnhancer(data) {
|
|
1154
|
+
function blockThemesEnhancer({ data }) {
|
|
1155
1155
|
const blockConfig = config.blocks.blocksConfig[data['@type']];
|
|
1156
1156
|
const blockStyleDefinitions =
|
|
1157
1157
|
// We look up for the blockThemes in the block's data, then in the global config
|
|
@@ -133,6 +133,7 @@ const validateFieldsPerFieldset = (
|
|
|
133
133
|
|
|
134
134
|
Object.entries(schema.properties).forEach(([fieldId, field]) => {
|
|
135
135
|
let fieldData = formData[fieldId];
|
|
136
|
+
field = { ...field, ...field.widgetOptions?.frontendOptions?.widgetProps };
|
|
136
137
|
|
|
137
138
|
// Validation per specific validator set (format property)
|
|
138
139
|
const hasSpecificValidator =
|
|
@@ -257,7 +257,7 @@ describe('FormValidation', () => {
|
|
|
257
257
|
});
|
|
258
258
|
});
|
|
259
259
|
|
|
260
|
-
it('string - min
|
|
260
|
+
it('string - min length', () => {
|
|
261
261
|
let newSchema = {
|
|
262
262
|
properties: {
|
|
263
263
|
...schema.properties,
|
|
@@ -283,7 +283,7 @@ describe('FormValidation', () => {
|
|
|
283
283
|
});
|
|
284
284
|
});
|
|
285
285
|
|
|
286
|
-
it('string - max
|
|
286
|
+
it('string - max length', () => {
|
|
287
287
|
let newSchema = {
|
|
288
288
|
properties: {
|
|
289
289
|
...schema.properties,
|
|
@@ -570,7 +570,7 @@ describe('FormValidation', () => {
|
|
|
570
570
|
});
|
|
571
571
|
});
|
|
572
572
|
|
|
573
|
-
it('password - min
|
|
573
|
+
it('password - min length', () => {
|
|
574
574
|
let newSchema = {
|
|
575
575
|
...schema,
|
|
576
576
|
properties: {
|
|
@@ -595,7 +595,41 @@ describe('FormValidation', () => {
|
|
|
595
595
|
});
|
|
596
596
|
});
|
|
597
597
|
|
|
598
|
-
it('
|
|
598
|
+
it('description - min length from server side', () => {
|
|
599
|
+
let newSchema = {
|
|
600
|
+
...schema,
|
|
601
|
+
properties: {
|
|
602
|
+
...schema.properties,
|
|
603
|
+
description: {
|
|
604
|
+
title: 'description',
|
|
605
|
+
type: 'string',
|
|
606
|
+
description: '',
|
|
607
|
+
widgetOptions: {
|
|
608
|
+
frontendOptions: {
|
|
609
|
+
widgetProps: {
|
|
610
|
+
minLength: 8,
|
|
611
|
+
},
|
|
612
|
+
},
|
|
613
|
+
},
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
required: [],
|
|
617
|
+
};
|
|
618
|
+
expect(
|
|
619
|
+
FormValidation.validateFieldsPerFieldset({
|
|
620
|
+
schema: newSchema,
|
|
621
|
+
formData: {
|
|
622
|
+
username: 'test username',
|
|
623
|
+
description: 'asd',
|
|
624
|
+
},
|
|
625
|
+
formatMessage,
|
|
626
|
+
}),
|
|
627
|
+
).toEqual({
|
|
628
|
+
description: [messages.minLength.defaultMessage],
|
|
629
|
+
});
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
it('password - max length', () => {
|
|
599
633
|
let newSchema = {
|
|
600
634
|
...schema,
|
|
601
635
|
properties: {
|
|
@@ -620,6 +654,40 @@ describe('FormValidation', () => {
|
|
|
620
654
|
});
|
|
621
655
|
});
|
|
622
656
|
|
|
657
|
+
it('description - max length from server side', () => {
|
|
658
|
+
let newSchema = {
|
|
659
|
+
...schema,
|
|
660
|
+
properties: {
|
|
661
|
+
...schema.properties,
|
|
662
|
+
description: {
|
|
663
|
+
title: 'description',
|
|
664
|
+
type: 'string',
|
|
665
|
+
description: '',
|
|
666
|
+
widgetOptions: {
|
|
667
|
+
frontendOptions: {
|
|
668
|
+
widgetProps: {
|
|
669
|
+
maxLength: 8,
|
|
670
|
+
},
|
|
671
|
+
},
|
|
672
|
+
},
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
required: [],
|
|
676
|
+
};
|
|
677
|
+
expect(
|
|
678
|
+
FormValidation.validateFieldsPerFieldset({
|
|
679
|
+
schema: newSchema,
|
|
680
|
+
formData: {
|
|
681
|
+
username: 'test username',
|
|
682
|
+
description: 'asdasdasdasdasd',
|
|
683
|
+
},
|
|
684
|
+
formatMessage,
|
|
685
|
+
}),
|
|
686
|
+
).toEqual({
|
|
687
|
+
description: [messages.maxLength.defaultMessage],
|
|
688
|
+
});
|
|
689
|
+
});
|
|
690
|
+
|
|
623
691
|
it('array - maxItems', () => {
|
|
624
692
|
let newSchema = {
|
|
625
693
|
properties: {
|
|
@@ -203,3 +203,17 @@ export const minItemsValidator = ({
|
|
|
203
203
|
? formatMessage(messages.minItems, { minItems: field.minItems })
|
|
204
204
|
: null;
|
|
205
205
|
};
|
|
206
|
+
|
|
207
|
+
export const defaultLanguageControlPanelValidator = ({
|
|
208
|
+
value,
|
|
209
|
+
formData,
|
|
210
|
+
formatMessage,
|
|
211
|
+
}: Validator) => {
|
|
212
|
+
const isValid =
|
|
213
|
+
value &&
|
|
214
|
+
(formData.available_languages.find(
|
|
215
|
+
(lang: { token: string }) => lang.token === value,
|
|
216
|
+
) ||
|
|
217
|
+
formData.available_languages.includes(value));
|
|
218
|
+
return !isValid ? formatMessage(messages.defaultLanguage) : null;
|
|
219
|
+
};
|
|
@@ -403,4 +403,9 @@ export const messages = defineMessages({
|
|
|
403
403
|
defaultMessage:
|
|
404
404
|
'The number of items must be greater than or equal to {minItems}',
|
|
405
405
|
},
|
|
406
|
+
defaultLanguage: {
|
|
407
|
+
id: "The selected default language must be in the list of the field 'Available languages'",
|
|
408
|
+
defaultMessage:
|
|
409
|
+
"The selected default language must be in the list of the field 'Available languages'",
|
|
410
|
+
},
|
|
406
411
|
});
|
package/src/start-client.jsx
CHANGED
|
@@ -58,6 +58,10 @@ export default function client() {
|
|
|
58
58
|
if (window.env.RAZZLE_LEGACY_TRAVERSE) {
|
|
59
59
|
config.settings.legacyTraverse = true;
|
|
60
60
|
}
|
|
61
|
+
// Support for setting the default language from a server environment variable
|
|
62
|
+
if (window.env.defaultLanguage) {
|
|
63
|
+
config.settings.defaultLanguage = window.env.defaultLanguage;
|
|
64
|
+
}
|
|
61
65
|
|
|
62
66
|
loadableReady(() => {
|
|
63
67
|
hydrateRoot(
|
package/src/storybook.jsx
CHANGED
|
@@ -1490,6 +1490,7 @@ export const FormUndoWrapper = ({
|
|
|
1490
1490
|
onClick={() => doUndo()}
|
|
1491
1491
|
aria-label="Undo"
|
|
1492
1492
|
disabled={!canUndo}
|
|
1493
|
+
type="button"
|
|
1493
1494
|
>
|
|
1494
1495
|
Undo
|
|
1495
1496
|
</Button>
|
|
@@ -1500,6 +1501,7 @@ export const FormUndoWrapper = ({
|
|
|
1500
1501
|
onClick={() => doRedo()}
|
|
1501
1502
|
aria-label="Redo"
|
|
1502
1503
|
disabled={!canRedo}
|
|
1504
|
+
type="button"
|
|
1503
1505
|
>
|
|
1504
1506
|
Redo
|
|
1505
1507
|
</Button>
|
|
@@ -187,7 +187,7 @@ export function styleToClassName(key: any, value: any, prefix?: string): any;
|
|
|
187
187
|
export function buildStyleClassNamesFromData(obj?: {}, prefix?: string): any;
|
|
188
188
|
export function buildStyleClassNamesExtenders({ block, content, data, classNames, }: any): any[];
|
|
189
189
|
export function styleDataToStyleObject(key: any, value: any, prefix?: string): any[];
|
|
190
|
-
export function buildStyleObjectFromData(data?: any, prefix?: string): any;
|
|
190
|
+
export function buildStyleObjectFromData(data?: any, prefix?: string, container?: {}): any;
|
|
191
191
|
export function getPreviousNextBlock({ content, block }: any): any[];
|
|
192
192
|
export function getBlocksHierarchy(properties: any): any;
|
|
193
193
|
export function findContainer(formData: object, { containerId }: {
|
|
@@ -26,4 +26,5 @@ export declare const endEventDateRangeValidator: ({ value, field, formData, form
|
|
|
26
26
|
export declare const patternValidator: ({ value, field, formatMessage, }: Validator) => any;
|
|
27
27
|
export declare const maxItemsValidator: ({ value, field, formatMessage, }: Validator) => any;
|
|
28
28
|
export declare const minItemsValidator: ({ value, field, formatMessage, }: Validator) => any;
|
|
29
|
+
export declare const defaultLanguageControlPanelValidator: ({ value, formData, formatMessage, }: Validator) => any;
|
|
29
30
|
export {};
|
|
@@ -585,4 +585,10 @@ export namespace messages {
|
|
|
585
585
|
let defaultMessage_97: string;
|
|
586
586
|
export { defaultMessage_97 as defaultMessage };
|
|
587
587
|
}
|
|
588
|
+
namespace defaultLanguage {
|
|
589
|
+
let id_98: string;
|
|
590
|
+
export { id_98 as id };
|
|
591
|
+
let defaultMessage_98: string;
|
|
592
|
+
export { defaultMessage_98 as defaultMessage };
|
|
593
|
+
}
|
|
588
594
|
}
|