diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 6623bf1ad03..927b95a92f5 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -32,5 +32,6 @@ build: - asdf reshim nodejs - pnpm install - pnpm build:registry + - pnpm build:components - (cd packages/volto && pnpm build-storybook -o ${READTHEDOCS_OUTPUT}/html/storybook) - make docs-rtd-pr-preview diff --git a/Makefile b/Makefile index 1184a88fd43..a47a86ac2a6 100644 --- a/Makefile +++ b/Makefile @@ -162,7 +162,7 @@ packages/helpers/dist: $(shell find packages/helpers/src -type f) pnpm build:helpers .PHONY: build-deps -build-deps: packages/registry/dist ## Build dependencies +build-deps: packages/registry/dist packages/components/dist ## Build dependencies .PHONY: build-all-deps build-all-deps: packages/registry/dist packages/components/dist packages/client/dist packages/providers/dist packages/helpers/dist ## Build all dependencies diff --git a/docs/source/_static/blockAlignment.png b/docs/source/_static/blockAlignment.png new file mode 100644 index 00000000000..c313b014d71 Binary files /dev/null and b/docs/source/_static/blockAlignment.png differ diff --git a/docs/source/_static/blockWidth.png b/docs/source/_static/blockWidth.png new file mode 100644 index 00000000000..84b46207537 Binary files /dev/null and b/docs/source/_static/blockWidth.png differ diff --git a/docs/source/_static/size.png b/docs/source/_static/size.png new file mode 100644 index 00000000000..c4be5b99f65 Binary files /dev/null and b/docs/source/_static/size.png differ diff --git a/docs/source/index.md b/docs/source/index.md index e983faf86f8..99e24da2634 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -72,6 +72,7 @@ contributing/index release-notes/index release-management-notes/index conceptual-guides/index +reference/index ``` % Only check change log entries in Volto documentation—not when it is included in the main Plone documentation—to ensure links work and do not redirect. diff --git a/docs/source/reference/index.md b/docs/source/reference/index.md new file mode 100644 index 00000000000..be9f22ba747 --- /dev/null +++ b/docs/source/reference/index.md @@ -0,0 +1,18 @@ +--- +myst: + html_meta: + "description": "Volto reference guides" + "property=og:description": "Volto reference guides" + "property=og:title": "Volto reference guides" + "keywords": "Volto, user interface, frontend, Plone, reference guides" +--- + +# Reference guides + +This section of the documentation contains reference guides for various aspects of Volto. + +```{toctree} +:maxdepth: 1 + +widgets +``` diff --git a/docs/source/reference/widgets.md b/docs/source/reference/widgets.md new file mode 100644 index 00000000000..6ddf9a9607b --- /dev/null +++ b/docs/source/reference/widgets.md @@ -0,0 +1,124 @@ +--- +myst: + html_meta: + "description": "Volto widgets" + "property=og:description": "Volto widgets" + "property=og:title": "Volto widgets" + "keywords": "Plone, Volto, widgets" +--- + +# Widgets + +This chapter describes the set of widgets available in Volto. + +## `ButtonsWidget` + +`ButtonsWidget` is a helper component for building widgets that have a list of buttons which can be toggled, allowing the selection of a single value only. +It's a minimal, extensible base widget used by other widgets. +It renders a set of mutually exclusive toggle buttons, and functions similarly to a radio input. + +With `ButtonsWidget`, you can do the following things. + +- Supply a configurable list of actions, including strings or style definitions. +- Customize a per-action icon and label via the `actionsInfoMap` prop. +- Filter out default actions with the `filterActions` prop. +- Provide default and current values via `default` and `value` props. +- Pass `disabled` or `isDisabled` props to prevent interaction. + +The following code example demonstrates the complete options for `ButtonsWidget`. + +```ts +type ActionInfo = [React.ReactElement, string] | [string, string]; + +type ActionValue = string | Record<`--${string}`, string>; + +export type ButtonsWidgetProps = { + /** + * Unique identifier for the widget. + */ + id: string; + + /** + * Callback function to handle changes. + */ + onChange: (id: string, value: ActionValue) => void; + + /** + * List of actions available for the widget. + */ + actions?: Array; + + /** + * Map containing additional the information (icon and i18n string) for each action. + */ + actionsInfoMap?: Record; + + /** + * List of actions to be filtered out. In case that we don't want the default ones + * we can filter them out. + */ + filterActions?: string[]; + + /** + * Current value of the widget. + */ + value?: ActionValue; + + /** + * Default value of the widget. + */ + default?: ActionValue; + + /** + * Indicates if the widget is disabled. + */ + disabled?: boolean; + + /** + * Indicates if the widget is disabled (alternative flag for compatibility reasons). + */ + isDisabled?: boolean; +}; +``` + +## `blockWidth` + +`blockWidth` is a widget to select a width from the defined `config.blocks.widths`. +It's based on the `ButtonsWidget`, so the actions and the styles to be applied are configurable. + +````{card} +```{image} ../_static/blockWidth.png +:alt: blockWidth +:target: ../_static/blockWidth.png +``` ++++ +_`blockWidth`_ +```` + +## `blockAlignment` + +`blockAlignment` is a widget to select the block alignment, one of either `left`, `right`, or `center`. +It's based on the `ButtonsWidget`, so the actions and the styles to be applied are configurable. + +````{card} +```{image} ../_static/blockAlignment.png +:alt: blockAlignment +:target: ../_static/blockAlignment.png +``` ++++ +_`blockAlignment`_ +```` + +## `size` + +`size` is a widget to select the block size from a default list of values, one of either `small`, `medium`, or `large`. +It's based on the `ButtonsWidget`, so the actions and the styles to be applied are configurable. + +````{card} +```{image} ../_static/size.png +:alt: size +:target: ../_static/size.png +``` ++++ +_`size`_ +```` diff --git a/docs/source/upgrade-guide/index.md b/docs/source/upgrade-guide/index.md index c6b30ebcbac..6327dc709dc 100644 --- a/docs/source/upgrade-guide/index.md +++ b/docs/source/upgrade-guide/index.md @@ -176,6 +176,32 @@ Volto now uses pnpm 10. If you have packages that use lifecycle scripts (such as `preinstall` or `postinstall`) in {file}`package.json`, you must configure `pnpm`'s [`onlyBuiltDependencies` setting](https://pnpm.io/settings#onlybuiltdependencies) to allow them. +### `AlignWidget` and `ButtonsWidget` are now Semantic UI-free +```{versionadded} Volto 19.0.0-alpha.10 +``` + +The `AlignWidget` and `ButtonsWidget` components have been refactored to remove their dependency on Semantic UI. +They are now based on the `@plone/components` library and refactored into TypeScript. +To differentiate them from the old Semantic UI-based widgets, they use different `classNames`, too. +This allows you to continue using the old widgets in either a shadow or customized version, if needed, without CSS conflicts. + +### `Size`, `blockWidth`, and `blockAlignment` widgets added +```{versionadded} Volto 19.0.0-alpha.10 +``` + +Three new widgets have been added based on the `ButtonsWidget` from `@plone/components`: +`size` widget +: For selecting size options of either `small`, `medium`, or `large`. + +`blockWidth` widget +: For selecting block width options of either `narrow`, `default`, `layout`, or `full`. + +`blockAlignment` widget +: For selecting block alignment options of either `left`, `center`, or `right`. + +They are available in the global widgets configuration and can be used in your add-ons. +They all are meant to be used using the `StyleWrapper` with custom CSS properties. + (upgrading-to-volto-18-x-x)= ## Upgrading to Volto 18.x.x diff --git a/packages/components/news/7555.feature b/packages/components/news/7555.feature new file mode 100644 index 00000000000..781c76028d0 --- /dev/null +++ b/packages/components/news/7555.feature @@ -0,0 +1 @@ +Added `Radio` component to basic set of components, proxied from RAC. @sneridagh diff --git a/packages/components/src/components/RadioGroup/RadioGroup.stories.tsx b/packages/components/src/components/RadioGroup/RadioGroup.stories.tsx index b84ad1dbb35..8178e4cbf93 100644 --- a/packages/components/src/components/RadioGroup/RadioGroup.stories.tsx +++ b/packages/components/src/components/RadioGroup/RadioGroup.stories.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import { RadioGroup } from './RadioGroup'; -import { Radio } from 'react-aria-components'; +import { Radio, RadioGroup } from './RadioGroup'; import type { Meta, StoryObj } from '@storybook/react-vite'; diff --git a/packages/components/src/components/RadioGroup/RadioGroup.tsx b/packages/components/src/components/RadioGroup/RadioGroup.tsx index 7090d0f15a1..532a332703d 100644 --- a/packages/components/src/components/RadioGroup/RadioGroup.tsx +++ b/packages/components/src/components/RadioGroup/RadioGroup.tsx @@ -4,6 +4,7 @@ import { Label, RadioGroup as RACRadioGroup, type RadioGroupProps as RACRadioGroupProps, + Radio, Text, type ValidationResult, } from 'react-aria-components'; @@ -31,3 +32,5 @@ export function RadioGroup({ ); } + +export { Radio }; diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index bab74f4f7e5..b189bb352d3 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -31,7 +31,7 @@ export { Modal } from './components/Modal/Modal'; export { NumberField } from './components/NumberField/NumberField'; export { Popover, type PopoverProps } from './components/Popover/Popover'; export { ProgressBar } from './components/ProgressBar/ProgressBar'; -export { RadioGroup } from './components/RadioGroup/RadioGroup'; +export { Radio, RadioGroup } from './components/RadioGroup/RadioGroup'; export { RangeCalendar } from './components/RangeCalendar/RangeCalendar'; export { SearchField } from './components/SearchField/SearchField'; export { Select, SelectItem } from './components/Select/Select'; diff --git a/packages/types/news/7555.feature b/packages/types/news/7555.feature new file mode 100644 index 00000000000..26f4a9ea3e2 --- /dev/null +++ b/packages/types/news/7555.feature @@ -0,0 +1 @@ +Added new widgets config typings. @sneridagh diff --git a/packages/types/src/config/Widgets.d.ts b/packages/types/src/config/Widgets.d.ts index 4766436cdb7..532a5db23a6 100644 --- a/packages/types/src/config/Widgets.d.ts +++ b/packages/types/src/config/Widgets.d.ts @@ -49,7 +49,10 @@ export type WidgetByWidgetTypes = | 'static_text' | 'hidden' | 'radio_group' - | 'checkbox_group'; + | 'checkbox_group' + | 'blockAlignment' + | 'blockWidth' + | 'size'; export type WidgetsConfigByWidget< K extends WidgetByWidgetTypes = WidgetByWidgetTypes, diff --git a/packages/volto/Makefile b/packages/volto/Makefile index d08bbd22752..258dad0d2aa 100644 --- a/packages/volto/Makefile +++ b/packages/volto/Makefile @@ -79,7 +79,7 @@ cypress-install: ## Install Cypress for acceptance tests (cd ../../ && pnpm build:components) .PHONY: build-deps -build-deps: ../registry/dist ## Build dependencies +build-deps: ../registry/dist ../components/dist ## Build dependencies .PHONY: i18n i18n: ## Extract and compile translations diff --git a/packages/volto/cypress/tests/core/blocks/blocks-teaser.js b/packages/volto/cypress/tests/core/blocks/blocks-teaser.js index 1c3c232ae96..fab9036d346 100644 --- a/packages/volto/cypress/tests/core/blocks/blocks-teaser.js +++ b/packages/volto/cypress/tests/core/blocks/blocks-teaser.js @@ -43,7 +43,9 @@ context('Blocks Acceptance Tests', () => { ).click(); cy.get('[aria-label="Select Blue Orchids"]').dblclick(); cy.wait(500); - cy.get('.align-buttons .ui.buttons button[aria-label="Center"]').click(); + cy.get( + '[class*="field-wrapper-align-"] .buttons input[aria-label="Center"]', + ).click({ force: true }); cy.get('#toolbar-save').click(); // THEN I can see the Teaser block diff --git a/packages/volto/locales/ca/LC_MESSAGES/volto.po b/packages/volto/locales/ca/LC_MESSAGES/volto.po index 456d028c85d..9af9a3260a6 100644 --- a/packages/volto/locales/ca/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ca/LC_MESSAGES/volto.po @@ -605,6 +605,7 @@ msgstr "Cel·la" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "Centre" @@ -1014,6 +1015,7 @@ msgstr "Data (el més nou primer)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1667,6 +1669,7 @@ msgstr "Des de" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "Complet" @@ -2052,6 +2055,7 @@ msgstr "" #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "" @@ -2082,6 +2086,7 @@ msgstr "Última versió" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "Disseny" @@ -2094,6 +2099,7 @@ msgstr "Imatge Principal" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "A l'esquerra" @@ -2292,6 +2298,7 @@ msgstr "El valor màxim és {len}." #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "" @@ -2404,6 +2411,7 @@ msgstr "Nom" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "" @@ -3120,6 +3128,7 @@ msgstr "Text enriquit" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "Dret" @@ -3516,6 +3525,7 @@ msgstr "Mida: {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "" diff --git a/packages/volto/locales/de/LC_MESSAGES/volto.po b/packages/volto/locales/de/LC_MESSAGES/volto.po index b3c5260b0e2..6a8ed71f354 100644 --- a/packages/volto/locales/de/LC_MESSAGES/volto.po +++ b/packages/volto/locales/de/LC_MESSAGES/volto.po @@ -604,6 +604,7 @@ msgstr "Zelle" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "Mittig" @@ -1013,6 +1014,7 @@ msgstr "Datum (neustes zuerst)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1666,6 +1668,7 @@ msgstr "E-Mail" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "Volle Breite" @@ -2051,6 +2054,7 @@ msgstr "Dies ist ein sprachunabhängiges Feld. Jeder Wert, den Sie hier eingeben #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "Groß" @@ -2081,6 +2085,7 @@ msgstr "Verfügbare Version" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "Layout" @@ -2093,6 +2098,7 @@ msgstr "Lead-Bild" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "Links" @@ -2291,6 +2297,7 @@ msgstr "Maximaler Wert ist {len}" #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "Mittel" @@ -2403,6 +2410,7 @@ msgstr "Name" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "Schmal" @@ -3119,6 +3127,7 @@ msgstr "Richtext" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "Rechte" @@ -3515,6 +3524,7 @@ msgstr "Grösse: {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "Klein" diff --git a/packages/volto/locales/en/LC_MESSAGES/volto.po b/packages/volto/locales/en/LC_MESSAGES/volto.po index 39f6f5495f0..edfcebd23ee 100644 --- a/packages/volto/locales/en/LC_MESSAGES/volto.po +++ b/packages/volto/locales/en/LC_MESSAGES/volto.po @@ -599,6 +599,7 @@ msgstr "" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "" @@ -1008,6 +1009,7 @@ msgstr "" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1661,6 +1663,7 @@ msgstr "" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "" @@ -2046,6 +2049,7 @@ msgstr "" #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "" @@ -2076,6 +2080,7 @@ msgstr "" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "" @@ -2088,6 +2093,7 @@ msgstr "" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "" @@ -2286,6 +2292,7 @@ msgstr "" #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "" @@ -2398,6 +2405,7 @@ msgstr "" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "" @@ -3114,6 +3122,7 @@ msgstr "" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "" @@ -3510,6 +3519,7 @@ msgstr "" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "" diff --git a/packages/volto/locales/es/LC_MESSAGES/volto.po b/packages/volto/locales/es/LC_MESSAGES/volto.po index 3ce975e1f16..0e56d556a4d 100644 --- a/packages/volto/locales/es/LC_MESSAGES/volto.po +++ b/packages/volto/locales/es/LC_MESSAGES/volto.po @@ -606,6 +606,7 @@ msgstr "Celda" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "Centro" @@ -1015,6 +1016,7 @@ msgstr "Fecha (primero los más recientes)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1668,6 +1670,7 @@ msgstr "De" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "Completo" @@ -2053,6 +2056,7 @@ msgstr "" #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "Grande" @@ -2083,6 +2087,7 @@ msgstr "Última versión" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "Plantilla" @@ -2095,6 +2100,7 @@ msgstr "Imagen principal" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "Izquierda" @@ -2293,6 +2299,7 @@ msgstr "El valor máximo es {len}." #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "Mediano" @@ -2405,6 +2412,7 @@ msgstr "Nombre" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "Filtrar" @@ -3121,6 +3129,7 @@ msgstr "Texto formateado" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "Derecha" @@ -3517,6 +3526,7 @@ msgstr "Tamaño: {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "Pequeño" diff --git a/packages/volto/locales/eu/LC_MESSAGES/volto.po b/packages/volto/locales/eu/LC_MESSAGES/volto.po index 93966474a0f..deeb83b101a 100644 --- a/packages/volto/locales/eu/LC_MESSAGES/volto.po +++ b/packages/volto/locales/eu/LC_MESSAGES/volto.po @@ -606,6 +606,7 @@ msgstr "Zelda" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "Erdiratu" @@ -1015,6 +1016,7 @@ msgstr "Data (berriena lehenengo)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1668,6 +1670,7 @@ msgstr "Nok" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "Osoa" @@ -2053,6 +2056,7 @@ msgstr "" #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "Handia" @@ -2083,6 +2087,7 @@ msgstr "Azken bertsioa" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "Itxura" @@ -2095,6 +2100,7 @@ msgstr "Irudia" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "Ezkerrean" @@ -2293,6 +2299,7 @@ msgstr "Balio handiena {len} da." #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "Ertaina" @@ -2405,6 +2412,7 @@ msgstr "Izena" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "Estutu" @@ -3121,6 +3129,7 @@ msgstr "Testu aberatsa" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "Eskuman" @@ -3517,6 +3526,7 @@ msgstr "Tamaina: {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "Txikia" diff --git a/packages/volto/locales/fi/LC_MESSAGES/volto.po b/packages/volto/locales/fi/LC_MESSAGES/volto.po index d7177daba34..e0e4793162b 100644 --- a/packages/volto/locales/fi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fi/LC_MESSAGES/volto.po @@ -604,6 +604,7 @@ msgstr "Sarake" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "Keskellä" @@ -1013,6 +1014,7 @@ msgstr "Päivä (uusin ensin)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1666,6 +1668,7 @@ msgstr "Sähköposti" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "Kokoleveästi" @@ -2051,6 +2054,7 @@ msgstr "" #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "Suuri" @@ -2081,6 +2085,7 @@ msgstr "Viimeisin versio" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "Asettelu" @@ -2093,6 +2098,7 @@ msgstr "Nostokuva" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "Vasemmalla" @@ -2291,6 +2297,7 @@ msgstr "Suurin mahdollinen arvo on {len}." #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "Keskikokoinen" @@ -2403,6 +2410,7 @@ msgstr "Nimi" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "Kavenna" @@ -3119,6 +3127,7 @@ msgstr "Muotoiltu teksti" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "Oikealla" @@ -3515,6 +3524,7 @@ msgstr "Koko: {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "Pieni" diff --git a/packages/volto/locales/fr/LC_MESSAGES/volto.po b/packages/volto/locales/fr/LC_MESSAGES/volto.po index 55d1fb29faf..ec08d22b548 100644 --- a/packages/volto/locales/fr/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fr/LC_MESSAGES/volto.po @@ -606,6 +606,7 @@ msgstr "Cellule" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "Centre" @@ -1015,6 +1016,7 @@ msgstr "Date (le plus récent en premier)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1668,6 +1670,7 @@ msgstr "De" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "Complet" @@ -2053,6 +2056,7 @@ msgstr "" #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "Large" @@ -2083,6 +2087,7 @@ msgstr "Dernière version" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "Disposition" @@ -2095,6 +2100,7 @@ msgstr "Image de garde" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "Gauche" @@ -2293,6 +2299,7 @@ msgstr "La valeur maximale est {len}." #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "Moyen" @@ -2405,6 +2412,7 @@ msgstr "Nom" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "Étroit" @@ -3121,6 +3129,7 @@ msgstr "Texte riche" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "Droit" @@ -3517,6 +3526,7 @@ msgstr "Taille : {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "Petit" diff --git a/packages/volto/locales/hi/LC_MESSAGES/volto.po b/packages/volto/locales/hi/LC_MESSAGES/volto.po index 5452180c78b..135fd4cbb2c 100644 --- a/packages/volto/locales/hi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/hi/LC_MESSAGES/volto.po @@ -599,6 +599,7 @@ msgstr "सेल" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "केंद्र" @@ -1008,6 +1009,7 @@ msgstr "तारीख (नवीनतम पहले)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1661,6 +1663,7 @@ msgstr "से" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "पूरा" @@ -2046,6 +2049,7 @@ msgstr "" #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "बड़ा" @@ -2076,6 +2080,7 @@ msgstr "नवीनतम संस्करण" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "लेआउट" @@ -2088,6 +2093,7 @@ msgstr "प्रमुख छवि" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "बाएँ" @@ -2286,6 +2292,7 @@ msgstr "अधिकतम मूल्य {len} है।" #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "मध्यम" @@ -2398,6 +2405,7 @@ msgstr "नाम" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "संकीर्ण" @@ -3114,6 +3122,7 @@ msgstr "रिचटेक्स्ट" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "दाईं ओर" @@ -3510,6 +3519,7 @@ msgstr "साइज: {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "छोटा" diff --git a/packages/volto/locales/it/LC_MESSAGES/volto.po b/packages/volto/locales/it/LC_MESSAGES/volto.po index 0582d94a28f..c135832a4eb 100644 --- a/packages/volto/locales/it/LC_MESSAGES/volto.po +++ b/packages/volto/locales/it/LC_MESSAGES/volto.po @@ -599,6 +599,7 @@ msgstr "Cella" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "Centrato" @@ -1008,6 +1009,7 @@ msgstr "Data (prima i più recenti)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1661,6 +1663,7 @@ msgstr "Da" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "A tutta larghezza" @@ -2046,6 +2049,7 @@ msgstr "Titolo dell'icona indipendente dalla lingua" #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "Grande" @@ -2076,6 +2080,7 @@ msgstr "Ultima versione" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "Layout" @@ -2088,6 +2093,7 @@ msgstr "Immagine di testata" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "Sinistra" @@ -2286,6 +2292,7 @@ msgstr "Il valore massimo è {len}." #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "Medio" @@ -2398,6 +2405,7 @@ msgstr "Nome" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "Restringi" @@ -3114,6 +3122,7 @@ msgstr "Testo formattato" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "Destra" @@ -3510,6 +3519,7 @@ msgstr "Dimensione: {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "Piccolo" diff --git a/packages/volto/locales/ja/LC_MESSAGES/volto.po b/packages/volto/locales/ja/LC_MESSAGES/volto.po index 6a1624a677d..69d5869fce3 100644 --- a/packages/volto/locales/ja/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ja/LC_MESSAGES/volto.po @@ -604,6 +604,7 @@ msgstr "セル" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "中央" @@ -1013,6 +1014,7 @@ msgstr "日付 (新しい順)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1666,6 +1668,7 @@ msgstr "メールアドレス" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "フルサイズ" @@ -2051,6 +2054,7 @@ msgstr "" #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "" @@ -2081,6 +2085,7 @@ msgstr "最新バージョン" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "レイアウト" @@ -2093,6 +2098,7 @@ msgstr "リード画像" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "左" @@ -2291,6 +2297,7 @@ msgstr "最大の値は {len} です。" #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "" @@ -2403,6 +2410,7 @@ msgstr "名前" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "" @@ -3119,6 +3127,7 @@ msgstr "Richtext" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "右" @@ -3515,6 +3524,7 @@ msgstr "サイズ: {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "" diff --git a/packages/volto/locales/nl/LC_MESSAGES/volto.po b/packages/volto/locales/nl/LC_MESSAGES/volto.po index c90745f9343..54aec73deb9 100644 --- a/packages/volto/locales/nl/LC_MESSAGES/volto.po +++ b/packages/volto/locales/nl/LC_MESSAGES/volto.po @@ -603,6 +603,7 @@ msgstr "Cel" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "Centreren" @@ -1012,6 +1013,7 @@ msgstr "Datum (nieuwste eerst)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1665,6 +1667,7 @@ msgstr "Van" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "Volledig" @@ -2050,6 +2053,7 @@ msgstr "Dit is een taalonafhankelijk veld. Elke hier ingevoerde waarde, overschr #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "Groot" @@ -2080,6 +2084,7 @@ msgstr "Laatste versie" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "Lay-out" @@ -2092,6 +2097,7 @@ msgstr "Leidende afbeelding" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "Links" @@ -2290,6 +2296,7 @@ msgstr "Maximum lengte is {len}." #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "Gemiddeld" @@ -2402,6 +2409,7 @@ msgstr "Naam" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "Smal" @@ -3118,6 +3126,7 @@ msgstr "Opgemaakte tekst" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "Rechts" @@ -3514,6 +3523,7 @@ msgstr "Grootte: {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "Klein" diff --git a/packages/volto/locales/pt/LC_MESSAGES/volto.po b/packages/volto/locales/pt/LC_MESSAGES/volto.po index fe489aa1cf3..8a4d389ad69 100644 --- a/packages/volto/locales/pt/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt/LC_MESSAGES/volto.po @@ -604,6 +604,7 @@ msgstr "Célula" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "Centro" @@ -1013,6 +1014,7 @@ msgstr "" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1666,6 +1668,7 @@ msgstr "De" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "Cheio" @@ -2051,6 +2054,7 @@ msgstr "" #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "" @@ -2081,6 +2085,7 @@ msgstr "" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "" @@ -2093,6 +2098,7 @@ msgstr "" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "Esquerda" @@ -2291,6 +2297,7 @@ msgstr "" #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "" @@ -2403,6 +2410,7 @@ msgstr "Nome" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "" @@ -3119,6 +3127,7 @@ msgstr "Richtext" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "Direita" @@ -3515,6 +3524,7 @@ msgstr "" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "" diff --git a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po index ebfa22b890f..0afb4f8fa03 100644 --- a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po @@ -605,6 +605,7 @@ msgstr "Célula" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "Centro" @@ -1014,6 +1015,7 @@ msgstr "Data (mais novo primeiro)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1667,6 +1669,7 @@ msgstr "E-mail" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "Cheia" @@ -2052,6 +2055,7 @@ msgstr "Esse é um campo independente do idioma. Qualquer valor que você inseri #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "Grande" @@ -2082,6 +2086,7 @@ msgstr "Última versão" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "Layout" @@ -2094,6 +2099,7 @@ msgstr "Imagem principal" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "Esquerda" @@ -2292,6 +2298,7 @@ msgstr "O valor máximo é {len}." #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "Médio" @@ -2404,6 +2411,7 @@ msgstr "Nome" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "Estreito" @@ -3120,6 +3128,7 @@ msgstr "Texto rico" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "Direita" @@ -3516,6 +3525,7 @@ msgstr "Tamanho: {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "Pequeno" diff --git a/packages/volto/locales/ro/LC_MESSAGES/volto.po b/packages/volto/locales/ro/LC_MESSAGES/volto.po index ee2163758a5..d90435e932c 100644 --- a/packages/volto/locales/ro/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ro/LC_MESSAGES/volto.po @@ -605,6 +605,7 @@ msgstr "Celulă" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "Centrează" @@ -1014,6 +1015,7 @@ msgstr "Data (cea mai recentă mai întâi)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1667,6 +1669,7 @@ msgstr "Din" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "Complet" @@ -2052,6 +2055,7 @@ msgstr "Acest câmp este independent de limbă. Orice valoare introdusă aici va #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "Mare" @@ -2082,6 +2086,7 @@ msgstr "Ultima versiune" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "Aspect" @@ -2094,6 +2099,7 @@ msgstr "Imaginea de start" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "Stânga" @@ -2292,6 +2298,7 @@ msgstr "Valoarea maximă este {len}." #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "Mediu" @@ -2404,6 +2411,7 @@ msgstr "Nume" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "Îngust" @@ -3120,6 +3128,7 @@ msgstr "Richtext" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "Dreapta" @@ -3516,6 +3525,7 @@ msgstr "Dimansiune: {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "Mic" diff --git a/packages/volto/locales/ru/LC_MESSAGES/volto.po b/packages/volto/locales/ru/LC_MESSAGES/volto.po index 1270a36b496..d9f55de8c94 100644 --- a/packages/volto/locales/ru/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ru/LC_MESSAGES/volto.po @@ -604,6 +604,7 @@ msgstr "Ячейка" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "По центру" @@ -1013,6 +1014,7 @@ msgstr "Дата (сначала новые)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1666,6 +1668,7 @@ msgstr "От" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "Полный" @@ -2051,6 +2054,7 @@ msgstr "Это независимое от языка поле. Любое зн #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "Большой" @@ -2081,6 +2085,7 @@ msgstr "Последняя версия" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "Макет" @@ -2093,6 +2098,7 @@ msgstr "Главное изображение" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "Слева" @@ -2291,6 +2297,7 @@ msgstr "Максимальное значение {len}." #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "Средний" @@ -2403,6 +2410,7 @@ msgstr "Имя" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "Узкий" @@ -3119,6 +3127,7 @@ msgstr "Форматированный текст" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "Справа" @@ -3515,6 +3524,7 @@ msgstr "Размер: {size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "Маленький" diff --git a/packages/volto/locales/volto.pot b/packages/volto/locales/volto.pot index 02f71fe1f87..de816e45914 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: 2025-10-10T18:16:11.479Z\n" +"POT-Creation-Date: 2025-10-28T09:22:24.223Z\n" "Last-Translator: Plone i18n \n" "Language-Team: Plone i18n \n" "Content-Type: text/plain; charset=utf-8\n" @@ -601,6 +601,7 @@ msgstr "" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "" @@ -1010,6 +1011,7 @@ msgstr "" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1663,6 +1665,7 @@ msgstr "" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "" @@ -2048,6 +2051,7 @@ msgstr "" #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "" @@ -2078,6 +2082,7 @@ msgstr "" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "" @@ -2090,6 +2095,7 @@ msgstr "" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "" @@ -2288,6 +2294,7 @@ msgstr "" #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "" @@ -2400,6 +2407,7 @@ msgstr "" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "" @@ -3116,6 +3124,7 @@ msgstr "" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "" @@ -3512,6 +3521,7 @@ msgstr "" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "" diff --git a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po index 51a0ee61442..634c4300ef2 100644 --- a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po +++ b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po @@ -605,6 +605,7 @@ msgstr "单元格" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Center" msgstr "中间" @@ -1014,6 +1015,7 @@ msgstr "日期(最新在前)" #: components/manage/Controlpanels/UndoControlpanel #: components/manage/Preferences/ChangePassword #: components/manage/Preferences/PersonalPreferences +#: components/manage/Widgets/BlockWidth #: components/manage/Widgets/SchemaWidget #: components/manage/Widgets/SelectWidget #: components/theme/Comments/CommentEditModal @@ -1667,6 +1669,7 @@ msgstr "" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Full" msgstr "" @@ -2052,6 +2055,7 @@ msgstr "" #. Default: "Large" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Large" msgstr "大" @@ -2082,6 +2086,7 @@ msgstr "最新版本" #. Default: "Layout" #: components/manage/Controlpanels/ContentTypesActions +#: components/manage/Widgets/BlockWidth msgid "Layout" msgstr "布局" @@ -2094,6 +2099,7 @@ msgstr "首图" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Left" msgstr "" @@ -2292,6 +2298,7 @@ msgstr "最大值为{len}。" #. Default: "Medium" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Medium" msgstr "中等" @@ -2404,6 +2411,7 @@ msgstr "名字" #. Default: "Narrow" #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockWidth msgid "Narrow" msgstr "" @@ -3120,6 +3128,7 @@ msgstr "富文本" #: components/manage/Blocks/Maps/Edit #: components/manage/Sidebar/AlignBlock #: components/manage/Widgets/AlignWidget +#: components/manage/Widgets/BlockAlignment msgid "Right" msgstr "" @@ -3516,6 +3525,7 @@ msgstr "大小:{size}" #. Default: "Small" #: components/manage/Widgets/ImageSizeWidget +#: components/manage/Widgets/Size msgid "Small" msgstr "小" diff --git a/packages/volto/news/7555.breaking b/packages/volto/news/7555.breaking new file mode 100644 index 00000000000..27834866ed7 --- /dev/null +++ b/packages/volto/news/7555.breaking @@ -0,0 +1,2 @@ +`AlignWidget` and `ButtonsWidget` are now Semantic UI-free, and they are now based in `@plone/components`. +See upgrade guide for more information. @sneridagh diff --git a/packages/volto/news/7555.feature b/packages/volto/news/7555.feature new file mode 100644 index 00000000000..da04f0a0de0 --- /dev/null +++ b/packages/volto/news/7555.feature @@ -0,0 +1 @@ +New `@plone/components`-based widgets: `Size`, `blockWidth`, and `blockAlignment`. All of them are `ButtonsWidget`-based. @sneridagh diff --git a/packages/volto/package.json b/packages/volto/package.json index 3a25c04801c..fe98f9a1222 100644 --- a/packages/volto/package.json +++ b/packages/volto/package.json @@ -183,6 +183,7 @@ "@dnd-kit/utilities": "3.2.2", "@loadable/component": "5.14.1", "@loadable/server": "5.14.0", + "@plone/components": "workspace:*", "@plone/registry": "workspace:*", "@plone/scripts": "workspace:*", "@plone/volto-slate": "workspace:*", diff --git a/packages/volto/src/components/manage/Widgets/AlignWidget.stories.jsx b/packages/volto/src/components/manage/Widgets/AlignWidget.stories.jsx index f314b0a4bdc..48b9945b7e6 100644 --- a/packages/volto/src/components/manage/Widgets/AlignWidget.stories.jsx +++ b/packages/volto/src/components/manage/Widgets/AlignWidget.stories.jsx @@ -7,6 +7,15 @@ export const Align = WidgetStory.bind({ widget: AlignWidget, }); +export const AlignDefaultLeft = WidgetStory.bind({ + props: { + id: 'align-default-left', + title: 'Align (default left)', + default: 'left', + }, + widget: AlignWidget, +}); + export default { title: 'Edit Widgets/Align', component: AlignWidget, diff --git a/packages/volto/src/components/manage/Widgets/AlignWidget.test.jsx b/packages/volto/src/components/manage/Widgets/AlignWidget.test.jsx deleted file mode 100644 index 278bfe4c670..00000000000 --- a/packages/volto/src/components/manage/Widgets/AlignWidget.test.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import configureStore from 'redux-mock-store'; -import { Provider } from 'react-intl-redux'; -import imageFullSVG from '@plone/volto/icons/image-full.svg'; - -import AlignWidget from './AlignWidget'; - -const mockStore = configureStore(); - -describe('renders an align widget component', () => { - it('basic', () => { - const store = mockStore({ - intl: { - locale: 'en', - messages: {}, - }, - }); - - const { asFragment } = render( - - {}} - /> - , - ); - - expect(asFragment()).toMatchSnapshot(); - }); - - it('extended with actions and actionsInfoMap props', () => { - const store = mockStore({ - intl: { - locale: 'en', - messages: {}, - }, - }); - - const { asFragment } = render( - - {}} - actions={['additional']} - actionsInfoMap={{ - additional: [imageFullSVG, 'Additional action title'], - }} - /> - , - ); - - expect(asFragment()).toMatchSnapshot(); - }); -}); diff --git a/packages/volto/src/components/manage/Widgets/AlignWidget.test.tsx b/packages/volto/src/components/manage/Widgets/AlignWidget.test.tsx new file mode 100644 index 00000000000..22b37f584eb --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/AlignWidget.test.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import { describe, it, expect, vi } from 'vitest'; +import { render } from '@testing-library/react'; +import configureStore from 'redux-mock-store'; +import { Provider } from 'react-intl-redux'; +import imageFullSVG from '@plone/volto/icons/image-full.svg'; + +import AlignWidget from './AlignWidget'; +import type { ButtonsWidgetProps } from './ButtonsWidget'; + +const mockStore = configureStore(); + +const renderAlignWidget = ( + props: Partial = {}, +): { + onChange: ButtonsWidgetProps['onChange']; + asFragment: () => DocumentFragment; + getByRole: ReturnType['getByRole']; +} => { + const { onChange: providedOnChange, ...restProps } = props; + const onChange = providedOnChange ?? vi.fn(); + const store = mockStore({ + intl: { + locale: 'en', + messages: {}, + }, + }); + + const widgetProps: ButtonsWidgetProps = { + id: 'align', + title: 'Alignment', + fieldSet: 'default', + onChange, + value: undefined, + default: undefined, + disabled: false, + isDisabled: false, + ...restProps, + }; + + const rendered = render( + + + , + ); + + return { + onChange, + asFragment: rendered.asFragment, + getByRole: rendered.getByRole, + }; +}; + +describe('AlignWidget', () => { + it('renders with default actions', () => { + const { asFragment } = renderAlignWidget(); + expect(asFragment()).toMatchSnapshot(); + }); + + it('renders with custom actions', () => { + const { asFragment } = renderAlignWidget({ + actions: ['additional'], + actionsInfoMap: { + additional: [imageFullSVG, 'Additional action title'], + }, + }); + + expect(asFragment()).toMatchSnapshot(); + }); + + it('selects the defaultAction when value is missing', () => { + const { getByRole } = renderAlignWidget({ + defaultAction: 'left', + }); + + expect(getByRole('radio', { name: 'Left' })).toBeChecked(); + }); + + it('selects the default when value is missing', () => { + const { getByRole } = renderAlignWidget({ + default: 'left', + }); + + expect(getByRole('radio', { name: 'Left' })).toBeChecked(); + }); + + it('does not override provided value with default', () => { + const { onChange } = renderAlignWidget({ + default: 'left', + value: 'center', + }); + + expect(onChange).not.toHaveBeenCalledWith('align', 'left'); + }); +}); diff --git a/packages/volto/src/components/manage/Widgets/AlignWidget.jsx b/packages/volto/src/components/manage/Widgets/AlignWidget.tsx similarity index 72% rename from packages/volto/src/components/manage/Widgets/AlignWidget.jsx rename to packages/volto/src/components/manage/Widgets/AlignWidget.tsx index 2855068e3e3..1081a1286b0 100644 --- a/packages/volto/src/components/manage/Widgets/AlignWidget.jsx +++ b/packages/volto/src/components/manage/Widgets/AlignWidget.tsx @@ -1,12 +1,16 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import ButtonsWidget from './ButtonsWidget'; +import ButtonsWidget, { + type ActionInfo, + type ButtonsWidgetProps, +} from './ButtonsWidget'; import imageLeftSVG from '@plone/volto/icons/image-left.svg'; import imageRightSVG from '@plone/volto/icons/image-right.svg'; import imageFitSVG from '@plone/volto/icons/image-fit.svg'; import imageNarrowSVG from '@plone/volto/icons/image-narrow.svg'; import imageWideSVG from '@plone/volto/icons/image-wide.svg'; import imageFullSVG from '@plone/volto/icons/image-full.svg'; +import type { IntlShape } from 'react-intl'; const messages = defineMessages({ left: { @@ -35,20 +39,32 @@ const messages = defineMessages({ }, }); -export const defaultActionsInfo = ({ intl }) => ({ +export const defaultActionsInfo = ({ + intl, +}: { + intl: IntlShape; +}): Record => ({ left: [imageLeftSVG, intl.formatMessage(messages.left)], - right: [imageRightSVG, intl.formatMessage(messages.right)], center: [imageFitSVG, intl.formatMessage(messages.center)], + right: [imageRightSVG, intl.formatMessage(messages.right)], narrow: [imageNarrowSVG, intl.formatMessage(messages.narrow)], wide: [imageWideSVG, intl.formatMessage(messages.wide)], full: [imageFullSVG, intl.formatMessage(messages.full)], }); -const AlignWidget = (props) => { +type AlignWidgetProps = ButtonsWidgetProps & { + defaultAction?: string; +}; + +const AlignWidget = (props: AlignWidgetProps) => { const intl = useIntl(); - const { actions = ['left', 'right', 'center', 'full'], actionsInfoMap } = - props; + const { + actions = ['left', 'center', 'right', 'full'], + actionsInfoMap, + default: defaultValue, + defaultAction, + } = props; const actionsInfo = { ...defaultActionsInfo({ intl }), @@ -60,7 +76,7 @@ const AlignWidget = (props) => { {...props} actions={actions} actionsInfoMap={actionsInfo} - defaultAction={props.defaultAction || 'center'} + default={defaultValue ?? defaultAction ?? 'center'} /> ); }; diff --git a/packages/volto/src/components/manage/Widgets/BlockAlignment.stories.tsx b/packages/volto/src/components/manage/Widgets/BlockAlignment.stories.tsx new file mode 100644 index 00000000000..e7660c9f5d3 --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/BlockAlignment.stories.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import type { Meta, StoryFn, StoryObj } from '@storybook/react'; + +import BlockAlignment from './BlockAlignment'; +import type { ButtonsWidgetProps } from './ButtonsWidget'; +import { + RealStoreWrapper as Wrapper, + FormUndoWrapper, +} from '@plone/volto/storybook'; + +const meta: Meta = { + title: 'Edit Widgets/BlockAlignment', + component: BlockAlignment, + decorators: [ + (Story) => ( +
+

Block alignment widget

+ +
+ ), + ], +}; + +export default meta; + +type Story = StoryObj; + +type TemplateParameters = { + initialValue?: ButtonsWidgetProps['value']; +}; + +const Template: StoryFn = (args, context) => { + const { initialValue } = + (context.parameters as TemplateParameters | undefined) ?? {}; + const { value: _ignoredValue, onChange: argsOnChange, ...restArgs } = args; + + return ( + + + {({ state, onChange }) => ( +
+ { + argsOnChange?.(id, nextValue); + onChange({ value: nextValue }); + }} + /> +
Value: {JSON.stringify(state.value, null, 2)}
+
+ )} +
+
+ ); +}; + +export const DefaultAlignment: Story = { + render: Template, + args: { + id: 'alignment', + title: 'Block alignment', + block: 'block', + fieldSet: 'default', + }, +}; + +export const CustomActions: Story = { + render: Template, + args: { + ...(DefaultAlignment.args ?? {}), + actions: [ + { + name: 'wide', + label: 'Wide', + style: { + '--block-alignment': 'var(--align-wide)', + }, + }, + { + name: 'full', + label: 'Full', + style: { + '--block-alignment': 'var(--align-full)', + }, + }, + ], + actionsInfoMap: { + wide: ['W', 'Wide alignment'], + full: ['F', 'Full width alignment'], + }, + }, +}; + +export const FilteredActions: Story = { + render: Template, + args: { + ...(DefaultAlignment.args ?? {}), + filterActions: ['center'], + }, +}; diff --git a/packages/volto/src/components/manage/Widgets/BlockAlignment.test.tsx b/packages/volto/src/components/manage/Widgets/BlockAlignment.test.tsx new file mode 100644 index 00000000000..981650e3575 --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/BlockAlignment.test.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import { describe, it, expect, vi } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/react'; +import configureStore from 'redux-mock-store'; +import { Provider } from 'react-intl-redux'; + +import BlockAlignment from './BlockAlignment'; +import type { ButtonsWidgetProps } from './ButtonsWidget'; + +const mockStore = configureStore(); + +const renderWidget = (props: Partial = {}) => { + const { onChange: providedOnChange, ...restProps } = props; + const onChange = providedOnChange ?? vi.fn(); + const widgetProps: ButtonsWidgetProps = { + id: 'alignment', + title: 'Block alignment', + fieldSet: 'default', + onChange, + value: undefined, + default: undefined, + disabled: false, + isDisabled: false, + ...restProps, + }; + + return { + onChange, + ...render( + + + , + ), + }; +}; + +describe('BlockAlignment', () => { + it('renders default alignment buttons', () => { + renderWidget(); + + expect(screen.getByRole('radio', { name: 'Left' })).toBeInTheDocument(); + expect(screen.getByRole('radio', { name: 'Center' })).toBeInTheDocument(); + expect(screen.getByRole('radio', { name: 'Right' })).toBeInTheDocument(); + }); + + it('filters actions when filterActions is provided', () => { + renderWidget({ filterActions: ['center'] }); + + expect(screen.getByRole('radio', { name: 'Center' })).toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Left' }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Right' }), + ).not.toBeInTheDocument(); + }); + + it('merges custom actions info map entries', () => { + renderWidget({ + actions: ['left', 'custom'], + actionsInfoMap: { + custom: ['Custom', 'Custom alignment'], + }, + }); + + expect(screen.getByRole('radio', { name: 'Left' })).toBeInTheDocument(); + expect( + screen.getByRole('radio', { name: 'Custom alignment' }), + ).toBeInTheDocument(); + }); + + it('renders when style definitions are provided as actions', () => { + renderWidget({ + actions: [ + { + name: 'wide', + label: 'Wide', + style: { + '--block-alignment': 'var(--align-wide)', + }, + }, + ], + }); + + expect(screen.getByRole('radio', { name: 'wide' })).toBeInTheDocument(); + }); + + it('invokes onChange with styles when default actions are used', () => { + const { onChange } = renderWidget(); + + fireEvent.click(screen.getByRole('radio', { name: 'Center' })); + + expect(onChange).toHaveBeenCalledWith('alignment', { + '--block-alignment': 'var(--align-center)', + }); + }); +}); diff --git a/packages/volto/src/components/manage/Widgets/BlockAlignment.tsx b/packages/volto/src/components/manage/Widgets/BlockAlignment.tsx new file mode 100644 index 00000000000..e484c363919 --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/BlockAlignment.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import { defineMessages, useIntl } from 'react-intl'; +import ButtonsWidget, { + type ActionInfo, + type ButtonsWidgetProps, +} from './ButtonsWidget'; +import imageFitSVG from '@plone/volto/icons/image-fit.svg'; +import imageLeftSVG from '@plone/volto/icons/image-left.svg'; +import imageRightSVG from '@plone/volto/icons/image-right.svg'; +import type { IntlShape } from 'react-intl'; +import type { StyleDefinition } from '@plone/types'; + +const messages = defineMessages({ + left: { + id: 'Left', + defaultMessage: 'Left', + }, + right: { + id: 'Right', + defaultMessage: 'Right', + }, + center: { + id: 'Center', + defaultMessage: 'Center', + }, +}); + +export const defaultActionsInfo = ({ + intl, +}: { + intl: IntlShape; +}): Record => ({ + left: [imageLeftSVG, intl.formatMessage(messages.left)], + right: [imageRightSVG, intl.formatMessage(messages.right)], + center: [imageFitSVG, intl.formatMessage(messages.center)], +}); + +const DEFAULT_ACTIONS: StyleDefinition[] = [ + { + style: { + '--block-alignment': 'var(--align-left)', + }, + name: 'left', + label: 'Left', + }, + { + style: { + '--block-alignment': 'var(--align-center)', + }, + name: 'center', + label: 'Center', + }, + { + style: { + '--block-alignment': 'var(--align-right)', + }, + name: 'right', + label: 'Right', + }, +]; + +const BlockAlignmentWidget = (props: ButtonsWidgetProps) => { + const intl = useIntl(); + + const { actions = DEFAULT_ACTIONS, actionsInfoMap, filterActions } = props; + const filteredActions = + filterActions && filterActions.length > 0 + ? actions.filter((action) => { + const actionName = typeof action === 'string' ? action : action.name; + return filterActions.includes(actionName); + }) + : actions; + + const actionsInfo = { + ...defaultActionsInfo({ intl }), + ...actionsInfoMap, + }; + + return ( + + ); +}; + +export default BlockAlignmentWidget; diff --git a/packages/volto/src/components/manage/Widgets/BlockWidth.stories.tsx b/packages/volto/src/components/manage/Widgets/BlockWidth.stories.tsx new file mode 100644 index 00000000000..983b0eee0f6 --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/BlockWidth.stories.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import type { Meta, StoryFn, StoryObj } from '@storybook/react'; + +import BlockWidth from './BlockWidth'; +import type { ButtonsWidgetProps } from './ButtonsWidget'; +import { + RealStoreWrapper as Wrapper, + FormUndoWrapper, +} from '@plone/volto/storybook'; + +const meta: Meta = { + title: 'Edit Widgets/BlockWidth', + component: BlockWidth, + decorators: [ + (Story) => ( +
+

Block width widget

+ +
+ ), + ], +}; + +export default meta; + +type Story = StoryObj; + +type TemplateParameters = { + initialValue?: ButtonsWidgetProps['value']; +}; + +const Template: StoryFn = (args, context) => { + const { initialValue } = + (context.parameters as TemplateParameters | undefined) ?? {}; + const { value: _ignoredValue, onChange: argsOnChange, ...restArgs } = args; + + return ( + + + {({ state, onChange }) => ( +
+ { + argsOnChange?.(id, nextValue); + onChange({ value: nextValue }); + }} + /> +
Value: {JSON.stringify(state.value, null, 2)}
+
+ )} +
+
+ ); +}; + +export const DefaultWidth: Story = { + render: Template, + args: { + id: 'blockWidth', + title: 'Block width', + block: 'block', + fieldSet: 'default', + }, +}; diff --git a/packages/volto/src/components/manage/Widgets/BlockWidth.test.tsx b/packages/volto/src/components/manage/Widgets/BlockWidth.test.tsx new file mode 100644 index 00000000000..fe6e4c4c9c3 --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/BlockWidth.test.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { describe, it, expect, vi } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/react'; +import configureStore from 'redux-mock-store'; +import { Provider } from 'react-intl-redux'; + +import BlockWidth from './BlockWidth'; +import type { ButtonsWidgetProps } from './ButtonsWidget'; + +const mockStore = configureStore(); + +const renderWidget = (props: Partial = {}) => { + const onChange = props.onChange ?? vi.fn(); + const widgetProps: ButtonsWidgetProps = { + id: 'blockWidth', + title: 'Block width', + fieldSet: 'default', + onChange, + value: undefined, + default: undefined, + disabled: false, + isDisabled: false, + ...props, + }; + + return { + onChange, + ...render( + + + , + ), + }; +}; + +describe('BlockWidth', () => { + it('renders the default action buttons provided by the widget', () => { + renderWidget(); + + expect(screen.getByRole('radio', { name: 'Narrow' })).toBeInTheDocument(); + expect(screen.getByRole('radio', { name: 'Default' })).toBeInTheDocument(); + expect(screen.getByRole('radio', { name: 'Layout' })).toBeInTheDocument(); + expect(screen.getByRole('radio', { name: 'Full' })).toBeInTheDocument(); + }); + + it('emits the correct value for the default actions', () => { + const { onChange } = renderWidget(); + + fireEvent.click(screen.getByRole('radio', { name: 'Layout' })); + + expect(onChange).toHaveBeenCalledWith('blockWidth', { + '--block-width': 'var(--layout-container-width)', + }); + }); +}); diff --git a/packages/volto/src/components/manage/Widgets/BlockWidth.tsx b/packages/volto/src/components/manage/Widgets/BlockWidth.tsx new file mode 100644 index 00000000000..e3fb115ebda --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/BlockWidth.tsx @@ -0,0 +1,101 @@ +import React from 'react'; +import { defineMessages, useIntl } from 'react-intl'; +import type { IntlShape } from 'react-intl'; +import ButtonsWidget, { + type ActionInfo, + type ButtonsWidgetProps, +} from './ButtonsWidget'; +import imageFitSVG from '@plone/volto/icons/image-fit.svg'; +import imageNarrowSVG from '@plone/volto/icons/image-narrow.svg'; +import imageWideSVG from '@plone/volto/icons/image-wide.svg'; +import imageFullSVG from '@plone/volto/icons/image-full.svg'; +import type { StyleDefinition } from '@plone/types'; + +const messages = defineMessages({ + narrow: { + id: 'Narrow', + defaultMessage: 'Narrow', + }, + default: { + id: 'Default', + defaultMessage: 'Default', + }, + layout: { + id: 'Layout', + defaultMessage: 'Layout', + }, + full: { + id: 'Full', + defaultMessage: 'Full', + }, +}); + +export const defaultActionsInfo = ({ + intl, +}: { + intl: IntlShape; +}): Record => ({ + narrow: [imageNarrowSVG, intl.formatMessage(messages.narrow)], + default: [imageFitSVG, intl.formatMessage(messages.default)], + layout: [imageWideSVG, intl.formatMessage(messages.layout)], + full: [imageFullSVG, intl.formatMessage(messages.full)], +}); + +const DEFAULT_ACTIONS: StyleDefinition[] = [ + { + style: { + '--block-width': 'var(--narrow-container-width)', + }, + name: 'narrow', + label: 'Narrow', + }, + { + style: { + '--block-width': 'var(--default-container-width)', + }, + name: 'default', + label: 'Default', + }, + { + style: { + '--block-width': 'var(--layout-container-width)', + }, + name: 'layout', + label: 'Layout', + }, + { + style: { + '--block-width': 'unset', + }, + name: 'full', + label: 'Full', + }, +]; + +const BlockWidthWidget = (props: ButtonsWidgetProps) => { + const intl = useIntl(); + + const { actions = DEFAULT_ACTIONS, actionsInfoMap, filterActions } = props; + const filteredActions = + filterActions && filterActions.length > 0 + ? actions.filter((action) => { + const actionName = typeof action === 'string' ? action : action.name; + return filterActions.includes(actionName); + }) + : actions; + + const actionsInfo = { + ...defaultActionsInfo({ intl }), + ...actionsInfoMap, + }; + + return ( + + ); +}; + +export default BlockWidthWidget; diff --git a/packages/volto/src/components/manage/Widgets/ButtonsWidget.jsx b/packages/volto/src/components/manage/Widgets/ButtonsWidget.jsx deleted file mode 100644 index 32c54a3287e..00000000000 --- a/packages/volto/src/components/manage/Widgets/ButtonsWidget.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper'; -import Icon from '@plone/volto/components/theme/Icon/Icon'; -import { Button } from 'semantic-ui-react'; - -// The ButtonsWidget -const ButtonsWidget = (props) => { - const { id, onChange, actions, actionsInfoMap, defaultAction, value } = props; - - React.useEffect(() => { - if (!props.value && props.default) { - props.onChange(props.id, props.default); - } - }); - - return ( - -
- {actions.map((action) => ( - - - - ))} -
-
- ); -}; - -export default ButtonsWidget; diff --git a/packages/volto/src/components/manage/Widgets/ButtonsWidget.stories.jsx b/packages/volto/src/components/manage/Widgets/ButtonsWidget.stories.jsx index 97a455428fb..a7e61876709 100644 --- a/packages/volto/src/components/manage/Widgets/ButtonsWidget.stories.jsx +++ b/packages/volto/src/components/manage/Widgets/ButtonsWidget.stories.jsx @@ -21,6 +21,67 @@ export const Buttons = WidgetStory.bind({ widget: ButtonsWidget, }); +export const ButtonsWithDefaultValue = WidgetStory.bind({ + props: { + id: 'buttons-default', + title: 'Buttons with Default Value', + default: 'centered', + actions: ['left', 'right', 'centered'], + actionsInfoMap: { + left: [textLeftSVG, 'Text Left'], + right: [textRightSVG, 'Text Right'], + centered: [textCenteredSVG, 'Text Centered'], + }, + }, + widget: ButtonsWidget, +}); + +export const ButtonsWithStyleDefinitions = WidgetStory.bind({ + props: { + id: 'buttons-style-definitions', + title: 'Buttons with Style Definitions', + actions: [ + { + name: 'small', + label: 'Small', + style: { '--button-size': 'small' }, + }, + { + name: 'medium', + label: 'Medium', + style: { '--button-size': 'medium' }, + }, + { + name: 'large', + label: 'Large', + style: { '--button-size': 'large' }, + }, + ], + actionsInfoMap: { + small: ['S', 'Small Buttons'], + medium: ['M', 'Medium Buttons'], + large: ['L', 'Large Buttons'], + }, + }, + widget: ButtonsWidget, +}); + +export const ButtonsFiltered = WidgetStory.bind({ + props: { + id: 'buttons-filtered', + title: 'Filtered Buttons', + actions: ['left', 'right', 'centered', 'justified'], + filterActions: ['centered', 'justified'], + actionsInfoMap: { + left: [textLeftSVG, 'Text Left'], + right: [textRightSVG, 'Text Right'], + justified: [textJustifiedSVG, 'Text Justified'], + centered: [textCenteredSVG, 'Text Centered'], + }, + }, + widget: ButtonsWidget, +}); + export default { title: 'Edit Widgets/Buttons', component: ButtonsWidget, diff --git a/packages/volto/src/components/manage/Widgets/ButtonsWidget.test.jsx b/packages/volto/src/components/manage/Widgets/ButtonsWidget.test.jsx deleted file mode 100644 index 824f1661d9e..00000000000 --- a/packages/volto/src/components/manage/Widgets/ButtonsWidget.test.jsx +++ /dev/null @@ -1,70 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import configureStore from 'redux-mock-store'; -import { Provider } from 'react-intl-redux'; -import imageFullSVG from '@plone/volto/icons/image-full.svg'; -import textJustifiedSVG from '@plone/volto/icons/align-justify.svg'; -import textCenteredSVG from '@plone/volto/icons/align-center.svg'; -import textLeftSVG from '@plone/volto/icons/align-left.svg'; -import textRightSVG from '@plone/volto/icons/align-right.svg'; - -import ButtonsWidget from './ButtonsWidget'; - -const mockStore = configureStore(); - -describe('renders an align widget component', () => { - it('basic', () => { - const store = mockStore({ - intl: { - locale: 'en', - messages: {}, - }, - }); - - const { asFragment } = render( - - {}} - actions={['left', 'right', 'centered', 'justified']} - actionsInfoMap={{ - left: [textLeftSVG, 'Text Left'], - right: [textRightSVG, 'Text Right'], - justified: [textJustifiedSVG, 'Text Justified'], - centered: [textCenteredSVG, 'Text Centered'], - }} - /> - , - ); - - expect(asFragment()).toMatchSnapshot(); - }); - - it('extended with actions and actionsInfoMap props', () => { - const store = mockStore({ - intl: { - locale: 'en', - messages: {}, - }, - }); - - const { asFragment } = render( - - {}} - actions={['additional']} - actionsInfoMap={{ - additional: [imageFullSVG, 'Additional action title'], - }} - /> - , - ); - - expect(asFragment()).toMatchSnapshot(); - }); -}); diff --git a/packages/volto/src/components/manage/Widgets/ButtonsWidget.test.tsx b/packages/volto/src/components/manage/Widgets/ButtonsWidget.test.tsx new file mode 100644 index 00000000000..00c99d33bb5 --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/ButtonsWidget.test.tsx @@ -0,0 +1,138 @@ +import React from 'react'; +import { describe, it, expect, vi } from 'vitest'; +import { render, fireEvent, screen } from '@testing-library/react'; +import configureStore from 'redux-mock-store'; +import { Provider } from 'react-intl-redux'; +import imageFullSVG from '@plone/volto/icons/image-full.svg'; +import textJustifiedSVG from '@plone/volto/icons/align-justify.svg'; +import textCenteredSVG from '@plone/volto/icons/align-center.svg'; +import textLeftSVG from '@plone/volto/icons/align-left.svg'; +import textRightSVG from '@plone/volto/icons/align-right.svg'; + +import ButtonsWidget from './ButtonsWidget'; + +const mockStore = configureStore(); + +const renderWidget = (ui: React.ReactElement) => + render( + + {ui} + , + ); + +describe('ButtonsWidget', () => { + it('renders string-based actions', () => { + const { asFragment } = renderWidget( + {}} + actions={['left', 'right', 'centered', 'justified']} + actionsInfoMap={{ + left: [textLeftSVG, 'Text Left'], + right: [textRightSVG, 'Text Right'], + justified: [textJustifiedSVG, 'Text Justified'], + centered: [textCenteredSVG, 'Text Centered'], + }} + />, + ); + + expect(asFragment()).toMatchSnapshot(); + }); + + it('renders actions info provided via props', () => { + const { asFragment } = renderWidget( + {}} + actions={['additional']} + actionsInfoMap={{ + additional: [imageFullSVG, 'Additional action title'], + }} + />, + ); + + expect(asFragment()).toMatchSnapshot(); + }); + + it('falls back to the action name when no info map entry is present', () => { + renderWidget( + {}} + actions={['missing']} + />, + ); + + expect(screen.getByRole('radio', { name: 'missing' })).toBeInTheDocument(); + }); + + it('normalizes style definitions when an action is pressed', () => { + const handleChange = vi.fn(); + + renderWidget( + , + ); + + fireEvent.click(screen.getByRole('radio', { name: 'Wide width' })); + + expect(handleChange).toHaveBeenCalledWith('align', { + '--layout-width': 'wide', + }); + }); + + it('selects default value from string names', () => { + renderWidget( + {}} + actions={[ + { + name: 'justified', + label: 'Justified', + style: { + '--layout-width': 'wide', + }, + }, + ]} + default="justified" + actionsInfoMap={{ + justified: [textJustifiedSVG, 'Text Justified'], + }} + />, + ); + + expect(screen.getByRole('radio', { name: 'Text Justified' })).toBeChecked(); + }); +}); diff --git a/packages/volto/src/components/manage/Widgets/ButtonsWidget.tsx b/packages/volto/src/components/manage/Widgets/ButtonsWidget.tsx new file mode 100644 index 00000000000..64e72b5220d --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/ButtonsWidget.tsx @@ -0,0 +1,175 @@ +import React from 'react'; +import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper'; +import Icon from '@plone/volto/components/theme/Icon/Icon'; +import { Radio, RadioGroup } from '@plone/components'; +import isEqual from 'lodash/isEqual'; +import type { StyleDefinition } from '@plone/types'; + +/** + * A tuple that has an icon in the first element and a i18n string in the second. + */ +export type ActionInfo = [React.ReactElement, string] | [string, string]; + +type ActionValue = string | Record<`--${string}`, string>; + +export type ButtonsWidgetProps = { + /** + * Unique identifier for the widget. + */ + id: string; + + /** + * Callback function to handle changes. + */ + onChange: (id: string, value: ActionValue) => void; + + /** + * List of actions available for the widget. + */ + actions?: Array; + + /** + * Map containing additional the information (icon and i18n string) for each action. + */ + actionsInfoMap?: Record; + + /** + * List of actions to be filtered out. In case that we don't want the default ones + * we can filter them out. + */ + filterActions?: string[]; + + /** + * Current value of the widget. + */ + value?: ActionValue; + + /** + * Default value of the widget. + */ + default?: ActionValue; + + /** + * Indicates if the widget is disabled. + */ + disabled?: boolean; + + /** + * Indicates if the widget is disabled (alternative flag for compatibility reasons). + */ + isDisabled?: boolean; + [key: string]: any; +}; + +type NormalizedAction = { + name: string; + value: ActionValue; +}; + +const ButtonsWidget = (props: ButtonsWidgetProps) => { + const { + disabled, + id, + onChange, + actions = [], + actionsInfoMap, + value, + isDisabled, + default: defaultValue, + } = props; + + const normalizedActions: NormalizedAction[] = actions.map((action) => + typeof action === 'string' + ? { name: action, value: action } + : { + name: action.name, + value: action.style ?? action.name, + }, + ); + + const selectedActionName = normalizedActions.find((action) => + isEqual(value, action.value), + )?.name; + + const defaultSelectedActionName = (() => { + if (!defaultValue) { + return undefined; + } + + if (typeof defaultValue === 'string') { + const matchedByName = normalizedActions.find( + ({ name }) => name === defaultValue, + ); + + if (matchedByName) { + return matchedByName.name; + } + } + + return normalizedActions.find(({ value: actionValue }) => + isEqual(defaultValue, actionValue), + )?.name; + })(); + + const radioGroupValueProps: { + value?: string; + defaultValue?: string; + } = selectedActionName + ? { value: selectedActionName } + : defaultSelectedActionName + ? { defaultValue: defaultSelectedActionName } + : {}; + + const handleChange = (selectedName: string) => { + const selectedAction = normalizedActions.find( + ({ name }) => name === selectedName, + ); + + if (selectedAction) { + onChange(id, selectedAction.value); + } + }; + + return ( + + + {normalizedActions.map((action) => { + const actionInfo = actionsInfoMap?.[action.name]; + const [iconOrText, ariaLabel] = actionInfo ?? [ + action.name, + action.name, + ]; + + return ( + + {typeof iconOrText === 'string' ? ( +
{iconOrText}
+ ) : ( + + )} +
+ ); + })} +
+
+ ); +}; + +export default ButtonsWidget; diff --git a/packages/volto/src/components/manage/Widgets/Size.stories.tsx b/packages/volto/src/components/manage/Widgets/Size.stories.tsx new file mode 100644 index 00000000000..53da0be5170 --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/Size.stories.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import type { Meta, StoryFn, StoryObj } from '@storybook/react'; + +import Size from './Size'; +import type { ButtonsWidgetProps } from './ButtonsWidget'; +import { + RealStoreWrapper as Wrapper, + FormUndoWrapper, +} from '@plone/volto/storybook'; + +const meta: Meta = { + title: 'Edit Widgets/Size', + component: Size, + decorators: [ + (Story) => ( +
+

Size widget

+ +
+ ), + ], +}; + +export default meta; + +type Story = StoryObj; + +type TemplateParameters = { + initialValue?: ButtonsWidgetProps['value']; +}; + +const Template: StoryFn = (args, context) => { + const { initialValue } = + (context.parameters as TemplateParameters | undefined) ?? {}; + const { value: _ignoredValue, onChange: argsOnChange, ...restArgs } = args; + + return ( + + + {({ state, onChange }) => ( +
+ { + argsOnChange?.(id, nextValue); + onChange({ value: nextValue }); + }} + /> +
Value: {JSON.stringify(state.value, null, 2)}
+
+ )} +
+
+ ); +}; + +export const DefaultSize: Story = { + render: Template, + args: { + id: 'size', + title: 'Size', + block: 'block', + fieldSet: 'default', + }, +}; diff --git a/packages/volto/src/components/manage/Widgets/Size.test.tsx b/packages/volto/src/components/manage/Widgets/Size.test.tsx new file mode 100644 index 00000000000..4c7eb18b085 --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/Size.test.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { describe, it, expect, vi } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/react'; +import configureStore from 'redux-mock-store'; +import { Provider } from 'react-intl-redux'; + +import Size from './Size'; +import type { ButtonsWidgetProps } from './ButtonsWidget'; + +const mockStore = configureStore(); + +const renderWidget = (props: Partial = {}) => { + const onChange = props.onChange ?? vi.fn(); + const widgetProps: ButtonsWidgetProps = { + id: 'size', + title: 'Size', + fieldSet: 'default', + onChange, + value: undefined, + default: undefined, + disabled: false, + isDisabled: false, + ...props, + }; + + return { + onChange, + ...render( + + + , + ), + }; +}; + +describe('Size widget', () => { + it('renders the default size options', () => { + renderWidget(); + + expect(screen.getByRole('radio', { name: 'Small' })).toBeInTheDocument(); + expect(screen.getByRole('radio', { name: 'Medium' })).toBeInTheDocument(); + expect(screen.getByRole('radio', { name: 'Large' })).toBeInTheDocument(); + }); + + it('returns the selected size name', () => { + const { onChange } = renderWidget(); + + fireEvent.click(screen.getByRole('radio', { name: 'Medium' })); + + expect(onChange).toHaveBeenCalledWith('size', 'm'); + }); +}); diff --git a/packages/volto/src/components/manage/Widgets/Size.tsx b/packages/volto/src/components/manage/Widgets/Size.tsx new file mode 100644 index 00000000000..9e44ebcbfe4 --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/Size.tsx @@ -0,0 +1,78 @@ +import { defineMessages, useIntl } from 'react-intl'; +import ButtonsWidget, { + type ActionInfo, + type ButtonsWidgetProps, +} from './ButtonsWidget'; +import type { IntlShape } from 'react-intl'; +import type { StyleDefinition } from '@plone/types'; + +const messages = defineMessages({ + s: { + id: 'Small', + defaultMessage: 'Small', + }, + m: { + id: 'Medium', + defaultMessage: 'Medium', + }, + l: { + id: 'Large', + defaultMessage: 'Large', + }, +}); + +export const defaultActionsInfo = ({ + intl, +}: { + intl: IntlShape; +}): Record => ({ + s: ['S', intl.formatMessage(messages.s)], + m: ['M', intl.formatMessage(messages.m)], + l: ['L', intl.formatMessage(messages.l)], +}); + +const DEFAULT_ACTIONS: StyleDefinition[] = [ + { + name: 's', + label: 'Small', + style: undefined, + }, + { + name: 'm', + label: 'Medium', + style: undefined, + }, + { + name: 'l', + label: 'Large', + style: undefined, + }, +]; + +const SizeWidget = (props: ButtonsWidgetProps) => { + const intl = useIntl(); + + const { actions = DEFAULT_ACTIONS, actionsInfoMap, filterActions } = props; + const filteredActions = + filterActions && filterActions.length > 0 + ? actions.filter((action) => { + const actionName = typeof action === 'string' ? action : action.name; + return filterActions.includes(actionName); + }) + : actions; + + const actionsInfo = { + ...defaultActionsInfo({ intl }), + ...actionsInfoMap, + }; + + return ( + + ); +}; + +export default SizeWidget; diff --git a/packages/volto/src/components/manage/Widgets/__snapshots__/AlignWidget.test.jsx.snap b/packages/volto/src/components/manage/Widgets/__snapshots__/AlignWidget.test.jsx.snap deleted file mode 100644 index 780b5333b8a..00000000000 --- a/packages/volto/src/components/manage/Widgets/__snapshots__/AlignWidget.test.jsx.snap +++ /dev/null @@ -1,235 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`renders an align widget component > basic 1`] = ` - -
-
-
-
-
- -
-
-
-
-
- -
-
- -
-
- -
-
- -
-
-
-
-
-
-
-`; - -exports[`renders an align widget component > extended with actions and actionsInfoMap props 1`] = ` - -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
-
-
-`; diff --git a/packages/volto/src/components/manage/Widgets/__snapshots__/AlignWidget.test.tsx.snap b/packages/volto/src/components/manage/Widgets/__snapshots__/AlignWidget.test.tsx.snap new file mode 100644 index 00000000000..48e4be2b439 --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/__snapshots__/AlignWidget.test.tsx.snap @@ -0,0 +1,314 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`AlignWidget > renders with custom actions 1`] = ` + +
+
+
+
+
+ +
+
+
+
+ + +
+
+
+
+
+
+`; + +exports[`AlignWidget > renders with default actions 1`] = ` + +
+
+
+
+
+ +
+
+
+
+ + + + + +
+
+
+
+
+
+`; diff --git a/packages/volto/src/components/manage/Widgets/__snapshots__/ButtonsWidget.test.jsx.snap b/packages/volto/src/components/manage/Widgets/__snapshots__/ButtonsWidget.test.jsx.snap index 6fd6910d117..9f2398dde87 100644 --- a/packages/volto/src/components/manage/Widgets/__snapshots__/ButtonsWidget.test.jsx.snap +++ b/packages/volto/src/components/manage/Widgets/__snapshots__/ButtonsWidget.test.jsx.snap @@ -1,9 +1,226 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`ButtonsWidget > renders actions info provided via props 1`] = ` + +
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+`; + +exports[`ButtonsWidget > renders string-based actions 1`] = ` + +
+
+
+
+
+ +
+
+
+
+ + + + +
+
+
+
+
+
+`; + exports[`renders an align widget component > basic 1`] = `
basic 1`] = ` class="eight wide column" >
-
- -
-
+ + + + -
-
+ + + + -
-
+ + + + -
+ + + +
@@ -146,7 +367,7 @@ exports[`renders an align widget component > basic 1`] = ` exports[`renders an align widget component > extended with actions and actionsInfoMap props 1`] = `
extended with actions and actionsIn class="eight wide column" >
-
- -
+ + + + +
diff --git a/packages/volto/src/components/manage/Widgets/__snapshots__/ButtonsWidget.test.tsx.snap b/packages/volto/src/components/manage/Widgets/__snapshots__/ButtonsWidget.test.tsx.snap new file mode 100644 index 00000000000..83f2b83825f --- /dev/null +++ b/packages/volto/src/components/manage/Widgets/__snapshots__/ButtonsWidget.test.tsx.snap @@ -0,0 +1,290 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`ButtonsWidget > renders actions info provided via props 1`] = ` + +
+
+
+
+
+ +
+
+
+
+ + +
+
+
+
+
+
+`; + +exports[`ButtonsWidget > renders string-based actions 1`] = ` + +
+
+
+
+
+ +
+
+
+
+ + + + + +
+
+
+
+
+
+`; diff --git a/packages/volto/src/components/manage/Widgets/index.tsx b/packages/volto/src/components/manage/Widgets/index.tsx index 230d3e0b508..9d8e4b4b37a 100644 --- a/packages/volto/src/components/manage/Widgets/index.tsx +++ b/packages/volto/src/components/manage/Widgets/index.tsx @@ -265,3 +265,24 @@ export const FormFieldWrapper = loadable( /* webpackChunkName: "Widgets" */ '@plone/volto/components/manage/Widgets/FormFieldWrapper' ), ); + +export const Size = loadable( + () => + import( + /* webpackChunkName: "Widgets" */ '@plone/volto/components/manage/Widgets/Size' + ), +); + +export const BlockAlignment = loadable( + () => + import( + /* webpackChunkName: "Widgets" */ '@plone/volto/components/manage/Widgets/BlockAlignment' + ), +); + +export const BlockWidth = loadable( + () => + import( + /* webpackChunkName: "Widgets" */ '@plone/volto/components/manage/Widgets/BlockWidth' + ), +); diff --git a/packages/volto/src/components/theme/App/App.jsx b/packages/volto/src/components/theme/App/App.jsx index 7c8b8ecb316..d0b7c5125a3 100644 --- a/packages/volto/src/components/theme/App/App.jsx +++ b/packages/volto/src/components/theme/App/App.jsx @@ -45,6 +45,7 @@ import MultilingualRedirector from '@plone/volto/components/theme/MultilingualRe import WorkingCopyToastsFactory from '@plone/volto/components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory'; import LockingToastsFactory from '@plone/volto/components/manage/LockingToastsFactory/LockingToastsFactory'; import RouteAnnouncer from '@plone/volto/components/theme/RouteAnnouncer/RouteAnnouncer'; +import SlotRenderer from '@plone/volto/components/theme/SlotRenderer/SlotRenderer'; /** * @export @@ -159,6 +160,7 @@ export class App extends Component { 'public-ui': !isCmsUI, })} /> +
diff --git a/packages/volto/src/components/theme/InjectPloneComponentsCSS/InjectPloneComponentsCSS.tsx b/packages/volto/src/components/theme/InjectPloneComponentsCSS/InjectPloneComponentsCSS.tsx new file mode 100644 index 00000000000..8e5761e99d2 --- /dev/null +++ b/packages/volto/src/components/theme/InjectPloneComponentsCSS/InjectPloneComponentsCSS.tsx @@ -0,0 +1,7 @@ +import '@plone/components/dist/basic.css'; + +const InjectPloneComponentsCSS = () => { + return null; +}; + +export default InjectPloneComponentsCSS; diff --git a/packages/volto/src/config/Widgets.jsx b/packages/volto/src/config/Widgets.jsx index d20d671f265..2b797f211de 100644 --- a/packages/volto/src/config/Widgets.jsx +++ b/packages/volto/src/config/Widgets.jsx @@ -35,6 +35,9 @@ import { RecurrenceWidget, RadioGroupWidget, CheckboxGroupWidget, + Size, + BlockAlignment, + BlockWidth, } from '@plone/volto/components/manage/Widgets'; import ArrayViewWidget from '@plone/volto/components/theme/Widgets/ArrayWidget'; @@ -102,6 +105,10 @@ export const widgetMapping = { hidden: HiddenWidget, radio_group: RadioGroupWidget, checkbox_group: CheckboxGroupWidget, + // SemanticUI Free widgets + blockAlignment: BlockAlignment, + blockWidth: BlockWidth, + size: Size, }, vocabulary: { 'plone.app.vocabularies.Catalog': ObjectBrowserWidget, diff --git a/packages/volto/src/config/slots.js b/packages/volto/src/config/slots.js index 265b3573ce0..b40ab60a725 100644 --- a/packages/volto/src/config/slots.js +++ b/packages/volto/src/config/slots.js @@ -1,7 +1,26 @@ +import loadable from '@loadable/component'; import RelatedItems from '@plone/volto/components/theme/RelatedItems/RelatedItems'; import Tags from '@plone/volto/components/theme/Tags/Tags'; +import { isCmsUi } from '@plone/volto/helpers/Url/Url'; + +const InjectPloneComponentsCSS = loadable( + () => + import( + '@plone/volto/components/theme/InjectPloneComponentsCSS/InjectPloneComponentsCSS' + ), + { fallback: null }, +); + +const onlyCMSUI = ({ location }) => isCmsUi(location?.pathname); const slots = { + aboveApp: [ + { + name: 'plone-components-css', + component: InjectPloneComponentsCSS, + predicates: [onlyCMSUI], + }, + ], belowContent: [ { name: 'tags', diff --git a/packages/volto/theme/themes/pastanaga/extras/widgets.less b/packages/volto/theme/themes/pastanaga/extras/widgets.less index 586e4df775d..9158fa935ec 100644 --- a/packages/volto/theme/themes/pastanaga/extras/widgets.less +++ b/packages/volto/theme/themes/pastanaga/extras/widgets.less @@ -292,3 +292,48 @@ body.babel-view .field.language-independent-field { margin-left: 8px; } } + +// ButtonsWidget and friends 2025 +:root { + --border-color-hover: var(--quanta-sapphire); + --border-color-pressed: var(--quanta-sapphire); + --border-color-disabled: #d4d4d5; + --text-color-disabled: #a8a8a8; +} + +.field.widget { + .buttons-widget { + display: flex; + align-items: center; + gap: 4px; + + .buttons-widget-option { + padding: 5px; + border-radius: 3px; + aspect-ratio: 1/1; + font-size: 1em; + line-height: initial; + + &[data-hovered='true'] { + box-shadow: inset 0 0 0 1px var(--border-color-hover, #2996da); + } + + &[data-selected='true'] { + box-shadow: inset 0 0 0 1px var(--border-color-pressed, #2996da); + } + + svg.icon { + margin: 0 !important; + opacity: 0.9; + vertical-align: top; + } + } + } +} + +.buttons-widget-option { + &[data-disabled='true'] { + border-color: var(--border-color-disabled); + color: var(--text-color-disabled); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 42f6ec9dc56..4bbc36124c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -851,6 +851,9 @@ importers: '@loadable/server': specifier: 5.14.0 version: 5.14.0(@loadable/component@5.14.1(react@18.2.0))(react@18.2.0) + '@plone/components': + specifier: workspace:* + version: link:../components '@plone/registry': specifier: workspace:* version: link:../registry