From 5485667ae55f6009279a858043fb797b774d5634 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Fri, 26 Jul 2024 12:02:24 +0200 Subject: [PATCH 01/58] Prepare for release @plone/blocks --- packages/blocks/.npmignore | 8 ++++++++ packages/blocks/.release-it.json | 28 +++++++++++++++++++++++++++ packages/blocks/CHANGELOG.md | 9 +++++++++ packages/blocks/README.md | 4 ++++ packages/blocks/news/0.internal | 1 + packages/blocks/package.json | 4 +--- packages/blocks/towncrier.toml | 33 ++++++++++++++++++++++++++++++++ 7 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 packages/blocks/.npmignore create mode 100644 packages/blocks/.release-it.json create mode 100644 packages/blocks/CHANGELOG.md create mode 100644 packages/blocks/news/0.internal create mode 100644 packages/blocks/towncrier.toml diff --git a/packages/blocks/.npmignore b/packages/blocks/.npmignore new file mode 100644 index 0000000000..0d8afd5727 --- /dev/null +++ b/packages/blocks/.npmignore @@ -0,0 +1,8 @@ +news +towncrier.toml +.changelog.draft +node_modules/ +.parcel-cache +.parcelrc +.release-it.json +.eslintrc.js diff --git a/packages/blocks/.release-it.json b/packages/blocks/.release-it.json new file mode 100644 index 0000000000..e22a4a4b58 --- /dev/null +++ b/packages/blocks/.release-it.json @@ -0,0 +1,28 @@ +{ + "plugins": { + "../scripts/prepublish.js": {} + }, + "hooks": { + "after:bump": [ + "pipx run towncrier build --draft --yes --version ${version} > .changelog.draft", + "pipx run towncrier build --yes --version ${version}" + ], + "after:release": "rm .changelog.draft" + }, + "npm": { + "publish": false + }, + "git": { + "changelog": "pipx run towncrier build --draft --yes --version 0.0.0", + "requireUpstream": false, + "requireCleanWorkingDir": false, + "commitMessage": "Release @plone/blocks ${version}", + "tagName": "plone-blocks-${version}", + "tagAnnotation": "Release @plone/blocks ${version}" + }, + "github": { + "release": true, + "releaseName": "@plone/blocks ${version}", + "releaseNotes": "cat .changelog.draft" + } +} diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md new file mode 100644 index 0000000000..cbe76b30b2 --- /dev/null +++ b/packages/blocks/CHANGELOG.md @@ -0,0 +1,9 @@ +# @plone/blocks Release Notes + + + + diff --git a/packages/blocks/README.md b/packages/blocks/README.md index c53c030b59..019efc997b 100644 --- a/packages/blocks/README.md +++ b/packages/blocks/README.md @@ -1,3 +1,7 @@ # `@plone/blocks` This package contains the default blocks provided by Plone. +This package is in alpha stages, experimental, under heavy development, and is subject to substantial changes in the future. +It's one of the Plone frontend strategic packages and part of the headless CMS story. + +`@plone/blocks` is not part of Volto, and thus, is not used by it. diff --git a/packages/blocks/news/0.internal b/packages/blocks/news/0.internal new file mode 100644 index 0000000000..ba6aac560b --- /dev/null +++ b/packages/blocks/news/0.internal @@ -0,0 +1 @@ +Initial release @sneridagh diff --git a/packages/blocks/package.json b/packages/blocks/package.json index 9bdf683897..4be4b49403 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -9,7 +9,7 @@ ], "funding": "https://github.com/sponsors/plone", "license": "MIT", - "version": "1.0.0", + "version": "1.0.0-alpha.0", "repository": { "type": "git", "url": "https://github.com/plone/volto.git" @@ -35,8 +35,6 @@ } }, "scripts": { - "watch": "parcel watch", - "build": "parcel build", "test": "vitest", "dry-release": "release-it --dry-run", "release": "release-it", diff --git a/packages/blocks/towncrier.toml b/packages/blocks/towncrier.toml new file mode 100644 index 0000000000..74d2320c8e --- /dev/null +++ b/packages/blocks/towncrier.toml @@ -0,0 +1,33 @@ +[tool.towncrier] +filename = "CHANGELOG.md" +directory = "news/" +title_format = "## {version} ({project_date})" +underlines = ["", "", ""] +template = "../scripts/templates/towncrier_template.jinja" +start_string = "\n" +issue_format = "[#{issue}](https://github.com/plone/volto/pull/{issue})" + +[[tool.towncrier.type]] +directory = "breaking" +name = "Breaking" +showcontent = true + +[[tool.towncrier.type]] +directory = "feature" +name = "Feature" +showcontent = true + +[[tool.towncrier.type]] +directory = "bugfix" +name = "Bugfix" +showcontent = true + +[[tool.towncrier.type]] +directory = "internal" +name = "Internal" +showcontent = true + +[[tool.towncrier.type]] +directory = "documentation" +name = "Documentation" +showcontent = true From ef4331c97a37dd2ae535508b4f903d4a6c1d4a75 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Fri, 26 Jul 2024 12:12:40 +0200 Subject: [PATCH 02/58] Release @plone/blocks 1.0.0-alpha.1 --- packages/blocks/CHANGELOG.md | 6 ++++++ packages/blocks/news/0.internal | 1 - packages/blocks/package.json | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) delete mode 100644 packages/blocks/news/0.internal diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md index cbe76b30b2..89cd5d8368 100644 --- a/packages/blocks/CHANGELOG.md +++ b/packages/blocks/CHANGELOG.md @@ -7,3 +7,9 @@ --> + +## 1.0.0-alpha.1 (2024-07-26) + +### Internal + +- Initial release @sneridagh [#0](https://github.com/plone/volto/pull/0) diff --git a/packages/blocks/news/0.internal b/packages/blocks/news/0.internal deleted file mode 100644 index ba6aac560b..0000000000 --- a/packages/blocks/news/0.internal +++ /dev/null @@ -1 +0,0 @@ -Initial release @sneridagh diff --git a/packages/blocks/package.json b/packages/blocks/package.json index 4be4b49403..3b945309d6 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -9,7 +9,7 @@ ], "funding": "https://github.com/sponsors/plone", "license": "MIT", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.1", "repository": { "type": "git", "url": "https://github.com/plone/volto.git" From a4476e26eccac5939c8267586c5e80d7bd3d6c30 Mon Sep 17 00:00:00 2001 From: Tom Schall Date: Fri, 26 Jul 2024 15:35:39 +0200 Subject: [PATCH 03/58] Refactor: Change content types to interfaces to make them extendable (#6191) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Víctor Fernández de Alba --- packages/types/news/6191.feature | 1 + packages/types/src/content/common.d.ts | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 packages/types/news/6191.feature diff --git a/packages/types/news/6191.feature b/packages/types/news/6191.feature new file mode 100644 index 0000000000..4377407fa9 --- /dev/null +++ b/packages/types/news/6191.feature @@ -0,0 +1 @@ +Change the types in `common.d.ts` to interfaces, to make them extendable. @tomschall \ No newline at end of file diff --git a/packages/types/src/content/common.d.ts b/packages/types/src/content/common.d.ts index 4a4091481b..be73df373a 100644 --- a/packages/types/src/content/common.d.ts +++ b/packages/types/src/content/common.d.ts @@ -19,7 +19,7 @@ export interface Expanders { workflow: WorkflowResponse; } -export type ContainedItem = { +export interface ContainedItem { '@id': string; '@type': string; description: string; @@ -27,9 +27,9 @@ export type ContainedItem = { image_scales: null; review_state: string; title: string; -}; +} -export type RelatedItem = { +export interface RelatedItem { '@id': string; '@type': string; UID: string; @@ -38,7 +38,7 @@ export type RelatedItem = { image_scales: Record | null; review_state: string; title: string; -}; +} export type ImageScale = { download: string; From 2c53ebfa96bb46ba0574ecc2cb71dbfc10f5550a Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Fri, 26 Jul 2024 15:36:13 +0200 Subject: [PATCH 04/58] Release @plone/types 1.0.0-alpha.18 --- packages/types/CHANGELOG.md | 6 ++++++ packages/types/news/6191.feature | 1 - packages/types/package.json | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) delete mode 100644 packages/types/news/6191.feature diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 4fc19146ba..8078129cb3 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -8,6 +8,12 @@ +## 1.0.0-alpha.18 (2024-07-26) + +### Feature + +- Change the types in `common.d.ts` to interfaces, to make them extendable. @tomschall [#6191](https://github.com/plone/volto/issues/6191) + ## 1.0.0-alpha.17 (2024-06-27) ### Internal diff --git a/packages/types/news/6191.feature b/packages/types/news/6191.feature deleted file mode 100644 index 4377407fa9..0000000000 --- a/packages/types/news/6191.feature +++ /dev/null @@ -1 +0,0 @@ -Change the types in `common.d.ts` to interfaces, to make them extendable. @tomschall \ No newline at end of file diff --git a/packages/types/package.json b/packages/types/package.json index 82ffe7bf4f..4e0cb5645b 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -9,7 +9,7 @@ ], "funding": "https://github.com/sponsors/plone", "license": "MIT", - "version": "1.0.0-alpha.17", + "version": "1.0.0-alpha.18", "repository": { "type": "git", "url": "https://github.com/plone/volto.git" From 85302345ea4f09292ea71a43b4d056b78633f206 Mon Sep 17 00:00:00 2001 From: sabrina-bongiovanni <116291154+sabrina-bongiovanni@users.noreply.github.com> Date: Mon, 29 Jul 2024 15:57:04 +0200 Subject: [PATCH 05/58] fix: state and styles for toolbar handler for hp (#6126) --- packages/volto/news/6126.bugfix | 1 + .../volto/src/components/manage/Toolbar/Toolbar.jsx | 2 +- .../Toolbar/__snapshots__/Toolbar.test.jsx.snap | 4 ++-- .../theme/themes/pastanaga/extras/toolbar.less | 13 ++++++++++--- 4 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 packages/volto/news/6126.bugfix diff --git a/packages/volto/news/6126.bugfix b/packages/volto/news/6126.bugfix new file mode 100644 index 0000000000..8db6a83f2e --- /dev/null +++ b/packages/volto/news/6126.bugfix @@ -0,0 +1 @@ +Fix the toolbar handler color for the homepage to match its "published" state. @sabrina-bongiovanni diff --git a/packages/volto/src/components/manage/Toolbar/Toolbar.jsx b/packages/volto/src/components/manage/Toolbar/Toolbar.jsx index f8b2abb944..170eaabf7e 100644 --- a/packages/volto/src/components/manage/Toolbar/Toolbar.jsx +++ b/packages/volto/src/components/manage/Toolbar/Toolbar.jsx @@ -623,7 +623,7 @@ class Toolbar extends Component { aria-label={this.props.intl.formatMessage( messages.shrinkToolbar, )} - className={cx({ + className={cx('toolbar-handler-button', { [this.props.content?.review_state]: this.props.content?.review_state, })} diff --git a/packages/volto/src/components/manage/Toolbar/__snapshots__/Toolbar.test.jsx.snap b/packages/volto/src/components/manage/Toolbar/__snapshots__/Toolbar.test.jsx.snap index 0d471fcf2b..ce6c7adde4 100644 --- a/packages/volto/src/components/manage/Toolbar/__snapshots__/Toolbar.test.jsx.snap +++ b/packages/volto/src/components/manage/Toolbar/__snapshots__/Toolbar.test.jsx.snap @@ -183,7 +183,7 @@ Array [ > - + 0 })} + > {config.blocks.blocksConfig[data?.['@type']]?.icon && ( handleRemove(id) : undefined} onSelectBlock={onSelectBlock} + errors={errors?.[id] || {}} /> ))} {createPortal( diff --git a/packages/volto/src/components/manage/Blocks/Container/Data.jsx b/packages/volto/src/components/manage/Blocks/Container/Data.jsx index d9b24bf6e0..83e75b469c 100644 --- a/packages/volto/src/components/manage/Blocks/Container/Data.jsx +++ b/packages/volto/src/components/manage/Blocks/Container/Data.jsx @@ -2,8 +2,15 @@ import { useIntl } from 'react-intl'; import { BlockDataForm } from '@plone/volto/components/manage/Form'; const ContainerData = (props) => { - const { block, blocksConfig, data, onChangeBlock, navRoot, contentType } = - props; + const { + block, + blocksConfig, + blocksErrors, + data, + onChangeBlock, + navRoot, + contentType, + } = props; const intl = useIntl(); const schema = blocksConfig[data['@type']].blockSchema({ intl }); @@ -28,6 +35,7 @@ const ContainerData = (props) => { blocksConfig={blocksConfig} navRoot={navRoot} contentType={contentType} + errors={blocksErrors} /> ); }; diff --git a/packages/volto/src/components/manage/Blocks/Image/ImageSidebar.jsx b/packages/volto/src/components/manage/Blocks/Image/ImageSidebar.jsx index 94a43e1d3c..dd35733475 100644 --- a/packages/volto/src/components/manage/Blocks/Image/ImageSidebar.jsx +++ b/packages/volto/src/components/manage/Blocks/Image/ImageSidebar.jsx @@ -10,8 +10,15 @@ import imageSVG from '@plone/volto/icons/image.svg'; import trashSVG from '@plone/volto/icons/delete.svg'; const ImageSidebar = (props) => { - const { blocksConfig, data, block, onChangeBlock, navRoot, contentType } = - props; + const { + blocksConfig, + blocksErrors, + data, + block, + onChangeBlock, + navRoot, + contentType, + } = props; const intl = useIntl(); const schema = ImageSchema({ formData: data, intl }); return ( @@ -98,6 +105,7 @@ const ImageSidebar = (props) => { blocksConfig={blocksConfig} navRoot={navRoot} contentType={contentType} + errors={blocksErrors} /> ); diff --git a/packages/volto/src/components/manage/Blocks/Listing/ListingData.jsx b/packages/volto/src/components/manage/Blocks/Listing/ListingData.jsx index a531009b3f..f19ba3ff3e 100644 --- a/packages/volto/src/components/manage/Blocks/Listing/ListingData.jsx +++ b/packages/volto/src/components/manage/Blocks/Listing/ListingData.jsx @@ -4,8 +4,15 @@ import { useIntl } from 'react-intl'; import { BlockDataForm } from '@plone/volto/components/manage/Form'; const ListingData = (props) => { - const { data, block, blocksConfig, onChangeBlock, navRoot, contentType } = - props; + const { + data, + block, + blocksConfig, + blocksErrors, + onChangeBlock, + navRoot, + contentType, + } = props; const intl = useIntl(); const schema = blocksConfig.listing.blockSchema({ ...props, @@ -28,6 +35,7 @@ const ListingData = (props) => { block={block} navRoot={navRoot} contentType={contentType} + errors={blocksErrors} /> ); }; diff --git a/packages/volto/src/components/manage/Blocks/Maps/MapsSidebar.jsx b/packages/volto/src/components/manage/Blocks/Maps/MapsSidebar.jsx index 1bc2179f0b..977b7dc870 100644 --- a/packages/volto/src/components/manage/Blocks/Maps/MapsSidebar.jsx +++ b/packages/volto/src/components/manage/Blocks/Maps/MapsSidebar.jsx @@ -18,7 +18,8 @@ const messages = defineMessages({ }); const MapsSidebar = (props) => { - const { data, block, onChangeBlock, navRoot, contentType } = props; + const { data, block, blocksErrors, onChangeBlock, navRoot, contentType } = + props; const intl = useIntl(); const schema = MapsSchema({ ...props, intl }); @@ -44,6 +45,7 @@ const MapsSidebar = (props) => { block={block} navRoot={navRoot} contentType={contentType} + errors={blocksErrors} /> )} diff --git a/packages/volto/src/components/manage/Blocks/Search/SearchBlockEdit.jsx b/packages/volto/src/components/manage/Blocks/Search/SearchBlockEdit.jsx index 184ee3579b..56ac5769e4 100644 --- a/packages/volto/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +++ b/packages/volto/src/components/manage/Blocks/Search/SearchBlockEdit.jsx @@ -23,6 +23,7 @@ const messages = defineMessages({ const SearchBlockEdit = (props) => { const { block, + blocksErrors, onChangeBlock, data, selected, @@ -94,6 +95,7 @@ const SearchBlockEdit = (props) => { formData={data} navRoot={navRoot} contentType={contentType} + errors={blocksErrors} /> diff --git a/packages/volto/src/components/manage/Blocks/Teaser/Data.jsx b/packages/volto/src/components/manage/Blocks/Teaser/Data.jsx index f57bf762a5..69bb3db42a 100644 --- a/packages/volto/src/components/manage/Blocks/Teaser/Data.jsx +++ b/packages/volto/src/components/manage/Blocks/Teaser/Data.jsx @@ -31,8 +31,15 @@ const messages = defineMessages({ }); const TeaserData = (props) => { - const { block, blocksConfig, data, onChangeBlock, navRoot, contentType } = - props; + const { + block, + blocksConfig, + blocksErrors, + data, + onChangeBlock, + navRoot, + contentType, + } = props; const dispatch = useDispatch(); const intl = useIntl(); @@ -161,6 +168,7 @@ const TeaserData = (props) => { actionButton={data.overwrite && ActionButton} navRoot={navRoot} contentType={contentType} + errors={blocksErrors} /> ); }; diff --git a/packages/volto/src/components/manage/Blocks/ToC/Edit.jsx b/packages/volto/src/components/manage/Blocks/ToC/Edit.jsx index d9c68c6ca4..dc369719a6 100644 --- a/packages/volto/src/components/manage/Blocks/ToC/Edit.jsx +++ b/packages/volto/src/components/manage/Blocks/ToC/Edit.jsx @@ -29,6 +29,7 @@ class Edit extends Component { block={this.props.block} navRoot={this.props.navRoot} contentType={this.props.contentType} + errors={this.props.blocksErrors} /> diff --git a/packages/volto/src/components/manage/Blocks/Video/VideoSidebar.jsx b/packages/volto/src/components/manage/Blocks/Video/VideoSidebar.jsx index 1a5b110f44..bec7228200 100644 --- a/packages/volto/src/components/manage/Blocks/Video/VideoSidebar.jsx +++ b/packages/volto/src/components/manage/Blocks/Video/VideoSidebar.jsx @@ -18,7 +18,8 @@ const messages = defineMessages({ }); const VideoSidebar = (props) => { - const { data, block, onChangeBlock, navRoot, contentType } = props; + const { data, block, blocksErrors, onChangeBlock, navRoot, contentType } = + props; const intl = useIntl(); const schema = VideoBlockSchema({ ...props, intl }); @@ -44,6 +45,7 @@ const VideoSidebar = (props) => { block={block} navRoot={navRoot} contentType={contentType} + errors={blocksErrors} /> )} diff --git a/packages/volto/src/components/manage/Form/Form.jsx b/packages/volto/src/components/manage/Form/Form.jsx index 1ebdca28f7..8b108d7dde 100644 --- a/packages/volto/src/components/manage/Form/Form.jsx +++ b/packages/volto/src/components/manage/Form/Form.jsx @@ -12,6 +12,7 @@ import { FormValidation, getBlocksFieldname, getBlocksLayoutFieldname, + hasBlocksData, messages, } from '@plone/volto/helpers'; import aheadSVG from '@plone/volto/icons/ahead.svg'; @@ -527,30 +528,92 @@ class Form extends Component { }) : {}; - if (keys(errors).length > 0) { + let blocksErrors = {}; + + if (hasBlocksData(formData)) { + // Validate blocks + const blocks = this.state.formData[getBlocksFieldname(formData)]; + const blocksLayout = + this.state.formData[getBlocksLayoutFieldname(formData)]; + const defaultSchema = { + properties: {}, + fieldsets: [], + required: [], + }; + blocksLayout.items.forEach((block) => { + let blockSchema = + config.blocks.blocksConfig[blocks[block]['@type']].blockSchema || + defaultSchema; + if (typeof blockSchema === 'function') { + blockSchema = blockSchema({ + intl: this.props.intl, + formData: blocks[block], + }); + } + const blockErrors = FormValidation.validateFieldsPerFieldset({ + schema: blockSchema, + formData: blocks[block], + formatMessage: this.props.intl.formatMessage, + }); + if (keys(blockErrors).length > 0) { + blocksErrors = { + ...blocksErrors, + [block]: { ...blockErrors }, + }; + } + }); + } + + if (keys(errors).length > 0 || keys(blocksErrors).length > 0) { const activeIndex = FormValidation.showFirstTabWithErrors({ errors, schema: this.props.schema, }); - this.setState( - { - errors, - activeIndex, - }, - () => { - Object.keys(errors).forEach((err) => - toast.error( - , - ), - ); + + this.setState({ + errors: { + ...errors, + ...(!isEmpty(blocksErrors) && { blocks: blocksErrors }), }, - ); - // Changes the focus to the metadata tab in the sidebar if error - this.props.setSidebarTab(0); + activeIndex, + }); + + if (keys(errors).length > 0) { + // Changes the focus to the metadata tab in the sidebar if error + Object.keys(errors).forEach((err) => + toast.error( + , + ), + ); + this.props.setSidebarTab(0); + } else if (keys(blocksErrors).length > 0) { + const errorField = Object.entries( + Object.entries(blocksErrors)[0][1], + )[0][0]; + const errorMessage = Object.entries( + Object.entries(blocksErrors)[0][1], + )[0][1]; + toast.error( + , + ); + this.props.setSidebarTab(1); + this.props.setUIState({ + selected: Object.keys(blocksErrors)[0], + multiSelected: [], + hovered: null, + }); + } } else { // Get only the values that have been modified (Edit forms), send all in case that // it's an add form @@ -730,6 +793,8 @@ class Form extends Component { history={this.props.history} location={this.props.location} token={this.props.token} + errors={this.state.errors} + blocksErrors={this.state.errors.blocks} /> {this.state.isClient && this.state.sidebarMetadataIsAvailable && diff --git a/packages/volto/src/components/manage/Form/InlineForm.jsx b/packages/volto/src/components/manage/Form/InlineForm.jsx index 64bfb21946..f3998f434e 100644 --- a/packages/volto/src/components/manage/Form/InlineForm.jsx +++ b/packages/volto/src/components/manage/Form/InlineForm.jsx @@ -142,7 +142,6 @@ const InlineForm = (props) => { content={error.message} /> )} -
{map(defaultFieldset.fields, (field, index) => ( @@ -157,7 +156,7 @@ const InlineForm = (props) => { onChangeField(id, value, itemInfo); }} key={field} - error={errors[field]} + error={errors?.[block]?.[field] || {}} block={block} /> ))} @@ -166,7 +165,6 @@ const InlineForm = (props) => { )}
- {other.map((fieldset, index) => (
@@ -199,7 +197,7 @@ const InlineForm = (props) => { onChangeField(id, value); }} key={field} - error={errors[field]} + error={errors?.[block]?.[field] || {}} block={block} /> ))} diff --git a/packages/volto/src/config/index.js b/packages/volto/src/config/index.js index 9c7b625fcc..ddce0abd9f 100644 --- a/packages/volto/src/config/index.js +++ b/packages/volto/src/config/index.js @@ -32,6 +32,7 @@ import applyAddonConfiguration, { addonsInfo } from 'load-volto-addons'; import ConfigRegistry from '@plone/volto/registry'; import { getSiteAsyncPropExtender } from '@plone/volto/helpers'; +import { registerValidators } from './validation'; const host = process.env.HOST || 'localhost'; const port = process.env.PORT || '3000'; @@ -209,8 +210,9 @@ let config = { }, addonRoutes: [], addonReducers: {}, - slots: {}, components, + slots: {}, + utilities: {}, }; // The apiExpanders depends on a config of the object, so it's done here @@ -238,5 +240,8 @@ ConfigRegistry.addonRoutes = config.addonRoutes; ConfigRegistry.addonReducers = config.addonReducers; ConfigRegistry.components = config.components; ConfigRegistry.slots = config.slots; +ConfigRegistry.utilities = config.utilities; + +registerValidators(ConfigRegistry); applyAddonConfiguration(ConfigRegistry); diff --git a/packages/volto/src/config/validation.ts b/packages/volto/src/config/validation.ts new file mode 100644 index 0000000000..5a92e004e9 --- /dev/null +++ b/packages/volto/src/config/validation.ts @@ -0,0 +1,155 @@ +import { ConfigType } from '@plone/registry'; + +import { + minLengthValidator, + maxLengthValidator, + urlValidator, + emailValidator, + isNumberValidator, + maximumValidator, + minimumValidator, + isIntegerValidator, + maxItemsValidator, + minItemsValidator, + hasUniqueItemsValidator, + startEventDateRangeValidator, + endEventDateRangeValidator, + patternValidator, +} from '@plone/volto/helpers/FormValidation/validators'; + +const registerValidators = (config: ConfigType) => { + config.registerUtility({ + name: 'minLength', + type: 'validator', + dependencies: { fieldType: 'string' }, + method: minLengthValidator, + }); + + config.registerUtility({ + name: 'maxLength', + type: 'validator', + dependencies: { fieldType: 'string' }, + method: maxLengthValidator, + }); + + config.registerUtility({ + name: 'pattern', + type: 'validator', + dependencies: { fieldType: 'string' }, + method: patternValidator, + }); + + config.registerUtility({ + name: 'minLength', + type: 'validator', + dependencies: { fieldType: 'password' }, + method: minLengthValidator, + }); + + config.registerUtility({ + name: 'maxLength', + type: 'validator', + dependencies: { fieldType: 'password' }, + method: maxLengthValidator, + }); + + config.registerUtility({ + name: 'pattern', + type: 'validator', + dependencies: { fieldType: 'password' }, + method: patternValidator, + }); + + config.registerUtility({ + name: 'email', + type: 'validator', + dependencies: { widget: 'email' }, + method: emailValidator, + }); + + config.registerUtility({ + name: 'url', + type: 'validator', + dependencies: { widget: 'url' }, + method: urlValidator, + }); + + config.registerUtility({ + name: 'number', + type: 'validator', + dependencies: { fieldType: 'number' }, + method: isNumberValidator, + }); + + config.registerUtility({ + name: 'minimum', + type: 'validator', + dependencies: { fieldType: 'number' }, + method: minimumValidator, + }); + + config.registerUtility({ + name: 'maximum', + type: 'validator', + dependencies: { fieldType: 'number' }, + method: maximumValidator, + }); + + config.registerUtility({ + name: 'integer', + type: 'validator', + dependencies: { fieldType: 'integer' }, + method: isIntegerValidator, + }); + + config.registerUtility({ + name: 'minimum', + type: 'validator', + dependencies: { fieldType: 'integer' }, + method: minimumValidator, + }); + + config.registerUtility({ + name: 'maximum', + type: 'validator', + dependencies: { fieldType: 'integer' }, + method: maximumValidator, + }); + + config.registerUtility({ + name: 'maxItems', + type: 'validator', + dependencies: { fieldType: 'array' }, + method: maxItemsValidator, + }); + + config.registerUtility({ + name: 'minItems', + type: 'validator', + dependencies: { fieldType: 'array' }, + method: minItemsValidator, + }); + + config.registerUtility({ + name: 'uniqueItems', + type: 'validator', + dependencies: { fieldType: 'array' }, + method: hasUniqueItemsValidator, + }); + + config.registerUtility({ + name: 'dateRangeValidator', + type: 'validator', + dependencies: { behaviorName: 'plone.eventbasic', fieldName: 'start' }, + method: startEventDateRangeValidator, + }); + + config.registerUtility({ + name: 'dateRangeValidator', + type: 'validator', + dependencies: { behaviorName: 'plone.eventbasic', fieldName: 'end' }, + method: endEventDateRangeValidator, + }); +}; + +export { registerValidators }; diff --git a/packages/volto/src/helpers/FormValidation/FormValidation.jsx b/packages/volto/src/helpers/FormValidation/FormValidation.jsx index 95c470fa77..f423f49d5a 100644 --- a/packages/volto/src/helpers/FormValidation/FormValidation.jsx +++ b/packages/volto/src/helpers/FormValidation/FormValidation.jsx @@ -1,4 +1,4 @@ -import { map, uniq, keys, intersection, isEmpty } from 'lodash'; +import { map, keys, intersection, isEmpty } from 'lodash'; import { messages } from '../MessageLabels/MessageLabels'; import config from '@plone/volto/registry'; import { toast } from 'react-toastify'; @@ -11,7 +11,12 @@ import Toast from '@plone/volto/components/manage/Toast/Toast'; * @param {string | number} valueToCompare can compare '47' < 50 * @param {Function} intlFunc */ -const validationMessage = (isValid, criterion, valueToCompare, intlFunc) => +export const validationMessage = ( + isValid, + criterion, + valueToCompare, + intlFunc, +) => !isValid ? intlFunc(messages[criterion], { len: valueToCompare, @@ -19,142 +24,7 @@ const validationMessage = (isValid, criterion, valueToCompare, intlFunc) => : null; /** - * Returns if based on the criterion the value is lower or equal - * @param {string | number} value can compare '47' < 50 - * @param {string | number} valueToCompare can compare '47' < 50 - * @param {string} maxCriterion - * @param {Function} intlFunc - */ -const isMaxPropertyValid = (value, valueToCompare, maxCriterion, intlFunc) => { - const isValid = valueToCompare !== undefined ? value <= valueToCompare : true; - return validationMessage(isValid, maxCriterion, valueToCompare, intlFunc); -}; - -/** - * Returns if based on the criterion the value is higher or equal - * @param {string | number} value can compare '47' < 50 - * @param {string | number} valueToCompare can compare '47' < 50 - * @param {string} minCriterion - * @param {Function} intlFunc - */ -const isMinPropertyValid = (value, valueToCompare, minCriterion, intlFunc) => { - const isValid = valueToCompare !== undefined ? value >= valueToCompare : true; - return validationMessage(isValid, minCriterion, valueToCompare, intlFunc); -}; - -const widgetValidation = { - email: { - isValidEmail: (emailValue, emailObj, intlFunc) => { - // Email Regex taken from from WHATWG living standard: - // https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type=email) - const emailRegex = - /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; - const isValid = emailRegex.test(emailValue); - return !isValid ? intlFunc(messages.isValidEmail) : null; - }, - minLength: (emailValue, emailObj, intlFunc) => - isMinPropertyValid( - emailValue.length, - emailObj.minLength, - 'minLength', - intlFunc, - ), - maxLength: (emailValue, emailObj, intlFunc) => - isMaxPropertyValid( - emailValue.length, - emailObj.maxLength, - 'maxLength', - intlFunc, - ), - }, - url: { - isValidURL: (urlValue, urlObj, intlFunc) => { - var urlRegex = new RegExp( - '^(https?:\\/\\/)?' + // validate protocol - '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // validate domain name - '((\\d{1,3}\\.){3}\\d{1,3}))|' + // validate OR ip (v4) address - '(localhost)' + // validate OR localhost address - '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // validate port and path - '(\\?[;&a-z\\d%_.~+=-]*)?' + // validate query string - '(\\#[-a-z\\d_]*)?$', // validate fragment locator - 'i', - ); - const isValid = urlRegex.test(urlValue); - return !isValid ? intlFunc(messages.isValidURL) : null; - }, - minLength: (urlValue, urlObj, intlFunc) => - isMinPropertyValid( - urlValue.length, - urlObj.minLength, - 'minLength', - intlFunc, - ), - maxLength: (urlValue, urlObj, intlFunc) => - isMaxPropertyValid( - urlValue.length, - urlObj.maxLength, - 'maxLength', - intlFunc, - ), - }, - password: { - minLength: (passwordValue, passwordObj, intlFunc) => - isMinPropertyValid( - passwordValue.length, - passwordObj.minLength, - 'minLength', - intlFunc, - ), - maxLength: (passwordValue, passwordObj, intlFunc) => - isMaxPropertyValid( - passwordValue.length, - passwordObj.maxLength, - 'maxLength', - intlFunc, - ), - }, - string: { - minLength: (value, itemObj, intlFunc) => - isMinPropertyValid( - value.length, - itemObj.minLength, - 'minLength', - intlFunc, - ), - maxLength: (value, itemObj, intlFunc) => - isMaxPropertyValid( - value.length, - itemObj.maxLength, - 'maxLength', - intlFunc, - ), - }, - number: { - isNumber: (value, itemObj, intlFunc) => { - const floatRegex = /^[+-]?\d+(\.\d+)?$/; - const isValid = !isNaN(value) && floatRegex.test(value); - return !isValid ? intlFunc(messages.isNumber) : null; - }, - minimum: (value, itemObj, intlFunc) => - isMinPropertyValid(value, itemObj.minimum, 'minimum', intlFunc), - maximum: (value, itemObj, intlFunc) => - isMaxPropertyValid(value, itemObj.maximum, 'maximum', intlFunc), - }, - integer: { - isInteger: (value, itemObj, intlFunc) => { - const intRegex = /^-?[0-9]+$/; - const isValid = !isNaN(value) && intRegex.test(value); - return !isValid ? intlFunc(messages.isInteger) : null; - }, - minimum: (value, itemObj, intlFunc) => - isMinPropertyValid(value, itemObj.minimum, 'minimum', intlFunc), - maximum: (value, itemObj, intlFunc) => - isMaxPropertyValid(value, itemObj.maximum, 'maximum', intlFunc), - }, -}; - -/** - * The string that comes my not be a valid JSON + * The string that comes might not be a valid JSON * @param {string} requestItem */ export const tryParseJSON = (requestItem) => { @@ -171,24 +41,6 @@ export const tryParseJSON = (requestItem) => { return resultObj; }; -/** - * Returns errors if obj has unique Items - * @param {Object} field - * @param {*} fieldData - * @returns {Object[string]} - list of errors - */ -const hasUniqueItems = (field, fieldData, formatMessage) => { - const errors = []; - if ( - field.uniqueItems && - fieldData && - uniq(fieldData).length !== fieldData.length - ) { - errors.push(formatMessage(messages.uniqueItems)); - } - return errors; -}; - /** * If required fields are undefined, return list of errors * @returns {Object[string]} - list of errors @@ -252,35 +104,122 @@ const validateFieldsPerFieldset = ( touchedField, ); - map(schema.properties, (field, fieldId) => { - const fieldWidgetType = field.widget || field.type; - const widgetValidationCriteria = widgetValidation[fieldWidgetType] - ? Object.keys(widgetValidation[fieldWidgetType]) - : []; - let fieldData = formData[fieldId]; - // test each criterion ex maximum, isEmail, isUrl, maxLength etc - const fieldErrors = widgetValidationCriteria + function checkFieldErrors(fieldValidationCriteria, field, fieldData) { + return fieldValidationCriteria .map((widgetCriterion) => { const errorMessage = fieldData === undefined || fieldData === null ? null - : widgetValidation[fieldWidgetType][widgetCriterion]( - fieldData, + : widgetCriterion.method({ + value: fieldData, field, + formData, formatMessage, - ); + }); return errorMessage; }) .filter((item) => !!item); + } + + Object.entries(schema.properties).forEach(([fieldId, field]) => { + let fieldData = formData[fieldId]; + + // Validation per specific validator set (format property) + const hasSpecificValidator = + field.widgetOptions?.frontendOptions?.format || field.format; + let specificFieldErrors = []; + if (hasSpecificValidator) { + const specificValidationCriteria = config.getUtilities({ + type: 'validator', + dependencies: { format: hasSpecificValidator }, + }); + + specificFieldErrors = checkFieldErrors( + specificValidationCriteria, + field, + fieldData, + ); + } + + // Validation per field type + const fieldType = field.type || 'string'; // defaults to string + const fieldTypeValidationCriteria = config.getUtilities({ + type: 'validator', + dependencies: { fieldType }, + }); + + const fieldErrors = checkFieldErrors( + fieldTypeValidationCriteria, + field, + fieldData, + ); + + // Validation per field widget + const widgetName = + field.widgetOptions?.frontendOptions?.widget || field.widget || ''; + + let widgetErrors = []; + if (widgetName) { + const widgetNameValidationCriteria = config.getUtilities({ + type: 'validator', + dependencies: { widget: widgetName }, + }); + + widgetErrors = checkFieldErrors( + widgetNameValidationCriteria, + field, + fieldData, + ); + } + + // Validation per specific behavior and field name (for content types) + const behaviorName = field.behavior; + let perBehaviorFieldErrors = []; + if (behaviorName) { + const specificPerBehaviorFieldValidationCriteria = config.getUtilities({ + type: 'validator', + dependencies: { behaviorName, fieldName: fieldId }, + }); + + perBehaviorFieldErrors = checkFieldErrors( + specificPerBehaviorFieldValidationCriteria, + field, + fieldData, + ); + } + + // Validation per block type validator (for blocks) + const blockType = formData['@type']; + let blockTypeFieldErrors = []; + if (blockType) { + const blockTypeFieldValidationCriteria = config.getUtilities({ + type: 'validator', + dependencies: { blockType, fieldName: fieldId }, + }); + + blockTypeFieldErrors = checkFieldErrors( + blockTypeFieldValidationCriteria, + field, + fieldData, + ); + } - const uniqueErrors = hasUniqueItems(field, fieldData, formatMessage); - const mergedErrors = [...fieldErrors, ...uniqueErrors]; + const mergedErrors = [ + ...specificFieldErrors, + ...fieldErrors, + ...widgetErrors, + ...perBehaviorFieldErrors, + ...blockTypeFieldErrors, + ]; if (mergedErrors.length > 0) { errors[fieldId] = [ ...(errors[fieldId] || []), + ...specificFieldErrors, ...fieldErrors, - ...uniqueErrors, + ...widgetErrors, + ...perBehaviorFieldErrors, + ...blockTypeFieldErrors, ]; } }); diff --git a/packages/volto/src/helpers/FormValidation/FormValidation.test.js b/packages/volto/src/helpers/FormValidation/FormValidation.test.js index 9f97c6849a..b5c40d4dd2 100644 --- a/packages/volto/src/helpers/FormValidation/FormValidation.test.js +++ b/packages/volto/src/helpers/FormValidation/FormValidation.test.js @@ -1,5 +1,7 @@ import FormValidation from './FormValidation'; import { messages } from '../MessageLabels/MessageLabels'; +import config from '@plone/volto/registry'; +import { urlValidator } from './validators'; const schema = { properties: { @@ -54,7 +56,7 @@ describe('FormValidation', () => { expect(FormValidation.validateFieldsPerFieldset()).toEqual({}); }); - it('validates missing required', () => { + it('required - validates missing', () => { expect( FormValidation.validateFieldsPerFieldset({ schema, @@ -66,7 +68,7 @@ describe('FormValidation', () => { }); }); - it('do not treat 0 as missing required value', () => { + it('required - do not treat 0 as missing required value', () => { let newSchema = { ...schema, properties: { @@ -98,7 +100,7 @@ describe('FormValidation', () => { ).toEqual({}); }); - it('validates incorrect email', () => { + it('email - validates incorrect', () => { expect( FormValidation.validateFieldsPerFieldset({ schema, @@ -110,7 +112,7 @@ describe('FormValidation', () => { }); }); - it('validates correct email', () => { + it('email - validates', () => { formData.email = 'test@domain.name'; expect( FormValidation.validateFieldsPerFieldset({ @@ -120,7 +122,8 @@ describe('FormValidation', () => { }), ).toEqual({}); }); - it('validates incorrect url', () => { + + it('url - validates incorrect url', () => { formData.url = 'foo'; expect( FormValidation.validateFieldsPerFieldset({ @@ -130,7 +133,8 @@ describe('FormValidation', () => { }), ).toEqual({ url: [messages.isValidURL.defaultMessage] }); }); - it('validates url', () => { + + it('url - validates', () => { formData.url = 'https://plone.org/'; expect( FormValidation.validateFieldsPerFieldset({ @@ -140,7 +144,8 @@ describe('FormValidation', () => { }), ).toEqual({}); }); - it('validates url with ip', () => { + + it('url - validates url with ip', () => { formData.url = 'http://127.0.0.1:8080/Plone'; expect( FormValidation.validateFieldsPerFieldset({ @@ -150,7 +155,8 @@ describe('FormValidation', () => { }), ).toEqual({}); }); - it('validates url with localhost', () => { + + it('url - validates url with localhost', () => { formData.url = 'http://localhost:8080/Plone'; expect( FormValidation.validateFieldsPerFieldset({ @@ -160,5 +166,827 @@ describe('FormValidation', () => { }), ).toEqual({}); }); + + it('widget - validator from block - Fails', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Default field', + description: '', + widget: 'url', + }, + }, + required: [], + }; + + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 'asd', + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.isValidURL.defaultMessage], + }); + }); + + it('type + widget - validator from block - Fails', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Default field', + description: '', + type: 'customfieldtype', + widget: 'url', + }, + }, + required: [], + }; + config.registerUtility({ + type: 'validator', + name: 'alwaysFail', + dependencies: { fieldType: 'customfieldtype' }, + method: () => 'Fails', + }); + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 'asd', + }, + formatMessage, + }), + ).toEqual({ + customField: ['Fails', messages.isValidURL.defaultMessage], + }); + }); + + it('widget - validator from content type set - Fails', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Default field', + description: '', + widgetOptions: { + frontendOptions: { + widget: 'url', + }, + }, + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 'asd', + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.isValidURL.defaultMessage], + }); + }); + + it('string - min lenght', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'password', + description: '', + minLength: '8', + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 'asd', + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.minLength.defaultMessage], + }); + }); + + it('string - max lenght', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'password', + description: '', + maxLength: '8', + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 'asdasdasdasdasd', + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.maxLength.defaultMessage], + }); + }); + + it('string - pattern - Fail', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'password', + description: '', + pattern: '^[a-zA-Z0-9]*$', + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 'as#', + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.pattern.defaultMessage], + }); + }); + + it('string - pattern - Succeeds', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'password', + description: '', + pattern: '^[a-zA-Z0-9]*$', + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 'asasd', + }, + formatMessage, + }), + ).toEqual({}); + }); + + it('number - isNumber - fails (not string|number as number)', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Number field', + type: 'number', + description: '', + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + //since 'number' can accept digits in string & number format hence testing it with an alphabet + customField: 'n', + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.isNumber.defaultMessage], + }); + }); + + it('number - isNumber - as string', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Number field', + type: 'number', + description: '', + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + //since 'number' can accept digits in string & number format hence testing it with an alphabet + customField: '1', + }, + formatMessage, + }), + ).toEqual({}); + }); + + it('number - isNumber - as number', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Number field', + type: 'number', + description: '', + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + //since 'number' can accept digits in string & number format hence testing it with an alphabet + customField: 1, + }, + formatMessage, + }), + ).toEqual({}); + }); + + it('number - minimum', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Number field', + type: 'number', + description: '', + minimum: 8, + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 1, + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.minimum.defaultMessage], + }); + }); + + it('number - maximum', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Number field', + type: 'number', + description: '', + maximum: 8, + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 10, + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.maximum.defaultMessage], + }); + }); + + it('integer - isInteger', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Integer field', + type: 'integer', + description: '', + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 1.5, + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.isInteger.defaultMessage], + }); + }); + + it('integer - minimum', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Integer field', + type: 'integer', + description: '', + minimum: 8, + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 1, + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.minimum.defaultMessage], + }); + }); + + it('integer - maximum', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Integer field', + type: 'integer', + description: '', + maximum: 8, + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 10, + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.maximum.defaultMessage], + }); + }); + + it('password - min lenght', () => { + let newSchema = { + ...schema, + properties: { + ...schema.properties, + password: { + title: 'password', + type: 'password', + description: '', + minLength: '8', + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { username: 'test username', password: 'asd' }, + formatMessage, + }), + ).toEqual({ + password: [messages.minLength.defaultMessage], + }); + }); + + it('password - max lenght', () => { + let newSchema = { + ...schema, + properties: { + ...schema.properties, + password: { + title: 'password', + type: 'password', + description: '', + maxLength: '8', + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { username: 'test username', password: 'asdasdasdasdasd' }, + formatMessage, + }), + ).toEqual({ + password: [messages.maxLength.defaultMessage], + }); + }); + + it('array - maxItems', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Array field', + type: 'array', + description: '', + maxItems: 1, + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: [1, 2], + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.maxItems.defaultMessage], + }); + }); + + it('array - minItems', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Array field', + type: 'array', + description: '', + minItems: 3, + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: [1], + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.minItems.defaultMessage], + }); + }); + + it('array - uniqueItems', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Array field', + type: 'array', + description: '', + uniqueItems: true, + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: [1, 1], + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.uniqueItems.defaultMessage], + }); + }); + + it('array - uniqueItems - false', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Array field', + type: 'array', + description: '', + uniqueItems: false, + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: [1, 1], + }, + formatMessage, + }), + ).toEqual({}); + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: [1], + }, + formatMessage, + }), + ).toEqual({}); + }); + + it('format - specific validator set - Errors', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Default field', + description: '', + format: 'url', + }, + }, + required: [], + }; + config.registerUtility({ + type: 'validator', + name: 'url', + dependencies: { format: 'url' }, + method: urlValidator, + }); + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 'foo', + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.isValidURL.defaultMessage], + }); + }); + + it('format - specific validator set - Succeeds', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Default field', + description: '', + format: 'url', + }, + }, + required: [], + }; + config.registerUtility({ + type: 'validator', + name: 'url', + dependencies: { format: 'url' }, + method: urlValidator, + }); + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 'https://plone.org/', + }, + formatMessage, + }), + ).toEqual({}); + }); + + it('format - specific validator from content type set - Fails', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Default field', + description: '', + widgetOptions: { + frontendOptions: { + format: 'url', + }, + }, + }, + }, + required: [], + }; + config.registerUtility({ + type: 'validator', + name: 'url', + dependencies: { format: 'url' }, + method: urlValidator, + }); + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 'asdasd', + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.isValidURL.defaultMessage], + }); + }); + + it('format - specific validator from content type set - Succeeds', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Default field', + description: '', + widgetOptions: { + frontendOptions: { + format: 'url', + }, + }, + }, + }, + required: [], + }; + config.registerUtility({ + type: 'validator', + name: 'url', + dependencies: { format: 'url' }, + method: urlValidator, + }); + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 'https://plone.org/', + }, + formatMessage, + }), + ).toEqual({}); + }); + + it('behavior + fieldName - Fails', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + behavior: 'plone.eventbasic', + title: 'Default field', + description: '', + }, + }, + required: [], + }; + config.registerUtility({ + type: 'validator', + name: 'url', + dependencies: { + behaviorName: 'plone.eventbasic', + fieldName: 'customField', + format: 'url', + }, + method: urlValidator, + }); + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + username: 'test username', + customField: 'asd', + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.isValidURL.defaultMessage], + }); + }); + + it('behavior + fieldName - start date in Event - Fails', () => { + let contentTypeSchema = { + properties: { + ...schema.properties, + start: { + behavior: 'plone.eventbasic', + type: 'string', + title: 'Start date', + description: '', + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: contentTypeSchema, + formData: { + start: '2024-08-01T11:00:00+00:00', + end: '2024-04-01T11:00:00+00:00', + }, + formatMessage, + }), + ).toEqual({ + start: [messages.startEventRange.defaultMessage], + }); + }); + + it('behavior + fieldName - end date in Event - Fails', () => { + let contentTypeSchema = { + properties: { + ...schema.properties, + end: { + behavior: 'plone.eventbasic', + type: 'string', + title: 'End date', + description: '', + }, + }, + required: [], + }; + expect( + FormValidation.validateFieldsPerFieldset({ + schema: contentTypeSchema, + formData: { + start: '2024-08-01T11:00:00+00:00', + end: '2024-04-01T11:00:00+00:00', + }, + formatMessage, + }), + ).toEqual({ + end: [messages.endEventRange.defaultMessage], + }); + }); + + it('block - per block type and fieldID specific - Fails', () => { + let newSchema = { + properties: { + ...schema.properties, + customField: { + title: 'Default field', + description: '', + }, + }, + required: [], + }; + config.registerUtility({ + type: 'validator', + dependencies: { blockType: 'slider', fieldName: 'customField' }, + method: urlValidator, + }); + expect( + FormValidation.validateFieldsPerFieldset({ + schema: newSchema, + formData: { + '@type': 'slider', + username: 'test username', + customField: 'asd', + }, + formatMessage, + }), + ).toEqual({ + customField: [messages.isValidURL.defaultMessage], + }); + }); }); }); diff --git a/packages/volto/src/helpers/FormValidation/validators.ts b/packages/volto/src/helpers/FormValidation/validators.ts new file mode 100644 index 0000000000..82e927b927 --- /dev/null +++ b/packages/volto/src/helpers/FormValidation/validators.ts @@ -0,0 +1,203 @@ +import { validationMessage } from './FormValidation'; +import { messages } from '@plone/volto/helpers/MessageLabels/MessageLabels'; + +type MinMaxValidator = { + value: string | number; + fieldSpec: string | number; + criterion: string; + formatMessage: Function; +}; + +type Validator = { + value: string; + field: Record; + formData: any; + formatMessage: Function; +}; + +export const isMaxPropertyValid = ({ + value, + fieldSpec, + criterion, + formatMessage, +}: MinMaxValidator) => { + const isValid = fieldSpec !== undefined ? value <= fieldSpec : true; + return validationMessage(isValid, criterion, fieldSpec, formatMessage); +}; + +export const isMinPropertyValid = ({ + value, + fieldSpec, + criterion, + formatMessage, +}: MinMaxValidator) => { + const isValid = fieldSpec !== undefined ? value >= fieldSpec : true; + return validationMessage(isValid, criterion, fieldSpec, formatMessage); +}; + +export const minLengthValidator = ({ + value, + field, + formatMessage, +}: Validator) => + isMinPropertyValid({ + value: value.length, + fieldSpec: field.minLength, + criterion: 'minLength', + formatMessage, + }); + +export const maxLengthValidator = ({ + value, + field, + formatMessage, +}: Validator) => + isMaxPropertyValid({ + value: value.length, + fieldSpec: field.maxLength, + criterion: 'maxLength', + formatMessage, + }); + +export const urlValidator = ({ value, formatMessage }: Validator) => { + const urlRegex = new RegExp( + '^(https?:\\/\\/)?' + // validate protocol + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // validate domain name + '((\\d{1,3}\\.){3}\\d{1,3}))|' + // validate OR ip (v4) address + '(localhost)' + // validate OR localhost address + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // validate port and path + '(\\?[;&a-z\\d%_.~+=-]*)?' + // validate query string + '(\\#[-a-z\\d_]*)?$', // validate fragment locator + 'i', + ); + const isValid = urlRegex.test(value); + return !isValid ? formatMessage(messages.isValidURL) : null; +}; + +export const emailValidator = ({ value, formatMessage }: Validator): string => { + // Email Regex taken from from WHATWG living standard: + // https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type=email) + const emailRegex = + /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; + const isValid = emailRegex.test(value); + return !isValid ? formatMessage(messages.isValidEmail) : null; +}; + +export const isNumberValidator = ({ value, formatMessage }: Validator) => { + const isNumeric = (string: string | number) => Number.isFinite(+string); + const floatRegex = /^[+-]?\d+(\.\d+)?$/; + const isValid = isNumeric(value) && floatRegex.test(value); + // const isValid = + // typeof value === 'string' && !isNaN(+value) && floatRegex.test(value); + return !isValid ? formatMessage(messages.isNumber) : null; +}; + +export const minimumValidator = ({ value, field, formatMessage }: Validator) => + isMinPropertyValid({ + value, + fieldSpec: field.minimum, + criterion: 'minimum', + formatMessage, + }); + +export const maximumValidator = ({ value, field, formatMessage }: Validator) => + isMaxPropertyValid({ + value, + fieldSpec: field.maximum, + criterion: 'maximum', + formatMessage, + }); + +export const isIntegerValidator = ({ value, formatMessage }: Validator) => { + const isNumeric = (string: string | number) => Number.isFinite(+string); + const intRegex = /^-?[0-9]+$/; + const isValid = isNumeric(value) && intRegex.test(value); + return !isValid ? formatMessage(messages.isInteger) : null; +}; + +export const hasUniqueItemsValidator = ({ + value, + field, + formatMessage, +}: Validator) => { + if (!field.uniqueItems) { + return null; + } + const isValid = + field.uniqueItems && + value && + // unique items + [...new Set(value)].length === value.length; + return !isValid ? formatMessage(messages.uniqueItems) : null; +}; + +export const startEventDateRangeValidator = ({ + value, + field, + formData, + formatMessage, +}: Validator) => { + const isValid = + value && formData.end && new Date(value) < new Date(formData.end); + return !isValid + ? formatMessage(messages.startEventRange, { + endDateValueOrEndFieldName: formData.end || 'end', + }) + : null; +}; + +export const endEventDateRangeValidator = ({ + value, + field, + formData, + formatMessage, +}: Validator) => { + const isValid = + value && formData.start && new Date(value) > new Date(formData.start); + return !isValid + ? formatMessage(messages.endEventRange, { + startDateValueOrStartFieldName: formData.start || 'start', + }) + : null; +}; + +export const patternValidator = ({ + value, + field, + formatMessage, +}: Validator) => { + if (!field.pattern) { + return null; + } + const regex = new RegExp(field.pattern); + const isValid = regex.test(value); + return !isValid ? formatMessage(messages.pattern) : null; +}; + +export const maxItemsValidator = ({ + value, + field, + formatMessage, +}: Validator) => { + if (!field.maxItems) { + return null; + } + const isValid = Array.isArray(value) && value.length <= field.maxItems; + return !isValid + ? formatMessage(messages.maxItems, { maxItems: field.maxItems }) + : null; +}; + +export const minItemsValidator = ({ + value, + field, + formatMessage, +}: Validator) => { + if (!field.minItems) { + return null; + } + const isValid = Array.isArray(value) && value.length >= field.minItems; + return !isValid + ? formatMessage(messages.minItems, { minItems: field.minItems }) + : null; +}; diff --git a/packages/volto/src/helpers/MessageLabels/MessageLabels.js b/packages/volto/src/helpers/MessageLabels/MessageLabels.js index a6483fe4c9..7549e09043 100644 --- a/packages/volto/src/helpers/MessageLabels/MessageLabels.js +++ b/packages/volto/src/helpers/MessageLabels/MessageLabels.js @@ -375,4 +375,32 @@ export const messages = defineMessages({ id: 'fileTooLarge', defaultMessage: 'This website does not accept files larger than {limit}', }, + blocksFieldsErrorTitle: { + id: 'blocksFieldsErrorTitle', + defaultMessage: 'Error in the block field {errorField}.', + }, + startEventRange: { + id: 'Event start date must be on or before {endDateValueOrEndFieldName}', + defaultMessage: + 'Event start date must be on or before {endDateValueOrEndFieldName}', + }, + endEventRange: { + id: 'Event end date must be on or after {startDateValueOrStartFieldName}', + defaultMessage: + 'Event end date must be on or after {startDateValueOrStartFieldName}', + }, + pattern: { + id: 'The value does not match the pattern {pattern}', + defaultMessage: 'The value does not match the pattern {pattern}', + }, + maxItems: { + id: 'The number of items must be less than or equal to {maxItems}', + defaultMessage: + 'The number of items must be less than or equal to {maxItems}', + }, + minItems: { + id: 'The number of items must be greater than or equal to {minItems}', + defaultMessage: + 'The number of items must be greater than or equal to {minItems}', + }, }); diff --git a/packages/volto/test-setup-config.jsx b/packages/volto/test-setup-config.jsx index 3f9d1dc8d8..b42bd47dc4 100644 --- a/packages/volto/test-setup-config.jsx +++ b/packages/volto/test-setup-config.jsx @@ -23,6 +23,7 @@ import { } from '@plone/volto/config/ControlPanels'; import ListingBlockSchema from '@plone/volto/components/manage/Blocks/Listing/schema'; +import { registerValidators } from '@plone/volto/config/validation'; config.set('settings', { apiPath: 'http://localhost:8080/Plone', @@ -153,9 +154,15 @@ config.set('components', { component: (props) => Image component mock, }, }); + +config.set('utilities', {}); + config.set('experimental', { addBlockButton: { enabled: false, }, }); + config.set('slots', {}); + +registerValidators(config); diff --git a/packages/volto/theme/themes/pastanaga/extras/sidebar.less b/packages/volto/theme/themes/pastanaga/extras/sidebar.less index 633cc070b2..5931049d89 100644 --- a/packages/volto/theme/themes/pastanaga/extras/sidebar.less +++ b/packages/volto/theme/themes/pastanaga/extras/sidebar.less @@ -518,6 +518,10 @@ padding-left: 0.5rem; text-overflow: ellipsis; white-space: nowrap; + + &.errored { + color: @red; + } } &.disable-interaction { From 9ec1cf8298a6a1b4b2cdab925a43951ba3725a79 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Tue, 30 Jul 2024 18:40:49 +0200 Subject: [PATCH 08/58] Release @plone/types 1.0.0-alpha.19 --- packages/types/CHANGELOG.md | 7 +++++++ packages/types/news/6161.feature | 2 -- packages/types/package.json | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 packages/types/news/6161.feature diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 8078129cb3..2b31ad63ca 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -8,6 +8,13 @@ +## 1.0.0-alpha.19 (2024-07-30) + +### Feature + +- Added `errors` shape to the `BlockEditProps`. + Added typings for `Utilities` registry. @sneridagh [#6161](https://github.com/plone/volto/issues/6161) + ## 1.0.0-alpha.18 (2024-07-26) ### Feature diff --git a/packages/types/news/6161.feature b/packages/types/news/6161.feature deleted file mode 100644 index c78c615f1d..0000000000 --- a/packages/types/news/6161.feature +++ /dev/null @@ -1,2 +0,0 @@ -Added `errors` shape to the `BlockEditProps`. -Added typings for `Utilities` registry. @sneridagh diff --git a/packages/types/package.json b/packages/types/package.json index 4e0cb5645b..b4e06bc361 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -9,7 +9,7 @@ ], "funding": "https://github.com/sponsors/plone", "license": "MIT", - "version": "1.0.0-alpha.18", + "version": "1.0.0-alpha.19", "repository": { "type": "git", "url": "https://github.com/plone/volto.git" From 47dc3680a6044ad91bae13c81c6e0db7f03d30f2 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Tue, 30 Jul 2024 18:41:42 +0200 Subject: [PATCH 09/58] Release @plone/registry 1.8.0 --- packages/registry/CHANGELOG.md | 10 ++++++++++ packages/registry/news/6109.documentation | 1 - packages/registry/news/6161.feature | 1 - packages/registry/package.json | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) delete mode 100644 packages/registry/news/6109.documentation delete mode 100644 packages/registry/news/6161.feature diff --git a/packages/registry/CHANGELOG.md b/packages/registry/CHANGELOG.md index 790eefd76f..bfc4bc4705 100644 --- a/packages/registry/CHANGELOG.md +++ b/packages/registry/CHANGELOG.md @@ -8,6 +8,16 @@ +## 1.8.0 (2024-07-30) + +### Feature + +- Added `Utilities` registry for `registerUtility`, `getUtility`, and `getUtilities`. @sneridagh [#6161](https://github.com/plone/volto/issues/6161) + +### Documentation + +- Changed a few typos within documentation, README's and comments. @FritzHoing [#6109](https://github.com/plone/volto/issues/6109) + ## 1.7.0 (2024-06-26) ### Feature diff --git a/packages/registry/news/6109.documentation b/packages/registry/news/6109.documentation deleted file mode 100644 index fce76c0c1f..0000000000 --- a/packages/registry/news/6109.documentation +++ /dev/null @@ -1 +0,0 @@ -Changed a few typos within documentation, README's and comments. @FritzHoing \ No newline at end of file diff --git a/packages/registry/news/6161.feature b/packages/registry/news/6161.feature deleted file mode 100644 index 005ffce826..0000000000 --- a/packages/registry/news/6161.feature +++ /dev/null @@ -1 +0,0 @@ -Added `Utilities` registry for `registerUtility`, `getUtility`, and `getUtilities`. @sneridagh diff --git a/packages/registry/package.json b/packages/registry/package.json index dd781a679d..df81edb35b 100644 --- a/packages/registry/package.json +++ b/packages/registry/package.json @@ -9,7 +9,7 @@ ], "funding": "https://github.com/sponsors/plone", "license": "MIT", - "version": "1.7.0", + "version": "1.8.0", "repository": { "type": "git", "url": "https://github.com/plone/volto.git" From 19e0ea8c044140e54b4e922ea3079941e53f6c22 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Tue, 30 Jul 2024 18:52:57 +0200 Subject: [PATCH 10/58] Fix build types --- packages/volto/tsconfig.declarations.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/volto/tsconfig.declarations.json b/packages/volto/tsconfig.declarations.json index b91debf6c8..7cf969f638 100644 --- a/packages/volto/tsconfig.declarations.json +++ b/packages/volto/tsconfig.declarations.json @@ -9,9 +9,10 @@ "esModuleInterop": true, "jsx": "react-jsx", "preserveSymlinks": true, + "downlevelIteration": true, "paths": { - "@plone/volto/*": ["./src/*"], + "@plone/volto/*": ["./src/*"] } }, - "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.tsx"], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.tsx"] } From d056fcc4450154be030e77123babb384b858a8d3 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Tue, 30 Jul 2024 18:53:47 +0200 Subject: [PATCH 11/58] Release 18.0.0-alpha.42 --- docs/source/release-notes/index.md | 42 +++++++++++++++++++ packages/volto/CHANGELOG.md | 42 +++++++++++++++++++ packages/volto/news/5002.bugfix | 1 - packages/volto/news/5338.bugfix | 1 - packages/volto/news/5464.bugfix | 1 - packages/volto/news/6109.documentation | 1 - packages/volto/news/6126.bugfix | 1 - packages/volto/news/6153.internal | 1 - packages/volto/news/6156.bugfix | 2 - packages/volto/news/6157.bugfix | 4 -- packages/volto/news/6161.breaking | 5 --- packages/volto/news/6162.breaking | 1 - packages/volto/news/6174.documentation | 1 - packages/volto/news/6175.documentation | 1 - packages/volto/news/6175.internal | 1 - packages/volto/news/6184.documentation | 1 - packages/volto/news/6188.documentation | 1 - packages/volto/news/6189.breaking | 1 - packages/volto/news/6189.bugfix | 1 - packages/volto/news/6192.documentation | 1 - packages/volto/news/6193.documentation | 1 - packages/volto/news/6217.internal | 1 - packages/volto/package.json | 2 +- packages/volto/types/components/index.d.ts | 1 - .../manage/Blocks/Block/Order/Order.d.ts | 3 +- packages/volto/types/config/Widgets.d.ts | 4 +- packages/volto/types/config/validation.d.ts | 3 ++ .../FormValidation/FormValidation.d.ts | 1 + .../helpers/FormValidation/validators.d.ts | 29 +++++++++++++ .../helpers/MessageLabels/MessageLabels.d.ts | 36 ++++++++++++++++ 30 files changed, 158 insertions(+), 33 deletions(-) delete mode 100644 packages/volto/news/5002.bugfix delete mode 100644 packages/volto/news/5338.bugfix delete mode 100644 packages/volto/news/5464.bugfix delete mode 100644 packages/volto/news/6109.documentation delete mode 100644 packages/volto/news/6126.bugfix delete mode 100644 packages/volto/news/6153.internal delete mode 100644 packages/volto/news/6156.bugfix delete mode 100644 packages/volto/news/6157.bugfix delete mode 100644 packages/volto/news/6161.breaking delete mode 100644 packages/volto/news/6162.breaking delete mode 100644 packages/volto/news/6174.documentation delete mode 100644 packages/volto/news/6175.documentation delete mode 100644 packages/volto/news/6175.internal delete mode 100644 packages/volto/news/6184.documentation delete mode 100644 packages/volto/news/6188.documentation delete mode 100644 packages/volto/news/6189.breaking delete mode 100644 packages/volto/news/6189.bugfix delete mode 100644 packages/volto/news/6192.documentation delete mode 100644 packages/volto/news/6193.documentation delete mode 100644 packages/volto/news/6217.internal create mode 100644 packages/volto/types/config/validation.d.ts create mode 100644 packages/volto/types/helpers/FormValidation/validators.d.ts diff --git a/docs/source/release-notes/index.md b/docs/source/release-notes/index.md index 8fea32ad54..91db671d96 100644 --- a/docs/source/release-notes/index.md +++ b/docs/source/release-notes/index.md @@ -17,6 +17,48 @@ myst: +## 18.0.0-alpha.42 (2024-07-30) + +### Breaking + +- Add foundations for extensible validation in forms. @sneridagh + + Breaking: + `packages/volto/src/helpers/FormValidation/FormValidation.jsx` has been heavily refactored. + If you shadowed this component in your project or add-on, you should review it and update it accordingly. [#6161](https://github.com/plone/volto/issues/6161) +- Remove `react-share` library and `SocialSharing` component @sneridagh [#6162](https://github.com/plone/volto/issues/6162) +- In the widget mapping, moved the `SchemaWidget` registration from the `id` object to the `widget` object, and added the `widget` key to the `schema` object in the `properties` object for `makeSchemaList`. @sneridagh [#6189](https://github.com/plone/volto/issues/6189) + +### Bugfix + +- Fixed UTC problems in `RecurrenceWidget`. @giuliaghisini [#5002](https://github.com/plone/volto/issues/5002) +- Do not send sorting information in the search block if no sort_on setting is configured @erral [#5338](https://github.com/plone/volto/issues/5338) +- Fixed pagination in search results by passing `pageSize` explicitly to all search API calls. @EshaanAgg [#5464](https://github.com/plone/volto/issues/5464) +- Fix the toolbar handler color for the homepage to match its "published" state. @sabrina-bongiovanni [#6126](https://github.com/plone/volto/issues/6126) +- Allow `ImageWidget` value to be an object and use the `@id` to get the value if present. + This is useful for fields that were using the `object_browser` widget previously to set values. @ichim-david [#6156](https://github.com/plone/volto/issues/6156) +- Persist data for the `backend-docker-start` Docker container in a Docker volume named `volto-backend-data`. + This way the data is persisted between runs of the container. + You can also delete the `data` volume to start fresh. + @ichim-david [#6157](https://github.com/plone/volto/issues/6157) +- Improve CSS for the `SchemaWidget` widget. @robgietema @sneridagh [#6189](https://github.com/plone/volto/issues/6189) + +### Internal + +- Debounced searching for users and groups in the `User Group Membership` Control Panel. @pnicolli [#6153](https://github.com/plone/volto/issues/6153) +- Update the link in the PLIP issue template to the new Plone 6 Documentation PLIP page. @stevepiercy [#6175](https://github.com/plone/volto/issues/6175) +- Added Cypress test for field types in example content - @Tishasoumya-02 [#6217](https://github.com/plone/volto/issues/6217) + +### Documentation + +- Changed a few typos within documentation, README's and comments. @FritzHoing [#6109](https://github.com/plone/volto/issues/6109) +- Use relative links to ensure static files get copied during documentation build. @stevepiercy [#6174](https://github.com/plone/volto/issues/6174) +- Clean up upgrade guide for `react-share` library and `SocialSharing` component. @stevepiercy [#6175](https://github.com/plone/volto/issues/6175) +- Add references for contributing to latest and earlier versions of Volto. @stevepiercy [#6184](https://github.com/plone/volto/issues/6184) +- Improved i18n docs regarding new translated messages not being picked up by the i18n translation mechanism when added in shadowed components. @pnicolli [#6188](https://github.com/plone/volto/issues/6188) +- Add a label and minor grammar fixes to i18n documentation. @stevepiercy [#6192](https://github.com/plone/volto/issues/6192) +- Polish upgrade docs and news items for `SchemaWidget`. @stevepiercy [#6193](https://github.com/plone/volto/issues/6193) + ## 18.0.0-alpha.41 (2024-07-05) ### Breaking diff --git a/packages/volto/CHANGELOG.md b/packages/volto/CHANGELOG.md index 8fea32ad54..91db671d96 100644 --- a/packages/volto/CHANGELOG.md +++ b/packages/volto/CHANGELOG.md @@ -17,6 +17,48 @@ myst: +## 18.0.0-alpha.42 (2024-07-30) + +### Breaking + +- Add foundations for extensible validation in forms. @sneridagh + + Breaking: + `packages/volto/src/helpers/FormValidation/FormValidation.jsx` has been heavily refactored. + If you shadowed this component in your project or add-on, you should review it and update it accordingly. [#6161](https://github.com/plone/volto/issues/6161) +- Remove `react-share` library and `SocialSharing` component @sneridagh [#6162](https://github.com/plone/volto/issues/6162) +- In the widget mapping, moved the `SchemaWidget` registration from the `id` object to the `widget` object, and added the `widget` key to the `schema` object in the `properties` object for `makeSchemaList`. @sneridagh [#6189](https://github.com/plone/volto/issues/6189) + +### Bugfix + +- Fixed UTC problems in `RecurrenceWidget`. @giuliaghisini [#5002](https://github.com/plone/volto/issues/5002) +- Do not send sorting information in the search block if no sort_on setting is configured @erral [#5338](https://github.com/plone/volto/issues/5338) +- Fixed pagination in search results by passing `pageSize` explicitly to all search API calls. @EshaanAgg [#5464](https://github.com/plone/volto/issues/5464) +- Fix the toolbar handler color for the homepage to match its "published" state. @sabrina-bongiovanni [#6126](https://github.com/plone/volto/issues/6126) +- Allow `ImageWidget` value to be an object and use the `@id` to get the value if present. + This is useful for fields that were using the `object_browser` widget previously to set values. @ichim-david [#6156](https://github.com/plone/volto/issues/6156) +- Persist data for the `backend-docker-start` Docker container in a Docker volume named `volto-backend-data`. + This way the data is persisted between runs of the container. + You can also delete the `data` volume to start fresh. + @ichim-david [#6157](https://github.com/plone/volto/issues/6157) +- Improve CSS for the `SchemaWidget` widget. @robgietema @sneridagh [#6189](https://github.com/plone/volto/issues/6189) + +### Internal + +- Debounced searching for users and groups in the `User Group Membership` Control Panel. @pnicolli [#6153](https://github.com/plone/volto/issues/6153) +- Update the link in the PLIP issue template to the new Plone 6 Documentation PLIP page. @stevepiercy [#6175](https://github.com/plone/volto/issues/6175) +- Added Cypress test for field types in example content - @Tishasoumya-02 [#6217](https://github.com/plone/volto/issues/6217) + +### Documentation + +- Changed a few typos within documentation, README's and comments. @FritzHoing [#6109](https://github.com/plone/volto/issues/6109) +- Use relative links to ensure static files get copied during documentation build. @stevepiercy [#6174](https://github.com/plone/volto/issues/6174) +- Clean up upgrade guide for `react-share` library and `SocialSharing` component. @stevepiercy [#6175](https://github.com/plone/volto/issues/6175) +- Add references for contributing to latest and earlier versions of Volto. @stevepiercy [#6184](https://github.com/plone/volto/issues/6184) +- Improved i18n docs regarding new translated messages not being picked up by the i18n translation mechanism when added in shadowed components. @pnicolli [#6188](https://github.com/plone/volto/issues/6188) +- Add a label and minor grammar fixes to i18n documentation. @stevepiercy [#6192](https://github.com/plone/volto/issues/6192) +- Polish upgrade docs and news items for `SchemaWidget`. @stevepiercy [#6193](https://github.com/plone/volto/issues/6193) + ## 18.0.0-alpha.41 (2024-07-05) ### Breaking diff --git a/packages/volto/news/5002.bugfix b/packages/volto/news/5002.bugfix deleted file mode 100644 index 574b134e0a..0000000000 --- a/packages/volto/news/5002.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixed UTC problems in `RecurrenceWidget`. @giuliaghisini diff --git a/packages/volto/news/5338.bugfix b/packages/volto/news/5338.bugfix deleted file mode 100644 index ce0925e897..0000000000 --- a/packages/volto/news/5338.bugfix +++ /dev/null @@ -1 +0,0 @@ -Do not send sorting information in the search block if no sort_on setting is configured @erral diff --git a/packages/volto/news/5464.bugfix b/packages/volto/news/5464.bugfix deleted file mode 100644 index b22367d7b6..0000000000 --- a/packages/volto/news/5464.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixed pagination in search results by passing `pageSize` explicitly to all search API calls. @EshaanAgg \ No newline at end of file diff --git a/packages/volto/news/6109.documentation b/packages/volto/news/6109.documentation deleted file mode 100644 index fc84275ecd..0000000000 --- a/packages/volto/news/6109.documentation +++ /dev/null @@ -1 +0,0 @@ -Changed a few typos within documentation, README's and comments. @FritzHoing diff --git a/packages/volto/news/6126.bugfix b/packages/volto/news/6126.bugfix deleted file mode 100644 index 8db6a83f2e..0000000000 --- a/packages/volto/news/6126.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix the toolbar handler color for the homepage to match its "published" state. @sabrina-bongiovanni diff --git a/packages/volto/news/6153.internal b/packages/volto/news/6153.internal deleted file mode 100644 index 6c8fd1188e..0000000000 --- a/packages/volto/news/6153.internal +++ /dev/null @@ -1 +0,0 @@ -Debounced searching for users and groups in the `User Group Membership` Control Panel. @pnicolli diff --git a/packages/volto/news/6156.bugfix b/packages/volto/news/6156.bugfix deleted file mode 100644 index 2c1ba09631..0000000000 --- a/packages/volto/news/6156.bugfix +++ /dev/null @@ -1,2 +0,0 @@ -Allow `ImageWidget` value to be an object and use the `@id` to get the value if present. -This is useful for fields that were using the `object_browser` widget previously to set values. @ichim-david \ No newline at end of file diff --git a/packages/volto/news/6157.bugfix b/packages/volto/news/6157.bugfix deleted file mode 100644 index 88e356670e..0000000000 --- a/packages/volto/news/6157.bugfix +++ /dev/null @@ -1,4 +0,0 @@ -Persist data for the `backend-docker-start` Docker container in a Docker volume named `volto-backend-data`. -This way the data is persisted between runs of the container. -You can also delete the `data` volume to start fresh. -@ichim-david \ No newline at end of file diff --git a/packages/volto/news/6161.breaking b/packages/volto/news/6161.breaking deleted file mode 100644 index 6c3a087353..0000000000 --- a/packages/volto/news/6161.breaking +++ /dev/null @@ -1,5 +0,0 @@ -Add foundations for extensible validation in forms. @sneridagh - -Breaking: -`packages/volto/src/helpers/FormValidation/FormValidation.jsx` has been heavily refactored. -If you shadowed this component in your project or add-on, you should review it and update it accordingly. diff --git a/packages/volto/news/6162.breaking b/packages/volto/news/6162.breaking deleted file mode 100644 index fd2f5b36bd..0000000000 --- a/packages/volto/news/6162.breaking +++ /dev/null @@ -1 +0,0 @@ -Remove `react-share` library and `SocialSharing` component @sneridagh diff --git a/packages/volto/news/6174.documentation b/packages/volto/news/6174.documentation deleted file mode 100644 index 388bbfe3e1..0000000000 --- a/packages/volto/news/6174.documentation +++ /dev/null @@ -1 +0,0 @@ -Use relative links to ensure static files get copied during documentation build. @stevepiercy diff --git a/packages/volto/news/6175.documentation b/packages/volto/news/6175.documentation deleted file mode 100644 index eaa30fc216..0000000000 --- a/packages/volto/news/6175.documentation +++ /dev/null @@ -1 +0,0 @@ -Clean up upgrade guide for `react-share` library and `SocialSharing` component. @stevepiercy diff --git a/packages/volto/news/6175.internal b/packages/volto/news/6175.internal deleted file mode 100644 index 1290a4fb48..0000000000 --- a/packages/volto/news/6175.internal +++ /dev/null @@ -1 +0,0 @@ -Update the link in the PLIP issue template to the new Plone 6 Documentation PLIP page. @stevepiercy diff --git a/packages/volto/news/6184.documentation b/packages/volto/news/6184.documentation deleted file mode 100644 index 80600cc452..0000000000 --- a/packages/volto/news/6184.documentation +++ /dev/null @@ -1 +0,0 @@ -Add references for contributing to latest and earlier versions of Volto. @stevepiercy diff --git a/packages/volto/news/6188.documentation b/packages/volto/news/6188.documentation deleted file mode 100644 index 623807c594..0000000000 --- a/packages/volto/news/6188.documentation +++ /dev/null @@ -1 +0,0 @@ -Improved i18n docs regarding new translated messages not being picked up by the i18n translation mechanism when added in shadowed components. @pnicolli diff --git a/packages/volto/news/6189.breaking b/packages/volto/news/6189.breaking deleted file mode 100644 index 89e756306a..0000000000 --- a/packages/volto/news/6189.breaking +++ /dev/null @@ -1 +0,0 @@ -In the widget mapping, moved the `SchemaWidget` registration from the `id` object to the `widget` object, and added the `widget` key to the `schema` object in the `properties` object for `makeSchemaList`. @sneridagh diff --git a/packages/volto/news/6189.bugfix b/packages/volto/news/6189.bugfix deleted file mode 100644 index b25ea08e8a..0000000000 --- a/packages/volto/news/6189.bugfix +++ /dev/null @@ -1 +0,0 @@ -Improve CSS for the `SchemaWidget` widget. @robgietema @sneridagh diff --git a/packages/volto/news/6192.documentation b/packages/volto/news/6192.documentation deleted file mode 100644 index a518e15d24..0000000000 --- a/packages/volto/news/6192.documentation +++ /dev/null @@ -1 +0,0 @@ -Add a label and minor grammar fixes to i18n documentation. @stevepiercy diff --git a/packages/volto/news/6193.documentation b/packages/volto/news/6193.documentation deleted file mode 100644 index 54a9474dcc..0000000000 --- a/packages/volto/news/6193.documentation +++ /dev/null @@ -1 +0,0 @@ -Polish upgrade docs and news items for `SchemaWidget`. @stevepiercy diff --git a/packages/volto/news/6217.internal b/packages/volto/news/6217.internal deleted file mode 100644 index 75258c4a17..0000000000 --- a/packages/volto/news/6217.internal +++ /dev/null @@ -1 +0,0 @@ -Added Cypress test for field types in example content - @Tishasoumya-02 \ No newline at end of file diff --git a/packages/volto/package.json b/packages/volto/package.json index f688a23ea7..8c84fe6c5d 100644 --- a/packages/volto/package.json +++ b/packages/volto/package.json @@ -9,7 +9,7 @@ } ], "license": "MIT", - "version": "18.0.0-alpha.41", + "version": "18.0.0-alpha.42", "repository": { "type": "git", "url": "git@github.com:plone/volto.git" diff --git a/packages/volto/types/components/index.d.ts b/packages/volto/types/components/index.d.ts index 4eb9dd175c..ce47419367 100644 --- a/packages/volto/types/components/index.d.ts +++ b/packages/volto/types/components/index.d.ts @@ -33,7 +33,6 @@ export { default as Logout } from "@plone/volto/components/theme/Logout/Logout"; export { default as Sitemap } from "@plone/volto/components/theme/Sitemap/Sitemap"; export { default as Search } from "@plone/volto/components/theme/Search/Search"; export { default as Comments } from "@plone/volto/components/theme/Comments/Comments"; -export { default as SocialSharing } from "@plone/volto/components/theme/SocialSharing/SocialSharing"; export { default as Register } from "@plone/volto/components/theme/Register/Register"; export { default as PasswordReset } from "@plone/volto/components/theme/PasswordReset/PasswordReset"; export { default as RequestPasswordReset } from "@plone/volto/components/theme/PasswordReset/RequestPasswordReset"; diff --git a/packages/volto/types/components/manage/Blocks/Block/Order/Order.d.ts b/packages/volto/types/components/manage/Blocks/Block/Order/Order.d.ts index a2e5bfc22b..159c7dcaa0 100644 --- a/packages/volto/types/components/manage/Blocks/Block/Order/Order.d.ts +++ b/packages/volto/types/components/manage/Blocks/Block/Order/Order.d.ts @@ -1,4 +1,4 @@ -export function Order({ items, onMoveBlock, onDeleteBlock, onSelectBlock, indentationWidth, removable, dndKitCore, dndKitSortable, dndKitUtilities, }: { +export function Order({ items, onMoveBlock, onDeleteBlock, onSelectBlock, indentationWidth, removable, dndKitCore, dndKitSortable, dndKitUtilities, errors, }: { items?: any[]; onMoveBlock: any; onDeleteBlock: any; @@ -8,6 +8,7 @@ export function Order({ items, onMoveBlock, onDeleteBlock, onSelectBlock, indent dndKitCore: any; dndKitSortable: any; dndKitUtilities: any; + errors: any; }): import("react/jsx-runtime").JSX.Element; declare const _default: any; export default _default; diff --git a/packages/volto/types/config/Widgets.d.ts b/packages/volto/types/config/Widgets.d.ts index 7eb3ea4e15..935fad5d80 100644 --- a/packages/volto/types/config/Widgets.d.ts +++ b/packages/volto/types/config/Widgets.d.ts @@ -1,6 +1,5 @@ export namespace widgetMapping { export namespace id { - export { SchemaWidget as schema }; export { TokenWidget as subjects }; export { QuerystringWidget as query }; export { RecurrenceWidget as recurrence }; @@ -34,6 +33,7 @@ export namespace widgetMapping { export { SelectAutoComplete as autocomplete }; export { ColorPickerWidget as color_picker }; export { SelectWidget as select }; + export { SchemaWidget as schema }; } export let vocabulary: { 'plone.app.vocabularies.Catalog': import("@loadable/component").LoadableComponent>, "ref"> & import("react").RefAttributes>>>; @@ -98,7 +98,6 @@ export namespace widgetMapping { } } export const defaultWidget: import("@loadable/component").LoadableComponent; -import { SchemaWidget } from '@plone/volto/components/manage/Widgets'; import { TokenWidget } from '@plone/volto/components/manage/Widgets'; import { QuerystringWidget } from '@plone/volto/components/manage/Widgets'; import { RecurrenceWidget } from '@plone/volto/components/manage/Widgets'; @@ -126,6 +125,7 @@ import { SelectMetadataWidget } from '@plone/volto/components/manage/Widgets'; import { SelectAutoComplete } from '@plone/volto/components/manage/Widgets'; import { ColorPickerWidget } from '@plone/volto/components/manage/Widgets'; import { SelectWidget } from '@plone/volto/components/manage/Widgets'; +import { SchemaWidget } from '@plone/volto/components/manage/Widgets'; import { CheckboxWidget } from '@plone/volto/components/manage/Widgets'; import { NumberWidget } from '@plone/volto/components/manage/Widgets'; import { getWidgetView } from '@plone/volto/helpers/Widget/widget'; diff --git a/packages/volto/types/config/validation.d.ts b/packages/volto/types/config/validation.d.ts new file mode 100644 index 0000000000..7ad62c8981 --- /dev/null +++ b/packages/volto/types/config/validation.d.ts @@ -0,0 +1,3 @@ +import { ConfigType } from '@plone/registry'; +declare const registerValidators: (config: ConfigType) => void; +export { registerValidators }; diff --git a/packages/volto/types/helpers/FormValidation/FormValidation.d.ts b/packages/volto/types/helpers/FormValidation/FormValidation.d.ts index 86de052865..28d094562b 100644 --- a/packages/volto/types/helpers/FormValidation/FormValidation.d.ts +++ b/packages/volto/types/helpers/FormValidation/FormValidation.d.ts @@ -1,3 +1,4 @@ +export function validationMessage(isValid: boolean, criterion: string, valueToCompare: string | number, intlFunc: Function): any; export function tryParseJSON(requestItem: string): any; export default FormValidation; export function validateFileUploadSize(file: File, intlFunc: Function): boolean; diff --git a/packages/volto/types/helpers/FormValidation/validators.d.ts b/packages/volto/types/helpers/FormValidation/validators.d.ts new file mode 100644 index 0000000000..1e90f7cc16 --- /dev/null +++ b/packages/volto/types/helpers/FormValidation/validators.d.ts @@ -0,0 +1,29 @@ +type MinMaxValidator = { + value: string | number; + fieldSpec: string | number; + criterion: string; + formatMessage: Function; +}; +type Validator = { + value: string; + field: Record; + formData: any; + formatMessage: Function; +}; +export declare const isMaxPropertyValid: ({ value, fieldSpec, criterion, formatMessage, }: MinMaxValidator) => any; +export declare const isMinPropertyValid: ({ value, fieldSpec, criterion, formatMessage, }: MinMaxValidator) => any; +export declare const minLengthValidator: ({ value, field, formatMessage, }: Validator) => any; +export declare const maxLengthValidator: ({ value, field, formatMessage, }: Validator) => any; +export declare const urlValidator: ({ value, formatMessage }: Validator) => any; +export declare const emailValidator: ({ value, formatMessage }: Validator) => string; +export declare const isNumberValidator: ({ value, formatMessage }: Validator) => any; +export declare const minimumValidator: ({ value, field, formatMessage }: Validator) => any; +export declare const maximumValidator: ({ value, field, formatMessage }: Validator) => any; +export declare const isIntegerValidator: ({ value, formatMessage }: Validator) => any; +export declare const hasUniqueItemsValidator: ({ value, field, formatMessage, }: Validator) => any; +export declare const startEventDateRangeValidator: ({ value, field, formData, formatMessage, }: Validator) => any; +export declare const endEventDateRangeValidator: ({ value, field, formData, formatMessage, }: Validator) => any; +export declare const patternValidator: ({ value, field, formatMessage, }: Validator) => any; +export declare const maxItemsValidator: ({ value, field, formatMessage, }: Validator) => any; +export declare const minItemsValidator: ({ value, field, formatMessage, }: Validator) => any; +export {}; diff --git a/packages/volto/types/helpers/MessageLabels/MessageLabels.d.ts b/packages/volto/types/helpers/MessageLabels/MessageLabels.d.ts index cd59ec6eca..b516c39dc8 100644 --- a/packages/volto/types/helpers/MessageLabels/MessageLabels.d.ts +++ b/packages/volto/types/helpers/MessageLabels/MessageLabels.d.ts @@ -549,4 +549,40 @@ export namespace messages { let defaultMessage_91: string; export { defaultMessage_91 as defaultMessage }; } + namespace blocksFieldsErrorTitle { + let id_92: string; + export { id_92 as id }; + let defaultMessage_92: string; + export { defaultMessage_92 as defaultMessage }; + } + namespace startEventRange { + let id_93: string; + export { id_93 as id }; + let defaultMessage_93: string; + export { defaultMessage_93 as defaultMessage }; + } + namespace endEventRange { + let id_94: string; + export { id_94 as id }; + let defaultMessage_94: string; + export { defaultMessage_94 as defaultMessage }; + } + namespace pattern { + let id_95: string; + export { id_95 as id }; + let defaultMessage_95: string; + export { defaultMessage_95 as defaultMessage }; + } + namespace maxItems { + let id_96: string; + export { id_96 as id }; + let defaultMessage_96: string; + export { defaultMessage_96 as defaultMessage }; + } + namespace minItems { + let id_97: string; + export { id_97 as id }; + let defaultMessage_97: string; + export { defaultMessage_97 as defaultMessage }; + } } From 82481e4d445f0890d1f5f6113e5966176e82d4d5 Mon Sep 17 00:00:00 2001 From: iRohitSingh <61353484+iRohitSingh@users.noreply.github.com> Date: Wed, 31 Jul 2024 05:57:42 +0530 Subject: [PATCH 12/58] Fix search block showing no option select in sort on property (#5055) Co-authored-by: David Glick --- .../volto/locales/ca/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/de/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/en/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/es/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/eu/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/fi/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/fr/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/hi/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/it/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/ja/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/nl/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/pt/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/pt_BR/LC_MESSAGES/volto.po | 10 ++ .../volto/locales/ro/LC_MESSAGES/volto.po | 10 ++ packages/volto/locales/volto.pot | 10 ++ .../volto/locales/zh_CN/LC_MESSAGES/volto.po | 10 ++ packages/volto/news/5055.bugfix | 1 + .../Blocks/Search/components/SortOn.jsx | 141 +++++++++++------- .../theme/themes/pastanaga/extras/blocks.less | 6 + 19 files changed, 253 insertions(+), 55 deletions(-) create mode 100644 packages/volto/news/5055.bugfix diff --git a/packages/volto/locales/ca/LC_MESSAGES/volto.po b/packages/volto/locales/ca/LC_MESSAGES/volto.po index 97299c5c52..882f8df983 100644 --- a/packages/volto/locales/ca/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ca/LC_MESSAGES/volto.po @@ -3414,6 +3414,11 @@ msgstr "Ordena" msgid "Sort on options" msgstr "Ordena les opcions" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3424,6 +3429,11 @@ msgstr "" msgid "Sorted" msgstr "" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/de/LC_MESSAGES/volto.po b/packages/volto/locales/de/LC_MESSAGES/volto.po index 3ae526f295..1089694cd9 100644 --- a/packages/volto/locales/de/LC_MESSAGES/volto.po +++ b/packages/volto/locales/de/LC_MESSAGES/volto.po @@ -3413,6 +3413,11 @@ msgstr "Sortieren nach" msgid "Sort on options" msgstr "Sortieroptionen" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3423,6 +3428,11 @@ msgstr "Transaktionen nach Nutzernamen, Pfad oder Datum sortieren" msgid "Sorted" msgstr "Sortiert" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/en/LC_MESSAGES/volto.po b/packages/volto/locales/en/LC_MESSAGES/volto.po index 309d263b01..29f2346b6e 100644 --- a/packages/volto/locales/en/LC_MESSAGES/volto.po +++ b/packages/volto/locales/en/LC_MESSAGES/volto.po @@ -3408,6 +3408,11 @@ msgstr "" msgid "Sort on options" msgstr "" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3418,6 +3423,11 @@ msgstr "" msgid "Sorted" msgstr "" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/es/LC_MESSAGES/volto.po b/packages/volto/locales/es/LC_MESSAGES/volto.po index bcd3892d27..50bc4e1924 100644 --- a/packages/volto/locales/es/LC_MESSAGES/volto.po +++ b/packages/volto/locales/es/LC_MESSAGES/volto.po @@ -3415,6 +3415,11 @@ msgstr "Ordenar por" msgid "Sort on options" msgstr "Ordenar por opciones" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3425,6 +3430,11 @@ msgstr "Ordenar transacciones por nombre de usuario, ruta o fecha" msgid "Sorted" msgstr "Ordenado" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/eu/LC_MESSAGES/volto.po b/packages/volto/locales/eu/LC_MESSAGES/volto.po index db6c019cb4..6c145f58f7 100644 --- a/packages/volto/locales/eu/LC_MESSAGES/volto.po +++ b/packages/volto/locales/eu/LC_MESSAGES/volto.po @@ -3415,6 +3415,11 @@ msgstr "Ordenazioa" msgid "Sort on options" msgstr "Ordenatu aukeren arabera" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3425,6 +3430,11 @@ msgstr "Ordenatu transakzioak erabiltzaile-izen, bide edo dataren arabera" msgid "Sorted" msgstr "Ordenatuta" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/fi/LC_MESSAGES/volto.po b/packages/volto/locales/fi/LC_MESSAGES/volto.po index a894243780..34a787d7b8 100644 --- a/packages/volto/locales/fi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fi/LC_MESSAGES/volto.po @@ -3413,6 +3413,11 @@ msgstr "Järjestys" msgid "Sort on options" msgstr "Lajittelun vaihtoehdot" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3423,6 +3428,11 @@ msgstr "Lajittele nimen, polun tai päivämäärän perusteella" msgid "Sorted" msgstr "Lajiteltu" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/fr/LC_MESSAGES/volto.po b/packages/volto/locales/fr/LC_MESSAGES/volto.po index 92c6836cb5..f50775acbd 100644 --- a/packages/volto/locales/fr/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fr/LC_MESSAGES/volto.po @@ -3415,6 +3415,11 @@ msgstr "Trier sur" msgid "Sort on options" msgstr "Options de tri" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3425,6 +3430,11 @@ msgstr "Trier les transaction par utilisateur, chemin ou date" msgid "Sorted" msgstr "Trié" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/hi/LC_MESSAGES/volto.po b/packages/volto/locales/hi/LC_MESSAGES/volto.po index 7acbc22305..a6fc3900ed 100644 --- a/packages/volto/locales/hi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/hi/LC_MESSAGES/volto.po @@ -3408,6 +3408,11 @@ msgstr "क्रमबद्ध करें द्वारा" msgid "Sort on options" msgstr "क्रमबद्ध करें द्वारा विकल्प" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3418,6 +3423,11 @@ msgstr "उपयोगकर्ता-नाम, पथ या तिथि द msgid "Sorted" msgstr "क्रमबद्ध किया गया" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/it/LC_MESSAGES/volto.po b/packages/volto/locales/it/LC_MESSAGES/volto.po index 11f9e88370..a00ed897bd 100644 --- a/packages/volto/locales/it/LC_MESSAGES/volto.po +++ b/packages/volto/locales/it/LC_MESSAGES/volto.po @@ -3408,6 +3408,11 @@ msgstr "Ordina per" msgid "Sort on options" msgstr "Opzioni di ordinamento" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3418,6 +3423,11 @@ msgstr "Ordina transazioni per Username, Percorso o Data" msgid "Sorted" msgstr "Ordinato" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/ja/LC_MESSAGES/volto.po b/packages/volto/locales/ja/LC_MESSAGES/volto.po index b9b6ceb98f..f6cd3cb259 100644 --- a/packages/volto/locales/ja/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ja/LC_MESSAGES/volto.po @@ -3413,6 +3413,11 @@ msgstr "ソート順" msgid "Sort on options" msgstr "" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3423,6 +3428,11 @@ msgstr "" msgid "Sorted" msgstr "" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/nl/LC_MESSAGES/volto.po b/packages/volto/locales/nl/LC_MESSAGES/volto.po index efdef3cd6f..567f561d6d 100644 --- a/packages/volto/locales/nl/LC_MESSAGES/volto.po +++ b/packages/volto/locales/nl/LC_MESSAGES/volto.po @@ -3412,6 +3412,11 @@ msgstr "" msgid "Sort on options" msgstr "" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3422,6 +3427,11 @@ msgstr "" msgid "Sorted" msgstr "" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/pt/LC_MESSAGES/volto.po b/packages/volto/locales/pt/LC_MESSAGES/volto.po index d245b26dfb..2c97866879 100644 --- a/packages/volto/locales/pt/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt/LC_MESSAGES/volto.po @@ -3413,6 +3413,11 @@ msgstr "" msgid "Sort on options" msgstr "" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3423,6 +3428,11 @@ msgstr "" msgid "Sorted" msgstr "" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po index 364922d608..1a322f378e 100644 --- a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po @@ -3414,6 +3414,11 @@ msgstr "Ordenado por" msgid "Sort on options" msgstr "Opções de ordenação" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3424,6 +3429,11 @@ msgstr "Ordenar transações por Nome de Usuário, Caminho ou Data" msgid "Sorted" msgstr "Ordenado" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/ro/LC_MESSAGES/volto.po b/packages/volto/locales/ro/LC_MESSAGES/volto.po index 7c203620ef..ec596db862 100644 --- a/packages/volto/locales/ro/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ro/LC_MESSAGES/volto.po @@ -3408,6 +3408,11 @@ msgstr "Sortare pe" msgid "Sort on options" msgstr "Sortare pe opțiuni" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3418,6 +3423,11 @@ msgstr "" msgid "Sorted" msgstr "" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/volto.pot b/packages/volto/locales/volto.pot index d38fea965b..861418c388 100644 --- a/packages/volto/locales/volto.pot +++ b/packages/volto/locales/volto.pot @@ -3410,6 +3410,11 @@ msgstr "" msgid "Sort on options" msgstr "" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3420,6 +3425,11 @@ msgstr "" msgid "Sorted" msgstr "" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po index 50d2927f3a..a2bfa17bbc 100644 --- a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po +++ b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po @@ -3414,6 +3414,11 @@ msgstr "排序" msgid "Sort on options" msgstr "根据选项排序" +#. Default: "Sort on {value}" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sort on {value}" +msgstr "" + #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" @@ -3424,6 +3429,11 @@ msgstr "按用户名、路径或日期对事务进行排序" msgid "Sorted" msgstr "排序" +#. Default: "Sorted on" +#: components/manage/Blocks/Search/components/SortOn +msgid "Sorted on" +msgstr "" + #. Default: "Source" #: components/manage/Blocks/HTML/Edit #: components/manage/Controlpanels/Relations/BrokenRelations diff --git a/packages/volto/news/5055.bugfix b/packages/volto/news/5055.bugfix new file mode 100644 index 0000000000..d089f7d0b4 --- /dev/null +++ b/packages/volto/news/5055.bugfix @@ -0,0 +1 @@ +Fix search block showing no option select in sort on property @iRohitSingh \ No newline at end of file diff --git a/packages/volto/src/components/manage/Blocks/Search/components/SortOn.jsx b/packages/volto/src/components/manage/Blocks/Search/components/SortOn.jsx index 01502ac685..de32097c3a 100644 --- a/packages/volto/src/components/manage/Blocks/Search/components/SortOn.jsx +++ b/packages/volto/src/components/manage/Blocks/Search/components/SortOn.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Button } from 'semantic-ui-react'; import { defineMessages, injectIntl } from 'react-intl'; import cx from 'classnames'; @@ -23,6 +22,10 @@ const messages = defineMessages({ id: 'Sort on', defaultMessage: 'Sort on', }, + sortOnButtonTitle: { + id: 'Sort on {value}', + defaultMessage: 'Sort on {value}', + }, ascending: { id: 'Ascending', defaultMessage: 'Ascending', @@ -31,6 +34,10 @@ const messages = defineMessages({ id: 'Descending', defaultMessage: 'Descending', }, + sortedOn: { + id: 'Sorted on', + defaultMessage: 'Sorted on', + }, }); const SortOn = (props) => { @@ -48,9 +55,17 @@ const SortOn = (props) => { const { sortable_indexes } = querystring; const Select = reactSelect.default; - const activeSortOn = sortOn || data?.query?.sort_on || ''; + const defaultSortOn = data?.query?.sort_on || ''; + const activeSortOn = sortOn || defaultSortOn; + + let { sortOnOptions = [] } = data; + sortOnOptions = [defaultSortOn, ...sortOnOptions]; + sortOnOptions = [...new Set(sortOnOptions)]; - const { sortOnOptions = [] } = data; + const showSelectField = sortOnOptions.length > 1; + if (!showSelectField && !activeSortOn) { + return; + } const value = { value: activeSortOn || intl.formatMessage(messages.noSelection), label: @@ -62,59 +77,75 @@ const SortOn = (props) => { return (
- - {intl.formatMessage(messages.sortOn)} - - ({ + value: k, + label: + sortable_indexes[k]?.title || + k || + intl.formatMessage(messages.noSelection), + })), + ]} + isSearchable={false} + value={value} + onChange={(data) => { + !isEditMode && setSortOn(data.value); + }} + /> + + ) : ( + + {intl.formatMessage(messages.sortedOn)} + {value.label} + + )}
- - + {activeSortOn ? ( + <> + + + + ) : null}
); }; diff --git a/packages/volto/theme/themes/pastanaga/extras/blocks.less b/packages/volto/theme/themes/pastanaga/extras/blocks.less index 8be103cb0c..099bba9913 100644 --- a/packages/volto/theme/themes/pastanaga/extras/blocks.less +++ b/packages/volto/theme/themes/pastanaga/extras/blocks.less @@ -1120,6 +1120,12 @@ div.image-upload-widget-image { flex-direction: row; align-items: center; margin-right: 0.5em; + + .sorted-label-value { + margin-right: 0.5em; + margin-left: 0.7em; + color: @lightGrey; + } } .sort-label { From 932962e0494d9bae2b07df2cd027943794777e2a Mon Sep 17 00:00:00 2001 From: Tisha Soumya Date: Thu, 1 Aug 2024 20:17:02 +0530 Subject: [PATCH 13/58] Teaser Block Image Overide (#6147) Co-authored-by: David Ichim --- .../tests/core/blocks/blocks-teaser.js | 87 ++++++++++++++----- packages/volto/news/{.gitkeep => .keep} | 0 packages/volto/news/6147.bugfix | 1 + .../manage/Blocks/Teaser/DefaultBody.jsx | 23 +++-- 4 files changed, 80 insertions(+), 31 deletions(-) rename packages/volto/news/{.gitkeep => .keep} (100%) create mode 100644 packages/volto/news/6147.bugfix diff --git a/packages/volto/cypress/tests/core/blocks/blocks-teaser.js b/packages/volto/cypress/tests/core/blocks/blocks-teaser.js index 79d3d77d67..0799753b35 100644 --- a/packages/volto/cypress/tests/core/blocks/blocks-teaser.js +++ b/packages/volto/cypress/tests/core/blocks/blocks-teaser.js @@ -14,11 +14,11 @@ context('Blocks Acceptance Tests', () => { }); it('As editor I can add a (standalone) Teaser block', () => { - // GIVEN a Document with the title document and a Document to reference with the title Blue Orchidees + // GIVEN a Document with the title document and a Document to reference with the title Blue Orchids cy.createContent({ contentType: 'Document', - contentId: 'blue-orchidees', - contentTitle: 'Blue Orchidees', + contentId: 'blue-orchids', + contentTitle: 'Blue Orchids', contentDescription: 'are growing on the mountain tops', image: true, path: '/document', @@ -35,7 +35,7 @@ context('Blocks Acceptance Tests', () => { cy.get( '.objectbrowser-field[aria-labelledby="fieldset-default-field-label-href"] button[aria-label="Open object browser"]', ).click(); - cy.get('[aria-label="Select Blue Orchidees"]').dblclick(); + cy.get('[aria-label="Select Blue Orchids"]').dblclick(); cy.wait(500); cy.get('.align-buttons .ui.buttons button[aria-label="Center"]').click(); cy.get('#toolbar-save').click(); @@ -45,19 +45,19 @@ context('Blocks Acceptance Tests', () => { cy.get('.block.teaser').should('have.class', 'has--align--center'); cy.get('.block.teaser .image-wrapper img') .should('have.attr', 'src') - .and('include', '/document/blue-orchidees/@@images/preview_image-'); - cy.get('.block.teaser .content h2').contains('Blue Orchidees'); + .and('include', '/document/blue-orchids/@@images/preview_image-'); + cy.get('.block.teaser .content h2').contains('Blue Orchids'); cy.get('.block.teaser .content p').contains( 'are growing on the mountain tops', ); }); it('As editor I can add a (standalone) Teaser block that always fetches the live data', () => { - // GIVEN a Document with the title document and a Document to reference with the title Blue Orchidees + // GIVEN a Document with the title document and a Document to reference with the title Blue Orchids cy.createContent({ contentType: 'Document', - contentId: 'blue-orchidees', - contentTitle: 'Blue Orchidees', + contentId: 'blue-orchids', + contentTitle: 'Blue Orchids', contentDescription: 'are growing on the mountain tops', image: true, path: '/document', @@ -73,16 +73,16 @@ context('Blocks Acceptance Tests', () => { cy.get( '.objectbrowser-field[aria-labelledby="fieldset-default-field-label-href"] button[aria-label="Open object browser"]', ).click(); - cy.get('[aria-label="Select Blue Orchidees"]').dblclick(); + cy.get('[aria-label="Select Blue Orchids"]').dblclick(); cy.wait(500); cy.get('#toolbar-save').click(); cy.visit('/document'); - cy.get('.block.teaser .content h2').contains('Blue Orchidees'); + cy.get('.block.teaser .content h2').contains('Blue Orchids'); cy.get('.block.teaser .content p').contains( 'are growing on the mountain tops', ); - cy.navigate('/document/blue-orchidees/edit'); + cy.navigate('/document/blue-orchids/edit'); cy.wait('@schema'); cy.getSlateTitle().type(' and Tulips'); cy.get('#field-description') @@ -90,21 +90,21 @@ context('Blocks Acceptance Tests', () => { .type('are beautifully growing on the mountain tops'); cy.get('#toolbar-save').click(); - cy.get('.documentFirstHeading').contains('Blue Orchidees and Tulips'); + cy.get('.documentFirstHeading').contains('Blue Orchids and Tulips'); // THEN I can see the updated content in the teaser cy.navigate('/document'); - cy.get('.block.teaser .content h2').contains('Blue Orchidees and Tulips'); + cy.get('.block.teaser .content h2').contains('Blue Orchids and Tulips'); cy.get('.block.teaser .content p').contains( 'are beautifully growing on the mountain tops', ); }); it('As editor I can create a Teaser block and overwrite the data which is is not updated when the target is changed', () => { - // GIVEN a Document with the title document and a Document to reference with the title Blue Orchidees + // GIVEN a Document with the title document and a Document to reference with the title Blue Orchids cy.createContent({ contentType: 'Document', - contentId: 'blue-orchidees', - contentTitle: 'Blue Orchidees', + contentId: 'blue-orchids', + contentTitle: 'Blue Orchids', contentDescription: 'are growing on the mountain tops', image: true, path: '/document', @@ -119,21 +119,62 @@ context('Blocks Acceptance Tests', () => { cy.get( '.objectbrowser-field[aria-labelledby="fieldset-default-field-label-href"] button[aria-label="Open object browser"]', ).click(); - cy.get('[aria-label="Select Blue Orchidees"]').dblclick(); + cy.get('[aria-label="Select Blue Orchids"]').dblclick(); cy.wait(500); cy.get('label[for="field-overwrite"]').click(); cy.get('#sidebar-properties #field-title').type(' and Tulips'); cy.get('#toolbar-save').click(); cy.visit('/document'); - cy.get('.block.teaser .content h2').contains('Blue Orchidees and Tulips'); + cy.get('.block.teaser .content h2').contains('Blue Orchids and Tulips'); - cy.visit('/document/blue-orchidees/edit'); + cy.visit('/document/blue-orchids/edit'); cy.get('.documentFirstHeading').type(' but no Tulips'); cy.get('#toolbar-save').click(); - cy.visit('/document/blue-orchidees'); - cy.get('.documentFirstHeading').contains('Blue Orchidees but no Tulips'); + cy.visit('/document/blue-orchids'); + cy.get('.documentFirstHeading').contains('Blue Orchids but no Tulips'); // THEN I still see the overwritten content in the teaser cy.visit('/document'); - cy.get('.block.teaser .content h2').contains('Blue Orchidees and Tulips'); + cy.get('.block.teaser .content h2').contains('Blue Orchids and Tulips'); + }); + + it('As editor I can add a Teaser block and override the data with an external image ', () => { + // GIVEN a Document with the title document and a Document to reference with the title Blue Orchids + cy.createContent({ + contentType: 'Document', + contentId: 'blue-orchids', + contentTitle: 'Blue Orchids', + contentDescription: 'are growing on the mountain tops', + image: true, + path: '/document', + }); + + cy.navigate('/document/edit'); + // WHEN I create a Teaser block and change the data of the referenced object + cy.get('.block .slate-editor [contenteditable=true]').click(); + cy.get('.button .block-add-button').click({ force: true }); + cy.get('.blocks-chooser .mostUsed .button.teaser') + .contains('Teaser') + .click({ force: true }); + cy.get( + '.objectbrowser-field[aria-labelledby="fieldset-default-field-label-href"] button[aria-label="Open object browser"]', + ).click(); + cy.get('[aria-label="Select Blue Orchids"]').dblclick(); + cy.wait(500); + cy.get('input[name="field-overwrite"]').check({ force: true }); + cy.get( + '.objectbrowser-field[aria-labelledby="fieldset-default-field-label-preview_image"]', + ) + .click() + .type( + `https://github.com/plone/volto/raw/main/logos/volto-colorful.png{enter}`, + ); + cy.get('#toolbar-save').click(); + cy.get('.image-wrapper > img') + .should('have.attr', 'src') + .and( + 'include', + 'https://github.com/plone/volto/raw/main/logos/volto-colorful.png', + ); + cy.get('.block.teaser .content h2').contains('Blue Orchids'); }); }); diff --git a/packages/volto/news/.gitkeep b/packages/volto/news/.keep similarity index 100% rename from packages/volto/news/.gitkeep rename to packages/volto/news/.keep diff --git a/packages/volto/news/6147.bugfix b/packages/volto/news/6147.bugfix new file mode 100644 index 0000000000..f8636db7f6 --- /dev/null +++ b/packages/volto/news/6147.bugfix @@ -0,0 +1 @@ +Fix `Teaser` block image override option to render external images and internal images pointing to image scales. @Tishasoumya-02 diff --git a/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx b/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx index e6d87f2236..0ec4a26fdc 100644 --- a/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx +++ b/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx @@ -22,6 +22,7 @@ const TeaserDefaultTemplate = (props) => { const intl = useIntl(); const href = data.href?.[0]; const image = data.preview_image?.[0]; + const url = data.preview_image?.[0]?.['@id']; const Image = config.getComponent('Image').component; const { openExternalLinkInNewTab } = config.settings; @@ -50,16 +51,22 @@ const TeaserDefaultTemplate = (props) => { } >
- {(href.hasPreviewImage || href.image_field || image) && ( + {url && !image?.image_field ? (
- +
+ ) : ( + (href.hasPreviewImage || href.image_field || image) && ( +
+ +
+ ) )}
{data?.head_title && ( From 4e5ecfde9f5c65a1b5f2d70dd2dd1bc7d5749f77 Mon Sep 17 00:00:00 2001 From: Fred van Dijk Date: Thu, 1 Aug 2024 18:42:56 +0200 Subject: [PATCH 14/58] Update volto.blocks behavior name (#6222) --- docs/source/blocks/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/blocks/introduction.md b/docs/source/blocks/introduction.md index c15b827e10..75740c0033 100644 --- a/docs/source/blocks/introduction.md +++ b/docs/source/blocks/introduction.md @@ -82,7 +82,7 @@ You can also add the behavior programmatically via GenericSetup: - + ``` From bca10eafc161991e496f58e263f91c0ff8fe06e5 Mon Sep 17 00:00:00 2001 From: iRohitSingh <61353484+iRohitSingh@users.noreply.github.com> Date: Fri, 2 Aug 2024 21:35:50 +0530 Subject: [PATCH 15/58] Add Cypress test for search block sort on property (#6224) Co-authored-by: Nilesh Co-authored-by: Steve Piercy --- .../tests/core/blocks/blocks-search.js | 110 ++++++++++++++++++ packages/volto/news/6226.bugfix | 1 + 2 files changed, 111 insertions(+) create mode 100644 packages/volto/news/6226.bugfix diff --git a/packages/volto/cypress/tests/core/blocks/blocks-search.js b/packages/volto/cypress/tests/core/blocks/blocks-search.js index 3867349870..6e355829ef 100644 --- a/packages/volto/cypress/tests/core/blocks/blocks-search.js +++ b/packages/volto/cypress/tests/core/blocks/blocks-search.js @@ -558,4 +558,114 @@ describe('Search Block Tests', () => { cy.get('#toolbar-save > .icon').click(); cy.wait(500); }); + it('Search block - test on select 1 sort on in listing criteria sort on', () => { + cy.visit('/'); + cy.get('#toolbar-add > .icon').click(); + cy.get('#toolbar-add-document').click(); + cy.getSlateTitle().focus().click().type('My Search Page'); + + // Add Search listing block + cy.addNewBlock('search'); + + // Add search query criteria + cy.get('#default-query-0-query .react-select__value-container').click(); + cy.get('#default-query-0-query .react-select__option') + .contains('Type') + .click(); + + cy.get('#default-query-0-query .fields:first-of-type > .field').click(); + cy.get( + '#default-query-0-query .fields:first-of-type > .field .react-select__option', + ) + .contains('Page') + .click(); + cy.get( + '#select-listingblock-sort-on > .react-select__control > .react-select__value-container', + ).click(); + cy.findByText('Effective date').click(); + cy.get('.field-wrapper-showSortOn .wrapper .ui label').click(); + //save page + cy.get('#toolbar-save').click(); + + // then we are able to see title and value + cy.get('span.sorted-label').should('have.text', 'Sorted onEffective date'); + cy.get('span.sorted-label-value').should('have.text', 'Effective date'); + // Verify the presence of Ascending button + cy.get('button[title="Ascending"]').should('be.visible'); + // Verify the presence of Descending button + cy.get('button[title="Descending"]').should('be.visible'); + }); + it('Search block - test on only one sort on option below.', () => { + cy.visit('/'); + cy.get('#toolbar-add > .icon').click(); + cy.get('#toolbar-add-document').click(); + cy.getSlateTitle().focus().click().type('My Search Page'); + + // Add Search listing block + cy.addNewBlock('search'); + cy.get('.field-wrapper-showSortOn .wrapper .ui label').click(); + cy.get( + '#field-sortOnOptions > .react-select__control > .react-select__value-container ', + ).click(); + cy.findByText('Effective date').click(); + //save page + cy.get('#toolbar-save').click(); + // then we are able to see label and sort option + cy.get('.sort-label').should('have.text', 'Sort on'); + cy.get('#select-search-sort-on').click(); + cy.findByText('Effective date').click({ force: true }); + cy.get( + 'div#select-search-sort-on.search-react-select-container.css-2b097c-container', + ).contains('Effective date'); + // Verify the presence of Ascending button + cy.get('button[title="Ascending"]').should('be.visible'); + // Verify the presence of Descending button + cy.get('button[title="Descending"]').should('be.visible'); + }); + it('Search block - test on select both listing sort on and sort on options', () => { + cy.visit('/'); + cy.get('#toolbar-add > .icon').click(); + cy.get('#toolbar-add-document').click(); + cy.getSlateTitle().focus().click().type('My Search Page'); + + // Add Search listing block + cy.addNewBlock('search'); + // Add search query criteria + cy.get('#default-query-0-query .react-select__value-container').click(); + cy.get('#default-query-0-query .react-select__option') + .contains('Type') + .click(); + + cy.get('#default-query-0-query .fields:first-of-type > .field').click(); + cy.get( + '#default-query-0-query .fields:first-of-type > .field .react-select__option', + ) + .contains('Page') + .click(); + cy.get( + '#select-listingblock-sort-on > .react-select__control > .react-select__value-container', + ).click(); + cy.findByText('Order in folder').click(); + // Add one sort on options below + cy.get('.field-wrapper-showSortOn .wrapper .ui label').click(); + cy.get('#field-sortOnOptions').click(); + cy.findByText('Effective date').click(); + // save page + cy.get('#toolbar-save').click(); + // then we are able to see label and sort option + cy.get('.sort-label').should('have.text', 'Sort on'); + cy.get('#select-search-sort-on').click(); + cy.findByText('Effective date').click({ force: true }); + cy.get( + 'div#select-search-sort-on.search-react-select-container.css-2b097c-container', + ).contains('Effective date'); + cy.get('#select-search-sort-on').click(); + cy.get( + 'div#select-search-sort-on.search-react-select-container.css-2b097c-container', + ).contains('Order in folder'); + // Verify the presence of Ascending button + cy.get('button[title="Ascending"]').should('be.visible'); + // Verify the presence of Descending button + cy.get('button[title="Descending"]').should('be.visible'); + }); }); diff --git a/packages/volto/news/6226.bugfix b/packages/volto/news/6226.bugfix new file mode 100644 index 0000000000..a571bd1d1e --- /dev/null +++ b/packages/volto/news/6226.bugfix @@ -0,0 +1 @@ +Add Cypress test for search block sort on property. @iRohitSingh From 9e312ed0b1a977bcc42a2e6a7d1b9406ae7fb605 Mon Sep 17 00:00:00 2001 From: David Glick Date: Fri, 2 Aug 2024 10:39:59 -0700 Subject: [PATCH 16/58] Remove unused i18n message (#6230) --- packages/volto/Makefile | 2 +- .../blocks/listing/blocks-listing-templates.js | 18 +++--------------- packages/volto/locales/ca/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/de/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/en/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/es/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/eu/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/fi/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/fr/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/hi/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/it/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/ja/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/nl/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/pt/LC_MESSAGES/volto.po | 5 ----- .../volto/locales/pt_BR/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/ro/LC_MESSAGES/volto.po | 5 ----- packages/volto/locales/volto.pot | 7 +------ .../volto/locales/zh_CN/LC_MESSAGES/volto.po | 5 ----- packages/volto/news/6230.bugfix | 1 + .../manage/Blocks/Search/components/SortOn.jsx | 4 ---- 20 files changed, 6 insertions(+), 101 deletions(-) create mode 100644 packages/volto/news/6230.bugfix diff --git a/packages/volto/Makefile b/packages/volto/Makefile index 39948f2749..79269d6505 100644 --- a/packages/volto/Makefile +++ b/packages/volto/Makefile @@ -155,7 +155,7 @@ deployment-acceptance-test: ## Start Cypress in interactive mode for tests in de .PHONY: deployment-acceptance-web-server-start deployment-acceptance-web-server-start: ## Start the reverse proxy (Traefik) in port 80 for deployment - cd cypress/docker && docker-compose -f seamless.yml up + cd cypress/docker && docker compose -f seamless.yml up .PHONY: deployment-ci-acceptance-test-run-all deployment-ci-acceptance-test-run-all: ## With a single command, run the backend, frontend, and the Cypress tests in headless mode for CI for deployment tests diff --git a/packages/volto/cypress/tests/core/blocks/listing/blocks-listing-templates.js b/packages/volto/cypress/tests/core/blocks/listing/blocks-listing-templates.js index 37af70bdcf..5de2f56cb5 100644 --- a/packages/volto/cypress/tests/core/blocks/listing/blocks-listing-templates.js +++ b/packages/volto/cypress/tests/core/blocks/listing/blocks-listing-templates.js @@ -34,11 +34,7 @@ describe('Folder Contents Tests', () => { cy.visit('/my-folder/my-document'); cy.get('.edit').click(); - cy.getSlate().click(); - cy.get('button.block-add-button').click(); - cy.get( - '[style="transition: opacity 500ms ease 0ms;"] > :nth-child(2) > .ui', - ).click(); + cy.addNewBlock('listing'); cy.get('#field-variation').click().type('summary{enter}'); cy.get('#toolbar-save').click(); cy.wait('@content'); @@ -74,11 +70,7 @@ describe('Folder Contents Tests', () => { cy.visit('/my-folder/my-document'); cy.get('.edit').click(); - cy.getSlate().click(); - cy.get('button.block-add-button').click(); - cy.get( - '[style="transition: opacity 500ms ease 0ms;"] > :nth-child(2) > .ui', - ).click(); + cy.addNewBlock('listing'); cy.get('#field-variation').click().type('summary{enter}'); cy.get('#toolbar-save').click(); cy.wait('@content'); @@ -116,11 +108,7 @@ describe('Folder Contents Tests', () => { cy.visit('/my-folder/my-document'); cy.get('.edit').click(); - cy.getSlate().click(); - cy.get('button.block-add-button').click(); - cy.get( - '[style="transition: opacity 500ms ease 0ms;"] > :nth-child(2) > .ui', - ).click(); + cy.addNewBlock('listing'); cy.get('#field-variation').click().type('imageGallery{enter}'); cy.get('#toolbar-save').click(); cy.wait('@content'); diff --git a/packages/volto/locales/ca/LC_MESSAGES/volto.po b/packages/volto/locales/ca/LC_MESSAGES/volto.po index 882f8df983..9e0b5e8754 100644 --- a/packages/volto/locales/ca/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ca/LC_MESSAGES/volto.po @@ -3414,11 +3414,6 @@ msgstr "Ordena" msgid "Sort on options" msgstr "Ordena les opcions" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/de/LC_MESSAGES/volto.po b/packages/volto/locales/de/LC_MESSAGES/volto.po index 1089694cd9..6bfbeeef7a 100644 --- a/packages/volto/locales/de/LC_MESSAGES/volto.po +++ b/packages/volto/locales/de/LC_MESSAGES/volto.po @@ -3413,11 +3413,6 @@ msgstr "Sortieren nach" msgid "Sort on options" msgstr "Sortieroptionen" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/en/LC_MESSAGES/volto.po b/packages/volto/locales/en/LC_MESSAGES/volto.po index 29f2346b6e..dd89217da3 100644 --- a/packages/volto/locales/en/LC_MESSAGES/volto.po +++ b/packages/volto/locales/en/LC_MESSAGES/volto.po @@ -3408,11 +3408,6 @@ msgstr "" msgid "Sort on options" msgstr "" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/es/LC_MESSAGES/volto.po b/packages/volto/locales/es/LC_MESSAGES/volto.po index 50bc4e1924..c3425875b0 100644 --- a/packages/volto/locales/es/LC_MESSAGES/volto.po +++ b/packages/volto/locales/es/LC_MESSAGES/volto.po @@ -3415,11 +3415,6 @@ msgstr "Ordenar por" msgid "Sort on options" msgstr "Ordenar por opciones" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/eu/LC_MESSAGES/volto.po b/packages/volto/locales/eu/LC_MESSAGES/volto.po index 6c145f58f7..903ab6ab45 100644 --- a/packages/volto/locales/eu/LC_MESSAGES/volto.po +++ b/packages/volto/locales/eu/LC_MESSAGES/volto.po @@ -3415,11 +3415,6 @@ msgstr "Ordenazioa" msgid "Sort on options" msgstr "Ordenatu aukeren arabera" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/fi/LC_MESSAGES/volto.po b/packages/volto/locales/fi/LC_MESSAGES/volto.po index 34a787d7b8..0d4e30e0a8 100644 --- a/packages/volto/locales/fi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fi/LC_MESSAGES/volto.po @@ -3413,11 +3413,6 @@ msgstr "Järjestys" msgid "Sort on options" msgstr "Lajittelun vaihtoehdot" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/fr/LC_MESSAGES/volto.po b/packages/volto/locales/fr/LC_MESSAGES/volto.po index f50775acbd..511169bdff 100644 --- a/packages/volto/locales/fr/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fr/LC_MESSAGES/volto.po @@ -3415,11 +3415,6 @@ msgstr "Trier sur" msgid "Sort on options" msgstr "Options de tri" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/hi/LC_MESSAGES/volto.po b/packages/volto/locales/hi/LC_MESSAGES/volto.po index a6fc3900ed..a16f6ec8f9 100644 --- a/packages/volto/locales/hi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/hi/LC_MESSAGES/volto.po @@ -3408,11 +3408,6 @@ msgstr "क्रमबद्ध करें द्वारा" msgid "Sort on options" msgstr "क्रमबद्ध करें द्वारा विकल्प" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/it/LC_MESSAGES/volto.po b/packages/volto/locales/it/LC_MESSAGES/volto.po index a00ed897bd..7affb80c3c 100644 --- a/packages/volto/locales/it/LC_MESSAGES/volto.po +++ b/packages/volto/locales/it/LC_MESSAGES/volto.po @@ -3408,11 +3408,6 @@ msgstr "Ordina per" msgid "Sort on options" msgstr "Opzioni di ordinamento" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/ja/LC_MESSAGES/volto.po b/packages/volto/locales/ja/LC_MESSAGES/volto.po index f6cd3cb259..63a0748be2 100644 --- a/packages/volto/locales/ja/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ja/LC_MESSAGES/volto.po @@ -3413,11 +3413,6 @@ msgstr "ソート順" msgid "Sort on options" msgstr "" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/nl/LC_MESSAGES/volto.po b/packages/volto/locales/nl/LC_MESSAGES/volto.po index 567f561d6d..f56b22131d 100644 --- a/packages/volto/locales/nl/LC_MESSAGES/volto.po +++ b/packages/volto/locales/nl/LC_MESSAGES/volto.po @@ -3412,11 +3412,6 @@ msgstr "" msgid "Sort on options" msgstr "" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/pt/LC_MESSAGES/volto.po b/packages/volto/locales/pt/LC_MESSAGES/volto.po index 2c97866879..ab7df60445 100644 --- a/packages/volto/locales/pt/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt/LC_MESSAGES/volto.po @@ -3413,11 +3413,6 @@ msgstr "" msgid "Sort on options" msgstr "" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po index 1a322f378e..dd13938deb 100644 --- a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po @@ -3414,11 +3414,6 @@ msgstr "Ordenado por" msgid "Sort on options" msgstr "Opções de ordenação" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/ro/LC_MESSAGES/volto.po b/packages/volto/locales/ro/LC_MESSAGES/volto.po index ec596db862..62cc1b89ac 100644 --- a/packages/volto/locales/ro/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ro/LC_MESSAGES/volto.po @@ -3408,11 +3408,6 @@ msgstr "Sortare pe" msgid "Sort on options" msgstr "Sortare pe opțiuni" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/volto.pot b/packages/volto/locales/volto.pot index 861418c388..b9df6cdeee 100644 --- a/packages/volto/locales/volto.pot +++ b/packages/volto/locales/volto.pot @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Plone\n" -"POT-Creation-Date: 2024-07-24T09:39:31.887Z\n" +"POT-Creation-Date: 2024-08-02T16:48:19.008Z\n" "Last-Translator: Plone i18n \n" "Language-Team: Plone i18n \n" "Content-Type: text/plain; charset=utf-8\n" @@ -3410,11 +3410,6 @@ msgstr "" msgid "Sort on options" msgstr "" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po index a2bfa17bbc..03009b30df 100644 --- a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po +++ b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po @@ -3414,11 +3414,6 @@ msgstr "排序" msgid "Sort on options" msgstr "根据选项排序" -#. Default: "Sort on {value}" -#: components/manage/Blocks/Search/components/SortOn -msgid "Sort on {value}" -msgstr "" - #. Default: "Sort transactions by User-Name, Path or Date" #: components/manage/Controlpanels/UndoControlpanel msgid "Sort transactions by User-Name, Path or Date" diff --git a/packages/volto/news/6230.bugfix b/packages/volto/news/6230.bugfix new file mode 100644 index 0000000000..7e8bc151cb --- /dev/null +++ b/packages/volto/news/6230.bugfix @@ -0,0 +1 @@ +Remove unused i18n message for SortOn component. @davisagli diff --git a/packages/volto/src/components/manage/Blocks/Search/components/SortOn.jsx b/packages/volto/src/components/manage/Blocks/Search/components/SortOn.jsx index de32097c3a..54a0730e6f 100644 --- a/packages/volto/src/components/manage/Blocks/Search/components/SortOn.jsx +++ b/packages/volto/src/components/manage/Blocks/Search/components/SortOn.jsx @@ -22,10 +22,6 @@ const messages = defineMessages({ id: 'Sort on', defaultMessage: 'Sort on', }, - sortOnButtonTitle: { - id: 'Sort on {value}', - defaultMessage: 'Sort on {value}', - }, ascending: { id: 'Ascending', defaultMessage: 'Ascending', From a9247147301ea1c364a347610e9ce1fd159596b4 Mon Sep 17 00:00:00 2001 From: Wesley Barroso Lopes Date: Fri, 2 Aug 2024 15:29:39 -0300 Subject: [PATCH 17/58] Displays validation error messages on control panel forms (#5950) Co-authored-by: David Glick --- .../volto/locales/ca/LC_MESSAGES/volto.po | 1 + .../volto/locales/de/LC_MESSAGES/volto.po | 1 + .../volto/locales/en/LC_MESSAGES/volto.po | 1 + .../volto/locales/es/LC_MESSAGES/volto.po | 1 + .../volto/locales/eu/LC_MESSAGES/volto.po | 1 + .../volto/locales/fi/LC_MESSAGES/volto.po | 1 + .../volto/locales/fr/LC_MESSAGES/volto.po | 1 + .../volto/locales/hi/LC_MESSAGES/volto.po | 1 + .../volto/locales/it/LC_MESSAGES/volto.po | 1 + .../volto/locales/ja/LC_MESSAGES/volto.po | 1 + .../volto/locales/nl/LC_MESSAGES/volto.po | 1 + .../volto/locales/pt/LC_MESSAGES/volto.po | 1 + .../volto/locales/pt_BR/LC_MESSAGES/volto.po | 1 + .../volto/locales/ro/LC_MESSAGES/volto.po | 1 + packages/volto/locales/volto.pot | 1 + .../volto/locales/zh_CN/LC_MESSAGES/volto.po | 1 + packages/volto/news/5274.bugfix | 1 + .../volto/src/components/manage/Add/Add.jsx | 10 ++- .../manage/Controlpanels/Controlpanel.jsx | 43 +++++++++- .../Controlpanels/Controlpanel.test.jsx | 79 +++++++++++++------ .../__snapshots__/Controlpanel.test.jsx.snap | 29 +++++-- .../volto/src/components/manage/Edit/Edit.jsx | 6 +- .../helpers/FormValidation/FormValidation.jsx | 19 ++++- packages/volto/src/helpers/index.js | 1 + 24 files changed, 163 insertions(+), 41 deletions(-) create mode 100644 packages/volto/news/5274.bugfix diff --git a/packages/volto/locales/ca/LC_MESSAGES/volto.po b/packages/volto/locales/ca/LC_MESSAGES/volto.po index 9e0b5e8754..152f81c308 100644 --- a/packages/volto/locales/ca/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ca/LC_MESSAGES/volto.po @@ -1334,6 +1334,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/de/LC_MESSAGES/volto.po b/packages/volto/locales/de/LC_MESSAGES/volto.po index 6bfbeeef7a..d2137d2acc 100644 --- a/packages/volto/locales/de/LC_MESSAGES/volto.po +++ b/packages/volto/locales/de/LC_MESSAGES/volto.po @@ -1333,6 +1333,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/en/LC_MESSAGES/volto.po b/packages/volto/locales/en/LC_MESSAGES/volto.po index dd89217da3..1e8ca22c1b 100644 --- a/packages/volto/locales/en/LC_MESSAGES/volto.po +++ b/packages/volto/locales/en/LC_MESSAGES/volto.po @@ -1328,6 +1328,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/es/LC_MESSAGES/volto.po b/packages/volto/locales/es/LC_MESSAGES/volto.po index c3425875b0..2772fbcc9e 100644 --- a/packages/volto/locales/es/LC_MESSAGES/volto.po +++ b/packages/volto/locales/es/LC_MESSAGES/volto.po @@ -1335,6 +1335,7 @@ msgstr "Entradas" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/eu/LC_MESSAGES/volto.po b/packages/volto/locales/eu/LC_MESSAGES/volto.po index 903ab6ab45..574ed30cec 100644 --- a/packages/volto/locales/eu/LC_MESSAGES/volto.po +++ b/packages/volto/locales/eu/LC_MESSAGES/volto.po @@ -1335,6 +1335,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/fi/LC_MESSAGES/volto.po b/packages/volto/locales/fi/LC_MESSAGES/volto.po index 0d4e30e0a8..09d7ae120a 100644 --- a/packages/volto/locales/fi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fi/LC_MESSAGES/volto.po @@ -1333,6 +1333,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/fr/LC_MESSAGES/volto.po b/packages/volto/locales/fr/LC_MESSAGES/volto.po index 511169bdff..701d23f3ea 100644 --- a/packages/volto/locales/fr/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fr/LC_MESSAGES/volto.po @@ -1335,6 +1335,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/hi/LC_MESSAGES/volto.po b/packages/volto/locales/hi/LC_MESSAGES/volto.po index a16f6ec8f9..0b26754a9a 100644 --- a/packages/volto/locales/hi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/hi/LC_MESSAGES/volto.po @@ -1328,6 +1328,7 @@ msgstr "एन्ट्रीज़" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/it/LC_MESSAGES/volto.po b/packages/volto/locales/it/LC_MESSAGES/volto.po index 7affb80c3c..2bf03717fc 100644 --- a/packages/volto/locales/it/LC_MESSAGES/volto.po +++ b/packages/volto/locales/it/LC_MESSAGES/volto.po @@ -1328,6 +1328,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/ja/LC_MESSAGES/volto.po b/packages/volto/locales/ja/LC_MESSAGES/volto.po index 63a0748be2..d3e2dd35e8 100644 --- a/packages/volto/locales/ja/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ja/LC_MESSAGES/volto.po @@ -1333,6 +1333,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/nl/LC_MESSAGES/volto.po b/packages/volto/locales/nl/LC_MESSAGES/volto.po index f56b22131d..d8ccaef78d 100644 --- a/packages/volto/locales/nl/LC_MESSAGES/volto.po +++ b/packages/volto/locales/nl/LC_MESSAGES/volto.po @@ -1332,6 +1332,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/pt/LC_MESSAGES/volto.po b/packages/volto/locales/pt/LC_MESSAGES/volto.po index ab7df60445..4325a6eaab 100644 --- a/packages/volto/locales/pt/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt/LC_MESSAGES/volto.po @@ -1333,6 +1333,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po index dd13938deb..415afc646a 100644 --- a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po @@ -1334,6 +1334,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/ro/LC_MESSAGES/volto.po b/packages/volto/locales/ro/LC_MESSAGES/volto.po index 62cc1b89ac..ba0d929a5f 100644 --- a/packages/volto/locales/ro/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ro/LC_MESSAGES/volto.po @@ -1328,6 +1328,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/volto.pot b/packages/volto/locales/volto.pot index b9df6cdeee..4af08ba0af 100644 --- a/packages/volto/locales/volto.pot +++ b/packages/volto/locales/volto.pot @@ -1330,6 +1330,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po index 03009b30df..c58f7fcf7a 100644 --- a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po +++ b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po @@ -1334,6 +1334,7 @@ msgstr "" #: components/manage/Add/Add #: components/manage/Controlpanels/AddonsControlpanel #: components/manage/Controlpanels/ContentTypeSchema +#: components/manage/Controlpanels/Controlpanel #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Edit/Edit #: components/manage/Form/InlineForm diff --git a/packages/volto/news/5274.bugfix b/packages/volto/news/5274.bugfix new file mode 100644 index 0000000000..da9be01184 --- /dev/null +++ b/packages/volto/news/5274.bugfix @@ -0,0 +1 @@ +Displays validation error messages on control panel forms. @wesleybl diff --git a/packages/volto/src/components/manage/Add/Add.jsx b/packages/volto/src/components/manage/Add/Add.jsx index 1c4fb668e6..4c91fda09c 100644 --- a/packages/volto/src/components/manage/Add/Add.jsx +++ b/packages/volto/src/components/manage/Add/Add.jsx @@ -5,7 +5,11 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { BodyClass, Helmet } from '@plone/volto/helpers'; +import { + BodyClass, + Helmet, + extractInvariantErrors, +} from '@plone/volto/helpers'; import { connect } from 'react-redux'; import { compose } from 'redux'; import { keys, isEmpty } from 'lodash'; @@ -182,9 +186,7 @@ class Add extends Component { const errorsList = tryParseJSON(error); let erroMessage; if (Array.isArray(errorsList)) { - const invariantErrors = errorsList - .filter((errorItem) => !('field' in errorItem)) - .map((errorItem) => errorItem['message']); + const invariantErrors = extractInvariantErrors(errorsList); if (invariantErrors.length > 0) { // Plone invariant validation message. erroMessage = invariantErrors.join(' - '); diff --git a/packages/volto/src/components/manage/Controlpanels/Controlpanel.jsx b/packages/volto/src/components/manage/Controlpanels/Controlpanel.jsx index a88acc3ae0..9970c962b5 100644 --- a/packages/volto/src/components/manage/Controlpanels/Controlpanel.jsx +++ b/packages/volto/src/components/manage/Controlpanels/Controlpanel.jsx @@ -8,7 +8,11 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { compose } from 'redux'; import { withRouter } from 'react-router-dom'; -import { Helmet } from '@plone/volto/helpers'; +import { + Helmet, + tryParseJSON, + extractInvariantErrors, +} from '@plone/volto/helpers'; import { createPortal } from 'react-dom'; import { Button, Container } from 'semantic-ui-react'; import { defineMessages, injectIntl } from 'react-intl'; @@ -44,6 +48,10 @@ const messages = defineMessages({ id: 'Info', defaultMessage: 'Info', }, + error: { + id: 'Error', + defaultMessage: 'Error', + }, }); /** @@ -93,7 +101,7 @@ class Controlpanel extends Component { super(props); this.onCancel = this.onCancel.bind(this); this.onSubmit = this.onSubmit.bind(this); - this.state = { isClient: false }; + this.state = { isClient: false, error: null }; } /** @@ -113,6 +121,36 @@ class Controlpanel extends Component { * @returns {undefined} */ UNSAFE_componentWillReceiveProps(nextProps) { + if (this.props.updateRequest.loading && nextProps.updateRequest.error) { + const message = + nextProps.updateRequest.error?.response?.body?.error?.message || + nextProps.updateRequest.error?.response?.body?.message || + nextProps.updateRequest.error?.response?.text || + ''; + + const error = + new DOMParser().parseFromString(message, 'text/html')?.all?.[0] + ?.textContent || message; + + const errorsList = tryParseJSON(error); + let invariantErrors = []; + if (Array.isArray(errorsList)) { + invariantErrors = extractInvariantErrors(errorsList); + } + + this.setState({ error: error }); + + if (invariantErrors.length > 0) { + toast.error( + , + ); + } + } + if (this.props.updateRequest.loading && nextProps.updateRequest.loaded) { toast.info( + jest.fn(({ requestError }) => ( +
+ {requestError ? `requestError : ${requestError}` : null} +
+ )), +); jest.mock('../Toolbar/Toolbar', () => jest.fn(() =>
)); +const store = mockStore({ + controlpanels: { + controlpanel: { + '@id': 'http://localhost:8080/Plone/@controlpanels/date-and-time', + title: 'Date and Time', + schema: { + fieldsets: [], + properties: [], + }, + data: {}, + }, + update: { + loading: false, + loaded: true, + error: { response: { body: { message: null } } }, + }, + }, + intl: { + locale: 'en', + messages: {}, + }, +}); + describe('Controlpanel', () => { it('renders a controlpanel component', () => { - const store = mockStore({ - controlpanels: { - controlpanel: { - '@id': 'http://localhost:8080/Plone/@controlpanels/date-and-time', - title: 'Date and Time', - schema: { - fieldsets: [], - properties: [], - }, - data: {}, - }, - update: { - loading: false, - loaded: true, - }, - }, - intl: { - locale: 'en', - messages: {}, - }, - }); const { container } = render( @@ -45,4 +52,28 @@ describe('Controlpanel', () => { expect(container).toMatchSnapshot(); }); + it('renders a controlpanel component with error', async () => { + const { container, rerender } = render( + + + +
+
+
, + ); + store.getState().controlpanels.update.loading = true; + store.getState().controlpanels.update.error.response.body.message = + "[{'message': 'Twitter username should not include the \"@\" prefix character.', 'field': 'twitter_username', 'error': 'ValidationError'}]"; + + rerender( + + + +
+
+
, + ); + await waitFor(() => screen.findByText(/Twitter/i)); + expect(container).toMatchSnapshot(); + }); }); diff --git a/packages/volto/src/components/manage/Controlpanels/__snapshots__/Controlpanel.test.jsx.snap b/packages/volto/src/components/manage/Controlpanels/__snapshots__/Controlpanel.test.jsx.snap index 2467ec704c..7af8cfbb54 100644 --- a/packages/volto/src/components/manage/Controlpanels/__snapshots__/Controlpanel.test.jsx.snap +++ b/packages/volto/src/components/manage/Controlpanels/__snapshots__/Controlpanel.test.jsx.snap @@ -1,6 +1,25 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Controlpanel renders a controlpanel component 1`] = ` +
+
+
+
+
+
+
+
+`; + +exports[`Controlpanel renders a controlpanel component with error 1`] = `
+ id="form" + > + requestError : [{'message': 'Twitter username should not include the "@" prefix character.', 'field': 'twitter_username', 'error': 'ValidationError'}] +
!('field' in errorItem)) - .map((errorItem) => errorItem['message']); + const invariantErrors = extractInvariantErrors(errorsList); if (invariantErrors.length > 0) { // Plone invariant validation message. erroMessage = invariantErrors.join(' - '); diff --git a/packages/volto/src/helpers/FormValidation/FormValidation.jsx b/packages/volto/src/helpers/FormValidation/FormValidation.jsx index f423f49d5a..5aa25dc0c4 100644 --- a/packages/volto/src/helpers/FormValidation/FormValidation.jsx +++ b/packages/volto/src/helpers/FormValidation/FormValidation.jsx @@ -35,7 +35,14 @@ export const tryParseJSON = (requestItem) => { try { resultObj = JSON.parse(requestItem.replace(/'/g, '"')); } catch (e) { - resultObj = null; + try { + // Treats strings like: `'String "double quotes"'` + resultObj = JSON.parse( + requestItem.replace(/"/g, '\\"').replace(/'/g, '"'), + ); + } catch (e) { + resultObj = null; + } } } return resultObj; @@ -338,3 +345,13 @@ export const validateFileUploadSize = (file, intlFunc) => { } return isValid; }; + +/** + * Extract invariant errors given an array of errors. + * @param {Array} erros + */ +export const extractInvariantErrors = (erros) => { + return erros + .filter((errorItem) => !('field' in errorItem)) + .map((errorItem) => errorItem['message']); +}; diff --git a/packages/volto/src/helpers/index.js b/packages/volto/src/helpers/index.js index 2988e16fbe..a7f2625f09 100644 --- a/packages/volto/src/helpers/index.js +++ b/packages/volto/src/helpers/index.js @@ -85,6 +85,7 @@ export { default as Helmet } from './Helmet/Helmet'; export { default as FormValidation } from './FormValidation/FormValidation'; export { validateFileUploadSize } from './FormValidation/FormValidation'; export { tryParseJSON } from './FormValidation/FormValidation'; +export { extractInvariantErrors } from './FormValidation/FormValidation'; export { difference, getColor, From be9caf74300ce944d9e89ef9c5d4ad380e73fe97 Mon Sep 17 00:00:00 2001 From: Mauro Amico Date: Tue, 6 Aug 2024 02:30:41 +0200 Subject: [PATCH 18/58] improve url regex (#6186) Co-authored-by: Steve Piercy --- packages/volto/news/6186.feature | 1 + packages/volto/src/helpers/Url/Url.test.js | 17 +++++++++++++++-- packages/volto/src/helpers/Url/urlRegex.js | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 packages/volto/news/6186.feature diff --git a/packages/volto/news/6186.feature b/packages/volto/news/6186.feature new file mode 100644 index 0000000000..bd02ea020f --- /dev/null +++ b/packages/volto/news/6186.feature @@ -0,0 +1 @@ +Improved URL regex to allow non-public or intranet URLs, such as `https://intranet/` or `file://server/share`. @mamico diff --git a/packages/volto/src/helpers/Url/Url.test.js b/packages/volto/src/helpers/Url/Url.test.js index c3e9491e95..b85ef9fc3a 100644 --- a/packages/volto/src/helpers/Url/Url.test.js +++ b/packages/volto/src/helpers/Url/Url.test.js @@ -255,11 +255,24 @@ describe('Url', () => { }); it('isUrl test 4', () => { const href = `https://www`; - expect(isUrl(href)).toBe(false); + expect(isUrl(href)).toBe(true); }); it('isUrl test 5', () => { + const href = `https://www/foo/bar`; + expect(isUrl(href)).toBe(true); + }); + it('isUrl test 6', () => { + // at the end of the day, this is a strange, but valid, URL const href = `www.e`; - expect(isUrl(href)).toBe(false); + expect(isUrl(href)).toBe(true); + }); + it('isUrl test 7', () => { + const href = `file://server/folder/file.txt`; + expect(isUrl(href)).toBe(true); + }); + it('isUrl test 8', () => { + const href = `file://server.dir.internal/folder/file.txt`; + expect(isUrl(href)).toBe(true); }); }); describe('getFieldURL', () => { diff --git a/packages/volto/src/helpers/Url/urlRegex.js b/packages/volto/src/helpers/Url/urlRegex.js index b4a21031da..c2de8f0f18 100644 --- a/packages/volto/src/helpers/Url/urlRegex.js +++ b/packages/volto/src/helpers/Url/urlRegex.js @@ -52,7 +52,7 @@ export const urlRegex = (_opts) => { })\\.?`; const port = '(?::\\d{2,5})?'; const path = '(?:[/?#][^\\s"]*)?'; - const regex = `(?:${protocol}|www\\.)${auth}(?:localhost|${ip}|${host}${domain}${tld})${port}${path}`; + const regex = `(?:${protocol}|www\\.)${auth}(?:localhost|${ip}|${host}${domain}${tld}|${host})${port}${path}`; return opts.exact ? new RegExp(`(?:^${regex}$)`, 'i') From a462acf25ed133ce3e1b7c31d6f74d0d3d707f9c Mon Sep 17 00:00:00 2001 From: David Glick Date: Mon, 5 Aug 2024 22:27:41 -0700 Subject: [PATCH 19/58] Return 302 response from SSR link view for logged out users (#6235) --- packages/volto/news/6235.bugfix | 1 + packages/volto/src/components/theme/View/LinkView.jsx | 4 ++++ packages/volto/src/components/theme/View/LinkView.test.jsx | 2 ++ 3 files changed, 7 insertions(+) create mode 100644 packages/volto/news/6235.bugfix diff --git a/packages/volto/news/6235.bugfix b/packages/volto/news/6235.bugfix new file mode 100644 index 0000000000..8967eedb2a --- /dev/null +++ b/packages/volto/news/6235.bugfix @@ -0,0 +1 @@ +Return a 302 response for server-side rendering of the Link view for unauthenticated users. @davisagli diff --git a/packages/volto/src/components/theme/View/LinkView.jsx b/packages/volto/src/components/theme/View/LinkView.jsx index f7a490ae17..e2f8538612 100644 --- a/packages/volto/src/components/theme/View/LinkView.jsx +++ b/packages/volto/src/components/theme/View/LinkView.jsx @@ -4,6 +4,7 @@ import { useHistory } from 'react-router-dom'; import { isInternalURL, flattenToAppURL } from '@plone/volto/helpers'; import { Container as SemanticContainer } from 'semantic-ui-react'; import { UniversalLink } from '@plone/volto/components'; +import { Redirect } from 'react-router-dom'; import { FormattedMessage } from 'react-intl'; import config from '@plone/volto/registry'; @@ -19,6 +20,9 @@ const LinkView = ({ token, content }) => { } } }, [content, history, token]); + if (__SERVER__ && !token && content.remoteUrl) { + return ; + } const { title, description, remoteUrl } = content; const { openExternalLinkInNewTab } = config.settings; const Container = diff --git a/packages/volto/src/components/theme/View/LinkView.test.jsx b/packages/volto/src/components/theme/View/LinkView.test.jsx index c9577d54f1..d8406a9a9d 100644 --- a/packages/volto/src/components/theme/View/LinkView.test.jsx +++ b/packages/volto/src/components/theme/View/LinkView.test.jsx @@ -7,6 +7,8 @@ import LinkView from './LinkView'; const mockStore = configureStore(); +global.__SERVER__ = false; // eslint-disable-line no-underscore-dangle + const store = mockStore({ userSession: { token: null, From 65e95471f06b0c2d0c7565bf7bcfee883d12b6ce Mon Sep 17 00:00:00 2001 From: David Glick Date: Mon, 5 Aug 2024 22:30:35 -0700 Subject: [PATCH 20/58] Fix loading .cjs in webpack (#6237) --- packages/volto/news/6237.bugfix | 1 + packages/volto/razzle.config.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 packages/volto/news/6237.bugfix diff --git a/packages/volto/news/6237.bugfix b/packages/volto/news/6237.bugfix new file mode 100644 index 0000000000..7bb075eac5 --- /dev/null +++ b/packages/volto/news/6237.bugfix @@ -0,0 +1 @@ +Fix loading of .cjs in webpack. @davisagli diff --git a/packages/volto/razzle.config.js b/packages/volto/razzle.config.js index f97363c904..b2ec16d7f7 100644 --- a/packages/volto/razzle.config.js +++ b/packages/volto/razzle.config.js @@ -277,7 +277,7 @@ const defaultModify = ({ // Don't load SVGs from ./src/icons with file-loader const fileLoader = config.module.rules.find(fileLoaderFinder); fileLoader.exclude = [ - /\.(config|variables|overrides)$/, + /\.(config|variables|overrides|cjs)$/, /icons\/.*\.svg$/, ...fileLoader.exclude, ]; From 1d58a3062037b428267a953b7d99d61351e3eae1 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 6 Aug 2024 01:11:26 -0700 Subject: [PATCH 21/58] Fix linkcheckbroken in docs (#6239) --- docs/source/contributing/acceptance-tests.md | 2 +- packages/volto/news/6239.documentation | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 packages/volto/news/6239.documentation diff --git a/docs/source/contributing/acceptance-tests.md b/docs/source/contributing/acceptance-tests.md index 6dd2a313ca..ea9b66ec1f 100644 --- a/docs/source/contributing/acceptance-tests.md +++ b/docs/source/contributing/acceptance-tests.md @@ -137,7 +137,7 @@ dispatch( ``` ```{seealso} -[Testing Redux Store](https://www.cypress.io/blog/2018/11/14/testing-redux-store/) +[Testing Redux Store](https://www.cypress.io/blog/testing-redux-store) ``` diff --git a/packages/volto/news/6239.documentation b/packages/volto/news/6239.documentation new file mode 100644 index 0000000000..84013472bd --- /dev/null +++ b/packages/volto/news/6239.documentation @@ -0,0 +1 @@ +Fix redirect of link in documentation to testing Redux store blog post. @stevepiercy From ac85e20d896a2aa99823bce4c3671cbc09515b1d Mon Sep 17 00:00:00 2001 From: Giulia Ghisini <51911425+giuliaghisini@users.noreply.github.com> Date: Tue, 6 Aug 2024 18:10:12 +0200 Subject: [PATCH 22/58] fix: handle form.ui.hovered (#6240) --- packages/volto/news/6240.bugfix | 2 ++ .../components/manage/Blocks/Block/Edit.jsx | 32 ++++++++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 packages/volto/news/6240.bugfix diff --git a/packages/volto/news/6240.bugfix b/packages/volto/news/6240.bugfix new file mode 100644 index 0000000000..f69fbc8a6c --- /dev/null +++ b/packages/volto/news/6240.bugfix @@ -0,0 +1,2 @@ +fixed change of form.ui.hovered when editing blocks, because if you have a FormBlock component inside another one, +onMouseOver fires for all stacked blocks and you cannot use the nested form. @giuliaghisini diff --git a/packages/volto/src/components/manage/Blocks/Block/Edit.jsx b/packages/volto/src/components/manage/Blocks/Block/Edit.jsx index 4b9c4419a5..19fd9a932e 100644 --- a/packages/volto/src/components/manage/Blocks/Block/Edit.jsx +++ b/packages/volto/src/components/manage/Blocks/Block/Edit.jsx @@ -144,12 +144,14 @@ export class Edit extends Component { {Block !== null ? (
{ + onMouseEnter={(e) => { + e.preventDefault(); + e.stopPropagation(); if (this.props.hovered !== this.props.id) { this.props.setUIState({ hovered: this.props.id }); } }} - onFocus={() => { + onFocus={(e) => { // TODO: This `onFocus` steals somehow the focus from the slate block // we have to investigate why this is happening // Apparently, I can't see any difference in the behavior @@ -158,7 +160,11 @@ export class Edit extends Component { // this.props.setUIState({ hovered: this.props.id }); // } }} - onMouseLeave={() => this.props.setUIState({ hovered: null })} + onMouseLeave={(e) => { + e.preventDefault(); + e.stopPropagation(); + this.props.setUIState({ hovered: null }); + }} onClick={(e) => { const isMultipleSelection = e.shiftKey || e.ctrlKey || e.metaKey; !this.props.selected && @@ -207,11 +213,21 @@ export class Edit extends Component { ) : (
- this.props.setUIState({ hovered: this.props.id }) - } - onFocus={() => this.props.setUIState({ hovered: this.props.id })} - onMouseLeave={() => this.props.setUIState({ hovered: null })} + onMouseEnter={(e) => { + e.preventDefault(); + e.stopPropagation(); + this.props.setUIState({ hovered: this.props.id }); + }} + onFocus={(e) => { + e.preventDefault(); + e.stopPropagation(); + this.props.setUIState({ hovered: this.props.id }); + }} + onMouseLeave={(e) => { + e.preventDefault(); + e.stopPropagation(); + this.props.setUIState({ hovered: null }); + }} onClick={() => !this.props.selected && this.props.onSelectBlock(this.props.id) } From 468317c0a7c2de69bc1b0225695b53f97a67d21f Mon Sep 17 00:00:00 2001 From: Wesley Barroso Lopes Date: Wed, 7 Aug 2024 12:22:41 -0300 Subject: [PATCH 23/58] Upgrade Cypress to 13.13.2 (#6241) --- packages/volto-testing/package.json | 2 +- packages/volto/news/6241.internal | 1 + packages/volto/package.json | 2 +- pnpm-lock.yaml | 95 +++++++++++++++++++++++++---- 4 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 packages/volto/news/6241.internal diff --git a/packages/volto-testing/package.json b/packages/volto-testing/package.json index fdbb555e78..bbee5af914 100644 --- a/packages/volto-testing/package.json +++ b/packages/volto-testing/package.json @@ -41,7 +41,7 @@ "@testing-library/jest-dom": "6.4.2", "@testing-library/react": "12.1.5", "axe-core": "4.8.4", - "cypress": "13.6.6", + "cypress": "13.13.2", "cypress-axe": "1.5.0", "cypress-file-upload": "5.0.8" }, diff --git a/packages/volto/news/6241.internal b/packages/volto/news/6241.internal new file mode 100644 index 0000000000..d157a14bc8 --- /dev/null +++ b/packages/volto/news/6241.internal @@ -0,0 +1 @@ +Upgrade Cypress to 13.13.2. @wesleybl diff --git a/packages/volto/package.json b/packages/volto/package.json index 8c84fe6c5d..03ee312ad8 100644 --- a/packages/volto/package.json +++ b/packages/volto/package.json @@ -325,7 +325,7 @@ "bundlewatch": "0.3.3", "circular-dependency-plugin": "5.2.2", "css-loader": "5.2.7", - "cypress": "13.6.6", + "cypress": "13.13.2", "cypress-axe": "1.5.0", "cypress-file-upload": "5.0.8", "deep-freeze": "0.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 61bb21e993..641f15931c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2045,7 +2045,7 @@ importers: version: 8.0.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@testing-library/cypress': specifier: 10.0.1 - version: 10.0.1(cypress@13.6.6) + version: 10.0.1(cypress@13.13.2) '@testing-library/jest-dom': specifier: 6.4.2 version: 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@26.6.3)(vitest@1.5.0(@types/node@20.12.7)(jsdom@16.7.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) @@ -2122,14 +2122,14 @@ importers: specifier: 5.2.7 version: 5.2.7(webpack@5.90.1(esbuild@0.20.2)) cypress: - specifier: 13.6.6 - version: 13.6.6 + specifier: 13.13.2 + version: 13.13.2 cypress-axe: specifier: 1.5.0 - version: 1.5.0(axe-core@4.4.2)(cypress@13.6.6) + version: 1.5.0(axe-core@4.4.2)(cypress@13.13.2) cypress-file-upload: specifier: 5.0.8 - version: 5.0.8(cypress@13.6.6) + version: 5.0.8(cypress@13.13.2) deep-freeze: specifier: 0.0.1 version: 0.0.1 @@ -2397,7 +2397,7 @@ importers: dependencies: '@testing-library/cypress': specifier: 10.0.1 - version: 10.0.1(cypress@13.6.6) + version: 10.0.1(cypress@13.13.2) '@testing-library/jest-dom': specifier: 6.4.2 version: 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(vitest@1.5.0(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) @@ -2408,14 +2408,14 @@ importers: specifier: 4.8.4 version: 4.8.4 cypress: - specifier: 13.6.6 - version: 13.6.6 + specifier: 13.13.2 + version: 13.13.2 cypress-axe: specifier: 1.5.0 - version: 1.5.0(axe-core@4.8.4)(cypress@13.6.6) + version: 1.5.0(axe-core@4.8.4)(cypress@13.13.2) cypress-file-upload: specifier: 5.0.8 - version: 5.0.8(cypress@13.6.6) + version: 5.0.8(cypress@13.13.2) devDependencies: release-it: specifier: ^17.1.1 @@ -10138,6 +10138,11 @@ packages: peerDependencies: cypress: '>3.0.0' + cypress@13.13.2: + resolution: {integrity: sha512-PvJQU33933NvS1StfzEb8/mu2kMy4dABwCF+yd5Bi7Qly1HOVf+Bufrygee/tlmty/6j5lX+KIi8j9Q3JUMbhA==} + engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} + hasBin: true + cypress@13.6.6: resolution: {integrity: sha512-S+2S9S94611hXimH9a3EAYt81QM913ZVA03pUmGDfLTFa5gyp85NJ8dJGSlEAEmyRsYkioS1TtnWtbv/Fzt11A==} engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} @@ -18132,6 +18137,10 @@ packages: resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} engines: {node: '>=8.17.0'} + tmp@0.2.3: + resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + engines: {node: '>=14.14'} + tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -27553,6 +27562,12 @@ snapshots: '@tanstack/store@0.1.3': {} + '@testing-library/cypress@10.0.1(cypress@13.13.2)': + dependencies: + '@babel/runtime': 7.20.6 + '@testing-library/dom': 9.3.4 + cypress: 13.13.2 + '@testing-library/cypress@10.0.1(cypress@13.6.6)': dependencies: '@babel/runtime': 7.20.6 @@ -31135,20 +31150,74 @@ snapshots: cyclist@1.0.2: {} + cypress-axe@1.5.0(axe-core@4.4.2)(cypress@13.13.2): + dependencies: + axe-core: 4.4.2 + cypress: 13.13.2 + cypress-axe@1.5.0(axe-core@4.4.2)(cypress@13.6.6): dependencies: axe-core: 4.4.2 cypress: 13.6.6 - cypress-axe@1.5.0(axe-core@4.8.4)(cypress@13.6.6): + cypress-axe@1.5.0(axe-core@4.8.4)(cypress@13.13.2): dependencies: axe-core: 4.8.4 - cypress: 13.6.6 + cypress: 13.13.2 + + cypress-file-upload@5.0.8(cypress@13.13.2): + dependencies: + cypress: 13.13.2 cypress-file-upload@5.0.8(cypress@13.6.6): dependencies: cypress: 13.6.6 + cypress@13.13.2: + dependencies: + '@cypress/request': 3.0.1 + '@cypress/xvfb': 1.2.4(supports-color@8.1.1) + '@types/sinonjs__fake-timers': 8.1.1 + '@types/sizzle': 2.3.8 + arch: 2.2.0 + blob-util: 2.0.2 + bluebird: 3.7.2 + buffer: 5.7.1 + cachedir: 2.4.0 + chalk: 4.1.2 + check-more-types: 2.24.0 + cli-cursor: 3.1.0 + cli-table3: 0.6.4 + commander: 6.2.1 + common-tags: 1.8.2 + dayjs: 1.11.10 + debug: 4.3.4(supports-color@8.1.1) + enquirer: 2.4.1 + eventemitter2: 6.4.7 + execa: 4.1.0 + executable: 4.1.1 + extract-zip: 2.0.1(supports-color@8.1.1) + figures: 3.2.0 + fs-extra: 9.1.0 + getos: 3.2.1 + is-ci: 3.0.1 + is-installed-globally: 0.4.0 + lazy-ass: 1.6.0 + listr2: 3.14.0(enquirer@2.4.1) + lodash: 4.17.21 + log-symbols: 4.1.0 + minimist: 1.2.8 + ospath: 1.2.2 + pretty-bytes: 5.6.0 + process: 0.11.10 + proxy-from-env: 1.0.0 + request-progress: 3.0.0 + semver: 7.6.0 + supports-color: 8.1.1 + tmp: 0.2.3 + untildify: 4.0.0 + yauzl: 2.10.0 + cypress@13.6.6: dependencies: '@cypress/request': 3.0.1 @@ -42554,6 +42623,8 @@ snapshots: dependencies: rimraf: 3.0.2 + tmp@0.2.3: {} + tmpl@1.0.5: {} to-arraybuffer@1.0.1: {} From eb611a8485457171a94409fd097906cc8ae4d16a Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 7 Aug 2024 09:58:34 -0700 Subject: [PATCH 24/58] Complete upgrade to Cypress 13.13.2 (#6242) Co-authored-by: David Ichim --- .github/workflows/acceptance.yml | 52 ++++++++++----------- apps/plone/package.json | 2 +- packages/volto/news/6242.internal | 1 + pnpm-lock.yaml | 75 +++---------------------------- 4 files changed, 33 insertions(+), 97 deletions(-) create mode 100644 packages/volto/news/6242.internal diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 0d53b79548..6d93708898 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -37,13 +37,13 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos @@ -85,13 +85,13 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos @@ -133,13 +133,13 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos @@ -181,13 +181,13 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos @@ -229,13 +229,13 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos @@ -277,13 +277,13 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos @@ -324,13 +324,13 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos @@ -372,13 +372,13 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos @@ -420,13 +420,13 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos @@ -469,13 +469,13 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos @@ -571,14 +571,14 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos @@ -627,13 +627,13 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000 http://localhost' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos-seamless @@ -677,13 +677,13 @@ jobs: wait-on: 'npx wait-on --httpTimeout 20000 http-get://127.0.0.1:55001/plone http://127.0.0.1:3000 http://localhost' # Upload Cypress screenshots - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: packages/volto/cypress/screenshots # Upload Cypress videos - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-videos diff --git a/apps/plone/package.json b/apps/plone/package.json index b7bcd8c73d..e87b8c95e3 100644 --- a/apps/plone/package.json +++ b/apps/plone/package.json @@ -288,7 +288,7 @@ "bundlewatch": "0.3.3", "circular-dependency-plugin": "5.2.2", "css-loader": "5.2.7", - "cypress": "13.6.6", + "cypress": "13.13.2", "cypress-axe": "1.5.0", "cypress-file-upload": "5.0.8", "deep-freeze": "0.0.1", diff --git a/packages/volto/news/6242.internal b/packages/volto/news/6242.internal new file mode 100644 index 0000000000..f362b022e0 --- /dev/null +++ b/packages/volto/news/6242.internal @@ -0,0 +1 @@ +Complete upgrade Cypress to 13.13.2. Bump actions/upload-artifact@v1 to v4. @stevepiercy diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 641f15931c..013c2c131a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -463,7 +463,7 @@ importers: version: 8.0.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@testing-library/cypress': specifier: 10.0.1 - version: 10.0.1(cypress@13.6.6) + version: 10.0.1(cypress@13.13.2) '@testing-library/jest-dom': specifier: 6.4.2 version: 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@26.6.3)(vitest@1.5.0(@types/node@20.12.7)(jsdom@16.7.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) @@ -537,14 +537,14 @@ importers: specifier: 5.2.7 version: 5.2.7(webpack@5.90.1(esbuild@0.20.2)) cypress: - specifier: 13.6.6 - version: 13.6.6 + specifier: 13.13.2 + version: 13.13.2 cypress-axe: specifier: 1.5.0 - version: 1.5.0(axe-core@4.4.2)(cypress@13.6.6) + version: 1.5.0(axe-core@4.4.2)(cypress@13.13.2) cypress-file-upload: specifier: 5.0.8 - version: 5.0.8(cypress@13.6.6) + version: 5.0.8(cypress@13.13.2) deep-freeze: specifier: 0.0.1 version: 0.0.1 @@ -10143,11 +10143,6 @@ packages: engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} hasBin: true - cypress@13.6.6: - resolution: {integrity: sha512-S+2S9S94611hXimH9a3EAYt81QM913ZVA03pUmGDfLTFa5gyp85NJ8dJGSlEAEmyRsYkioS1TtnWtbv/Fzt11A==} - engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} - hasBin: true - d@1.0.2: resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} engines: {node: '>=0.12'} @@ -27568,12 +27563,6 @@ snapshots: '@testing-library/dom': 9.3.4 cypress: 13.13.2 - '@testing-library/cypress@10.0.1(cypress@13.6.6)': - dependencies: - '@babel/runtime': 7.20.6 - '@testing-library/dom': 9.3.4 - cypress: 13.6.6 - '@testing-library/dom@6.16.0': dependencies: '@babel/runtime': 7.20.6 @@ -31155,11 +31144,6 @@ snapshots: axe-core: 4.4.2 cypress: 13.13.2 - cypress-axe@1.5.0(axe-core@4.4.2)(cypress@13.6.6): - dependencies: - axe-core: 4.4.2 - cypress: 13.6.6 - cypress-axe@1.5.0(axe-core@4.8.4)(cypress@13.13.2): dependencies: axe-core: 4.8.4 @@ -31169,10 +31153,6 @@ snapshots: dependencies: cypress: 13.13.2 - cypress-file-upload@5.0.8(cypress@13.6.6): - dependencies: - cypress: 13.6.6 - cypress@13.13.2: dependencies: '@cypress/request': 3.0.1 @@ -31218,51 +31198,6 @@ snapshots: untildify: 4.0.0 yauzl: 2.10.0 - cypress@13.6.6: - dependencies: - '@cypress/request': 3.0.1 - '@cypress/xvfb': 1.2.4(supports-color@8.1.1) - '@types/sinonjs__fake-timers': 8.1.1 - '@types/sizzle': 2.3.8 - arch: 2.2.0 - blob-util: 2.0.2 - bluebird: 3.7.2 - buffer: 5.7.1 - cachedir: 2.4.0 - chalk: 4.1.2 - check-more-types: 2.24.0 - cli-cursor: 3.1.0 - cli-table3: 0.6.4 - commander: 6.2.1 - common-tags: 1.8.2 - dayjs: 1.11.10 - debug: 4.3.4(supports-color@8.1.1) - enquirer: 2.4.1 - eventemitter2: 6.4.7 - execa: 4.1.0 - executable: 4.1.1 - extract-zip: 2.0.1(supports-color@8.1.1) - figures: 3.2.0 - fs-extra: 9.1.0 - getos: 3.2.1 - is-ci: 3.0.1 - is-installed-globally: 0.4.0 - lazy-ass: 1.6.0 - listr2: 3.14.0(enquirer@2.4.1) - lodash: 4.17.21 - log-symbols: 4.1.0 - minimist: 1.2.8 - ospath: 1.2.2 - pretty-bytes: 5.6.0 - process: 0.11.10 - proxy-from-env: 1.0.0 - request-progress: 3.0.0 - semver: 7.6.0 - supports-color: 8.1.1 - tmp: 0.2.1 - untildify: 4.0.0 - yauzl: 2.10.0 - d@1.0.2: dependencies: es5-ext: 0.10.64 From 790cd3d93e398fd7e723dc88b6d21217734ac594 Mon Sep 17 00:00:00 2001 From: David Glick Date: Fri, 9 Aug 2024 11:01:51 -0700 Subject: [PATCH 25/58] Allow aliases that redirect to an external URL (#6247) --- .../volto/locales/ca/LC_MESSAGES/volto.po | 9 ++---- .../volto/locales/de/LC_MESSAGES/volto.po | 11 ++----- .../volto/locales/en/LC_MESSAGES/volto.po | 9 ++---- .../volto/locales/es/LC_MESSAGES/volto.po | 11 ++----- .../volto/locales/eu/LC_MESSAGES/volto.po | 11 ++----- .../volto/locales/fi/LC_MESSAGES/volto.po | 11 ++----- .../volto/locales/fr/LC_MESSAGES/volto.po | 11 ++----- .../volto/locales/hi/LC_MESSAGES/volto.po | 11 ++----- .../volto/locales/it/LC_MESSAGES/volto.po | 11 ++----- .../volto/locales/ja/LC_MESSAGES/volto.po | 9 ++---- .../volto/locales/nl/LC_MESSAGES/volto.po | 9 ++---- .../volto/locales/pt/LC_MESSAGES/volto.po | 9 ++---- .../volto/locales/pt_BR/LC_MESSAGES/volto.po | 11 ++----- .../volto/locales/ro/LC_MESSAGES/volto.po | 9 ++---- packages/volto/locales/volto.pot | 11 ++----- .../volto/locales/zh_CN/LC_MESSAGES/volto.po | 11 ++----- packages/volto/news/6247.bugfix | 1 + .../manage/Controlpanels/Aliases.jsx | 32 +++---------------- .../__snapshots__/Aliases.test.jsx.snap | 2 +- 19 files changed, 48 insertions(+), 151 deletions(-) create mode 100644 packages/volto/news/6247.bugfix diff --git a/packages/volto/locales/ca/LC_MESSAGES/volto.po b/packages/volto/locales/ca/LC_MESSAGES/volto.po index 152f81c308..f3c7319b5a 100644 --- a/packages/volto/locales/ca/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ca/LC_MESSAGES/volto.po @@ -1293,9 +1293,9 @@ msgstr "Introduïu el nom complet, per exemple John Smith." msgid "Enter map Embed Code" msgstr "Introduïu el codi d'inserció del mapa" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." msgstr "" #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." @@ -3607,11 +3607,6 @@ msgstr "Mida de memòria objectiu per memòria cau en bytes" msgid "Target number of objects in memory per cache" msgstr "Nombre objectiu d'objectes a la memòria per memòria cau" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "" - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/de/LC_MESSAGES/volto.po b/packages/volto/locales/de/LC_MESSAGES/volto.po index d2137d2acc..e82069d811 100644 --- a/packages/volto/locales/de/LC_MESSAGES/volto.po +++ b/packages/volto/locales/de/LC_MESSAGES/volto.po @@ -1292,10 +1292,10 @@ msgstr "Tragen Sie bitte Ihren vollen Namen ein." msgid "Enter map Embed Code" msgstr "Karten-Einbettungscode eingeben" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." -msgstr "Geben Sie den absoluten Pfad des Ziels ein. Der Pfad muss mit '/' beginnen. Das Ziel muss existieren oder ein existierender, alternativer Pfad zum Ziel sein." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." +msgstr "Geben Sie den absoluten Pfad des Ziels ein. Das Ziel muss existieren oder ein existierender, alternativer Pfad zum Ziel sein." #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." #: components/manage/Aliases/Aliases @@ -3606,11 +3606,6 @@ msgstr "Ziel-Speichergröße pro Cache in Bytes" msgid "Target number of objects in memory per cache" msgstr "Ziel-Anzahl von Objekten im Speicher pro Cache" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "Der Zielpfad muss mit einem Schrägstrich beginnen." - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/en/LC_MESSAGES/volto.po b/packages/volto/locales/en/LC_MESSAGES/volto.po index 1e8ca22c1b..d930720ad3 100644 --- a/packages/volto/locales/en/LC_MESSAGES/volto.po +++ b/packages/volto/locales/en/LC_MESSAGES/volto.po @@ -1287,9 +1287,9 @@ msgstr "" msgid "Enter map Embed Code" msgstr "" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." msgstr "" #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." @@ -3601,11 +3601,6 @@ msgstr "" msgid "Target number of objects in memory per cache" msgstr "" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "" - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/es/LC_MESSAGES/volto.po b/packages/volto/locales/es/LC_MESSAGES/volto.po index 2772fbcc9e..97b72b3100 100644 --- a/packages/volto/locales/es/LC_MESSAGES/volto.po +++ b/packages/volto/locales/es/LC_MESSAGES/volto.po @@ -1294,10 +1294,10 @@ msgstr "Introduzca el nombre completo, por ejemplo Leonardo Caballero." msgid "Enter map Embed Code" msgstr "Introduzca el código embebido de un mapa" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." -msgstr "Introduzca la ruta absoluta del destino. La ruta debe comenzar por '/'. El destino debe existir o ser una url alternativa de otro destino." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." +msgstr "Introduzca la ruta absoluta del destino. El destino debe existir o ser una url alternativa de otro destino." #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." #: components/manage/Aliases/Aliases @@ -3608,11 +3608,6 @@ msgstr "Número objetivo del tamaño de la memoria por caché en bytes" msgid "Target number of objects in memory per cache" msgstr "Número objetivo de elementos en la memoria por caché" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "El camino de destino debe comenzar con /." - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/eu/LC_MESSAGES/volto.po b/packages/volto/locales/eu/LC_MESSAGES/volto.po index 574ed30cec..83a0cee142 100644 --- a/packages/volto/locales/eu/LC_MESSAGES/volto.po +++ b/packages/volto/locales/eu/LC_MESSAGES/volto.po @@ -1294,10 +1294,10 @@ msgstr "Idatzi izen osoa, adb. Jon Garmendia." msgid "Enter map Embed Code" msgstr "Sartu itsasteko kodea" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." -msgstr "Idatzi helburuaren bide absolutua. Bidea '/' karakterearekin hasi behar da. Helburua existitu egin behar da edo ordezko helbide bat izan behar da." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." +msgstr "Idatzi helburuaren bide absolutua. Helburua existitu egin behar da edo ordezko helbide bat izan behar da." #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." #: components/manage/Aliases/Aliases @@ -3608,11 +3608,6 @@ msgstr "Katxe bakoitzaren tamaina " msgid "Target number of objects in memory per cache" msgstr "Katxe bakoitzaren elementu-kopurua" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "Helburuaren bidea / karakterearekin hasi behar da" - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/fi/LC_MESSAGES/volto.po b/packages/volto/locales/fi/LC_MESSAGES/volto.po index 09d7ae120a..0758f31b16 100644 --- a/packages/volto/locales/fi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fi/LC_MESSAGES/volto.po @@ -1292,10 +1292,10 @@ msgstr "Syötä koko nimi, esimerkiksi Lumi Vuorinen." msgid "Enter map Embed Code" msgstr "Syötä Google Maps -upotuskoodi" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." -msgstr "Syötä kohteen osoite. Osoitteen tulee alkaa merkillä '/'. Kohteen tulee olla olemassa tai sen tulee olla olemassaoleva vaihtoehtoinen URL-polku kohteelle." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." +msgstr "Syötä kohteen osoite. Kohteen tulee olla olemassa tai sen tulee olla olemassaoleva vaihtoehtoinen URL-polku kohteelle." #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." #: components/manage/Aliases/Aliases @@ -3606,11 +3606,6 @@ msgstr "Välimuistin tavoitekoko tavuina" msgid "Target number of objects in memory per cache" msgstr "Välimuistin tavoitekoko olioiden määrässä" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "Kohdeosoitteen tulee alkaa kauttaviivalla." - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/fr/LC_MESSAGES/volto.po b/packages/volto/locales/fr/LC_MESSAGES/volto.po index 701d23f3ea..b7ce547a00 100644 --- a/packages/volto/locales/fr/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fr/LC_MESSAGES/volto.po @@ -1294,10 +1294,10 @@ msgstr "Saisissez votre nom complet (par exemple : John Smith)" msgid "Enter map Embed Code" msgstr "Saisissez le code intégré de la carte" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." -msgstr "Saisissez le chemin absolu de la cible. Le chemin doit commencer par '/'. La cible doit exister ou être un chemin d'URL alternatif existant vers la cible." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." +msgstr "Saisissez le chemin absolu de la cible. La cible doit exister ou être un chemin d'URL alternatif existant vers la cible." #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." #: components/manage/Aliases/Aliases @@ -3608,11 +3608,6 @@ msgstr "Taille de la mémoire par cache en octets" msgid "Target number of objects in memory per cache" msgstr "Nombre d'objets en mémoire par cache" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "Le chemin de destination d'URL doit commencer par une barre oblique." - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/hi/LC_MESSAGES/volto.po b/packages/volto/locales/hi/LC_MESSAGES/volto.po index 0b26754a9a..9c3a11198d 100644 --- a/packages/volto/locales/hi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/hi/LC_MESSAGES/volto.po @@ -1287,10 +1287,10 @@ msgstr "पूरा नाम दर्ज करें, उदाहरण क msgid "Enter map Embed Code" msgstr "नक्शे का एम्बेड कोड दर्ज करें" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." -msgstr "लक्ष्य का पूर्ण रास्ता दर्ज करें। पथ '/ ' से प्रारंभ होना चाहिए। लक्ष्य मौजूद होना चाहिए या लक्ष्य के लिए मौजूदा वैकल्पिक URL पथ होना चाहिए।" +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." +msgstr "लक्ष्य का पूर्ण रास्ता दर्ज करें। लक्ष्य मौजूद होना चाहिए या लक्ष्य के लिए मौजूदा वैकल्पिक URL पथ होना चाहिए।" #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." #: components/manage/Aliases/Aliases @@ -3601,11 +3601,6 @@ msgstr "प्रति कैश के लिए लक्षित मेम msgid "Target number of objects in memory per cache" msgstr "प्रति कैश में मेमोरी में ऑब्जेक्ट की लक्षित संख्या" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "लक्षित URL पथ को स्लैश के साथ शुरू होना चाहिए।" - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/it/LC_MESSAGES/volto.po b/packages/volto/locales/it/LC_MESSAGES/volto.po index 2bf03717fc..410468bf31 100644 --- a/packages/volto/locales/it/LC_MESSAGES/volto.po +++ b/packages/volto/locales/it/LC_MESSAGES/volto.po @@ -1287,10 +1287,10 @@ msgstr "Inserisci il tuo nome completo, ad esempio Mario Rossi." msgid "Enter map Embed Code" msgstr "Inserisci il codice di incorporamento della mappa" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." -msgstr "Inserisci il path assoluto per la destinazione. Il path deve iniziare con '/'. La destinazione deve già esistere o essere un url alternativo per la destinazione." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." +msgstr "Inserisci il path assoluto per la destinazione. La destinazione deve già esistere o essere un url alternativo per la destinazione." #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." #: components/manage/Aliases/Aliases @@ -3601,11 +3601,6 @@ msgstr "Dimensionei target della memoria per cache in byte" msgid "Target number of objects in memory per cache" msgstr "Numero target di oggetti in memoria per cache" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "Il percorso url di destinazione deve cominciare con uno slash." - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/ja/LC_MESSAGES/volto.po b/packages/volto/locales/ja/LC_MESSAGES/volto.po index d3e2dd35e8..a6d8c12eff 100644 --- a/packages/volto/locales/ja/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ja/LC_MESSAGES/volto.po @@ -1292,9 +1292,9 @@ msgstr "氏名(フルネーム)を入力。例: Hanako Suzuki や 山田 msgid "Enter map Embed Code" msgstr "地図のEmbedコードを入力" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." msgstr "" #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." @@ -3606,11 +3606,6 @@ msgstr "キャッシュごとの最大容量(byte単位)" msgid "Target number of objects in memory per cache" msgstr "キャッシュごとのメモリー上の最大オブジェクト数" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "" - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/nl/LC_MESSAGES/volto.po b/packages/volto/locales/nl/LC_MESSAGES/volto.po index d8ccaef78d..36862df6b0 100644 --- a/packages/volto/locales/nl/LC_MESSAGES/volto.po +++ b/packages/volto/locales/nl/LC_MESSAGES/volto.po @@ -1291,9 +1291,9 @@ msgstr "Vul de volledige naam in, bijvoorbeeld Jan Smit." msgid "Enter map Embed Code" msgstr "" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." msgstr "" #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." @@ -3605,11 +3605,6 @@ msgstr "" msgid "Target number of objects in memory per cache" msgstr "" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "" - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/pt/LC_MESSAGES/volto.po b/packages/volto/locales/pt/LC_MESSAGES/volto.po index 4325a6eaab..7a7a258d87 100644 --- a/packages/volto/locales/pt/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt/LC_MESSAGES/volto.po @@ -1292,9 +1292,9 @@ msgstr "Escreva o nome completo. Por exemplo, José Silva." msgid "Enter map Embed Code" msgstr "" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." msgstr "" #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." @@ -3606,11 +3606,6 @@ msgstr "" msgid "Target number of objects in memory per cache" msgstr "" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "" - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po index 415afc646a..2bffc9fcfc 100644 --- a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po @@ -1293,10 +1293,10 @@ msgstr "Digite o nome completo. Por exemplo, José da Silva." msgid "Enter map Embed Code" msgstr "Informe o código Embed do mapa" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." -msgstr "Informe o caminho absoluto do destino. O caminho deve começar com '/'. O destino deve existir ou ser um caminho da URL alternativa existente para o destino." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." +msgstr "Informe o caminho absoluto do destino. O destino deve existir ou ser um caminho da URL alternativa existente para o destino." #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." #: components/manage/Aliases/Aliases @@ -3607,11 +3607,6 @@ msgstr "Tamanho de memória por cache em bytes" msgid "Target number of objects in memory per cache" msgstr "Número de objetos na memória por cache" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "O caminho da URL de destino deve começar com uma barra." - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/ro/LC_MESSAGES/volto.po b/packages/volto/locales/ro/LC_MESSAGES/volto.po index ba0d929a5f..fd6e513083 100644 --- a/packages/volto/locales/ro/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ro/LC_MESSAGES/volto.po @@ -1287,9 +1287,9 @@ msgstr "Introduceți numele complet, de exemplu, Valentin Popescu." msgid "Enter map Embed Code" msgstr "Introduceți codul de integrare a hărții" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." msgstr "" #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." @@ -3601,11 +3601,6 @@ msgstr "Dimensiunea țintă a memoriei raportat la cache în octeți" msgid "Target number of objects in memory per cache" msgstr "Numărul țintă de obiecte din memorie raportat la cache" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "" - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/volto.pot b/packages/volto/locales/volto.pot index 4af08ba0af..e32387cd36 100644 --- a/packages/volto/locales/volto.pot +++ b/packages/volto/locales/volto.pot @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Plone\n" -"POT-Creation-Date: 2024-08-02T16:48:19.008Z\n" +"POT-Creation-Date: 2024-08-09T15:47:03.838Z\n" "Last-Translator: Plone i18n \n" "Language-Team: Plone i18n \n" "Content-Type: text/plain; charset=utf-8\n" @@ -1289,9 +1289,9 @@ msgstr "" msgid "Enter map Embed Code" msgstr "" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." msgstr "" #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." @@ -3603,11 +3603,6 @@ msgstr "" msgid "Target number of objects in memory per cache" msgstr "" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "" - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po index c58f7fcf7a..aa121d750f 100644 --- a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po +++ b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po @@ -1293,10 +1293,10 @@ msgstr "输入您的姓名,如:张三" msgid "Enter map Embed Code" msgstr "输入 map 嵌入代码" -#. Default: "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." +#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." #: components/manage/Controlpanels/Aliases -msgid "Enter the absolute path of the target. The path must start with '/'. Target must exist or be an existing alternative url path to the target." -msgstr "输入目标的绝对路径。路径必须以‘/’开头。目标必须存在,或者是指向目标的现有可选的url路径。" +msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target." +msgstr "输入目标的绝对路径。目标必须存在,或者是指向目标的现有可选的url路径。" #. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring." #: components/manage/Aliases/Aliases @@ -3607,11 +3607,6 @@ msgstr "每个缓存目标以字节为单位的内存大小" msgid "Target number of objects in memory per cache" msgstr "" -#. Default: "Target url path must start with a slash." -#: components/manage/Controlpanels/Aliases -msgid "Target url path must start with a slash." -msgstr "目标url路径必须以斜杠开头。" - #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" diff --git a/packages/volto/news/6247.bugfix b/packages/volto/news/6247.bugfix new file mode 100644 index 0000000000..c74a667f3b --- /dev/null +++ b/packages/volto/news/6247.bugfix @@ -0,0 +1 @@ +In the URL Management control panel, allow external URLs as targets. @davisagli diff --git a/packages/volto/src/components/manage/Controlpanels/Aliases.jsx b/packages/volto/src/components/manage/Controlpanels/Aliases.jsx index c5b80bed90..f5db983ba8 100644 --- a/packages/volto/src/components/manage/Controlpanels/Aliases.jsx +++ b/packages/volto/src/components/manage/Controlpanels/Aliases.jsx @@ -1,9 +1,4 @@ -/** - * Moderate comments component. - * @module components/manage/Controlpanels/Aliases - */ - -import React, { Component } from 'react'; +import { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { compose } from 'redux'; @@ -93,7 +88,6 @@ class Aliases extends Component { altUrlPath: '', isAltUrlCorrect: false, targetUrlPath: '', - isTargetUrlCorrect: false, aliasesToRemove: [], errorMessageAdd: '', filterQuery: '', @@ -160,14 +154,6 @@ class Aliases extends Component { this.setState({ isAltUrlCorrect: false }); } } - - if (prevState.targetUrlPath !== this.state.targetUrlPath) { - if (this.state.targetUrlPath.charAt(0) === '/') { - this.setState({ isTargetUrlCorrect: true }); - } else { - this.setState({ isTargetUrlCorrect: false }); - } - } } /** @@ -295,7 +281,7 @@ class Aliases extends Component { * @returns {undefined} */ handleSubmitAlias() { - if (this.state.isAltUrlCorrect && this.state.isTargetUrlCorrect) { + if (this.state.isAltUrlCorrect) { this.props.addAliases('', { items: [ { @@ -423,8 +409,8 @@ class Aliases extends Component {

@@ -437,15 +423,6 @@ class Aliases extends Component { this.handleTargetUrlChange(e.target.value) } /> - {!this.state.isTargetUrlCorrect && - this.state.targetUrlPath !== '' && ( -

- -

- )}
- {this.state.errorMessageAdd && ( - - - - -

{this.state.errorMessageAdd}

-
+ return ( +
+ + +
+ + + {title} }} + /> + + + +
+ +
+

+ +

+ + handleAltUrlChange(e.target.value)} + /> + {!isAltUrlCorrect && altUrlPath !== '' && ( +

+ +

)} -
- -
- -
- -
-
- -
- - - this.handleFilterQueryChange(e.target.value) - } - /> - -
- -
- {filterChoices.map((o, i) => ( - - this.handleSelectFilterType(o)} + +
+ +
+

+ +

+ + handleTargetUrlChange(e.target.value)} + /> + + + {errorMessageAdd && ( + + + - - ))} - - { - this.handleCreateDate(value); - }} + +

{errorMessageAdd}

+
+ )} +
+
+
+ +
+ +
+
+ +
+ + handleFilterQueryChange(e.target.value)} + /> + +
+ +
+ {filterChoices.map((o, i) => ( + + handleSelectFilterType(o)} /> - -
- -
- - - - - - - - - - - - - - - - - - - - - {this.props.aliases.items.length > 0 && - this.props.aliases.items.map((alias, i) => ( - - - - this.handleCheckAlias(value) - } - checked={this.state.aliasesToRemove.includes( - alias.path, - )} - value={alias.path} - /> - - -

{alias.path}

-
- -

{alias['redirect-to']}

-
- -

{alias.datetime}

-
- -

{`${alias.manual}`}

-
-
- ))} -
-
-
+ { + handleCreateDate(value); }} - > - {this.state.pages && ( - this.handlePageChange(e, o)} - /> - )} - - - : - - {map(itemsPerPageChoices, (size) => ( - this.handleItemsPerPage(e, o)} - > - {size} - + /> + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + {aliases.items.length > 0 && + aliases.items.map((alias, i) => ( + + + + handleCheckAlias(value) + } + checked={aliasesToRemove.includes(alias.path)} + value={alias.path} + /> + + +

{alias.path}

+
+ +

{alias['redirect-to']}

+
+ +

{alias.datetime}

+
+ +

{`${alias.manual}`}

+
+
))} - - -
+
+ {pages && ( + handlePageChange(e, o)} /> - - - - -
-
- {this.state.isClient && - createPortal( - this.onCancel()}> - + + : + + {map(itemsPerPageChoices, (size) => ( + handleItemsPerPage(e, o)} + > + {size} + + ))} + +
+
- ); - } -} + + + + + + + {isClient && + createPortal( + onCancel()}> + + + } + />, + document.getElementById('toolbar'), + )} +
+ ); +}; -export default compose( - injectIntl, - connect( - (state, props) => ({ - aliases: state.aliases, - pathname: props.location.pathname, - }), - { addAliases, getAliases, removeAliases }, - ), -)(Aliases); +export default Aliases; diff --git a/packages/volto/src/components/manage/Controlpanels/Aliases.stories.jsx b/packages/volto/src/components/manage/Controlpanels/Aliases.stories.jsx new file mode 100644 index 0000000000..5732d888a0 --- /dev/null +++ b/packages/volto/src/components/manage/Controlpanels/Aliases.stories.jsx @@ -0,0 +1,74 @@ +import { injectIntl } from 'react-intl'; +import React from 'react'; +import AliasesComponent from './Aliases'; +import { RealStoreWrapper as Wrapper } from '@plone/volto/storybook'; + +const IntlAliasesComponent = injectIntl(AliasesComponent); + +function StoryComponent(args) { + return ( + +
+ + + ); +} + +export const Aliases = StoryComponent.bind({}); + +export default { + title: 'Internal Components/ControlPanels/Aliases', + component: Aliases, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + argTypes: {}, +}; diff --git a/packages/volto/src/components/manage/Controlpanels/__snapshots__/Aliases.test.jsx.snap b/packages/volto/src/components/manage/Controlpanels/__snapshots__/Aliases.test.jsx.snap index 813ab768a4..fda6016602 100644 --- a/packages/volto/src/components/manage/Controlpanels/__snapshots__/Aliases.test.jsx.snap +++ b/packages/volto/src/components/manage/Controlpanels/__snapshots__/Aliases.test.jsx.snap @@ -180,11 +180,7 @@ exports[`Aliases renders an aliases control component 1`] = `
-
-
+ /> + +
+ - - -
{item.description}
- {item.uninstall_profile_id === '' && ( -
- - - -
+ :   + {item.version} +
+ + +
+ ))} + + + + : + + + + + + {installedAddons.map((item) => ( +
+ + {item.title} + + {item.upgrade_info.available && ( + + )} + + + +
{item.description}
+ + {item.upgrade_info.available && ( + )} - + {item.uninstall_profile_id && ( - -
- - :   - {item.version} -
-
- -
- ))} -
-
- - : - - - - - - {this.props.installedAddons.map((item) => ( -
- - {item.title} - - {item.upgrade_info.available && ( - )} - +
+ - - -
{item.description}
- - {item.upgrade_info.available && ( - - )} - {item.uninstall_profile_id && ( - - )} - -
- - :   {item.version} -
-
- -
- ))} - - - - )} - + :   {item.version} +
+ + +
+ ))} + + + + )} + - {this.state.isClient && - createPortal( - - - - - - } - />, - document.getElementById('toolbar'), - )} - - ); - } -} + {isClient && + createPortal( + + + + + + } + />, + document.getElementById('toolbar'), + )} + + ); +}; -export default compose( - injectIntl, - connect( - (state, props) => ({ - installedAddons: state.addons.installedAddons, - availableAddons: state.addons.availableAddons, - upgradableAddons: state.addons.upgradableAddons, - loadingAddons: state.addons.loading, - pathname: props.location.pathname, - }), - { installAddon, listAddons, uninstallAddon, upgradeAddon }, - ), -)(AddonsControlpanel); +export default AddonsControlpanel; From 5d97f9fcb905ee149c5bb9a58ce26fe98aba0c33 Mon Sep 17 00:00:00 2001 From: Tisha Soumya Date: Sat, 10 Aug 2024 22:42:03 +0530 Subject: [PATCH 31/58] Refactor DatabaseInformation (#4986) Co-authored-by: David Glick --- .../volto/locales/ca/LC_MESSAGES/volto.po | 5 - .../volto/locales/de/LC_MESSAGES/volto.po | 5 - .../volto/locales/en/LC_MESSAGES/volto.po | 5 - .../volto/locales/es/LC_MESSAGES/volto.po | 5 - .../volto/locales/eu/LC_MESSAGES/volto.po | 5 - .../volto/locales/fi/LC_MESSAGES/volto.po | 5 - .../volto/locales/fr/LC_MESSAGES/volto.po | 5 - .../volto/locales/hi/LC_MESSAGES/volto.po | 5 - .../volto/locales/it/LC_MESSAGES/volto.po | 5 - .../volto/locales/ja/LC_MESSAGES/volto.po | 5 - .../volto/locales/nl/LC_MESSAGES/volto.po | 5 - .../volto/locales/pt/LC_MESSAGES/volto.po | 5 - .../volto/locales/pt_BR/LC_MESSAGES/volto.po | 5 - .../volto/locales/ro/LC_MESSAGES/volto.po | 5 - packages/volto/locales/volto.pot | 7 +- .../volto/locales/zh_CN/LC_MESSAGES/volto.po | 5 - packages/volto/news/4986.feature | 1 + .../Controlpanels/DatabaseInformation.jsx | 391 ++++++++---------- 18 files changed, 164 insertions(+), 310 deletions(-) create mode 100644 packages/volto/news/4986.feature diff --git a/packages/volto/locales/ca/LC_MESSAGES/volto.po b/packages/volto/locales/ca/LC_MESSAGES/volto.po index f3c7319b5a..69687ab2b8 100644 --- a/packages/volto/locales/ca/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ca/LC_MESSAGES/volto.po @@ -3626,11 +3626,6 @@ msgstr "Text" msgid "Thank you." msgstr "Gràcies." -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "El Gestor de bases de dades us permet veure la informació d'estat de la base de dades" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/de/LC_MESSAGES/volto.po b/packages/volto/locales/de/LC_MESSAGES/volto.po index e82069d811..d35ff24b4c 100644 --- a/packages/volto/locales/de/LC_MESSAGES/volto.po +++ b/packages/volto/locales/de/LC_MESSAGES/volto.po @@ -3625,11 +3625,6 @@ msgstr "Text" msgid "Thank you." msgstr "Vielen Dank." -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "Der Datenbank-Manager zeigt Ihnen Status-Informationen zu der Datenbank" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/en/LC_MESSAGES/volto.po b/packages/volto/locales/en/LC_MESSAGES/volto.po index d930720ad3..a5b40a7988 100644 --- a/packages/volto/locales/en/LC_MESSAGES/volto.po +++ b/packages/volto/locales/en/LC_MESSAGES/volto.po @@ -3620,11 +3620,6 @@ msgstr "" msgid "Thank you." msgstr "" -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/es/LC_MESSAGES/volto.po b/packages/volto/locales/es/LC_MESSAGES/volto.po index 97b72b3100..6e284bfd75 100644 --- a/packages/volto/locales/es/LC_MESSAGES/volto.po +++ b/packages/volto/locales/es/LC_MESSAGES/volto.po @@ -3627,11 +3627,6 @@ msgstr "Texto" msgid "Thank you." msgstr "Gracias." -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "El Gestor de la Base de Datos permite ver la información del estado de la base de datos" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/eu/LC_MESSAGES/volto.po b/packages/volto/locales/eu/LC_MESSAGES/volto.po index 83a0cee142..b7c57b8ee1 100644 --- a/packages/volto/locales/eu/LC_MESSAGES/volto.po +++ b/packages/volto/locales/eu/LC_MESSAGES/volto.po @@ -3627,11 +3627,6 @@ msgstr "Testua" msgid "Thank you." msgstr "Eskerrik asko." -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "Datu-basearen kudeatzaileak datu-basearen egoera erakusten ditu" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/fi/LC_MESSAGES/volto.po b/packages/volto/locales/fi/LC_MESSAGES/volto.po index 0758f31b16..16a81a822c 100644 --- a/packages/volto/locales/fi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fi/LC_MESSAGES/volto.po @@ -3625,11 +3625,6 @@ msgstr "Teksti" msgid "Thank you." msgstr "Kiitos." -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "Tietokantamanageri sallii sinun katsoa tietokannan tilannetietoa." - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/fr/LC_MESSAGES/volto.po b/packages/volto/locales/fr/LC_MESSAGES/volto.po index b7ce547a00..b405214240 100644 --- a/packages/volto/locales/fr/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fr/LC_MESSAGES/volto.po @@ -3627,11 +3627,6 @@ msgstr "Texte" msgid "Thank you." msgstr "Merci." -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "Le gestionnaire de base de données vous permet d'afficher les informations d'état de la base de données" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/hi/LC_MESSAGES/volto.po b/packages/volto/locales/hi/LC_MESSAGES/volto.po index 9c3a11198d..c21aa3608f 100644 --- a/packages/volto/locales/hi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/hi/LC_MESSAGES/volto.po @@ -3620,11 +3620,6 @@ msgstr "टेक्स्ट" msgid "Thank you." msgstr "धन्यवाद।" -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "डेटाबेस प्रबंधक आपको डेटाबेस स्थिति जानकारी देखने की सुविधा प्रदान करता है" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/it/LC_MESSAGES/volto.po b/packages/volto/locales/it/LC_MESSAGES/volto.po index 410468bf31..fbdee8436e 100644 --- a/packages/volto/locales/it/LC_MESSAGES/volto.po +++ b/packages/volto/locales/it/LC_MESSAGES/volto.po @@ -3620,11 +3620,6 @@ msgstr "Testo" msgid "Thank you." msgstr "Grazie." -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "Il Database Manager ti permette di vedere le informazioni di stato del database" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/ja/LC_MESSAGES/volto.po b/packages/volto/locales/ja/LC_MESSAGES/volto.po index a6d8c12eff..60d63a02e7 100644 --- a/packages/volto/locales/ja/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ja/LC_MESSAGES/volto.po @@ -3625,11 +3625,6 @@ msgstr "テキスト" msgid "Thank you." msgstr "ありがとうございます。" -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "この画面でデータベースの状態を確認できます。" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/nl/LC_MESSAGES/volto.po b/packages/volto/locales/nl/LC_MESSAGES/volto.po index 36862df6b0..c6330673c2 100644 --- a/packages/volto/locales/nl/LC_MESSAGES/volto.po +++ b/packages/volto/locales/nl/LC_MESSAGES/volto.po @@ -3624,11 +3624,6 @@ msgstr "" msgid "Thank you." msgstr "Bedankt." -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/pt/LC_MESSAGES/volto.po b/packages/volto/locales/pt/LC_MESSAGES/volto.po index 7a7a258d87..f7350c0098 100644 --- a/packages/volto/locales/pt/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt/LC_MESSAGES/volto.po @@ -3625,11 +3625,6 @@ msgstr "Texto" msgid "Thank you." msgstr "Obrigado." -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po index 2bffc9fcfc..22e61c7f06 100644 --- a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po @@ -3626,11 +3626,6 @@ msgstr "Texto" msgid "Thank you." msgstr "Obrigado." -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "O Gerenciador de banco de dados permite que você visualize informações de status do banco de dados" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/ro/LC_MESSAGES/volto.po b/packages/volto/locales/ro/LC_MESSAGES/volto.po index fd6e513083..4ccc25cc34 100644 --- a/packages/volto/locales/ro/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ro/LC_MESSAGES/volto.po @@ -3620,11 +3620,6 @@ msgstr "Text" msgid "Thank you." msgstr "Mulțumesc." -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "Managerul bazei de date vă permite să vizualizați informațiile despre starea bazei de date" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/volto.pot b/packages/volto/locales/volto.pot index e32387cd36..7e870490fc 100644 --- a/packages/volto/locales/volto.pot +++ b/packages/volto/locales/volto.pot @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Plone\n" -"POT-Creation-Date: 2024-08-09T15:47:03.838Z\n" +"POT-Creation-Date: 2024-08-10T17:00:27.117Z\n" "Last-Translator: Plone i18n \n" "Language-Team: Plone i18n \n" "Content-Type: text/plain; charset=utf-8\n" @@ -3622,11 +3622,6 @@ msgstr "" msgid "Thank you." msgstr "" -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po index aa121d750f..cddef470f6 100644 --- a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po +++ b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po @@ -3626,11 +3626,6 @@ msgstr "文本" msgid "Thank you." msgstr "" -#. Default: "The Database Manager allow you to view database status information" -#: components/manage/Controlpanels/DatabaseInformation -msgid "The Database Manager allow you to view database status information" -msgstr "数据库管理器允许您查看数据库状态信息" - #. Default: "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." #: components/theme/RequestTimeout/RequestTimeout msgid "The backend is not responding, due to a server timeout or a connection problem of your device. Please check your connection and try again." diff --git a/packages/volto/news/4986.feature b/packages/volto/news/4986.feature new file mode 100644 index 0000000000..caaac5aa19 --- /dev/null +++ b/packages/volto/news/4986.feature @@ -0,0 +1 @@ +Refactor Controlpanel databaseInformation from class component to functional component. @Tishasoumya-02 \ No newline at end of file diff --git a/packages/volto/src/components/manage/Controlpanels/DatabaseInformation.jsx b/packages/volto/src/components/manage/Controlpanels/DatabaseInformation.jsx index 04b29655ad..00d0a1e238 100644 --- a/packages/volto/src/components/manage/Controlpanels/DatabaseInformation.jsx +++ b/packages/volto/src/components/manage/Controlpanels/DatabaseInformation.jsx @@ -1,18 +1,13 @@ -/** - * Users controlpanel container. - * @module components/manage/Controlpanels/DatabaseInformation - */ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { compose } from 'redux'; -import { Link } from 'react-router-dom'; +import { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Link, useLocation } from 'react-router-dom'; import { createPortal } from 'react-dom'; import { Container, Divider, Message, Segment, Table } from 'semantic-ui-react'; -import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; +import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { getDatabaseInformation } from '@plone/volto/actions'; import { Helmet } from '@plone/volto/helpers'; +import { useClient } from '@plone/volto/hooks'; import { Icon, Toolbar } from '@plone/volto/components'; import backSVG from '@plone/volto/icons/back.svg'; @@ -27,228 +22,166 @@ const messages = defineMessages({ }, }); -/** - * DatabaseInformation class. - * @class DatabaseInformation - * @extends Component - */ -class DatabaseInformation extends Component { - /** - * Property types. - * @property {Object} propTypes Property types. - * @static - */ - static propTypes = { - getDatabaseInformation: PropTypes.func.isRequired, - }; +const DatabaseInformation = () => { + const intl = useIntl(); + const dispatch = useDispatch(); + const { pathname } = useLocation(); + const isClient = useClient(); + const databaseInformation = useSelector( + (state) => state.controlpanels.databaseinformation, + ); - /** - * Constructor - * @method constructor - * @param {Object} props Component properties - * @constructs DiffComponent - */ - constructor(props) { - super(props); - this.state = { isClient: false }; - } + useEffect(() => { + dispatch(getDatabaseInformation()); + }, [dispatch]); - /** - * Component did mount - * @method componentDidMount - * @returns {undefined} - */ - componentDidMount() { - this.props.getDatabaseInformation(); - this.setState({ isClient: true }); - } - - /** - * Render method. - * @method render - * @returns {string} Markup for the component. - */ - render() { - return this.props.databaseInformation ? ( - - - - - - - - + return databaseInformation ? ( + + + + + + + + + - - - - - - - - - - - - - - - - {this.props.databaseInformation.db_name} - - - - - - - - {this.props.databaseInformation.database_size} - - - - - - - - {this.props.databaseInformation.db_size} - - - - - - - - {this.props.databaseInformation.cache_size} - - - - - - - - {this.props.databaseInformation.cache_length} - - - - - - - - {this.props.databaseInformation.cache_length_bytes} - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - {this.props.databaseInformation.cache_detail_length.map( - (item) => ( - - {item.connection} - {item.ngsize} - {item.size} - - ), - )} -
-
-
+ +
+ + + + + + + {databaseInformation.db_name} + + + + + + {databaseInformation.database_size} + + + + + + {databaseInformation.db_size} + + + + + + {databaseInformation.cache_size} + + + + + + {databaseInformation.cache_length} + + + + + + + {databaseInformation.cache_length_bytes} + + + +
+
+ + - {this.state.isClient && - createPortal( - - - - - - } - />, - document.getElementById('toolbar'), - )} -
- ) : null; - } -} + + + + + + + + + + + + + + + + + + + + {databaseInformation.cache_detail_length.map((item) => ( + + {item.connection} + {item.ngsize} + {item.size} + + ))} +
+ + + {isClient && + createPortal( + + + + + + } + />, + document.getElementById('toolbar'), + )} + + ) : null; +}; -export default compose( - injectIntl, - connect( - (state, props) => ({ - databaseInformation: state.controlpanels.databaseinformation, - pathname: props.location.pathname, - }), - { getDatabaseInformation }, - ), -)(DatabaseInformation); +export default DatabaseInformation; From 4050e50f7d6621f755d1d1bdf0a8dbac8afc4ebe Mon Sep 17 00:00:00 2001 From: Tisha Soumya Date: Sat, 10 Aug 2024 23:01:08 +0530 Subject: [PATCH 32/58] Refactor Toolbar/More component (#4955) Co-authored-by: nileshgulia1 Co-authored-by: me@jeffersonbledsoe.com Co-authored-by: David Glick --- packages/volto/news/4955.feature | 1 + .../src/components/manage/Toolbar/More.jsx | 707 ++++++++---------- 2 files changed, 309 insertions(+), 399 deletions(-) create mode 100644 packages/volto/news/4955.feature diff --git a/packages/volto/news/4955.feature b/packages/volto/news/4955.feature new file mode 100644 index 0000000000..4884c96572 --- /dev/null +++ b/packages/volto/news/4955.feature @@ -0,0 +1 @@ +Refactor Toolbar/More component from class to functional component. @Tishasoumya-02 \ No newline at end of file diff --git a/packages/volto/src/components/manage/Toolbar/More.jsx b/packages/volto/src/components/manage/Toolbar/More.jsx index 58e1644684..a40a74fe9f 100644 --- a/packages/volto/src/components/manage/Toolbar/More.jsx +++ b/packages/volto/src/components/manage/Toolbar/More.jsx @@ -1,16 +1,11 @@ -/** - * More component. - * @module components/manage/Toolbar/More - */ - -import React, { Component } from 'react'; -import { defineMessages, injectIntl } from 'react-intl'; +import React, { useState, useEffect } from 'react'; +import { defineMessages, useIntl } from 'react-intl'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { compose } from 'redux'; -import { Link, withRouter } from 'react-router-dom'; +import { useDispatch, useSelector, shallowEqual } from 'react-redux'; +import { Link, useHistory } from 'react-router-dom'; import { find } from 'lodash'; import { toast } from 'react-toastify'; + import { Toast } from '@plone/volto/components'; import { Pluggable, Plug } from '@plone/volto/components/manage/Pluggable'; import { @@ -24,9 +19,8 @@ import { createWorkingCopy, removeWorkingCopy, } from '@plone/volto/actions'; -import { flattenToAppURL, getBaseUrl } from '@plone/volto/helpers'; +import { flattenToAppURL, getBaseUrl, usePrevious } from '@plone/volto/helpers'; import config from '@plone/volto/registry'; - import rightArrowSVG from '@plone/volto/icons/right-key.svg'; import userSVG from '@plone/volto/icons/user.svg'; import applySVG from '@plone/volto/icons/ready.svg'; @@ -112,80 +106,49 @@ const messages = defineMessages({ }, }); -/** - * More container class. - * @class More - * @extends Component - */ -class More extends Component { - static propTypes = { - actions: PropTypes.shape({ - object: PropTypes.arrayOf(PropTypes.object), - object_buttons: PropTypes.arrayOf(PropTypes.object), - user: PropTypes.arrayOf(PropTypes.object), - }), - pathname: PropTypes.string.isRequired, - content: PropTypes.shape({ - title: PropTypes.string, - '@type': PropTypes.string, - is_folderish: PropTypes.bool, - review_state: PropTypes.string, - }), - loadComponent: PropTypes.func.isRequired, - closeMenu: PropTypes.func.isRequired, - }; +const More = (props) => { + const dispatch = useDispatch(); + const intl = useIntl(); + const history = useHistory(); + const [, setPushed] = useState(false); + const pathname = props.pathname; - /** - * Default properties. - * @property {Object} defaultProps Default properties. - * @static - */ - static defaultProps = { - actions: null, - content: null, - }; - state = { - openManageTranslations: false, - pushed: false, - }; + const content = useSelector((state) => state.content?.data, shallowEqual); + const workingCopy = useSelector((state) => state.workingCopy, shallowEqual); - push = (selector) => { - this.setState(() => ({ - pushed: true, - })); - this.props.loadComponent(selector); - document.removeEventListener('mousedown', this.handleClickOutside, false); - }; + const actions = useSelector((state) => state.actions.actions, shallowEqual); + + const workingCopyApply = workingCopy?.apply.loading; + const workingCopyCreate = workingCopy?.create.loading; + const workingCopyRemove = workingCopy?.remove.loading; - componentDidUpdate(prevProps, prevState) { + const prevWorkingCopyApplyLoading = usePrevious(workingCopyApply); + const prevWorkingCopyCreateLoading = usePrevious(workingCopyCreate); + const prevWorkingCopyRemoveLoading = usePrevious(workingCopyRemove); + + const push = (selector) => { + setPushed(true); + props.loadComponent(selector); + document.removeEventListener('mousedown', props.handleClickOutside, false); + }; + useEffect(() => { let erroredAction = ''; - if ( - prevProps.workingCopy.apply.loading && - this.props.workingCopy.apply.error - ) { + if (prevWorkingCopyApplyLoading && workingCopy.apply.error) { erroredAction = 'apply'; - } else if ( - prevProps.workingCopy.create.loading && - this.props.workingCopy.create.error - ) { + } else if (prevWorkingCopyCreateLoading && workingCopy.create.error) { erroredAction = 'create'; - } else if ( - prevProps.workingCopy.remove.loading && - this.props.workingCopy.remove.error - ) { + } else if (prevWorkingCopyRemoveLoading && workingCopy.remove.error) { erroredAction = 'remove'; } if (erroredAction) { - const errorStatus = this.props.workingCopy[erroredAction].error.status; + const errorStatus = workingCopy[erroredAction].error.status; if (errorStatus === 401 || errorStatus === 403) { toast.error( , { toastId: 'workingCopyErrorUnauthorized', @@ -196,10 +159,8 @@ class More extends Component { toast.error( , { toastId: 'workingCopyGenericError', @@ -208,347 +169,295 @@ class More extends Component { ); } } - } + }, [ + workingCopy, + prevWorkingCopyApplyLoading, + prevWorkingCopyCreateLoading, + prevWorkingCopyRemoveLoading, + intl, + ]); - /** - * Render method. - * @method render - * @returns {string} Markup for the component. - */ - render() { - const path = getBaseUrl(this.props.pathname); - const editAction = find(this.props.actions.object, { id: 'edit' }); - const historyAction = find(this.props.actions.object, { id: 'history' }); - const sharingAction = find(this.props.actions.object, { - id: 'local_roles', - }); + const path = getBaseUrl(pathname); + const editAction = find(actions.object, { id: 'edit' }); + const historyAction = find(actions.object, { id: 'history' }); + const sharingAction = find(actions.object, { + id: 'local_roles', + }); - const rulesAction = find(this.props.actions.object, { - id: 'contentrules', - }); + const rulesAction = find(actions.object, { + id: 'contentrules', + }); - const aliasesAction = find(this.props.actions.object_buttons, { - id: 'redirection', - }); + const aliasesAction = find(actions.object_buttons, { + id: 'redirection', + }); - const { content, intl } = this.props; - - const dateOptions = { - year: 'numeric', - month: 'long', - day: 'numeric', - }; + const dateOptions = { + year: 'numeric', + month: 'long', + day: 'numeric', + }; - return ( -
-
-

{this.props.content.title}

- -
-
-
    - - - {this.props.content['@type'] !== 'Plone Site' && ( - // Plone Site does not have workflow -
  • - -
  • - )} -
    - - {this.props.content['@type'] !== 'Plone Site' && ( - // Plone Site does not have view (yet) -
  • - {editAction && } -
  • - )} -
    - - {this.props.content['@type'] !== 'Plone Site' && ( - // Plone Site does not have history (yet) + return ( +
    +
    +

    {content.title}

    + +
    +
    +
      + + + {content['@type'] !== 'Plone Site' && ( + // Plone Site does not have workflow +
    • + +
    • + )} +
      + + {content['@type'] !== 'Plone Site' && ( + // Plone Site does not have view (yet) +
    • + {editAction && } +
    • + )} +
      + + {content['@type'] !== 'Plone Site' && ( + // Plone Site does not have history (yet) +
    • + +
      + + {historyAction?.title || + intl.formatMessage(messages.history)} + + +
      + + +
    • + )} +
      + + {sharingAction && ( +
    • + + {intl.formatMessage(messages.sharing)} + + +
    • + )} +
      + + {aliasesAction && ( +
    • + + {intl.formatMessage(messages.aliases)} + + +
    • + )} +
      + {path !== '' && + !config.settings.excludeLinksAndReferencesMenuItem && ( +
    • - -
      - - {historyAction?.title || - this.props.intl.formatMessage(messages.history)} - - -
      + + {intl.formatMessage(messages.linkstoitem)}
    • - )} -
      - - {sharingAction && ( + + )} + + {rulesAction && ( +
    • + + {intl.formatMessage(messages.rules)} + + +
    • + )} +
      +
    +
    + + {(pluggables) => ( + <> + {pluggables.length > 0 && ( + <> +
    +

    {intl.formatMessage(messages.manageContent)}

    +
    +
    +
      + {pluggables.map((p) => ( + <>{p()} + ))} +
    +
    + + )} + + )} +
    + {config.settings.hasWorkingCopySupport && + content['@type'] !== 'Plone Site' && ( + <> + {!content.working_copy && ( +
  • - - {this.props.intl.formatMessage(messages.sharing)} +
  • - )} -
    - - {aliasesAction && ( + + )} + {content.working_copy && content.working_copy_of && ( +
  • - - {this.props.intl.formatMessage(messages.aliases)} - - +
  • - )} -
    - {path !== '' && - !config.settings.excludeLinksAndReferencesMenuItem && ( - -
  • - - {this.props.intl.formatMessage(messages.linkstoitem)} - - -
  • -
    - )} - - {rulesAction && (
  • - - {this.props.intl.formatMessage(messages.rules)} + +
  • +
    + )} + {content.working_copy && !content.working_copy_of && ( + +
  • + props.closeMenu()} + > + {intl.formatMessage(messages.viewWorkingCopy)}
  • - )} -
    -
-
- - {(pluggables) => ( - <> - {pluggables.length > 0 && ( - <> -
-

- {this.props.intl.formatMessage(messages.manageContent)} -

-
-
-
    - {pluggables.map((p) => ( - <>{p()} - ))} -
-
- - )} - - )} -
- {config.settings.hasWorkingCopySupport && - this.props.content['@type'] !== 'Plone Site' && ( - <> - {!this.props.content.working_copy && ( - -
  • - -
  • -
    - )} - {this.props.content.working_copy && - this.props.content.working_copy_of && ( - -
  • - -
  • -
  • - -
  • -
    - )} - {this.props.content.working_copy && - !this.props.content.working_copy_of && ( - -
  • - this.props.closeMenu()} - > - {this.props.intl.formatMessage( - messages.viewWorkingCopy, - )} - - -
  • -
    - )} - - )} - {editAction && config.settings.isMultilingual && ( - -
  • - - {this.props.intl.formatMessage(messages.ManageTranslations)} + + +
  • +
    + )} +
    + ); +}; - - - - - )} -
    - ); - } -} +More.propTypes = { + loadComponent: PropTypes.func.isRequired, + closeMenu: PropTypes.func.isRequired, +}; -export default compose( - injectIntl, - withRouter, - connect( - (state, props) => ({ - actions: state.actions.actions, - pathname: props.pathname, - content: state.content.data, - lang: state.intl.locale, - workingCopy: state.workingCopy, - }), - { applyWorkingCopy, createWorkingCopy, removeWorkingCopy }, - ), -)(More); +export default More; From 86d9a503596c9ab46aea8776dd45e1970805ea45 Mon Sep 17 00:00:00 2001 From: Tisha Soumya Date: Sun, 11 Aug 2024 00:18:22 +0530 Subject: [PATCH 33/58] Refactor RenderGroups (#4993) Co-authored-by: David Glick --- packages/volto/news/4993.feature | 1 + .../Controlpanels/Groups/RenderGroups.jsx | 196 +++++++----------- .../__snapshots__/RenderGroups.test.jsx.snap | 12 +- 3 files changed, 81 insertions(+), 128 deletions(-) create mode 100644 packages/volto/news/4993.feature diff --git a/packages/volto/news/4993.feature b/packages/volto/news/4993.feature new file mode 100644 index 0000000000..f5f831e506 --- /dev/null +++ b/packages/volto/news/4993.feature @@ -0,0 +1 @@ +Refactor ControlPanels/Groups RenderGroups from class components to functional component. @Tishasoumya-02 \ No newline at end of file diff --git a/packages/volto/src/components/manage/Controlpanels/Groups/RenderGroups.jsx b/packages/volto/src/components/manage/Controlpanels/Groups/RenderGroups.jsx index f707003ed1..c1117a59bd 100644 --- a/packages/volto/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +++ b/packages/volto/src/components/manage/Controlpanels/Groups/RenderGroups.jsx @@ -1,134 +1,86 @@ -/** - * Users controlpanel groups. - * @module components/manage/Controlpanels/UsersControlpanelGroups - */ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { FormattedMessage, injectIntl } from 'react-intl'; +import { FormattedMessage } from 'react-intl'; import { Dropdown, Table, Checkbox } from 'semantic-ui-react'; import trashSVG from '@plone/volto/icons/delete.svg'; import ploneSVG from '@plone/volto/icons/plone.svg'; import { Icon } from '@plone/volto/components'; import { canAssignRole } from '@plone/volto/helpers'; -/** - * UsersControlpanelGroups class. - * @class UsersControlpanelGroups - * @extends Component - */ -class RenderGroups extends Component { - /** - * Property types. - * @property {Object} propTypes Property types. - * @static - */ - static propTypes = { - //single group - group: PropTypes.shape({ - title: PropTypes.string, - description: PropTypes.string, - email: PropTypes.string, - groupname: PropTypes.string, - roles: PropTypes.arrayOf(PropTypes.string), - }).isRequired, - - roles: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string, - }), - ).isRequired, - inheritedRole: PropTypes.array, - onDelete: PropTypes.func.isRequired, - isUserManager: PropTypes.bool.isRequired, +const RenderGroups = (props) => { + const onChange = (event, { value }) => { + const [group, role] = value.split('.'); + props.updateGroups(group, role); }; - - /** - * Constructor - * @method constructor - * @param {Object} props Component properties - * @constructs Sharing - */ - constructor(props) { - super(props); - this.onChange = this.onChange.bind(this); - } - - /** - * @param {*} event - * @param {*} { value } - * @memberof UsersControlpanelUser - */ - onChange(event, { value }) { - const [group, role] = value.split('&role='); - this.props.updateGroups(group, role); - } - - /** - *@param {*} - *@returns {Boolean} - *@memberof RenderGroups - */ - isAuthGroup = (roleId) => { - return this.props.inheritedRole.includes(roleId); + const isAuthGroup = (roleId) => { + return props.inheritedRole.includes(roleId); }; - - canDeleteGroup() { - if (this.props.isUserManager) return true; - return !this.props.group.roles.includes('Manager'); - } - - /** - * Render method. - * @method render - * @returns {string} Markup for the component. - */ - render() { - return ( - - {this.props.group.groupname} - {this.props.roles.map((role) => ( - - {this.props.inheritedRole && - this.props.inheritedRole.includes(role.id) && - this.props.group.roles.includes('Authenticated') ? ( - - ) : ( - - )} - - ))} - - {this.canDeleteGroup() && ( - - - - - - - - + const canDeleteGroup = () => { + if (props.isUserManager) return true; + return !props.group.roles.includes('Manager'); + }; + return ( + + {props.group.groupname} + {props.roles.map((role) => ( + + {props.inheritedRole && + props.inheritedRole.includes(role.id) && + props.group.roles.includes('Authenticated') ? ( + + ) : ( + )} - - ); - } -} + ))} + + {canDeleteGroup() && ( + + + + + + + + + )} + + + ); +}; + +RenderGroups.propTypes = { + //single group + group: PropTypes.shape({ + title: PropTypes.string, + description: PropTypes.string, + email: PropTypes.string, + groupname: PropTypes.string, + roles: PropTypes.arrayOf(PropTypes.string), + }).isRequired, -export default injectIntl(RenderGroups); + roles: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string, + }), + ).isRequired, + inheritedRole: PropTypes.array, + onDelete: PropTypes.func.isRequired, +}; +export default RenderGroups; diff --git a/packages/volto/src/components/manage/Controlpanels/Groups/__snapshots__/RenderGroups.test.jsx.snap b/packages/volto/src/components/manage/Controlpanels/Groups/__snapshots__/RenderGroups.test.jsx.snap index bb19f13b4d..2e896ea639 100644 --- a/packages/volto/src/components/manage/Controlpanels/Groups/__snapshots__/RenderGroups.test.jsx.snap +++ b/packages/volto/src/components/manage/Controlpanels/Groups/__snapshots__/RenderGroups.test.jsx.snap @@ -26,7 +26,7 @@ exports[`UsersControlpanelGroups renders a UsersControlpanelGroups component 1`] readOnly={true} tabIndex={0} type="checkbox" - value="Administrators&role=Member" + value="Administrators.Member" />
    @@ -48,7 +48,7 @@ exports[`UsersControlpanelGroups renders a UsersControlpanelGroups component 1`] readOnly={true} tabIndex={0} type="checkbox" - value="Administrators&role=Reader" + value="Administrators.Reader" />
    @@ -70,7 +70,7 @@ exports[`UsersControlpanelGroups renders a UsersControlpanelGroups component 1`] readOnly={true} tabIndex={0} type="checkbox" - value="Administrators&role=Manager" + value="Administrators.Manager" />
    @@ -155,7 +155,7 @@ exports[`UsersControlpanelGroups renders a UsersControlpanelGroups component wit readOnly={true} tabIndex={0} type="checkbox" - value="Administrators&role=Member" + value="Administrators.Member" />
    @@ -177,7 +177,7 @@ exports[`UsersControlpanelGroups renders a UsersControlpanelGroups component wit readOnly={true} tabIndex={0} type="checkbox" - value="Administrators&role=Reader" + value="Administrators.Reader" />
    @@ -199,7 +199,7 @@ exports[`UsersControlpanelGroups renders a UsersControlpanelGroups component wit readOnly={true} tabIndex={-1} type="checkbox" - value="Administrators&role=Manager" + value="Administrators.Manager" />