From 0a7001ae6912ca36069549090c3ccb534b8d8dec Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Sun, 8 Dec 2024 13:02:51 +0100 Subject: [PATCH 1/7] Kicking off Plone 7 PLIPs directives --- PLIPs.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 PLIPs.md diff --git a/PLIPs.md b/PLIPs.md new file mode 100644 index 0000000000..44fae5765d --- /dev/null +++ b/PLIPs.md @@ -0,0 +1,24 @@ +# Useful Information while developing PLIPs + +## 6321 - SemanticUI free CMSUI + +Link to PLIP: [SemanticUI free CMSUI](https://github.com/plone/volto/issues/6321) + +### Directives + +This PLIP is targeted to Plone 7. +The main goal is to get rid of SemanticUI powered components in the CMSUI. +It is expected to be a long lived branch, and we probably will want some of the changes already available for the API-first story, let's make some assumptions and rules. + +All SemanticUI basic, fundamental components should be replaced by their counterparts in `@plone/components`. +Every time that we find that this components are missing, we should create them in `@plone/components` then used in Volto components. +These components will follow the `@plone/components` best practices, written in TypeScript, have accessibility tests and Storybook story. +We'd like to have these new components out of this PR and used right away, so let's make all the commits around them as atomic as possible so we can cherry pick them safely and push for them in the main branch. + +Every new component used in the CMSUI will be transfered from Volto core to a package: `@plone/cmsui`. +By doing so, we will keep track of every new "clean" component and potentially, being able to extract them when we want, and use them in "real" life right away. +It will help also to avoid merging hell. +When this PLIP is done, the `src/components/manage` folder should be empty (or almost empty). +Code in Volto will remain references and imports to the `@plone/cmsui` package. + +Once we start, only meaningful changes in `main` in CMSUI components will be backported, in order to again, avoid merging hell. From 9f0c532914d041f5b3dc86045847034693d798f0 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Sun, 8 Dec 2024 13:04:21 +0100 Subject: [PATCH 2/7] Better name for the PLIP --- PLIPs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PLIPs.md b/PLIPs.md index 44fae5765d..d5728297a1 100644 --- a/PLIPs.md +++ b/PLIPs.md @@ -1,8 +1,8 @@ # Useful Information while developing PLIPs -## 6321 - SemanticUI free CMSUI +## 6321 - Remove Semantic UI from CMSUI -Link to PLIP: [SemanticUI free CMSUI](https://github.com/plone/volto/issues/6321) +Link to PLIP: [Remove Semantic UI from CMSUI](https://github.com/plone/volto/issues/6321) ### Directives From e465a2e573e3b336d46ca5b39425afebe26c56e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Sun, 8 Dec 2024 15:04:34 +0100 Subject: [PATCH 3/7] Apply suggestions from code review Co-authored-by: Steve Piercy --- PLIPs.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/PLIPs.md b/PLIPs.md index d5728297a1..9ffa9935a2 100644 --- a/PLIPs.md +++ b/PLIPs.md @@ -1,4 +1,11 @@ -# Useful Information while developing PLIPs +# Plone Improvement Proposals (PLIPs) + +For details of the PLIP process, read the following. + +- [Plone Improvement Proposals (PLIPs)](https://6.docs.plone.org/contributing/core/plips.html) +- [PLIP review](https://6.docs.plone.org/contributing/core/plip-review.html) + +You can also [browse the current list of open PLIPs for Volto](https://github.com/plone/volto/labels/03%20type%3A%20feature%20(plip)). ## 6321 - Remove Semantic UI from CMSUI @@ -7,18 +14,18 @@ Link to PLIP: [Remove Semantic UI from CMSUI](https://github.com/plone/volto/iss ### Directives This PLIP is targeted to Plone 7. -The main goal is to get rid of SemanticUI powered components in the CMSUI. +The main goal is to get rid of Semantic UI powered components in the CMSUI. It is expected to be a long lived branch, and we probably will want some of the changes already available for the API-first story, let's make some assumptions and rules. -All SemanticUI basic, fundamental components should be replaced by their counterparts in `@plone/components`. -Every time that we find that this components are missing, we should create them in `@plone/components` then used in Volto components. -These components will follow the `@plone/components` best practices, written in TypeScript, have accessibility tests and Storybook story. +All Semantic UI basic, fundamental components should be replaced by their counterparts in `@plone/components`. +Every time that we find missing components, we should create them in `@plone/components` and use them in Volto components. +These components will follow the `@plone/components` best practices, including being written in TypeScript, have accessibility tests, and have a Storybook story. We'd like to have these new components out of this PR and used right away, so let's make all the commits around them as atomic as possible so we can cherry pick them safely and push for them in the main branch. -Every new component used in the CMSUI will be transfered from Volto core to a package: `@plone/cmsui`. -By doing so, we will keep track of every new "clean" component and potentially, being able to extract them when we want, and use them in "real" life right away. +Every new component used in the CMSUI will be transfered from Volto core to the package `@plone/cmsui`. +By doing so, we will keep track of every new "clean" component, and potentially—being able to extract them when we want—we can use them in "real" life right away. It will help also to avoid merging hell. When this PLIP is done, the `src/components/manage` folder should be empty (or almost empty). Code in Volto will remain references and imports to the `@plone/cmsui` package. -Once we start, only meaningful changes in `main` in CMSUI components will be backported, in order to again, avoid merging hell. +Once we start, only meaningful changes in `main` in CMSUI components will be backported, again to avoid merging hell. From 3aac22bc82979d644c4e1df28be86faa2bdf8f4b Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Thu, 12 Dec 2024 19:34:23 +0100 Subject: [PATCH 4/7] Move relevant info for this PLIP to #6321 --- PLIPs.md | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/PLIPs.md b/PLIPs.md index 9ffa9935a2..669bdccfb1 100644 --- a/PLIPs.md +++ b/PLIPs.md @@ -6,26 +6,3 @@ For details of the PLIP process, read the following. - [PLIP review](https://6.docs.plone.org/contributing/core/plip-review.html) You can also [browse the current list of open PLIPs for Volto](https://github.com/plone/volto/labels/03%20type%3A%20feature%20(plip)). - -## 6321 - Remove Semantic UI from CMSUI - -Link to PLIP: [Remove Semantic UI from CMSUI](https://github.com/plone/volto/issues/6321) - -### Directives - -This PLIP is targeted to Plone 7. -The main goal is to get rid of Semantic UI powered components in the CMSUI. -It is expected to be a long lived branch, and we probably will want some of the changes already available for the API-first story, let's make some assumptions and rules. - -All Semantic UI basic, fundamental components should be replaced by their counterparts in `@plone/components`. -Every time that we find missing components, we should create them in `@plone/components` and use them in Volto components. -These components will follow the `@plone/components` best practices, including being written in TypeScript, have accessibility tests, and have a Storybook story. -We'd like to have these new components out of this PR and used right away, so let's make all the commits around them as atomic as possible so we can cherry pick them safely and push for them in the main branch. - -Every new component used in the CMSUI will be transfered from Volto core to the package `@plone/cmsui`. -By doing so, we will keep track of every new "clean" component, and potentially—being able to extract them when we want—we can use them in "real" life right away. -It will help also to avoid merging hell. -When this PLIP is done, the `src/components/manage` folder should be empty (or almost empty). -Code in Volto will remain references and imports to the `@plone/cmsui` package. - -Once we start, only meaningful changes in `main` in CMSUI components will be backported, again to avoid merging hell. From ae84d28f9364283c41e3664a9e307acd1300d07c Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Thu, 12 Dec 2024 21:06:45 +0100 Subject: [PATCH 5/7] Init @plone/cmsui --- packages/cmsui/.eslintrc.cjs | 11 +++ packages/cmsui/.release-it.json | 29 +++++++ packages/cmsui/.storybook/Logo.svg | 21 ++++++ packages/cmsui/.storybook/main.ts | 41 ++++++++++ packages/cmsui/.storybook/manager.js | 6 ++ packages/cmsui/.storybook/preview-head.html | 3 + packages/cmsui/.storybook/preview.ts | 24 ++++++ packages/cmsui/.storybook/storybook-base.css | 19 +++++ packages/cmsui/.storybook/theme.ts | 10 +++ packages/cmsui/.stylelintrc | 14 ++++ packages/cmsui/CHANGELOG.md | 11 +++ packages/cmsui/README.md | 8 ++ packages/cmsui/index.ts | 5 ++ packages/cmsui/main.css | 0 packages/cmsui/news/.gitkeep | 0 packages/cmsui/package.json | 79 ++++++++++++++++++++ packages/cmsui/setupTesting.ts | 3 + packages/cmsui/stories.ts | 22 ++++++ packages/cmsui/towncrier.toml | 33 ++++++++ packages/cmsui/tsconfig.json | 13 ++++ packages/cmsui/vitest.config.ts | 14 ++++ 21 files changed, 366 insertions(+) create mode 100644 packages/cmsui/.eslintrc.cjs create mode 100644 packages/cmsui/.release-it.json create mode 100644 packages/cmsui/.storybook/Logo.svg create mode 100644 packages/cmsui/.storybook/main.ts create mode 100644 packages/cmsui/.storybook/manager.js create mode 100644 packages/cmsui/.storybook/preview-head.html create mode 100644 packages/cmsui/.storybook/preview.ts create mode 100644 packages/cmsui/.storybook/storybook-base.css create mode 100644 packages/cmsui/.storybook/theme.ts create mode 100644 packages/cmsui/.stylelintrc create mode 100644 packages/cmsui/CHANGELOG.md create mode 100644 packages/cmsui/README.md create mode 100644 packages/cmsui/index.ts create mode 100644 packages/cmsui/main.css create mode 100644 packages/cmsui/news/.gitkeep create mode 100644 packages/cmsui/package.json create mode 100644 packages/cmsui/setupTesting.ts create mode 100644 packages/cmsui/stories.ts create mode 100644 packages/cmsui/towncrier.toml create mode 100644 packages/cmsui/tsconfig.json create mode 100644 packages/cmsui/vitest.config.ts diff --git a/packages/cmsui/.eslintrc.cjs b/packages/cmsui/.eslintrc.cjs new file mode 100644 index 0000000000..b3a71fc44c --- /dev/null +++ b/packages/cmsui/.eslintrc.cjs @@ -0,0 +1,11 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + overrides: [ + { + files: ['**/*.ts', '**/*.tsx'], + extends: [ + 'plugin:react/jsx-runtime', // We only want this for non-library code (eg. volto add-ons) + ], + }, + ], +}; diff --git a/packages/cmsui/.release-it.json b/packages/cmsui/.release-it.json new file mode 100644 index 0000000000..507f953edc --- /dev/null +++ b/packages/cmsui/.release-it.json @@ -0,0 +1,29 @@ +{ + "plugins": { + "../scripts/prepublish.js": {} + }, + "hooks": { + "after:bump": [ + "pipx run towncrier build --draft --yes --version ${version} > .changelog.draft", + "pipx run towncrier build --yes --version ${version}" + ], + "after:release": "rm .changelog.draft" + }, + "npm": { + "publish": false + }, + "git": { + "commitArgs": ["--no-verify"], + "changelog": "pipx run towncrier build --draft --yes --version 0.0.0", + "requireUpstream": false, + "requireCleanWorkingDir": false, + "commitMessage": "Release @plone/cmsui ${version}", + "tagName": "plone-cmsui-${version}", + "tagAnnotation": "Release @plone/cmsui ${version}" + }, + "github": { + "release": true, + "releaseName": "@plone/cmsui ${version}", + "releaseNotes": "cat .changelog.draft" + } +} diff --git a/packages/cmsui/.storybook/Logo.svg b/packages/cmsui/.storybook/Logo.svg new file mode 100644 index 0000000000..5a7ba56902 --- /dev/null +++ b/packages/cmsui/.storybook/Logo.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/cmsui/.storybook/main.ts b/packages/cmsui/.storybook/main.ts new file mode 100644 index 0000000000..cf9f4ddec3 --- /dev/null +++ b/packages/cmsui/.storybook/main.ts @@ -0,0 +1,41 @@ +import type { StorybookConfig } from '@storybook/react-vite'; +import { mergeConfig } from 'vite'; + +const config: StorybookConfig = { + // For some reason the property does not allow negation + // https://github.com/storybookjs/storybook/issues/11181#issuecomment-1535288804 + stories: [ + '../components/**/*.mdx', + '../components/**/*.stories.@(js|jsx|ts|tsx)', + ], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-interactions', + ], + framework: { + name: '@storybook/react-vite', + options: {}, + }, + docs: { + autodocs: 'tag', + }, + typescript: { + reactDocgen: 'react-docgen-typescript', + reactDocgenTypescriptOptions: { + compilerOptions: { + allowSyntheticDefaultImports: false, + esModuleInterop: false, + }, + propFilter: () => true, + }, + }, + async viteFinal(config) { + return mergeConfig(config, { + build: { + minify: false, + }, + }); + }, +}; +export default config; diff --git a/packages/cmsui/.storybook/manager.js b/packages/cmsui/.storybook/manager.js new file mode 100644 index 0000000000..2e62084432 --- /dev/null +++ b/packages/cmsui/.storybook/manager.js @@ -0,0 +1,6 @@ +import { addons } from '@storybook/manager-api'; +import theme from './theme'; + +addons.setConfig({ + theme, +}); diff --git a/packages/cmsui/.storybook/preview-head.html b/packages/cmsui/.storybook/preview-head.html new file mode 100644 index 0000000000..05da1e9dfb --- /dev/null +++ b/packages/cmsui/.storybook/preview-head.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/packages/cmsui/.storybook/preview.ts b/packages/cmsui/.storybook/preview.ts new file mode 100644 index 0000000000..362843c9bc --- /dev/null +++ b/packages/cmsui/.storybook/preview.ts @@ -0,0 +1,24 @@ +import './storybook-base.css'; +import '@plone/components/dist/basic.css'; +import '../main.css'; +import config from '@plone/registry'; +import installSlots from '../config'; +import installBlocks from '@plone/blocks'; + +config.set('slots', {}); +config.set('utilities', {}); +installSlots(config); +installBlocks(config); + +export const parameters = { + backgrounds: { + default: 'light', + }, + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, +}; diff --git a/packages/cmsui/.storybook/storybook-base.css b/packages/cmsui/.storybook/storybook-base.css new file mode 100644 index 0000000000..f55ba4bf27 --- /dev/null +++ b/packages/cmsui/.storybook/storybook-base.css @@ -0,0 +1,19 @@ +/* Base styles */ +:root { + --basic-font-family: system-ui; + --basic-font-size: 16px; + background: var(--background-color); + font-family: var(--basic-font-family); + font-size: var(--basic-font-size); + line-height: 1.5; +} + +.sbdocs.sbdocs-content { + p { + font-size: 16px; + } +} + +#storybook-root { + width: 100vw; +} diff --git a/packages/cmsui/.storybook/theme.ts b/packages/cmsui/.storybook/theme.ts new file mode 100644 index 0000000000..3262e1f700 --- /dev/null +++ b/packages/cmsui/.storybook/theme.ts @@ -0,0 +1,10 @@ +import { create } from '@storybook/theming/create'; +import logo from './Logo.svg'; + +export default create({ + base: 'light', + brandTitle: '@plone/components StoryBook', + brandUrl: 'https://plone-components.netlify.app/', + brandImage: logo, + brandTarget: '_self', +}); diff --git a/packages/cmsui/.stylelintrc b/packages/cmsui/.stylelintrc new file mode 100644 index 0000000000..8ac62f8d0f --- /dev/null +++ b/packages/cmsui/.stylelintrc @@ -0,0 +1,14 @@ +{ + "extends": ["stylelint-config-idiomatic-order"], + "plugins": ["stylelint-prettier"], + "overrides": [ + { + "files": ["**/*.scss"], + "customSyntax": "postcss-scss" + } + ], + "rules": { + "prettier/prettier": true, + "order/properties-alphabetical-order": null + } +} diff --git a/packages/cmsui/CHANGELOG.md b/packages/cmsui/CHANGELOG.md new file mode 100644 index 0000000000..2969638257 --- /dev/null +++ b/packages/cmsui/CHANGELOG.md @@ -0,0 +1,11 @@ +# @plone/cmsui Release Notes + + + + + +## 1.0.0 (unreleased) diff --git a/packages/cmsui/README.md b/packages/cmsui/README.md new file mode 100644 index 0000000000..ee5d757cd6 --- /dev/null +++ b/packages/cmsui/README.md @@ -0,0 +1,8 @@ +# `@plone/cmsui` + +This package provides default structural slots for Plone 7 and the API-first story. + +> [!WARNING] +> This package or app is experimental. +> The community offers no support whatsoever for it. +> Breaking changes may occur without notice. diff --git a/packages/cmsui/index.ts b/packages/cmsui/index.ts new file mode 100644 index 0000000000..7258290153 --- /dev/null +++ b/packages/cmsui/index.ts @@ -0,0 +1,5 @@ +import type { ConfigType } from '@plone/registry'; + +export default function install(config: ConfigType) { + return config; +} diff --git a/packages/cmsui/main.css b/packages/cmsui/main.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cmsui/news/.gitkeep b/packages/cmsui/news/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cmsui/package.json b/packages/cmsui/package.json new file mode 100644 index 0000000000..28597732df --- /dev/null +++ b/packages/cmsui/package.json @@ -0,0 +1,79 @@ +{ + "name": "@plone/cmsui", + "description": "Plone CMSUI components", + "maintainers": [ + { + "name": "Plone Foundation", + "url": "https://plone.org" + } + ], + "funding": "https://github.com/sponsors/plone", + "license": "MIT", + "version": "1.0.0", + "repository": { + "type": "git", + "url": "https://github.com/plone/volto.git" + }, + "bugs": { + "url": "https://github.com/plone/volto/issues" + }, + "homepage": "https://plone.org", + "keywords": [ + "volto", + "plone", + "plone6", + "react", + "helpers" + ], + "publishConfig": { + "access": "public" + }, + "main": "index.ts", + "scripts": { + "test": "vitest", + "dry-release": "release-it --dry-run", + "release": "release-it", + "release-major-alpha": "release-it major --preRelease=alpha", + "release-alpha": "release-it --preRelease=alpha", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + }, + "dependencies": { + "@plone/client": "workspace:*", + "@plone/components": "workspace:*", + "@plone/registry": "workspace:*", + "react-aria-components": "^1.5.0" + }, + "devDependencies": { + "@plone/types": "workspace:*", + "@storybook/addon-essentials": "^8.0.4", + "@storybook/addon-interactions": "^8.0.4", + "@storybook/addon-links": "^8.0.4", + "@storybook/addon-mdx-gfm": "^8.0.4", + "@storybook/blocks": "^8.0.4", + "@storybook/manager-api": "^8.0.4", + "@storybook/react": "^8.0.4", + "@storybook/react-vite": "^8.0.4", + "@storybook/theming": "^8.0.4", + "@types/react": "^18", + "@types/react-dom": "^18", + "eslint-plugin-storybook": "^0.8.0", + "jest-axe": "^8.0.0", + "release-it": "17.1.1", + "storybook": "^8.0.4", + "tsconfig": "workspace:*", + "typescript": "^5.6.3", + "vite": "^5.4.8", + "vitest": "^2.1.3", + "vitest-axe": "^0.1.0" + } +} diff --git a/packages/cmsui/setupTesting.ts b/packages/cmsui/setupTesting.ts new file mode 100644 index 0000000000..8bc87fa36e --- /dev/null +++ b/packages/cmsui/setupTesting.ts @@ -0,0 +1,3 @@ +import '@testing-library/jest-dom'; +import { toHaveNoViolations } from 'jest-axe'; +expect.extend(toHaveNoViolations); diff --git a/packages/cmsui/stories.ts b/packages/cmsui/stories.ts new file mode 100644 index 0000000000..6a668df163 --- /dev/null +++ b/packages/cmsui/stories.ts @@ -0,0 +1,22 @@ +export const storyData = { + blocks: { + '7ab29abe-b38c-406b-94d7-b270e544a998': { + '@type': 'slate', + value: [ + { + type: 'p', + children: [ + { + text: 'Lorem ipsum dolor sit amet eu tempus ornare elit. Curabitur egestas quisque molestie pellentesque nunc imperdiet posuere morbi nunc eleifend. Volutpat enim augue blandit aliquam interdum pulvinar eu mattis congue. Eleifend mauris ut fermentum egestas mi faucibus adipiscing arcu nibh scelerisque justo habitasse. Mi consectetur hac maecenas leo dictumst vitae phasellus quam praesent vivamus nullam imperdiet integer mauris.', + }, + ], + }, + ], + plaintext: + 'Lorem ipsum dolor sit amet eu tempus ornare elit. Curabitur egestas quisque molestie pellentesque nunc imperdiet posuere morbi nunc eleifend. Volutpat enim augue blandit aliquam interdum pulvinar eu mattis congue. Eleifend mauris ut fermentum egestas mi faucibus adipiscing arcu nibh scelerisque justo habitasse. Mi consectetur hac maecenas leo dictumst vitae phasellus quam praesent vivamus nullam imperdiet integer mauris.', + }, + }, + blocks_layout: { + items: ['7ab29abe-b38c-406b-94d7-b270e544a998'], + }, +}; diff --git a/packages/cmsui/towncrier.toml b/packages/cmsui/towncrier.toml new file mode 100644 index 0000000000..3ef721f378 --- /dev/null +++ b/packages/cmsui/towncrier.toml @@ -0,0 +1,33 @@ +[tool.towncrier] +filename = "CHANGELOG.md" +directory = "news/" +title_format = "## {version} ({project_date})" +underlines = ["", "", ""] +template = "../scripts/templates/towncrier_template.jinja" +start_string = "\n" +issue_format = "[#{issue}](https://github.com/plone/volto/issues/{issue})" + +[[tool.towncrier.type]] +directory = "breaking" +name = "Breaking" +showcontent = true + +[[tool.towncrier.type]] +directory = "feature" +name = "Feature" +showcontent = true + +[[tool.towncrier.type]] +directory = "bugfix" +name = "Bugfix" +showcontent = true + +[[tool.towncrier.type]] +directory = "internal" +name = "Internal" +showcontent = true + +[[tool.towncrier.type]] +directory = "documentation" +name = "Documentation" +showcontent = true diff --git a/packages/cmsui/tsconfig.json b/packages/cmsui/tsconfig.json new file mode 100644 index 0000000000..2965402840 --- /dev/null +++ b/packages/cmsui/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "tsconfig/react-library.json", + "include": ["**/*.ts", "**/*.tsx"], + "exclude": [ + "node_modules", + "build", + "public", + "coverage", + "src/**/*.test.{js,jsx,ts,tsx}", + "src/**/*.spec.{js,jsx,ts,tsx}", + "src/**/*.stories.{js,jsx,ts,tsx}" + ] +} diff --git a/packages/cmsui/vitest.config.ts b/packages/cmsui/vitest.config.ts new file mode 100644 index 0000000000..fddf5f61f7 --- /dev/null +++ b/packages/cmsui/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vitest/config'; + +// https://vitejs.dev/config/ +export default defineConfig({ + test: { + globals: true, + environment: 'jsdom', + setupFiles: './setupTesting.ts', + // you might want to disable it, if you don't have tests that rely on CSS + // since parsing CSS is slow + css: true, + exclude: ['**/node_modules/**', '**/lib/**'], + }, +}); From 7918026b56a6efb51e30b18e762e0c069f4ad193 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Fri, 13 Dec 2024 17:58:13 +0100 Subject: [PATCH 6/7] Fix lock --- pnpm-lock.yaml | 97 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae6dfec91f..3f68d68045 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -517,6 +517,91 @@ importers: specifier: ^7.2.0 version: 7.2.0(debug@4.3.4) + packages/cmsui: + dependencies: + '@plone/client': + specifier: workspace:* + version: link:../client + '@plone/components': + specifier: workspace:* + version: link:../components + '@plone/registry': + specifier: workspace:* + version: link:../registry + react: + specifier: ^16.8.0 || ^17.0.0 || ^18.0.0 + version: 18.2.0 + react-aria-components: + specifier: ^1.5.0 + version: 1.5.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react-dom: + specifier: ^16.8.0 || ^17.0.0 || ^18.0.0 + version: 18.2.0(react@18.2.0) + devDependencies: + '@plone/types': + specifier: workspace:* + version: link:../types + '@storybook/addon-essentials': + specifier: ^8.0.4 + version: 8.0.8(@types/react@18.3.12)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@storybook/addon-interactions': + specifier: ^8.0.4 + version: 8.0.8(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.28.1)(sass@1.75.0)(terser@5.30.3)) + '@storybook/addon-links': + specifier: ^8.0.4 + version: 8.0.8(react@18.2.0) + '@storybook/addon-mdx-gfm': + specifier: ^8.0.4 + version: 8.0.8 + '@storybook/blocks': + specifier: ^8.0.4 + version: 8.0.8(@types/react@18.3.12)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@storybook/manager-api': + specifier: ^8.0.4 + version: 8.0.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@storybook/react': + specifier: ^8.0.4 + version: 8.0.8(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.6.3) + '@storybook/react-vite': + specifier: ^8.0.4 + version: 8.0.8(@preact/preset-vite@2.8.2(@babel/core@7.25.8)(vite@5.4.9(@types/node@20.12.7)(less@3.11.1)(lightningcss@1.28.1)(sass@1.75.0)(terser@5.30.3)))(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.9(@types/node@20.12.7)(less@3.11.1)(lightningcss@1.28.1)(sass@1.75.0)(terser@5.30.3)) + '@storybook/theming': + specifier: ^8.0.4 + version: 8.0.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@types/react': + specifier: ^18 + version: 18.3.12 + '@types/react-dom': + specifier: ^18 + version: 18.3.1 + eslint-plugin-storybook: + specifier: ^0.8.0 + version: 0.8.0(eslint@8.57.0)(typescript@5.6.3) + jest-axe: + specifier: ^8.0.0 + version: 8.0.0 + release-it: + specifier: 17.1.1 + version: 17.1.1(typescript@5.6.3) + storybook: + specifier: ^8.0.4 + version: 8.0.8(@babel/preset-env@7.24.4(@babel/core@7.25.8))(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: ^5.6.3 + version: 5.6.3 + vite: + specifier: ^5.4.8 + version: 5.4.9(@types/node@20.12.7)(less@3.11.1)(lightningcss@1.28.1)(sass@1.75.0)(terser@5.30.3) + vitest: + specifier: ^2.1.3 + version: 2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.28.1)(sass@1.75.0)(terser@5.30.3) + vitest-axe: + specifier: ^0.1.0 + version: 0.1.0(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.28.1)(sass@1.75.0)(terser@5.30.3)) + packages/components: dependencies: '@react-aria/utils': @@ -18378,9 +18463,11 @@ snapshots: '@babel/plugin-transform-async-to-generator@7.24.1(@babel/core@7.25.8)': dependencies: '@babel/core': 7.25.8 - '@babel/helper-module-imports': 7.24.3 + '@babel/helper-module-imports': 7.25.7 '@babel/helper-plugin-utils': 7.25.7 '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.25.8) + transitivePeerDependencies: + - supports-color '@babel/plugin-transform-block-scoped-functions@7.24.1(@babel/core@7.24.4)': dependencies: @@ -19143,11 +19230,11 @@ snapshots: '@babel/preset-env@7.24.4(@babel/core@7.25.8)': dependencies: - '@babel/compat-data': 7.24.4 + '@babel/compat-data': 7.25.8 '@babel/core': 7.25.8 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/helper-validator-option': 7.23.5 + '@babel/helper-compilation-targets': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-validator-option': 7.25.7 '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.4(@babel/core@7.25.8) '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.1(@babel/core@7.25.8) '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.1(@babel/core@7.25.8) From 317253c7919bd0715eb328a23890d83d98406fad Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Sat, 14 Dec 2024 11:57:41 +0100 Subject: [PATCH 7/7] WIP - First components --- .stylelintignore | 1 + .../configuration/settings-reference.md | 7 + packages/cmsui/components/Field/Field.tsx | 185 ++++++++++++++++++ packages/cmsui/config/widgets.ts | 9 + packages/cmsui/index.ts | 4 + packages/cmsui/package.json | 1 + packages/components/README.md | 12 +- .../components/src/stories/Introduction.mdx | 18 +- .../src/styles/basic/BlockToolbar.css | 2 +- .../src/styles/basic/Breadcrumbs.css | 2 +- .../components/src/styles/basic/Button.css | 2 +- .../components/src/styles/basic/Calendar.css | 2 +- .../components/src/styles/basic/Checkbox.css | 2 +- .../src/styles/basic/CheckboxGroup.css | 2 +- .../components/src/styles/basic/ColorArea.css | 2 +- .../src/styles/basic/ColorField.css | 2 +- .../src/styles/basic/ColorPicker.css | 2 +- .../src/styles/basic/ColorSlider.css | 2 +- .../src/styles/basic/ColorSwatch.css | 2 +- .../src/styles/basic/ColorSwatchPicker.css | 2 +- .../src/styles/basic/ColorWheel.css | 2 +- .../components/src/styles/basic/ComboBox.css | 2 +- .../components/src/styles/basic/Container.css | 2 +- .../components/src/styles/basic/DateField.css | 2 +- .../src/styles/basic/DatePicker.css | 2 +- .../src/styles/basic/DateRangePicker.css | 2 +- .../components/src/styles/basic/Dialog.css | 2 +- .../src/styles/basic/Disclosure.css | 2 +- packages/components/src/styles/basic/Form.css | 2 +- .../components/src/styles/basic/GridList.css | 2 +- .../components/src/styles/basic/Label.css | 2 +- packages/components/src/styles/basic/Link.css | 2 +- .../components/src/styles/basic/ListBox.css | 2 +- packages/components/src/styles/basic/Menu.css | 2 +- .../components/src/styles/basic/Meter.css | 2 +- .../components/src/styles/basic/Modal.css | 2 +- .../src/styles/basic/NumberField.css | 2 +- .../components/src/styles/basic/Popover.css | 2 +- .../src/styles/basic/ProgressBar.css | 2 +- .../src/styles/basic/RadioGroup.css | 2 +- .../src/styles/basic/RangeCalendar.css | 2 +- .../src/styles/basic/SearchField.css | 2 +- .../components/src/styles/basic/Select.css | 2 +- .../components/src/styles/basic/Slider.css | 2 +- .../components/src/styles/basic/Switch.css | 2 +- .../components/src/styles/basic/Table.css | 2 +- packages/components/src/styles/basic/Tabs.css | 2 +- .../components/src/styles/basic/TagGroup.css | 2 +- .../components/src/styles/basic/TextField.css | 2 +- .../components/src/styles/basic/TimeField.css | 2 +- .../src/styles/basic/ToggleButton.css | 2 +- .../components/src/styles/basic/Toolbar.css | 2 +- .../components/src/styles/basic/Tooltip.css | 2 +- .../components/src/styles/basic/icons.css | 2 +- packages/types/src/config/Settings.d.ts | 1 + packages/volto/package.json | 4 + .../volto/src/components/manage/Form/Form.jsx | 3 +- .../src/components/manage/Form/InlineForm.jsx | 2 +- packages/volto/src/config/index.js | 8 + packages/volto/src/helpers/Html/Html.jsx | 6 + packages/volto/src/loadSemanticUI.less | 4 + packages/volto/src/styles/main.css | 2 + packages/volto/src/styles/sidebar.css | 19 ++ packages/volto/src/styles/variables.css | 3 + packages/volto/src/theme.js | 15 +- pnpm-lock.yaml | 17 +- 66 files changed, 339 insertions(+), 74 deletions(-) create mode 100644 packages/cmsui/components/Field/Field.tsx create mode 100644 packages/cmsui/config/widgets.ts create mode 100644 packages/volto/src/loadSemanticUI.less create mode 100644 packages/volto/src/styles/main.css create mode 100644 packages/volto/src/styles/sidebar.css create mode 100644 packages/volto/src/styles/variables.css diff --git a/.stylelintignore b/.stylelintignore index 4b2be235a0..f52d70d0db 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -2,3 +2,4 @@ dist docs node_modules packages/registry/lib +build diff --git a/docs/source/configuration/settings-reference.md b/docs/source/configuration/settings-reference.md index 54eaebef20..ae5e32cc76 100644 --- a/docs/source/configuration/settings-reference.md +++ b/docs/source/configuration/settings-reference.md @@ -463,6 +463,13 @@ querystringSearchGet [See an explanation of character limits in URLs](https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers/417184#417184). Please test this setting properly before enabling in a production site. +cssLayers + If you want to use CSS layers in Volto styling, it's possible to define them making sure that they are defined and applied at the very top level of the page (head tag). + By using this configuration, you can pass the layer list definition as an array: + + ```js + config.settings.cssLayers = ['reset', 'plone-components', 'layout', 'addons', 'theme']; + ``` ``` ## Views settings diff --git a/packages/cmsui/components/Field/Field.tsx b/packages/cmsui/components/Field/Field.tsx new file mode 100644 index 0000000000..9e91e94eb0 --- /dev/null +++ b/packages/cmsui/components/Field/Field.tsx @@ -0,0 +1,185 @@ +import config from '@plone/registry'; +import type { + WidgetsConfigById, + WidgetsConfigByFactory, + WidgetsConfigByType, + WidgetsConfigByVocabulary, + WidgetsConfigByWidget, +} from '@plone/types'; + +type FieldProps = { + id: keyof WidgetsConfigById; + widget: keyof WidgetsConfigByWidget; + vocabulary: { '@id': keyof WidgetsConfigByVocabulary }; + choices: string; + type: keyof WidgetsConfigByType; + focus: boolean; + mode: string; + widgetOptions: any; + factory: keyof WidgetsConfigByFactory; + onChange: (id: string, value: any) => void; + placeholder: string; + title: string; +}; + +const MODE_HIDDEN = 'hidden'; //hidden mode. If mode is hidden, field is not rendered + +/** + * Get default widget + */ +const getWidgetDefault = (): React.ComponentType => config.widgets.default; + +/** + * Get widget by field's `id` attribute + */ +const getWidgetByFieldId = ( + id: FieldProps['id'], +): React.ComponentType | null => config.widgets.id[id] || null; + +/** + * Get widget by factory attribute + */ +const getWidgetByFactory = ( + factory: FieldProps['factory'], +): React.ComponentType | null => config.widgets.factory?.[factory] || null; + +/** + * Get widget by field's `widget` attribute + */ +const getWidgetByName = ( + widget: FieldProps['widget'], +): React.ComponentType | null => + typeof widget === 'string' + ? config.widgets.widget[widget] || getWidgetDefault() + : null; + +/** + * Get widget by tagged values + * + +directives.widget( + 'fieldname', + frontendOptions={ + "widget": 'specialwidget', + "version": 'extra' + }) + + */ +const getWidgetFromTaggedValues = (widgetOptions: { + frontendOptions: { widget: FieldProps['widget']; widgetProps: any }; +}): React.ComponentType | null => + typeof widgetOptions?.frontendOptions?.widget === 'string' + ? config.widgets.widget[widgetOptions.frontendOptions.widget] + : null; + +/** + * Get widget props from tagged values + * + +directives.widget( + "fieldname", + frontendOptions={ + "widget": "specialwidget", + "widgetProps": {"prop1": "specialprop"} + }) + + */ +const getWidgetPropsFromTaggedValues = (widgetOptions: { + frontendOptions: { widget: string; widgetProps: any }; +}): React.ComponentType | null => + typeof widgetOptions?.frontendOptions?.widgetProps === 'object' + ? widgetOptions.frontendOptions.widgetProps + : null; + +/** + * Get widget by field's `vocabulary` attribute + */ +const getWidgetByVocabulary = ( + vocabulary: FieldProps['vocabulary'], +): React.ComponentType | null => + vocabulary && vocabulary['@id'] + ? config.widgets.vocabulary[ + vocabulary['@id'].replace( + /^.*\/@vocabularies\//, + '', + ) as keyof WidgetsConfigByVocabulary + ] + : null; + +/** + * Get widget by field's hints `vocabulary` attribute in widgetOptions + */ +const getWidgetByVocabularyFromHint = ( + props: FieldProps, +): React.ComponentType | null => + props.widgetOptions && props.widgetOptions.vocabulary + ? config.widgets.vocabulary[ + props.widgetOptions.vocabulary['@id'].replace( + /^.*\/@vocabularies\//, + '', + ) as keyof WidgetsConfigByVocabulary + ] + : null; + +/** + * Get widget by field's `choices` attribute + */ +const getWidgetByChoices = ( + props: FieldProps, +): React.ComponentType | null => { + if (props.choices) { + return config.widgets.choices; + } + + if (props.vocabulary) { + // If vocabulary exists, then it means it's a choice field in disguise with + // no widget specified that probably contains a string then we force it + // to be a select widget instead + return config.widgets.choices; + } + + return null; +}; + +/** + * Get widget by field's `type` attribute + */ +const getWidgetByType = (type: FieldProps['type']): React.ComponentType => + config.widgets.type[type] || null; + +const Field = (props: FieldProps) => { + const Widget = + getWidgetByFieldId(props.id) || + getWidgetFromTaggedValues(props.widgetOptions) || + getWidgetByName(props.widget) || + getWidgetByChoices(props) || + getWidgetByVocabulary(props.vocabulary) || + getWidgetByVocabularyFromHint(props) || + getWidgetByFactory(props.factory) || + getWidgetByType(props.type) || + getWidgetDefault(); + + if (props.mode === MODE_HIDDEN) { + return null; + } + + // Adding the widget props from tagged values (if any) + const widgetProps = { + ...props, + label: props.title, + placeholder: props.placeholder || 'Type something...', + // Temporary translator from the old widget signature (id, value) to the new one (value) + onChange: (arg1: string, arg2: string | undefined) => { + if (!arg2 === undefined) { + props.onChange(props.id, arg1); + } else { + props.onChange(props.id, arg2); + } + }, + ...getWidgetPropsFromTaggedValues(props.widgetOptions), + }; + + return ; +}; + +export default Field; diff --git a/packages/cmsui/config/widgets.ts b/packages/cmsui/config/widgets.ts new file mode 100644 index 0000000000..3ff5ba82fb --- /dev/null +++ b/packages/cmsui/config/widgets.ts @@ -0,0 +1,9 @@ +import type { ConfigType } from '@plone/registry'; +import { QuantaTextAreaField, QuantaTextField } from '@plone/components'; + +export default function install(config: ConfigType) { + config.widgets.default = QuantaTextField; + config.widgets.widget.textarea = QuantaTextAreaField; + + return config; +} diff --git a/packages/cmsui/index.ts b/packages/cmsui/index.ts index 7258290153..4804a02ba9 100644 --- a/packages/cmsui/index.ts +++ b/packages/cmsui/index.ts @@ -1,5 +1,9 @@ import type { ConfigType } from '@plone/registry'; +import installWidgets from './config/widgets'; +import '@plone/components/dist/basic.css'; +import '@plone/components/dist/quanta.css'; export default function install(config: ConfigType) { + installWidgets(config); return config; } diff --git a/packages/cmsui/package.json b/packages/cmsui/package.json index 28597732df..d397a3f337 100644 --- a/packages/cmsui/package.json +++ b/packages/cmsui/package.json @@ -28,6 +28,7 @@ "publishConfig": { "access": "public" }, + "type": "module", "main": "index.ts", "scripts": { "test": "vitest", diff --git a/packages/components/README.md b/packages/components/README.md index af2f8c0589..ba7a55d313 100644 --- a/packages/components/README.md +++ b/packages/components/README.md @@ -62,11 +62,19 @@ Using them as a baseline will allow you to quickly build your theme around them. `@plone/components` basic styles provide a simple, yet powerful, set of tokenized custom CSS properties that will help you customize your own styles on the top of the basic styling. You can override them in your classes while maintaining them for others. +### CSS layers + +This package use CSS layers to provide a way to style the components in a more flexible way. +It uses the `plone-components` layer name to scope all the CSS declarations in the package. +The basic styling uses the nested `plone-components.base` named layer. +You can use the `plone-components` layer to override the basic styling, or use the `plone-components.base` layer to override the basic styling in a more specific way. + ### Quanta This package also features the Quanta components. -The Quanta theme is an example of it. -These components use the basic styling as a baseline, not only in styling, but also in the component side, reusing the CSS and custom CSS properties in it. +These components use the basic styling as a baseline, extending them to achieve Quanta look and feel. +They also extend the basic React components in a composable way. +The Quanta styling is scoped in the `plone-components.quanta` named layer. Quanta is built upon the basic styles in an additive way. The use of the Quanta CSS implies using it upon basic styling. diff --git a/packages/components/src/stories/Introduction.mdx b/packages/components/src/stories/Introduction.mdx index 41ecc9f5d6..8ddefc56b6 100644 --- a/packages/components/src/stories/Introduction.mdx +++ b/packages/components/src/stories/Introduction.mdx @@ -5,16 +5,17 @@ import { Meta } from '@storybook/blocks'; # `@plone/components` -This package contains ReactJS components for using Plone as a headless CMS. +This package contains ReactJS components for Plone 7 and Plone's API-first CMS story. The purpose of this package is to provide an agnostic set of baseline components to build Plone sites upon. ## Usage -Using the package manager of your choice (npm, yarn, pnpm) install the package: +Using the package manager of your choice (npm, yarn, pnpm) to install the package in your app. +Use pnpm in case you are adding them a Volto add-on/workspace: ```shell -yarn add @plone/components +pnpm add @plone/components ``` Whenever you want to use the components, all are exported as named exports: @@ -53,12 +54,19 @@ or selectively: import '@plone/components/src/styles/basic/TextField.css'; ``` +### CSS layers + +This package use CSS layers to provide a way to style the components in a more flexible way. +It uses the `plone-components` layer name to scope all the CSS declarations in the package. +The basic styling uses the nested `plone-components.base` named layer. +You can use the `plone-components` layer to override the basic styling, or use the `plone-components.base` layer to override the basic styling in a more specific way. ## Quanta This package also features the Quanta components. -The Quanta theme is an example of it. -These components use the basic styling as a baseline, not only in styling, but also in the component side, reusing the CSS and custom CSS properties in it. +These components use the basic styling as a baseline, extending them to achieve Quanta look and feel. +They also extend the basic React components in a composable way. +The Quanta styling is scoped in the `plone-components.quanta` named layer. Quanta is built upon the basic styles in an additive way. The use of the Quanta CSS implies using it upon basic styling. diff --git a/packages/components/src/styles/basic/BlockToolbar.css b/packages/components/src/styles/basic/BlockToolbar.css index 19c101a290..1799ce9bb0 100644 --- a/packages/components/src/styles/basic/BlockToolbar.css +++ b/packages/components/src/styles/basic/BlockToolbar.css @@ -4,7 +4,7 @@ @import './Menu.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .blocks-toolbar { display: flex; flex-wrap: wrap; diff --git a/packages/components/src/styles/basic/Breadcrumbs.css b/packages/components/src/styles/basic/Breadcrumbs.css index 0aaa86cac1..d6d5a8e83c 100644 --- a/packages/components/src/styles/basic/Breadcrumbs.css +++ b/packages/components/src/styles/basic/Breadcrumbs.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Breadcrumbs { display: flex; align-items: center; diff --git a/packages/components/src/styles/basic/Button.css b/packages/components/src/styles/basic/Button.css index c6849f7fb4..d4e79025e3 100644 --- a/packages/components/src/styles/basic/Button.css +++ b/packages/components/src/styles/basic/Button.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Button { padding: 8px 8px; border: 1px solid var(--border-color); diff --git a/packages/components/src/styles/basic/Calendar.css b/packages/components/src/styles/basic/Calendar.css index ec81b10c4d..0c16b5071c 100644 --- a/packages/components/src/styles/basic/Calendar.css +++ b/packages/components/src/styles/basic/Calendar.css @@ -1,7 +1,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Calendar { width: fit-content; max-width: 100%; diff --git a/packages/components/src/styles/basic/Checkbox.css b/packages/components/src/styles/basic/Checkbox.css index 46f856ac1d..7ca3ed1197 100644 --- a/packages/components/src/styles/basic/Checkbox.css +++ b/packages/components/src/styles/basic/Checkbox.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Checkbox { --selected-color: var(--highlight-background); --selected-color-pressed: var(--highlight-background-pressed); diff --git a/packages/components/src/styles/basic/CheckboxGroup.css b/packages/components/src/styles/basic/CheckboxGroup.css index 1939115950..ca543fcb41 100644 --- a/packages/components/src/styles/basic/CheckboxGroup.css +++ b/packages/components/src/styles/basic/CheckboxGroup.css @@ -3,7 +3,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-CheckboxGroup { display: flex; flex-direction: column; diff --git a/packages/components/src/styles/basic/ColorArea.css b/packages/components/src/styles/basic/ColorArea.css index e5a81b7512..fa49256001 100644 --- a/packages/components/src/styles/basic/ColorArea.css +++ b/packages/components/src/styles/basic/ColorArea.css @@ -1,6 +1,6 @@ @import './ColorSlider.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ColorArea { width: 192px; height: 192px; diff --git a/packages/components/src/styles/basic/ColorField.css b/packages/components/src/styles/basic/ColorField.css index 41442cbbe8..d09a7709f5 100644 --- a/packages/components/src/styles/basic/ColorField.css +++ b/packages/components/src/styles/basic/ColorField.css @@ -2,7 +2,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ColorField { display: flex; flex-direction: column; diff --git a/packages/components/src/styles/basic/ColorPicker.css b/packages/components/src/styles/basic/ColorPicker.css index 9bfd25e9cb..344565fdbf 100644 --- a/packages/components/src/styles/basic/ColorPicker.css +++ b/packages/components/src/styles/basic/ColorPicker.css @@ -10,7 +10,7 @@ @import './Select.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .color-picker { display: flex; align-items: center; diff --git a/packages/components/src/styles/basic/ColorSlider.css b/packages/components/src/styles/basic/ColorSlider.css index f61f4b0d5e..cd54265e3a 100644 --- a/packages/components/src/styles/basic/ColorSlider.css +++ b/packages/components/src/styles/basic/ColorSlider.css @@ -1,4 +1,4 @@ -@layer plone-components { +@layer plone-components.base { .react-aria-ColorSlider { display: grid; max-width: 300px; diff --git a/packages/components/src/styles/basic/ColorSwatch.css b/packages/components/src/styles/basic/ColorSwatch.css index f617ec5341..f8127e8756 100644 --- a/packages/components/src/styles/basic/ColorSwatch.css +++ b/packages/components/src/styles/basic/ColorSwatch.css @@ -1,6 +1,6 @@ @import './ColorSlider.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ColorSwatch { width: 32px; height: 32px; diff --git a/packages/components/src/styles/basic/ColorSwatchPicker.css b/packages/components/src/styles/basic/ColorSwatchPicker.css index 5a82e24a96..c91c37180a 100644 --- a/packages/components/src/styles/basic/ColorSwatchPicker.css +++ b/packages/components/src/styles/basic/ColorSwatchPicker.css @@ -2,7 +2,7 @@ @import './ColorField.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ColorSwatchPicker { display: flex; flex-wrap: wrap; diff --git a/packages/components/src/styles/basic/ColorWheel.css b/packages/components/src/styles/basic/ColorWheel.css index c7a953896d..fcff769737 100644 --- a/packages/components/src/styles/basic/ColorWheel.css +++ b/packages/components/src/styles/basic/ColorWheel.css @@ -1,4 +1,4 @@ -@layer plone-components { +@layer plone-components.base { .react-aria-ColorThumb { width: 20px; height: 20px; diff --git a/packages/components/src/styles/basic/ComboBox.css b/packages/components/src/styles/basic/ComboBox.css index fc597eb7ee..f3a2d64b7d 100644 --- a/packages/components/src/styles/basic/ComboBox.css +++ b/packages/components/src/styles/basic/ComboBox.css @@ -5,7 +5,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ComboBox { color: var(--text-color); diff --git a/packages/components/src/styles/basic/Container.css b/packages/components/src/styles/basic/Container.css index f7448a20b2..da97f93bf3 100644 --- a/packages/components/src/styles/basic/Container.css +++ b/packages/components/src/styles/basic/Container.css @@ -1,4 +1,4 @@ -@layer plone-components { +@layer plone-components.base { .q.container { container-type: inline-size; diff --git a/packages/components/src/styles/basic/DateField.css b/packages/components/src/styles/basic/DateField.css index d0220e4999..7eca458b11 100644 --- a/packages/components/src/styles/basic/DateField.css +++ b/packages/components/src/styles/basic/DateField.css @@ -2,7 +2,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-DateField { color: var(--text-color); } diff --git a/packages/components/src/styles/basic/DatePicker.css b/packages/components/src/styles/basic/DatePicker.css index dcfd458c83..796e0439e5 100644 --- a/packages/components/src/styles/basic/DatePicker.css +++ b/packages/components/src/styles/basic/DatePicker.css @@ -6,7 +6,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-DatePicker { color: var(--text-color); diff --git a/packages/components/src/styles/basic/DateRangePicker.css b/packages/components/src/styles/basic/DateRangePicker.css index bb44b073c1..c48d9ac33f 100644 --- a/packages/components/src/styles/basic/DateRangePicker.css +++ b/packages/components/src/styles/basic/DateRangePicker.css @@ -6,7 +6,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-DateRangePicker { color: var(--text-color); diff --git a/packages/components/src/styles/basic/Dialog.css b/packages/components/src/styles/basic/Dialog.css index 45ca2210c8..64366c4693 100644 --- a/packages/components/src/styles/basic/Dialog.css +++ b/packages/components/src/styles/basic/Dialog.css @@ -3,7 +3,7 @@ @import './Modal.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Dialog { padding: 30px; outline: none; diff --git a/packages/components/src/styles/basic/Disclosure.css b/packages/components/src/styles/basic/Disclosure.css index 5704d093c1..cb8cb529db 100644 --- a/packages/components/src/styles/basic/Disclosure.css +++ b/packages/components/src/styles/basic/Disclosure.css @@ -1,7 +1,7 @@ @import './theme.css'; @import './Button.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Disclosure { .react-aria-Button[slot='trigger'] { display: flex; diff --git a/packages/components/src/styles/basic/Form.css b/packages/components/src/styles/basic/Form.css index cebcb43466..00b864fa21 100644 --- a/packages/components/src/styles/basic/Form.css +++ b/packages/components/src/styles/basic/Form.css @@ -2,7 +2,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Form { display: flex; flex-direction: column; diff --git a/packages/components/src/styles/basic/GridList.css b/packages/components/src/styles/basic/GridList.css index 22ab490bcf..91944e9b21 100644 --- a/packages/components/src/styles/basic/GridList.css +++ b/packages/components/src/styles/basic/GridList.css @@ -3,7 +3,7 @@ @import './ToggleButton.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-GridList { display: flex; overflow: auto; diff --git a/packages/components/src/styles/basic/Label.css b/packages/components/src/styles/basic/Label.css index f3755f1ddc..1544ab27d9 100644 --- a/packages/components/src/styles/basic/Label.css +++ b/packages/components/src/styles/basic/Label.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Label { /* TODO: Review since taken from default quanta */ font-size: 0.9rem; diff --git a/packages/components/src/styles/basic/Link.css b/packages/components/src/styles/basic/Link.css index 8f91a261ed..08e46bddd5 100644 --- a/packages/components/src/styles/basic/Link.css +++ b/packages/components/src/styles/basic/Link.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Link { position: relative; color: var(--link-color); diff --git a/packages/components/src/styles/basic/ListBox.css b/packages/components/src/styles/basic/ListBox.css index 9cec61d80a..e9a5d92adf 100644 --- a/packages/components/src/styles/basic/ListBox.css +++ b/packages/components/src/styles/basic/ListBox.css @@ -1,7 +1,7 @@ @import './Checkbox.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ListBox { display: flex; overflow: auto; diff --git a/packages/components/src/styles/basic/Menu.css b/packages/components/src/styles/basic/Menu.css index 594f7b7ea1..dbf9370209 100644 --- a/packages/components/src/styles/basic/Menu.css +++ b/packages/components/src/styles/basic/Menu.css @@ -2,7 +2,7 @@ @import './Popover.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Menu { overflow: auto; min-width: 150px; diff --git a/packages/components/src/styles/basic/Meter.css b/packages/components/src/styles/basic/Meter.css index 2ab6709510..2947240afe 100644 --- a/packages/components/src/styles/basic/Meter.css +++ b/packages/components/src/styles/basic/Meter.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Meter { --fill-color: forestgreen; diff --git a/packages/components/src/styles/basic/Modal.css b/packages/components/src/styles/basic/Modal.css index 544e1c224d..b1c90d8578 100644 --- a/packages/components/src/styles/basic/Modal.css +++ b/packages/components/src/styles/basic/Modal.css @@ -2,7 +2,7 @@ @import './TextField.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ModalOverlay { position: fixed; z-index: 100; diff --git a/packages/components/src/styles/basic/NumberField.css b/packages/components/src/styles/basic/NumberField.css index a5ed24c70a..b657f6b201 100644 --- a/packages/components/src/styles/basic/NumberField.css +++ b/packages/components/src/styles/basic/NumberField.css @@ -2,7 +2,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-NumberField { margin-bottom: 8px; color: var(--text-color); diff --git a/packages/components/src/styles/basic/Popover.css b/packages/components/src/styles/basic/Popover.css index 89e8f419e5..5712ae8fcf 100644 --- a/packages/components/src/styles/basic/Popover.css +++ b/packages/components/src/styles/basic/Popover.css @@ -3,7 +3,7 @@ @import './Switch.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Popover { --background-color: var(--overlay-background); max-width: 250px; diff --git a/packages/components/src/styles/basic/ProgressBar.css b/packages/components/src/styles/basic/ProgressBar.css index 2a171ea53b..12119e67eb 100644 --- a/packages/components/src/styles/basic/ProgressBar.css +++ b/packages/components/src/styles/basic/ProgressBar.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ProgressBar { display: grid; width: 250px; diff --git a/packages/components/src/styles/basic/RadioGroup.css b/packages/components/src/styles/basic/RadioGroup.css index 2a05774dcc..07b905498d 100644 --- a/packages/components/src/styles/basic/RadioGroup.css +++ b/packages/components/src/styles/basic/RadioGroup.css @@ -2,7 +2,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-RadioGroup { display: flex; flex-direction: column; diff --git a/packages/components/src/styles/basic/RangeCalendar.css b/packages/components/src/styles/basic/RangeCalendar.css index 025c9ee54d..f409bb678b 100644 --- a/packages/components/src/styles/basic/RangeCalendar.css +++ b/packages/components/src/styles/basic/RangeCalendar.css @@ -1,7 +1,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-RangeCalendar { width: fit-content; max-width: 100%; diff --git a/packages/components/src/styles/basic/SearchField.css b/packages/components/src/styles/basic/SearchField.css index ef23082851..ccd7d27570 100644 --- a/packages/components/src/styles/basic/SearchField.css +++ b/packages/components/src/styles/basic/SearchField.css @@ -2,7 +2,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-SearchField { display: grid; width: fit-content; diff --git a/packages/components/src/styles/basic/Select.css b/packages/components/src/styles/basic/Select.css index bcffcaedf1..592796bc13 100644 --- a/packages/components/src/styles/basic/Select.css +++ b/packages/components/src/styles/basic/Select.css @@ -4,7 +4,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Select { color: var(--text-color); diff --git a/packages/components/src/styles/basic/Slider.css b/packages/components/src/styles/basic/Slider.css index 00f6a8bb76..2de1068078 100644 --- a/packages/components/src/styles/basic/Slider.css +++ b/packages/components/src/styles/basic/Slider.css @@ -1,7 +1,7 @@ @import './NumberField.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Slider { display: grid; max-width: 300px; diff --git a/packages/components/src/styles/basic/Switch.css b/packages/components/src/styles/basic/Switch.css index 85b4e44b9f..0508d2aa29 100644 --- a/packages/components/src/styles/basic/Switch.css +++ b/packages/components/src/styles/basic/Switch.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Switch { display: flex; align-items: center; diff --git a/packages/components/src/styles/basic/Table.css b/packages/components/src/styles/basic/Table.css index 5ac6d90256..6a55a242cc 100644 --- a/packages/components/src/styles/basic/Table.css +++ b/packages/components/src/styles/basic/Table.css @@ -5,7 +5,7 @@ @import './Menu.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { :root { --plone-table-border: 0 none; --plone-table-border-radius: 0; diff --git a/packages/components/src/styles/basic/Tabs.css b/packages/components/src/styles/basic/Tabs.css index 9662ecd6a4..811168c931 100644 --- a/packages/components/src/styles/basic/Tabs.css +++ b/packages/components/src/styles/basic/Tabs.css @@ -2,7 +2,7 @@ @import './Link.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Tabs { display: flex; color: var(--text-color); diff --git a/packages/components/src/styles/basic/TagGroup.css b/packages/components/src/styles/basic/TagGroup.css index d50abf9140..9fc9df8916 100644 --- a/packages/components/src/styles/basic/TagGroup.css +++ b/packages/components/src/styles/basic/TagGroup.css @@ -1,7 +1,7 @@ @import './ToggleButton.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-TagGroup { display: flex; flex-direction: column; diff --git a/packages/components/src/styles/basic/TextField.css b/packages/components/src/styles/basic/TextField.css index f99b9178df..4be3b73c95 100644 --- a/packages/components/src/styles/basic/TextField.css +++ b/packages/components/src/styles/basic/TextField.css @@ -2,7 +2,7 @@ @import './Label.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-TextField { display: flex; width: fit-content; diff --git a/packages/components/src/styles/basic/TimeField.css b/packages/components/src/styles/basic/TimeField.css index 4fb876a93b..b1ca5453e7 100644 --- a/packages/components/src/styles/basic/TimeField.css +++ b/packages/components/src/styles/basic/TimeField.css @@ -2,7 +2,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-TimeField { color: var(--text-color); } diff --git a/packages/components/src/styles/basic/ToggleButton.css b/packages/components/src/styles/basic/ToggleButton.css index 6e7ad2bb39..50dd494b1a 100644 --- a/packages/components/src/styles/basic/ToggleButton.css +++ b/packages/components/src/styles/basic/ToggleButton.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ToggleButton { padding: 8px 8px; border: 1px solid var(--border-color); diff --git a/packages/components/src/styles/basic/Toolbar.css b/packages/components/src/styles/basic/Toolbar.css index 306f570fc7..07b03e84a9 100644 --- a/packages/components/src/styles/basic/Toolbar.css +++ b/packages/components/src/styles/basic/Toolbar.css @@ -4,7 +4,7 @@ @import './Menu.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Toolbar { display: flex; flex-wrap: wrap; diff --git a/packages/components/src/styles/basic/Tooltip.css b/packages/components/src/styles/basic/Tooltip.css index 8965f2bd7e..8bec7761ed 100644 --- a/packages/components/src/styles/basic/Tooltip.css +++ b/packages/components/src/styles/basic/Tooltip.css @@ -1,7 +1,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Tooltip { max-width: 150px; padding: 2px 8px; diff --git a/packages/components/src/styles/basic/icons.css b/packages/components/src/styles/basic/icons.css index ec69303237..6cfd1b3a50 100644 --- a/packages/components/src/styles/basic/icons.css +++ b/packages/components/src/styles/basic/icons.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { :root { /* These has to be mapped to global css custom properties based on the SG scales */ --quanta-icon-default-size-s: 18px; diff --git a/packages/types/src/config/Settings.d.ts b/packages/types/src/config/Settings.d.ts index 68419e2db2..61e87a8955 100644 --- a/packages/types/src/config/Settings.d.ts +++ b/packages/types/src/config/Settings.d.ts @@ -101,4 +101,5 @@ export interface SettingsConfig { includeSiteTitle: boolean; titleAndSiteTitleSeparator: string; }; + cssLayers: string[]; } diff --git a/packages/volto/package.json b/packages/volto/package.json index 64d9b1c45b..6ad031b51e 100644 --- a/packages/volto/package.json +++ b/packages/volto/package.json @@ -30,6 +30,9 @@ "coreAddons": { "volto-slate": { "package": "@plone/volto-slate" + }, + "cmsui": { + "package": "@plone/cmsui" } }, "main": "src/index.js", @@ -179,6 +182,7 @@ "dependencies": { "@loadable/component": "5.14.1", "@loadable/server": "5.14.0", + "@plone/cmsui": "workspace:*", "@plone/registry": "workspace:*", "@plone/scripts": "workspace:*", "@plone/volto-slate": "workspace:*", diff --git a/packages/volto/src/components/manage/Form/Form.jsx b/packages/volto/src/components/manage/Form/Form.jsx index d19b68a603..e5b358b3e0 100644 --- a/packages/volto/src/components/manage/Form/Form.jsx +++ b/packages/volto/src/components/manage/Form/Form.jsx @@ -5,7 +5,8 @@ import Icon from '@plone/volto/components/theme/Icon/Icon'; import Toast from '@plone/volto/components/manage/Toast/Toast'; -import { Field, BlocksForm } from '@plone/volto/components/manage/Form'; +import { BlocksForm } from '@plone/volto/components/manage/Form'; +import Field from '@plone/cmsui/components/Field/Field'; import BlocksToolbar from '@plone/volto/components/manage/Form/BlocksToolbar'; import UndoToolbar from '@plone/volto/components/manage/Form/UndoToolbar'; import { difference } from '@plone/volto/helpers/Utils/Utils'; diff --git a/packages/volto/src/components/manage/Form/InlineForm.jsx b/packages/volto/src/components/manage/Form/InlineForm.jsx index a92f6aaba2..6e132a9f74 100644 --- a/packages/volto/src/components/manage/Form/InlineForm.jsx +++ b/packages/volto/src/components/manage/Form/InlineForm.jsx @@ -14,7 +14,7 @@ import { arrayRange, } from '@plone/volto/helpers/Utils/Utils'; import Icon from '@plone/volto/components/theme/Icon/Icon'; -import { Field } from '@plone/volto/components/manage/Form'; +import Field from '@plone/cmsui/components/Field/Field'; import { applySchemaDefaults } from '@plone/volto/helpers/Blocks/Blocks'; import upSVG from '@plone/volto/icons/up-key.svg'; diff --git a/packages/volto/src/config/index.js b/packages/volto/src/config/index.js index 48ab6e0159..f4a497b546 100644 --- a/packages/volto/src/config/index.js +++ b/packages/volto/src/config/index.js @@ -177,6 +177,14 @@ let config = { includeSiteTitle: false, titleAndSiteTitleSeparator: '-', }, + cssLayers: [ + 'reset', + 'semanticUI', + 'plone-components', + 'layout', + 'addons', + 'theme', + ], }, experimental: { addBlockButton: { diff --git a/packages/volto/src/helpers/Html/Html.jsx b/packages/volto/src/helpers/Html/Html.jsx index 90237ca65f..096f485388 100644 --- a/packages/volto/src/helpers/Html/Html.jsx +++ b/packages/volto/src/helpers/Html/Html.jsx @@ -102,6 +102,12 @@ class Html extends Component { {head.meta.toComponent()} {head.link.toComponent()} {head.script.toComponent()} + + {config.settings.cssLayers && ( + // Load the CSS layers from config, if any + + )} + {head.style.toComponent()}