diff --git a/.eslintignore b/.eslintignore
index 7df5c928c1..528a96d8de 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -4,3 +4,6 @@ packages/volto
packages/volto-guillotina
!.*
dist
+packages/registry/lib
+packages/registry/docs
+apps/rr7/.react-router
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 0e5d22de5d..215aafba63 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -56,6 +56,7 @@ const config = {
],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/ban-ts-comment': 0,
},
},
{
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000000..24ab7677d5
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,15 @@
+- [ ] I signed and returned the [Plone Contributor Agreement](https://plone.org/foundation/contributors-agreement), and received and accepted an invitation to join a team in the Plone GitHub organization.
+- [ ] I verified there aren't other open [pull requests](https://github.com/plone/volto/pulls) for the same change.
+- [ ] I followed the guidelines in [Contributing to Volto](https://6.docs.plone.org/volto/contributing/index.html).
+- [ ] I succesfully ran [code linting checks](https://6.docs.plone.org/volto/contributing/linting.html) on my changes locally.
+- [ ] I succesfully ran [unit tests](https://6.docs.plone.org/volto/contributing/testing.html) on my changes locally.
+- [ ] I succesfully ran [acceptance tests](https://6.docs.plone.org/volto/contributing/acceptance-tests.html) on my changes locally.
+- [ ] If needed, I added new tests for my changes.
+- [ ] If needed, I added [documentation](https://6.docs.plone.org/volto/contributing/documentation.html#narrative-documentation) for my changes, either in the Storybook or narrative documentation.
+- [ ] I included a [change log entry](https://6.docs.plone.org/contributing/index.html#contributing-change-log-label) in my commits.
+
+-----
+
+If your pull request closes an open issue, include the exact text below, immediately followed by the issue number. When your pull request gets merged, then that issue will close automatically.
+
+Closes #
diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml
index 6d93708898..82480849c7 100644
--- a/.github/workflows/acceptance.yml
+++ b/.github/workflows/acceptance.yml
@@ -9,7 +9,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -57,7 +57,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -105,7 +105,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -153,7 +153,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -201,7 +201,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -249,7 +249,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -296,7 +296,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
+ node-version: [22.x]
steps:
- uses: actions/checkout@v4
@@ -344,7 +344,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
+ node-version: [22.x]
steps:
- uses: actions/checkout@v4
@@ -392,7 +392,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
+ node-version: [22.x]
steps:
- uses: actions/checkout@v4
@@ -440,7 +440,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
+ node-version: [22.x]
# python-version: [3.7]
steps:
- uses: actions/checkout@v4
@@ -489,7 +489,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
env:
generator-directory: ./packages/generator-volto
project-directory: ./my-volto-app
@@ -521,6 +521,9 @@ jobs:
- name: Install yalc
run: npm -g install yalc
+ - name: Build dependencies
+ run: make build-deps
+
- name: Install a yalc'ed version of the current Volto in the project - publish
run: |
yalc publish packages/types
@@ -597,7 +600,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
+ node-version: [22.x]
steps:
- uses: actions/checkout@v4
@@ -647,7 +650,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
+ node-version: [22.x]
steps:
- uses: actions/checkout@v4
diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml
index 47fa637159..418c2c4f18 100644
--- a/.github/workflows/changelog.yml
+++ b/.github/workflows/changelog.yml
@@ -6,7 +6,7 @@ on:
- main
env:
- node-version: 20.x
+ node-version: 22.x
jobs:
towncrier:
diff --git a/.github/workflows/code-analysis.yml b/.github/workflows/code-analysis.yml
index b0519c45da..3878529afb 100644
--- a/.github/workflows/code-analysis.yml
+++ b/.github/workflows/code-analysis.yml
@@ -2,7 +2,7 @@ name: Code Analysis Check
on: [push, pull_request]
env:
- node-version: 20.x
+ node-version: 22.x
jobs:
prettier:
diff --git a/.github/workflows/deployment_tests.yml b/.github/workflows/deployment_tests.yml
index 5f22b40b67..9f6edc6e22 100644
--- a/.github/workflows/deployment_tests.yml
+++ b/.github/workflows/deployment_tests.yml
@@ -2,7 +2,7 @@ name: Deployment Tests
on: [push, pull_request]
env:
- node-version: 20.x
+ node-version: 22.x
jobs:
vitessr:
diff --git a/.github/workflows/docs-rtd-pr-preview.yml b/.github/workflows/docs-rtd-pr-preview.yml
index 83d7e72494..a123d8ef75 100644
--- a/.github/workflows/docs-rtd-pr-preview.yml
+++ b/.github/workflows/docs-rtd-pr-preview.yml
@@ -10,6 +10,7 @@ on:
- "docs/source/**"
- .readthedocs.yaml
- requirements-docs.txt
+ - "packages/registry/docs/**"
permissions:
pull-requests: write
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 75da9c3140..0c868b5779 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -10,6 +10,9 @@ on:
paths:
- 'docs/**'
- 'styles/**'
+ - '.github/workflows/docs.yml'
+ - 'requirements-docs.txt'
+ - '.vale.ini'
jobs:
docs:
@@ -32,9 +35,7 @@ jobs:
run: pip install virtualenv
- name: pip install requirements
- run: |
- pip install -r requirements-docs.txt
- sudo snap install --edge vale
+ run: pip install -r requirements-docs.txt
- name: Check for broken links
run: make docs-linkcheckbroken
@@ -43,7 +44,4 @@ jobs:
run: make docs-html
- name: Run vale
- run: |
- git clone https://github.com/errata-ai/Microsoft.git
- cp -r ./Microsoft/Microsoft ./styles
- vale --no-exit ./docs
+ run: make docs-vale VALEOPTS=--no-exit
diff --git a/.github/workflows/readme-link-check.yml b/.github/workflows/readme-link-check.yml
index 7f53b65824..42c96e1455 100644
--- a/.github/workflows/readme-link-check.yml
+++ b/.github/workflows/readme-link-check.yml
@@ -20,4 +20,4 @@ jobs:
- name: Check links in README.md with awesome_bot
run: |
gem install awesome_bot
- awesome_bot --request-delay 1 --allow-dupe --white-list http://localhost:8080/Plone,http://localhost:3000,https://github.com/kitconcept/volto-blocks-grid.git,https://my-server-DNS-name.tld/api --files PACKAGES.md,README.md,packages/blocks/README.md,packages/client/README.md,packages/components/README.md,packages/generator-volto/README.md,packages/registry/README.md,packages/scripts/README.md,packages/tsconfig/README.md,packages/types/README.md,packages/volto-slate/README.md,apps/nextjs/README.md,apps/remix/README.md,apps/vite-ssr/README.md
+ awesome_bot --request-delay 1 --allow-dupe --white-list http://localhost:8080/Plone,http://localhost:8080,http://localhost:3000,https://github.com/kitconcept/volto-blocks-grid.git,https://my-server-DNS-name.tld/api --files PACKAGES.md,README.md,packages/blocks/README.md,packages/client/README.md,packages/components/README.md,packages/generator-volto/README.md,packages/registry/README.md,packages/scripts/README.md,packages/tsconfig/README.md,packages/types/README.md,packages/volto-slate/README.md,apps/nextjs/README.md,apps/remix/README.md,apps/vite-ssr/README.md
diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml
index b479cc7fb1..138349a40b 100644
--- a/.github/workflows/unit.yml
+++ b/.github/workflows/unit.yml
@@ -2,7 +2,7 @@ name: Unit Tests
on: [push, pull_request]
env:
- node-version: 20.x
+ node-version: 22.x
jobs:
volto:
@@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -40,7 +40,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -77,16 +77,14 @@ jobs:
runs-on: ubuntu-latest
strategy:
fail-fast: false
- matrix:
- node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v4
# node setup
- - name: Use Node.js ${{ matrix.node-version }}
+ - name: Use Node.js ${{ env.node-version }}
uses: actions/setup-node@v4
with:
- node-version: ${{ matrix.node-version }}
+ node-version: ${{ env.node-version }}
- name: Enable corepack
run: corepack enable
@@ -152,16 +150,14 @@ jobs:
runs-on: ubuntu-latest
strategy:
fail-fast: false
- matrix:
- node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v4
# node setup
- - name: Use Node.js ${{ matrix.node-version }}
+ - name: Use Node.js ${{ env.node-version }}
uses: actions/setup-node@v4
with:
- node-version: ${{ matrix.node-version }}
+ node-version: ${{ env.node-version }}
- name: Enable corepack
run: corepack enable
diff --git a/.gitignore b/.gitignore
index 99907cc0fd..bf0baa3966 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,6 +72,4 @@ docs/_build/
/.tool-versions
docs/source/news
-.turbo
-.parcel-cache/
tsconfig.tsbuildinfo
diff --git a/.npmrc b/.npmrc
index 71c684383d..bc0f657128 100644
--- a/.npmrc
+++ b/.npmrc
@@ -3,4 +3,3 @@ public-hoist-pattern[]=*prettier*
public-hoist-pattern[]=*stylelint*
public-hoist-pattern[]=*cypress*
public-hoist-pattern[]=*process*
-public-hoist-pattern[]=*parcel*
diff --git a/.prettierignore b/.prettierignore
index cf39cfcd94..1749ca9b36 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -5,6 +5,7 @@
styles/rules/*
node_modules
packages/volto/types/*
+packages/registry/docs/*
storybook-static
apps/vite-ssr/src/routeTree.gen.ts
apps/vite/src/routeTree.gen.ts
diff --git a/Makefile b/Makefile
index 568b297d80..72656906b6 100644
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,7 @@ include variables.mk
# Sphinx variables
# You can set these variables from the command line.
SPHINXOPTS ?=
+VALEOPTS ?=
# Internal variables.
SPHINXBUILD = "$(realpath bin/sphinx-build)"
SPHINXAUTOBUILD = "$(realpath bin/sphinx-autobuild)"
@@ -66,7 +67,7 @@ test: ## Run unit tests
.PHONY: clean
clean: ## Clean development environment
rm -rf node_modules
- find ./packages -name node_modules -exec rm -rf {} \;
+ find ./packages -name node_modules -not -path "./packages/volto/__tests__/*" -exec rm -rf {} \;
.PHONY: install
install: ## Set up development environment
@@ -120,7 +121,7 @@ docs-linkcheckbroken: bin/python docs-news ## Run linkcheck and show only broke
.PHONY: docs-vale
docs-vale: bin/python docs-news ## Install (once) and run Vale style, grammar, and spell checks
bin/vale sync
- bin/vale --no-wrap $(VALEFILES)
+ bin/vale --no-wrap $(VALEOPTS) $(VALEFILES)
@echo
@echo "Vale is finished; look for any errors in the above output."
@@ -147,6 +148,10 @@ packages/components/dist: $(shell find packages/components/src -type f)
.PHONY: build-deps
build-deps: packages/registry/dist ## Build dependencies
+.PHONY: i18n
+i18n: ## Converts your po files into json to translate volto frontend
+ $(MAKE) -C "./packages/volto/" i18n
+
## Storybook
.PHONY: storybook-start
diff --git a/PACKAGES.md b/PACKAGES.md
index 12193c73fe..5f72ef477c 100644
--- a/PACKAGES.md
+++ b/PACKAGES.md
@@ -2,24 +2,44 @@
This document describes the packages that come with Volto, the default frontend for Plone 6.
+These packages are part of Plone's API-first story.
+Most of them are experimental and are marked in their respective `README` files.
+Plone 6.1.x (Volto 18) depends on:
+- `@plone/registry`
+- `@plone/scripts`
+- `@plone/volto-slate`
+
+and as a development dependency:
+- `@plone/types`
+
+Plone 6.0.x (Volto 17 and below) does not use any of them.
+
+These packages are expected to be used and become part of Plone 7.
+Some of them might become part of Plone 6.1.x minor versions.
+
+The packages are divided into three categories or types:
+
+- core
+- utilities
+- add-ons
+
## `@plone/types`
Plone types is a special development package.
It contains the Plone typings for TypeScript.
-It's considered a core package, and it's the only package that the other core packages can rely on as
-a `devDependency` in your project configuration.
+It's considered a core package, and it's the only package that the other core packages can rely on as a `devDependency` in your project configuration.
This package contains `.d.ts` typing definitions, curated by hand.
-Due to the nature of this package, it does not need bundling.
-It's published "as is", so you can import the type definitions from anywhere in your code.
+Due to the nature of this package, it does not need to be built nor bundled.
+It is published "as is", so you can import the type definitions from anywhere in your code.
## Core packages
-- `@plone/registry`
- `@plone/client`
- `@plone/components`
+- `@plone/registry`
### Rules
@@ -28,26 +48,38 @@ Core packages must not depend on any other `@plone/*` package, with only one exc
They must be published and bundled in a traditional (transpiled) way.
The bundle of these packages must work on both CommonJS and ECMAScript Module (ESM) environments.
-## Feature packages
-
-- `@plone/contents`
+## Utilities packages
-## Utility packages
-
-- `@plone/blocks`
+- `@plone/providers`
- `@plone/helpers`
-- `@plone/drivers`
-- `@plone/rsc`
### Rules
Utility packages can depend on core packages and other utility packages.
-They must be published in a traditional way, bundled.
+They must be published in the traditional way, as a bundle.
This bundle must work on both CommonJS and ESM environments.
+## Add-on packages
+
+- `@plone/blocks`
+- `@plone/slots`
+- `@plone/theming`
+- `@plone/contents`
+
+
+### Rules
+
+Add-on or feature packages, can depend on any other package.
+You must distribute them as source code, and not transpile them.
+They must provide a default configuration registry loader as the default main entry point export.
+Unlike Volto add-ons, do *NOT* place the code in the `src` folder.
+If you do not transpile the package, the direct resolution must work out of the box, where both the bundler and TypeScript resolution are direct.
+They must be loadable as any other add-on, and contain an add-on registry compatible `install`-able default export.
+
+
## Development utility packages
These are packages that are not bundled, and they are used in conjunction with Volto core or Volto projects.
@@ -55,7 +87,7 @@ They contain utilities that are useful for the development of a Volto project.
Some of them are released:
- `@plone/scripts`
-- `@plone/generator-volto`
+- `@plone/generator-volto` (deprecated)
Some of them are used by the build, and separated in packages for convenience.
diff --git a/RELEASING.md b/RELEASING.md
index 55a6def6e7..44e2dac6a0 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -44,13 +44,15 @@ The release process calls `towncrier`.
It is a Python library that uses the Python utility `pipx`.
This utility allows you to call and execute Python modules without installing them as a prerequisite in your system.
It works similar to the NodeJS `npx` utility.
-On macOS, you can install `pipx` into your system:
+
+Install {term}`pipx` for your active Python, and ensure it is on your `$PATH`.
+Carefully read the console output for further instructions, if needed.
```shell
-brew install pipx
+python3 -m pip install pipx
+pipx ensurepath
```
-Or follow detailed instructions in the `pipx` documentation for [Installation](https://pypa.github.io/pipx/installation/).
## Running the release process
diff --git a/apps/nextjs/.eslintrc.js b/apps/nextjs/.eslintrc.js
deleted file mode 100644
index d0e37670c7..0000000000
--- a/apps/nextjs/.eslintrc.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/** @type {import('eslint').Linter.Config} */
-module.exports = {
- extends: 'next/core-web-vitals',
- ignorePatterns: ['.next/**', 'dist/**', 'node_modules/**'],
- settings: {
- next: {
- rootDir: 'apps/nextjs/',
- },
- },
-};
diff --git a/apps/nextjs/.eslintrc.json b/apps/nextjs/.eslintrc.json
new file mode 100644
index 0000000000..035e32e888
--- /dev/null
+++ b/apps/nextjs/.eslintrc.json
@@ -0,0 +1,9 @@
+{
+ "extends": ["next/core-web-vitals", "next/typescript"],
+ "ignorePatterns": [".next/**", "dist/**", "node_modules/**"],
+ "settings": {
+ "next": {
+ "rootDir": "apps/nextjs/"
+ }
+ }
+}
diff --git a/apps/nextjs/.gitignore b/apps/nextjs/.gitignore
index e5671ba458..29b159ef05 100644
--- a/apps/nextjs/.gitignore
+++ b/apps/nextjs/.gitignore
@@ -4,6 +4,7 @@
/node_modules
/.pnp
.pnp.js
+.yarn/install-state.gz
# testing
/coverage
diff --git a/apps/nextjs/LICENSE b/apps/nextjs/LICENSE
index c0af2b1b65..f6a5160bd5 100644
--- a/apps/nextjs/LICENSE
+++ b/apps/nextjs/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2023 Plone Foundation
+Copyright (c) 2024 Plone Foundation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/apps/nextjs/README.md b/apps/nextjs/README.md
index 88a23215b6..4660a9939a 100644
--- a/apps/nextjs/README.md
+++ b/apps/nextjs/README.md
@@ -1,10 +1,16 @@
# Plone on Next.js
-This is a proof of concept of a [Next.js](https://nextjs.org) app, using the app router and the `@plone/client` and `@plone/components` library. This is intended to serve as both a playground for the development of both packages and as demo of Plone using Next.js.
+This is a proof of concept of a [Next.js](https://nextjs.org) app, using the app router and the `@plone/client` and `@plone/components` library.
+This is intended to serve as both a playground for the development of both packages and as a demo of Plone using Next.js.
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
## Development
-To start, from the root of the monorepo:
+To start, from the root of the monorepo, issue the following commands in a shell session.
```shell
pnpm install
@@ -20,65 +26,93 @@ make backend-docker-start
## Deployment at Vercel
-
-We introduce an environment variable `API_SERVER_URL`.
-You need to create this environment variable in the Vercel deployment's control panel, specifying the URL where your backend API server is deployed, and the route where the API is located, as shown.
+For deploying your app at Vercel, you need to create the environment variable `API_SERVER_URL` in Vercel's deployment control panel, specifying the URL where your backend API server is deployed, and the route where the API is located, as shown.
```shell
API_SERVER_URL=https://my-server-DNS-name.tld/api
```
+For production deployments, you will need to force the deployment URL, otherwise you will have issues with CORS.
+To do so, set another environment variable for the production URL, `NEXT_PRODUCTION_URL`.
+This URL needs to be scheme-less, without `http` or `https`, and consist only of the domain name:
+
+```shell
+NEXT_PRODUCTION_URL=my-nextjs-production-DNS-name.tld
+```
+
### Application rewrite configuragtion
-To avoid issues with CORS and maintain the server counterpart private, our Next.js app should have a rewrite, configured as follows:
+To avoid issues with CORS and maintain the server counterpart private, your Next.js app should have a rewrite, configured as follows:
```jsx
const nextConfig = {
// Rewrite to the backend to avoid CORS
async rewrites() {
- const apiServerURL =
- process.env.API_SERVER_URL ||
- 'http://localhost:8080/Plone/%2B%2Bapi%2B%2B';
+ let apiServerURL, vhmRewriteRule;
+ if (
+ process.env.API_SERVER_URL &&
+ (process.env.NEXT_PRODUCTION_URL || process.env.NEXT_PUBLIC_VERCEL_URL)
+ ) {
+ // We are in Vercel
+ apiServerURL = process.env.API_SERVER_URL;
+ vhmRewriteRule = `/VirtualHostBase/https/${
+ process.env.NEXT_PRODUCTION_URL
+ ? // We are in the production deployment
+ process.env.NEXT_PRODUCTION_URL
+ : // We are in the preview deployment
+ process.env.NEXT_PUBLIC_VERCEL_URL
+ }%3A443/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot`;
+ } else if (process.env.API_SERVER_URL) {
+ // We are in development
+ apiServerURL = process.env.API_SERVER_URL;
+ vhmRewriteRule =
+ '/VirtualHostBase/http/localhost%3A3000/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot';
+ } else {
+ // We are in development and the API_SERVER_URL is not set, so we use a local backend
+ apiServerURL = 'http://localhost:8080';
+ vhmRewriteRule =
+ '/VirtualHostBase/http/localhost%3A3000/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot';
+ }
return [
{
source: '/\\+\\+api\\+\\+/:slug*',
destination:
- `${apiServerURL}/VirtualHostBase/https/${process.env.NEXT_PUBLIC_VERCEL_URL}%3A443/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot/:slug*`,
+ `${apiServerURL}${vhmRewriteRule}/:slug*`,
},
];
},
};
```
-Plone Client uses the `++api++` prefix as default, so we should create a redirect in our app pointing to the API server, but using Plone's traditional virtual host management configuration.
+Plone Client uses the `++api++` prefix as default, so you should create a redirect in your app pointing to the API server, but using Plone's traditional virtual host management configuration.
-Next.js rewrites are picky on the `destination` field, because its rewrite library does not support URLs with regular expression operators.
-Therefore, we can't use the usual `++api++` route for the rewrite.
-This will allow us to infer the current server URL—even in deployed branches and pull requests—without touching the rewrite rules.
-We will fallback to configure a `api` route in our reverse proxy of choice.
+Next.js rewrites are picky with the `destination` field, because its rewrite library does not support URLs with regular expression operators.
+Therefore, you can't use the usual `++api++` route for the rewrite.
+This will allow you to infer the current server URL—even in deployed branches and pull requests—without touching the rewrite rules.
+You will fallback to configure a `api` route in your reverse proxy of choice.
### Plone backend
You have to deploy the Plone backend elsewhere, since Vercel is serverless oriented.
-We need to set up the rewrite rule in Next.js's `rewrite` feature as shown in the previous section.
+You need to set up the rewrite rule in Next.js's `rewrite` feature as shown in the previous section.
-We will fallback to configure an `api` route in our reverse proxy of choice.
+You will fallback to configure an `api` route in your reverse proxy of choice.
-For example, if we use `traefik`:
+For example, if you use `traefik`:
```yaml
- ## VHM rewrite /api/ (Plone Next.js)
- - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.regex=^/api($$|/.*)"
- ## We remove the incoming /api and just use the path
- - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.replacement=$$1"
-
- ## /api router
- - traefik.http.routers.rt-backend-api.rule=Host(`my_server_DNS_name`) && PathPrefix(`/api`)
- - traefik.http.routers.rt-backend-api.entrypoints=https
- - traefik.http.routers.rt-backend-api.tls=true
- - traefik.http.routers.rt-backend-api.service=svc-backend
- - traefik.http.routers.rt-backend-api.middlewares=gzip,mw-backend-vhm-api
+## VHM rewrite /api/ (Plone Next.js)
+- "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.regex=^/api($$|/.*)"
+## We remove the incoming /api and just use the path
+- "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.replacement=$$1"
+
+## /api router
+- traefik.http.routers.rt-backend-api.rule=Host(`my_server_DNS_name`) && PathPrefix(`/api`)
+- traefik.http.routers.rt-backend-api.entrypoints=https
+- traefik.http.routers.rt-backend-api.tls=true
+- traefik.http.routers.rt-backend-api.service=svc-backend
+- traefik.http.routers.rt-backend-api.middlewares=gzip,mw-backend-vhm-api
```
## About this app
@@ -95,16 +129,18 @@ pnpm dev
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+You can start editing the page by modifying `app/page.tsx`.
+The page auto-updates as you edit the file.
## Learn More
To learn more about Next.js, take a look at the following resources:
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and its API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/).
+Your feedback and contributions are welcome!
## Deploy on Vercel
diff --git a/apps/nextjs/next.config.js b/apps/nextjs/next.config.mjs
similarity index 53%
rename from apps/nextjs/next.config.js
rename to apps/nextjs/next.config.mjs
index ae8d5d1813..2bc72a90f0 100644
--- a/apps/nextjs/next.config.js
+++ b/apps/nextjs/next.config.mjs
@@ -1,7 +1,10 @@
-const path = require('path');
+// import path from 'path';
/** @type {import('next').NextConfig} */
const nextConfig = {
+ typescript: {
+ ignoreBuildErrors: true,
+ },
// sassOptions: {
// includePaths: [path.join(__dirname, 'src/lib/components/src/styles')],
// },
@@ -18,17 +21,26 @@ const nextConfig = {
// Rewrite to the backend to avoid CORS
async rewrites() {
let apiServerURL, vhmRewriteRule;
- if (process.env.API_SERVER_URL) {
- apiServerURL = process.env.API_SERVER_URL;
- vhmRewriteRule = `/VirtualHostBase/https/${process.env.NEXT_PUBLIC_VERCEL_URL}%3A443/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot`;
- } else if (
+ if (
process.env.API_SERVER_URL &&
- !process.env.NEXT_PUBLIC_VERCEL_URL
+ (process.env.NEXT_PRODUCTION_URL || process.env.NEXT_PUBLIC_VERCEL_URL)
) {
- throw new Error(
- 'API_SERVER_URL set and NEXT_PUBLIC_VERCEL_URL not present.',
- );
+ // We are in Vercel
+ apiServerURL = process.env.API_SERVER_URL;
+ vhmRewriteRule = `/VirtualHostBase/https/${
+ process.env.NEXT_PRODUCTION_URL
+ ? // We are in the production deployment
+ process.env.NEXT_PRODUCTION_URL
+ : // We are in the preview deployment
+ process.env.NEXT_PUBLIC_VERCEL_URL
+ }%3A443/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot`;
+ } else if (process.env.API_SERVER_URL) {
+ // We are in development
+ apiServerURL = process.env.API_SERVER_URL;
+ vhmRewriteRule =
+ '/VirtualHostBase/http/localhost%3A3000/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot';
} else {
+ // We are in development and the API_SERVER_URL is not set, so we use a local backend
apiServerURL = 'http://localhost:8080';
vhmRewriteRule =
'/VirtualHostBase/http/localhost%3A3000/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot';
@@ -46,4 +58,4 @@ const nextConfig = {
},
};
-module.exports = nextConfig;
+export default nextConfig;
diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json
index 6d4a71dc0f..8b935ff8bb 100644
--- a/apps/nextjs/package.json
+++ b/apps/nextjs/package.json
@@ -13,21 +13,22 @@
"@plone/blocks": "workspace: *",
"@plone/client": "workspace: *",
"@plone/components": "workspace: *",
- "@plone/registry": "workspace: *",
"@plone/providers": "workspace: *",
- "@tanstack/react-query": "^5.37.1",
- "next": "14.2.2",
+ "@plone/registry": "workspace: *",
+ "@tanstack/react-query": "^5.59.0",
+ "next": "14.2.14",
"react": "^18",
- "react-aria-components": "^1.1.1",
+ "react-aria-components": "^1.4.0",
"react-dom": "^18"
},
"devDependencies": {
- "@tanstack/react-query-devtools": "^5.37.1",
+ "@plone/types": "workspace: *",
+ "@tanstack/react-query-devtools": "^5.59.0",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
- "eslint-config-next": "14.2.2",
- "typescript": "^5.4.5"
+ "eslint-config-next": "14.2.14",
+ "typescript": "^5.6.3"
}
}
diff --git a/apps/nextjs/src/app/Providers.tsx b/apps/nextjs/src/app/Providers.tsx
index 27b47036f6..e1c0fa9295 100644
--- a/apps/nextjs/src/app/Providers.tsx
+++ b/apps/nextjs/src/app/Providers.tsx
@@ -1,15 +1,32 @@
'use client';
import React from 'react';
-import { useRouter } from 'next/navigation';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { PloneClientProvider } from '@plone/providers';
+import {
+ useRouter,
+ usePathname,
+ useSearchParams,
+ useParams,
+} from 'next/navigation';
+import { QueryClient } from '@tanstack/react-query';
+import { PloneProvider } from '@plone/providers';
import PloneClient from '@plone/client';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
-import { RouterProvider } from 'react-aria-components';
-import { FlattenToAppURLProvider } from '@plone/components';
import { flattenToAppURL } from './utils';
import config from './config';
+// Custom hook to unify the location object between NextJS and Plone
+function useLocation() {
+ const pathname = usePathname();
+ const search = useSearchParams();
+
+ return {
+ pathname,
+ search,
+ searchStr: '',
+ hash: (typeof window !== 'undefined' && window.location.hash) || '',
+ href: (typeof window !== 'undefined' && window.location.href) || '',
+ };
+}
+
const Providers: React.FC<{
children?: React.ReactNode;
}> = ({ children }) => {
@@ -36,19 +53,25 @@ const Providers: React.FC<{
}),
);
- let router = useRouter();
+ const router = useRouter();
return (
-
-
-
-
- {children}
-
-
-
-
-
+ {
+ router.push(to);
+ }}
+ useParams={useParams}
+ useHref={(to) => flattenToAppURL(to)}
+ flattenToAppURL={flattenToAppURL}
+ >
+ {children}
+
+
);
};
diff --git a/apps/nextjs/src/app/config.ts b/apps/nextjs/src/app/config.ts
index 47bad754da..8d0b440630 100644
--- a/apps/nextjs/src/app/config.ts
+++ b/apps/nextjs/src/app/config.ts
@@ -1,12 +1,30 @@
-const settings = {
- apiPath: process.env.NEXT_PUBLIC_VERCEL_URL
- ? // Vercel does not prepend the schema to the NEXT_PUBLIC_VERCEL_URL automatic env var
- `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`
- : 'http://localhost:3000',
-};
+import config from '@plone/registry';
+import type { ConfigType } from '@plone/registry';
+import { slate } from '@plone/blocks';
+import { blocksConfig } from '@plone/blocks';
-const config = {
- settings,
+const settings: Partial = {
+ slate,
};
+if (process.env.NEXT_PUBLIC_VERCEL_URL) {
+ // This app is at Vercel
+ if (process.env.NEXT_PRODUCTION_URL) {
+ // This app is in a production deployment, so set the apiPath to the production URL
+ settings.apiPath = process.env.NEXT_PRODUCTION_URL;
+ } else {
+ // This app is in a preview deployment, so set the apiPath to the Vercel URL
+ settings.apiPath = `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`;
+ }
+} else {
+ // This app is in development, so set the apiPath to localhost
+ settings.apiPath = 'http://localhost:3000/';
+}
+
+// @ts-expect-error Improve typings
+config.set('settings', settings);
+
+// @ts-expect-error Improve typings
+config.set('blocks', { blocksConfig });
+
export default config;
diff --git a/apps/nextjs/src/app/content.tsx b/apps/nextjs/src/app/content.tsx
index 92e757e7da..84e2b244b7 100644
--- a/apps/nextjs/src/app/content.tsx
+++ b/apps/nextjs/src/app/content.tsx
@@ -1,46 +1,30 @@
'use client';
-
import { useQuery } from '@tanstack/react-query';
import { usePathname } from 'next/navigation';
-import Link from 'next/link';
-import { flattenToAppURL } from './utils';
import { usePloneClient } from '@plone/providers';
-import { Breadcrumbs } from '@plone/components';
+import { Breadcrumbs, RenderBlocks } from '@plone/components';
+import config from '@plone/registry';
+
import '@plone/components/dist/basic.css';
export default function Content() {
const { getContentQuery } = usePloneClient();
const pathname = usePathname();
- const { data, isLoading } = useQuery(getContentQuery({ path: pathname }));
+ const { data } = useQuery(getContentQuery({ path: pathname }));
if (data) {
return (
);
}
diff --git a/apps/nextjs/src/app/layout.tsx b/apps/nextjs/src/app/layout.tsx
index 3c118fb9fa..8d254759a2 100644
--- a/apps/nextjs/src/app/layout.tsx
+++ b/apps/nextjs/src/app/layout.tsx
@@ -5,7 +5,7 @@ import Providers from './Providers';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
- title: 'NextJS-powered Plone',
+ title: 'Next.js app powered by Plone',
description: '',
};
diff --git a/apps/nextjs/src/middleware.ts b/apps/nextjs/src/middleware.ts
index 59c2d7b5d4..736d67530c 100644
--- a/apps/nextjs/src/middleware.ts
+++ b/apps/nextjs/src/middleware.ts
@@ -2,10 +2,7 @@ import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
- // Clone the request headers and set a new header `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers);
- // console.log(request.nextUrl.pathname);
- // requestHeaders.set('x-hello-from-middleware1', 'hello');
// You can also set request headers in NextResponse.rewrite
const response = NextResponse.next({
diff --git a/apps/nextjs/tsconfig.json b/apps/nextjs/tsconfig.json
index 33bcb0c4f4..4b7888fa43 100644
--- a/apps/nextjs/tsconfig.json
+++ b/apps/nextjs/tsconfig.json
@@ -1,38 +1,34 @@
{
- "compilerOptions": {
- "target": "es5",
- "lib": [
- "dom",
- "dom.iterable",
- "esnext"
- ],
- "allowJs": true,
- "skipLibCheck": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "noEmit": true,
- "esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "bundler",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "preserve",
- "incremental": true,
- "plugins": [
- {
- "name": "next"
- }
- ]
- },
- "include": [
- "next-env.d.ts",
- "**/*.ts",
- "**/*.tsx",
- "**/*.js",
- "**/*.jx",
- ".next/types/**/*.ts"
+ "compilerOptions": {
+ "forceConsistentCasingInFileNames": true,
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
],
- "exclude": [
- "node_modules",
- ]
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ "**/*.js",
+ "**/*.jsx",
+ ".next/types/**/*.ts"
+ ],
+ "exclude": ["node_modules"]
}
diff --git a/apps/plone/.editorconfig b/apps/plone/.editorconfig
deleted file mode 100644
index 6c43b88c57..0000000000
--- a/apps/plone/.editorconfig
+++ /dev/null
@@ -1,34 +0,0 @@
-# EditorConfig Configurtaion file, for more details see:
-# http://EditorConfig.org
-# EditorConfig is a convention description, that could be interpreted
-# by multiple editors to enforce common coding conventions for specific
-# file types
-
-# top-most EditorConfig file:
-# Will ignore other EditorConfig files in Home directory or upper tree level.
-root = true
-
-
-[*] # For All Files
-# Unix-style newlines with a newline ending every file
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
-# Set default charset
-charset = utf-8
-# Indent style default
-indent_style = space
-
-[*.{py,cfg,ini}]
-# 4 space indentation
-indent_size = 4
-
-[*.{html,dtml,pt,zpt,xml,zcml,js,jsx,json,less,css,yaml,yml}]
-# 2 space indentation
-indent_size = 2
-
-[{Makefile,.gitmodules}]
-# Tab indentation (no size specified, but view as 4 spaces)
-indent_style = tab
-indent_size = unset
-tab_width = unset
diff --git a/apps/plone/.eslintrc.js b/apps/plone/.eslintrc.js
deleted file mode 100644
index 6148e2e62f..0000000000
--- a/apps/plone/.eslintrc.js
+++ /dev/null
@@ -1,63 +0,0 @@
-const fs = require('fs');
-const projectRootPath = __dirname;
-const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry');
-
-let voltoPath = './node_modules/@plone/volto';
-
-let configFile;
-if (fs.existsSync(`${projectRootPath}/tsconfig.json`))
- configFile = `${projectRootPath}/tsconfig.json`;
-else if (fs.existsSync(`${projectRootPath}/jsconfig.json`))
- configFile = `${projectRootPath}/jsconfig.json`;
-
-if (configFile) {
- const jsConfig = require(configFile).compilerOptions;
- const pathsConfig = jsConfig.paths;
- if (pathsConfig['@plone/volto'])
- voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`;
-}
-
-const reg = new AddonConfigurationRegistry(__dirname);
-
-// Extends ESlint configuration for adding the aliases to `src` directories in Volto addons
-const addonAliases = Object.keys(reg.packages).map((o) => [
- o,
- reg.packages[o].modulePath,
-]);
-
-const addonExtenders = reg.getEslintExtenders().map((m) => require(m));
-
-const defaultConfig = {
- extends: `${voltoPath}/.eslintrc`,
- ignorePatterns: [
- // '.storybook/**/*',
- 'src/addons/**/node_modules',
- 'src/addons/**/cypress',
- 'src/addons/**/build',
- '!src/addons/volto-volto-project',
- ],
- settings: {
- 'import/resolver': {
- alias: {
- map: [
- ['@plone/volto', '@plone/volto/src'],
- ['@plone/volto-slate', '@plone/volto-slate/src'],
- ...addonAliases,
- ['@root', `${__dirname}/src`],
- ['~', `${__dirname}/src`],
- ],
- extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
- },
- 'babel-plugin-root-import': {
- rootPathSuffix: 'src',
- },
- },
- },
-};
-
-const config = addonExtenders.reduce(
- (acc, extender) => extender.modify(acc),
- defaultConfig,
-);
-
-module.exports = config;
diff --git a/apps/plone/.gitignore b/apps/plone/.gitignore
deleted file mode 100644
index a8b3ab728b..0000000000
--- a/apps/plone/.gitignore
+++ /dev/null
@@ -1,82 +0,0 @@
-.vscode/
-logs
-*.log
-npm-debug.log*
-.DS_Store
-
-coverage
-
-# Node
-node_modules
-coverage
-jsdoc
-webpack-assets.json
-webpack-stats.json
-npm-debug.log
-dist
-junit.xml
-eslint.xml
-yarn-error.log
-build
-
-# Other
-.DS_Store
-.idea
-lighthouse-report.html
-.vscode/
-.#*
-*~
-
-# Python
-/bin/
-/lib/
-.Python
-include
-pip-selfcheck.json
-pyvenv.cfg
-share
-
-# locales
-locales/*.json
-
-# Tests
-/tests/bin
-/tests/develop-eggs
-/tests/parts
-/tests/.installed.cfg
-*.pyc
-geckodriver.log
-log.html
-output.xml
-report.html
-selenium-screenshot-*.png
-/selenium/
-cypress/videos/
-cypress/screenshots
-
-# Local environment setup
-.env
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-# generic
-data
-omelette
-
-# build
-public/critical.css
-src/addons/*
-/cache
-
-# yarn 3
-.pnp.*
-.yarn/*
-!.yarn/patches
-!.yarn/plugins
-!.yarn/releases
-!.yarn/sdks
-!.yarn/versions
-
-!src/addons/volto-volto-project
diff --git a/apps/plone/.prettierignore b/apps/plone/.prettierignore
deleted file mode 100644
index 84cca9725b..0000000000
--- a/apps/plone/.prettierignore
+++ /dev/null
@@ -1,3 +0,0 @@
-src/**/CHANGELOG.md
-src/**/README.md
-!src/addons/volto-volto-project
\ No newline at end of file
diff --git a/apps/plone/.storybook/main.js b/apps/plone/.storybook/main.js
deleted file mode 100644
index 0455b194c9..0000000000
--- a/apps/plone/.storybook/main.js
+++ /dev/null
@@ -1,177 +0,0 @@
-const webpack = require('webpack');
-const fs = require('fs');
-const path = require('path');
-
-const projectRootPath = path.resolve('.');
-const lessPlugin = require('@plone/volto/webpack-plugins/webpack-less-plugin');
-const scssPlugin = require('razzle-plugin-scss');
-
-const createConfig = require('../node_modules/razzle/config/createConfigAsync.js');
-const razzleConfig = require(path.join(projectRootPath, 'razzle.config.js'));
-
-const SVGLOADER = {
- test: /icons\/.*\.svg$/,
- use: [
- {
- loader: 'svg-loader',
- },
- {
- loader: 'svgo-loader',
- options: {
- plugins: [
- {
- name: 'preset-default',
- params: {
- overrides: {
- convertPathData: false,
- removeViewBox: false,
- },
- },
- },
- 'removeTitle',
- 'removeUselessStrokeAndFill',
- ],
- },
- },
- ],
-};
-
-const defaultRazzleOptions = {
- verbose: false,
- debug: {},
- buildType: 'iso',
- cssPrefix: 'static/css',
- jsPrefix: 'static/js',
- enableSourceMaps: true,
- enableReactRefresh: true,
- enableTargetBabelrc: false,
- enableBabelCache: true,
- forceRuntimeEnvVars: [],
- mediaPrefix: 'static/media',
- staticCssInDev: false,
- emitOnErrors: false,
- disableWebpackbar: false,
- browserslist: [
- '>1%',
- 'last 4 versions',
- 'Firefox ESR',
- 'not ie 11',
- 'not dead',
- ],
-};
-
-module.exports = {
- core: {
- builder: 'webpack5',
- },
- stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
- addons: [
- '@storybook/addon-links',
- '@storybook/addon-essentials',
- // '@storybook/preset-scss',
- ],
- webpackFinal: async (config, { configType }) => {
- // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
- // You can change the configuration based on that.
- // 'PRODUCTION' is used when building the static version of storybook.
-
- // Make whatever fine-grained changes you need
- let baseConfig;
- baseConfig = await createConfig(
- 'web',
- 'dev',
- {
- // clearConsole: false,
- modifyWebpackConfig: razzleConfig.modifyWebpackConfig,
- plugins: razzleConfig.plugins,
- },
- webpack,
- false,
- undefined,
- [],
- defaultRazzleOptions,
- );
- const AddonConfigurationRegistry = require('@plone/volto/addon-registry');
-
- const registry = new AddonConfigurationRegistry(projectRootPath);
-
- config = lessPlugin({ registry }).modifyWebpackConfig({
- env: { target: 'web', dev: 'dev' },
- webpackConfig: config,
- webpackObject: webpack,
- options: {},
- });
-
- config = scssPlugin.modifyWebpackConfig({
- env: { target: 'web', dev: 'dev' },
- webpackConfig: config,
- webpackObject: webpack,
- options: { razzleOptions: {} },
- });
-
- // Put the SVG loader on top and prevent the asset/resource rule
- // from processing the app's SVGs
- config.module.rules.unshift(SVGLOADER);
- const fileLoaderRule = config.module.rules.find((rule) =>
- rule.test.test('.svg'),
- );
- fileLoaderRule.exclude = /icons\/.*\.svg$/;
-
- config.plugins.unshift(
- new webpack.DefinePlugin({
- __DEVELOPMENT__: true,
- __CLIENT__: true,
- __SERVER__: false,
- }),
- );
-
- const resultConfig = {
- ...config,
- resolve: {
- ...config.resolve,
- alias: { ...config.resolve.alias, ...baseConfig.resolve.alias },
- fallback: { ...config.resolve.fallback, zlib: false },
- },
- };
-
- // Addons have to be loaded with babel
- const addonPaths = registry.addonNames.map((addon) =>
- fs.realpathSync(registry.packages[addon].modulePath),
- );
- resultConfig.module.rules[1].exclude = (input) =>
- // exclude every input from node_modules except from @plone/volto
- /node_modules\/(?!(@plone\/volto)\/)/.test(input) &&
- // If input is in an addon, DON'T exclude it
- !addonPaths.some((p) => input.includes(p));
-
- const addonExtenders = registry.getAddonExtenders().map((m) => require(m));
-
- const extendedConfig = addonExtenders.reduce(
- (acc, extender) =>
- extender.modify(acc, { target: 'web', dev: 'dev' }, config),
- resultConfig,
- );
-
- // Note: we don't actually support razzle plugins, which are also a feature
- // of the razzle.extend.js addons file. Those features are probably
- // provided in a different manner by Storybook plugins (for example scss
- // loaders).
-
- return extendedConfig;
- },
- babel: async (options) => {
- return {
- ...options,
- plugins: [
- ...options.plugins,
- [
- './node_modules/babel-plugin-root-import/build/index.js',
- {
- rootPathSuffix: './src',
- },
- ],
- ],
- // any extra options you want to set
- };
- },
-};
diff --git a/apps/plone/.storybook/manager.js b/apps/plone/.storybook/manager.js
deleted file mode 100644
index cda31aa1fe..0000000000
--- a/apps/plone/.storybook/manager.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { addons } from '@storybook/addons';
-
-addons.setConfig({
- isFullscreen: false,
- showNav: true,
- showPanel: true,
- panelPosition: 'bottom',
- sidebarAnimations: true,
- enableShortcuts: true,
- isToolshown: true,
- theme: undefined,
- selectedPanel: undefined,
- initialActive: 'sidebar',
- showRoots: true,
-});
diff --git a/apps/plone/.storybook/preview.js b/apps/plone/.storybook/preview.js
deleted file mode 100644
index bcbfccb212..0000000000
--- a/apps/plone/.storybook/preview.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import '@plone/volto/config'; // This is the bootstrap for the global config - client side
-import React from 'react';
-import { StaticRouter } from 'react-router-dom';
-import { IntlProvider } from 'react-intl';
-// eslint-disable-next-line import/no-unresolved
-import enMessages from '@root/../locales/en.json';
-
-import '@root/theme';
-
-export const parameters = {
- actions: { argTypesRegex: '^on[A-Z].*' },
- controls: {
- matchers: {
- color: /(background|color)$/i,
- date: /Date$/,
- },
- },
-};
-
-export const decorators = [
- (Story) => (
-
-
-
-
-
- ),
-];
diff --git a/apps/plone/Makefile b/apps/plone/Makefile
deleted file mode 100644
index 4a5a606c1f..0000000000
--- a/apps/plone/Makefile
+++ /dev/null
@@ -1,106 +0,0 @@
-### Defensive settings for make:
-# https://tech.davis-hansson.com/p/make/
-SHELL:=bash
-.ONESHELL:
-.SHELLFLAGS:=-eu -o pipefail -c
-.SILENT:
-.DELETE_ON_ERROR:
-MAKEFLAGS+=--warn-undefined-variables
-MAKEFLAGS+=--no-builtin-rules
-
-# Update the versions depending on your project requirements | Last Updated 2023-03-02
-DOCKER_IMAGE=plone/server-dev:6.0.6
-DOCKER_IMAGE_ACCEPTANCE=plone/server-acceptance:6.0.6
-KGS=
-NODEBIN = ./node_modules/.bin
-
-# Plone 5 legacy
-DOCKER_IMAGE5=plone/plone-backend:5.2.12
-KGS5=plone.restapi==8.43.3 plone.volto==4.1.0 plone.rest==3.0.1
-TESTING_ADDONS=plone.app.robotframework==2.0.0 plone.app.testing==7.0.0
-
-# Project settings
-
-DIR=$(shell basename $$(pwd))
-
-# Recipe snippets for reuse
-
-# We like colors
-# From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects
-RED=`tput setaf 1`
-GREEN=`tput setaf 2`
-RESET=`tput sgr0`
-YELLOW=`tput setaf 3`
-
-
-# Top-level targets
-.PHONY: all
-all: project
-
-.PHONY: help
-help: ## Show this help.
- @echo -e "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)"
-
-.PHONY: start-backend-docker
-start-backend-docker: ## Starts a Docker-based backend
- @echo "$(GREEN)==> Start Docker-based Plone Backend$(RESET)"
- docker run -it --rm --name=backend -p 8080:8080 -e SITE=Plone -e ADDONS='$(KGS)' $(DOCKER_IMAGE)
-
-.PHONY: install
-install: ## Install the frontend
- @echo "Install frontend"
- $(MAKE) omelette
- $(MAKE) preinstall
- yarn install
-
-.PHONY: preinstall
-preinstall: ## Preinstall task, checks if missdev (mrs-developer) is present and runs it
- if [ -f $$(pwd)/mrs.developer.json ]; then make develop; fi
-
-.PHONY: develop
-develop: ## Runs missdev in the local project (mrs.developer.json should be present)
- if [ -f $$(pwd)/jsconfig.json ]; then npx -p mrs-developer missdev --config=jsconfig.json --output=addons --fetch-https; fi
- if [ ! -f $$(pwd)/jsconfig.json ]; then npx -p mrs-developer missdev --output=addons --fetch-https; fi
-
-.PHONY: omelette
-omelette: ## Creates the omelette folder that contains a link to the installed version of Volto (a softlink pointing to node_modules/@plone/volto)
- if [ ! -d omelette ]; then ln -sf node_modules/@plone/volto omelette; fi
-
-.PHONY: patches
-patches:
- /bin/bash patches/patchit.sh > /dev/null 2>&1 ||true
-
-.PHONY: start-test-acceptance-server start-test-backend
-start-test-acceptance-server start-test-backend : ## Start Test Plone Backend
- @echo "$(GREEN)==> Start Test Plone Backend$(RESET)"
- docker run -i --rm -p 55001:55001 $(DOCKER_IMAGE_ACCEPTANCE)
- ## KGS in case you need a Plone 5.2 series (comment/remove above line):
- # docker run -i --rm -e ZSERVER_HOST=0.0.0.0 -e ZSERVER_PORT=55001 -p 55001:55001 -e ADDONS='$(KGS5) $(TESTING_ADDONS)' -e APPLY_PROFILES=plone.app.contenttypes:plone-content,plone.restapi:default,plone.volto:default-homepage -e CONFIGURE_PACKAGES=plone.app.contenttypes,plone.restapi,plone.volto,plone.volto.cors $(DOCKER_IMAGE5) ./bin/robot-server plone.app.robotframework.testing.VOLTO_ROBOT_TESTING
-
-.PHONY: start-test-acceptance-frontend
-start-test-acceptance-frontend: ## Start the Acceptance Frontend Fixture
- RAZZLE_API_PATH=http://127.0.0.1:55001/plone yarn build && yarn start:prod
-
-.PHONY: test-acceptance
-test-acceptance: ## Start Core Cypress Acceptance Tests
- $(NODEBIN)/cypress open
-
-.PHONY: test-acceptance-headless
-test-acceptance-headless: ## Start Core Cypress Acceptance Tests in headless mode
- $(NODEBIN)/cypress run
-
-.PHONY: full-test-acceptance
-full-test-acceptance: ## Runs Core Full Acceptance Testing in headless mode
- $(NODEBIN)/start-test "make start-test-acceptance-server" http-get://127.0.0.1:55001/plone "make start-test-acceptance-frontend" http://127.0.0.1:3000 "make test-acceptance-headless"
-
-.PHONY: test-acceptance
-test-acceptance-addon: ## Start Core Cypress Acceptance Tests for an addon
- $(NODEBIN)/cypress open -P $(ADDONPATH)
-
-.PHONY: test-acceptance-headless
-test-acceptance-addon-headless: ## Start Core Cypress Acceptance Tests for an addon in headless mode
- $(NODEBIN)/cypress run -P $(ADDONPATH)
-
-.PHONY: full-test-acceptance-addon
-full-test-acceptance-addon: ## Runs Core Full Acceptance Testing for an addon in headless mode
- $(NODEBIN)/start-test "make start-test-acceptance-server" http-get://127.0.0.1:55001/plone "make start-test-acceptance-frontend" http://127.0.0.1:3000 "make test-acceptance-addon-headless"
diff --git a/apps/plone/README.md b/apps/plone/README.md
deleted file mode 100644
index da034e8ad3..0000000000
--- a/apps/plone/README.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# README.md
-
-This README guides you for development with Volto and Plone.
-
-
-## Documentation
-
-[Volto Hands-On](https://training.plone.org/voltohandson/index.html) is a training on how to create your own website.
-
-
-## Quick Start
-
-Below is a list of useful commands.
-
-### `make install`
-
-Installs and checks out the `mrs-developer` directives (`make develop`), creates a shortcut to the Volto source code (`omelette` folder), then triggers the install of the frontend environment.
-
-
-### `yarn start`
-
-Runs the project in development mode.
-You can view your application at `http://localhost:3000`.
-
-The page will reload if you make edits.
-
-
-### `yarn build`
-
-Builds the app for production to the `build` folder.
-
-The build is minified and the filenames include the hashes.
-Your app is ready to be deployed!
-
-
-### `yarn start:prod`
-
-Runs the compiled app in production.
-
-You can view your application at `http://localhost:3000`.
-
-
-### `yarn test`
-
-Runs the test watcher (Jest) in an interactive mode.
-By default, runs tests related to files changed since the last commit.
-
-
-### `yarn i18n`
-
-Runs the test i18n runner, which extracts all the translation strings and generates the needed files.
-
-### mrs-developer
-
-[mrs-developer](https://github.com/collective/mrs-developer) is a great tool for developing multiple packages at the same time.
-
-mrs-developer should work with this project by running the configured shortcut script:
-
-```bash
-make develop
-```
-
-Volto's latest Razzle configuration will honor your `tsconfig.json` or `jsconfig.json` file for any customizations.
-
-In case you don't want (or can't) install mrs-developer globally, you can install it in this project by running:
-
-```bash
-yarn add -W mrs-developer
-```
-
-
-## Acceptance tests
-
-To run the project acceptance tests while developing using Cypress as the test runner, there are some `Makefile` commands in place in the repository root.
-
-Run them in order:
-
-- `start-test-acceptance-server`: Start the server fixture in Docker (previous build required).
-- `start-test-acceptance-frontend`: Start the Core Acceptance Frontend Fixture in development mode.
-- `test-acceptance`: Start the Core Cypress Acceptance Tests in development mode.
-- `full-test-acceptance`: Start the entire suite (backend + frontend + headless tests) using Cypress Acceptance Tests in headless (CI) mode.
diff --git a/apps/plone/babel.config.js b/apps/plone/babel.config.js
deleted file mode 100644
index a900a7555e..0000000000
--- a/apps/plone/babel.config.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('@plone/volto/babel');
diff --git a/apps/plone/cypress.config.js b/apps/plone/cypress.config.js
deleted file mode 100644
index 08d55e62ba..0000000000
--- a/apps/plone/cypress.config.js
+++ /dev/null
@@ -1,8 +0,0 @@
-const { defineConfig } = require('cypress');
-
-module.exports = defineConfig({
- e2e: {
- baseUrl: 'http://localhost:3000',
- specPattern: 'cypress/tests/**/*.cy.{js,jsx}',
- },
-});
diff --git a/apps/plone/cypress/fixtures/broccoli.jpg b/apps/plone/cypress/fixtures/broccoli.jpg
deleted file mode 100644
index 456706d662..0000000000
Binary files a/apps/plone/cypress/fixtures/broccoli.jpg and /dev/null differ
diff --git a/apps/plone/cypress/fixtures/example.json b/apps/plone/cypress/fixtures/example.json
deleted file mode 100644
index 02e4254378..0000000000
--- a/apps/plone/cypress/fixtures/example.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "Using fixtures to represent data",
- "email": "hello@cypress.io",
- "body": "Fixtures are a great way to mock data for responses to routes"
-}
diff --git a/apps/plone/cypress/fixtures/file.pdf b/apps/plone/cypress/fixtures/file.pdf
deleted file mode 100644
index ef9c798194..0000000000
Binary files a/apps/plone/cypress/fixtures/file.pdf and /dev/null differ
diff --git a/apps/plone/cypress/fixtures/halfdome2022.jpg b/apps/plone/cypress/fixtures/halfdome2022.jpg
deleted file mode 100644
index 58c9411ca2..0000000000
Binary files a/apps/plone/cypress/fixtures/halfdome2022.jpg and /dev/null differ
diff --git a/apps/plone/cypress/fixtures/image.png b/apps/plone/cypress/fixtures/image.png
deleted file mode 100644
index 4c109bab8d..0000000000
Binary files a/apps/plone/cypress/fixtures/image.png and /dev/null differ
diff --git a/apps/plone/cypress/plugins/index.js b/apps/plone/cypress/plugins/index.js
deleted file mode 100644
index dffed2532f..0000000000
--- a/apps/plone/cypress/plugins/index.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// ***********************************************************
-// This example plugins/index.js can be used to load plugins
-//
-// You can change the location of this file or turn off loading
-// the plugins file with the 'pluginsFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/plugins-guide
-// ***********************************************************
-
-// This function is called when a project is opened or re-opened (e.g. due to
-// the project's config changing)
-
-module.exports = (on, config) => {
- // `on` is used to hook into various events Cypress emits
- // `config` is the resolved Cypress config
-};
diff --git a/apps/plone/cypress/support/commands.js b/apps/plone/cypress/support/commands.js
deleted file mode 100644
index 03fe48c752..0000000000
--- a/apps/plone/cypress/support/commands.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import '@plone/volto/cypress/add-commands';
-
-// Set PLONE_SITE_ID and PLONE_API_URL as cypress environment variables
-// if testing without using localhost or a site id of `plone`.
-
-// --- CUSTOM COMMANDS -------------------------------------------------------------
-Cypress.Commands.add('custom_command', () => {
- // Custom code here...
-});
diff --git a/apps/plone/cypress/support/e2e.js b/apps/plone/cypress/support/e2e.js
deleted file mode 100644
index 48912f7f19..0000000000
--- a/apps/plone/cypress/support/e2e.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import 'cypress-axe';
-import 'cypress-file-upload';
-import './commands';
-import { setup, teardown } from './reset-fixture';
-
-beforeEach(function () {
- cy.log('Setting up API fixture');
- setup();
-});
-
-afterEach(function () {
- cy.log('Tearing down API fixture');
- teardown();
-});
diff --git a/apps/plone/cypress/support/reset-fixture.js b/apps/plone/cypress/support/reset-fixture.js
deleted file mode 100644
index 4e2b4372d5..0000000000
--- a/apps/plone/cypress/support/reset-fixture.js
+++ /dev/null
@@ -1,43 +0,0 @@
-function setup() {
- const api_url = Cypress.env('API_PATH') || 'http://localhost:55001/plone';
- cy.request({
- method: 'POST',
- url: `${api_url}/RobotRemote`,
- headers: { Accept: 'text/xml', 'content-type': 'text/xml' },
- body: 'run_keywordremote_zodb_setupplone.app.robotframework.testing.PLONE_ROBOT_TESTING',
- }).then(() => cy.log('Setting up API fixture'));
-}
-
-function teardown() {
- const api_url = Cypress.env('API_PATH') || 'http://localhost:55001/plone';
- cy.request({
- method: 'POST',
- url: `${api_url}/RobotRemote`,
- headers: { Accept: 'text/xml', 'content-type': 'text/xml' },
- body: 'run_keywordremote_zodb_teardownplone.app.robotframework.testing.PLONE_ROBOT_TESTING',
- }).then(() => cy.log('Tearing down API fixture'));
-}
-
-function main() {
- const command = process.argv[2];
- switch (command) {
- case 'setup':
- setup();
- break;
- case 'teardown':
- teardown();
- break;
- default:
- setup();
- }
-}
-
-// This is the equivalent of `if __name__ == '__main__'` in Python :)
-if (require.main === module) {
- main();
-}
-
-module.exports = {
- setup,
- teardown,
-};
diff --git a/apps/plone/locales/de/LC_MESSAGES/volto.po b/apps/plone/locales/de/LC_MESSAGES/volto.po
deleted file mode 100644
index 49d2e20662..0000000000
--- a/apps/plone/locales/de/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,20 +0,0 @@
-# Translation of volto.pot to German
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2016-10-22 16:41-0500\n"
-"Last-Translator: German \n"
-"Language: de\n"
-"Language-Team: German \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"MIME-Version: 1.0\n"
-"Language-Code: de\n"
-"Language-Name: Deutsch\n"
-"Preferred-Encodings: utf-8 latin1\n"
-"X-Is-Fallback-For: de-at de-li de-lu de-ch de-de\n"
-
-
diff --git a/apps/plone/locales/en/LC_MESSAGES/volto.po b/apps/plone/locales/en/LC_MESSAGES/volto.po
deleted file mode 100644
index 12b9a47628..0000000000
--- a/apps/plone/locales/en/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,14 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: \n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: \n"
-"PO-Revision-Date: \n"
-"Last-Translator: \n"
-"Language: \n"
-"Language-Team: \n"
-"Content-Type: \n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: \n"
-
-
diff --git a/apps/plone/locales/es/LC_MESSAGES/volto.po b/apps/plone/locales/es/LC_MESSAGES/volto.po
deleted file mode 100644
index 784b64fb17..0000000000
--- a/apps/plone/locales/es/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,21 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-12-03 03:20-0400\n"
-"PO-Revision-Date: 2019-12-12 06:07-0400\n"
-"Last-Translator: Leonardo J. Caballero G. \n"
-"Language: es\n"
-"Language-Team: ES \n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"MIME-Version: 1.0\n"
-"X-Generator: Virtaal 0.7.1\n"
-"Language-Code: es\n"
-"Language-Name: Español\n"
-"Preferred-Encodings: utf-8\n"
-"Domain: volto\n"
-"X-Is-Fallback-For: es-ar es-bo es-cl es-co es-cr es-do es-ec es-es es-sv es-gt es-hn es-mx es-ni es-pa es-py es-pe es-pr es-us es-uy es-ve\n"
-
-
diff --git a/apps/plone/locales/eu/LC_MESSAGES/volto.po b/apps/plone/locales/eu/LC_MESSAGES/volto.po
deleted file mode 100644
index 984adca72e..0000000000
--- a/apps/plone/locales/eu/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,19 +0,0 @@
-# Translation of volto.pot to EU
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2016-10-22 16:41-0500\n"
-"Last-Translator: Plone i18n \n"
-"Language: eu\n"
-"Language-Team: Plone i18n \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"MIME-Version: 1.0\n"
-"Language-Code: eu\n"
-"Language-Name: eu\n"
-"Preferred-Encodings: utf-8 latin1\n"
-
-
diff --git a/apps/plone/locales/fr/LC_MESSAGES/volto.po b/apps/plone/locales/fr/LC_MESSAGES/volto.po
deleted file mode 100644
index f6050e4530..0000000000
--- a/apps/plone/locales/fr/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,20 +0,0 @@
-# Translation of volto.pot to French
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2016-10-22 16:41-0500\n"
-"Last-Translator: Benoît Suttor \n"
-"Language: fr\n"
-"Language-Team: French \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"MIME-Version: 1.0\n"
-"Language-Code: fr\n"
-"Language-Name: French\n"
-"Preferred-Encodings: utf-8 latin1\n"
-"X-Is-Fallback-For: fr-be fr-ca fr-lu fr-mc fr-ch fr-fr\n"
-
-
diff --git a/apps/plone/locales/it/LC_MESSAGES/volto.po b/apps/plone/locales/it/LC_MESSAGES/volto.po
deleted file mode 100644
index 12b9a47628..0000000000
--- a/apps/plone/locales/it/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,14 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: \n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: \n"
-"PO-Revision-Date: \n"
-"Last-Translator: \n"
-"Language: \n"
-"Language-Team: \n"
-"Content-Type: \n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: \n"
-
-
diff --git a/apps/plone/locales/ja/LC_MESSAGES/volto.po b/apps/plone/locales/ja/LC_MESSAGES/volto.po
deleted file mode 100644
index a543785fbf..0000000000
--- a/apps/plone/locales/ja/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,19 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2019-11-15 12:06+0900\n"
-"Last-Translator: Manabu TERADA \n"
-"Language: ja\n"
-"Language-Team: Plone Japanese Team \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"MIME-Version: 1.0\n"
-"Language-Code: ja\n"
-"Language-Name: Japanese\n"
-"Preferred-Encodings: utf-8\n"
-"X-Is-Fallback-For: ja-jp\n"
-
-
diff --git a/apps/plone/locales/nl/LC_MESSAGES/volto.po b/apps/plone/locales/nl/LC_MESSAGES/volto.po
deleted file mode 100644
index a9816b4cea..0000000000
--- a/apps/plone/locales/nl/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,18 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: PlonenPOT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: \n"
-"PO-Revision-Date: 2017-02-28 08:52+0100\n"
-"Last-Translator: Dutch \n"
-"Language: nl\n"
-"Language-Team: Dutch \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"MIME-Version: 1.0\n"
-"Language-Code: nl\n"
-"Language-Name: Nederlands\n"
-"Preferred-Encodings: utf-8\n"
-
-
diff --git a/apps/plone/locales/pt/LC_MESSAGES/volto.po b/apps/plone/locales/pt/LC_MESSAGES/volto.po
deleted file mode 100644
index 3713b40e7d..0000000000
--- a/apps/plone/locales/pt/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,19 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-12-17T20:38:19.520Z\n"
-"PO-Revision-Date: \n"
-"Last-Translator: Emanuel de Jesus , 2019\n"
-"Language: pt\n"
-"Language-Team: Portuguese (https://www.transifex.com/plone/teams/14552/pt/)\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"MIME-Version: 1.0\n"
-"Domain: volto\n"
-"Language-Code: pt\n"
-"Language-Name: Portuguese\n"
-"Preferred-Encodings: utf-8\n"
-
-
diff --git a/apps/plone/locales/pt_BR/LC_MESSAGES/volto.po b/apps/plone/locales/pt_BR/LC_MESSAGES/volto.po
deleted file mode 100644
index d788679fdc..0000000000
--- a/apps/plone/locales/pt_BR/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,18 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2017-02-28 08:52+0100\n"
-"Last-Translator: Léu Almeida \n"
-"Language: pt-BR\n"
-"Language-Team: Plone i18n \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"MIME-Version: 1.0\n"
-"Language-Code: pt-br\n"
-"Language-Name: Português do Brasil\n"
-"Preferred-Encodings: utf-8\n"
-
-
diff --git a/apps/plone/locales/ro/LC_MESSAGES/volto.po b/apps/plone/locales/ro/LC_MESSAGES/volto.po
deleted file mode 100644
index 42c60f4d17..0000000000
--- a/apps/plone/locales/ro/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,18 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2016-10-22 16:41-0500\n"
-"Last-Translator: Plone i18n \n"
-"Language: ro\n"
-"Language-Team: Plone i18n \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"MIME-Version: 1.0\n"
-"Language-Code: ro\n"
-"Language-Name: Romanian\n"
-"Preferred-Encodings: utf-8 latin1\n"
-
-
diff --git a/apps/plone/locales/volto.pot b/apps/plone/locales/volto.pot
deleted file mode 100644
index 430c431165..0000000000
--- a/apps/plone/locales/volto.pot
+++ /dev/null
@@ -1,16 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"POT-Creation-Date: 2018-10-03T09:01:24.737Z\n"
-"Last-Translator: Plone i18n \n"
-"Language-Team: Plone i18n \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"Language-Code: en\n"
-"Language-Name: English\n"
-"Preferred-Encodings: utf-8\n"
-"Domain: volto\n"
-
-
diff --git a/apps/plone/mrs.developer.json b/apps/plone/mrs.developer.json
deleted file mode 100644
index 502bb075b9..0000000000
--- a/apps/plone/mrs.developer.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "volto-volto-project": {
- "local": "addons/volto-volto-project/src"
- }
-}
\ No newline at end of file
diff --git a/apps/plone/package.json b/apps/plone/package.json
deleted file mode 100644
index e87b8c95e3..0000000000
--- a/apps/plone/package.json
+++ /dev/null
@@ -1,361 +0,0 @@
-{
- "name": "plone",
- "description": "Plone CMS Volto frontend project",
- "license": "MIT",
- "version": "1.0.0",
- "scripts": {
- "start": "razzle start",
- "build": "razzle build --noninteractive",
- "lint": "./node_modules/eslint/bin/eslint.js --max-warnings=0 'src/**/*.{js,jsx,ts,tsx}'",
- "lint:fix": "./node_modules/eslint/bin/eslint.js --fix 'src/**/*.{js,jsx,ts,tsx}'",
- "lint:ci": "./node_modules/eslint/bin/eslint.js --max-warnings=0 -f checkstyle 'src/**/*.{js,jsx,ts,tsx}' > eslint.xml",
- "prettier": "./node_modules/.bin/prettier --single-quote --check 'src/**/*.{js,jsx,ts,tsx,css,scss}'",
- "prettier:fix": "./node_modules/.bin/prettier --single-quote --write 'src/**/*.{js,jsx,ts,tsx,css,scss}'",
- "prettier:ci": "./node_modules/.bin/prettier --single-quote --check 'src/**/*.{js,jsx,ts,tsx,css,scss}'",
- "stylelint": "stylelint 'theme/**/*.{css,scss,less}' 'src/**/*.{css,scss,less}'",
- "stylelint:overrides": "stylelint 'theme/**/*.overrides' 'src/**/*.overrides'",
- "stylelint:fix": "yarn stylelint --fix && yarn stylelint:overrides --fix",
- "test": "razzle test --passWithNoTests",
- "typecheck": "tsc --project tsconfig.json --noEmit",
- "cypress:open": "make test-acceptance",
- "cypress:run": "test-acceptance-headless",
- "start:prod": "NODE_ENV=production node build/server.js",
- "i18n": "rm -rf build/messages && NODE_ENV=production i18n",
- "storybook": "start-storybook -p 6006",
- "build-storybook": "build-storybook"
- },
- "workspaces": [
- "src/addons/volto-volto-project"
- ],
- "addons": [
- "volto-volto-project"
- ],
- "jest": {
- "modulePathIgnorePatterns": [
- "api"
- ],
- "transform": {
- "^.+\\.js(x)?$": "babel-jest",
- "^.+\\.(png)$": "jest-file",
- "^.+\\.(jpg)$": "jest-file",
- "^.+\\.(svg)$": "./node_modules/@plone/volto/jest-svgsystem-transform.js"
- },
- "transformIgnorePatterns": [
- "/node_modules/(?!@plone/volto).+\\.js$"
- ],
- "moduleNameMapper": {
- "@plone/volto/cypress/(.*)$": "/node_modules/@plone/volto/cypress/$1",
- "@plone/volto/addon-registry": "/node_modules/@plone/volto/addon-registry",
- "@plone/volto/webpack-plugins/webpack-less-plugin": "/node_modules/@plone/volto/webpack-plugins/webpack-less-plugin",
- "@plone/volto/babel": "/node_modules/@plone/volto/babel",
- "@plone/volto/(.*)$": "/node_modules/@plone/volto/src/$1",
- "@plone/volto-slate/(.*)$": "/node_modules/@plone/volto/packages/volto-slate/src/$1",
- "load-volto-addons": "/node_modules/@plone/volto/jest-addons-loader.js",
- "@package/(.*)$": "/src/$1",
- "@root/(.*)$": "/src/$1",
- "~/(.*)$": "/src/$1",
- "\\.(css|less|scss|sass)$": "identity-obj-proxy"
- },
- "coverageThreshold": {
- "global": {
- "branches": 10,
- "functions": 10,
- "lines": 10,
- "statements": 10
- }
- },
- "setupFiles": [
- "@plone/volto/test-setup-globals.js",
- "@plone/volto/test-setup-config.jsx"
- ],
- "globals": {
- "__DEV__": true
- }
- },
- "prettier": {
- "trailingComma": "all",
- "singleQuote": true,
- "overrides": [
- {
- "files": "*.overrides",
- "options": {
- "parser": "less"
- }
- }
- ]
- },
- "stylelint": {
- "extends": [
- "stylelint-config-idiomatic-order"
- ],
- "plugins": [
- "stylelint-prettier"
- ],
- "overrides": [
- {
- "files": [
- "**/*.less"
- ],
- "customSyntax": "postcss-less"
- },
- {
- "files": [
- "**/*.overrides"
- ],
- "customSyntax": "postcss-less"
- },
- {
- "files": [
- "**/*.scss"
- ],
- "customSyntax": "postcss-scss"
- }
- ],
- "rules": {
- "prettier/prettier": true,
- "rule-empty-line-before": [
- "always-multi-line",
- {
- "except": [
- "first-nested"
- ],
- "ignore": [
- "after-comment"
- ]
- }
- ]
- },
- "ignoreFiles": "theme/themes/default/**/*.overrides"
- },
- "browserslist": [
- ">1%",
- "last 4 versions",
- "Firefox ESR",
- "not ie 11",
- "not dead"
- ],
- "engines": {
- "node": "^16 || ^18 || ^20"
- },
- "dependencies": {
- "@loadable/component": "5.14.1",
- "@loadable/server": "5.14.0",
- "@plone/registry": "workspace:*",
- "@plone/scripts": "workspace:*",
- "@plone/volto": "workspace:*",
- "@plone/volto-slate": "workspace:*",
- "@redux-devtools/extension": "^3.3.0",
- "classnames": "2.2.6",
- "connected-react-router": "6.8.0",
- "debug": "4.3.2",
- "decorate-component-with-props": "1.2.1",
- "dependency-graph": "0.10.0",
- "detect-browser": "5.1.0",
- "diff": "3.5.0",
- "express": "4.19.2",
- "filesize": "6",
- "github-slugger": "1.4.0",
- "history": "4.10.1",
- "hoist-non-react-statics": "3.3.2",
- "http-proxy-middleware": "2.0.1",
- "image-extensions": "1.1.0",
- "immutable": "3",
- "is-hotkey": "0.2.0",
- "is-url": "1.2.4",
- "jotai": "2.0.3",
- "jwt-decode": "2.2.0",
- "linkify-it": "3.0.2",
- "locale": "0.1.0",
- "lodash": "4.17.21",
- "lodash-move": "1.1.1",
- "moment": "2.29.4",
- "object-assign": "4.1.1",
- "prepend-http": "2",
- "pretty-bytes": "5.3.0",
- "prismjs": "1.27.0",
- "process": "^0.11.10",
- "promise-file-reader": "1.0.2",
- "prop-types": "15.7.2",
- "query-string": "7.1.0",
- "rc-time-picker": "3.7.3",
- "react": "18.2.0",
- "react-anchor-link-smooth-scroll": "1.0.12",
- "react-animate-height": "2.0.17",
- "react-beautiful-dnd": "13.0.0",
- "react-cookie": "4.1.1",
- "react-dates": "21.5.1",
- "react-detect-click-outside": "1.1.1",
- "react-dnd": "5.0.0",
- "react-dnd-html5-backend": "5.0.1",
- "react-dom": "18.2.0",
- "react-dropzone": "11.1.0",
- "react-fast-compare": "2.0.4",
- "react-image-gallery": "1.2.7",
- "react-intersection-observer": "9.1.0",
- "react-intl": "3.12.1",
- "react-intl-redux": "2.3.0",
- "react-medium-image-zoom": "3.0.15",
- "react-popper": "^2.3.0",
- "react-redux": "8.1.2",
- "react-router": "5.2.0",
- "react-router-config": "5.1.1",
- "react-router-dom": "5.2.0",
- "react-router-hash-link": "2.4.3",
- "react-select": "4.3.1",
- "react-select-async-paginate": "0.5.3",
- "react-share": "2.3.1",
- "react-side-effect": "2.1.2",
- "react-simple-code-editor": "0.7.1",
- "react-sortable-hoc": "2.0.0",
- "react-test-renderer": "18.2.0",
- "react-toastify": "5.5.0",
- "react-transition-group": "4.4.5",
- "react-virtualized": "9.22.3",
- "redux": "4.2.1",
- "redux-actions": "3.0.0",
- "redux-connect": "10.0.0",
- "redux-localstorage-simple": "2.5.1",
- "redux-mock-store": "1.5.4",
- "redux-thunk": "2.4.2",
- "rrule": "2.7.1",
- "semantic-ui-less": "2.4.1",
- "semantic-ui-react": "2.1.5",
- "serialize-javascript": "3.1.0",
- "slate": "0.100.0",
- "slate-hyperscript": "0.100.0",
- "slate-react": "0.98.4",
- "superagent": "3.8.2",
- "tlds": "1.203.1",
- "undoo": "0.5.0",
- "universal-cookie": "4.0.4",
- "universal-cookie-express": "4.0.3",
- "url": "^0.11.3",
- "use-deep-compare-effect": "1.8.1",
- "uuid": "^8.3.2",
- "volto-volto-project": "workspace:*"
- },
- "devDependencies": {
- "@babel/core": "^7.0.0",
- "@babel/eslint-parser": "7.22.15",
- "@babel/plugin-proposal-export-default-from": "7.18.10",
- "@babel/plugin-proposal-export-namespace-from": "7.18.9",
- "@babel/plugin-proposal-json-strings": "7.18.6",
- "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
- "@babel/plugin-proposal-throw-expressions": "7.18.6",
- "@babel/plugin-syntax-export-namespace-from": "7.8.3",
- "@babel/runtime": "7.20.6",
- "@babel/types": "7.20.5",
- "@jest/globals": "^29.7.0",
- "@loadable/babel-plugin": "5.13.2",
- "@loadable/webpack-plugin": "5.15.2",
- "@plone/scripts": "workspace:*",
- "@plone/types": "1.0.0-alpha.11",
- "@plone/volto-coresandbox": "workspace:*",
- "@sinonjs/fake-timers": "^6.0.1",
- "@storybook/addon-actions": "^8.0.4",
- "@storybook/addon-controls": "^8.0.4",
- "@storybook/addon-docs": "^8.0.4",
- "@storybook/addon-essentials": "^8.0.4",
- "@storybook/addon-links": "^8.0.4",
- "@storybook/addon-webpack5-compiler-babel": "^3.0.3",
- "@storybook/builder-webpack5": "^6.5.15",
- "@storybook/manager-webpack5": "^6.5.15",
- "@storybook/react": "^8.0.4",
- "@storybook/react-webpack5": "^8.0.4",
- "@storybook/theming": "^8.0.4",
- "@testing-library/cypress": "10.0.1",
- "@testing-library/jest-dom": "6.4.2",
- "@testing-library/react": "14.2.0",
- "@testing-library/react-hooks": "8.0.1",
- "@types/jest": "^29.5.8",
- "@types/loadable__component": "^5.13.9",
- "@types/lodash": "^4.14.201",
- "@types/react": "^18",
- "@types/react-dom": "^18",
- "@types/react-router-dom": "^5.3.3",
- "@types/react-test-renderer": "18.0.7",
- "@types/uuid": "^9.0.2",
- "@typescript-eslint/eslint-plugin": "^7.7.0",
- "@typescript-eslint/parser": "^7.7.0",
- "autoprefixer": "10.4.8",
- "axe-core": "4.4.2",
- "babel-loader": "9.1.0",
- "babel-plugin-add-module-exports": "0.2.1",
- "babel-plugin-lodash": "3.3.4",
- "babel-plugin-react-intl": "5.1.17",
- "babel-plugin-root-import": "6.1.0",
- "babel-preset-razzle": "4.2.18",
- "bundlewatch": "0.3.3",
- "circular-dependency-plugin": "5.2.2",
- "css-loader": "5.2.7",
- "cypress": "13.13.2",
- "cypress-axe": "1.5.0",
- "cypress-file-upload": "5.0.8",
- "deep-freeze": "0.0.1",
- "eslint": "^8.57.0",
- "eslint-config-prettier": "^9.1.0",
- "eslint-config-react-app": "^7.0.1",
- "eslint-import-resolver-alias": "^1.1.2",
- "eslint-import-resolver-babel-plugin-root-import": "^1.1.1",
- "eslint-import-resolver-typescript": "^3.6.1",
- "eslint-plugin-import": "^2.29.1",
- "eslint-plugin-jsx-a11y": "^6.7.1",
- "eslint-plugin-prettier": "^5.1.3",
- "eslint-plugin-react": "^7.34.1",
- "eslint-plugin-react-hooks": "^4.6.0",
- "full-icu": "1.4.0",
- "glob": "7.1.6",
- "html-webpack-plugin": "5.5.0",
- "identity-obj-proxy": "3.0.0",
- "jest": "26.6.3",
- "jest-environment-jsdom": "^26",
- "jest-file": "1.0.0",
- "jest-junit": "8.0.0",
- "jsdom": "^16.7.0",
- "jsonwebtoken": "9.0.0",
- "less": "3.11.1",
- "less-loader": "11.1.0",
- "lodash-webpack-plugin": "0.11.6",
- "mini-css-extract-plugin": "2.7.2",
- "moment-locales-webpack-plugin": "1.2.0",
- "mrs-developer": "^2.1.1",
- "postcss": "8.4.31",
- "postcss-flexbugs-fixes": "5.0.2",
- "postcss-less": "6.0.0",
- "postcss-load-config": "3.1.4",
- "postcss-loader": "7.0.2",
- "postcss-overrides": "3.1.4",
- "postcss-scss": "4.0.6",
- "prettier": "3.2.5",
- "razzle": "4.2.18",
- "razzle-dev-utils": "4.2.18",
- "razzle-plugin-scss": "4.2.18",
- "react-docgen-typescript-plugin": "^1.0.5",
- "react-error-overlay": "6.0.9",
- "react-is": "^18.2.0",
- "release-it": "^17.1.1",
- "semver": "^7.5.4",
- "start-server-and-test": "1.14.0",
- "storybook": "^8.0.4",
- "style-loader": "3.3.1",
- "stylelint": "^16.3.1",
- "stylelint-config-idiomatic-order": "10.0.0",
- "stylelint-prettier": "5.0.0",
- "svg-loader": "0.0.2",
- "svgo-loader": "3.0.3",
- "terser-webpack-plugin": "5.3.6",
- "tmp": "0.2.1",
- "ts-jest": "^26.4.2",
- "ts-loader": "9.4.4",
- "tsconfig": "*",
- "typescript": "^5.4.2",
- "use-trace-update": "1.3.2",
- "wait-on": "6.0.0",
- "webpack": "5.90.1",
- "webpack-bundle-analyzer": "4.10.1",
- "webpack-dev-server": "4.11.1",
- "webpack-node-externals": "3.0.0",
- "why": "0.6.2"
- },
- "theme": "volto-volto-project"
-}
diff --git a/apps/plone/patches/patchit.sh b/apps/plone/patches/patchit.sh
deleted file mode 100644
index 7cd11b5edc..0000000000
--- a/apps/plone/patches/patchit.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-patch --quiet -p0 -N node_modules/razzle/config/createJestConfig.js < patches/razzle-jest.patch
diff --git a/apps/plone/patches/razzle-jest.patch b/apps/plone/patches/razzle-jest.patch
deleted file mode 100644
index 5b822cf57f..0000000000
--- a/apps/plone/patches/razzle-jest.patch
+++ /dev/null
@@ -1,10 +0,0 @@
---- node_modules/razzle/config/createJestConfig.js 2020-05-09 19:48:07.276871617 +0200
-+++ patched.js 2020-05-09 19:50:07.049471664 +0200
-@@ -60,6 +60,7 @@
- 'moduleDirectories',
- 'moduleFileExtensions',
- 'moduleNameMapper',
-+ 'modulePathIgnorePatterns',
- 'modulePaths',
- 'snapshotSerializers',
- 'setupFiles',
diff --git a/apps/plone/public/android-chrome-192x192.png b/apps/plone/public/android-chrome-192x192.png
deleted file mode 100644
index 5a31bad458..0000000000
Binary files a/apps/plone/public/android-chrome-192x192.png and /dev/null differ
diff --git a/apps/plone/public/android-chrome-512x512.png b/apps/plone/public/android-chrome-512x512.png
deleted file mode 100644
index b522d84589..0000000000
Binary files a/apps/plone/public/android-chrome-512x512.png and /dev/null differ
diff --git a/apps/plone/public/apple-touch-icon.png b/apps/plone/public/apple-touch-icon.png
deleted file mode 100644
index f0a5f8bc85..0000000000
Binary files a/apps/plone/public/apple-touch-icon.png and /dev/null differ
diff --git a/apps/plone/public/favicon-16x16.png b/apps/plone/public/favicon-16x16.png
deleted file mode 100644
index bf6cf158a0..0000000000
Binary files a/apps/plone/public/favicon-16x16.png and /dev/null differ
diff --git a/apps/plone/public/favicon-32x32.png b/apps/plone/public/favicon-32x32.png
deleted file mode 100644
index 0411131449..0000000000
Binary files a/apps/plone/public/favicon-32x32.png and /dev/null differ
diff --git a/apps/plone/public/favicon.ico b/apps/plone/public/favicon.ico
deleted file mode 100644
index b9c0d0c0a6..0000000000
Binary files a/apps/plone/public/favicon.ico and /dev/null differ
diff --git a/apps/plone/public/icon.svg b/apps/plone/public/icon.svg
deleted file mode 100644
index 72640f02bd..0000000000
--- a/apps/plone/public/icon.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-
diff --git a/apps/plone/public/index.html.spa b/apps/plone/public/index.html.spa
deleted file mode 100644
index 27e655dcab..0000000000
--- a/apps/plone/public/index.html.spa
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
- React App
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/plone/public/robots.txt b/apps/plone/public/robots.txt
deleted file mode 100644
index 54fb98911d..0000000000
--- a/apps/plone/public/robots.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-User-agent: *
-
diff --git a/apps/plone/public/site.webmanifest b/apps/plone/public/site.webmanifest
deleted file mode 100644
index b20abb7cbb..0000000000
--- a/apps/plone/public/site.webmanifest
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "name": "",
- "short_name": "",
- "icons": [
- {
- "src": "/android-chrome-192x192.png",
- "sizes": "192x192",
- "type": "image/png"
- },
- {
- "src": "/android-chrome-512x512.png",
- "sizes": "512x512",
- "type": "image/png"
- }
- ],
- "theme_color": "#ffffff",
- "background_color": "#ffffff",
- "display": "standalone"
-}
diff --git a/apps/plone/razzle.config.js b/apps/plone/razzle.config.js
deleted file mode 100644
index 6bab4ae583..0000000000
--- a/apps/plone/razzle.config.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Replace with custom razzle config when needed.
- * @module razzle.config
- */
-const fs = require('fs');
-
-let voltoPath = './node_modules/@plone/volto';
-
-let configFile;
-if (fs.existsSync(`${this.projectRootPath}/tsconfig.json`))
- configFile = `${this.projectRootPath}/tsconfig.json`;
-else if (fs.existsSync(`${this.projectRootPath}/jsconfig.json`))
- configFile = `${this.projectRootPath}/jsconfig.json`;
-
-if (configFile) {
- const jsConfig = require(configFile).compilerOptions;
- const pathsConfig = jsConfig.paths;
- if (pathsConfig['@plone/volto'])
- voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`;
-}
-
-const defaultVoltoRazzleConfig = require(`${voltoPath}/razzle.config`);
-const { modifyWebpackConfig } = defaultVoltoRazzleConfig;
-
-const customModifyWebpackConfig = ({
- env: { target, dev },
- webpackConfig,
- webpackObject,
- options,
-}) => {
- const config = modifyWebpackConfig({
- env: { target, dev },
- webpackConfig,
- webpackObject,
- options,
- });
- // add custom code here..
- return config;
-};
-
-module.exports = {
- ...defaultVoltoRazzleConfig,
- modifyWebpackConfig: customModifyWebpackConfig,
-};
diff --git a/apps/plone/src/addons/volto-volto-project/.github/workflows/acceptance.yml b/apps/plone/src/addons/volto-volto-project/.github/workflows/acceptance.yml
deleted file mode 100644
index 51d072e448..0000000000
--- a/apps/plone/src/addons/volto-volto-project/.github/workflows/acceptance.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-name: Acceptance tests
-on: [push]
-
-env:
- ADDON_NAME: "volto-volto-project"
- ADDON_PATH: "volto-volto-project"
- VOLTO_VERSION: "17.0.0"
-
-jobs:
-
- acceptance:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v3
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
-
- - name: Install Cypress
- run: |
- cd acceptance
- yarn
-
- - name: "Cypress: Acceptance tests"
- uses: cypress-io/github-action@v6
- env:
- BABEL_ENV: production
- CYPRESS_RETRIES: 2
- with:
- parallel: false
- browser: chrome
- working-directory: acceptance
- spec: cypress/tests/*.js
- install: false
- start: |
- docker compose -f ci.yml --profile prod up
- wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000'
-
- # Upload Cypress screenshots
- - uses: actions/upload-artifact@v3
- if: failure()
- with:
- name: cypress-screenshots-acceptance
- path: acceptance/cypress/screenshots
-
- # Upload Cypress videos
- - uses: actions/upload-artifact@v3
- if: failure()
- with:
- name: cypress-videos-acceptance
- path: acceptance/cypress/videos
diff --git a/apps/plone/src/addons/volto-volto-project/.github/workflows/changelog.yml b/apps/plone/src/addons/volto-volto-project/.github/workflows/changelog.yml
deleted file mode 100644
index 113171e7e5..0000000000
--- a/apps/plone/src/addons/volto-volto-project/.github/workflows/changelog.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: Changelog check
-on:
- pull_request:
- types: [assigned, opened, synchronize, reopened, labeled, unlabeled]
- branches:
- - main
-
-env:
- node-version: 18.x
-
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- with:
- # Fetch all history
- fetch-depth: '0'
-
- - name: Install pipx
- run: pip install towncrier
-
- # node setup
- - name: Use Node.js ${{ env.node-version }}
- uses: actions/setup-node@v3
- with:
- node-version: ${{ env.node-version }}
- cache: 'yarn'
-
- # node install
- - name: Install dependencies
- run: yarn
-
- - name: Check for presence of a Change Log fragment (only pull requests)
- run: |
- # Fetch the pull request' base branch so towncrier will be able to
- # compare the current branch with the base branch.
- # Source: https://github.com/actions/checkout/#fetch-all-branches.
- git fetch --no-tags origin main
- towncrier check --compare-with origin/main
- env:
- BASE_BRANCH: ${{ github.base_ref }}
- if: github.event_name == 'pull_request'
diff --git a/apps/plone/src/addons/volto-volto-project/.github/workflows/code.yml b/apps/plone/src/addons/volto-volto-project/.github/workflows/code.yml
deleted file mode 100644
index a7eeb58513..0000000000
--- a/apps/plone/src/addons/volto-volto-project/.github/workflows/code.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: Code analysis checks
-on: [push]
-jobs:
- codeanalysis:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- node-version: [18.x]
-
- steps:
- - name: Main checkout
- uses: actions/checkout@v3
-
- - name: Linting
- run: make lint
diff --git a/apps/plone/src/addons/volto-volto-project/.github/workflows/unit.yml b/apps/plone/src/addons/volto-volto-project/.github/workflows/unit.yml
deleted file mode 100644
index 8dbb3de936..0000000000
--- a/apps/plone/src/addons/volto-volto-project/.github/workflows/unit.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: Unit Tests
-on: [push]
-jobs:
- unit:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- node-version: [18.x]
-
- steps:
- - name: Main checkout
- uses: actions/checkout@v3
-
- - name: Unit tests
- run: make test-ci
diff --git a/apps/plone/src/addons/volto-volto-project/.gitignore b/apps/plone/src/addons/volto-volto-project/.gitignore
deleted file mode 100644
index f11144ebfb..0000000000
--- a/apps/plone/src/addons/volto-volto-project/.gitignore
+++ /dev/null
@@ -1,27 +0,0 @@
-.vscode/
-.history
-logs
-*.log
-npm-debug.log*
-.DS_Store
-*.swp
-yarn-error.log
-
-node_modules
-build
-dist
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-.changelog.draft
-
-# yarn 3
-.pnp.*
-.yarn/*
-!.yarn/patches
-!.yarn/plugins
-!.yarn/releases
-!.yarn/sdks
-!.yarn/versions
diff --git a/apps/plone/src/addons/volto-volto-project/CHANGELOG.md b/apps/plone/src/addons/volto-volto-project/CHANGELOG.md
deleted file mode 100644
index 413fb920e1..0000000000
--- a/apps/plone/src/addons/volto-volto-project/CHANGELOG.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Release Notes
-
-
-
-
diff --git a/apps/plone/src/addons/volto-volto-project/Makefile b/apps/plone/src/addons/volto-volto-project/Makefile
deleted file mode 100644
index c13a5ffc57..0000000000
--- a/apps/plone/src/addons/volto-volto-project/Makefile
+++ /dev/null
@@ -1,151 +0,0 @@
-# Yeoman Volto App development
-
-### Defensive settings for make:
-# https://tech.davis-hansson.com/p/make/
-SHELL:=bash
-.ONESHELL:
-.SHELLFLAGS:=-eu -o pipefail -c
-.SILENT:
-.DELETE_ON_ERROR:
-MAKEFLAGS+=--warn-undefined-variables
-MAKEFLAGS+=--no-builtin-rules
-
-CURRENT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
-
-# Recipe snippets for reuse
-
-# We like colors
-# From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects
-RED=`tput setaf 1`
-GREEN=`tput setaf 2`
-RESET=`tput sgr0`
-YELLOW=`tput setaf 3`
-
-PLONE_VERSION=6
-VOLTO_VERSION=17.0.0
-
-ADDON_NAME='volto-volto-project'
-ADDON_PATH='volto-volto-project'
-COMPOSE_FILE=dockerfiles/docker-compose.yml
-ACCEPTANCE_COMPOSE=acceptance/docker-compose.yml
-CMD=CURRENT_DIR=${CURRENT_DIR} ADDON_NAME=${ADDON_NAME} ADDON_PATH=${ADDON_PATH} VOLTO_VERSION=${VOLTO_VERSION} PLONE_VERSION=${PLONE_VERSION} docker compose
-DOCKER_COMPOSE=${CMD} -p ${ADDON_PATH} -f ${COMPOSE_FILE}
-DEV_COMPOSE=COMPOSE_PROFILES=dev ${DOCKER_COMPOSE}
-LIVE_COMPOSE=COMPOSE_PROFILES=dev ${DOCKER_COMPOSE}
-ACCEPTANCE=${CMD} -p ${ADDON_PATH}-acceptance -f ${ACCEPTANCE_COMPOSE}
-
-.PHONY: build-backend
-build-backend: ## Build
- @echo "$(GREEN)==> Build Backend Container $(RESET)"
- ${DEV_COMPOSE} build backend
-
-.PHONY: start-backend
-start-backend: ## Starts Docker backend
- @echo "$(GREEN)==> Start Docker-based Plone Backend $(RESET)"
- ${DEV_COMPOSE} up backend -d
-
-.PHONY: stop-backend
-stop-backend: ## Stop Docker backend
- @echo "$(GREEN)==> Stop Docker-based Plone Backend $(RESET)"
- ${DEV_COMPOSE} stop backend
-
-.PHONY: build-live
-build-live: ## Build Addon live
- @echo "$(GREEN)==> Build Addon development container $(RESET)"
- ${LIVE_COMPOSE} build addon-live
-
-.PHONY: build-addon
-build-addon: ## Build Addon dev
- @echo "$(GREEN)==> Build Addon development container $(RESET)"
- ${DEV_COMPOSE} build addon-dev
-
-.PHONY: start-dev
-start-dev: ## Starts Dev container
- @echo "$(GREEN)==> Start Addon Development container $(RESET)"
- ${DEV_COMPOSE} up addon-dev
-
-.PHONY: dev
-dev: ## Develop the addon
- @echo "$(GREEN)==> Start Development Environment $(RESET)"
- make build-backend
- make start-backend
- make build-addon
- make start-dev
-
-.PHONY: help
-help: ## Show this help.
- @echo -e "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)"
-
-## Setup the local environment
-.PHONY: install
-install: ## Install the local environment, Cypress, build acceptance containers
- yarn
- make install-acceptance
-
-# Dev Helpers
-.PHONY: i18n
-i18n: ## Sync i18n
- @echo "$(YELLOW)==> Do not forget to setup the local environment (make install) $(RESET)"
- yarn i18n
-
-.PHONY: format
-format: ## Format codebase
- ${DEV_COMPOSE} run --rm addon-dev lint:fix
- ${DEV_COMPOSE} run --rm addon-dev prettier:fix
- ${DEV_COMPOSE} run --rm addon-dev stylelint:fix
-
-.PHONY: lint
-lint: ## Lint Codebase
- ${DEV_COMPOSE} run --rm addon-dev lint
- ${DEV_COMPOSE} run --rm addon-dev prettier
- ${DEV_COMPOSE} run --rm addon-dev stylelint --allow-empty-input
-
-.PHONY: test
-test: ## Run unit tests
- ${DEV_COMPOSE} run --rm addon-dev test --watchAll
-
-.PHONY: test-ci
-test-ci: ## Run unit tests in CI
- ${DEV_COMPOSE} run -e CI=1 addon-dev test
-
-## Acceptance
-.PHONY: install-acceptance
-install-acceptance: ## Install Cypress, build acceptance containers
- (cd acceptance && yarn)
- ${ACCEPTANCE} --profile dev --profile prod build
-
-.PHONY: start-test-acceptance-server
-start-test-acceptance-server: ## Start acceptance server (for use it in while developing)
- ${ACCEPTANCE} --profile dev up
-
-.PHONY: start-test-acceptance-server-prod
-start-test-acceptance-server-prod: ## Start acceptance server in prod (used by CI)
- ${ACCEPTANCE} --profile prod up -d
-
-.PHONY: test-acceptance
-test-acceptance: ## Start Cypress (for use it while developing)
- (cd acceptance && ./node_modules/.bin/cypress open)
-
-.PHONY: test-acceptance-headless
-test-acceptance-headless: ## Run cypress tests in CI
- (cd acceptance && ./node_modules/.bin/cypress run)
-
-.PHONY: stop-test-acceptance-server
-stop-test-acceptance-server: ## Stop acceptance server (for use it while finished developing)
- ${ACCEPTANCE} --profile dev down
-
-.PHONY: status-test-acceptance-server
-status-test-acceptance-server: ## Status of Acceptance Server (for use it while developing)
- ${ACCEPTANCE} ps
-
-.PHONY: debug-frontend
-debug-frontend: ## Run bash in the Frontend container (for debug infrastructure purposes)
- ${DEV_COMPOSE} run --entrypoint bash addon-dev
-
-.PHONY: pull-backend-image
-pull-backend-image: ## Pulls and updates the backend image (for use it while developing)
- docker pull ghcr.io/voltovolto-project:latest
-
-.PHONY: release
-release: ## Release a version of the add-on
- yarn release
diff --git a/apps/plone/src/addons/volto-volto-project/README.md b/apps/plone/src/addons/volto-volto-project/README.md
deleted file mode 100644
index df94efe809..0000000000
--- a/apps/plone/src/addons/volto-volto-project/README.md
+++ /dev/null
@@ -1,135 +0,0 @@
-# volto-volto-project
-
-You can develop an add-on in isolation using the boilerplate provided by the add-on generator.
-The project is configured with the current add-on installed and ready to work with.
-This is useful to bootstrap an isolated environment that can be used to quickly develop the add-on or for demo purposes.
-It's also useful when testing an add-on in a CI environment.
-
-This process is similar to when you develop a Plone backend add-on in Python, then embed a ready to use Plone build (using buildout or pip) to develop and test the package.
-
-The Dockerized approach performs all these actions in a custom built Docker environment:
-
-1. generates a vanilla project using the official Volto Yo Generator (`@plone/generator-volto`)
-2. configures it to use the add-on with the name stated in the `package.json`
-3. links the root of the add-on inside the created project
-
-After that you can use the inner Dockerized project, and run any standard Volto command for linting, acceptance test, or unit tests using Makefile commands provided for your convenience.
-
-
-## Set up the environment
-
-Run once:
-
-```shell
-make dev
-```
-
-This will build and launch the backend and frontend containers.
-There's no need to build them again after the first time, unless something has changed from the container setup.
-
-To make your IDE play well with this setup, you must run `yarn` once to install the required packages:
-
-```shell
-yarn
-```
-
-
-## Development
-
-This section describes various Makefile commands to ease development.
-
-
-### Build the containers manually
-
-```shell
-make build-backend
-make build-addon
-```
-
-
-### Run the containers
-
-```shell
-make start-dev
-```
-
-This will start both the frontend and backend containers.
-
-
-### Stop backend (Docker)
-
-After developing, you should stop the running backend.
-
-```shell
-make stop-backend
-```
-
-
-### Linting
-
-```shell
-make lint
-```
-
-
-### Formatting
-
-```shell
-make format
-```
-
-
-### i18n
-
-```shell
-make i18n
-```
-
-
-### Unit tests
-
-```shell
-make test
-```
-
-
-### Acceptance tests
-
-First install requirements to run acceptance tests with the following command.
-Run it only once.
-
-```shell
-make install-acceptance
-```
-
-The frontend runs in development mode to allow development while writing.
-To start the servers:
-
-```shell
-make start-test-acceptance-server
-```
-
-To run Cypress tests after you finish development:
-
-```shell
-make test-acceptance
-```
-
-Shut down the backend server after you complete development with tests.
-
-```shell
-make stop-test-acceptance-server
-```
-
-
-## Release
-
-```shell
-make release
-```
-
-To release a release candidate (rc) version:
-
-```shell
-make release-rc
-```
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/ci.yml b/apps/plone/src/addons/volto-volto-project/acceptance/ci.yml
deleted file mode 100644
index b0c643462a..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/ci.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-version: "3"
-
-services:
-
- addon-acceptance:
- build:
- context: ../
- dockerfile: ./dockerfiles/Dockerfile
- args:
- ADDON_NAME: "${ADDON_NAME}"
- ADDON_PATH: "${ADDON_PATH}"
- VOLTO_VERSION: ${VOLTO_VERSION:-17}
- environment:
- RAZZLE_INTERNAL_API_PATH: http://backend-acceptance:55001/plone
- RAZZLE_API_PATH: http://localhost:55001/plone
- ports:
- - 3000:3000
- depends_on:
- - backend-acceptance
- profiles:
- - prod
-
- backend-acceptance:
- image: plone/server-acceptance:${PLONE_VERSION:-6}
- ports:
- - 55001:55001
- profiles:
- - dev
- - prod
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress.config.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress.config.js
deleted file mode 100644
index d841d7ab7c..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress.config.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const { defineConfig } = require('cypress');
-
-module.exports = defineConfig({
- viewportWidth: 1280,
- e2e: {
- baseUrl: 'http://localhost:3000',
- specPattern: 'cypress/tests/*.cy.{js,jsx}',
- },
-});
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/broccoli.jpg b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/broccoli.jpg
deleted file mode 100644
index 456706d662..0000000000
Binary files a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/broccoli.jpg and /dev/null differ
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/example.json b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/example.json
deleted file mode 100644
index 02e4254378..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/example.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "Using fixtures to represent data",
- "email": "hello@cypress.io",
- "body": "Fixtures are a great way to mock data for responses to routes"
-}
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/file.pdf b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/file.pdf
deleted file mode 100644
index ef9c798194..0000000000
Binary files a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/file.pdf and /dev/null differ
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/halfdome2022.jpg b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/halfdome2022.jpg
deleted file mode 100644
index 58c9411ca2..0000000000
Binary files a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/halfdome2022.jpg and /dev/null differ
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/image.png b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/image.png
deleted file mode 100644
index 4c109bab8d..0000000000
Binary files a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/image.png and /dev/null differ
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/plugins/index.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/plugins/index.js
deleted file mode 100644
index dffed2532f..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/plugins/index.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// ***********************************************************
-// This example plugins/index.js can be used to load plugins
-//
-// You can change the location of this file or turn off loading
-// the plugins file with the 'pluginsFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/plugins-guide
-// ***********************************************************
-
-// This function is called when a project is opened or re-opened (e.g. due to
-// the project's config changing)
-
-module.exports = (on, config) => {
- // `on` is used to hook into various events Cypress emits
- // `config` is the resolved Cypress config
-};
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/commands.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/commands.js
deleted file mode 100644
index 44a5000326..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/commands.js
+++ /dev/null
@@ -1 +0,0 @@
-import '@plone/volto-testing/cypress/support/commands';
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/e2e.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/e2e.js
deleted file mode 100644
index 48912f7f19..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/e2e.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import 'cypress-axe';
-import 'cypress-file-upload';
-import './commands';
-import { setup, teardown } from './reset-fixture';
-
-beforeEach(function () {
- cy.log('Setting up API fixture');
- setup();
-});
-
-afterEach(function () {
- cy.log('Tearing down API fixture');
- teardown();
-});
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/reset-fixture.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/reset-fixture.js
deleted file mode 100644
index 4e2b4372d5..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/reset-fixture.js
+++ /dev/null
@@ -1,43 +0,0 @@
-function setup() {
- const api_url = Cypress.env('API_PATH') || 'http://localhost:55001/plone';
- cy.request({
- method: 'POST',
- url: `${api_url}/RobotRemote`,
- headers: { Accept: 'text/xml', 'content-type': 'text/xml' },
- body: 'run_keywordremote_zodb_setupplone.app.robotframework.testing.PLONE_ROBOT_TESTING',
- }).then(() => cy.log('Setting up API fixture'));
-}
-
-function teardown() {
- const api_url = Cypress.env('API_PATH') || 'http://localhost:55001/plone';
- cy.request({
- method: 'POST',
- url: `${api_url}/RobotRemote`,
- headers: { Accept: 'text/xml', 'content-type': 'text/xml' },
- body: 'run_keywordremote_zodb_teardownplone.app.robotframework.testing.PLONE_ROBOT_TESTING',
- }).then(() => cy.log('Tearing down API fixture'));
-}
-
-function main() {
- const command = process.argv[2];
- switch (command) {
- case 'setup':
- setup();
- break;
- case 'teardown':
- teardown();
- break;
- default:
- setup();
- }
-}
-
-// This is the equivalent of `if __name__ == '__main__'` in Python :)
-if (require.main === module) {
- main();
-}
-
-module.exports = {
- setup,
- teardown,
-};
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/slate.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/slate.js
deleted file mode 100644
index 8ed5b53ab2..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/slate.js
+++ /dev/null
@@ -1,14 +0,0 @@
-export const createSlateBlock = () => {
- cy.get('.ui.basic.icon.button.block-add-button').first().click();
- cy.get('.blocks-chooser .title').contains('Text').click();
- cy.get('.ui.basic.icon.button.slate').contains('Text').click();
- return getSelectedSlateEditor();
-};
-
-export const getSelectedSlateEditor = () => {
- return cy.get('.slate-editor.selected [contenteditable=true]').click();
-};
-
-export const getSlateEditorAndType = (selector, type) => {
- return cy.get(selector).focus().click().wait(1000).type(type).wait(1000);
-};
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/tests/basic.cy.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/tests/basic.cy.js
deleted file mode 100644
index 2f55da049a..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/tests/basic.cy.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import {
- getSlateEditorAndType,
- getSelectedSlateEditor,
-} from '../support/slate';
-
-context('Basic Acceptance Tests', () => {
- describe('Text Block Tests', () => {
- beforeEach(() => {
- cy.intercept('GET', `/**/*?expand*`).as('content');
- cy.intercept('GET', '/**/Document').as('schema');
-
- // given a logged in editor and a page in edit mode
- cy.autologin();
- cy.createContent({
- contentType: 'Document',
- contentId: 'document',
- contentTitle: 'Document',
- });
- cy.visit('/');
- cy.wait('@content');
- });
-
- it('As editor I can add a page with a text block', function () {
- // when I add a page with a text block
- cy.get('#toolbar-add').click();
- cy.get('#toolbar-add-document').click();
- cy.get('.documentFirstHeading')
- .type('My Page')
- .get('.documentFirstHeading')
- .contains('My Page');
-
- getSlateEditorAndType(
- '.block .slate-editor [contenteditable=true]',
- 'This is the text',
- );
-
- getSelectedSlateEditor().contains('This is the text');
- cy.get('#toolbar-save').click();
- cy.wait('@content');
- cy.url().should('eq', Cypress.config().baseUrl + '/my-page');
- });
-
- it('As editor I can add a link to a text block', function () {
- cy.navigate('/document/edit');
- cy.wait('@schema');
-
- cy.get('.block.inner.title .documentFirstHeading');
-
- cy.log('when I create a link');
-
- cy.getSlateEditorAndType(
- 'Colorless green ideas sleep furiously.',
- ).setSlateSelection('furiously');
- cy.clickSlateButton('Add link');
- cy.get('.slate-toolbar .link-form-container input').type(
- 'https://google.com{enter}',
- );
-
- cy.get('#toolbar-save', { timeout: 10000 }).click();
- cy.wait('@content');
-
- cy.log('then the page view should contain a link');
-
- cy.get('.container p').contains('Colorless green ideas sleep furiously.');
- cy.get('.container p a')
- .should('have.text', 'furiously')
- .and('have.attr', 'href')
- .and('include', 'https://google.com');
- });
- });
-});
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/docker-compose.yml b/apps/plone/src/addons/volto-volto-project/acceptance/docker-compose.yml
deleted file mode 100644
index 0af8d4a15f..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/docker-compose.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-version: "3"
-
-services:
-
- addon-dev:
- build:
- context: ../
- dockerfile: ./dockerfiles/Dockerfile.acceptance
- args:
- ADDON_NAME: "${ADDON_NAME}"
- ADDON_PATH: "${ADDON_PATH}"
- VOLTO_VERSION: ${VOLTO_VERSION:-17}
- volumes:
- - ${CURRENT_DIR}:/app/src/addons/${ADDON_PATH}/
- environment:
- RAZZLE_INTERNAL_API_PATH: http://backend-acceptance:55001/plone
- RAZZLE_API_PATH: http://localhost:55001/plone
- HOST: 0.0.0.0
- ports:
- - 3000:3000
- - 3001:3001
- tty: true
- depends_on:
- - backend-acceptance
- profiles:
- - dev
-
- addon-acceptance:
- build:
- context: ../
- dockerfile: ./dockerfiles/Dockerfile
- args:
- ADDON_NAME: "${ADDON_NAME}"
- ADDON_PATH: "${ADDON_PATH}"
- VOLTO_VERSION: ${VOLTO_VERSION:-17}
- environment:
- RAZZLE_INTERNAL_API_PATH: http://backend-acceptance:55001/plone
- RAZZLE_API_PATH: http://localhost:55001/plone
- HOST: 0.0.0.0
- ports:
- - 3000:3000
- depends_on:
- - backend-acceptance
- profiles:
- - prod
-
- backend-acceptance:
- image: plone/server-acceptance:${PLONE_VERSION:-6}
- ports:
- - 55001:55001
- profiles:
- - dev
- - prod
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/package.json b/apps/plone/src/addons/volto-volto-project/acceptance/package.json
deleted file mode 100644
index c11c8e33cb..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "dependencies": {
- "@plone/volto-testing": "^4.0.0"
- }
-}
diff --git a/apps/plone/src/addons/volto-volto-project/babel.config.js b/apps/plone/src/addons/volto-volto-project/babel.config.js
deleted file mode 100644
index 51bd52b5df..0000000000
--- a/apps/plone/src/addons/volto-volto-project/babel.config.js
+++ /dev/null
@@ -1,17 +0,0 @@
-module.exports = function (api) {
- api.cache(true);
- const presets = ['razzle'];
- const plugins = [
- [
- 'react-intl', // React Intl extractor, required for the whole i18n infrastructure to work
- {
- messagesDir: './build/messages/',
- },
- ],
- ];
-
- return {
- plugins,
- presets,
- };
-};
diff --git a/apps/plone/src/addons/volto-volto-project/dockerfiles/Dockerfile b/apps/plone/src/addons/volto-volto-project/dockerfiles/Dockerfile
deleted file mode 100644
index c413663a7f..0000000000
--- a/apps/plone/src/addons/volto-volto-project/dockerfiles/Dockerfile
+++ /dev/null
@@ -1,27 +0,0 @@
-# syntax=docker/dockerfile:1
-ARG VOLTO_VERSION
-FROM plone/frontend-builder:${VOLTO_VERSION} as builder
-
-ARG ADDON_NAME
-ARG ADDON_PATH
-
-# Copy addon code
-COPY --chown=node:node ./ /app/src/addons/${ADDON_PATH}/
-
-# Install
-RUN </node_modules/@plone/volto/cypress',
- '@plone/volto/babel': '/node_modules/@plone/volto/babel',
- '@plone/volto/(.*)$': '/node_modules/@plone/volto/src/$1',
- '@package/(.*)$': '/src/$1',
- '@root/(.*)$': '/src/$1',
- '~/(.*)$': '/src/$1',
- 'load-volto-addons':
- '/node_modules/@plone/volto/jest-addons-loader.js',
- '\\.(css|less|scss|sass)$': 'identity-obj-proxy',
- },
- transform: {
- '^.+\\.js(x)?$': 'babel-jest',
- '^.+\\.(png)$': 'jest-file',
- '^.+\\.(jpg)$': 'jest-file',
- '^.+\\.(svg)$': './node_modules/@plone/volto/jest-svgsystem-transform.js',
- },
- coverageThreshold: {
- global: {
- branches: 5,
- functions: 5,
- lines: 5,
- statements: 5,
- },
- },
-};
diff --git a/apps/plone/src/addons/volto-volto-project/locales/volto.pot b/apps/plone/src/addons/volto-volto-project/locales/volto.pot
deleted file mode 100644
index 1b22bc00e9..0000000000
--- a/apps/plone/src/addons/volto-volto-project/locales/volto.pot
+++ /dev/null
@@ -1,14 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"POT-Creation-Date: 2021-01-29T12:34:47.097Z\n"
-"Last-Translator: Plone i18n \n"
-"Language-Team: Plone i18n \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"Language-Code: en\n"
-"Language-Name: English\n"
-"Preferred-Encodings: utf-8\n"
-"Domain: volto\n"
diff --git a/apps/plone/src/addons/volto-volto-project/package.json b/apps/plone/src/addons/volto-volto-project/package.json
deleted file mode 100644
index 3a01e12350..0000000000
--- a/apps/plone/src/addons/volto-volto-project/package.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- "name": "volto-volto-project",
- "version": "0.1.0",
- "description": "volto-volto-project: Volto add-on",
- "main": "src/index.ts",
- "license": "MIT",
- "keywords": [
- "volto-addon",
- "volto",
- "plone",
- "react"
- ],
- "publishConfig": {
- "access": "public"
- },
- "scripts": {
- "i18n": "rm -rf build/messages && NODE_ENV=production i18n --addon",
- "dry-release": "release-it --dry-run",
- "release": "release-it",
- "release-major-alpha": "release-it major --preRelease=alpha",
- "release-alpha": "release-it --preRelease=alpha",
- "release-rc": "release-it --preRelease=rc"
- },
- "devDependencies": {
- "@babel/eslint-parser": "7.22.15",
- "@plone/scripts": "workspace:*",
- "eslint": "8.49.0",
- "eslint-config-prettier": "9.0.0",
- "eslint-config-react-app": "7.0.1",
- "eslint-plugin-flowtype": "8.0.3",
- "eslint-plugin-import": "2.28.1",
- "eslint-plugin-jsx-a11y": "^6.7.1",
- "eslint-plugin-prettier": "5.0.0",
- "eslint-plugin-react": "7.33.2",
- "eslint-plugin-react-hooks": "4.6.0",
- "postcss-scss": "4.0.8",
- "prettier": "3.0.3",
- "release-it": "^16.1.5",
- "stylelint": "15.10.3",
- "stylelint-config-idiomatic-order": "9.0.0",
- "stylelint-config-sass-guidelines": "10.0.0",
- "stylelint-prettier": "4.0.2"
- }
-}
diff --git a/apps/plone/src/addons/volto-volto-project/src/index.ts b/apps/plone/src/addons/volto-volto-project/src/index.ts
deleted file mode 100644
index 2681f2ee1d..0000000000
--- a/apps/plone/src/addons/volto-volto-project/src/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { ConfigData } from '@plone/registry';
-
-const applyConfig = (config: ConfigData) => {
- return config;
-};
-
-export default applyConfig;
diff --git a/apps/plone/src/addons/volto-volto-project/src/theme/theme.config b/apps/plone/src/addons/volto-volto-project/src/theme/theme.config
deleted file mode 100644
index 5d3e8f2dd3..0000000000
--- a/apps/plone/src/addons/volto-volto-project/src/theme/theme.config
+++ /dev/null
@@ -1,88 +0,0 @@
-/*******************************
- Theme Selection
-*******************************/
-
-/* To override a theme for an individual element specify theme name below */
-
-/* Global */
-@site : 'pastanaga';
-@reset : 'pastanaga';
-
-/* Elements */
-@button : 'pastanaga';
-@container : 'pastanaga';
-@divider : 'pastanaga';
-@flag : 'pastanaga';
-@header : 'pastanaga';
-@icon : 'pastanaga';
-@image : 'pastanaga';
-@input : 'pastanaga';
-@label : 'pastanaga';
-@list : 'pastanaga';
-@loader : 'pastanaga';
-@placeholder : 'pastanaga';
-@rail : 'pastanaga';
-@reveal : 'pastanaga';
-@segment : 'pastanaga';
-@step : 'pastanaga';
-
-/* Collections */
-@breadcrumb : 'pastanaga';
-@form : 'pastanaga';
-@grid : 'pastanaga';
-@menu : 'pastanaga';
-@message : 'pastanaga';
-@table : 'pastanaga';
-
-/* Modules */
-@accordion : 'pastanaga';
-@checkbox : 'pastanaga';
-@dimmer : 'pastanaga';
-@dropdown : 'pastanaga';
-@embed : 'pastanaga';
-@modal : 'pastanaga';
-@nag : 'pastanaga';
-@popup : 'pastanaga';
-@progress : 'pastanaga';
-@rating : 'pastanaga';
-@search : 'pastanaga';
-@shape : 'pastanaga';
-@sidebar : 'pastanaga';
-@sticky : 'pastanaga';
-@tab : 'pastanaga';
-@transition : 'pastanaga';
-
-/* Views */
-@ad : 'pastanaga';
-@card : 'pastanaga';
-@comment : 'pastanaga';
-@feed : 'pastanaga';
-@item : 'pastanaga';
-@statistic : 'pastanaga';
-
-/* Extras */
-@main : 'pastanaga';
-@custom : 'pastanaga';
-
-/*******************************
- Folders
-*******************************/
-
-/* Path to theme packages */
-@themesFolder : '~volto-themes';
-
-/* Path to site override folder */
-@siteFolder : "volto-volto-project/theme";
-
-/*******************************
- Import Theme
-*******************************/
-
-@import (multiple) "~semantic-ui-less/theme.less";
-@fontPath : "~volto-themes/@{theme}/assets/fonts";
-
-.loadAddonOverrides() {
- @import (optional) "@{siteFolder}/@{addon}/@{addontype}s/@{addonelement}.overrides";
-}
-
-/* End Config */
diff --git a/apps/plone/src/addons/volto-volto-project/towncrier.toml b/apps/plone/src/addons/volto-volto-project/towncrier.toml
deleted file mode 100644
index 489f792262..0000000000
--- a/apps/plone/src/addons/volto-volto-project/towncrier.toml
+++ /dev/null
@@ -1,33 +0,0 @@
-[tool.towncrier]
-filename = "CHANGELOG.md"
-directory = "news/"
-title_format = "## {version} ({project_date})"
-underlines = ["", "", ""]
-template = "./node_modules/@plone/scripts/templates/towncrier_template.jinja"
-start_string = "\n"
-issue_format = "[#{issue}](https://github.com/voltovolto-project/pull/{issue})"
-
-[[tool.towncrier.type]]
-directory = "breaking"
-name = "Breaking"
-showcontent = true
-
-[[tool.towncrier.type]]
-directory = "feature"
-name = "Feature"
-showcontent = true
-
-[[tool.towncrier.type]]
-directory = "bugfix"
-name = "Bugfix"
-showcontent = true
-
-[[tool.towncrier.type]]
-directory = "internal"
-name = "Internal"
-showcontent = true
-
-[[tool.towncrier.type]]
-directory = "documentation"
-name = "Documentation"
-showcontent = true
diff --git a/apps/plone/src/client.js b/apps/plone/src/client.js
deleted file mode 100644
index 9be1247a3a..0000000000
--- a/apps/plone/src/client.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import client from '@plone/volto/start-client';
-
-client();
-
-if (module.hot) {
- module.hot.accept();
-}
diff --git a/apps/plone/src/config.js b/apps/plone/src/config.js
deleted file mode 100644
index 9e0bea7532..0000000000
--- a/apps/plone/src/config.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/** Volto Project Configuration
- * The recommended way for configuring a Volto project is using an add-on.
- * The project should only be considered as boilerplate, and all the code and
- * configuration should happen and be placed in add-ons.
- *
- * Both configuring directly and placing code in a project is discouraged, and might be
- * removed at some point from Volto.
- *
- * The local project is left for backwards compatibility for existing projects.
- */
-
-// [Internal] All the imports of modules required for the configuration *must* happen
-// here BEFORE the following line
-import '@plone/volto/config';
-
-export default function applyConfig(config) {
- return config;
-}
diff --git a/apps/plone/src/customizations/README.md b/apps/plone/src/customizations/README.md
deleted file mode 100644
index 29ccb1bcee..0000000000
--- a/apps/plone/src/customizations/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-
-# Local customizations
-
-To override a file in the Volto package, add the directory structure and file to this folder.
-For example, to override the `Logo.svg` file, add the directory structure `components/theme/Logo`, and add the file `Logo.svg` to it.
-To apply your customizations, restart the server.
diff --git a/apps/plone/src/index.js b/apps/plone/src/index.js
deleted file mode 100644
index 9ae184cc6b..0000000000
--- a/apps/plone/src/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import start from '@plone/volto/start-server';
-
-const reloadServer = start();
-
-if (module.hot) {
- reloadServer();
-}
diff --git a/apps/plone/src/reducers/index.js b/apps/plone/src/reducers/index.js
deleted file mode 100644
index 520fa47c6d..0000000000
--- a/apps/plone/src/reducers/index.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Root reducer.
- * @module reducers/root
- */
-
-import defaultReducers from '@plone/volto/reducers';
-
-/**
- * Root reducer.
- * @function
- * @param {Object} state Current state.
- * @param {Object} action Action to be handled.
- * @returns {Object} New state.
- */
-const reducers = {
- ...defaultReducers,
- // Add your reducers here
-};
-
-export default reducers;
diff --git a/apps/plone/src/routes.js b/apps/plone/src/routes.js
deleted file mode 100644
index d23ac9305d..0000000000
--- a/apps/plone/src/routes.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Routes.
- * @module routes
- */
-
-import { defaultRoutes } from '@plone/volto/routes';
-import config from '@plone/volto/registry';
-
-/**
- * Routes array.
- * @array
- * @returns {array} Routes.
- */
-const routes = [
- {
- path: '/',
- component: config.getComponent('App').component, // Change this if you want a different component
- routes: [
- // Add your routes here
- ...(config.addonRoutes || []),
- ...defaultRoutes,
- ],
- },
-];
-
-export default routes;
diff --git a/apps/plone/src/theme.js b/apps/plone/src/theme.js
deleted file mode 100644
index 41018009fb..0000000000
--- a/apps/plone/src/theme.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import 'semantic-ui-less/semantic.less';
-import '@plone/volto/../theme/themes/pastanaga/extras/extras.less';
diff --git a/apps/plone/tsconfig.json b/apps/plone/tsconfig.json
deleted file mode 100644
index a6ea060da5..0000000000
--- a/apps/plone/tsconfig.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "compilerOptions": {
- "target": "ESNext",
- "lib": ["DOM", "DOM.Iterable", "ESNext"],
- "module": "commonjs",
- "allowJs": true,
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "moduleResolution": "Node",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "noEmit": true,
- "jsx": "react-jsx",
- "paths": {
- "@plone/volto/*": ["../node_modules/@plone/volto/src/*", "../node_modules/@plone/volto/types/*"]
- },
- "baseUrl": "src"
- },
- "include": ["src"],
- "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/apps/remix/README.md b/apps/remix/README.md
index a339650494..f9d62166c0 100644
--- a/apps/remix/README.md
+++ b/apps/remix/README.md
@@ -3,6 +3,10 @@
This is a proof of concept of a [Remix](https://remix.run) app, using the `@plone/client` and `@plone/components` libraries.
This is intended to serve as both a playground for the development of both packages and as a demo of Plone using Remix.
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
## Development
diff --git a/apps/remix/app/client.ts b/apps/remix/app/client.ts
new file mode 100644
index 0000000000..0eec9cd62e
--- /dev/null
+++ b/apps/remix/app/client.ts
@@ -0,0 +1,8 @@
+import ploneClient from '@plone/client';
+import config from '@plone/registry';
+
+const cli = ploneClient.initialize({
+ apiPath: config.settings.apiPath,
+});
+
+export { cli as ploneClient };
diff --git a/apps/remix/app/config.ts b/apps/remix/app/config.ts
index c2f3c47921..ae8881a358 100644
--- a/apps/remix/app/config.ts
+++ b/apps/remix/app/config.ts
@@ -1,9 +1,15 @@
+import config from '@plone/registry';
+import { blocksConfig, slate } from '@plone/blocks';
+
const settings = {
apiPath: 'http://localhost:8080/Plone',
+ slate,
};
-const config = {
- settings,
-};
+// @ts-expect-error We need to fix typing
+config.set('settings', settings);
+
+// @ts-expect-error We need to fix typing
+config.set('blocks', { blocksConfig });
export default config;
diff --git a/apps/remix/app/entry.client.tsx b/apps/remix/app/entry.client.tsx
index 3f01ce18b1..4680b84a57 100644
--- a/apps/remix/app/entry.client.tsx
+++ b/apps/remix/app/entry.client.tsx
@@ -7,6 +7,7 @@
import { RemixBrowser } from '@remix-run/react';
import { startTransition, StrictMode } from 'react';
import { hydrateRoot } from 'react-dom/client';
+import './config';
startTransition(() => {
hydrateRoot(
diff --git a/apps/remix/app/entry.server.tsx b/apps/remix/app/entry.server.tsx
index dda7030aea..9af132970a 100644
--- a/apps/remix/app/entry.server.tsx
+++ b/apps/remix/app/entry.server.tsx
@@ -11,6 +11,7 @@ import { createReadableStreamFromReadable } from '@remix-run/node';
import { RemixServer } from '@remix-run/react';
import { isbot } from 'isbot';
import { renderToPipeableStream } from 'react-dom/server';
+import './config';
const ABORT_DELAY = 5_000;
diff --git a/apps/remix/app/root.tsx b/apps/remix/app/root.tsx
index f76fa2596c..ecfb28c8be 100644
--- a/apps/remix/app/root.tsx
+++ b/apps/remix/app/root.tsx
@@ -2,22 +2,33 @@ import { cssBundleHref } from '@remix-run/css-bundle';
import type { LinksFunction } from '@remix-run/node';
import {
Links,
- LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
+ useHref,
+ useLocation,
+ useNavigate,
+ useParams,
} from '@remix-run/react';
import { useState } from 'react';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { PloneClientProvider } from '@plone/providers';
+import { QueryClient } from '@tanstack/react-query';
import PloneClient from '@plone/client';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+import '@plone/components/dist/basic.css';
+import { flattenToAppURL } from './utils';
+import { PloneProvider } from '@plone/providers';
+import config from '@plone/registry';
+
export const links: LinksFunction = () => [
...(cssBundleHref ? [{ rel: 'stylesheet', href: cssBundleHref }] : []),
];
+function useHrefLocal(to: string) {
+ return useHref(flattenToAppURL(to));
+}
+
export default function App() {
const [queryClient] = useState(
() =>
@@ -34,10 +45,16 @@ export default function App() {
const [ploneClient] = useState(() =>
PloneClient.initialize({
- apiPath: 'http://localhost:8080/Plone',
+ apiPath: config.settings.apiPath,
}),
);
+ const RRNavigate = useNavigate();
+
+ const navigate = (to: string) => {
+ return RRNavigate(flattenToAppURL(to));
+ };
+
return (
@@ -47,15 +64,19 @@ export default function App() {
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
);
diff --git a/apps/remix/app/routes/$.tsx b/apps/remix/app/routes/$.tsx
new file mode 100644
index 0000000000..abd1fd4bbd
--- /dev/null
+++ b/apps/remix/app/routes/$.tsx
@@ -0,0 +1,79 @@
+import {
+ json,
+ type LoaderFunctionArgs,
+ type MetaFunction,
+} from '@remix-run/node';
+import {
+ dehydrate,
+ QueryClient,
+ HydrationBoundary,
+ useQuery,
+} from '@tanstack/react-query';
+import { flattenToAppURL } from '../utils';
+import { useLoaderData, useLocation } from '@remix-run/react';
+import { usePloneClient } from '@plone/providers';
+import { Breadcrumbs, RenderBlocks } from '@plone/components';
+import config from '@plone/registry';
+import { ploneClient } from '../client';
+
+export const meta: MetaFunction = () => {
+ return [
+ { title: 'Plone on Remix' },
+ { name: 'description', content: 'Welcome to Plone on Remix!' },
+ ];
+};
+
+const expand = ['breadcrumbs', 'navigation'];
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export const loader = async ({ params, request }: LoaderFunctionArgs) => {
+ const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ // With SSR, we usually want to set some default staleTime
+ // above 0 to avoid refetching immediately on the client
+ staleTime: 60 * 1000,
+ },
+ },
+ });
+
+ const { getContentQuery } = ploneClient;
+
+ await queryClient.prefetchQuery(
+ getContentQuery({ path: flattenToAppURL(request.url), expand }),
+ );
+
+ return json({ dehydratedState: dehydrate(queryClient) });
+};
+
+function Page() {
+ const { getContentQuery } = usePloneClient();
+ const pathname = useLocation().pathname;
+ const { data } = useQuery(getContentQuery({ path: pathname, expand }));
+
+ if (!data) return null;
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export default function Index() {
+ const { dehydratedState } = useLoaderData();
+
+ return (
+
+
+
+ );
+}
diff --git a/apps/remix/app/routes/_index.tsx b/apps/remix/app/routes/_index.tsx
index 1736d4a86b..2ad6a19bbe 100644
--- a/apps/remix/app/routes/_index.tsx
+++ b/apps/remix/app/routes/_index.tsx
@@ -9,10 +9,12 @@ import {
HydrationBoundary,
useQuery,
} from '@tanstack/react-query';
-import ploneClient from '@plone/client';
import { flattenToAppURL } from '../utils';
import { useLoaderData, useLocation } from '@remix-run/react';
import { usePloneClient } from '@plone/providers';
+import { Breadcrumbs, RenderBlocks } from '@plone/components';
+import config from '@plone/registry';
+import { ploneClient } from '../client';
export const meta: MetaFunction = () => {
return [
@@ -35,10 +37,7 @@ export const loader = async ({ params, request }: LoaderFunctionArgs) => {
},
});
- const cli = ploneClient.initialize({
- apiPath: 'http://localhost:8080/Plone',
- });
- const { getContentQuery } = cli;
+ const { getContentQuery } = ploneClient;
await queryClient.prefetchQuery(
getContentQuery({ path: flattenToAppURL(request.url), expand }),
@@ -47,17 +46,34 @@ export const loader = async ({ params, request }: LoaderFunctionArgs) => {
return json({ dehydratedState: dehydrate(queryClient) });
};
-export default function Index() {
- const { dehydratedState } = useLoaderData();
+function Page() {
const { getContentQuery } = usePloneClient();
const pathname = useLocation().pathname;
const { data } = useQuery(getContentQuery({ path: pathname, expand }));
+ if (!data) return null;
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export default function Index() {
+ const { dehydratedState } = useLoaderData();
+
return (
-
-
{JSON.stringify(data, null, 2)}
-
+
);
}
diff --git a/apps/remix/app/utils.ts b/apps/remix/app/utils.ts
index f6513198f8..c297613f90 100644
--- a/apps/remix/app/utils.ts
+++ b/apps/remix/app/utils.ts
@@ -9,9 +9,9 @@ import config from './config';
*/
export function flattenToAppURL(url: string) {
const { settings } = config;
+
return (
- (url &&
- url.replace(settings.apiPath, '').replace('http://localhost:3000', '')) ||
- '/'
+ url &&
+ url.replace(settings.apiPath, '').replace('http://localhost:3000', '')
);
}
diff --git a/apps/remix/package.json b/apps/remix/package.json
index b422edc87d..31d7ec65e8 100644
--- a/apps/remix/package.json
+++ b/apps/remix/package.json
@@ -11,24 +11,27 @@
"typecheck": "tsc"
},
"dependencies": {
+ "@plone/blocks": "workspace: *",
"@plone/client": "workspace: *",
+ "@plone/components": "workspace: *",
"@plone/providers": "workspace: *",
- "@remix-run/css-bundle": "^2.4.0",
- "@remix-run/node": "^2.8.1",
- "@remix-run/react": "^2.8.1",
- "@remix-run/serve": "^2.8.1",
- "@tanstack/react-query": "^5.37.1",
+ "@plone/registry": "workspace: *",
+ "@remix-run/css-bundle": "^2.13.1",
+ "@remix-run/node": "^2.13.1",
+ "@remix-run/react": "^2.13.1",
+ "@remix-run/serve": "^2.13.1",
+ "@tanstack/react-query": "^5.59.0",
"isbot": "^4.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
- "@remix-run/dev": "^2.8.1",
- "@tanstack/react-query-devtools": "^5.37.1",
+ "@remix-run/dev": "^2.13.1",
+ "@tanstack/react-query-devtools": "^5.59.0",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
- "typescript": "^5.4.2",
- "vite": "^5.1.0",
+ "typescript": "^5.6.3",
+ "vite": "^5.4.9",
"vite-tsconfig-paths": "^4.2.1"
},
"engines": {
diff --git a/apps/remix/tsconfig.json b/apps/remix/tsconfig.json
index 9d87dd378f..42f049fa4a 100644
--- a/apps/remix/tsconfig.json
+++ b/apps/remix/tsconfig.json
@@ -25,8 +25,6 @@
"paths": {
"~/*": ["./app/*"]
},
-
- // Vite takes care of building everything, not tsc.
"noEmit": true
}
}
diff --git a/apps/remix/vite.config.ts b/apps/remix/vite.config.ts
index eeb1fd83d0..03bfa30a99 100644
--- a/apps/remix/vite.config.ts
+++ b/apps/remix/vite.config.ts
@@ -1,12 +1,20 @@
import { vitePlugin as remix } from '@remix-run/dev';
-import { installGlobals } from '@remix-run/node';
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
-
-installGlobals();
+import { PloneRegistryVitePlugin } from '@plone/registry/vite-plugin';
export default defineConfig({
- plugins: [remix(), tsconfigPaths()],
+ plugins: [
+ remix({
+ future: {
+ v3_fetcherPersist: true,
+ v3_relativeSplatPath: true,
+ v3_throwAbortReason: true,
+ },
+ }),
+ tsconfigPaths(),
+ PloneRegistryVitePlugin(),
+ ],
server: {
port: 3000,
},
diff --git a/apps/rr7/.eslintrc.cjs b/apps/rr7/.eslintrc.cjs
new file mode 100644
index 0000000000..b4a6a65b4d
--- /dev/null
+++ b/apps/rr7/.eslintrc.cjs
@@ -0,0 +1,80 @@
+/**
+ * This is intended to be a basic starting point for linting in your app.
+ * It relies on recommended configs out of the box for simplicity, but you can
+ * and should modify this configuration to best suit your team's needs.
+ */
+
+/** @type {import('eslint').Linter.Config} */
+module.exports = {
+ root: true,
+ parserOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ env: {
+ browser: true,
+ commonjs: true,
+ es6: true,
+ },
+
+ // Base config
+ extends: ['eslint:recommended'],
+
+ overrides: [
+ // React
+ {
+ files: ['**/*.{js,jsx,ts,tsx}'],
+ plugins: ['react', 'jsx-a11y'],
+ extends: [
+ 'plugin:react/recommended',
+ 'plugin:react/jsx-runtime',
+ 'plugin:react-hooks/recommended',
+ 'plugin:jsx-a11y/recommended',
+ ],
+ settings: {
+ react: {
+ version: 'detect',
+ },
+ formComponents: ['Form'],
+ linkComponents: [
+ { name: 'Link', linkAttribute: 'to' },
+ { name: 'NavLink', linkAttribute: 'to' },
+ ],
+ },
+ },
+
+ // Typescript
+ {
+ files: ['**/*.{ts,tsx}'],
+ plugins: ['@typescript-eslint', 'import'],
+ parser: '@typescript-eslint/parser',
+ settings: {
+ 'import/internal-regex': '^~/',
+ 'import/resolver': {
+ node: {
+ extensions: ['.ts', '.tsx'],
+ },
+ typescript: {
+ alwaysTryTypes: true,
+ },
+ },
+ },
+ extends: [
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:import/recommended',
+ 'plugin:import/typescript',
+ ],
+ },
+
+ // Node
+ {
+ files: ['.eslintrc.js'],
+ env: {
+ node: true,
+ },
+ },
+ ],
+};
diff --git a/apps/rr7/.gitignore b/apps/rr7/.gitignore
new file mode 100644
index 0000000000..f1eb112b25
--- /dev/null
+++ b/apps/rr7/.gitignore
@@ -0,0 +1,7 @@
+node_modules
+
+/.cache
+/build
+.env
+.react-router
+.registry.loader.js
diff --git a/apps/rr7/README.md b/apps/rr7/README.md
new file mode 100644
index 0000000000..d6b9daf12c
--- /dev/null
+++ b/apps/rr7/README.md
@@ -0,0 +1,30 @@
+# Plone on React Router 7
+
+This is a proof of concept of a [React Router](https://reactrouter.com/dev/docs) app, using the `@plone/*` libraries.
+This is intended to serve as both a playground for the development of both packages and as a demo of Plone using Remix.
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
+
+## Development
+
+To start, from the root of the monorepo, issue the following commands.
+
+```shell
+pnpm install
+pnpm --filter plone-remix run dev
+```
+
+Then start the Plone backend.
+
+% TODO MAKEFILE
+```shell
+make backend-docker-start
+```
+
+
+## About this app
+
+- [Remix Docs](https://remix.run/docs/en/main)
diff --git a/apps/rr7/app/client.ts b/apps/rr7/app/client.ts
new file mode 100644
index 0000000000..0eec9cd62e
--- /dev/null
+++ b/apps/rr7/app/client.ts
@@ -0,0 +1,8 @@
+import ploneClient from '@plone/client';
+import config from '@plone/registry';
+
+const cli = ploneClient.initialize({
+ apiPath: config.settings.apiPath,
+});
+
+export { cli as ploneClient };
diff --git a/apps/rr7/app/config.ts b/apps/rr7/app/config.ts
new file mode 100644
index 0000000000..e7133efdce
--- /dev/null
+++ b/apps/rr7/app/config.ts
@@ -0,0 +1,15 @@
+import config from '@plone/registry';
+import { blocksConfig, slate } from '@plone/blocks';
+
+const settings = {
+ apiPath: 'http://localhost:3000',
+ slate,
+};
+
+// @ts-expect-error We need to fix typing
+config.set('settings', settings);
+
+// @ts-expect-error We need to fix typing
+config.set('blocks', { blocksConfig });
+
+export default config;
diff --git a/apps/rr7/app/root.tsx b/apps/rr7/app/root.tsx
new file mode 100644
index 0000000000..50808c5b17
--- /dev/null
+++ b/apps/rr7/app/root.tsx
@@ -0,0 +1,98 @@
+import { useState } from 'react';
+import {
+ Links,
+ Meta,
+ Outlet,
+ Scripts,
+ ScrollRestoration,
+ useHref,
+ useLocation,
+ useNavigate,
+ useParams,
+} from 'react-router';
+import type { LinksFunction } from 'react-router';
+
+import { QueryClient } from '@tanstack/react-query';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+import PloneClient from '@plone/client';
+import { PloneProvider } from '@plone/providers';
+import { flattenToAppURL } from './utils';
+import config from '@plone/registry';
+import './config';
+
+import '@plone/components/dist/basic.css';
+
+function useHrefLocal(to: string) {
+ return useHref(flattenToAppURL(to));
+}
+
+export const links: LinksFunction = () => [
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
+ {
+ rel: 'preconnect',
+ href: 'https://fonts.gstatic.com',
+ crossOrigin: 'anonymous',
+ },
+ {
+ rel: 'stylesheet',
+ href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
+ },
+];
+
+export function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
+
+export default function App() {
+ const [queryClient] = useState(
+ () =>
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ // With SSR, we usually want to set some default staleTime
+ // above 0 to avoid refetching immediately on the client
+ staleTime: 60 * 1000,
+ },
+ },
+ }),
+ );
+
+ const [ploneClient] = useState(() =>
+ PloneClient.initialize({
+ apiPath: config.settings.apiPath,
+ }),
+ );
+
+ const RRNavigate = useNavigate();
+ const navigate = (to: string) => {
+ return RRNavigate(flattenToAppURL(to));
+ };
+
+ return (
+
+
+
+
+ );
+}
diff --git a/apps/rr7/app/routes.ts b/apps/rr7/app/routes.ts
new file mode 100644
index 0000000000..579d64cd84
--- /dev/null
+++ b/apps/rr7/app/routes.ts
@@ -0,0 +1,7 @@
+import type { RouteConfig } from '@react-router/dev/routes';
+import { index, route } from '@react-router/dev/routes';
+
+export const routes: RouteConfig = [
+ index('routes/home.tsx'),
+ route('*', 'routes/$.tsx'),
+];
diff --git a/apps/rr7/app/routes/$.tsx b/apps/rr7/app/routes/$.tsx
new file mode 100644
index 0000000000..5216c4d188
--- /dev/null
+++ b/apps/rr7/app/routes/$.tsx
@@ -0,0 +1,2 @@
+import Content, { loader } from './home';
+export { loader, Content as default };
diff --git a/apps/rr7/app/routes/home.tsx b/apps/rr7/app/routes/home.tsx
new file mode 100644
index 0000000000..c470ad9f42
--- /dev/null
+++ b/apps/rr7/app/routes/home.tsx
@@ -0,0 +1,79 @@
+import type { LoaderArgs } from '../routes/+types.home';
+import {
+ dehydrate,
+ QueryClient,
+ HydrationBoundary,
+ useQuery,
+ useQueryClient,
+} from '@tanstack/react-query';
+import { flattenToAppURL } from '../utils';
+import { useLoaderData, useLocation } from 'react-router';
+import { usePloneClient } from '@plone/providers';
+import { Breadcrumbs, RenderBlocks } from '@plone/components';
+import config from '@plone/registry';
+import { ploneClient } from '../client';
+
+import type { MetaFunction } from 'react-router';
+
+export const meta: MetaFunction = () => {
+ return [
+ { title: 'Plone on React Router 7' },
+ { name: 'description', content: 'Welcome to Plone!' },
+ ];
+};
+
+const expand = ['breadcrumbs', 'navigation'];
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export async function loader({ params, request }: LoaderArgs) {
+ const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ // With SSR, we usually want to set some default staleTime
+ // above 0 to avoid refetching immediately on the client
+ staleTime: 60 * 1000,
+ },
+ },
+ });
+
+ const { getContentQuery } = ploneClient;
+
+ await queryClient.prefetchQuery(
+ getContentQuery({ path: flattenToAppURL(request.url), expand }),
+ );
+
+ return { dehydratedState: dehydrate(queryClient) };
+}
+
+function Page() {
+ const { getContentQuery } = usePloneClient();
+ const pathname = useLocation().pathname;
+ const { data } = useQuery(getContentQuery({ path: pathname, expand }));
+
+ if (!data) return 'Loading...';
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export default function Content() {
+ const { dehydratedState } = useLoaderData();
+ const queryClient = useQueryClient();
+
+ return (
+
+
+
+ );
+}
diff --git a/apps/rr7/app/utils.ts b/apps/rr7/app/utils.ts
new file mode 100644
index 0000000000..c297613f90
--- /dev/null
+++ b/apps/rr7/app/utils.ts
@@ -0,0 +1,17 @@
+import config from './config';
+
+/**
+ * Flatten to app server URL - Given a URL if it starts with the API server URL
+ * this method flattens it (removes) the server part
+ * TODO: Update it when implementing non-root based app location (on a
+ * directory other than /, eg. /myapp)
+ * @method flattenToAppURL
+ */
+export function flattenToAppURL(url: string) {
+ const { settings } = config;
+
+ return (
+ url &&
+ url.replace(settings.apiPath, '').replace('http://localhost:3000', '')
+ );
+}
diff --git a/apps/rr7/package.json b/apps/rr7/package.json
new file mode 100644
index 0000000000..fa204ea503
--- /dev/null
+++ b/apps/rr7/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "plone-rr7",
+ "private": true,
+ "sideEffects": false,
+ "type": "module",
+ "scripts": {
+ "dev": "react-router dev",
+ "build": "react-router build",
+ "start:prod": "react-router-serve ./build/server/index.js",
+ "typecheck": "react-router typegen && tsc",
+ "typegen": "react-router typegen"
+ },
+ "dependencies": {
+ "@react-router/node": "7.0.0-pre.4",
+ "@react-router/serve": "7.0.0-pre.4",
+ "@tanstack/react-query": "^5.59.0",
+ "@tanstack/react-query-devtools": "^5.59.0",
+ "isbot": "^5.1.17",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-router": "7.0.0-pre.4"
+ },
+ "devDependencies": {
+ "@plone/blocks": "workspace:*",
+ "@plone/client": "workspace:*",
+ "@plone/components": "workspace:*",
+ "@plone/providers": "workspace:*",
+ "@plone/registry": "workspace:*",
+ "@react-router/dev": "7.0.0-pre.4",
+ "@types/react": "^18.3.9",
+ "@types/react-dom": "^18.3.0",
+ "typescript": "^5.6.3",
+ "vite": "^5.4.9",
+ "vite-tsconfig-paths": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+}
diff --git a/apps/rr7/public/favicon.ico b/apps/rr7/public/favicon.ico
new file mode 100644
index 0000000000..5dbdfcddcb
Binary files /dev/null and b/apps/rr7/public/favicon.ico differ
diff --git a/apps/rr7/tsconfig.json b/apps/rr7/tsconfig.json
new file mode 100644
index 0000000000..29b2316386
--- /dev/null
+++ b/apps/rr7/tsconfig.json
@@ -0,0 +1,33 @@
+{
+ "include": [
+ "**/*.ts",
+ "**/*.tsx",
+ "**/.server/**/*.ts",
+ "**/.server/**/*.tsx",
+ "**/.client/**/*.ts",
+ "**/.client/**/*.tsx",
+ ".react-router/types/**/*"
+ ],
+ "compilerOptions": {
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
+ "types": ["@react-router/node", "vite/client"],
+ "isolatedModules": true,
+ "esModuleInterop": true,
+ "jsx": "react-jsx",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "resolveJsonModule": true,
+ "target": "ES2022",
+ "strict": true,
+ "allowJs": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "baseUrl": ".",
+ "paths": {
+ "~/*": ["./app/*"]
+ },
+ "noEmit": true,
+ "rootDirs": [".", "./.react-router/types"],
+ "plugins": [{ "name": "@react-router/dev" }]
+ }
+}
diff --git a/apps/rr7/vite.config.ts b/apps/rr7/vite.config.ts
new file mode 100644
index 0000000000..723e0323af
--- /dev/null
+++ b/apps/rr7/vite.config.ts
@@ -0,0 +1,28 @@
+import { reactRouter } from '@react-router/dev/vite';
+import tsconfigPaths from 'vite-tsconfig-paths';
+import { defineConfig } from 'vite';
+import { PloneRegistryVitePlugin } from '@plone/registry/vite-plugin';
+
+export default defineConfig({
+ plugins: [
+ reactRouter({
+ // Server-side render by default, to enable SPA mode set this to `false`
+ ssr: true,
+ }),
+ tsconfigPaths(),
+ PloneRegistryVitePlugin(),
+ ],
+ server: {
+ port: 3000,
+ proxy: {
+ '^/\\+\\+api\\+\\+($$|/.*)': {
+ target:
+ 'http://localhost:8080/VirtualHostBase/http/localhost:3000/Plone/++api++/VirtualHostRoot',
+ rewrite: (path) => {
+ console.log(path);
+ return path.replace('/++api++', '');
+ },
+ },
+ },
+ },
+});
diff --git a/apps/vite-ssr/README.md b/apps/vite-ssr/README.md
index 2894982c36..f672cf5770 100644
--- a/apps/vite-ssr/README.md
+++ b/apps/vite-ssr/README.md
@@ -1,6 +1,11 @@
# Plone on Vite with SSR mode
-This is a proof of concept of a [Vite](https://vitejs.dev) build, using `@plone/client` and `@plone/components` libraries.
+This is a proof of concept of a [Vite](https://vite.dev/) build, using `@plone/client` and `@plone/components` libraries.
This is intended to serve as both a playground for the development of both packages and as a demo of Plone using Vite built with server side rendering (SSR).
It also uses [TanStack Router](https://tanstack.com/router/latest/docs/framework/react/overview) for its routing library.
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
diff --git a/apps/vite-ssr/package.json b/apps/vite-ssr/package.json
index 99ff75eb99..6a25419d6e 100644
--- a/apps/vite-ssr/package.json
+++ b/apps/vite-ssr/package.json
@@ -17,15 +17,14 @@
"@plone/providers": "workspace:*",
"@plone/registry": "workspace:*",
"@plone/blocks": "workspace:*",
- "@tanstack/react-query": "^5.37.1",
- "@tanstack/react-router": "^1.28.2",
- "@tanstack/react-router-server": "^1.28.2",
- "@tanstack/router-devtools": "^1.28.2",
- "@tanstack/router-vite-plugin": "^1.28.2",
+ "@tanstack/react-query": "^5.59.0",
+ "@tanstack/react-router": "^1.34.9",
+ "@tanstack/start": "^1.34.9",
+ "@tanstack/router-vite-plugin": "^1.34.8",
"axios": "^1.6.5",
"get-port": "^7.0.0",
"react": "^18.2.0",
- "react-aria-components": "^1.1.1",
+ "react-aria-components": "^1.4.0",
"react-dom": "^18.2.0",
"sirv": "^2.0.4"
},
@@ -34,7 +33,8 @@
"@babel/generator": "^7.23.6",
"@plone/types": "workspace:*",
"@rollup/plugin-babel": "^6.0.4",
- "@tanstack/react-query-devtools": "^5.37.1",
+ "@tanstack/react-query-devtools": "^5.59.0",
+ "@tanstack/router-devtools": "^1.34.9",
"@types/express": "^4.17.21",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
@@ -44,7 +44,7 @@
"isbot": "^4.3.0",
"node-fetch": "^3.3.2",
"serve-static": "^1.15.0",
- "typescript": "^5.4.2",
+ "typescript": "^5.6.3",
"vite": "^5.2.9",
"vite-plugin-babel": "^1.2.0"
}
diff --git a/apps/vite-ssr/src/entry-client.tsx b/apps/vite-ssr/src/entry-client.tsx
index e178ab84f1..1f2e057b2c 100644
--- a/apps/vite-ssr/src/entry-client.tsx
+++ b/apps/vite-ssr/src/entry-client.tsx
@@ -1,6 +1,6 @@
import ReactDOM from 'react-dom/client';
-import { StartClient } from '@tanstack/react-router-server/client';
+import { StartClient } from '@tanstack/start/client';
import { createRouter } from './router';
import './config';
diff --git a/apps/vite-ssr/src/entry-server.tsx b/apps/vite-ssr/src/entry-server.tsx
index f701a08446..af56e3e91f 100644
--- a/apps/vite-ssr/src/entry-server.tsx
+++ b/apps/vite-ssr/src/entry-server.tsx
@@ -3,7 +3,7 @@ import ReactDOMServer from 'react-dom/server';
import { createMemoryHistory } from '@tanstack/react-router';
import { ServerResponse } from 'http';
import express from 'express';
-import { StartServer } from '@tanstack/react-router-server/server';
+import { StartServer } from '@tanstack/start/server';
import { createRouter } from './router';
import './config';
diff --git a/apps/vite-ssr/src/routeTree.gen.ts b/apps/vite-ssr/src/routeTree.gen.ts
index 81ad7b6840..64cb826aac 100644
--- a/apps/vite-ssr/src/routeTree.gen.ts
+++ b/apps/vite-ssr/src/routeTree.gen.ts
@@ -37,14 +37,23 @@ const IndexRoute = IndexImport.update({
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
'/$': {
+ id: '/$'
+ path: '/$'
+ fullPath: '/$'
preLoaderRoute: typeof SplatImport
parentRoute: typeof rootRoute
}
'/error': {
+ id: '/error'
+ path: '/error'
+ fullPath: '/error'
preLoaderRoute: typeof ErrorImport
parentRoute: typeof rootRoute
}
@@ -53,10 +62,34 @@ declare module '@tanstack/react-router' {
// Create and export the route tree
-export const routeTree = rootRoute.addChildren([
+export const routeTree = rootRoute.addChildren({
IndexRoute,
SplatRoute,
ErrorRoute,
-])
+})
/* prettier-ignore-end */
+
+/* ROUTE_MANIFEST_START
+{
+ "routes": {
+ "__root__": {
+ "filePath": "__root.tsx",
+ "children": [
+ "/",
+ "/$",
+ "/error"
+ ]
+ },
+ "/": {
+ "filePath": "index.tsx"
+ },
+ "/$": {
+ "filePath": "$.tsx"
+ },
+ "/error": {
+ "filePath": "error.tsx"
+ }
+ }
+}
+ROUTE_MANIFEST_END */
diff --git a/apps/vite-ssr/src/router.tsx b/apps/vite-ssr/src/router.tsx
index 7a3a80f339..512782c598 100644
--- a/apps/vite-ssr/src/router.tsx
+++ b/apps/vite-ssr/src/router.tsx
@@ -1,16 +1,9 @@
-import {
- QueryClient,
- QueryClientProvider,
- dehydrate,
- hydrate,
-} from '@tanstack/react-query';
+import { QueryClient, dehydrate, hydrate } from '@tanstack/react-query';
import PloneClient from '@plone/client';
import { createRouter as createReactRouter } from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';
import { PloneClientProvider } from '@plone/providers';
-import { FlattenToAppURLProvider } from '@plone/components';
-import { flattenToAppURL } from './utils';
const queryClient = new QueryClient({
defaultOptions: {
@@ -45,12 +38,8 @@ export function createRouter() {
},
Wrap: ({ children }: { children: any }) => {
return (
-
-
-
- {children}
-
-
+
+ {children}
);
},
diff --git a/apps/vite-ssr/src/routes/__root.tsx b/apps/vite-ssr/src/routes/__root.tsx
index ef78a13d81..74bc65767a 100644
--- a/apps/vite-ssr/src/routes/__root.tsx
+++ b/apps/vite-ssr/src/routes/__root.tsx
@@ -4,14 +4,16 @@ import {
Outlet,
createRootRouteWithContext,
useRouter,
- useRouterState,
+ useLocation,
} from '@tanstack/react-router';
-import { DehydrateRouter } from '@tanstack/react-router-server/client';
+import { DehydrateRouter } from '@tanstack/start/client';
import { RouterContext } from '../routerContext';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { RouterProvider } from 'react-aria-components';
import '@plone/components/dist/basic.css';
+import { AppRouterProvider } from '@plone/providers';
+import { flattenToAppURL } from '../utils';
export const Route = createRootRouteWithContext()({
component: RootComponent,
@@ -54,11 +56,19 @@ function RootComponent() {
)}
- router.navigate({ to: path })}
+
+ router.navigate({ to: flattenToAppURL(path) })
+ }
+ // TODO: Investigate why this fails in @tanstack/router :/
+ useHref={(to) => {
+ return flattenToAppURL(to);
+ }}
+ flattenToAppURL={flattenToAppURL}
>
{/* Start rendering router matches */}
-
+
diff --git a/apps/vite-ssr/src/utils.ts b/apps/vite-ssr/src/utils.ts
index 4c1b6f5968..044a5b66dd 100644
--- a/apps/vite-ssr/src/utils.ts
+++ b/apps/vite-ssr/src/utils.ts
@@ -12,7 +12,9 @@ export function flattenToAppURL(url: string | undefined) {
const { settings } = config;
return (
(url &&
- url.replace(settings.apiPath, '').replace('http://localhost:3000', '')) ||
+ url
+ .replace(settings.apiPath, '')
+ .replace('http://localhost:8080/Plone', '')) ||
'/'
);
}
diff --git a/apps/vite/README.md b/apps/vite/README.md
index 417e695ef3..3e508c1a97 100644
--- a/apps/vite/README.md
+++ b/apps/vite/README.md
@@ -1,6 +1,11 @@
# Plone on Vite
-This is a proof of concept of a [Vite](https://vitejs.dev) build, using `@plone/client` and `@plone/components` libraries.
+This is a proof of concept of a [Vite](https://vite.dev/) build, using `@plone/client` and `@plone/components` libraries.
This is intended to serve as both a playground for the development of both packages and as a demo of Plone using Vite.
It also uses [TanStack Router](https://tanstack.com/router/latest/docs/framework/react/overview) for its routing library.
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
diff --git a/apps/vite/package.json b/apps/vite/package.json
index 4bef020587..ec91f232ee 100644
--- a/apps/vite/package.json
+++ b/apps/vite/package.json
@@ -15,18 +15,19 @@
"@plone/registry": "workspace:*",
"@plone/blocks": "workspace:*",
"@tanstack/react-query": "^5.37.1",
- "@tanstack/react-router": "^1.29.2",
+ "@tanstack/react-router": "^1.67.0",
+ "@tanstack/router-devtools": "^1.67.0",
"axios": "^1.6.5",
"react": "^18.2.0",
- "react-aria-components": "^1.2.1",
+ "react-aria-components": "^1.4.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@tanstack/react-query-devtools": "^5.37.1",
- "@tanstack/router-devtools": "^1.29.2",
+ "@tanstack/router-plugin": "^1.66.1",
"@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18",
- "@vitejs/plugin-react": "^4.2.1",
- "vite": "^5.0.13"
+ "@vitejs/plugin-react": "^4.3.2",
+ "vite": "^5.4.9"
}
}
diff --git a/apps/vite/src/main.tsx b/apps/vite/src/main.tsx
index 7bdb58def6..204f5a8a31 100644
--- a/apps/vite/src/main.tsx
+++ b/apps/vite/src/main.tsx
@@ -1,12 +1,16 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
-import { RouterProvider, createRouter } from '@tanstack/react-router';
+import {
+ RouterProvider,
+ createRouter,
+ useLocation,
+ useParams,
+} from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { QueryClient } from '@tanstack/react-query';
import PloneClient from '@plone/client';
-import { PloneClientProvider } from '@plone/providers';
-import { FlattenToAppURLProvider } from '@plone/providers';
+import { PloneProvider } from '@plone/providers';
import { flattenToAppURL } from './utils';
import './config';
@@ -50,12 +54,21 @@ const rootElement = document.getElementById('app')!;
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
-
-
-
-
-
-
- ,
+
+ router.navigate({ to: flattenToAppURL(path) })
+ }
+ // TODO: Investigate why this fails in @tanstack/router :/
+ useHref={(to) => {
+ return flattenToAppURL(to);
+ }}
+ useParams={useParams}
+ flattenToAppURL={flattenToAppURL}
+ >
+
+ ,
);
}
diff --git a/apps/vite/src/routeTree.gen.ts b/apps/vite/src/routeTree.gen.ts
index 20ade03340..2c6342ccd1 100644
--- a/apps/vite/src/routeTree.gen.ts
+++ b/apps/vite/src/routeTree.gen.ts
@@ -31,10 +31,16 @@ const IndexRoute = IndexImport.update({
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
'/$': {
+ id: '/$'
+ path: '/$'
+ fullPath: '/$'
preLoaderRoute: typeof SplatImport
parentRoute: typeof rootRoute
}
@@ -43,6 +49,63 @@ declare module '@tanstack/react-router' {
// Create and export the route tree
-export const routeTree = rootRoute.addChildren([IndexRoute, SplatRoute])
+export interface FileRoutesByFullPath {
+ '/': typeof IndexRoute
+ '/$': typeof SplatRoute
+}
+
+export interface FileRoutesByTo {
+ '/': typeof IndexRoute
+ '/$': typeof SplatRoute
+}
+
+export interface FileRoutesById {
+ __root__: typeof rootRoute
+ '/': typeof IndexRoute
+ '/$': typeof SplatRoute
+}
+
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath
+ fullPaths: '/' | '/$'
+ fileRoutesByTo: FileRoutesByTo
+ to: '/' | '/$'
+ id: '__root__' | '/' | '/$'
+ fileRoutesById: FileRoutesById
+}
+
+export interface RootRouteChildren {
+ IndexRoute: typeof IndexRoute
+ SplatRoute: typeof SplatRoute
+}
+
+const rootRouteChildren: RootRouteChildren = {
+ IndexRoute: IndexRoute,
+ SplatRoute: SplatRoute,
+}
+
+export const routeTree = rootRoute
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes()
/* prettier-ignore-end */
+
+/* ROUTE_MANIFEST_START
+{
+ "routes": {
+ "__root__": {
+ "filePath": "__root.tsx",
+ "children": [
+ "/",
+ "/$"
+ ]
+ },
+ "/": {
+ "filePath": "index.tsx"
+ },
+ "/$": {
+ "filePath": "$.tsx"
+ }
+ }
+}
+ROUTE_MANIFEST_END */
diff --git a/apps/vite/src/routes/__root.tsx b/apps/vite/src/routes/__root.tsx
index da25473043..a60ef833f4 100644
--- a/apps/vite/src/routes/__root.tsx
+++ b/apps/vite/src/routes/__root.tsx
@@ -1,15 +1,9 @@
import * as React from 'react';
-import {
- Link,
- Outlet,
- createRootRouteWithContext,
- useRouter,
-} from '@tanstack/react-router';
+import { Outlet, createRootRouteWithContext } from '@tanstack/react-router';
import { TanStackRouterDevtools } from '@tanstack/router-devtools';
import { QueryClient } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
-import { RouterProvider } from 'react-aria-components';
import PloneClient from '@plone/client';
import '@plone/components/dist/basic.css';
@@ -22,13 +16,11 @@ export const Route = createRootRouteWithContext<{
});
function RootComponent() {
- const router = useRouter();
-
return (
- router.navigate({ to: path })}>
+ <>
-
+ >
);
}
diff --git a/apps/vite/src/routes/index.tsx b/apps/vite/src/routes/index.tsx
index 81b7ae161c..b7619d303c 100644
--- a/apps/vite/src/routes/index.tsx
+++ b/apps/vite/src/routes/index.tsx
@@ -1,5 +1,5 @@
-import { createFileRoute } from '@tanstack/react-router';
import * as React from 'react';
+import { createFileRoute } from '@tanstack/react-router';
import { flattenToAppURL } from '../utils';
import { useSuspenseQuery } from '@tanstack/react-query';
import { usePloneClient } from '@plone/providers';
@@ -23,6 +23,7 @@ function IndexComponent() {
const { data } = useSuspenseQuery(
getContentQuery({ path: flattenToAppURL('/'), expand }),
);
+
return (
<>
= 0)
- return;
- function pulse() {
- var i;
- Search._pulse_status = (Search._pulse_status + 1) % 4;
- var dotString = '';
- for (i = 0; i < Search._pulse_status; i++)
- dotString += '.';
- Search.dots.text(dotString);
- if (Search._pulse_status > -1)
- window.setTimeout(pulse, 500);
- }
- pulse();
- },
-
- /**
- * perform a search for something (or wait until index is loaded)
- */
- performSearch : function(query, doc_section) {
- // create the required interface elements
- this.out = $('#search-results');
- this.title = $('').appendTo(this.out);
- this.dots = $('').appendTo(this.title);
- this.status = $('
').appendTo(this.out);
- this.output = $('
').appendTo(this.out);
-
- $('#search-progress').text(_('Preparing search...'));
- this.startPulse();
-
- // index already loaded, the browser was quick!
- if (this.hasIndex()) {
- this.query(query, doc_section);
- } else {
- this.deferQuery(query);
- }
- },
-
- /**
- * execute search (requires search index to be loaded)
- */
- query : function(query, doc_section) {
- var i;
-
- // stem the searchterms and add them to the correct list
- var stemmer = new Stemmer();
- var searchterms = [];
- var excluded = [];
- var hlterms = [];
- var tmp = splitQuery(query);
- var objectterms = [];
- for (i = 0; i < tmp.length; i++) {
- if (tmp[i] !== "") {
- objectterms.push(tmp[i].toLowerCase());
- }
-
- if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i] === "") {
- // skip this "word"
- continue;
- }
- // stem the word
- var word = stemmer.stemWord(tmp[i].toLowerCase());
- // prevent stemmer from cutting word smaller than two chars
- if(word.length < 3 && tmp[i].length >= 3) {
- word = tmp[i];
- }
- var toAppend;
- // select the correct list
- if (word[0] == '-') {
- toAppend = excluded;
- word = word.substr(1);
- }
- else {
- toAppend = searchterms;
- hlterms.push(tmp[i].toLowerCase());
- }
- // only add if not already in the list
- if (!$u.contains(toAppend, word))
- toAppend.push(word);
- }
- var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
-
- // console.debug('SEARCH: searching for:');
- // console.info('required: ', searchterms);
- // console.info('excluded: ', excluded);
-
- // prepare search
- var terms = this._index.terms;
- var titleterms = this._index.titleterms;
-
- // array of [filename, title, anchor, descr, score]
- var results = [];
- $('#search-progress').empty();
-
- // lookup as object
- for (i = 0; i < objectterms.length; i++) {
- var others = [].concat(objectterms.slice(0, i),
- objectterms.slice(i+1, objectterms.length));
- results = results.concat(this.performObjectSearch(objectterms[i], others));
- }
-
- // lookup as search terms in fulltext
- results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
-
- // let the scorer override scores with a custom scoring function
- if (Scorer.score) {
- for (i = 0; i < results.length; i++)
- results[i][4] = Scorer.score(results[i]);
- }
-
- // Filter results by doc_section
- if (doc_section && doc_section !== 'all') {
- results = results.filter(result => {
- let condition = result[0].split('/')[0] === doc_section;
- return condition
- })
- }
-
- // Enrich item with parent doc_section title
- for (i = 0; i < results.length; i++)
- results[i][6] = results[i][6] || 'TODO Documentation title';
-
- // now sort the results by score (in opposite order of appearance, since the
- // display function below uses pop() to retrieve items) and then
- // alphabetically
- results.sort(function(a, b) {
- var left = a[4];
- var right = b[4];
- if (left > right) {
- return 1;
- } else if (left < right) {
- return -1;
- } else {
- // same score: sort alphabetically
- left = a[1].toLowerCase();
- right = b[1].toLowerCase();
- return (left > right) ? -1 : ((left < right) ? 1 : 0);
- }
- });
-
-
- // print the results
- var resultCount = results.length;
- function displayNextItem() {
- // results left, load the summary and display it
- if (results.length) {
- var item = results.pop();
- var listItem = $('');
- var requestUrl = "";
- var linkUrl = "";
- if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {
- // dirhtml builder
- var dirname = item[0] + '/';
- if (dirname.match(/\/index\/$/)) {
- dirname = dirname.substring(0, dirname.length-6);
- } else if (dirname == 'index/') {
- dirname = '';
- }
- requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname;
- linkUrl = requestUrl;
-
- } else {
- // normal html builders
- requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX;
- linkUrl = item[0] + DOCUMENTATION_OPTIONS.LINK_SUFFIX;
- }
- listItem.append($('').attr('href',
- linkUrl +
- highlightstring + item[2]).html(item[1]));
-
- listItem.append($('' + item[6] + ''));
-
- if (item[3]) {
- listItem.append($(' (' + item[3] + ')'));
- Search.output.append(listItem);
- setTimeout(function() {
- displayNextItem();
- }, 5);
- } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
- $.ajax({url: requestUrl,
- dataType: "text",
- complete: function(jqxhr, textstatus) {
- var data = jqxhr.responseText;
- if (data !== '' && data !== undefined) {
- listItem.append(Search.makeSearchSummary(data, searchterms, hlterms));
- }
- Search.output.append(listItem);
- setTimeout(function() {
- displayNextItem();
- }, 5);
- }});
- } else {
- // no source available, just display title
- Search.output.append(listItem);
- setTimeout(function() {
- displayNextItem();
- }, 5);
- }
- }
- // search finished, update title and status message
- else {
- Search.stopPulse();
- Search.title.text(_('Search Results'));
- if (!resultCount)
- Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
- else
- Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
- Search.status.fadeIn(500);
- }
- }
- displayNextItem();
- },
-
- /**
- * search for object names
- */
- performObjectSearch : function(object, otherterms) {
- var filenames = this._index.filenames;
- var docnames = this._index.docnames;
- var objects = this._index.objects;
- var objnames = this._index.objnames;
- var titles = this._index.titles;
-
- var i;
- var results = [];
-
- for (var prefix in objects) {
- for (var name in objects[prefix]) {
- var fullname = (prefix ? prefix + '.' : '') + name;
- var fullnameLower = fullname.toLowerCase()
- if (fullnameLower.indexOf(object) > -1) {
- var score = 0;
- var parts = fullnameLower.split('.');
- // check for different match types: exact matches of full name or
- // "last name" (i.e. last dotted part)
- if (fullnameLower == object || parts[parts.length - 1] == object) {
- score += Scorer.objNameMatch;
- // matches in last name
- } else if (parts[parts.length - 1].indexOf(object) > -1) {
- score += Scorer.objPartialMatch;
- }
- var match = objects[prefix][name];
- var objname = objnames[match[1]][2];
- var title = titles[match[0]];
- // If more than one term searched for, we require other words to be
- // found in the name/title/description
- if (otherterms.length > 0) {
- var haystack = (prefix + ' ' + name + ' ' +
- objname + ' ' + title).toLowerCase();
- var allfound = true;
- for (i = 0; i < otherterms.length; i++) {
- if (haystack.indexOf(otherterms[i]) == -1) {
- allfound = false;
- break;
- }
- }
- if (!allfound) {
- continue;
- }
- }
- var descr = objname + _(', in ') + title;
-
- var anchor = match[3];
- if (anchor === '')
- anchor = fullname;
- else if (anchor == '-')
- anchor = objnames[match[1]][1] + '-' + fullname;
- // add custom score for some objects according to scorer
- if (Scorer.objPrio.hasOwnProperty(match[2])) {
- score += Scorer.objPrio[match[2]];
- } else {
- score += Scorer.objPrioDefault;
- }
- results.push([docnames[match[0]], fullname, '#'+anchor, descr, score, filenames[match[0]]]);
- }
- }
- }
-
- return results;
- },
-
- /**
- * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
- */
- escapeRegExp : function(string) {
- return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
- },
-
- /**
- * search for full-text terms in the index
- */
- performTermsSearch : function(searchterms, excluded, terms, titleterms) {
- var docnames = this._index.docnames;
- var filenames = this._index.filenames;
- var titles = this._index.titles;
-
- var i, j, file;
- var fileMap = {};
- var scoreMap = {};
- var results = [];
-
- // perform the search on the required terms
- for (i = 0; i < searchterms.length; i++) {
- var word = searchterms[i];
- var files = [];
- var _o = [
- {files: terms[word], score: Scorer.term},
- {files: titleterms[word], score: Scorer.title}
- ];
- // add support for partial matches
- if (word.length > 2) {
- var word_regex = this.escapeRegExp(word);
- for (var w in terms) {
- if (w.match(word_regex) && !terms[word]) {
- _o.push({files: terms[w], score: Scorer.partialTerm})
- }
- }
- for (var w in titleterms) {
- if (w.match(word_regex) && !titleterms[word]) {
- _o.push({files: titleterms[w], score: Scorer.partialTitle})
- }
- }
- }
-
- // no match but word was a required one
- if ($u.every(_o, function(o){return o.files === undefined;})) {
- break;
- }
- // found search word in contents
- $u.each(_o, function(o) {
- var _files = o.files;
- if (_files === undefined)
- return
-
- if (_files.length === undefined)
- _files = [_files];
- files = files.concat(_files);
-
- // set score for the word in each file to Scorer.term
- for (j = 0; j < _files.length; j++) {
- file = _files[j];
- if (!(file in scoreMap))
- scoreMap[file] = {};
- scoreMap[file][word] = o.score;
- }
- });
-
- // create the mapping
- for (j = 0; j < files.length; j++) {
- file = files[j];
- if (file in fileMap && fileMap[file].indexOf(word) === -1)
- fileMap[file].push(word);
- else
- fileMap[file] = [word];
- }
- }
-
- // now check if the files don't contain excluded terms
- for (file in fileMap) {
- var valid = true;
-
- // check if all requirements are matched
- var filteredTermCount = // as search terms with length < 3 are discarded: ignore
- searchterms.filter(function(term){return term.length > 2}).length
- if (
- fileMap[file].length != searchterms.length &&
- fileMap[file].length != filteredTermCount
- ) continue;
-
- // ensure that none of the excluded terms is in the search result
- for (i = 0; i < excluded.length; i++) {
- if (terms[excluded[i]] == file ||
- titleterms[excluded[i]] == file ||
- $u.contains(terms[excluded[i]] || [], file) ||
- $u.contains(titleterms[excluded[i]] || [], file)) {
- valid = false;
- break;
- }
- }
-
- // if we have still a valid result we can add it to the result list
- if (valid) {
- // select one (max) score for the file.
- // for better ranking, we should calculate ranking by using words statistics like basic tf-idf...
- var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));
- function getParentTitle(f) {
- let parentdocname = docnames[f].split('/')[0] + '/index';
- let parentID = docnames.indexOf(parentdocname);
- let title = parentID === -1 ? 'Plone Documentation' : titles[parentID];
- return title
- }
- results.push([docnames[file], titles[file], '', null, score, filenames[file], getParentTitle(file)]);
- }
- }
- return results;
- },
-
- /**
- * helper function to return a node containing the
- * search summary for a given text. keywords is a list
- * of stemmed words, hlwords is the list of normal, unstemmed
- * words. the first one is used to find the occurrence, the
- * latter for highlighting it.
- */
- makeSearchSummary : function(htmlText, keywords, hlwords) {
- var text = Search.htmlToText(htmlText);
- var textLower = text.toLowerCase();
- var start = 0;
- $.each(keywords, function() {
- var i = textLower.indexOf(this.toLowerCase());
- if (i > -1)
- start = i;
- });
- start = Math.max(start - 120, 0);
- var excerpt = ((start > 0) ? '...' : '') +
- $.trim(text.substr(start, 240)) +
- ((start + 240 - text.length) ? '...' : '');
- var rv = $('').text(excerpt);
- $.each(hlwords, function() {
- rv = rv.highlightText(this, 'highlighted');
- });
- return rv;
- }
-};
-
-$(document).ready(function() {
- Search.init();
- if ($.trim($(".topbar-contents .bd-toc").html()) === "") {
- $(".topbar-contents .bd-toc").css("visibility", "hidden");
- }
- $('select[name="doc_section"]').change(function() {
- this.form.submit();
- });
-});
diff --git a/docs/source/addons/best-practices.md b/docs/source/addons/best-practices.md
deleted file mode 100644
index af2109c5d7..0000000000
--- a/docs/source/addons/best-practices.md
+++ /dev/null
@@ -1,87 +0,0 @@
----
-myst:
- html_meta:
- "description": "Integrate your add-on with Volto's add-on framework"
- "property=og:description": "Integrate your add-on with Volto's add-on framework"
- "property=og:title": "Best practices for add-ons"
- "keywords": "Volto, Plone, frontend, React, best practices, add-ons"
----
-
-# Best practices for add-ons
-
-Although the add-on framework is relatively new in Volto's world, there are
-quite a few generic add-ons that can be used in any Volto project.
-
-Based on the experience gained developing some of these add-ons, we
-recommend that you follow (no need for strictness, of course) these rough
-guidelines:
-
-## Integrate your add-on with Volto's add-on framework
-
-Just like Plone add-ons provide some features by default, Volto add-ons should
-register some features by default. For example, if your add-on provides widgets,
-register the most basic configuration of that widget with a name that can be
-used.
-
-On more complicated cases, see if you can structure your code to use the
-`settings` {term}`configuration registry`, or stash your configuration in your block
-registration, for example.
-
-As an example: let's say we're building a Color Picker widget and we want to
-provide a palette of colors from which to choose. The widget should integrate
-with a default `settings.colorWidgetPalette`, which would be a list of colors.
-
-And of course, also provide a widget factory so it can be used to create
-multiple instances of that color widget with custom color palettes.
-
-### Provide additional configuration
-
-An add-on can ship with multiple {term}`Volto configuration loader`s. This makes it
-possible to provide configuration methods for demo purposes, for example, or to
-ship with a default "shallow" integration, then provide another separate
-configuration loader for a deeper integration.
-
-## Avoid shadowing Volto files
-
-This rule is meant to be broken. If you find that you need to customize
-a particular file from Volto and you have multiple projects, better to create
-an add-on that holds that customized file, so that you have a single place to
-maintain that "file fork", but otherwise it's a good idea to avoid shipping
-generic add-ons with Volto customizations. Make sure to include this information
-as a warning in your add-on description!
-
-See if your use case is generic enough, maybe Volto needs to be extended to
-cover that use case, directly in core.
-
-## Minimal documentation
-
-Deadlines can be rough and documentation tends to be pushed as last priority,
-but please add a minimal Readme with a couple of lines and, most importantly,
-a screenshot.
-
-Ideally, the Readme should also include install instructions and details on any
-possible settings.
-
-## Testing the add-on
-
-It is not easy, right now, to ship an add-on with a self-bootstraping and
-testing framework. But you can create a separate minimal Volto project that can
-hold the Cypress integration tests and trigger the CI tests.
-
-## Use appropriate npmjs tags
-
-If you're releasing your add-on to `npmjs.com`, please consider adding the
-following tags, next to your add-on-specific tags:
-
-- `volto-addon`
-- `volto`
-- `plone`
-- `react`
-
-## Include in `collective/awesome-volto`
-
-Even if you think your add-on is not generic or it's tricky to integrate, please
-consider including your add-on in the
-[`collective/awesome-volto`](https://github.com/collective/awesome-volto) add-ons
-list. This provides visibility to your add-on but also further solidifies
-Volto's position in our Plone community.
diff --git a/docs/source/addons/i18n.md b/docs/source/addons/i18n.md
deleted file mode 100644
index 4ae542e544..0000000000
--- a/docs/source/addons/i18n.md
+++ /dev/null
@@ -1,37 +0,0 @@
----
-myst:
- html_meta:
- "description": "Internationalize your add-on and override translations"
- "property=og:description": "Internationalize your add-on and override translations"
- "property=og:title": "Add-on Internationalization"
- "keywords": "Internationalization, i18n, add-on"
----
-
-# Add-on Internationalization
-
-The internationalization workflow is the same as in main Volto: you develop your add-on, then add the translations to your code.
-See {ref}`create-i18n-strings` for how to mark strings and phrases as translatable.
-
-Your add-on has a `locales` folder with a `.pot` file.
-
-1. Create the following structure in your add-ons `locales` folder for every language you want to support.
- As an example for the language Italian:
-
- ```text
- it
- └── LC_MESSAGES
- └── volto.po
- ```
-
-1. Run `yarn i18n` in the context of your add-on.
-1. Go to each `.po` file in your `locales` folder, and write the translations for each translation literal.
-
-In the context of your project, run `yarn i18n` to merge the add-on translations with the ones of your project.
-
-
-## Override translations
-
-If you have multiple add-ons installed in your project, the translations are loaded in the order your add-ons are listed in `package.json`.
-If two add-ons provide different translations for the same message, then the last defined add-on wins.
-
-When running `yarn i18n` in the context of your project, the project's own locales are processed last and can override translations from any add-on.
diff --git a/docs/source/addons/index.md b/docs/source/addons/index.md
deleted file mode 100644
index ed13a38d17..0000000000
--- a/docs/source/addons/index.md
+++ /dev/null
@@ -1,544 +0,0 @@
----
-myst:
- html_meta:
- "description": "Volto add-ons extend the core functionality of the Plone CMS frontend."
- "property=og:description": "Volto add-ons extend the core functionality of the Plone CMS frontend."
- "property=og:title": "Volto add-ons"
- "keywords": "Volto, add-on, extensions, frontend, Plone"
----
-
-# Volto add-ons
-
-```{toctree}
-:maxdepth: 1
-
-i18n
-best-practices
-theme
-public-folder
-```
-
-There are several advanced scenarios where we might want to have more control
-and flexibility beyond using the plain Volto project to build a site.
-
-We can build Volto {term}`add-on` products and make them available as generic
-JavaScript packages that can be included in any Volto project. By doing so we
-can provide code and component reutilization across projects and, of course,
-benefit from open source collaboration.
-
-```{note}
-By declaring a JavaScript package as a Volto add-on, Volto provides
-several integration features: language features (so they can be transpiled
-by Babel), whole-process customization via razzle.extend.js and
-integration with Volto's {term}`configuration registry`.
-```
-
-The add-on can be published to an npm registry or directly installed from github
-by Yarn. By using [mrs-develop](https://github.com/collective/mrs-developer),
-it's possible to have a workflow similar to zc.buildout's mr.developer, where
-you can "checkout" an add-on for development.
-
-An add-on can be almost anything that a Volto project can be. They can:
-
-- provide additional views and blocks
-- override or extend Volto's builtin views, blocks, settings
-- shadow (customize) Volto's (or another add-on's) modules
-- register custom routes
-- provide custom {term}`Redux` actions and reducers
-- register custom Express middleware for Volto's server process
-- tweak Volto's Webpack configuration, load custom Razzle and Webpack plugins
-- even provide a custom theme, just like a regular Volto project does.
-
-## Configuring a Volto project to use an add-on
-
-You can install a Volto add-on just like any other JS package:
-
-```shell
-yarn add name-of-add-on
-```
-
-If the add-on is not published on npm, you can retrieve it directly from Github:
-
-```shell
-yarn add collective/volto-dropdownmenu
-```
-
-Next, you'll need to add the add-on (identified by its JS package name) to the
-`addons` key of your Volto project's `package.json`. More details in the next
-section.
-
-### Loading add-on configuration
-
-As a convenience, an add-on can export configuration functions that can mutate,
-in-place, the overall Volto {term}`configuration registry`. An add-on can export multiple
-configurations methods, making it possible to selectively choose which specific
-add-on functionality you want to load.
-
-In your Volto project's ``package.json`` you can allow the add-on to alter the
-global configuration by adding, in the `addons` key, a list of Volto add-on
-package names, like:
-
-```js
-{
- "name": "my-nice-volto-project",
-
- "addons": [
- "acme-volto-foo-add-on",
- "@plone/some-add-on",
- "collective-another-volto-add-on"
- ],
-
-}
-```
-
-```{warning}
-Adding the add-on package to the `addons` key is mandatory! It allows Volto
-to treat that package properly and provide it with BabelJS language
-features. In Plone terminology, it is like including a Python egg to the
-`zcml` section of zc.buildout.
-```
-
-Some add-ons might choose to allow the Volto project to selectively load some of
-their configuration, so they may offer additional configuration functions,
-which you can load by overloading the add-on name in the `addons` package.json
-key, like so:
-
-```{code-block} json
-:emphasize-lines: 4
-
-{
- "name": "my-nice-volto-project",
- "addons": [
- "acme-volto-foo-add-on:loadOptionalBlocks,overrideSomeDefaultBlock",
- "volto-ga"
- ],
-}
-```
-
-```{note}
-The additional comma-separated names should be exported from the add-on
-package's ``index.js``. The main configuration function should be exported as
-the default. An add-on's default configuration method will always be loaded.
-```
-
-If for some reason, you want to manually load the add-on, you could always do,
-in your project's ``config.js`` module:
-
-```js
-import loadExampleAddon, { enableOptionalBlocks } from 'volto-example-add-on';
-import * as voltoConfig from '@plone/volto/config';
-
-const config = enableOptionalBlocks(loadExampleAddon(voltoConfig));
-
-export blocks = {
- ...config.blocks,
-}
-```
-
-As this is a common operation, Volto provides a helper method for this:
-
-```js
-import { applyConfig } from '@plone/volto/helpers';
-import * as voltoConfig from '@plone/volto/config';
-
-const config = applyConfig([
- enableOptionalBlocks,
- loadExampleAddon
-], voltoConfig);
-
-export blocks = {
- ...config.blocks,
-}
-```
-
-The `applyConfig` helper ensures that each configuration methods returns the
-config object, avoiding odd and hard to track errors when developing add-ons.
-
-## Creating add-ons
-
-Volto add-on packages are just CommonJS packages. The only requirement is that
-they point the `main` key of their `package.json` to a module that exports, as
-a default function that acts as a {term}`Volto configuration loader`.
-
-Although you could simply use `npm init` to generate an add-on initial code,
-we now have a nice
-[Yeoman-based generator](https://github.com/plone/generator-volto) that you can use:
-
-```shell
-npm install -g @plone/generator-volto
-yo @plone/volto:addon [] [options]
-```
-
-Volto will automatically provide aliases for your (unreleased) package, so that
-once you've released it, you don't need to change import paths, since you can
-use the final ones from the very beginning. This means that you can use imports
-such as `import { Something } from '@plone/my-volto-add-on'` without any extra
-configuration.
-
-### Use mrs-developer to manage the development cycle
-
-#### Add mrs-developer dependency and related script
-
-[Eric Brehault](https://github.com/ebrehault) ported this amazing Python tool,
-which provides a way to pull a package from git and set it up as a dependency
-for the current project codebase.
-
-To facilitate add-on development lifecycle we recommend using
-[mrs-developer](https://www.npmjs.com/package/mrs-developer).
-
-By doing this, you can develop both the project and the add-on product as if
-they were both part of the current codebase. Once the add-on development is
-done, you can publish the package to an npm repository.
-
-```shell
-yarn add mrs-developer
-```
-
-Then, in `package.json`:
-
-```{code-block} json
-:emphasize-lines: 2
-"scripts": {
- "develop": "missdev --config=jsconfig.json --output=addons",
-}
-```
-
-We can configure `mrs-developer` to use any directory that you want. Here we
-are telling it to create the directory `src/addons` and put the packages
-managed by `mrs-developer` inside.
-
-#### mrs.developer.json
-
-This is the configuration file that instructs `mrs-developer` from where it has
-to pull the packages. So, create `mrs.developer.json` and add:
-
-```json
-{
- "acme-volto-foo-add-on": {
- "package": "@acme/volto-foo-add-on",
- "url": "git@github.com:acme/my-volto-add-on.git",
- "path": "src"
- }
-}
-```
-
-Then run:
-
-```shell
-make develop
-```
-
-Now the add-on is found in `src/addons/`.
-
-```{note}
-`package` property is optional, set it up only if your package has a scope.
-`src` is required if the content of your add-on is located in the `src`
-directory (but, as that is the convention recommended for all Volto add-on
-packages, you will always include it)
-```
-
-If you want to know more about `mrs-developer` config options, please refer to
-[its npm page](https://www.npmjs.com/package/mrs-developer).
-
-#### tsconfig.json / jsconfig.json
-
-`mrs-developer` automatically creates this file for you, but if you choose not
-to use mrs-developer, you'll have to add something like this to your
-`tsconfig.json` or `jsconfig.json` file in the Volto project root:
-
-```json
-{
- "compilerOptions": {
- "paths": {
- "acme-volto-foo-add-on": [
- "addons/acme-volto-foo-add-on/src"
- ]
- },
- "baseUrl": "src"
- }
-}
-```
-
-```{warning}
-Please note that both `paths` and `baseUrl` are required to match your
-project layout.
-```
-
-```{tip}
-You should use the `src` path inside your package and point the `main` key
-in `package.json` to the `index.js` file in `src/index.js`.
-```
-
-### Customizations
-
-add-on packages can include customization folders, just like the Volto projects.
-The customizations are resolved in the order: add-ons (as sorted in the `addons`
-key of your project's `package.json`) then the customizations in the Volto
-project, last one wins.
-
-```{tip}
-See the {ref}`advanced-customization-scenarios-label`
-section on how to enhance this pattern and how to include customizations
-inside add-ons.
-```
-
-### Providing add-on configuration
-
-The default export of your add-on main `index.js` file should be a function with
-the signature ``config => config``.
-That is, it should take the ``global`` configuration object and return it,
-possibly mutated or changed. So your main `index.js` will look like:
-
-```js
-export default function applyConfig(config) {
- config.blocks.blocksConfig.faq_viewer = {
- id: 'faq_viewer',
- title: 'FAQ Viewer',
- edit: FAQBlockEdit,
- view: FAQBlockView,
- icon: chartIcon,
- group: 'common',
- restricted: false,
- mostUsed: true,
- sidebarTab: 1,
- };
- return config;
-}
-```
-
-And the `package.json` file of your add-on:
-
-```json
-{
- "main": "src/index.js",
-}
-```
-
-```{warning}
-An add-on's default configuration method will always be loaded.
-```
-
-#### Multiple add-on configurations
-
-You can export additional configuration functions from your add-on's main
-`index.js`.
-
-```js
-import applyConfig, {loadOptionalBlocks,overrideSomeDefaultBlock} from './config';
-
-export { loadOptionalBlocks, overrideSomeDefaultBlock };
-export default applyConfig;
-```
-
-## Add third-party dependencies to your add-on
-
-If you're developing the add-on and you wish to add an external dependency, you'll have to switch your project to be a [Yarn Workspaces root](https://yarnpkg.com/features/workspaces).
-
-So you'll need to add, in your Volto project's `package.json`:
-
-```json
-"private": true,
-"workspaces": [],
-```
-
-Then populate the `workspaces` key with the path to your development add-ons:
-
-```json
-"workspaces": [
- "src/addons/my-volto-add-on"
-]
-```
-You'll have to manage the add-on dependencies via the workspace root (your Volto
-project). For example, to add a new dependency:
-
-```shell
-yarn workspace @plone/my-volto-add-on add some-third-party-package
-```
-
-You can run `yarn workspaces info` to see a list of workspaces defined.
-
-In case you want to add new dependencies to the Volto project, now you'll have
-to run the `yarn add` command with the `-W` switch:
-
-```shell
-yarn add -W some-dependency
-```
-
-## Extending Razzle from an add-on
-
-Just like you can extend Razzle's configuration from the project, you can do so
-with an add-on, as well. You should provide a `razzle.extend.js` file in your
-add-on root folder. An example of such file where the theme.config alias is
-changed, to enable a custom Semantic theme inside the add-on:
-
-
-```js
-const plugins = (defaultPlugins) => {
- return defaultPlugins;
-};
-const modify = (config, { target, dev }, webpack) => {
- const themeConfigPath = `${__dirname}/theme/theme.config`;
- config.resolve.alias['../../theme.config$'] = themeConfigPath;
-
- return config;
-};
-
-module.exports = {
- plugins,
- modify,
-};
-```
-
-## Extending Eslint configuration from an add-on
-
-Starting with Volto v16.4.0, you can also customize the Eslint configuration
-from an add-on. You should provide a `eslint.extend.js` file in your
-add-on root folder, which exports a `modify(defaultConfig)` function. For
-example, to host some code outside the regular `src/` folder of your add-on,
-this `eslint.extend.js` file is needed:
-
-```js
-const path = require('path');
-
-module.exports = {
- modify(defaultConfig) {
- const aliasMap = defaultConfig.settings['import/resolver'].alias.map;
- const addonPath = aliasMap.find(
- ([name]) => name === '@plone-collective/some-volto-add-on',
- )[1];
-
- const extraPath = path.resolve(`${addonPath}/../extra`);
- aliasMap.push(['@plone-collective/extra', extraPath]);
-
- return defaultConfig;
- },
-};
-```
-
-This would allow the `@plone-collective/some-volto-add-on` to host some code
-outside of its normal `src/` folder, let's say in the `extra` folder, and that
-code would be available under the `@plone-collective/extra` name. Note: this is
-taking care only of the Eslint integration. For proper language support, you'll
-still need to do it in the `razzle.extend.js` of your add-on.
-
-## add-on dependencies
-
-Sometimes your add-on depends on another add-on. You can declare add-on dependency
-in your add-on's `addons` key, just like you do in your project. By doing so,
-that other add-on's configuration loader is executed first, so you can depend on
-the configuration being already applied. Another benefit is that you'll have
-to declare only the "top level" add-on in your project, the dependencies will be
-discovered and automatically treated as Volto add-ons. For example, `volto-slate`
-depends on `volto-object-widget`'s configuration being already applied, so
-`volto-slate` can declare in its `package.json`:
-
-```json
-{
- "name": "volto-slate",
-
- "addons": ["@eeacms/volto-object-widget"]
-}
-```
-
-And of course, the dependency add-on can depend, on its turn, on other add-ons
-which will be loaded as well. Circular dependencies should be avoided.
-
-## Problems with untranspiled add-on dependencies
-
-When using external add-ons in your project, sometimes you will run into add-ons
-that are not securely transpiled or haven't been transpiled at all. In that case
-you might see an error like the following:
-
-```console
-Module parse failed: Unexpected token (10:41) in @react-leaflet/core/esm/path.js
-...
-const options = props.pathOptions ?? {};
-...
-```
-
-Babel automatically transpiles the code in your add-on, but `node_modules` are
-excluded from this process, so we need to include the add-on path in the list of
-modules to be transpiled. This can be accomplished by customizing the webpack
-configuration in the `razzle.config.js` file in your add-on. For example,
-suppose that we want to use react-leaflet, which has a known transpilation
-issue:
-
-```js
-const path = require('path');
-const makeLoaderFinder = require('razzle-dev-utils/makeLoaderFinder');
-
-const babelLoaderFinder = makeLoaderFinder('babel-loader');
-
-const jsConfig = require('./jsconfig').compilerOptions;
-
-const pathsConfig = jsConfig.paths;
-let voltoPath = './node_modules/@plone/volto';
-Object.keys(pathsConfig).forEach((pkg) => {
- if (pkg === '@plone/volto') {
- voltoPath = `./${jsConfig.baseUrl}/${pathsConfig[pkg][0]}`;
- }
-});
-
-const { modifyWebpackConfig, plugins } = require(`${voltoPath}/razzle.config`);
-
-const customModifyWebpackConfig = ({ env, webpackConfig, webpackObject, options }) => {
- const config = modifyWebpackConfig({
- env,
- webpackConfig,
- webpackObject,
- options,
- });
- const babelLoader = config.module.rules.find(babelLoaderFinder);
- const { include } = babelLoader;
- const corePath = path.join(
- path.dirname(require.resolve('@react-leaflet/core')),
- '..',
- );
- const esmPath = path.join(
- path.dirname(require.resolve('react-leaflet')),
- '..',
- );
-
- include.push(corePath);
- include.push(esmPath);
- return config;
-};
-
-module.exports = { modifyWebpackConfig: customModifyWebpackConfig, plugins };
-```
-
-First we need some setup to get the webpack configuration from Volto's configuration.
-Once we have that, we need to resolve the path to the desired add-ons and push it
-into the Babel loader include list. After this, the add-ons will load correctly.
-
-## Testing add-ons
-
-We should let jest know about our aliases and make them available to it to
-resolve them, so in `package.json`:
-
-```{code-block} json
-:emphasize-lines: 6
-
-"jest": {
- "moduleNameMapper": {
- "@plone/volto/(.*)$": "/node_modules/@plone/volto/src/$1",
- "@package/(.*)$": "/src/$1",
- "@plone/some-volto-add-on/(.*)$": "/src/addons/@plone/some-volto-add-on/src/$1",
- "my-volto-add-on/(.*)$": "/src/addons/my-volto-add-on/src/$1",
- "~/(.*)$": "/src/$1"
- },
-```
-
-```{tip}
-We're in the process of moving the default scaffolding generators to
-provide a `jest.config.js` file in Volto, making this step unneeded.
-```
-
-You can use `yarn test src/addons/add-on-name` to run tests.
-
-## Code linting
-
-If you have generated your Volto project recently (after the summer of 2020),
-you don't have to do anything to have automatic integration with ESLint,
-otherwise make sure to upgrade your project's `.eslintrc` to the `.eslintrc.js`
-version, according to the {doc}`../upgrade-guide/index`.
diff --git a/docs/source/addons/theme.md b/docs/source/addons/theme.md
deleted file mode 100644
index d14d559140..0000000000
--- a/docs/source/addons/theme.md
+++ /dev/null
@@ -1,230 +0,0 @@
----
-myst:
- html_meta:
- "description": "Create a theme add-on"
- "property=og:description": "Create a theme add-on"
- "property=og:title": "Create a theme add-on"
- "keywords": "Volto, Plone, Semantic UI, CSS, Volto theme"
----
-
-# Create a Volto theme add-on
-
-We can create a Volto Add-on that acts as a Volto theme Add-on, so we can detach it from the project files.
-The advantage is that you convert the project Volto theme in a pluggable one, so you can deploy the same theme in different projects.
-You can even have themes depending on conditions that you could inject on build time.
-This is the purpose of `volto.config.js`, the ability of declaring `add-ons` and the active `theme` programmatically. See {ref}`volto-config-js` for more information.
-For convenience, it can also be set via a `THEME` environment variable.
-
-1. Add a `theme` key in your `volto.config.js` file in the root of your project:
-
-```js
-module.exports = {
- addons: [],
- theme: 'volto-my-theme'
-};
-```
-
-or add a key in your `package.json` project:
-
-```json
-"theme": "volto-my-theme"
-```
-
-or via a `THEME` variable:
-
-```shell
-THEME='volto-my-theme' yarn start
-```
-
-2. Create a directory `src/theme` in your add-on, then add this file `theme.config`, replacing `` with your add-on name:
-
-```less
-/*******************************
- Theme Selection
-*******************************/
-
-/* To override a theme for an individual element specify theme name below */
-
-/* Global */
-@site : 'pastanaga';
-@reset : 'pastanaga';
-
-/* Elements */
-@button : 'pastanaga';
-@container : 'pastanaga';
-@divider : 'pastanaga';
-@flag : 'pastanaga';
-@header : 'pastanaga';
-@icon : 'pastanaga';
-@image : 'pastanaga';
-@input : 'pastanaga';
-@label : 'pastanaga';
-@list : 'pastanaga';
-@loader : 'pastanaga';
-@placeholder : 'pastanaga';
-@rail : 'pastanaga';
-@reveal : 'pastanaga';
-@segment : 'pastanaga';
-@step : 'pastanaga';
-
-/* Collections */
-@breadcrumb : 'pastanaga';
-@form : 'pastanaga';
-@grid : 'pastanaga';
-@menu : 'pastanaga';
-@message : 'pastanaga';
-@table : 'pastanaga';
-
-/* Modules */
-@accordion : 'pastanaga';
-@checkbox : 'pastanaga';
-@dimmer : 'pastanaga';
-@dropdown : 'pastanaga';
-@embed : 'pastanaga';
-@modal : 'pastanaga';
-@nag : 'pastanaga';
-@popup : 'pastanaga';
-@progress : 'pastanaga';
-@rating : 'pastanaga';
-@search : 'pastanaga';
-@shape : 'pastanaga';
-@sidebar : 'pastanaga';
-@sticky : 'pastanaga';
-@tab : 'pastanaga';
-@transition : 'pastanaga';
-
-/* Views */
-@ad : 'pastanaga';
-@card : 'pastanaga';
-@comment : 'pastanaga';
-@feed : 'pastanaga';
-@item : 'pastanaga';
-@statistic : 'pastanaga';
-
-/* Extras */
-@main : 'pastanaga';
-@custom : 'pastanaga';
-
-/*******************************
- Folders
-*******************************/
-
-/* Path to theme packages */
-@themesFolder : '~volto-themes';
-
-/* Path to site override folder */
-@siteFolder : "/theme";
-
-/*******************************
- Import Theme
-*******************************/
-
-@import (multiple) "~semantic-ui-less/theme.less";
-@fontPath : "~volto-themes/@{theme}/assets/fonts";
-
-.loadAddonOverrides() {
- @import (optional) "@{siteFolder}/@{addon}/@{addontype}s/@{addonelement}.overrides";
-}
-
-/* End Config */
-```
-
-3. Declare the theme as an add-on by adding its name to the value for the `addons` key in either `volto.config.js` or `package.json` of your project.
-4. After starting Volto, the theme should be active.
- Now you can add overrides to the default theme in `src/theme`, same as you would in a project.
-5. Now you can safely delete your project's `theme` folder, since the one in the add-on will take precedence and a project can only have one active theme at a time.
-
-## Using your own theming escape hatch
-
-Volto theming uses Semantic UI theming capabilities to define and extend a theme for your site.
-However, while maintaining and playing well with the Semantic UI Volto base, using a traditional CSS approach can be done using the LESS preprocessor-based `extras` escape hatch.
-
-At the same time, one can either discard or complement the extras escape hatch and add your own, by customizing the `theme.js` module in Volto.
-
-```js
-import 'semantic-ui-less/semantic.less';
-import '@plone/volto/../theme/themes/pastanaga/extras/extras.less';
-
-// You can add more entry points for theming
-import '@kitconcept/volto-light-theme/theme/main.scss';
-```
-
-Customizing it is a special use case in Volto: add a `./@root/theme.js` file structure in your `customizations` folder in your add-on or project.
-
-You may want to do this to create a complete new theming experience adapted to your way of doing things that do not match the current Volto theming experience.
-For example, if you want to use another preprocessor in the theme, like SCSS.
-Maybe because your client forces you to have another entirely base of pre-made components based on another library other than Semantic UI:
-See {ref}`volto-custom-theming-strategy` for an example of a custom theme escape hatch.
-
-While building your own escape hatch for theming, you can use the preprocessor of your choice (in the example, SCSS) while maintaining the "base" Volto theme, but customizing it using the resultant CSS.
-
-You can see an example of such a theme in: https://github.com/kitconcept/volto-light-theme
-
-## Modify a custom theme from another add-on
-
-Sometimes you have a custom theme that you want to reuse through all your projects, but with some differences, maintaining the base.
-Usually, the only option would be to use an add-on that adds more CSS to the base theme, using imports that will load after the theme.
-However, there is a problem with this approach.
-You cannot use existing theme variables, including breakpoints, on these new styles.
-Similarly, it gets somewhat detached from the normal flow of the loaded theme.
-The same applies for add-ons, as they are detached from the current theme.
-One could use a Semantic UI approach for making this work, but it's Semantic UI bound.
-
-```{warning}
-This is only possible when using your own escape hatch, and works only with SCSS-based themes, and not with Semantic UI themes, since it enables a couple of entry points that only support SCSS files.
-For an example of how it could be used, see: https://github.com/kitconcept/volto-light-theme
-```
-
-If your custom escape hatch defines a custom theme using SCSS, you can take advantage of this feature.
-Although not limited to this, it would be possible to extend this feature to add more entry points, using another preprocessor or theming approach.
-
-This feature enables two entry points: variables and main.
-From your add-on code, you can extend an existing theme by creating a file corresponding to each entry point:
-
-* `./src/theme/_variables.scss`
-* `./src/theme/_main.scss`
-
-### Variables (`addonsThemeCustomizationsVariables`)
-
-Use this entry point file to modify the original variables of the current loaded theme by adding the entry point before the theme variable definitions.
-In the theme, it should be imported as shown below:
-
-```scss hl_lines="2"
-@import 'addonsThemeCustomizationsVariables';
-@import 'variables';
-@import 'typography';
-@import 'utils';
-@import 'layout';
-```
-
-```{warning}
-Following SCSS best practices, your theme variables should be "overridable" using the `!default` flag.
-This assigns a value to a variable _only_ if that variable isn't defined or its value is [`null`](https://sass-lang.com/documentation/values/null).
-Otherwise, the existing value will be used.
-For more information, see https://sass-lang.com/documentation/variables#default-values
-```
-
-Volto will not only load your add-on entry point files, but it will also detect all the add-ons that have these entry point files and import them grouped under a single file.
-It will also automatically add an `addonsThemeCustomizationsVariables` alias that can be referenced from the theme as shown above.
-
-### Main (`addonsThemeCustomizationsMain`)
-
-This entry point is intended to add your own style definitions, complementing those in the theme.
-You should add it after all the CSS of your theme:
-
-```scss hl_lines="6"
-@import 'blocks/search';
-@import 'blocks/listing';
-
-@import 'temp';
-
-@import 'addonsThemeCustomizationsMain';
-
-/* No CSS beyond this point */
-```
-
-Volto will also detect all the add-ons that have these entry point files, and import them grouped under a single file, and will automatically add an `addonsThemeCustomizationsMain` alias that can be referenced from the theme as shown above.
-
-```{note}
-It will only work in combination with the theme declaration in `volto.config.js` or in `package.json`.
-```
diff --git a/docs/source/backend/index.md b/docs/source/backend/index.md
index 8dbab251c4..614be62855 100644
--- a/docs/source/backend/index.md
+++ b/docs/source/backend/index.md
@@ -34,6 +34,23 @@ Block transformers
Search and indexing integration
: By providing the right adapters, you can extract searchable text from blocks.
+Client reducer content transforms
+: These transforms run in the client when the response from the backend is received.
+ These are useful when you need to modify the response from the backend on-the-fly for amending the backend data, such as a data migration of any kind.
+ You can register a utility that mutates the response at your convenience.
+
+ ```ts
+ import { upgradeV20241023 } from './upgrades/upgradeV20241023';
+
+ config.registerUtility({
+ name: 'upgradeV20241023',
+ type: 'transform',
+ dependencies: { reducer: 'content' },
+ method: upgradeV20241023,
+ });
+ ```
+
+ The `type` of the utility needs to be `transform`, and the `dependencies` set to `{reducer: 'content'}`.
## Proxied backend routes
diff --git a/docs/source/client/quick-start.md b/docs/source/client/quick-start.md
index dd3174a9b7..ad62cd181f 100644
--- a/docs/source/client/quick-start.md
+++ b/docs/source/client/quick-start.md
@@ -21,9 +21,11 @@ These functions can be used in other use cases like command line helpers, script
To install the Javascript Plone client run the following command:
```shell
-yarn add @plone/client
+pnpm add @plone/client
```
+or use your package manager of choice.
+
## `ploneClient` entry point
The main artifact that the client provides is the `ploneClient` entry point.
diff --git a/docs/source/conceptual-guides/add-ons.md b/docs/source/conceptual-guides/add-ons.md
new file mode 100644
index 0000000000..da1d986f5d
--- /dev/null
+++ b/docs/source/conceptual-guides/add-ons.md
@@ -0,0 +1,234 @@
+---
+myst:
+ html_meta:
+ "description": "Volto add-ons extend the core functionality of the Plone CMS user interface."
+ "property=og:description": "Volto add-ons extend the core functionality of the Plone CMS user interface."
+ "property=og:title": "Volto add-ons"
+ "keywords": "Volto, add-on, extensions, user interface, frontend, Plone"
+---
+
+# Volto add-on concepts
+
+This guide describes Volto add-on concepts.
+
+
+## What is a Volto add-on?
+
+Volto add-ons are just CommonJS or ESM packages.
+Their main purpose is to encapsulate logic, configuration, components, customizations, and even themes in a reusable way.
+
+Suppose you want to have more control and flexibility beyond the plain Volto project when building a site.
+You can build a Volto {term}`add-on` and make it available as a generic JavaScript package.
+Then you can reuse and include it in any Volto project.
+
+An add-on can configure or provide any of the following aspects of Volto.
+
+- Provide additional views and blocks.
+- Override or extend Volto's built-in views, blocks, and settings.
+- Shadow or customize Volto's, or another add-on's, modules.
+- Register custom routes.
+- Provide custom {term}`Redux` actions and reducers.
+- Register custom Express middleware for Volto's server process.
+- Tweak Volto's webpack configuration, loading custom Razzle and webpack plugins.
+- Provide even a custom theme.
+
+
+## Volto registry
+
+Volto has a built-in extensible and pluggable system to enhance the Plone CMS user interface.
+It helps developers extend Volto in a pluggable way through {term}`add-on`s.
+This system is implemented through Volto's registry.
+
+For Volto 17 and earlier, the registry was integrated into Volto core.
+
+From Volto 18 onward, the Volto registry is in its own package [`@plone/registry`](https://plone-registry.readthedocs.io/).
+
+
+## Add-on configuration pipeline
+
+A Volto app's configuration is determined through a pipeline starting with Volto's default configuration, then each of your app's add-ons' configuration.
+In Volto 17 and earlier, you can also use project configuration at the end of the pipeline after any add-ons.
+
+```{deprecated} Volto 18.0.0
+The project configuration approach is deprecated and will be removed in Volto 19.
+```
+
+Add-ons are applied in the order they are declared in the `addons` key of {file}`package.json` or programmatically via a provided configuration file.
+Add-ons can override configuration coming from other add-ons, providing a hierarchy of configuration stacks.
+
+Add-ons can be chained, where each one can configure the app in some way.
+If needed, each add-on in the chain can override or extend the previous configuration that other add-ons set.
+Thus, the order in which you register add-ons matters.
+
+Add-ons can define shadowed components.
+"Component shadowing" is a technique for overriding modules of other packages at build time.
+This technique builds upon the `resolve.aliases` facilities of bundlers, so modules can be replaced when the app is being built.
+
+Volto will automatically provide aliases for your package.
+Once you've released it, you don't need to change import paths, since you can use the final ones from the very beginning.
+This means that you can use imports, such as `import { Something } from '@plone/my-volto-add-on'` without any extra configuration.
+
+```{note}
+By declaring a JavaScript package as a Volto add-on, Volto provides several integration features.
+These include {doc}`JavaScript language features <../contributing/language-features>` with transpilation by Babel, whole-process customization via {file}`razzle.extend.js`, and integration with Volto's {term}`configuration registry`.
+```
+
+
+### Use cases
+
+In practice with the configuration pipeline, for example, you can create a "policy" core add-on for your project, and use another add-on for your project's theme.
+This way the project itself renders as a simple boilerplate, which you can extend or rebuild at any time.
+
+You can also reuse add-ons across projects, and adjust them using other add-ons, depending on the other projects' requirements.
+
+
+% TODO: Should this section be moved to a how-to guide?
+## Add-on configuration
+
+The default export of your add-on's main {file}`index.js` file should be a function with the signature `config => config`.
+That is, it should take the `global` configuration object and return it, possibly mutated or changed.
+An {file}`index.js` file should contain the following code.
+
+```js
+export default function applyConfig(config) {
+ config.blocks.blocksConfig.faq_viewer = {
+ id: 'faq_viewer',
+ title: 'FAQ Viewer',
+ edit: FAQBlockEdit,
+ view: FAQBlockView,
+ icon: chartIcon,
+ group: 'common',
+ restricted: false,
+ mostUsed: true,
+ sidebarTab: 1,
+ security: {
+ addPermission: [],
+ view: [],
+ },
+ };
+ return config;
+}
+```
+
+And the {file}`package.json` file of your add-on should contain the following code.
+
+```json
+{
+ "main": "src/index.js",
+}
+```
+
+In effect, Volto does the equivalent of the following pseudocode:
+
+```js
+import installMyVoltoAddon from 'my-volto-addon'
+
+// ... in the configuration registry setup step:
+const configRegistry = installMyVoltoAddon(defaultRegistry);
+```
+
+The Volto add-on needs to export a default function that receives the Volto configuration registry.
+Then it is free to change the registry.
+Finally, it must return that registry.
+
+Volto will execute all the add-on configuration functions in a chain to compute the final configuration registry.
+
+```{note}
+An add-on's default configuration method will always be loaded.
+```
+
+```{seealso}
+See [@kitconcept/volto-button-block](https://github.com/kitconcept/volto-button-block) as an example.
+```
+
+
+### Provide optional add-on configurations
+
+You can export additional configuration functions from your add-on's main {file}`index.js`.
+
+```js
+import applyConfig, {loadOptionalBlocks,overrideSomeDefaultBlock} from './config';
+
+export { loadOptionalBlocks, overrideSomeDefaultBlock };
+export default applyConfig;
+```
+
+```{seealso}
+{doc}`../development/add-ons/load-add-on-configuration`
+```
+
+
+% TODO: Should this section be moved to a how-to guide?
+### Define your add-ons programmatically
+
+The `addons` key in the {file}`package.json` file alone might not be flexible enough in complex scenarios.
+You can programmatically load your add-ons outside your {file}`package.json` file using a {file}`volto.config.js` file with the following content.
+
+```js
+module.exports = {
+ addons: ['@eeacms/volto-accordion-block']
+}
+```
+
+This creates an "escape hatch", where you can use logic and environment conditions to define the add-ons to load in the current project, as in the next example.
+The add-ons that you define here will be added to the existing ones in {file}`package.json`.
+
+```js
+let addons = [];
+if (process.env.MY_SPECIAL_ENV_VAR) { // Does not have to be RAZZLE_
+ addons = ['volto-my-awesome-special-add-on'];
+}
+
+if (process.env.MARKER_FOR_MY_SECRET_PROJECT) { // Does not have to be RAZZLE_
+ addons = [
+ '@kitconcept/volto-heading-block',
+ '@kitconcept/volto-slider-block',
+ 'volto-my-secret-project-add-on',
+ ];
+}
+
+module.exports = {
+ addons: addons,
+};
+```
+
+```{important}
+You must add the `addons` key with the value of your add-on package's name wherever you configure it.
+In Plone terminology, it is like including a Python egg in the `zcml` section of `zc.buildout`.
+```
+
+```{seealso}
+{doc}`../configuration/volto-config-js`
+```
+
+
+## Add-on dependencies
+
+Add-ons can depend on any other JavaScript package, including other Volto add-ons.
+To do this, specify the name of your Volto add-on dependency in your `dependencies` key of your {file}`package.json` file.
+Then create a new `addons` key in the {file}`package.json` file of your add-on, where you specify the extra Volto add-on dependency.
+By doing this, the add-ons can "chain load" one another.
+
+```json
+{
+ "name": "volto-slate",
+
+ "addons": ["@eeacms/volto-object-widget"]
+}
+```
+
+
+## Publish an add-on
+
+Volto add-ons should not be transpiled.
+They should be released as "source" packages.
+
+Their primary entry point (the `main` key of their {file}`package.json`) must point to a module that exports a default function, which acts as a default configuration loader for that package.
+
+You can publish an add-on to an npm registry or to a remote repository host such as GitHub or GitLab, like any other package.
+If you publish your add-on to the [npm Registry](https://www.npmjs.com/) or make your repository public, as a bonus, you will benefit from collaborating on open source software.
+
+
+% Where does this go?
+By using [`mrs-developer`](https://github.com/collective/mrs-developer), it's possible to have a workflow similar to `zc.buildout`'s `mr.developer`, where you can "checkout" an add-on for development.
+[Eric Brehault](https://github.com/ebrehault) ported this amazing Python tool.
diff --git a/docs/source/conceptual-guides/index.md b/docs/source/conceptual-guides/index.md
new file mode 100644
index 0000000000..a81d8cd024
--- /dev/null
+++ b/docs/source/conceptual-guides/index.md
@@ -0,0 +1,19 @@
+---
+myst:
+ html_meta:
+ "description": "Volto conceptual guides"
+ "property=og:description": "Volto conceptual guides"
+ "property=og:title": "Conceptual guides"
+ "keywords": "Volto, user interface, frontend, Plone, conceptual guides"
+---
+
+# Conceptual guides
+
+This section of the documentation contains conceptual guides for various aspects of Volto.
+
+```{toctree}
+:hidden:
+:maxdepth: 2
+
+add-ons
+```
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 360ce22c48..4c010d58b4 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -63,6 +63,7 @@
"sphinx_copybutton",
"sphinx_examples",
"sphinxcontrib.video",
+ "sphinxcontrib.youtube",
"sphinxext.opengraph",
]
@@ -89,11 +90,8 @@
# Ignore github.com pages with anchors
r"https://github.com/.*#.*",
# Ignore other specific anchors
- # r"https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi", # TODO retest with latest Sphinx when upgrading theme. chromewebstore recently changed its URL and has "too many redirects".
- # r"https://chromewebstore.google.com/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd", # TODO retest with latest Sphinx when upgrading theme. chromewebstore recently changed its URL and has "too many redirects".
r"https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors#Identifying_the_issue",
r"https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-version-10-0",
- # r"https://stackoverflow.com", # volto and documentation # TODO retest with latest Sphinx.
]
linkcheck_anchors = True
linkcheck_timeout = 5
@@ -117,7 +115,7 @@
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = [
"spelling_wordlist.txt",
- "contributing/branch-policy.md",
+ "_inc/*",
]
suppress_warnings = [
@@ -179,7 +177,7 @@
"path_to_docs": "docs",
"repository_branch": "main",
"repository_url": "https://github.com/plone/volto",
- "search_bar_text": "Search", # TODO: Confirm usage of search_bar_text in plone-sphinx-theme
+ "search_bar_text": "Search",
"use_edit_page_button": True,
"use_issues_button": True,
"use_repository_button": True,
@@ -187,7 +185,7 @@
# Announce that we have an opensearch plugin
# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_use_opensearch
-html_use_opensearch = "https://6.docs.plone.org" # TODO: Confirm usage of opensearch in theme
+html_use_opensearch = "https://6.docs.plone.org"
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
@@ -289,20 +287,19 @@
# An extension that allows replacements for code blocks that
# are not supported in `rst_epilog` or other substitutions.
# https://stackoverflow.com/a/56328457/2214933
-def source_replace(app, docname, source):
- result = source[0]
- for key in app.config.source_replacements:
- result = result.replace(key, app.config.source_replacements[key])
- source[0] = result
+# def source_replace(app, docname, source):
+# result = source[0]
+# for key in app.config.source_replacements:
+# result = result.replace(key, app.config.source_replacements[key])
+# source[0] = result
# Dict of replacements.
-source_replacements = {
- "{NVM_VERSION}": "0.39.5",
-}
+# source_replacements = {
+# }
def setup(app):
- app.add_config_value("source_replacements", {}, True)
- app.connect("source-read", source_replace)
+ # app.add_config_value("source_replacements", {}, True)
+ # app.connect("source-read", source_replace)
app.add_config_value("context", "volto", "env")
diff --git a/docs/source/configuration/environmentvariables.md b/docs/source/configuration/environmentvariables.md
index a63c6144f1..701ce5c7c2 100644
--- a/docs/source/configuration/environmentvariables.md
+++ b/docs/source/configuration/environmentvariables.md
@@ -21,7 +21,7 @@ All configurable environment variables work at runtime, not only at build time.
You could, for example, build your Volto application, then start it in production with the `RAZZLE_API_PATH` environment variable.
```shell
-yarn build && RAZZLE_API_PATH=https://plone.org yarn start:prod
+pnpm build && RAZZLE_API_PATH=https://plone.org pnpm start:prod
```
This brings you a lot of power since you don't have to rebuild on every configuration change.
@@ -40,7 +40,7 @@ You can also generate builds on your continuous integration, then deploy them an
However, if you are not able to upgrade the packages `plone.restapi` (8.12.1 or greater) and `plone.rest` (2.0.0a1 or greater) in the backend, you can adjust your web server configuration and use the `RAZZLE_LEGACY_TRAVERSE` flag.
```shell
- RAZZLE_LEGACY_TRAVERSE=true yarn start:prod
+ RAZZLE_LEGACY_TRAVERSE=true pnpm start:prod
```
`VOLTO_ROBOTSTXT`
@@ -50,7 +50,7 @@ You can also generate builds on your continuous integration, then deploy them an
```shell
VOLTO_ROBOTSTXT="User-agent: *
- Disallow: /" yarn start
+ Disallow: /" pnpm start
```
```{note}
@@ -67,19 +67,19 @@ You can also generate builds on your continuous integration, then deploy them an
It helps you identify problems with a customization that does not work as you expect.
```shell
- DEBUG=volto:shadowing yarn start
+ DEBUG=volto:shadowing pnpm start
```
`i18n` enables the log of missing internationalization messages in the console.
```shell
- DEBUG=volto:i18n yarn start
+ DEBUG=volto:i18n pnpm start
```
`*` enables logging everywhere it exists in Volto.
```shell
- DEBUG=volto:* yarn start
+ DEBUG=volto:* pnpm start
```
`DEBUG_ADDONS_LOADER`
@@ -107,34 +107,34 @@ You can also generate builds on your continuous integration, then deploy them an
`ADDONS` can be used to temporarily add an add-on to your build for testing purposes.
```shell
- yarn add volto-slate
- ADDONS=volto-slate:asDefault yarn start
+ pnpm add @kitconcept/volto-light-theme
+ ADDONS=@kitconcept/volto-light-theme pnpm start
```
`ADDONS` can also be used to temporarily enable a feature or a set of customizations.
```shell
# given a folder './packages/coresandbox', like in vanilla Volto
- ADDONS=coresandbox:multilingualFixture yarn start
+ ADDONS=coresandbox:multilingualFixture pnpm start
```
If you need to specify several add-ons, separate them with a semicolon (`;`):
```shell
- ADDONS="test-addon;test-addon2" yarn start
+ ADDONS="test-addon;test-addon2" pnpm start
```
-
+
You can specify profiles for installation:
-
+
```shell
- ADDONS="test-addon:profile1;test-addon2:profile2" yarn start
+ ADDONS="test-addon:profile1;test-addon2:profile2" pnpm start
```
The following code snippets demonstrate how to configure add-ons.
First in `package.json`:
-
+
```json
"addons": [
"@kitconcept/volto-blocks-grid"
@@ -152,8 +152,8 @@ You can also generate builds on your continuous integration, then deploy them an
And finally using `ADDONS`:
```shell
- yarn add volto-slate
- ADDONS=volto-slate:asDefault yarn start
+ pnpm add volto-slate
+ ADDONS=volto-slate:asDefault pnpm start
```
As a result, your app will load the add-ons in the following order:
@@ -165,14 +165,14 @@ You can also generate builds on your continuous integration, then deploy them an
```{important}
The `ADDONS` key is a Volto specific configuration.
Simply setting `ADDONS` doesn't download the JavaScript package.
- This has to be covered another way, by either installing the add-on package (with `yarn add`), or loading it as a development package with `mrs-developer`.
+ This has to be covered another way, by either installing the add-on package (with `pnpm add`), or loading it as a development package with `mrs-developer`.
```
`BUILD_DIR`
This is a runtime-only environment variable that directs the build to run Volto from a specific location, other than the default folder `build`.
```shell
- yarn
+ pnpm install
BUILD_DIR=dist node dist/server.js
```
@@ -182,7 +182,7 @@ You can also generate builds on your continuous integration, then deploy them an
It can be relative to the current project or absolute.
```shell
- VOLTOCONFIG=../../volto.config.js yarn start
+ VOLTOCONFIG=../../volto.config.js pnpm start
```
````
diff --git a/docs/source/configuration/how-to.md b/docs/source/configuration/how-to.md
index 5d4eafd897..b8f8da6147 100644
--- a/docs/source/configuration/how-to.md
+++ b/docs/source/configuration/how-to.md
@@ -20,7 +20,7 @@ then access any of its internal configuration to retrieve the configuration you
like:
```js
-const absoluteUrl = `${config.settings.apiPath}/${content.url}`
+const absoluteUrl = `${config.settings.apiPath}/${content.url}`;
```
Both the main project and individual add-ons can extend Volto's configuration registry.
@@ -35,7 +35,8 @@ Both use the same method, using a function as the default export. This function
add-ons, it must be provided in the main `index.js` module of the add-on. For project's
it must be provided in the `src/config.js` module of the project.
-See the {doc}`../addons/index` section for extended information on how to work with add-ons.
+See the {doc}`../conceptual-guides/add-ons` and {doc}`../development/add-ons/index` sections for extended information on how to work with add-ons.
+
## Extending configuration in a project
@@ -150,3 +151,31 @@ export default function applyConfig(config) {
return config;
}
+```
+
+## `nonContentRoutes` and `nonContentRoutesPublic`
+
+`nonContentRoutes` is a list of routes reserved in Volto for its functionality as a content management system.
+These functions include user authentication and registration, changing settings through control panels, generating a site map, and other functions.
+Examples of these routes include `/login`, `/register`, and `/\/controlpanel\/.*$/`.
+Editors can't use them for content.
+The HTML attribute class value `cms-ui` is applied to members of `nonContentRoutes`.
+You can configure `nonContentRoutes` with either a regular expression or a string representing the end of the URI.
+
+`nonContentRoutesPublic` is a subset of `nonContentRoutes`.
+These routes are used for public sections of a Volto site that do not require authentication.
+This subset includes `/login`, `/search`, and `/sitemap`.
+
+The following example shows how to configure settings for `nonContentRoutes` and `nonContentRoutesPublic`.
+
+```js
+export default function applyConfig(config) {
+ config.settings = {
+ ...config.settings,
+ nonContentRoutes:[....],
+ nonContentRoutesPublic: [....]
+ };
+
+ return config;
+}
+```
diff --git a/docs/source/configuration/internalproxy.md b/docs/source/configuration/internalproxy.md
index ec8612fe65..dde6c9b369 100644
--- a/docs/source/configuration/internalproxy.md
+++ b/docs/source/configuration/internalproxy.md
@@ -61,7 +61,7 @@ export const settings = {
or use the environment variable:
```bash
-RAZZLE_DEV_PROXY_API_PATH=http://localhost:8081/mysite yarn start
+RAZZLE_DEV_PROXY_API_PATH=http://localhost:8081/mysite pnpm start
```
This redefines the request path from the internal proxy of the server side Node.js process to the Plone content backend API, but leaves the frontend Volto process making all content requests to `http://localhost:3000/++api++/`.
diff --git a/docs/source/configuration/volto-config-js.md b/docs/source/configuration/volto-config-js.md
index a8e4c4faa7..8bb16ce951 100644
--- a/docs/source/configuration/volto-config-js.md
+++ b/docs/source/configuration/volto-config-js.md
@@ -70,11 +70,11 @@ This environment variable allows you to specify a custom location for {file}`vol
It can be relative to the current project or absolute.
```shell
-VOLTOCONFIG=../../volto.config.js yarn start
+VOLTOCONFIG=../../volto.config.js pnpm start
```
```shell
-VOLTOCONFIG=$(pwd)/volto.config.js yarn start
+VOLTOCONFIG=$(pwd)/volto.config.js pnpm start
```
You can also set it from the root of the monorepo:
diff --git a/docs/source/configuration/volto-slate/index.md b/docs/source/configuration/volto-slate/index.md
index d8f51c4726..475cc3653c 100644
--- a/docs/source/configuration/volto-slate/index.md
+++ b/docs/source/configuration/volto-slate/index.md
@@ -13,7 +13,7 @@ myst:
`volto-slate` is an interactive default text editor for Volto, developed on top of {term}`Slate` and integrated into the core system.
It offers enhanced WYSIWYG functionality and behavior.
-See a [brief elevator pitch for `volto-slate`](https://www.youtube.com/watch?v=SOz-rk5e4_w).
+See a [brief elevator pitch for `volto-slate`](https://www.youtube-nocookie.com/embed/SOz-rk5e4_w?privacy_mode=1).
We believe that Volto's rich text form editor (the Volto Composite Page editor) needs strong integration between the rich text capabilities and the rest of the Volto blocks.
Some examples of the kind of strong integration we have in mind:
diff --git a/docs/source/configuration/zero-config-builds.md b/docs/source/configuration/zero-config-builds.md
index 7b4c396075..5ac09d7d87 100644
--- a/docs/source/configuration/zero-config-builds.md
+++ b/docs/source/configuration/zero-config-builds.md
@@ -17,7 +17,7 @@ In the past (before Volto 13), Volto was configured in build time using several
environment variables, commonly supplied via the command line, such as the following:
```shell
-PORT=11001 RAZZLE_API_PATH=https://plone.org/api yarn build`
+PORT=11001 RAZZLE_API_PATH=https://plone.org/api pnpm build`
```
and since Razzle is an isomorphic application, some of these values passed on build time, were
diff --git a/docs/source/contributing/acceptance-tests.md b/docs/source/contributing/acceptance-tests.md
index ea9b66ec1f..c74bd9d4cb 100644
--- a/docs/source/contributing/acceptance-tests.md
+++ b/docs/source/contributing/acceptance-tests.md
@@ -72,7 +72,7 @@ There is a directory per spec.
This directory is hot reloaded with your changes as you write the tests.
```{seealso}
-[Cypress documentation](https://docs.cypress.io/guides/overview/why-cypress)
+[Cypress documentation](https://docs.cypress.io/app/get-started/why-cypress)
```
diff --git a/docs/source/contributing/developing-core.md b/docs/source/contributing/developing-core.md
index eb79d97c8a..f0ffb5462e 100644
--- a/docs/source/contributing/developing-core.md
+++ b/docs/source/contributing/developing-core.md
@@ -48,7 +48,8 @@ Volto has the following folder structure.
├─ apps/
│ ├─ plone
│ ├─ nextjs
-│ └─ remix
+│ ├─ remix
+│ └─ rr7
├─ packages/
│ ├─ volto
│ ├─ client
@@ -71,15 +72,15 @@ Volto has the following folder structure.
```
-## Development pre-requisites
+## Development prerequisites
-To set up a Volto core development environment, your system must satisfy the following pre-requisites.
+To set up a Volto core development environment, your system must satisfy the following prerequisites.
-```{include} ./install-operating-system.md
+```{include} ../_inc/_install-operating-system.md
```
- {term}`nvm`
-- {term}`Node.js` LTS 20.x
+- {term}`Node.js` LTS 22.x
- {term}`pnpm`
- {term}`GNU make`
- {term}`Docker`
@@ -93,7 +94,7 @@ When developing a project using Plone, Yarn or other package managers may be use
### nvm
-```{include} ./install-nvm.md
+```{include} ../_inc/_install-nvm.md
```
@@ -102,7 +103,7 @@ When developing a project using Plone, Yarn or other package managers may be use
We recommend that you install Node.js using nvm.
Alternatively you can install Node.js using Homebrew or other package installer.
-```{include} ./install-nodejs.md
+```{include} ../_inc/_install-nodejs.md
```
@@ -135,19 +136,19 @@ Compare the output to the [latest pnpm release number](https://www.npmjs.com/pac
### Make
-```{include} ./install-make.md
+```{include} ../_inc/_install-make.md
```
### Docker
-```{include} ./install-docker.md
+```{include} ../_inc/_install-docker.md
```
### Git
-```{include} ../contributing/install-git.md
+```{include} ../_inc/_install-git.md
```
@@ -165,7 +166,7 @@ cd volto
Install the frontend dependencies.
```shell
-pnpm install
+make install
```
@@ -330,13 +331,6 @@ By default, the use of TypeScript is required in Plone frontend libraries, Volto
The monorepository consists of several core libraries.
-### Volto project generator
-
-`@plone/generator-volto` is a Yeoman generator that helps you set up Volto via command line.
-It generates all the boilerplate needed to start developing a Plone Volto project.
-It is used by [CookieCutter Plone Starter](https://github.com/collective/cookiecutter-plone-starter), the recommended way to set up Plone projects.
-The generator features an `addon` template for scaffolding Volto add-ons in your projects.
-
### Registry
`@plone/registry` provides support for building an add-on registry and infrastructure for JavaScript and TypeScript-based apps.
@@ -354,6 +348,17 @@ Used by Volto, you can also use it in other JavaScript frameworks and environmen
`@plone/volto-slate` is the glue package that provides support for the Slate library in Volto.
+### Volto project generator
+
+`@plone/generator-volto` is a Yeoman generator that helps you set up Volto via command line.
+It generates all the boilerplate needed to start developing a Plone Volto project.
+It is used by [CookieCutter Plone Starter](https://github.com/collective/cookiecutter-plone-starter), the recommended way to set up Plone projects.
+The generator features an `addon` template for scaffolding Volto add-ons in your projects.
+
+```{deprecated} 18.0.0-alpha.43
+For Volto 18, `@plone/generator-volto` is replaced by [Cookieplone](https://github.com/plone/cookieplone).
+```
+
## Supported frontends
@@ -362,7 +367,7 @@ Volto is the default frontend, and is React-based.
Classic UI is the Python-based, server-side rendered frontend.
In Volto's `apps` folder, you'll find a Volto project scaffolding that uses Volto as a library.
-This is the same as that which you'll have when you run the Volto generator or `cookiecutter-plone-starter`.
+This is the same as that which you'll have when you follow the instructions in {doc}`plone:install/create-project`).
## Experimental frontends
@@ -395,6 +400,16 @@ You can try it out using the following command.
pnpm --filter plone-remix dev
```
+### React Router 7
+
+This frontend is a proof of concept using React Router 7 with Plone.
+
+You can try it out using the following command.
+
+```shell
+pnpm --filter plone-rr7 dev
+```
+
### Vite build (client only)
This frontend is a proof of concept using a custom client build based in Vite with Plone.
diff --git a/docs/source/contributing/index.md b/docs/source/contributing/index.md
index cc6b8d03b2..46f2240f2b 100644
--- a/docs/source/contributing/index.md
+++ b/docs/source/contributing/index.md
@@ -44,7 +44,7 @@ The Volto Team reviews pull requests only from people with a GitHub account who
## Branch policy
-```{include} ./branch-policy.md
+```{include} ../_inc/_branch-policy.md
```
diff --git a/docs/source/contributing/language-features.md b/docs/source/contributing/language-features.md
index 258582172a..979fed7793 100644
--- a/docs/source/contributing/language-features.md
+++ b/docs/source/contributing/language-features.md
@@ -1,73 +1,63 @@
---
myst:
html_meta:
- "description": "Volto is developed using Babel to transpile modern JavaScript to JavaScript that browsers are able to understand and execute."
- "property=og:description": "Volto is developed using Babel to transpile modern JavaScript to JavaScript that browsers are able to understand and execute."
- "property=og:title": "Language features and conventions"
- "keywords": "Volto, Plone, frontend, React, Babel, translations, language, internationalization, i18n, localization, transpilation"
+ "description": "Volto uses several tools and follows conventions that provide features and browser support for the JavaScript language."
+ "property=og:description": "Volto uses several tools and follows conventions that provide features and browser support for the JavaScript language."
+ "property=og:title": "JavaScript language features and browser support"
+ "keywords": "Volto, Plone, frontend, React, Babel, JavaScript, transpilation"
---
+% Mixture of conceptual guide and how-to guide
+# JavaScript language features and browser support
-# Language features and conventions
+Volto uses several tools and follows conventions that provide features and browser support for the JavaScript language.
+
+% Conceptual guide
## Babel
-Volto is developed using Babel to transpile modern JavaScript to JavaScript that
-browsers are able to understand and execute.
+Babel transpiles {term}`ECMAScript` code, including React and JSX, into a backwards compatible version of JavaScript in current and older browsers or environments.
-Ecma International's TC39 (https://tc39.es/) is a group of JavaScript developers,
-implementers, academics, and more, collaborating with the community to maintain and
-evolve the definition of JavaScript. They stablished a process
-(https://tc39.es/process-document/) where the proposals are discussed, developed, and
-eventually approved (or dropped). The process has five stages (0 to 4) where reaching
-the stage 4 means the proposal is accepted and it becomes part of the JavaScript
-specification.
+Babel provides features and syntax that you can use in code when you develop on Volto.
+These features derive from the proposals that the {term}`TC39` produces.
-Babel enables a series of features and syntax that the developer can use in code to
-develop Volto on. These features are the proposals the TC39 is working on in the
-different stages of evolution.
+Volto uses `babel-razzle-preset`, which in turns uses `@babel/preset-env`, which together enable the use of all [TC39 finished proposals](https://github.com/tc39/proposals/blob/HEAD/finished-proposals.md#finished-proposals).
-Volto uses `babel-razzle-preset` which in turns uses `@babel/preset-env` which enables
-the use of all TC39 proposals currently in TC39's stage 4
-(https://github.com/tc39/proposals/blob/HEAD/finished-proposals.md#finished-proposals).
-### Browser compatibility
+% How-to guide
+## Browser compatibility
-Babel preset-env uses `browserlist` which gives the ability to micromanage the
-transformations needed by the current project depending of the browser support you are
-currently targeting.
+`@babel/preset-env` uses `browserslist`, which you can use to manage the transformations needed to target specific browser support in your project.
+This reduces the size of bundles, as Babel will apply only the required transforms that your target environment needs.
-By doing this, it enables the bundles to be smaller, as the resulting code does not need to
-support old browsers (thus, transform your code to ES5 compatible code) as Babel will
-apply only the required transforms that your target environments need. For more
-information: https://babeljs.io/docs/babel-preset-env#browserslist-integration
+```{seealso}
+https://babeljs.io/docs/babel-preset-env#browserslist-integration
+```
-Volto project generators use this browserlist by default (you can find it in your local `package.json`):
+Volto project generators use `browserslist` queries by default, which is in your local {file}`package.json`.
+You can adjust this file according to the environments you want to target.
```json
- "browserslist": [
- ">1%",
- "last 4 versions",
- "Firefox ESR",
- "not ie 11",
- "not dead"
- ],
+"browserslist": [
+ ">1%",
+ "last 4 versions",
+ "Firefox ESR",
+ "not dead"
+],
+```
+
+```{seealso}
+For usage and syntax, see the `browserslist` documentation of [Queries](https://github.com/browserslist/browserslist#queries).
```
-which you can adjust depending on the environments you are targeting in your local
-`package.json` file. You can find more information about how the queries in `broserlist`
-works in: https://github.com/browserslist/browserslist#queries
-### Support to deprecated browsers
+% How-to guide
+## Support of deprecated browsers
```{warning}
-Volto does not support deprecated browsers from its vendor (eg. IE11).
+Volto does not support deprecated browsers, such as Internet Explorer 11.
```
-If you still need to support deprecated browsers, you should use `browserslist` in your
-project to enable the required transforms for the target deprecated environments you
-have to support.
+If you still need to support deprecated browsers, you should use `browserslist` in your project to enable the required transforms for the target deprecated environments you must support.
-However, Volto (or its dependencies) might not be compatible with old browsers anyways,
-and you might need to provide some other workarounds to make the build work (and the
-deprecated browser not crash). You can refer to {doc}`this (outdated)
-document <../development/ie11compat>` in order to get some hints on how to do it.
+However, Volto or its dependencies might not be compatible with old browsers.
+You might need to create some workarounds to make the build work, and the deprecated browser not crash.
diff --git a/docs/source/contributing/linting.md b/docs/source/contributing/linting.md
index cd291fd556..feea6f0307 100644
--- a/docs/source/contributing/linting.md
+++ b/docs/source/contributing/linting.md
@@ -76,7 +76,7 @@ From here we will have access to the commands to check for errors and to fix the
You can run the pnpm `eslint`, `prettier`, and `stylelint` commands from the Volto package folder:
```shell
-pnpm lint
+pnpm lint
pnpm prettier
pnpm stylelint
```
@@ -89,8 +89,13 @@ pnpm prettier:fix
pnpm stylelint:fix
```
-````{note}
-The same commands can be found in your Volto add-on projects, as seen in the [`package.json.tpl`](https://github.com/plone/volto/blob/main/packages/generator-volto/generators/app/templates/package.json.tpl#L10) file.
+```{versionadded} Volto 18.0.0-alpha.43
+[Cookieplone](https://github.com/plone/cookieplone) is now the recommended way to develop Volto projects, using it as a boilerplate generator.
+Cookieplone uses the frontend code installed using `pnpm` instead of `yarn`.
+```
+
+````{deprecated} Volto 18.0.0
+The same commands can be found in your Volto legacy add-ons and projects created with `@plone/generator-volto`, as seen in the [`package.json.tpl`](https://github.com/plone/volto/blob/main/packages/generator-volto/generators/app/templates/package.json.tpl#L10) file.
You will use similar commands to run the linting commands, but with `yarn` instead of `pnpm`:
diff --git a/docs/source/contributing/testing.md b/docs/source/contributing/testing.md
index a1244c9496..37f241151d 100644
--- a/docs/source/contributing/testing.md
+++ b/docs/source/contributing/testing.md
@@ -66,10 +66,9 @@ This makes it faster and easier to test code changes.
In GitHub workflows or for testing add-ons, it's useful to use an alternate Jest configuration.
Volto provides a way to do so using a file {file}`jest.config.js`, or pointing the test runner to a file of your choice, using the `RAZZLE_JEST_CONFIG` environment variable.
-Because the Volto add-ons and Volto add-ons projects still use `yarn`, you must run the test command using `yarn` instead of `pnpm`.
```shell
-RAZZLE_JEST_CONFIG=my-custom-jest-config.js yarn test
+RAZZLE_JEST_CONFIG=my-custom-jest-config.js pnpm test
```
```{note}
@@ -85,7 +84,7 @@ Sometimes you need to enable different configurations and enable optional compon
You can use the `ADDONS` environment variable to define them.
```bash
-ADDONS=test-addon,test-addon2 yarn start
+ADDONS=test-addon,test-addon2 pnpm start
```
See {doc}`../configuration/environmentvariables` for more information.
diff --git a/docs/source/contributing/version-policy.md b/docs/source/contributing/version-policy.md
index a262808480..fe42d3d1d5 100644
--- a/docs/source/contributing/version-policy.md
+++ b/docs/source/contributing/version-policy.md
@@ -69,11 +69,12 @@ See also [Plone REST API Python and Plone compatibility](https://github.com/plon
## Node.js
Volto runs using [Node.js](https://nodejs.org/en).
-Volto always supports only [LTS versions of Node.js](https://github.com/nodejs/release#release-schedule).
+Volto supports only the latest two [LTS versions of Node.js](https://github.com/nodejs/release#release-schedule).
We recommend using the current LTS version.
+- Node.js 22 LTS: Supported since Volto 18.
- Node.js 20 LTS: Supported since Volto 17.
-- Node.js 18 LTS: Supported since Volto 17.
+- Node.js 18: No longer supported. It was supported in Volto 16 - 17.
- Node.js 16: No longer supported. It was supported in Volto 14 - 16.
- Node.js 14: No longer supported. It was supported in Volto 8.8.0 - 16.
- Node.js 12: No longer supported. It was supported in Volto 4 - 15.
@@ -91,7 +92,7 @@ We do not guarantee that outdated browsers, such as Internet Explorer 11, are su
## Branch policy
-```{include} ./branch-policy.md
+```{include} ../_inc/_branch-policy.md
```
diff --git a/docs/source/deploying/seamless-mode.md b/docs/source/deploying/seamless-mode.md
index a678728a73..0173bdbaf3 100644
--- a/docs/source/deploying/seamless-mode.md
+++ b/docs/source/deploying/seamless-mode.md
@@ -48,13 +48,13 @@ All the environment variables that are configurable now work at runtime, not at
Before Volto 13, you'd do:
```bash
-RAZZLE_API_PATH=https://plone.org yarn build && yarn start:prod
+RAZZLE_API_PATH=https://plone.org pnpm build && pnpm start:prod
```
From Volto 13 onwards, you can now do:
```bash
-yarn build && RAZZLE_API_PATH=https://plone.org yarn start:prod
+pnpm build && RAZZLE_API_PATH=https://plone.org pnpm start:prod
```
````
@@ -130,7 +130,7 @@ server {
error_log /dev/stdout;
# [seamless mode] Recommended as default configuration, using seamless mode new plone.rest traversal
- # yarn build && yarn start:prod
+ # pnpm build && pnpm start:prod
location ~ /\+\+api\+\+($|/.*) {
rewrite ^/\+\+api\+\+($|/.*) /VirtualHostBase/http/myservername.org/Plone/++api++/VirtualHostRoot/$1 break;
proxy_pass http://backend;
@@ -138,7 +138,7 @@ server {
# Legacy deployment example, using RAZZLE_LEGACY_TRAVERSE Volto won't append ++api++ automatically
# Recommended only if you can't upgrade to latest `plone.restapi` and `plone.rest`
- # yarn build && RAZZLE_API_PATH=http://myservername.org/api RAZZLE_LEGACY_TRAVERSE=true yarn start:prod
+ # pnpm build && RAZZLE_API_PATH=http://myservername.org/api RAZZLE_LEGACY_TRAVERSE=true pnpm start:prod
# location ~ /api($|/.*) {
# rewrite ^/api($|/.*) /VirtualHostBase/http/myservername.org/Plone/VirtualHostRoot/_vh_api$1 break;
# proxy_pass http://backend;
diff --git a/docs/source/deploying/sentry.md b/docs/source/deploying/sentry.md
index f92ba34ecc..4266ffd4b8 100644
--- a/docs/source/deploying/sentry.md
+++ b/docs/source/deploying/sentry.md
@@ -82,7 +82,7 @@ SENTRY_AUTH_TOKEN=foo \
SENTRY_ORG=my_organization \
SENTRY_PROJECT=new_project \
SENTRY_RELEASE=2.0.0 \
-SENTRY_DSN=https://boo@sentry.com/1 yarn build
+SENTRY_DSN=https://boo@sentry.com/1 pnpm build
node build/server.js
```
@@ -265,7 +265,7 @@ SENTRY_PROJECT=new_project
SENTRY_RELEASE=2.0.0
SENTRY_DSN=https://boo@sentry.com/1
SENTRY_FRONTEND_CONFIG='{"tags":{"site":"www.test.com","app":"test_app"},"extras":{"logger":"javascript-frontend", "release":"1.4.1"}}'
-SENTRY_BACKEND_CONFIG='{"tags":{"site":"www.test.com","app":"test_app"} yarn build
+SENTRY_BACKEND_CONFIG='{"tags":{"site":"www.test.com","app":"test_app"} pnpm build
node build/server.js
```
diff --git a/docs/source/deploying/simple.md b/docs/source/deploying/simple.md
index 1215cb3cd5..31b3894a3e 100644
--- a/docs/source/deploying/simple.md
+++ b/docs/source/deploying/simple.md
@@ -12,7 +12,7 @@ myst:
Volto is a Node.js application that runs on your machine/server and listens to a port. Once you are ready to deploy it, you should build it running:
```bash
-$ yarn build
+$ pnpm build
```
The Volto configuration determines the external URL Volto will be served, so if you just issue this command, the build will get that values and build an static bundle with that values (PORT=3000, API_PATH=http://localhost:8080/Plone).
@@ -20,13 +20,13 @@ The Volto configuration determines the external URL Volto will be served, so if
In order to make Volto work on a server under an specific DNS name, you must parametrize the build like:
```bash
-$ PORT=volto_node_process_port RAZZLE_API_PATH=https://mywebsite.com/api yarn build
+$ PORT=volto_node_process_port RAZZLE_API_PATH=https://mywebsite.com/api pnpm build
```
After the build, the bundle is created in `/build` folder, then in order to launch your application you can run:
```bash
-$ yarn start:prod
+$ pnpm start:prod
```
or
```bash
diff --git a/docs/source/development/add-ons/best-practices.md b/docs/source/development/add-ons/best-practices.md
new file mode 100644
index 0000000000..c023301634
--- /dev/null
+++ b/docs/source/development/add-ons/best-practices.md
@@ -0,0 +1,82 @@
+---
+myst:
+ html_meta:
+ "description": "Best practices for developing Volto add-ons"
+ "property=og:description": "Best practices for developing Volto add-ons"
+ "property=og:title": "Best practices for add-ons"
+ "keywords": "Volto, Plone, frontend, React, best, practices, add-ons"
+---
+
+# Best practices for add-ons
+
+This document describes the best practices when you develop your add-on.
+
+
+## Integrate your add-on with Volto's add-on framework
+
+Just like Plone add-ons provide some features by default, Volto add-ons should register some features by default.
+For example, if your add-on provides widgets, then register the most basic configuration of that widget with a name that it uses.
+
+For more complicated cases, see if you can structure your code to use the `settings` {term}`configuration registry`, or stash your configuration in your block registration.
+
+Let's say you're building a color picker widget.
+You might want to provide a palette of colors from which to choose.
+Your widget should integrate with a default `settings.colorWidgetPalette`, which would be a list of colors.
+
+You should also provide a widget factory, so it can be used to create multiple instances of that color widget with custom color palettes.
+
+
+## Provide additional configuration
+
+An add-on can ship with multiple {term}`Volto configuration loader`s.
+This makes it possible to provide multiple configuration methods for specific demonstration purposes.
+Alternatively you could ship your add-on with a default "shallow" integration, then provide another separate configuration loader for a deeper integration.
+
+
+## Avoid shadowing core Volto files
+
+This rule is meant to be broken.
+If you need to customize a specific file in Volto core and you have multiple projects, it may be better to create an add-on that holds that customized file.
+Doing so creates a single place to maintain that "file fork".
+Otherwise, it's best to avoid shipping generic add-ons with Volto core customizations.
+
+If you customize core Volto files, you should warn consumers of your add-on in its description.
+
+If your use case is generic enough, [file a feature request](https://github.com/plone/volto/issues/new?assignees=&labels=04+type%3A+enhancement&projects=&template=feature_request.md&title=) to discuss with the Volto Team whether your customization should be included directly in core.
+
+
+## Documentation
+
+"If it ain't documented, it's broken."
+
+At least create a README with a brief description and a screenshot or video of what your add-on does.
+
+Ideally, the README should include requirements or compatability with various versions of Volto and Plone, and installation and configuration instructions.
+
+
+## Test the add-on
+
+```{versionadded} Volto 18.0.0-alpha.43
+```
+
+Cookieplone provides a self-bootstrapping and testing framework in Volto 18.
+See {doc}`test-add-ons-18`.
+
+Previously in Volto 17 and early alpha versions of Volto 18, it was not easy to ship an add-on with a self-bootstrapping and testing framework.
+However, for these older versions of Volto you can create a separate minimal Volto project that can hold the Cypress integration tests and trigger the CI tests.
+
+
+## Use appropriate npm Registry tags
+
+If you release your add-on to the [npm Registry](https://www.npmjs.com/), consider adding the following tags, next to your add-on-specific tags.
+
+- `volto-addon`
+- `volto`
+- `plone`
+- `react`
+
+
+## Add to `collective/awesome-volto`
+
+Consider adding your add-on to the [`collective/awesome-volto`](https://github.com/collective/awesome-volto) add-ons list.
+This list provides visibility to your add-on, as well as further solidifies Volto's position in the Plone community.
diff --git a/docs/source/development/add-ons/create-an-add-on-17.md b/docs/source/development/add-ons/create-an-add-on-17.md
new file mode 100644
index 0000000000..6034a5ef57
--- /dev/null
+++ b/docs/source/development/add-ons/create-an-add-on-17.md
@@ -0,0 +1,20 @@
+---
+myst:
+ html_meta:
+ "description": "How to create an add-on for Volto 17"
+ "property=og:description": "How to create an add-on for Volto 17"
+ "property=og:title": "Create an add-on for Volto 17"
+ "keywords": "add-on, Volto, create"
+---
+
+# Create an add-on for Volto 17
+
+Volto add-on packages are just CommonJS packages.
+The only requirement is that they point the `main` key of their {file}`package.json` to a module that exports as a default function, acting as a {term}`Volto configuration loader`.
+
+You can use Plone's Yeoman-based generator [`generator-volto`](https://github.com/plone/generator-volto) to create a Volto add-on.
+
+```shell
+npm install -g @plone/generator-volto
+yo @plone/volto:addon [] [options]
+```
diff --git a/docs/source/development/add-ons/create-an-add-on-18.md b/docs/source/development/add-ons/create-an-add-on-18.md
new file mode 100644
index 0000000000..15ffca9d6a
--- /dev/null
+++ b/docs/source/development/add-ons/create-an-add-on-18.md
@@ -0,0 +1,200 @@
+---
+myst:
+ html_meta:
+ "description": "How to create an add-on for Volto 18"
+ "property=og:description": "How to create an add-on for Volto 18"
+ "property=og:title": "Create an add-on for Volto 18"
+ "keywords": "add-on, Volto, create, development"
+---
+
+# Create an add-on for Volto 18
+
+This chapter describes how you can create an add-on using Volto 18 or later for the Plone user interface, while having full control over its development and deployment.
+
+```{versionadded} Volto 18.0.0-alpha.43
+{term}`Cookieplone` is now the method to create a Plone add-on with Volto version 18.0.0-alpha.43 and above.
+```
+
+## System requirements
+
+Follow the section {ref}`plone:create-project-cookieplone-system-requirements` to set up your system.
+
+
+## Generate the add-on project
+
+To develop an add-on for only the frontend, then run the following command to generate your add-on project using the `frontend_addon` Cookieplone template.
+To develop add-ons for each the frontend and backend that work together, then instead use the Cookieplone template `project` in the command.
+See {doc}`plone:install/create-project-cookieplone` for details of the latter scenario.
+The following output assumes the former scenario.
+
+```shell
+pipx run cookieplone frontend_addon
+```
+
+```console
+❯ pipx run cookieplone frontend_addon
+⚠️ cookieplone is already on your PATH and installed at
+ /Users//.local/bin/cookieplone. Downloading and running anyway.
+╭──────────────────────────────── cookieplone ─────────────────────────────────╮
+│ │
+│ .xxxxxxxxxxxxxx. │
+│ ;xxxxxxxxxxxxxxxxxxxxxx; │
+│ ;xxxxxxxxxxxxxxxxxxxxxxxxxxxx; │
+│ xxxxxxxxxx xxxxxxxxxx │
+│ xxxxxxxx. .xxxxxxxx │
+│ xxxxxxx xxxxxxx: xxxxxxx │
+│ :xxxxxx xxxxxxxxxx xxxxxx: │
+│ :xxxxx+ xxxxxxxxxxx +xxxxx: │
+│ .xxxxx. :xxxxxxxxxx .xxxxx. │
+│ xxxxx+ ;xxxxxxxx +xxxxx │
+│ xxxxx +xx. xxxxx. │
+│ xxxxx: .xxxxxxxx :xxxxx │
+│ xxxxx .xxxxxxxxxx xxxxx │
+│ xxxxx xxxxxxxxxxx xxxxx │
+│ xxxxx .xxxxxxxxxx xxxxx │
+│ xxxxx: .xxxxxxxx :xxxxx │
+│ .xxxxx ;xx. ... xxxxx. │
+│ xxxxx+ :xxxxxxxx +xxxxx │
+│ .xxxxx. :xxxxxxxxxx .xxxxx. │
+│ :xxxxx+ xxxxxxxxxxx ;xxxxx: │
+│ :xxxxxx xxxxxxxxxx xxxxxx: │
+│ xxxxxxx xxxxxxx; xxxxxxx │
+│ xxxxxxxx. .xxxxxxxx │
+│ xxxxxxxxxx xxxxxxxxxx │
+│ ;xxxxxxxxxxxxxxxxxxxxxxxxxxxx+ │
+│ ;xxxxxxxxxxxxxxxxxxxxxx; │
+│ .xxxxxxxxxxxxxx. │
+│ │
+╰──────────────────────────────────────────────────────────────────────────────╯
+You've downloaded /Users//.cookiecutters/cookieplone-templates before.
+Is it okay to delete and re-download it? [y/n] (y):
+╭─────────────────────────── Volto Addon Generator ────────────────────────────╮
+│ │
+│ Creating a new Volto Addon │
+│ │
+│ Sanity check results: │
+│ │
+│ │
+│ - Node: ✓ │
+│ - git: ✓ │
+│ │
+╰──────────────────────────────────────────────────────────────────────────────╯
+ [1/8] Add-on Title (Volto Add-on):
+ [2/8] Add-on (Short name of the addon) (volto-addon):
+ [3/8] A short description of your addon (A new add-on for Volto):
+ [4/8] Author (Plone Community):
+ [5/8] Author E-mail (collective@plone.org):
+ [6/8] GitHub Username or Organization (collective):
+ [7/8] Package name on NPM (volto-addon):
+ [8/8] Volto version (18.0.0-alpha.46):
+╭────────────────────────── Volto Add-on generation ───────────────────────────╮
+│ │
+│ Summary: │
+│ │
+│ - Volto version: 18.0.0-alpha.46 │
+│ - Output folder: /Users//Development/plone/volto-addon │
+│ │
+│ │
+╰──────────────────────────────────────────────────────────────────────────────╯
+╭─────────────────────── 🎉 New addon was generated 🎉 ────────────────────────╮
+│ │
+│ volto-addon │
+│ │
+│ Now, enter the generated directory and finish the install: │
+│ │
+│ cd volto-addon │
+│ make install │
+│ │
+│ start coding, and push to your organization. │
+│ │
+│ Sorry for the convenience, │
+│ The Plone Community. │
+│ │
+│ https://plone.org/ │
+╰──────────────────────────────────────────────────────────────────────────────╯
+```
+
+Cookieplone creates a folder with the name of the add-on, in this example, `volto-addon`.
+
+Change your current working directory to {file}`volto-addon`.
+
+```shell
+cd volto-addon
+```
+
+To install the add-on setup, use the following command.
+
+```shell
+make install
+```
+
+
+## Start Plone backend Docker container
+
+In the currently open shell session, issue the following command.
+
+```shell
+make backend-docker-start
+```
+
+```console
+❯ make backend-docker-start
+==> Start Docker-based Plone Backend
+=======================================================================================
+Creating Plone volto SITE: Plone
+Aditional profiles:
+THIS IS NOT MEANT TO BE USED IN PRODUCTION
+Read about it: https://6.docs.plone.org/install/containers/images/backend.html
+=======================================================================================
+Ignoring index for /app/var/filestorage/Data.fs
+INFO:Plone Site Creation:Creating a new Plone site @ Plone
+INFO:Plone Site Creation: - Using the voltolighttheme distribution and answers from /app/scripts/default.json
+INFO:Plone Site Creation: - Stopping site creation, as there is already a site with id Plone at the instance. Set DELETE_EXISTING=1 to delete the existing site before creating a new one.
+Using default configuration
+2024-10-11 16:12:47 INFO [chameleon.config:39][MainThread] directory cache: /app/var/cache.
+2024-10-11 16:12:48 INFO [plone.restapi.patches:16][MainThread] PATCH: Disabled ZPublisher.HTTPRequest.ZopeFieldStorage.VALUE_LIMIT. This enables file uploads larger than 1MB.
+2024-10-11 16:12:49 INFO [plone.volto:23][MainThread] Aliasing collective.folderish classes to plone.volto classes.
+2024-10-11 16:12:50 INFO [Zope:42][MainThread] Ready to handle requests
+Starting server in PID 1.
+2024-10-11 16:12:50 INFO [waitress:486][MainThread] Serving on http://0.0.0.0:8080
+```
+
+This will start a clean Plone server for development purposes so you can start developing your add-on.
+
+
+## Start Plone development frontend
+
+Create a second shell session in a new window.
+Change your current working directory to {file}`volto-addon`.
+Start the Plone development frontend with the following command.
+
+```shell
+make start
+```
+
+```console
+webpack 5.90.1 compiled successfully in 11004 ms
+sswp> Handling Hot Module Reloading
+Using volto.config.js in: //frontend/volto.config.js
+✅ Server-side HMR Enabled!
+Volto is running in SEAMLESS mode
+Proxying API requests from http://localhost:3000/++api++ to http://localhost:8080/Plone
+🎭 Volto started at 0.0.0.0:3000 🚀
+```
+
+Note that the Plone frontend uses an internal proxy server to connect with the Plone backend.
+Open a browser at the following URL to visit your Plone site.
+
+http://localhost:3000
+
+You will see a page similar to the following.
+
+```{image} /_static/plone-home-page.png
+:alt: Plone home page
+:class: figure
+```
+
+Your newly created add-on will be installed with vanilla core Volto.
+You can start developing it in the add-on package located in {file}`packages/volto-addon`.
+
+You can stop the site with {kbd}`ctrl-c`.
diff --git a/docs/source/development/add-ons/extend-eslint-add-on.md b/docs/source/development/add-ons/extend-eslint-add-on.md
new file mode 100644
index 0000000000..6017463332
--- /dev/null
+++ b/docs/source/development/add-ons/extend-eslint-add-on.md
@@ -0,0 +1,43 @@
+---
+myst:
+ html_meta:
+ "description": "Extend ESLint configuration from an add-on"
+ "property=og:description": "Extend ESLint configuration from an add-on"
+ "property=og:title": "Extend ESLint configuration from an add-on"
+ "keywords": "Volto, add-on, extensions, frontend, Plone, configuration, ESLint, lint"
+---
+
+# Extend ESLint configuration from an add-on
+
+```{versionadded} Volto 16.4.0
+```
+
+Starting with Volto v16.4.0, you can customize the ESLint configuration from an add-on.
+You should provide a {file}`eslint.extend.js` file in your add-on's root folder, which exports a `modify(defaultConfig)` function.
+For example, to host some code outside the regular {file}`src/` folder of your add-on, you need to add the following {file}`eslint.extend.js` file:
+
+```js
+const path = require('path');
+
+module.exports = {
+ modify(defaultConfig) {
+ const aliasMap = defaultConfig.settings['import/resolver'].alias.map;
+ const addonPath = aliasMap.find(
+ ([name]) => name === '@plone-collective/some-volto-add-on',
+ )[1];
+
+ const extraPath = path.resolve(`${addonPath}/../extra`);
+ aliasMap.push(['@plone-collective/extra', extraPath]);
+
+ return defaultConfig;
+ },
+};
+```
+
+This allows the add-on `@plone-collective/some-volto-add-on` to host some code outside its normal {file}`src/` folder.
+If you put that code in the {file}`extra` folder, that code would be available under the `@plone-collective/extra` name.
+
+```{note}
+This takes care only of the ESLint integration.
+For proper language support, you'll still need to configure it in the {file}`razzle.extend.js` file of your add-on.
+```
diff --git a/docs/source/development/add-ons/extend-webpack-add-on.md b/docs/source/development/add-ons/extend-webpack-add-on.md
new file mode 100644
index 0000000000..c7fbd69aed
--- /dev/null
+++ b/docs/source/development/add-ons/extend-webpack-add-on.md
@@ -0,0 +1,55 @@
+---
+myst:
+ html_meta:
+ "description": "Extend webpack setup from an add-on with razzle.extend.js"
+ "property=og:description": "Extend webpack setup from an add-on with razzle.extend.js"
+ "property=og:title": "Extend webpack setup from an add-on"
+ "keywords": "Volto, Plone, webpack, add-on, razzle.extend.js, Razzle"
+---
+
+# Extend webpack setup from an add-on
+
+```{deprecated} Volto 18
+The project configuration approach as described in this document is deprecated in Volto 18 and will be removed in Volto 19.
+You should instead follow the add-on approach as described in {doc}`../../conceptual-guides/add-ons`.
+```
+
+Just like you can extend Razzle's configuration from the project, you can do the same with an add-on.
+You should provide a {file}`razzle.extend.js` file in your add-on root folder.
+The following code example manages two things.
+
+- Add a new webpack plugin, [`webpack-bundle-analyzer`](https://www.npmjs.com/package/webpack-bundle-analyzer).
+- Reconfigure the `theme.config` alias, to enable a custom Semantic UI theme inside the add-on.
+
+ ```js
+ const analyzerPlugin = {
+ name: 'bundle-analyzer',
+ options: {
+ analyzerHost: '0.0.0.0',
+ analyzerMode: 'static',
+ generateStatsFile: true,
+ statsFilename: 'stats.json',
+ reportFilename: 'reports.html',
+ openAnalyzer: false,
+ },
+ };
+
+ const plugins = (defaultPlugins) => {
+ return defaultPlugins.concat([analyzerPlugin]);
+ };
+ const modify = (config, { target, dev }, webpack) => {
+ const themeConfigPath = `${__dirname}/theme/theme.config`;
+ config.resolve.alias['../../theme.config$'] = themeConfigPath;
+
+ return config;
+ };
+
+ module.exports = {
+ plugins,
+ modify,
+ };
+ ```
+
+```{seealso}
+[`volto-searchlib`'s {file}`razzle.extend.js`](https://github.com/eea/volto-searchlib/blob/d84fec8eec1def0088d8025eaf5d7197074b95a7/razzle.extend.js) file for an example of how to include additional paths for the Babel configuration, and how to add additional webpack name aliases.
+```
diff --git a/docs/source/development/add-ons/i18n.md b/docs/source/development/add-ons/i18n.md
new file mode 100644
index 0000000000..ab8d838f8f
--- /dev/null
+++ b/docs/source/development/add-ons/i18n.md
@@ -0,0 +1,39 @@
+---
+myst:
+ html_meta:
+ "description": "Internationalize your add-on and override translations"
+ "property=og:description": "Internationalize your add-on and override translations"
+ "property=og:title": "Add-on Internationalization"
+ "keywords": "Volto, internationalization, i18n, add-on"
+---
+
+# Add-on internationalization
+
+The {term}`internationalization` (i18n) workflow in and add-on is similar to core Volto.
+You develop your add-on, then add the translations to your code.
+
+See {ref}`create-i18n-strings` for how to mark strings and phrases as translatable.
+
+Your add-on has a {file}`locales` folder with a `.pot` file.
+
+1. Create the following structure in your add-ons {file}`locales` folder for every language you want to support.
+ As an example for the language Italian:
+
+ ```text
+ it
+ └── LC_MESSAGES
+ └── volto.po
+ ```
+
+1. Run `pnpm i18n` in the context of your add-on.
+1. Go to each `.po` file in your {file}`locales` folder, and write the translations for each translation literal.
+
+In the context of your project, run `pnpm i18n` to merge the add-on translations with the ones of your project.
+
+
+## Override translations
+
+If you have multiple add-ons installed in your project, the translations are loaded in the order your add-ons are listed in {file}`package.json`.
+If two add-ons provide different translations for the same message, then the last defined add-on wins.
+
+When running `pnpm i18n` in the context of your project, the project's own locales are processed last and can override translations from any add-on.
diff --git a/docs/source/development/add-ons/index.md b/docs/source/development/add-ons/index.md
new file mode 100644
index 0000000000..97296c4a12
--- /dev/null
+++ b/docs/source/development/add-ons/index.md
@@ -0,0 +1,30 @@
+---
+myst:
+ html_meta:
+ "description": "How to develop Volto add-ons"
+ "property=og:description": "How to develop Volto add-ons"
+ "property=og:title": "Develop Volto add-ons"
+ "keywords": "Volto, Plone, CMS, add-on"
+---
+
+# Develop Volto add-ons
+
+```{toctree}
+:maxdepth: 1
+
+install-an-add-on
+install-an-add-on-dev-18
+install-an-add-on-dev-17
+load-add-on-configuration
+create-an-add-on-18
+create-an-add-on-17
+test-add-ons-18
+test-add-ons-17
+extend-webpack-add-on
+extend-eslint-add-on
+troubleshoot-transpilation
+i18n
+best-practices
+theme
+public-folder
+```
diff --git a/docs/source/development/add-ons/install-an-add-on-dev-17.md b/docs/source/development/add-ons/install-an-add-on-dev-17.md
new file mode 100644
index 0000000000..8c938c6391
--- /dev/null
+++ b/docs/source/development/add-ons/install-an-add-on-dev-17.md
@@ -0,0 +1,124 @@
+---
+myst:
+ html_meta:
+ "description": "How to install an add-on in development mode in Volto 17 in your Plone project"
+ "property=og:description": "How to install an add-on in development mode in Volto 17 in your Plone project"
+ "property=og:title": "Install an add-on in development mode in Volto 17"
+ "keywords": "Volto, Plone, add-on, stable, development, mode"
+---
+
+# Install an add-on in development mode in Volto 17
+
+Use [`mrs-developer`](https://www.npmjs.com/package/mrs-developer) to manage the development cycle of Volto add-ons.
+This tool pulls the remote code and configures the current project, making the add-on available for the build.
+By doing this, you can develop both the project and the add-on product as if they were both part of the current codebase.
+
+`mrs-developer` is included and installed by default when you generate a project with the generator.
+Use the following command to install the configuration of `mrs.developer.json` in your project.
+
+```shell
+make install
+```
+
+
+## Configure `mrs-developer`
+
+{file}`mrs.developer.json` is the configuration file that instructs `mrs-developer` from where it should pull the packages.
+The generator includes an empty one for you.
+Edit {file}`mrs.developer.json` and add the following code.
+
+```json
+{
+ "acme-volto-foo-addon": {
+ "package": "@acme/volto-foo-addon",
+ "url": "git@github.com:acme/my-volto-addon.git",
+ "path": "src"
+ }
+}
+```
+
+Then run:
+
+```bash
+make install
+```
+
+Now the add-on appears in `src/addons/`.
+
+```{note}
+The `package` property is optional.
+Use it only if your package has a namespace.
+
+`src` is required if the content of your add-on is located in the `src` directory.
+Since that is the convention for all Volto add-on packages, you must always include it.
+```
+
+```{seealso}
+See [`mrs-developer` configuration options](https://www.npmjs.com/package/mrs-developer).
+```
+
+
+## Resolve import paths
+
+Your project uses a file to configure import paths, either {file}`tsconfig.json` or {file}`jsconfig.json` at the Volto project root.
+`mrs-developer` automatically manages the content of this file for you.
+If you choose not to use `mrs-developer`, you'll have to manually add configuration to this file.
+
+```json
+{
+ "compilerOptions": {
+ "paths": {
+ "acme-volto-foo-addon": [
+ "addons/acme-volto-foo-addon/src"
+ ]
+ },
+ "baseUrl": "src"
+ }
+}
+```
+
+
+```{warning}
+Both values for `paths` and `baseUrl` must match your project's layout.
+```
+
+```{tip}
+You should use the `src` path inside your package and point the `main` key in {file}`package.json` to the {file}`index.js` file in {file}`src/index.js`.
+```
+
+
+## Add-on development lifecycle
+
+If you want to "disable" using the development version of an add-on, or keep a more stable version of `mrs.developer.json` in your source code repository, you can set its status by adding a `develop` key to {file}`mrs.developer.json` as shown.
+
+```json
+{
+ "acme-volto-foo-addon": {
+ "package": "@acme/volto-foo-addon",
+ "url": "git@github.com:acme/my-volto-addon.git",
+ "path": "src",
+ "develop": true
+ }
+}
+```
+
+Whenever you change a value in your {file}`mrs.developer.json`, you must run `make install` again.
+
+
+## Add-on dependencies, yarn workspaces
+
+If your add-on needs to bring in additional JavaScript package dependencies, you'll have to set your add-on package as a "Yarn workspace".
+You should add a `workspaces` key to the {file}`package.json` of your Volto project.
+
+```json
+"workspaces": ["src/addons/my-volto-addon"],
+```
+
+It is common practice to use a star (`*`) glob pattern for the workspaces.
+
+```json
+"workspaces": ["src/addons/*"],
+```
+
+If you do this, make sure to always clean up the `src/addons` folder whenever you toggle the development status of an add-on, as the existence of the add-on folder under `src/addons` will still influence yarn.
+To do so, run `make install` again to remove the no longer required package.
diff --git a/docs/source/development/add-ons/install-an-add-on-dev-18.md b/docs/source/development/add-ons/install-an-add-on-dev-18.md
new file mode 100644
index 0000000000..7828759fc7
--- /dev/null
+++ b/docs/source/development/add-ons/install-an-add-on-dev-18.md
@@ -0,0 +1,143 @@
+---
+myst:
+ html_meta:
+ "description": "How to install an add-on in development mode in Volto 18 in your Plone project"
+ "property=og:description": "How to install an add-on in development mode in Volto 18 in your Plone project"
+ "property=og:title": "Install an add-on in development mode in Volto 18"
+ "keywords": "Volto, Plone, add-on, development, mode"
+---
+
+# Install an add-on in development mode in Volto 18
+
+Use [`mrs-developer`](https://www.npmjs.com/package/mrs-developer) to manage the development cycle of Volto add-ons.
+This tool pulls the remote code and configures the current project, making the add-on available for the build.
+By doing this, you can develop both the project and the add-on product as if they were both part of the current codebase.
+
+`mrs-developer` is included and installed by default when you generate a project with Cookieplone.
+Use the following command to install the configuration of `mrs.developer.json` in your project.
+
+```shell
+make install
+```
+
+Next, you need to add the add-on to the `addons` key of your Plone project's {file}`package.json`.
+
+```json
+{
+ "name": "my-volto-project",
+ "addons": [
+ "name-of-add-on"
+ ]
+}
+```
+
+```{seealso}
+Alternatively, you can use {file}`volto.config.js` to declare add-ons in your Plone project.
+See {doc}`../../configuration/volto-config-js`.
+```
+
+## Configure `mrs-developer`
+
+{file}`mrs.developer.json` is the configuration file that instructs `mrs-developer` from where it should pull the packages.
+Cookieplone includes an empty one for you.
+Edit {file}`mrs.developer.json` and add the following code.
+
+```json
+{
+ "acme-volto-foo-addon": {
+ "output": "packages",
+ "package": "@acme/volto-foo-addon",
+ "url": "git@github.com:acme/my-volto-addon.git",
+ "path": "src"
+ }
+}
+```
+
+Then run:
+
+```bash
+make install
+```
+
+Now the add-on appears in `packages/acme-volto-foo-addon/`.
+
+```{note}
+The `package` property is optional.
+Use it only if your package has a namespace.
+
+`src` is required if the content of your add-on is located in the `src` directory.
+Since that is the convention for all Volto add-on packages, you must always include it.
+```
+
+```{seealso}
+See [`mrs-developer` configuration options](https://www.npmjs.com/package/mrs-developer).
+```
+
+
+## Resolve import paths
+
+```{versionadded} Volto 18.0.0-alpha.43
+```
+
+The Cookieplone setup uses `pnpm` to resolve import paths.
+You have nothing to do here.
+
+
+## Add-on development lifecycle
+
+If you want to "disable" using the development version of an add-on, or keep a more stable version of `mrs.developer.json` in your source code repository, you can set its status by adding a `develop` key to {file}`mrs.developer.json` as shown.
+
+```json
+{
+ "acme-volto-foo-addon": {
+ "output": "packages",
+ "package": "@acme/volto-foo-addon",
+ "url": "git@github.com:acme/my-volto-addon.git",
+ "path": "src",
+ "develop": true
+ }
+}
+```
+
+Whenever you change a value in your {file}`mrs.developer.json`, you must run `make install` again.
+
+
+## Add-on dependencies
+
+If your add-on needs to bring in additional JavaScript package dependencies, you'll have to declare them as normal package dependencies.
+
+
+## `pnpm` workspaces
+
+You need to configure your add-ons using a `pnpm` workspace.
+You can configure them using the file {file}`pnpm-workspace.yaml` and declare all your development add-ons in there.
+
+```yaml
+packages:
+ - 'core/packages/*'
+ - 'packages/*'
+```
+
+If the add-on you are developing was created using {term}`Cookieplone`, then you have to add the following to {file}`pnpm-workspace.yaml` detect them.
+
+```yaml
+packages:
+ - 'core/packages'
+ - 'packages/my-policy-addon'
+ - 'packages/**/packages/*'
+```
+
+Note the nesting of `packages` since a {term}`Cookieplone` generated add-on will have a `packages` folder in itself.
+You can explicitly declare the add-ons, too.
+
+```yaml
+packages:
+ - 'core/packages'
+ - 'packages/my-policy-addon'
+ - 'packages/my-development-mode-addon/packages/my-development-mode-addon'
+ - 'packages/**/packages/*'
+```
+
+```{important}
+Run `make install` after any change in {file}`pnpm-workspace.yaml` to update the setup.
+```
diff --git a/docs/source/development/add-ons/install-an-add-on.md b/docs/source/development/add-ons/install-an-add-on.md
new file mode 100644
index 0000000000..8dfc9df327
--- /dev/null
+++ b/docs/source/development/add-ons/install-an-add-on.md
@@ -0,0 +1,69 @@
+---
+myst:
+ html_meta:
+ "description": "How to install an add-on in Volto"
+ "property=og:description": "How to install an add-on in Volto"
+ "property=og:title": "Install an add-on in Volto"
+ "keywords": "add-on, Volto, install"
+---
+
+# Install an add-on in Volto
+
+This document describes how to install an add-on in Volto.
+
+You can install an add-on just like any other JavaScript package from the [npm Registry](https://www.npmjs.com/).
+
+`````{tab-set}
+:sync-group: install-add-on
+
+````{tab-item} Volto 18
+:sync: volto-18
+```shell
+pnpm --filter add
+```
+````
+
+````{tab-item} Volto 17
+:sync: volto-17
+```shell
+yarn add
+```
+````
+`````
+
+If the add-on is not published on the npm Registry, [you can install it directly from GitHub](https://pnpm.io/cli/add#install-from-git-repository).
+
+
+`````{tab-set}
+:sync-group: install-add-on
+
+````{tab-item} Volto 18
+:sync: volto-18
+```shell
+pnpm add collective/volto-dropdownmenu
+```
+````
+
+````{tab-item} Volto 17
+:sync: volto-17
+```shell
+yarn add collective/volto-dropdownmenu
+```
+````
+`````
+
+Next, you need to add the add-on to the `addons` key of your Plone project's {file}`package.json`.
+
+```json
+{
+ "name": "my-volto-project",
+ "addons": [
+ "name-of-add-on"
+ ]
+}
+```
+
+```{seealso}
+Alternatively, you can use {file}`volto.config.js` to declare add-ons in your Plone project.
+See {doc}`../../configuration/volto-config-js`.
+```
diff --git a/docs/source/development/add-ons/load-add-on-configuration.md b/docs/source/development/add-ons/load-add-on-configuration.md
new file mode 100644
index 0000000000..33f0a21c9b
--- /dev/null
+++ b/docs/source/development/add-ons/load-add-on-configuration.md
@@ -0,0 +1,65 @@
+---
+myst:
+ html_meta:
+ "description": "Load configuration from add-ons"
+ "property=og:description": "Load configuration from add-ons"
+ "property=og:title": "Load configuration from add-ons"
+ "keywords": "Volto, add-on, extensions, frontend, Plone, configuration"
+---
+
+# Load configuration from add-ons
+
+As a convenience, an add-on can export configuration functions that can mutate in-place the overall Volto {term}`configuration registry`.
+An add-on can export multiple configuration methods, making it possible to selectively choose which specific add-on functionality you want to load.
+
+Some add-ons might allow the Volto project to selectively load some of their configuration, so they may offer additional configuration functions.
+You can load them by overloading the add-on name in the `addons` {file}`package.json` key, as shown.
+
+```{code-block} json
+:emphasize-lines: 4
+
+{
+ "name": "my-nice-volto-project",
+ "addons": [
+ "acme-volto-foo-add-on:loadOptionalBlocks,overrideSomeDefaultBlock",
+ "volto-ga"
+ ],
+}
+```
+
+```{note}
+The additional comma-separated names should be exported from the add-on package's {file}`index.js`.
+The main configuration function should be exported as the default.
+An add-on's default configuration method will always be loaded.
+```
+
+If for some reason you want to manually load the add-on, you can edit your project's {file}`config.js` module:
+
+```js
+import loadExampleAddon, { enableOptionalBlocks } from 'volto-example-add-on';
+import * as voltoConfig from '@plone/volto/config';
+
+const config = enableOptionalBlocks(loadExampleAddon(voltoConfig));
+
+export blocks = {
+ ...config.blocks,
+}
+```
+
+Volto provides a helper method `applyConfig` to do the same.
+
+```js
+import { applyConfig } from '@plone/volto/helpers';
+import * as voltoConfig from '@plone/volto/config';
+
+const config = applyConfig([
+ enableOptionalBlocks,
+ loadExampleAddon
+], voltoConfig);
+
+export blocks = {
+ ...config.blocks,
+}
+```
+
+The `applyConfig` helper ensures that each configuration method returns the configuration object, avoiding errors when developing add-ons.
diff --git a/docs/source/addons/public-folder.md b/docs/source/development/add-ons/public-folder.md
similarity index 70%
rename from docs/source/addons/public-folder.md
rename to docs/source/development/add-ons/public-folder.md
index 188a9e155a..aec4ab220c 100644
--- a/docs/source/addons/public-folder.md
+++ b/docs/source/development/add-ons/public-folder.md
@@ -1,10 +1,10 @@
---
myst:
html_meta:
- "description": "How to add static served files from your add-on to your build"
- "property=og:description": "How to add static served files to the build from an add-on"
+ "description": "How to add static files from your add-on to your build"
+ "property=og:description": "How to add static files from your add-on to your build"
"property=og:title": "Add static files from your add-on to your build"
- "keywords": "Volto, Plone, Semantic UI, CSS, Volto theme, add-on, static, assets, files, build"
+ "keywords": "Volto, Plone, Semantic UI, CSS, theme, add-on, static, assets, files, build"
---
# Add static files from your add-on to your build
@@ -22,8 +22,7 @@ It is useful to define static files such as the following:
## Procedure to include static files
-Create a folder named `public` at the root of your add-on, and add the static files to it.
+Create a folder named {file}`public` at the root of your add-on, and add the static files to it.
The build process will copy the files, taking into account all add-ons' defined order.
The build process copies first the static files defined by Volto, then the static files from add-ons as defined by their configuration order.
The last defined file overwrites any previously defined files.
-
diff --git a/docs/source/development/add-ons/test-add-ons-17.md b/docs/source/development/add-ons/test-add-ons-17.md
new file mode 100644
index 0000000000..047c508869
--- /dev/null
+++ b/docs/source/development/add-ons/test-add-ons-17.md
@@ -0,0 +1,104 @@
+---
+myst:
+ html_meta:
+ "description": "How to test add-ons in Volto 17"
+ "property=og:description": "How to test add-ons in Volto 17"
+ "property=og:title": "Test add-ons in Volto 17"
+ "keywords": "Volto, Plone, testing, CI, add-ons"
+---
+
+# Test add-ons in Volto 17
+
+Volto uses {term}`Jest` for unit tests.
+You must configure {file}`package.json` to let Jest know about your aliases and make them available to it to resolve them.
+
+```{code-block} json
+:emphasize-lines: 6
+
+"jest": {
+ "moduleNameMapper": {
+ "@plone/volto/(.*)$": "/node_modules/@plone/volto/src/$1",
+ "@package/(.*)$": "/src/$1",
+ "@plone/some-volto-addon/(.*)$": "/src/addons/@plone/some-volto-addon/src/$1",
+ "my-volto-addon/(.*)$": "/src/addons/my-volto-addon/src/$1",
+ "~/(.*)$": "/src/$1"
+ }
+}
+```
+
+You can use `yarn test src/addons/addon-name` to run tests.
+
+
+## Override Jest configuration
+
+In {term}`CI` or for testing add-ons, it's useful to modify Jest's {file}`package.json` configuration file.
+You can use a {file}`jest.config.js` file, or point the test runner to a file of your choice, using the `RAZZLE_JEST_CONFIG` environment variable.
+
+```shell
+RAZZLE_JEST_CONFIG=my-custom-jest-config.js yarn test
+```
+
+Both configurations are merged in a way that the keys of the configuration provided override the initial {file}`package.json` configuration, either in Volto or in your projects.
+
+
+## Test add-ons in isolation
+
+Testing an add-on in isolation, as you would when you develop a Plone Python backend add-on, can be a bit challenging, since an add-on needs a working project in order to bootstrap itself.
+The latest generator has the boilerplate needed to bootstrap a dockerized environment where you can run any test to your add-on.
+
+
+### Set up the environment
+
+Run the following command once.
+
+```shell
+make dev
+```
+
+
+### Build the containers manually
+
+Run the following commands.
+
+```shell
+make build-backend
+make build-addon
+```
+
+
+### Unit tests
+
+Run the following command.
+
+```shell
+make test
+```
+
+
+### Acceptance tests
+
+Use {term}`Cypress` to run acceptance tests.
+Run the following command once.
+
+```shell
+make install-acceptance
+```
+
+To start the servers, run the following command.
+
+```shell
+make start-test-acceptance-server
+```
+
+You run the frontend in development mode, so you can develop while writing tests.
+Run the following command to run Cypress tests afterward.
+
+```shell
+make test-acceptance
+```
+
+When finished, shut down the backend server.
+
+```shell
+make stop-test-acceptance-server
+```
diff --git a/docs/source/development/add-ons/test-add-ons-18.md b/docs/source/development/add-ons/test-add-ons-18.md
new file mode 100644
index 0000000000..cb55a72ad3
--- /dev/null
+++ b/docs/source/development/add-ons/test-add-ons-18.md
@@ -0,0 +1,63 @@
+---
+myst:
+ html_meta:
+ "description": "Test add-ons in Volto 18"
+ "property=og:description": "Test add-ons in Volto 18"
+ "property=og:title": "Test add-ons in Volto 18"
+ "keywords": "Volto, Plone, testing, test, CI, add-ons"
+---
+
+# Test add-ons in Volto 18
+
+```{warning}
+This guide assumes that you've used {term}`Cookieplone` to create your add-on boilerplate.
+```
+
+Volto uses {term}`Jest` for unit tests.
+You can create unit tests for testing your add-on.
+
+Run the following command.
+
+```shell
+make test
+```
+
+## Override Jest configuration
+
+In {term}`CI` or for testing add-ons, it's useful to modify Jest's {file}`package.json` configuration file.
+You can use the file {file}`jest.config.js` provided by the boilerplate.
+The test command will load it and apply it.
+
+```{warning}
+Do not modify the existing keys in there if you don't know what you are doing, since some of them are required for the tests to run properly in the Volto context.
+```
+
+Both configurations are merged in a way that the keys of the configuration provided override the initial {file}`package.json` configuration, either in Volto or in your projects.
+
+```{note}
+For more background on testing add-ons in Volto 18, see {doc}`../../contributing/testing`, since the developer experience has been unified for both add-ons and Volto core.
+```
+
+### Acceptance tests
+
+Use {term}`Cypress` to run acceptance tests.
+
+To start the backend server, run the following command.
+This will start a Docker container with a vanilla Plone backend.
+
+```shell
+make acceptance-backend-start
+```
+
+To start the frontend acceptance server in development mode, run the following command.
+
+```shell
+make acceptance-frontend-dev-start
+```
+
+You can run the frontend in development mode, so you can develop while writing tests.
+Run the following command to run Cypress tests afterward.
+
+```shell
+make acceptance-test
+```
diff --git a/docs/source/development/add-ons/theme.md b/docs/source/development/add-ons/theme.md
new file mode 100644
index 0000000000..914e3b1295
--- /dev/null
+++ b/docs/source/development/add-ons/theme.md
@@ -0,0 +1,248 @@
+---
+myst:
+ html_meta:
+ "description": "How to create a Volto theme add-on"
+ "property=og:description": "How to create a Volto theme add-on"
+ "property=og:title": "Create a Volto theme add-on"
+ "keywords": "Volto, Plone, Semantic UI, CSS, theme, add-on"
+---
+
+# Create a Volto theme add-on
+
+You can create a Volto theme add-on, keeping it separate from your project files.
+By making your Volto theme add-on pluggable, you can deploy the same theme in different projects.
+You can even create themes that depend on conditions that you inject at build time.
+
+The file {file}`volto.config.js` provides the ability to programmatically declare add-ons and the active theme.
+See {ref}`volto-config-js` for more information.
+For convenience, it can also be set via a `THEME` environment variable.
+
+1. In your {file}`volto.config.js` file at the root of your project, add a `theme` key with the value of your theme's name.
+
+ ```js
+ module.exports = {
+ addons: [],
+ theme: 'volto-my-theme'
+ };
+ ```
+
+ Alternatively, you can add a `theme` key in your {file}`package.json` project.
+
+ ```json
+ "theme": "volto-my-theme"
+ ```
+
+ Or you can set the theme name through the `THEME` environment variable.
+
+ ```shell
+ THEME='volto-my-theme' pnpm start
+ ```
+
+2. Create a directory {file}`src/theme` in your add-on.
+ Inside that directory, create a new file {file}`theme.config`, adding the following content, but replacing `` with your add-on name.
+
+ ```less
+ /*******************************
+ Theme Selection
+ *******************************/
+
+ /* To override a theme for an individual element specify theme name below */
+
+ /* Global */
+ @site : 'pastanaga';
+ @reset : 'pastanaga';
+
+ /* Elements */
+ @button : 'pastanaga';
+ @container : 'pastanaga';
+ @divider : 'pastanaga';
+ @flag : 'pastanaga';
+ @header : 'pastanaga';
+ @icon : 'pastanaga';
+ @image : 'pastanaga';
+ @input : 'pastanaga';
+ @label : 'pastanaga';
+ @list : 'pastanaga';
+ @loader : 'pastanaga';
+ @placeholder : 'pastanaga';
+ @rail : 'pastanaga';
+ @reveal : 'pastanaga';
+ @segment : 'pastanaga';
+ @step : 'pastanaga';
+
+ /* Collections */
+ @breadcrumb : 'pastanaga';
+ @form : 'pastanaga';
+ @grid : 'pastanaga';
+ @menu : 'pastanaga';
+ @message : 'pastanaga';
+ @table : 'pastanaga';
+
+ /* Modules */
+ @accordion : 'pastanaga';
+ @checkbox : 'pastanaga';
+ @dimmer : 'pastanaga';
+ @dropdown : 'pastanaga';
+ @embed : 'pastanaga';
+ @modal : 'pastanaga';
+ @nag : 'pastanaga';
+ @popup : 'pastanaga';
+ @progress : 'pastanaga';
+ @rating : 'pastanaga';
+ @search : 'pastanaga';
+ @shape : 'pastanaga';
+ @sidebar : 'pastanaga';
+ @sticky : 'pastanaga';
+ @tab : 'pastanaga';
+ @transition : 'pastanaga';
+
+ /* Views */
+ @ad : 'pastanaga';
+ @card : 'pastanaga';
+ @comment : 'pastanaga';
+ @feed : 'pastanaga';
+ @item : 'pastanaga';
+ @statistic : 'pastanaga';
+
+ /* Extras */
+ @main : 'pastanaga';
+ @custom : 'pastanaga';
+
+ /*******************************
+ Folders
+ *******************************/
+
+ /* Path to theme packages */
+ @themesFolder : '~volto-themes';
+
+ /* Path to site override folder */
+ @siteFolder : "/theme";
+
+ /*******************************
+ Import Theme
+ *******************************/
+
+ @import (multiple) "~semantic-ui-less/theme.less";
+ @fontPath : "~volto-themes/@{theme}/assets/fonts";
+
+ .loadAddonOverrides() {
+ @import (optional) "@{siteFolder}/@{addon}/@{addontype}s/@{addonelement}.overrides";
+ }
+
+ /* End Config */
+ ```
+
+3. Declare the theme as an add-on by adding its name to the value for the `addons` key in either {file}`volto.config.js` or {file}`package.json` in your project.
+
+4. After starting Volto, the theme should be active.
+ Now you can add overrides to the default theme in {file}`src/theme`, the same as you would in a project.
+
+5. Finally, you can safely delete your project's original {file}`theme` folder, since the one in the add-on will take precedence, and a project can only have one active theme at a time.
+
+
+## Using your own theming escape hatch
+
+Volto theming uses Semantic UI theming capabilities to define and extend a theme for your site.
+However, while maintaining and playing well with the Semantic UI Volto base, you can use a traditional CSS approach using the LESS preprocessor-based `extras` escape hatch.
+
+At the same time, you can either discard or complement the `extras` escape hatch and add your own, by customizing the {file}`theme.js` module in Volto.
+
+```js
+import 'semantic-ui-less/semantic.less';
+import '@plone/volto/../theme/themes/pastanaga/extras/extras.less';
+
+// You can add more entry points for theming
+import '@kitconcept/volto-light-theme/theme/main.scss';
+```
+
+Customizing the base theme is a special use case in Volto.
+To begin, add a {file}`./@root/theme.js` file structure in your {file}`customizations` folder in your add-on or project.
+
+You may want to do this to create a completely new theming experience adapted to your way of doing things that do not match the current Volto theming experience.
+For example, if you want to use another preprocessor in the theme, such as SCSS.
+Or perhaps your client requires the base consist entirely of pre-made components based on another library beside Semantic UI.
+See {ref}`volto-custom-theming-strategy` for an example of a custom theme escape hatch.
+
+While building your own escape hatch for theming, you can use the preprocessor of your choice, while maintaining the "base" Volto theme, but customizing it using the resultant CSS.
+
+You can see an example of such a theme in [Volto Light Theme](https://github.com/kitconcept/volto-light-theme).
+
+
+## Modify a custom theme from another add-on
+
+Sometimes you have a custom theme that you want to reuse through all your projects, but with some differences, maintaining the base.
+Usually, the only option would be to use an add-on that adds more CSS to the base theme, using imports that will load after the theme.
+However, there is a problem with this approach.
+You cannot use existing theme variables, including breakpoints, on these new styles.
+Similarly, it gets somewhat detached from the normal flow of the loaded theme.
+The same applies for add-ons, as they are detached from the current theme.
+You could use a Semantic UI approach for making this work, but then it's bound to Semantic UI.
+
+```{warning}
+This is only possible when using your own escape hatch, and works only with SCSS-based themes, and not with Semantic UI themes, since it enables a couple of entry points that only support SCSS files.
+For an example of how it could be used, see [Volto Light Theme](https://github.com/kitconcept/volto-light-theme).
+```
+
+If your custom escape hatch defines a custom theme using SCSS, you can take advantage of this feature.
+Although not limited to this, it would be possible to extend this feature to add more entry points, using another preprocessor or theming approach.
+
+This feature enables two entry point files, {file}`_variables.scss` and {file}`_main.scss`.
+From your add-on code, you can extend an existing theme by creating a file corresponding to each entry point:
+
+- {file}`./src/theme/_variables.scss`
+- {file}`./src/theme/_main.scss`
+
+
+### Variables
+
+You can use the entry point `addonsThemeCustomizationsVariables` to modify the original variables of the currently loaded theme by adding the entry point before the theme variable definitions.
+In the theme, it should be imported as shown below.
+
+```{code-block} scss
+:emphasize-lines: 2
+
+@import 'addonsThemeCustomizationsVariables';
+@import 'variables';
+@import 'typography';
+@import 'utils';
+@import 'layout';
+```
+
+````{warning}
+Following SCSS best practices, your theme variables should be "overridable" using the `!default` flag.
+This assigns a value to a variable _only_ if that variable isn't defined or its value is [`null`](https://sass-lang.com/documentation/values/null).
+Otherwise, the existing value will be used.
+
+```{seealso}
+https://sass-lang.com/documentation/variables#default-values
+```
+````
+
+Volto will not only load your add-on entry point files, but it will also detect all the add-ons that have these entry point files, and import them grouped under a single file.
+It will also automatically add an `addonsThemeCustomizationsVariables` alias that you can reference from the theme as shown above.
+
+
+### Main
+
+You can use the entry point `addonsThemeCustomizationsMain` to add your own style definitions, complementing those in the theme.
+You should add it after all the CSS of your theme:
+
+```{code-block} scss
+:emphasize-lines: 6
+
+@import 'blocks/search';
+@import 'blocks/listing';
+
+@import 'temp';
+
+@import 'addonsThemeCustomizationsMain';
+
+/* No CSS beyond this point */
+```
+
+Volto will also detect all the add-ons that have these entry point files, and import them grouped under a single file.
+It will also automatically add an `addonsThemeCustomizationsMain` alias that you can reference from the theme as shown above.
+
+```{note}
+It will only work in combination with the theme declaration in {file}`volto.config.js` or in {file}`package.json`.
+```
diff --git a/docs/source/development/add-ons/troubleshoot-transpilation.md b/docs/source/development/add-ons/troubleshoot-transpilation.md
new file mode 100644
index 0000000000..2cf7e761b6
--- /dev/null
+++ b/docs/source/development/add-ons/troubleshoot-transpilation.md
@@ -0,0 +1,78 @@
+---
+myst:
+ html_meta:
+ "description": "Troubleshoot untranspiled add-on dependencies"
+ "property=og:description": "Troubleshoot untranspiled add-on dependencies"
+ "property=og:title": "Troubleshoot untranspiled add-on dependencies"
+ "keywords": "Volto, add-on, extensions, frontend, Plone, configuration, troubleshoot"
+---
+
+# Troubleshoot untranspiled add-on dependencies
+
+```{note}
+In Volto 18 and later, Babel improved support for ES specifications, such as for the null coalescence operator.
+However the following procedure can be useful in other scenarios.
+```
+
+When using external add-ons in your project, sometimes you will run into add-ons that are not securely transpiled or haven't been transpiled at all.
+In that case, you might see an error such as the following:
+
+```console
+Module parse failed: Unexpected token (10:41) in @react-leaflet/core/esm/path.js
+...
+const options = props.pathOptions ?? {};
+...
+```
+
+Babel automatically transpiles the code in your add-on, but {file}`node_modules` are excluded from this process.
+You need to include the add-on path in the list of modules to be transpiled.
+To do so, customize the webpack configuration in the {file}`razzle.config.js` file in your add-on.
+For example, suppose that you want to use react-leaflet, which has a known transpilation issue.
+
+```js
+const path = require('path');
+const makeLoaderFinder = require('razzle-dev-utils/makeLoaderFinder');
+
+const babelLoaderFinder = makeLoaderFinder('babel-loader');
+
+const jsConfig = require('./jsconfig').compilerOptions;
+
+const pathsConfig = jsConfig.paths;
+let voltoPath = './node_modules/@plone/volto';
+Object.keys(pathsConfig).forEach((pkg) => {
+ if (pkg === '@plone/volto') {
+ voltoPath = `./${jsConfig.baseUrl}/${pathsConfig[pkg][0]}`;
+ }
+});
+
+const { modifyWebpackConfig, plugins } = require(`${voltoPath}/razzle.config`);
+
+const customModifyWebpackConfig = ({ env, webpackConfig, webpackObject, options }) => {
+ const config = modifyWebpackConfig({
+ env,
+ webpackConfig,
+ webpackObject,
+ options,
+ });
+ const babelLoader = config.module.rules.find(babelLoaderFinder);
+ const { include } = babelLoader;
+ const corePath = path.join(
+ path.dirname(require.resolve('@react-leaflet/core')),
+ '..',
+ );
+ const esmPath = path.join(
+ path.dirname(require.resolve('react-leaflet')),
+ '..',
+ );
+
+ include.push(corePath);
+ include.push(esmPath);
+ return config;
+};
+
+module.exports = { modifyWebpackConfig: customModifyWebpackConfig, plugins };
+```
+
+First you need some setup to get the webpack configuration from Volto's configuration.
+Once you have that, you need to resolve the path to the desired add-ons, and push it into the Babel loader include list.
+After this, the add-ons will load correctly.
diff --git a/docs/source/development/creating-project.md b/docs/source/development/creating-project.md
index 5052533f81..f5ef93fefc 100644
--- a/docs/source/development/creating-project.md
+++ b/docs/source/development/creating-project.md
@@ -9,60 +9,15 @@ myst:
# Create a Volto project without a backend
-This document shows how to create a Volto project with the frontend only when you have your own existing backend, such as Plone {term}`Classic UI`, {term}`Nick`, or {term}`Guillotina`.
+```{versionadded} Volto 18.0.0-alpha.43
+```
+
+[Cookieplone](https://github.com/plone/cookieplone) is now the recommended way to develop Volto projects, using it as a boilerplate generator.
+Even if you don't need the backend, you can create a Plone project, then use only the {file}`frontend` folder for your purposes.
+As a bonus, it will contain the means for deploying your project.
```{seealso}
To create a full Plone project with both frontend and backend, see {doc}`plone:install/create-project` instead.
To contribute to Volto, see {doc}`../contributing/developing-core`.
```
-
-For using Volto for a project—in other words, use Volto as a library—you should use Volto's project generator `@plone/generator-volto`.
-It's a boilerplate project generator based on Yeoman that will provide you with the basic files and folder structure to bootstrap a Volto site.
-In addition to bootstrapping stand-alone Volto projects, it can also bootstrap Volto add-ons.
-
-1. Open a terminal and execute:
-
- ```shell
- npm install -g yo @plone/generator-volto
- # Install the latest and stable release of Volto with the following command
- yo @plone/volto
- # or you can install the "canary" release, including any alpha release
- yo @plone/volto --canary
- # or you can install any specific released version
- yo @plone/volto --volto=15.0.0
- # you can even pass a GitHub repo and specific branch
- yo @plone/volto --volto=plone/volto#16.0.0
- # you can bootstrap with add-ons
- yo @plone/volto --addon=volto-form-block
- ```
-
-2. Answer the questions when prompted, and provide the name of the new app (folder) to be created.
- For the sake of this documentation, use `myvoltoproject` as the project name.
-
- ````{note}
- You can run the generator with parameters to tailor your requirements.
-
- ```shell
- yo @plone/volto --help
- ```
-
- ```{seealso}
- [`@plone/generator-volto` `README.md`](https://github.com/plone/volto/blob/main/packages/generator-volto/README.md).
- ```
- ````
-
-3. Change your working directory to the newly created folder `myvoltoproject` (or whatever name you entered).
-
- ```shell
- cd myvoltoproject
- ```
-
-4. `@plone/generator-volto` installed the dependencies for you.
- Start the project.
-
- ```shell
- yarn start
- ```
-
- This starts the development server, which compiles the project code, and when done, it serves the app at http://localhost:3000.
diff --git a/docs/source/development/environment-variables.md b/docs/source/development/environment-variables.md
index 6b8b79efc3..78dc734177 100644
--- a/docs/source/development/environment-variables.md
+++ b/docs/source/development/environment-variables.md
@@ -33,7 +33,7 @@ docker run -it --rm -p 55001:8080 -e SITE=Plone -e ADDONS='plone.restapi==8.20.0
```
```shell
-RAZZLE_DEV_PROXY_API_PATH='http://localhost:55001/Plone' yarn start
+RAZZLE_DEV_PROXY_API_PATH='http://localhost:55001/Plone' pnpm start
```
Let's say that you do have it in a customized site id: http://localhost:55001/myplonesite
@@ -43,7 +43,7 @@ docker run -it --rm -p 55001:8080 -e SITE=myplonesite -e ADDONS='plone.restapi==
```
```shell
-RAZZLE_DEV_PROXY_API_PATH='http://localhost:55001/myplonesite' yarn start
+RAZZLE_DEV_PROXY_API_PATH='http://localhost:55001/myplonesite' pnpm start
```
### Debug an external site (provided you have access to it)
@@ -55,7 +55,7 @@ This is an advanced feature, and needs understanding of what you are doing and w
Let's say you want to debug a deployed site in production, but the build does not allow you to look deeper into the tracebacks. You could bootstrap a frontend in your machine, and point it to the production server, combining environment variables like:
```
-RAZZLE_DEV_PROXY_API_PATH=https://2021.ploneconf.org RAZZLE_PROXY_REWRITE_TARGET=https://2021.ploneconf.org/++api++ yarn start
+RAZZLE_DEV_PROXY_API_PATH=https://2021.ploneconf.org RAZZLE_PROXY_REWRITE_TARGET=https://2021.ploneconf.org/++api++ pnpm start
```
This has the drawback that could be that the proxy does not work well with the proxied SSL connection.
@@ -63,7 +63,7 @@ This has the drawback that could be that the proxy does not work well with the p
If you have access (via tunnel) to the port of the deployed backend is even more easier:
```
-RAZZLE_DEV_PROXY_API_PATH=http://2021.ploneconf.org:8080/Plone yarn start
+RAZZLE_DEV_PROXY_API_PATH=http://2021.ploneconf.org:8080/Plone pnpm start
```
This will use the internal proxy to access the backend, bypassing CORS.
diff --git a/docs/source/development/how-to-restrict-blocks.md b/docs/source/development/how-to-restrict-blocks.md
index 15fee224ed..62931acdf8 100644
--- a/docs/source/development/how-to-restrict-blocks.md
+++ b/docs/source/development/how-to-restrict-blocks.md
@@ -22,12 +22,25 @@ The function has this signature:
block: BlockConfigBase;
navRoot: Content;
contentType: string;
+ user: Object
}) => boolean;
}
```
-Where `properties` is the current object data and `block` is the block being evaluated in `BlockChooser`.
-`navRoot` is the nearest navigation root object and `contentType` is the current content type.
+`properties`
+: The current object data.
+
+`block`
+: The block being evaluated in `BlockChooser`.
+
+`navRoot`
+: The nearest navigation root object.
+
+`contentType`
+: The current content type.
+
+`user`
+: An object that represents the currently authenticated user.
In the following configuration example, you can restrict a block so that it cannot be added unless the content type is `News Item` or the content item is in a specific path in the content tree (`/folder`):
diff --git a/docs/source/development/i18n.md b/docs/source/development/i18n.md
index 15e9c32e8c..7fb232a9b6 100644
--- a/docs/source/development/i18n.md
+++ b/docs/source/development/i18n.md
@@ -11,13 +11,13 @@ myst:
{term}`Internationalization` (i18n) is the process of creating user interfaces which are suitable for different languages and cultural contexts.
-This chapter describes the most common use cases for internationalization when developing your {doc}`../addons/index` or contributing to the Volto core itself.
+This chapter describes the most common use cases for internationalization when developing your {doc}`../development/add-ons/index` or contributing to the Volto core itself.
## Process and file structure overview
Volto uses the library [react-intl](https://www.npmjs.com/package/react-intl) to provide translations for any potential language.
-Anything in the [official documentation of react-intl](https://formatjs.io/docs/react-intl/) also applies to Volto.
+Anything in the [official documentation of react-intl](https://formatjs.github.io/docs/react-intl/) also applies to Volto.
The workflow for creating *new* translatable text strings is as follows:
@@ -79,13 +79,13 @@ function HelloWorld(props) {
The identifier `hello_world` is then commonly used across all the translations.
There are more features available, such as using placeholders.
-See the documentation for all features in the [`FormattedMessage` component](https://formatjs.io/docs/react-intl/components#formattedmessage).
+See the documentation for all features in the [`FormattedMessage` component](https://formatjs.github.io/docs/react-intl/components#formattedmessage).
### Translate attributes
As `FormatMessage` is only suitable for creating text within HTML elements, it cannot be used for translating individual attributes.
-But with the method [`formatMessage`](https://formatjs.io/docs/react-intl/api/#formatmessage), there is another way to translate primitive strings.
+But with the method [`formatMessage`](https://formatjs.github.io/docs/react-intl/api/#formatmessage), there is another way to translate primitive strings.
This approach can be best explained with an example.
Assume you have a component called `TeaserImage` which contains an image that has, for accessibility reasons, the `alt` attribute.
@@ -97,7 +97,7 @@ To translate the `alt` attribute, you have to do the following steps:
import { defineMessages, injectIntl, intlShape } from 'react-intl';
```
-2. Define a message (or more) via [`defineMessages`](https://formatjs.io/docs/react-intl/api/#definemessagesdefinemessage):
+2. Define a message (or more) via [`defineMessages`](https://formatjs.github.io/docs/react-intl/api/#definemessagesdefinemessage):
```js
const messages = defineMessages({
@@ -145,7 +145,7 @@ Volto provides an i18n extraction script to get all translatable strings from yo
You can invoke this script with the following command.
```sh
-yarn i18n
+pnpm i18n
```
This will generate the following output:
@@ -181,7 +181,7 @@ defineMessages({
});
```
-Then run `yarn i18n`.
+Then run `pnpm i18n`.
You will find the translation ready to override in your `locales` directory, such as `locales/de/LC_MESSAGES/volto.po`.
```text
@@ -190,7 +190,7 @@ msgid "Back"
msgstr "My overridden translation"
```
-After you set the override, then run `yarn i18n` again to create the `de.json` translation files.
+After you set the override, then run `pnpm i18n` again to create the `de.json` translation files.
Restart Volto to see the changes applied.
@@ -204,10 +204,10 @@ If this was not the case, then all the translations in the customized components
You can add or override translated messages in your customizations by following the steps described in {ref}`override-i18n-messages`.
-## Contribute translations for an unsupported language
+(contribute-translations-for-an-unsupported-language-label)=
-The Volto project welcomes all speakers from the world to include any language, which is not supported yet.
+## Contribute translations for an unsupported language
-If your language's `.po` file is not available in Volto, [open an issue in GitHub](https://github.com/plone/volto/issues).
-Either you can create the `.po` file or we can do it for you.
-After that, you can start contributing your translations.
+```{seealso}
+{ref}`contributing-plone-core-translations-translate-volto-label`
+```
diff --git a/docs/source/development/ie11compat.md b/docs/source/development/ie11compat.md
deleted file mode 100644
index 16735ed7ae..0000000000
--- a/docs/source/development/ie11compat.md
+++ /dev/null
@@ -1,64 +0,0 @@
----
-myst:
- html_meta:
- "description": "Legacy browser support with polyfills, babel-env, and pre- and post-transpiling"
- "property=og:description": "Legacy browser support with polyfills, babel-env, and pre- and post-transpiling"
- "property=og:title": "Legacy Browser Support (IE11 compatibility)"
- "keywords": "Volto, Plone, frontend, React, IE11 compatibility, polyfills, legacy browser support"
----
-
-# Legacy Browser Support (IE11 compatibility)
-
-There are some caveats if we still want to target IE11 as supported browser.
-
-```{important}
-This documentation is orientative. Volto does NOT support legacy or vendor deprecated browsers (as in IE11).
-```
-
-## Version pinning
-
-These package versions should be pinned to this especific versions, unless
-their code or dependencies have some es6 only compatible, because their
-maintainers mainly target the Node.js world.
-
-* "query-string": "4.1.0"
-* "superagent": "3.8.2"
-
-## Polyfills
-
-Then in the project that should target it, these changes are required:
-
-add as a dependency `@babel/polyfill`.
-
- yarn add @babel/polyfill
-
-and in `src/client.jsx`:
-
-```js
-import '@babel/polyfill';
-```
-
-```{seealso}
-See https://babeljs.io/docs/babel-polyfill for more updated information
-```
-
-## babel-env
-
-Razzle supports `@babel/preset-env`, that supports including `browserlist` in
-`package.json`. So you can add this to `package.json`:
-
-```json
- "browserslist": [
- "last 2 version",
- "IE 11"
- ],
-```
-
-This supports the query specific DSL for `browserlist` targeting the browsers
-that you need to add.
-
-## Pre-transpiling
-
-Some packages in `node_modules` are ES6 only, for some older browsers, you might want to add a pre (or post) transpiling. There's a script `pre-build-transpiling.js` (Volto root folder) that might help you with it. Also this command line might help:
-
- ./node_modules/.bin/babel --presets="@babel/env" XXX --out-dir XXX
diff --git a/docs/source/development/index.md b/docs/source/development/index.md
index 90c28191f8..b0e1d64b1a 100644
--- a/docs/source/development/index.md
+++ b/docs/source/development/index.md
@@ -21,6 +21,7 @@ Or jump in to any topic listed below.
overview
creating-project
+add-ons/index
folder-structure
environment-variables
customizing-components
@@ -36,5 +37,4 @@ pluggables
widget
how-to-restrict-blocks
color-picker-widget
-ie11compat
```
diff --git a/docs/source/development/lazyload.md b/docs/source/development/lazyload.md
index 668a7190b8..7a04d373cf 100644
--- a/docs/source/development/lazyload.md
+++ b/docs/source/development/lazyload.md
@@ -36,7 +36,7 @@ You can find the complete `@loadable/component` documentation here: https://load
You can check the code splitting state by using the included bundle analyzer:
```shell
-yarn analyze
+pnpm --filter=volto analyze
```
A browser will open with the bundle inspector.
diff --git a/docs/source/index.md b/docs/source/index.md
index ef65b2294f..e96dcc0e1d 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -1,18 +1,18 @@
---
myst:
html_meta:
- "description": "Volto is the React-based frontend for the Plone CMS. It is the default UI for the Plone 6 release."
- "property=og:description": "Volto is the React-based frontend for the Plone CMS. It is the default UI for the Plone 6 release."
- "property=og:title": "Frontend"
- "keywords": "Volto, Plone, frontend, React"
+ "description": "The Volto user interface (UI) is a React-based frontend for the Plone CMS. It is the default user interface starting with the release of Plone 6."
+ "property=og:description": "The Volto user interface (UI) is a React-based frontend for the Plone CMS. It is the default user interface starting with the release of Plone 6."
+ "property=og:title": "Volto UI"
+ "keywords": "Volto, Plone, frontend, user interface, React"
---
(volto-index-label)=
-# Frontend
+# Volto UI
-Volto is a React-based frontend for the [Plone CMS](https://plone.org).
-It is the default frontend starting with the Plone 6 release.
+The Volto user interface (UI) is a React-based frontend for the [Plone content management system](https://plone.org).
+It is the default user interface starting with the release of Plone 6.
Volto provides an attractive proposition: integration with the modern frontend development world, access to the huge ecosystem of React libraries and add-ons, combined with the ability to use the mature Plone CMS backend as a development platform.
@@ -38,7 +38,7 @@ An integrator is someone who uses Volto to build a project.
### Users
-A user of Volto is someone who edits content in a Plone content management system with Volto as the frontend.
+A user of Volto is someone who edits content in a Plone content management system with Volto as the user interface.
- {doc}`user-manual/index` provides information about how to manage content in a Plone site.
@@ -71,6 +71,7 @@ tutorials/index
contributing/index
release-notes/index
release-management-notes/index
+conceptual-guides/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/release-notes/index.md b/docs/source/release-notes/index.md
index 91db671d96..e5524fc087 100644
--- a/docs/source/release-notes/index.md
+++ b/docs/source/release-notes/index.md
@@ -17,6 +17,807 @@ myst:
+## 18.2.3 (2024-12-09)
+
+### Bugfix
+
+- Fixed circular import error in dev with HMR in core Views and Widgets shadow customizations. @sneridagh [#6526](https://github.com/plone/volto/issues/6526)
+
+## 18.2.2 (2024-12-09)
+
+### Bugfix
+
+- Fixed circular import error in dev with HMR in core Blocks shadow customizations. @sneridagh [#6525](https://github.com/plone/volto/issues/6525)
+
+## 18.2.1 (2024-12-09)
+
+### Bugfix
+
+- Fixes ICS download in non-public event content. @sneridagh [#6515](https://github.com/plone/volto/issues/6515)
+- Fixed circular import error in dev with HMR in `App` component when imported in the main default config. @sneridagh [#6524](https://github.com/plone/volto/issues/6524)
+
+### Internal
+
+- Fix extension in files containing JSX. @sneridagh [#6520](https://github.com/plone/volto/issues/6520)
+
+## 18.2.0 (2024-12-08)
+
+### Internal
+
+- Removed all imports from barrel files for components, actions, helpers, hooks and lodash. @pnicolli [#6509](https://github.com/plone/volto/issues/6509)
+
+## 18.1.2 (2024-12-05)
+
+### Bugfix
+
+- Remove `not ie 11` from browserslist configuration, because it is now included in `not dead`. @stevepiercy [#6501](https://github.com/plone/volto/issues/6501)
+- Fix warnings related to `snapshot.managedPaths` on startup. @davisagli [#6511](https://github.com/plone/volto/issues/6511)
+
+### Internal
+
+- Add Accessibility acceptance tests for content types. @ana-oprea @ichim-david [#6339](https://github.com/plone/volto/issues/6339)
+- Fixed the `README.md` for Volto core when releasing to npm registry: copy it from the root of the monorepo on release. @sneridagh [#6510](https://github.com/plone/volto/issues/6510)
+
+### Documentation
+
+- Add new Volto trainings to tutorials. @stevepiercy [#6499](https://github.com/plone/volto/issues/6499)
+- `html_use_opensearch` value must not have a trailing slash. Clean up comments. @stevepiercy [#6502](https://github.com/plone/volto/issues/6502)
+
+## 18.1.1 (2024-11-21)
+
+### Bugfix
+
+- Do not break toolbar if layout id is not registered in layoutViewsNamesMapping. @cekk [#6485](https://github.com/plone/volto/issues/6485)
+- Replace _all_ spaces with `-` in `BodyClass` classes, instead of with `-` or `` depending on the content type or section. @giuliaghisini [#6487](https://github.com/plone/volto/issues/6487)
+
+### Internal
+
+- Update instructions to install `pipx` in `RELEASING.md`. @stevepiercy [#6496](https://github.com/plone/volto/issues/6496)
+
+### Documentation
+
+- More privacy concerning youtube links and fixing link check warnings for youtube playlist links. @stevepiercy @ksuess [#4203](https://github.com/plone/volto/issues/4203)
+- Remove conflicting `searchtools.js` file from documentation to allow default Sphinx search in main Plone documentation. @stevepiercy [#6482](https://github.com/plone/volto/issues/6482)
+- Add support for sphinxcontrib-youtube. @stevepiercy [#6486](https://github.com/plone/volto/issues/6486)
+- Refactor documentation includes to align with main documentation pattern. @stevepiercy [#6495](https://github.com/plone/volto/issues/6495)
+
+## 18.1.0 (2024-11-11)
+
+### Feature
+
+- Update Dutch translations. @fredvd [#6476](https://github.com/plone/volto/issues/6476)
+
+### Bugfix
+
+- URL Management control panel: Show errors from a failed CSV upload. @davisagli [#6473](https://github.com/plone/volto/issues/6473)
+- Added missing style Helmet serialization in the HTML component to make it work in SSR. @sneridagh
+ Fix deprecation notice for the usage of apple-mobile-web-app-capable. [#6480](https://github.com/plone/volto/issues/6480)
+
+### Internal
+
+- Added React Router 7 experimental PoC. @sneridagh [#6472](https://github.com/plone/volto/issues/6472)
+
+### Documentation
+
+- Overhaul and update of the add-ons section in documentation. @sneridagh [#6397](https://github.com/plone/volto/issues/6397)
+
+## 18.0.3 (2024-11-05)
+
+### Bugfix
+
+- Fixed image generation because of a bug in the docker build with registry typings. @sneridagh [#6471](https://github.com/plone/volto/issues/6471)
+
+## 18.0.2 (2024-11-05)
+
+### Internal
+
+- Fix missing export src in @plone/components Also improve packaging in packages. @sneridagh [#6470](https://github.com/plone/volto/issues/6470)
+
+## 18.0.1 (2024-11-05)
+
+### Bugfix
+
+- Fix ERR_REQUIRE from ESM module requiring CJS module in `@plone/registry` fix tests. @sneridagh [#6458](https://github.com/plone/volto/issues/6458)
+
+### Internal
+
+- Improve exports in @plone/registry, adapt the Jest test setup. @sneridagh [#6461](https://github.com/plone/volto/issues/6461)
+- Removed `parcel` completely from core. @sneridagh [#6469](https://github.com/plone/volto/issues/6469)
+
+### Documentation
+
+- Fix display of nvm version when installing it for Contributing to Volto. @stevepiercy [#6460](https://github.com/plone/volto/issues/6460)
+
+## 18.0.0 (2024-10-31)
+
+(Summary)
+
+### Breaking
+
+- Dropped support for Node.js 18, since Volto only supports the last two LTS versions. @sneridagh
+ Volto might continue to work on it, but it will be no longer tested in CI. [#6371](https://github.com/plone/volto/issues/6371)
+- Added `react/jsx-key` rule for ESlint to detect missing key property in iterators. @sneridagh [#6387](https://github.com/plone/volto/issues/6387)
+- Move `Tags` component to the slot `belowContent`. @wesleybl [#6269](https://github.com/plone/volto/issues/6269)
+- Add foundations for extensible validation in forms. @sneridagh
+ - `packages/volto/src/helpers/FormValidation/FormValidation.jsx` has been heavily refactored.
+ - If you shadowed this component in your project or add-on, you should review it and update it accordingly. [#6161](https://github.com/plone/volto/issues/6161)
+- Remove `react-share` library and `SocialSharing` component @sneridagh [#6162](https://github.com/plone/volto/issues/6162)
+- In the widget mapping, moved the `SchemaWidget` registration from the `id` object to the `widget` object, and added the `widget` key to the `schema` object in the `properties` object for `makeSchemaList`. @sneridagh [#6189](https://github.com/plone/volto/issues/6189)
+- Fixed image widget position and look and feel in sidebar. @ichim-david
+ - Updated the markup of the widget in the sidebar to render the widget on a single column bellow the label.
+ - AddLink Pop-up of the widget is now rendered inside `toolbar-inner` instead of the document body, this fixes the positioning of the toolbar when scrolling. [#6159](https://github.com/plone/volto/issues/6159)
+- Added `ImageWidget` component with upload, drop, external, and inline capabilities. @sneridagh @ichim-david @dobri1408
+ - The user experience of the image upload has changed.
+ - The input field is now a row of buttons.
+ - The input field's placeholder text was moved above the buttons.
+ - Together these changes improve usability both on small screens and in small containers, such as when the widget is in grid block elements.
+ - If you shadow the image block edit component, make sure it continues to work as you expect, or update it to use the new image upload widget component. [#5607](https://github.com/plone/volto/issues/5607)
+- Improve container detection, `config.settings.containerBlockTypes` is no longer needed @sneridagh [#6099](https://github.com/plone/volto/issues/6099)
+- Fix JavaScript events association on error pages. Also remove settings `config.settings.serverConfig.extractScripts.errorPages`. Now scripts are added to error pages, regardless of whether we are in production mode or not. @wesleybl [#6048](https://github.com/plone/volto/issues/6048)
+- Breaking from the original slots implementation:
+ - Now `config.getSlots` in the configuration registry takes the argument `location` instead of `pathname`.
+ - This allows to have more expressive conditions, and fulfill the use case of the `Add` form.
+ @sneridagh [#6063](https://github.com/plone/volto/issues/6063)
+- Use `id` instead of `title` for the fieldset's generated value when rendering a `Field` component in a form. @sneridagh [#5921](https://github.com/plone/volto/issues/5921)
+- Moved `devDependencies` and `dependencies` to where they belong. @sneridagh [#5879](https://github.com/plone/volto/issues/5879)
+- Remove legacy `text`, `table` and `hero` blocks based in `draftJS` @sneridagh [#5846](https://github.com/plone/volto/issues/5846)
+- Upgrade Volto core to use React 18.2.0 @sneridagh [#3221](https://github.com/plone/volto/issues/3221)
+- Improved accessibility of logo component. @Molochem [#5776](https://github.com/plone/volto/issues/5776)
+- Remove the isDisabled from all fields in the left side form of the babel view, make them read only instead @sneridagh [#5762](https://github.com/plone/volto/issues/5762)
+- Experimental feature flag for new add block button enabled by default. @sneridagh [#4947](https://github.com/plone/volto/issues/4947)
+- Moved add-on registry to its own package. @sneridagh [#4949](https://github.com/plone/volto/issues/4949)
+- Sidebar formtabs item links are now real buttons with `ui button` class for proper keyboard and screen reader support @ichim-david [#5294](https://github.com/plone/volto/issues/5294)
+
+### Feature
+
+- Update Romanian translation. @avoinea [#6454](https://github.com/plone/volto/issues/6454)
+- Update Brazilian Portuguese translation [@ericof]
+- Enhance the `buildStyleObjectFromData` helper. @sneridagh [#6445](https://github.com/plone/volto/issues/6445)
+- URL Management control panel: add a filter for redirects created after a given date.
+ **Note:** This requires `Products.CMFPlone` 6.0.14 and `plone.restapi` 9.8.0 or later.
+ @davisagli [#6414](https://github.com/plone/volto/issues/6414)
+- URL Management control panel: Move the form for adding a new redirect into a modal. @davisagli [#6421](https://github.com/plone/volto/issues/6421)
+- URL Management control panel: Add feature to bulk upload redirects in a CSV file.
+ **Note:** This requires `plone.restapi` 9.8.0 or later.
+ @davisagli [#6421](https://github.com/plone/volto/issues/6421)
+- Introducing "client transforms for Redux reducers". @sneridagh [#6422](https://github.com/plone/volto/issues/6422)
+- URL Management control panel: Add a way to edit existing aliases. @davisagli [#6425](https://github.com/plone/volto/issues/6425)
+- Added `config.settings.nonContentRoutesPublic` to avoid `isCmsUi` issues in these public routes. @giuliaghisini [#6173](https://github.com/plone/volto/issues/6173)
+- Add language independent field icon. @iRohitSingh [#6297](https://github.com/plone/volto/issues/6297)
+- Improve DiffField.jsx to render only the blocks on the page with better support for displaying HTML elements such as images. @dobri1408 [#6309](https://github.com/plone/volto/issues/6309)
+- Updated Italian Italian translations. @gianniftp [#6342](https://github.com/plone/volto/issues/6342)
+- Update Brazilian Portuguese translations. @ericof
+- Refactor AlbumView from class to functional component. @Tishasoumya-02 [#4077](https://github.com/plone/volto/issues/4077)
+- Add setting `unwantedControlPanelsFields` and use it in the function `filterControlPanelsSchema`. @wesleybl [#4819](https://github.com/plone/volto/issues/4819)
+- Refactor the `Register` component from class-based to functional. @Tishasoumya-02 [#4861](https://github.com/plone/volto/issues/4861)
+- Refactor Toolbar/More component from class to functional component. @Tishasoumya-02 [#4955](https://github.com/plone/volto/issues/4955)
+- Refactor Blocks/LeadImage/Edit component from class to functional component. @Tishasoumya-02 [#4959](https://github.com/plone/volto/issues/4959)
+- Refactor Blocks/Video/Edit component from class to functional component. @Tishasoumya-02 [#4960](https://github.com/plone/volto/issues/4960)
+- Refactor ControlPanel/Users/Aliases component , replacing class component lifecycle methods with functional hooks. @Tishasoumya-02 [#4985](https://github.com/plone/volto/issues/4985)
+- Refactor Controlpanel databaseInformation from class component to functional component. @Tishasoumya-02 [#4986](https://github.com/plone/volto/issues/4986)
+- Refactor ControlPanels/Groups RenderGroups from class components to functional component. @Tishasoumya-02 [#4993](https://github.com/plone/volto/issues/4993)
+- Refactor AddonsControlpanel from class to functional components. @Tishasoumya [#4995](https://github.com/plone/volto/issues/4995)
+- Refactor Preference/Change Password from class to functional component. @Tishasoumya-02 [#5044](https://github.com/plone/volto/issues/5044)
+- Refactor Preference/PersonalPreferences from class to functional component. @Tishasoumya-02 [#5045](https://github.com/plone/volto/issues/5045)
+- Refactor Preference/PersonalInformation from Class to Functional component. @Tishasoumya-02 [#5046](https://github.com/plone/volto/issues/5046)
+- Refactor Contents/ContentsUploadModal & Storybook-@Tishasoumya-02 [#5047](https://github.com/plone/volto/issues/5047)
+- Storybook test for ContentBreadcrumbs component. @Tishasoumya [#5048](https://github.com/plone/volto/issues/5048)
+- Storybook Actions component test. @Tishasoumya-02 [#5049](https://github.com/plone/volto/issues/5049)
+- Refactor Display from class to functional component. @Tishasoumya-02 [#5066](https://github.com/plone/volto/issues/5066)
+- Refactor `ReferenceWidget` from class-based to functional component. @Tishasoumya [#5093](https://github.com/plone/volto/issues/5093)
+- Refactor the table of contents block component from a class component to a functional component. @Prince0906 [#6167](https://github.com/plone/volto/issues/6167)
+- Improved URL regex to allow non-public or intranet URLs, such as `https://intranet/` or `file://server/share`. @mamico [#6186](https://github.com/plone/volto/issues/6186)
+- Refactor the `DatetimeWidget` component from a class component to a functional component. @Raman-Luhach [#6213](https://github.com/plone/volto/issues/6213)
+- The schema for the `ContentsPropertiesModal` can be enhanced using the `contentPropertiesSchemaEnhancer` setting.
+ Also, the properties form is now prepopulated with values if all selected items share the same value. @davisagli [#6248](https://github.com/plone/volto/issues/6248)
+- Pass the `user`, `navRoot` and `contentType` objects to the `restricted` function of the block settings. @wesleybl [#6264](https://github.com/plone/volto/issues/6264)
+- (feat): Add loading visual and succes message in controlpanel when deleting users and groups @dobri1408 [#6127](https://github.com/plone/volto/issues/6127)
+- Add option for live teasers @steffenri @pbauer @nileshgulia1 @tlotze [#6023](https://github.com/plone/volto/issues/6023)
+- Improve shadowing by including the support for js->jsx extensions in old shadows. This allow support for upcoming renaming of files that should be jsx and are js. @sneridagh [#6113](https://github.com/plone/volto/issues/6113)
+- Added blocks layout navigator @robgietema @sneridagh [#5642](https://github.com/plone/volto/issues/5642)
+- Add support for reading the add-ons `tsconfig.json` paths and add them to the build resolve aliases @sneridagh [#6096](https://github.com/plone/volto/issues/6096)
+- Added object browser icon view @robgietema [#5279](https://github.com/plone/volto/issues/5279)
+- Refactor TextWidget. @Tishasoumya-02 [#6020](https://github.com/plone/volto/issues/6020)
+- Refactor IdWidget -@Tishasoumya-02 [#6027](https://github.com/plone/volto/issues/6027)
+- The `ContentTypeCondition` now supports the `Add` form, and detects when you create a content type that is set in the condition. @sneridagh
+ Added a new `BodyClass` helper while adding a new content type of the form `is-adding-contenttype-mycontenttype`. @sneridagh [#6063](https://github.com/plone/volto/issues/6063)
+- Add support for configurable `public` directory defined per add-on. @sneridagh [#6072](https://github.com/plone/volto/issues/6072)
+- Removes navigation settings that are not used by Volto. @wesleybl [#5961](https://github.com/plone/volto/issues/5961)
+- Add Hindi translation in Volto. @iFlameing [#6015](https://github.com/plone/volto/issues/6015)
+- Add parameters to `ContentsUploadModal` to be reusable in different scenarios. @erral [#5881](https://github.com/plone/volto/issues/5881)
+- Print error message from request in toast, if `clipboardRequest` return an error. @cekk [#5932](https://github.com/plone/volto/issues/5932)
+- Add id attribute to discussions container and individual comments [@ericof] [#5904](https://github.com/plone/volto/issues/5904)
+- Improve the usage of `RAZZLE_JEST_CONFIG`. @sneridagh [#5901](https://github.com/plone/volto/issues/5901)
+- Match props passed to the BlockView if reused from the BlockEdit @sneridagh [#5876](https://github.com/plone/volto/issues/5876)
+- Added download link to filename in file widget @sabrina-bongiovanni [#5880](https://github.com/plone/volto/issues/5880)
+- Do not display options for Site Administrator to create, modify, or delete Manager users. @wesleybl [#5244](https://github.com/plone/volto/issues/5244)
+- Support for slots @sneridagh [#5775](https://github.com/plone/volto/issues/5775)
+- Add accordion to metadata form. @robgietema [#5760](https://github.com/plone/volto/issues/5760)
+- Added the `ignore` property to allow exceptions to rules that are applied to all routes. @dobri1408 [#5621](https://github.com/plone/volto/issues/5621)
+- Add global form state. @robgietema [#5721](https://github.com/plone/volto/issues/5721)
+- Allow editor to edit metadata during bulk upload. @iFlameing [#5549](https://github.com/plone/volto/issues/5549)
+- Added `aria-live="polite"` in `Contents.jsx` to improve accessibility for the Contents page. @Hrittik20 [#5617](https://github.com/plone/volto/issues/5617)
+- Support for passing whole data object of the initial blocks in local config. Refactor initial block type to its own helpers. @sneridagh [#5718](https://github.com/plone/volto/issues/5718)
+- Improve validation of IdWidget @tedw [#3716](https://github.com/plone/volto/issues/3716)
+- Add image preview in object browser widget. @robgietema [#5658](https://github.com/plone/volto/issues/5658)
+- Enhanced `ColorPickerWidget` with additional color definitions, saving it as an object instead of a string. @sneridagh [#5585](https://github.com/plone/volto/issues/5585)
+- Allow to opt out of the nested prefixed name build in the custom CSS properties style name generator if an object is found in the style wrapper object. @sneridagh [#5586](https://github.com/plone/volto/issues/5586)
+- Added support for custom CSS properties in the `StyleWrapper`. @sneridagh [#5581](https://github.com/plone/volto/issues/5581)
+- Added conditional variations support. @sneridagh @robgietema [#5424](https://github.com/plone/volto/issues/5424)
+- Added `navRoot` and `contentType` to `restricted` key in blocks configuration. @sneridagh [#5517](https://github.com/plone/volto/issues/5517)
+- Add support for `preview_image_link` behavior in Volto Image component @sneridagh [#5523](https://github.com/plone/volto/issues/5523)
+- New monorepo @sneridagh [#5409](https://github.com/plone/volto/issues/5409)
+- Use special breadcrumb in control panel @tiberiuichim [#5292](https://github.com/plone/volto/issues/5292)
+- Plone type definitions in its own package @sneridagh [#5397](https://github.com/plone/volto/issues/5397)
+- Use container from component registry in sitemap component and also refactor the class
+ to functional component. @iRohitSingh [#5418](https://github.com/plone/volto/issues/5418)
+- Improvements and completeness of the ContentMetadataTags component @ericof @sneridagh [#5433](https://github.com/plone/volto/issues/5433)
+- List plone.app.linkintegrity breaches with links to the pages in the delete confirmation modal. @jaroel [#5234](https://github.com/plone/volto/issues/5234)
+
+### Bugfix
+
+- Fix error "Exception: No query supplied" in the listing block, when there is no criteria selected. @tedw87 [#3839](https://github.com/plone/volto/issues/3839)
+- Fixed page changes not being announced to screen reader users. @JeffersonBledsoe [#5288](https://github.com/plone/volto/issues/5288)
+- Fix useSelector is returned a different result when called with the same parameters in IdWidget. @iFlameing [#6449](https://github.com/plone/volto/issues/6449)
+- Moved `applyBlockDefaults` one component up so the style is computed correctly. @sneridagh [#6451](https://github.com/plone/volto/issues/6451)
+- Fix site setup access check by using `@actions` endpoint to validate permissions. @Faakhir30 [#6355](https://github.com/plone/volto/issues/6355)
+- Fix redirect after login if the `Login` component is used on a route other than `/login` or `/logout`. @dobri1408 [#6419](https://github.com/plone/volto/issues/6419)
+- URL Management control panel: Improve layout for long paths. @davisagli [#6421](https://github.com/plone/volto/issues/6421)
+- URL Management control panel: add missing translations. @davisagli [#6436](https://github.com/plone/volto/issues/6436)
+- Revert "Fix block chooser search is not focusable when clicked on add button" #5867 @sneridagh
+ It was causing a regression described in #6389 [#6390](https://github.com/plone/volto/issues/6390)
+- Added missing arg for `buildStyleClassNamesExtenders` @sneridagh [#6381](https://github.com/plone/volto/issues/6381)
+- Increase specificity of table header style selector to properly override colors for better contrast @jackahl [#2487](https://github.com/plone/volto/issues/2487)
+- Change Form input:focus text color to the `textColor` value for a11y.
+ Add Cypress test for contact form inputs. @ThomasKindermann @tedw87 [#2570](https://github.com/plone/volto/issues/2570)
+- Add missing `for` attribute to checkbox label to improve accessibility. @gomez [#6249](https://github.com/plone/volto/issues/6249)
+- Fixed build style classnames in edit mode. Also use buildStyleClassNamesExtenders. @giuliaghisini [#6259](https://github.com/plone/volto/issues/6259)
+- Fetch `user` before pass it to the `restricted` function of the block settings. @wesleybl [#6293](https://github.com/plone/volto/issues/6293)
+- Join validation errors in one single toast and update errors from response. @cekk
+- Toast content now has a
wrapper instead of a
. @cekk [#6295](https://github.com/plone/volto/issues/6295)
+- Fixed findBlocks when no blocks are passed. @giuliaghisini [#6299](https://github.com/plone/volto/issues/6299)
+- Fixed toolbar buttons not having a focus outline. @JeffersonBledsoe [#6313](https://github.com/plone/volto/issues/6313)
+- fix: SidebarPopup close on ESC keypress @nileshgulia1 [#6315](https://github.com/plone/volto/issues/6315)
+- Changed sidebar accordion text colour from @teal to @textColor. @JeffersonBledsoe [#6330](https://github.com/plone/volto/issues/6330)
+- Labels accessibility for ArrayWidget, SelectWidget, TokenWidget. @folix-01 [#6332](https://github.com/plone/volto/issues/6332)
+- Use lighter blue as link color in inverted tables to improve contrast for a11y @jackahl [#6334](https://github.com/plone/volto/issues/6334)
+- Fix search block showing no option select in sort on property @iRohitSingh [#5055](https://github.com/plone/volto/issues/5055)
+- Displays validation error messages on control panel forms. @wesleybl [#5274](https://github.com/plone/volto/issues/5274)
+- Fix `initialValue` block setting. @wesleybl [#5971](https://github.com/plone/volto/issues/5971)
+- When user changes location, set the `userSession.token` value based on cookie. This fixes the login status not being properly determined by the application. @tiberiu-ichim [#6071](https://github.com/plone/volto/issues/6071)
+- Fix `Teaser` block image override option to render external images and internal images pointing to image scales. @Tishasoumya-02 [#6147](https://github.com/plone/volto/issues/6147)
+- Add Cypress test for search block sort on property. @iRohitSingh [#6226](https://github.com/plone/volto/issues/6226)
+- Remove unused i18n message for SortOn component. @davisagli [#6230](https://github.com/plone/volto/issues/6230)
+- Return a 302 response for server-side rendering of the Link view for unauthenticated users. @davisagli [#6235](https://github.com/plone/volto/issues/6235)
+- Fix loading of .cjs in webpack. @davisagli [#6237](https://github.com/plone/volto/issues/6237)
+- fixed change of form.ui.hovered when editing blocks, because if you have a FormBlock component inside another one,
+ onMouseOver fires for all stacked blocks and you cannot use the nested form. @giuliaghisini [#6240](https://github.com/plone/volto/issues/6240)
+- Changed imports from relative to absolute to avoid type errors in add-on tests. @wesleybl [#6244](https://github.com/plone/volto/issues/6244)
+- In the URL Management control panel, allow external URLs as targets. @davisagli [#6247](https://github.com/plone/volto/issues/6247)
+- Disable save button when loading POST query @sabrina-bongiovanni [#6252](https://github.com/plone/volto/issues/6252)
+- Fix `TypeError: values[0] is undefined` in Contents properties modal. @davisagli [#6258](https://github.com/plone/volto/issues/6258)
+- Fix error in `SortOn` component when no sort is selected. @davisagli [#6273](https://github.com/plone/volto/issues/6273)
+- Place actions storybook in correct path. @Tishasoumya-02 [#6275](https://github.com/plone/volto/issues/6275)
+- Remove `Generate tabs for items other than folders` field from navigation control panel. @wesleybl [#6278](https://github.com/plone/volto/issues/6278)
+- Fix error rendering empty grid blocks. @MAX-786 [#6279](https://github.com/plone/volto/issues/6279)
+- Fixed instant error on click on an item of the "Order" tab @sneridagh [#6284](https://github.com/plone/volto/issues/6284)
+- Fixed UTC problems in `RecurrenceWidget`. @giuliaghisini [#5002](https://github.com/plone/volto/issues/5002)
+- Do not send sorting information in the search block if no sort_on setting is configured @erral [#5338](https://github.com/plone/volto/issues/5338)
+- Fixed pagination in search results by passing `pageSize` explicitly to all search API calls. @EshaanAgg [#5464](https://github.com/plone/volto/issues/5464)
+- Fix the toolbar handler color for the homepage to match its "published" state. @sabrina-bongiovanni [#6126](https://github.com/plone/volto/issues/6126)
+- Allow `ImageWidget` value to be an object and use the `@id` to get the value if present.
+ This is useful for fields that were using the `object_browser` widget previously to set values. @ichim-david [#6156](https://github.com/plone/volto/issues/6156)
+- Persist data for the `backend-docker-start` Docker container in a Docker volume named `volto-backend-data`.
+ This way the data is persisted between runs of the container.
+ You can also delete the `data` volume to start fresh.
+ @ichim-david [#6157](https://github.com/plone/volto/issues/6157)
+- Improve CSS for the `SchemaWidget` widget. @robgietema @sneridagh [#6189](https://github.com/plone/volto/issues/6189)
+- Revisit login/logout process, better catching of edge cases @sneridagh [#6155](https://github.com/plone/volto/issues/6155)
+- Restored browse link in `Slate` `AddLink` Pop-up. @ichim-david
+ Fixed recursive error when uploading an image using the `Image` widget. @sneridagh
+ Fixed image display when using an external URL. @sneridagh
+ Fixed the position of the `Image` widget toolbar when scrolling by changing the position of the toolbar to be within the widget area instead of the body. @ichim-david
+ Improved display of `AddLink` Pop-up when using it inside the `Image` widget where we don't have a link picker. @ichim-david [#6159](https://github.com/plone/volto/issues/6159)
+- Fix aria-label of items that are `folderish` in ObjectBrowserNav component when performing item search. Previously it said `Select item.title` now `Browse item.title`. This brings it in line with the aria-label when not performing an item search. @sneridagh [#6150](https://github.com/plone/volto/issues/6150)
+- Cleanup Image widget and pass down onSelectItem prop if any @sneridagh [#6132](https://github.com/plone/volto/issues/6132)
+- Whitelist some dynamic imports to suppress vite warnings in storybook @tomschall [#6133](https://github.com/plone/volto/issues/6133)
+- fix reset teaser source button giving undefined error when no target is selected @nileshgulia1 [#6121](https://github.com/plone/volto/issues/6121)
+- Added three missing German translations. [@jensens] [#6124](https://github.com/plone/volto/issues/6124)
+- Ensure that sidebar field will not steal focus when metadata is edited @dobri1408 [#5983](https://github.com/plone/volto/issues/5983)
+- Fixed 'diff' path for nonContentRoutes. @giuliaghisini [#6102](https://github.com/plone/volto/issues/6102)
+- Prevent duplicated UUIDs in inner blocks when copying container blocks @sneridagh [#6112](https://github.com/plone/volto/issues/6112)
+- Support nested directories in public folder add-on sync folders both in dev and build mode @sneridagh [#6098](https://github.com/plone/volto/issues/6098)
+- export getFieldURL from Url.js in helpers @dobri1408 [#6100](https://github.com/plone/volto/issues/6100)
+- Fix internalUrl Widget to Reflect Prop Changes via onChangeBlock @dorbi1408 @ichim-david [#6036](https://github.com/plone/volto/issues/6036)
+- Add default 'l' and 'center' values to size and align fields of `Image` block.
+ This fixes data not having any value adding proper options to the `Image` block. @ichim-david [#6046](https://github.com/plone/volto/issues/6046)
+- Fix public folder in dev mode, now it starts by default with the default Volto core defined public files @sneridagh [#6081](https://github.com/plone/volto/issues/6081)
+- Fix link in pop-up in `RelationsMatrix.jsx`. @stevepiercy [#6085](https://github.com/plone/volto/issues/6085)
+- Fix Uncaught RangeError: date value is not finite in DateTimeFormat.format. @mauritsvanrees [#6087](https://github.com/plone/volto/issues/6087)
+- relations control panel. Restrict eglible relation targets according relation constraints of fields vocabulary. @ksuess [#6091](https://github.com/plone/volto/issues/6091)
+- Better `Icon` component JSDoc typings @sneridagh [#6095](https://github.com/plone/volto/issues/6095)
+- Fix block chooser search is not focusable when clicked on add button @iRohitSingh [#5866](https://github.com/plone/volto/issues/5866)
+- Fixed skiplink links not tracking focus correctly @JeffersonBledsoe [#5959](https://github.com/plone/volto/issues/5959)
+- Remove left and right padding from _Event > Edit recurrence > Repeat on_ buttons when repeating for weekly or yearly events for big fonts, preventing overflow. @sabrina-bongiovanni [#6070](https://github.com/plone/volto/issues/6070)
+- Return a redirect response from Volto server-side rendering if the API request was redirected. @JeffersonBledsoe @mamico [#4834](https://github.com/plone/volto/issues/4834)
+- Fixed separator edit class spilling outside of the block toolbar @JeffersonBledsoe [#6010](https://github.com/plone/volto/issues/6010)
+- Fix duplication of execution of the build dependencies script on start @sneridagh [#6022](https://github.com/plone/volto/issues/6022)
+- In the EditBlockWrapper, pass the `showRestricted` prop to the BlockChooserButton. @JeffersonBledsoe [#6026](https://github.com/plone/volto/issues/6026)
+- Replace relative Import Path with Alias Import in 'Form.jsx' @MAX-786 [#6040](https://github.com/plone/volto/issues/6040)
+- Allow X-Robots-Tag header in images/files middleware @sneridagh [#6043](https://github.com/plone/volto/issues/6043)
+- Replaced the spinner with a progress bar that shows the number of files being uploaded (Uploading x files out of y), and positioned the progress bar properly, while uploading a large number of files. @victorchrollo14 [#5620](https://github.com/plone/volto/issues/5620)
+- Disable `jsx-a11y/label-has-associated-control` so that we can use `eslint-plugin-jsx-a11y` version 6.8.0 if it's pulled by other dependencies. @ichim-david [#5785](https://github.com/plone/volto/issues/5785)
+- Fix some type definitions in JSDocs @sneridagh [#6014](https://github.com/plone/volto/issues/6014)
+- Use `pnpm` 9.1.1 @sneridagh
+ Remove `postinstall` script for building dependencies @sneridagh [#6017](https://github.com/plone/volto/issues/6017)
+- Fix image disappears after pressing the Enter key on title field in image content-type. @iFlameing [#5973](https://github.com/plone/volto/issues/5973)
+- Defines the last 4 parameters of the `asyncConnect` function with optional. @wesleybl [#5985](https://github.com/plone/volto/issues/5985)
+- Fix server side sidebar rendering @sneridagh [#5993](https://github.com/plone/volto/issues/5993)
+- Add possibility to pass down `locale`, `messages` and `defaultLocale` properties inside the `customStore` object to `IntlProvider`. With this change we can control react-intl language provider from inside storybook and switch for example from english to german with storybook args. [#5976](https://github.com/plone/volto/issues/5976)
+- Fix no router link in logo @sneridagh [#5981](https://github.com/plone/volto/issues/5981)
+- Fix broken user portrait in personal tools menu. @davisagli [#2927](https://github.com/plone/volto/issues/2927)
+- Fix pt_BR translation of invalid email message. @wesleybl [#5953](https://github.com/plone/volto/issues/5953)
+- Fixed rendering if ConditionalLink has no children @pnicolli [#5963](https://github.com/plone/volto/issues/5963)
+- Fix flaky test 'As editor I can add links' by using getSlateEditorAndType. @ksuess [#5965](https://github.com/plone/volto/issues/5965)
+- Enhanced navigation reducer in Volto to keep items extra-data sent from the navigation endpoint @Hrittik20 [#5772](https://github.com/plone/volto/issues/5772)
+- Improve `ColorPickerWidget` typings @sneridagh [#5948](https://github.com/plone/volto/issues/5948)
+- Fix edge case in search options mangling when the options are false-ish sneridagh [#5869](https://github.com/plone/volto/issues/5869)
+- Does not show borders in addon block inputs. @wesleybl [#5894](https://github.com/plone/volto/issues/5894)
+- Fix `error is null` in `FormFieldWrapper.jsx`. @mauritsvanrees [#5919](https://github.com/plone/volto/issues/5919)
+- Updated Italian locales file `volto.po` with translation for "yes" from "si" to "sì". @yurj [#5924](https://github.com/plone/volto/issues/5924)
+- Fix self-registration form. @davisagli [#5935](https://github.com/plone/volto/issues/5935)
+- Correctly sort facet values if they are numbers @erral [#5864](https://github.com/plone/volto/issues/5864)
+- Cross-package manager Volto path resolver in `webpack-relative-resolver` @sneridagh [#5893](https://github.com/plone/volto/issues/5893)
+- Add BBB code for removed WysiwygWidget @sneridagh [#5874](https://github.com/plone/volto/issues/5874)
+- (fix): make search block sort and facets work on edit @dobri1408 [#5262](https://github.com/plone/volto/issues/5262)
+- Upgrade `@typescript-eslint` version @sneridagh [#5844](https://github.com/plone/volto/issues/5844)
+- Fix the introduction of a mutable (referenced) object when assigning the default inner `blocksConfig` object for the `grid` block, pass by value instead. sneridagh [#5850](https://github.com/plone/volto/issues/5850)
+- Fix other occurrences of mutable (referenced) objects when assigning the default inner `blocksConfig` object for the `grid` block, pass by value instead. sneridagh [#5859](https://github.com/plone/volto/issues/5859)
+- Fix translation error message. @robgietema [#5835](https://github.com/plone/volto/issues/5835)
+- Pass down content, pathname and navRoot to the `SlotComponent` for convenience @sneridagh [#5841](https://github.com/plone/volto/issues/5841)
+- Fix `setMetadataFocus` so it does not break if the element to be focused is not an `input` @sneridagh [#5843](https://github.com/plone/volto/issues/5843)
+- Show validation error message as string instead of list. @wesleybl [#1868](https://github.com/plone/volto/issues/1868)
+- Removed css from `contents.less` that made the Contents table break words in tiny sections due to small table headers such as `ID` or `UID`. @ichim.david [#5742](https://github.com/plone/volto/issues/5742)
+- Fix Link to Item and Aliases view not updating content in multilingual site. @iFlameing [#5820](https://github.com/plone/volto/issues/5820)
+- Modified build-deps make command to check if registry files are newer than dist to force rebuild. @ichim-david [#5825](https://github.com/plone/volto/issues/5825)
+- Reset global Form state onSubmit and onCancel in Add and Edit forms @sneridagh [#5827](https://github.com/plone/volto/issues/5827)
+- Fix HMR problems, upgrade react-refresh and @pmmmwh/react-refresh-webpack-plugin to latest @sneridagh [#5833](https://github.com/plone/volto/issues/5833)
+- Fixed toolbar menus not closing when clicking again on the toolbar buttons that show menus. @ichim-david
+ Add focus-visible rule to toolbar buttons so that it's visible to the user what button is focused when using tab navigation @ichim-david [#5645](https://github.com/plone/volto/issues/5645)
+- Enhance findBlocks to check for blocks also in data for add-ons such as @eeacms/volto-tabs-block. @ichim-david [#5796](https://github.com/plone/volto/issues/5796)
+- Fixed ArrayWidget sorting items. @giuliaghisini [#5805](https://github.com/plone/volto/issues/5805)
+- Fix sidebar form update. @robgietema [#5779](https://github.com/plone/volto/issues/5779)
+- Fixed listing SSR rendering by sending `subrequestId` instead of `id` only within `getAsyncData`, similar to calling `getQueryStringResults` directly. @ichim-david [#5688](https://github.com/plone/volto/issues/5688)
+- Enhanced Makefile paths to address whitespace compatibility issues. @Vivek-04022001 [#5715](https://github.com/plone/volto/issues/5715)
+- Fix console logging in acceptance server Makefile commands. @davisagli [#5748](https://github.com/plone/volto/issues/5748)
+- Add extra wait calls to listing block tests to avoid sporadic failures. @ichim-david [#5753](https://github.com/plone/volto/issues/5753)
+- Add @plone/components as external library.
+ Make the Terser plugin accept ESNext features.
+ Fix inline `svg` elements in LESS files. @sneridagh [#5766](https://github.com/plone/volto/issues/5766)
+- Fixed wrong conditional proprieties on `ObjectBrowser` for multiple selection. @deodorhunter @Wagner3UB [#4190](https://github.com/plone/volto/issues/4190)
+- Remove turbo from monorepo commands until it's really necessary @sneridagh [#5715](https://github.com/plone/volto/issues/5715)
+- Removed unmaintained and unused razzle-plugin-bundle-analyze in favor of webpack-bundle-analyzer. @ichim-david
+ Updated extending Razzle from an add-on section to remove code that didn't belong to that recipe. @ichim-david [#5671](https://github.com/plone/volto/issues/5671)
+- Fix multilingual redirector where it doesn't take into account the stored cookie in SSR. @robgietema [#5628](https://github.com/plone/volto/issues/5628)
+- Fix blocks chooser index and add a bit of breath to the left in the search input @sneridagh [#5647](https://github.com/plone/volto/issues/5647)
+- Fix `links-to-item` should be a protected route. @iFlameing [#5666](https://github.com/plone/volto/issues/5666)
+- Removed git merge conflicts from french volto.po locale file. @ichim-david [#5681](https://github.com/plone/volto/issues/5681)
+- In the recurrence widget, set the vertical alignment of the `edit` button to `middle`. @Ravi-kumar9347 [#5359](https://github.com/plone/volto/issues/5359)
+- Improve generation of type declarations. Fixes some key types propagation. @sneridagh [#5624](https://github.com/plone/volto/issues/5624)
+- Unify start command, trigger `build:deps` command @sneridagh [#5633](https://github.com/plone/volto/issues/5633)
+- Merge the StyleWrapper styles with the draggable props from b-D&D. @sneridagh
+ This fixes the D&D bug introduced in https://github.com/plone/volto/pull/5581 [#5652](https://github.com/plone/volto/issues/5652)
+- Use a textarea in the form for editing an existing comment. @Ravi-kumar9347 [#5265](https://github.com/plone/volto/issues/5265)
+- The hamburger icon to open the mobile/tablet navigation is now hidden if there are no navigation items. @Aarav238 [#5353](https://github.com/plone/volto/issues/5353)
+- Fix preview image component @steffenri [#5379](https://github.com/plone/volto/issues/5379)
+- Fix autopopulated value of facet when settings the value for another one. @iFlameing [#5432](https://github.com/plone/volto/issues/5432)
+- Fix sitemap for multilingual sites
+ [erral] [#5501](https://github.com/plone/volto/issues/5501)
+- Replace createRef with useRef in SidebarPopup
+ [razvanMiu] [#5519](https://github.com/plone/volto/issues/5519)
+- Fixed the project generator's ESLint configuration, added code quality checks to the CI to ensure a generated project can run these checks, and added documentation for how to reconfigure ESLint in projects. @sneridagh [#5530](https://github.com/plone/volto/issues/5530)
+- Fixed edge case error in Unauthorised page and Login route behavior @sneridagh [#5536](https://github.com/plone/volto/issues/5536)
+- changed typo of pnp to pnpm. @ujjwaleee26 [#5537](https://github.com/plone/volto/issues/5537)
+- Fix the right order of parameters in normalizeExternalData.js @dobri1408 [#5347](https://github.com/plone/volto/issues/5347)
+- Initialize data in form before the checks for the `blocks` and `blocks_layout` are done This fix an edge case when the data from the server content is empty, then the fields are populated, but the initialized data is snapshot after the check (and amendments) are done. @sneridagh [#5445](https://github.com/plone/volto/issues/5445)
+- Replaced `toNumber` with `parseFloat` to avoid an error when validating the `plone.restapi` version. @shibbu264 [#5448](https://github.com/plone/volto/issues/5448)
+- Initialize only the development addons present in `compilerOptions.paths` filtered by the add-ons registered @sneridagh [#5463](https://github.com/plone/volto/issues/5463)
+- Refactoring the code for extraction of videoDetails from the video URL, adding code for extracting videoDetails from youtube video URLs with '/live/' in its URL which previously used to throw an error and adding jest tests for same. @IshaanDasgupta [#5416](https://github.com/plone/volto/issues/5416)
+- Revert "Improvements and completeness of the ContentMetadataTags component (#5433) @sneridagh [#5449](https://github.com/plone/volto/issues/5449)
+- Searchbox and clear button inside blocks-chooser with visible focus and fixed the clear button label - @Wagner3UB [#5335](https://github.com/plone/volto/issues/5335)
+- Fix image paths in development mode. @robgietema [#5429](https://github.com/plone/volto/issues/5429)
+- Bring back deprecated Yeoman install method used for running yarnInstall @ichim-david [#5436](https://github.com/plone/volto/issues/5436)
+- Fix empty link element left hanging when hit enter at end of link. @iFlameing @tiberiuichim [#5291](https://github.com/plone/volto/issues/5291)
+- Add build process to registry package and fixes @sneridagh [#5364](https://github.com/plone/volto/issues/5364)
+- Fixed import in Storybook configuration, related to the recent registry change. @sneridagh [#5368](https://github.com/plone/volto/issues/5368)
+- Improve importing from the new registry from Volto code itself @sneridagh [#5373](https://github.com/plone/volto/issues/5373)
+
+### Internal
+
+- Update Browserlist @sneridagh [#6455](https://github.com/plone/volto/issues/6455)
+- Used `resource title` instead of `resource type` in page title on edit. @Faakhir30 [#6308](https://github.com/plone/volto/issues/6308)
+- Added support for Node.js 22, since it will become LTS on 2024-10-29. @sneridagh [#6371](https://github.com/plone/volto/issues/6371)
+- Adapt `@plone/registry` as an ESM module, and fix its imports. @sneridagh [#6399](https://github.com/plone/volto/issues/6399)
+- Fix `clean` make command @sneridagh [#6403](https://github.com/plone/volto/issues/6403)
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407)
+- Update `caniuse` Oct24 @sneridagh [#6408](https://github.com/plone/volto/issues/6408)
+- Remove last tests still in Node.js 18 @sneridagh [#6418](https://github.com/plone/volto/issues/6418)
+- Remove mention of `SEAMLESS` mode in the server bootstrap. @sneridagh [#6424](https://github.com/plone/volto/issues/6424)
+- Added missing improvement to the `build-deps` Makefile command. @sneridagh [#6383](https://github.com/plone/volto/issues/6383)
+- Added a pull request template as a checklist before submitting a PR. @stevepiercy [#6365](https://github.com/plone/volto/issues/6365)
+- Refactored the `SelectWidget` component to a functional component. @lorstenoplo [#5570](https://github.com/plone/volto/issues/5570)
+- Allow setting the language for `make backend-docker-start`. Default is `en`. Added usage documentation in Contributing > Develop Volto core, and reorganized it. Cleaned up Configuration > Multilingual. @stevepiercy [#6231](https://github.com/plone/volto/issues/6231)
+- Revert rename of `.gitkeep`. @stevepiercy [#6232](https://github.com/plone/volto/issues/6232)
+- Upgrade Cypress to 13.13.2. @wesleybl [#6241](https://github.com/plone/volto/issues/6241)
+- Complete upgrade Cypress to 13.13.2. Bump actions/upload-artifact@v1 to v4. @stevepiercy [#6242](https://github.com/plone/volto/issues/6242)
+- Add `make i18n` command. @Faakhir30 [#6274](https://github.com/plone/volto/issues/6274)
+- Update to Plone 6.0.13 @sneridagh [#6285](https://github.com/plone/volto/issues/6285)
+- Debounced searching for users and groups in the `User Group Membership` Control Panel. @pnicolli [#6153](https://github.com/plone/volto/issues/6153)
+- Update the link in the PLIP issue template to the new Plone 6 Documentation PLIP page. @stevepiercy [#6175](https://github.com/plone/volto/issues/6175)
+- Added Cypress test for field types in example content - @Tishasoumya-02 [#6217](https://github.com/plone/volto/issues/6217)
+- Automatically add a PLIP issue to the PLIP project board. @stevepiercy [#6134](https://github.com/plone/volto/issues/6134)
+- Renamed `constants/Languages.js` to `constants/Languages.cjs` @sneridagh [#6130](https://github.com/plone/volto/issues/6130)
+- Improved the existing GitHub workflows by encapsulating a common operation into a reusable action for easier maintenance. @FritzHoing, @ichim-david [#6108](https://github.com/plone/volto/issues/6108)
+- Upgrade `react-intl` to maximum 3.x series to fix a bundling issue.
+- Rename missing js file that must be jsx. @sneridagh [#6128](https://github.com/plone/volto/issues/6128)
+- Rename the `Makefile` commands to unify them with the new agreed naming scheme. @sneridagh [#6104](https://github.com/plone/volto/issues/6104)
+- Remove `api` folder. Add `backend` folder using latest backend best practices. @sneridagh [#6110](https://github.com/plone/volto/issues/6110)
+- Rename files with wrong extension `js->jsx` when they contain JSX. @sneridagh [#6114](https://github.com/plone/volto/issues/6114)
+- Automatically set a new issue's label to `03 type: feature (plip)` for PLIPs. @stevepiercy [#6122](https://github.com/plone/volto/issues/6122)
+- Fix test script in monorepo root @sneridagh [#6051](https://github.com/plone/volto/issues/6051)
+- Use pnpm corepack instead of installing it in CI @sneridagh [#6038](https://github.com/plone/volto/issues/6038)
+- Add client and providers to the SSR externals list @sneridagh [#6045](https://github.com/plone/volto/issues/6045)
+- Allow `Makefile` options to be modified by a `Makefile.local` file if present. @ichim-david [#5997](https://github.com/plone/volto/issues/5997)
+- Modified `locking` and `block-listing` cypress tests
+ to use more `assertions` instead of wait times in order to improve
+ the reliability of the tests. @ichim-david [#5998](https://github.com/plone/volto/issues/5998)
+- Update to use Plone 6.0.11 @sneridagh [#5989](https://github.com/plone/volto/issues/5989)
+- Flexibilize the pins for all ESlint deps, in Volto and generators @sneridagh [#5991](https://github.com/plone/volto/issues/5991)
+- Cleaned up useless injectIntl in DefaultView @pnicolli [#5994](https://github.com/plone/volto/issues/5994)
+- Reduced JavaScript bundle size of the production build. Code split several internal modules: Controlpanels, Form, Widgets among other small ones. @pnicolli @deodorhunter [#5295](https://github.com/plone/volto/issues/5295)
+- Improvements to the monorepo setup with utilities, especially ESLint. Build cached option to speedup operations. @sneridagh [#5969](https://github.com/plone/volto/issues/5969)
+- Changed relative path to absolute in `DefaultView.jsx` for the `RenderBlocks` component to make it easier to customize. @agan-k [#5917](https://github.com/plone/volto/issues/5917)
+- Fix cypress test "As editor, I can unlock a locked page". @wesleybl [#5933](https://github.com/plone/volto/issues/5933)
+- Fix cypress test "Navigate to different pages on two different listings". @ichim-david [#5934](https://github.com/plone/volto/issues/5934)
+- Add a new label `needs: triage` to new bug reports. @stevepiercy [#5940](https://github.com/plone/volto/issues/5940)
+- Remove dangling unused lines in StoryBook config @sneridagh [#5911](https://github.com/plone/volto/issues/5911)
+- Upgrade Storybook to version 8. @sneridagh [#5912](https://github.com/plone/volto/issues/5912)
+- Move `testing-library` Cypress commands import to inner commands, so it can be imported from the outside. @sneridagh [#5906](https://github.com/plone/volto/issues/5906)
+- Add new cypress helper for slate `getSlateEditorAndType` @sneridagh [#5909](https://github.com/plone/volto/issues/5909)
+- Upgrade @typescript-eslint @sneridagh [#5910](https://github.com/plone/volto/issues/5910)
+- Bump all the versions in GitHub workflows. @stevepiercy [#5873](https://github.com/plone/volto/issues/5873)
+- Upgrade versions of Cypress @sneridagh [#5845](https://github.com/plone/volto/issues/5845)
+- Add new readmes to CI. @stevepiercy [#5837](https://github.com/plone/volto/issues/5837)
+- Uses Plone 6.0.10.1 in tests. @wesleybl [#5830](https://github.com/plone/volto/issues/5830)
+- Update dependencies
+ Fix prettier due to new version @sneridagh [#5815](https://github.com/plone/volto/issues/5815)
+- New types declarations with @types/react@18 - make tsc happy @sneridagh [#5814](https://github.com/plone/volto/issues/5814)
+- Upgade `semantic-ui-react` to latest version (2.1.5) @sneridagh [#5632](https://github.com/plone/volto/issues/5632)
+- Add cypress tests for the "links-to-item" view of content items @jackahl [#5427](https://github.com/plone/volto/issues/5427)
+- Polish po file handling @erral [#5542](https://github.com/plone/volto/issues/5542)
+- Unify variables in `Makefile`s. @sneridagh [#5637](https://github.com/plone/volto/issues/5637)
+- Added Cypress tests to verify that the modal dialog for deleting linked items pops up correctly. @Molochem [#5529](https://github.com/plone/volto/issues/5529)
+- Fix stylelint violations @sneridagh [#5544](https://github.com/plone/volto/issues/5544)
+- Update Plone to 6.0.9 @sneridagh [#5562](https://github.com/plone/volto/issues/5562)
+- Add missing dependency on jsdom @sneridagh [#5490](https://github.com/plone/volto/issues/5490)
+- Fix cypress artifacts path in acceptance tests. @davisagli [#5498](https://github.com/plone/volto/issues/5498)
+- Make sure that the dependencies are built and available on all processes @sneridagh [#5514](https://github.com/plone/volto/issues/5514)
+- Cleaned up Registry and renamed `packagesFolderAddons` to `coreAddons`. @sneridagh [#5464](https://github.com/plone/volto/issues/5464)
+- Deleted wrongly placed news items, added a check for them, issue a nice message in CI @sneridagh [#5470](https://github.com/plone/volto/issues/5470)
+- Updated yarnhook to 0.6.1 in order to support pnpm 8 @sneridagh [#5444](https://github.com/plone/volto/issues/5444)
+- Update internal Plone version to 6.0.8 @sneridagh [#5384](https://github.com/plone/volto/issues/5384)
+- Unused files in root cleanup @sneridagh [#5367](https://github.com/plone/volto/issues/5367)
+
+### Documentation
+
+- Added upgrade guide fix for HMR. @sneridagh [#6446](https://github.com/plone/volto/issues/6446)
+- Added deprecation notices to the upgrade guide for Volto 18. @sneridagh [#6426](https://github.com/plone/volto/issues/6426)
+- Replace `yarn` with `pnpm` wherever necessary. @sneridagh [#6433](https://github.com/plone/volto/issues/6433)
+- Rename page title from Frontend to Volto UI. @stevepiercy [#6438](https://github.com/plone/volto/issues/6438)
+- Add reference to Translate Volto in the main documentation. @stevepiercy [#6386](https://github.com/plone/volto/issues/6386)
+- Removed pin on Vale for documentation spelling, grammar, and style checks, and upgrade to v3.x configuration.
+ Updated CI and `Makefile` to allow `VALEOPTS=--no-exit`. @stevepiercy [#6376](https://github.com/plone/volto/issues/6376)
+- Update references to cookiecutter-plone-starter in docs. @davisagli [#6289](https://github.com/plone/volto/issues/6289)
+- - Revised Cookieplone section in the Upgrade Guide. @stevepiercy [#6290](https://github.com/plone/volto/issues/6290)
+- - Fix the MyST syntax for the label `upgrade-18-cookieplone-label`. @stevepiercy [#6360](https://github.com/plone/volto/issues/6360)
+- Fixed spelling of prerequisites. @stevepiercy [#6362](https://github.com/plone/volto/issues/6362)
+- Fix links to Vite website. @stevepiercy [#6366](https://github.com/plone/volto/issues/6366)
+- Fix redirect of link in documentation to testing Redux store blog post. @stevepiercy [#6239](https://github.com/plone/volto/issues/6239)
+- Fix redirect to Docker documentation. @stevepiercy [#6262](https://github.com/plone/volto/issues/6262)
+- Added upgrade guide notice about CookiePlone as the recommended way for generating projects and add-on boilerplate @sneridagh [#6286](https://github.com/plone/volto/issues/6286)
+- Changed a few typos within documentation, README's and comments. @FritzHoing [#6109](https://github.com/plone/volto/issues/6109)
+- Use relative links to ensure static files get copied during documentation build. @stevepiercy [#6174](https://github.com/plone/volto/issues/6174)
+- Clean up upgrade guide for `react-share` library and `SocialSharing` component. @stevepiercy [#6175](https://github.com/plone/volto/issues/6175)
+- Add references for contributing to latest and earlier versions of Volto. @stevepiercy [#6184](https://github.com/plone/volto/issues/6184)
+- Improved i18n docs regarding new translated messages not being picked up by the i18n translation mechanism when added in shadowed components. @pnicolli [#6188](https://github.com/plone/volto/issues/6188)
+- Add a label and minor grammar fixes to i18n documentation. @stevepiercy [#6192](https://github.com/plone/volto/issues/6192)
+- Polish upgrade docs and news items for `SchemaWidget`. @stevepiercy [#6193](https://github.com/plone/volto/issues/6193)
+- Fix link to renamed `src/constants/Languages.cjs`. @stevepiercy [#6135](https://github.com/plone/volto/issues/6135)
+- Use Plone Sphinx Theme. Migrate from Netlify to Read the Docs for pull request preview builds. @stevepiercy [#6030](https://github.com/plone/volto/issues/6030)
+- Build documentation and Storybook only when their files change. @stevepiercy [#6037](https://github.com/plone/volto/issues/6037)
+- Add sphinx-examples extension, update examples, align docs requirements with main documentation, and fix JSON example in upgrade guide. @stevepiercy [#6011](https://github.com/plone/volto/issues/6011)
+- Put nvm installation section into a separate include file. @animus888 [#5968](https://github.com/plone/volto/issues/5968)
+- Add missing step in Storybook 6 to 8 migration. @sneridagh [#5913](https://github.com/plone/volto/issues/5913)
+- Fix redirect of `https://sustainability.eionet.europa.eu` to `https://www.eea.europa.eu/en/topics/at-a-glance/sustainability/`. @stevepiercy [#5941](https://github.com/plone/volto/issues/5941)
+- Cleanup obsolete EEA projects from README and update info about EEA main website. @avoinea [#5943](https://github.com/plone/volto/issues/5943)
+- Update information about Quanta plans. [@jensens] [#5903](https://github.com/plone/volto/issues/5903)
+- Updated testing and code quality with information on how to contribute to Volto core. @ichim-david [#5341](https://github.com/plone/volto/issues/5341)
+- `Volto 18.0.0-alpha.21` and `volto-update-deps` documentation @sneridagh [#5892](https://github.com/plone/volto/issues/5892)
+- Update `volto-slate` configuration documentation to indicate it is now part of Volto core. @MostafaMagdyy [#5342](https://github.com/plone/volto/issues/5342)
+- Modified `slate.useLinkedHeadings` documentation to mention feature is disabled for authenticated users after #5225 changes. @ichim-david [#5885](https://github.com/plone/volto/issues/5885)
+- Update link to cssnano documentation. @stevepiercy [#5853](https://github.com/plone/volto/issues/5853)
+- Update docs for the `defaultBlockType` setting. @davisagli [#5854](https://github.com/plone/volto/issues/5854)
+- The proper name is Semantic UI. @stevepiercy [#5855](https://github.com/plone/volto/issues/5855)
+- Add missing nextjs install step. @gomez [#5857](https://github.com/plone/volto/issues/5857)
+- Add reference to Docker installation for some Linux distributions. @stevepiercy [#5861](https://github.com/plone/volto/issues/5861)
+- Fix broken link to TanStack Query. @stevepiercy [#5871](https://github.com/plone/volto/issues/5871)
+- Fix linkcheckbroken of `README.md` at the source of the file. @stevepiercy [#5834](https://github.com/plone/volto/issues/5834)
+- Improve wayfinding for various Volto audiences. @stevepiercy [#5730](https://github.com/plone/volto/issues/5730)
+- Linkcheck thinks `README.md` is `http://README.md`. Bad linkcheck, no more 🍺 for you. @stevepiercy [#5816](https://github.com/plone/volto/issues/5816)
+- Added Release Management Notes. @sneridagh @stevepiercy [#5358](https://github.com/plone/volto/issues/5358)
+- Delete redundant `developing-a-project.md`. @stevepiercy [#5675](https://github.com/plone/volto/issues/5675)
+- Removed Memori and TwinCreator websites from `README.md` no longer made using Volto and giving 404 error. @ichim-david [#5802](https://github.com/plone/volto/issues/5802)
+- Overhaul environment variables documentation. @stevepiercy [#4581](https://github.com/plone/volto/issues/4581)
+- Reorganize `README.md`, merging content into authoritative locations. Add `awesome_bot` to check links in all READMEs. @stevepiercy [#5437](https://github.com/plone/volto/issues/5437)
+- Replace outdated diff with a link to current file. @stevepiercy [#5703](https://github.com/plone/volto/issues/5703)
+- Document when the 'links and references' view was added. @davisagli [#5756](https://github.com/plone/volto/issues/5756)
+- Update links to Redux and React developer extensions for Chrome. @stevepiercy [#5757](https://github.com/plone/volto/issues/5757)
+- Chromewebstore recently changed its URL and has "too many redirects", so it needs to be excluded from linkcheck. @stevepiercy [#5761](https://github.com/plone/volto/issues/5761)
+- Add Git as a pre-requisite. @stevepiercy [#5769](https://github.com/plone/volto/issues/5769)
+- Block search engines from indexing content on Netlify preview builds. @stevepiercy [#5725](https://github.com/plone/volto/issues/5725)
+- Fixed redirect of `https://tanstack.com/query/v4/docs/react/guides/ssr` to `https://tanstack.com/query/v4/docs/framework/react/guides/ssr`. @stevepiercy [#5700](https://github.com/plone/volto/issues/5700)
+- Clarify how to create a Volto project with the frontend only when you have your own existing backend. @stevepiercy [#3723](https://github.com/plone/volto/issues/3723)
+- Enabled hyperlinking from narrative documentation to Storybook entries while developing, in Netlify preview builds, and when deployed to the main production Plone documentation. Documented usage and syntax in {ref}`link-to-storybook-entries-from-documentation`. @stevepiercy [#5599](https://github.com/plone/volto/issues/5599)
+- Update cross-reference to main documentation from `install-from-packages` to `create-project`. @stevepiercy [#5654](https://github.com/plone/volto/issues/5654)
+- Temporarily pin `sphinxcontrib-*help` dependencies so documentation can build. @stevepiercy [#5655](https://github.com/plone/volto/issues/5655)
+- Pin Vale to 2.30.0 to allow build of documentation until we can upgrade to v3.x. @stevepiercy [#5656](https://github.com/plone/volto/issues/5656)
+- Use correct Pygments lexer for SCSS. @stevepiercy [#5673](https://github.com/plone/volto/issues/5673)
+- Fixed a broken reference to create a project in documentation. @stevepiercy [#5692](https://github.com/plone/volto/issues/5692)
+- Align the project names in "Create a Volto project without a backend". @stevepiercy [#5694](https://github.com/plone/volto/issues/5694)
+- Clarified how CSS properties work. @stevepiercy [#5591](https://github.com/plone/volto/issues/5591)
+- Removed outdated acceptance tests docs @sneridagh [#5533](https://github.com/plone/volto/issues/5533)
+- Add `how-to-restrict-blocks` to `recipes/index.md`, avoiding Sphinx warning. @stevepiercy [#5546](https://github.com/plone/volto/issues/5546)
+- Point developers to correct installation documentation, replaced yarn commands with pnpm, and correct some misspellings. We also now use includes to keep instructions consistent within Volto and in the main Plone 6 documentation. Finally the `NVM_VERSION` substitution now works. @ujjwaleee26 @stevepiercy [#5556](https://github.com/plone/volto/issues/5556)
+- Fix redirects. @stevepiercy [#5563](https://github.com/plone/volto/issues/5563)
+- Fix redirect for `nvm.fish`. @stevepiercy [#5569](https://github.com/plone/volto/issues/5569)
+- Less comment blocks must have `*` as the first character on the second and subsequent lines, else the Pygments lexer fails. @stevepiercy [#5500](https://github.com/plone/volto/issues/5500)
+- Revert #5500. See #5499 for actual cause of Pygments failure. @stevepiercy [#5504](https://github.com/plone/volto/issues/5504)
+- Clarified where to run Make commands for building documentation. @stevepiercy [#5505](https://github.com/plone/volto/issues/5505)
+- Changed installation of Vale from manual to automatic via `make docs-vale`. @stevepiercy [#5508](https://github.com/plone/volto/issues/5508)
+- Fixed broken links after monorepo merge. @stevepiercy [#5459](https://github.com/plone/volto/issues/5459)
+- Added `volto-generator` compatibility with Volto to documentation. @stevepiercy [#5467](https://github.com/plone/volto/issues/5467)
+- Updated the symlink from `docs/source/news` to point to `packages/volto/news`. @stevepiercy [#5471](https://github.com/plone/volto/issues/5471)
+- Remove outdated note from upgrade guide for 17 related to ipv6 preferred by Node 18 @sneridagh [#5481](https://github.com/plone/volto/issues/5481)
+- Remove mention of LTS in Volto #4905. @stevepiercy [#4905](https://github.com/plone/volto/issues/4905)
+- Added documentation to contributing. @stevepiercy [#5377](https://github.com/plone/volto/issues/5377)
+- Remove orphans and move branch, version, and support policies into better locations in documentation. @stevepiercy [#5385](https://github.com/plone/volto/issues/5385)
+- Updated Node.js version required for Volto. @Noobham [#5389](https://github.com/plone/volto/issues/5389)
+- Fixed reference link to configuration/settings. @stevepiercy [#5410](https://github.com/plone/volto/issues/5410)
+- JSX is now an official lexer in Pygments. @stevepiercy [#5412](https://github.com/plone/volto/issues/5412)
+- Improved the Makefil to check for the existence of a symlink from docs to news, and create one only if it exists, else do nothing. @stevepiercy [#5375](https://github.com/plone/volto/issues/5375)
+- Improved wording in branch policy. @stevepiercy [#5376](https://github.com/plone/volto/issues/5376)
+- Simplify linkcheck configuration with a regex. @stevepiercy [#5378](https://github.com/plone/volto/issues/5378)
+- Updated branch policy. @sneridagh [#5363](https://github.com/plone/volto/issues/5363)
+- Remove "legacy" term, now that we use "no longer supported". @stevepiercy [#5370](https://github.com/plone/volto/issues/5370)
+- Reduce the severity level of `Microsoft.Contractions` and `Microsoft.Units` from `error` to `suggestion` when running `make docs-vale` in preparation for requiring Vale passing without errors. @stevepiercy [#5371](https://github.com/plone/volto/issues/5371)
+- Improved wording in branching policy. @sneridagh [#5372](https://github.com/plone/volto/issues/5372)
+
+## 18.0.0-alpha.49 (2024-10-31)
+
+### Feature
+
+- Update Romanian translation. @avoinea [#6454](https://github.com/plone/volto/issues/6454)
+- Update Brazilian Portuguese translation [@ericof]
+
+### Internal
+
+- Update Browserlist @sneridagh [#6455](https://github.com/plone/volto/issues/6455)
+
+## 18.0.0-alpha.48 (2024-10-30)
+
+### Breaking
+
+- Wrap Table of Contents block in a `nav` tag, instead of a `div` tag, for better representation improving the block accessibility. @Tishasoumya-02 @ichim-david [#6082](https://github.com/plone/volto/issues/6082)
+
+### Feature
+
+- Enhance the `buildStyleObjectFromData` helper. @sneridagh [#6445](https://github.com/plone/volto/issues/6445)
+
+### Bugfix
+
+- Fix error "Exception: No query supplied" in the listing block, when there is no criteria selected. @tedw87 [#3839](https://github.com/plone/volto/issues/3839)
+- Fixed page changes not being announced to screen reader users. @JeffersonBledsoe [#5288](https://github.com/plone/volto/issues/5288)
+- Fix useSelector is returned a different result when called with the same parameters in IdWidget. @iFlameing [#6449](https://github.com/plone/volto/issues/6449)
+- Moved `applyBlockDefaults` one component up so the style is computed correctly. @sneridagh [#6451](https://github.com/plone/volto/issues/6451)
+
+### Documentation
+
+- Added upgrade guide fix for HMR. @sneridagh [#6446](https://github.com/plone/volto/issues/6446)
+
+## 18.0.0-alpha.47 (2024-10-27)
+
+### Breaking
+
+- Dropped support for Node.js 18, since Volto only supports the last two LTS versions. @sneridagh
+ Volto might continue to work on it, but it will be no longer tested in CI. [#6371](https://github.com/plone/volto/issues/6371)
+
+### Feature
+
+- URL Management control panel: add a filter for redirects created after a given date.
+ **Note:** This requires `Products.CMFPlone` 6.0.14 and `plone.restapi` 9.8.0 or later.
+ @davisagli [#6414](https://github.com/plone/volto/issues/6414)
+- URL Management control panel: Move the form for adding a new redirect into a modal. @davisagli [#6421](https://github.com/plone/volto/issues/6421)
+- URL Management control panel: Add feature to bulk upload redirects in a CSV file.
+ **Note:** This requires `plone.restapi` 9.8.0 or later.
+ @davisagli [#6421](https://github.com/plone/volto/issues/6421)
+- Introducing "client transforms for Redux reducers". @sneridagh [#6422](https://github.com/plone/volto/issues/6422)
+- URL Management control panel: Add a way to edit existing aliases. @davisagli [#6425](https://github.com/plone/volto/issues/6425)
+
+### Bugfix
+
+- Fix site setup access check by using `@actions` endpoint to validate permissions. @Faakhir30 [#6355](https://github.com/plone/volto/issues/6355)
+- Fix redirect after login if the `Login` component is used on a route other than `/login` or `/logout`. @dobri1408 [#6419](https://github.com/plone/volto/issues/6419)
+- URL Management control panel: Improve layout for long paths. @davisagli [#6421](https://github.com/plone/volto/issues/6421)
+- URL Management control panel: add missing translations. @davisagli [#6436](https://github.com/plone/volto/issues/6436)
+
+### Internal
+
+- Used `resource title` instead of `resource type` in page title on edit. @Faakhir30 [#6308](https://github.com/plone/volto/issues/6308)
+- Added support for Node.js 22, since it will become LTS on 2024-10-29. @sneridagh [#6371](https://github.com/plone/volto/issues/6371)
+- Adapt `@plone/registry` as an ESM module, and fix its imports. @sneridagh [#6399](https://github.com/plone/volto/issues/6399)
+- Fix `clean` make command @sneridagh [#6403](https://github.com/plone/volto/issues/6403)
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407)
+- Update `caniuse` Oct24 @sneridagh [#6408](https://github.com/plone/volto/issues/6408)
+- Remove last tests still in Node.js 18 @sneridagh [#6418](https://github.com/plone/volto/issues/6418)
+- Remove mention of `SEAMLESS` mode in the server bootstrap. @sneridagh [#6424](https://github.com/plone/volto/issues/6424)
+
+### Documentation
+
+- Added deprecation notices to the upgrade guide for Volto 18. @sneridagh [#6426](https://github.com/plone/volto/issues/6426)
+- Replace `yarn` with `pnpm` wherever necessary. @sneridagh [#6433](https://github.com/plone/volto/issues/6433)
+- Rename page title from Frontend to Volto UI. @stevepiercy [#6438](https://github.com/plone/volto/issues/6438)
+
+## 18.0.0-alpha.46 (2024-10-10)
+
+### Breaking
+
+- Added `react/jsx-key` rule for ESlint to detect missing key property in iterators. @sneridagh [#6387](https://github.com/plone/volto/issues/6387)
+
+### Bugfix
+
+- Revert "Fix block chooser search is not focusable when clicked on add button" #5867 @sneridagh
+ It was causing a regression described in #6389 [#6390](https://github.com/plone/volto/issues/6390)
+
+### Internal
+
+- Added missing improvement to the `build-deps` Makefile command. @sneridagh [#6383](https://github.com/plone/volto/issues/6383)
+
+### Documentation
+
+- Add reference to Translate Volto in the main documentation. @stevepiercy [#6386](https://github.com/plone/volto/issues/6386)
+
+## 18.0.0-alpha.45 (2024-10-08)
+
+### Bugfix
+
+- Added missing arg for `buildStyleClassNamesExtenders` @sneridagh [#6381](https://github.com/plone/volto/issues/6381)
+
+### Documentation
+
+- Removed pin on Vale for documentation spelling, grammar, and style checks, and upgrade to v3.x configuration.
+ Updated CI and `Makefile` to allow `VALEOPTS=--no-exit`. @stevepiercy [#6376](https://github.com/plone/volto/issues/6376)
+
+## 18.0.0-alpha.44 (2024-10-03)
+
+### Feature
+
+- Added `config.settings.nonContentRoutesPublic` to avoid `isCmsUi` issues in these public routes. @giuliaghisini [#6173](https://github.com/plone/volto/issues/6173)
+- Add language independent field icon. @iRohitSingh [#6297](https://github.com/plone/volto/issues/6297)
+- Improve DiffField.jsx to render only the blocks on the page with better support for displaying HTML elements such as images. @dobri1408 [#6309](https://github.com/plone/volto/issues/6309)
+- Updated Italian Italian translations. @gianniftp [#6342](https://github.com/plone/volto/issues/6342)
+- Update Brazilian Portuguese translations. @ericof
+
+### Bugfix
+
+- Increase specificity of table header style selector to properly override colors for better contrast @jackahl [#2487](https://github.com/plone/volto/issues/2487)
+- Change Form input:focus text color to the `textColor` value for a11y.
+ Add Cypress test for contact form inputs. @ThomasKindermann @tedw87 [#2570](https://github.com/plone/volto/issues/2570)
+- Add missing `for` attribute to checkbox label to improve accessibility. @gomez [#6249](https://github.com/plone/volto/issues/6249)
+- - Fixed build style classnames in edit mode. Also use buildStyleClassNamesExtenders. @giuliaghisini [#6259](https://github.com/plone/volto/issues/6259)
+- Fetch `user` before pass it to the `restricted` function of the block settings. @wesleybl [#6293](https://github.com/plone/volto/issues/6293)
+- - Join validation errors in one single toast and update errors from response. @cekk
+ - Toast content now has a
wrapper instead of a
. @cekk [#6295](https://github.com/plone/volto/issues/6295)
+- Fixed findBlocks when no blocks are passed. @giuliaghisini [#6299](https://github.com/plone/volto/issues/6299)
+- Fixed toolbar buttons not having a focus outline. @JeffersonBledsoe [#6313](https://github.com/plone/volto/issues/6313)
+- fix: SidebarPopup close on ESC keypress @nileshgulia1 [#6315](https://github.com/plone/volto/issues/6315)
+- Changed sidebar accordion text colour from @teal to @textColor. @JeffersonBledsoe [#6330](https://github.com/plone/volto/issues/6330)
+- Labels accessibility for ArrayWidget, SelectWidget, TokenWidget. @folix-01 [#6332](https://github.com/plone/volto/issues/6332)
+- Use lighter blue as link color in inverted tables to improve contrast for a11y @jackahl [#6334](https://github.com/plone/volto/issues/6334)
+
+### Internal
+
+- Added a pull request template as a checklist before submitting a PR. @stevepiercy [#6365](https://github.com/plone/volto/issues/6365)
+
+### Documentation
+
+- Update references to cookiecutter-plone-starter in docs. @davisagli [#6289](https://github.com/plone/volto/issues/6289)
+- - Revised Cookieplone section in the Upgrade Guide. @stevepiercy [#6290](https://github.com/plone/volto/issues/6290)
+- - Fix the MyST syntax for the label `upgrade-18-cookieplone-label`. @stevepiercy [#6360](https://github.com/plone/volto/issues/6360)
+- Fixed spelling of prerequisites. @stevepiercy [#6362](https://github.com/plone/volto/issues/6362)
+- Fix links to Vite website. @stevepiercy [#6366](https://github.com/plone/volto/issues/6366)
+
+## 18.0.0-alpha.43 (2024-09-13)
+
+### Breaking
+
+- Move `Tags` component to the slot `belowContent`. @wesleybl [#6269](https://github.com/plone/volto/issues/6269)
+
+### Feature
+
+- Refactor AlbumView from class to functional component. @Tishasoumya-02 [#4077](https://github.com/plone/volto/issues/4077)
+- Add setting `unwantedControlPanelsFields` and use it in the function `filterControlPanelsSchema`. @wesleybl [#4819](https://github.com/plone/volto/issues/4819)
+- Refactor the `Register` component from class-based to functional. @Tishasoumya-02 [#4861](https://github.com/plone/volto/issues/4861)
+- Refactor Toolbar/More component from class to functional component. @Tishasoumya-02 [#4955](https://github.com/plone/volto/issues/4955)
+- Refactor Blocks/LeadImage/Edit component from class to functional component. @Tishasoumya-02 [#4959](https://github.com/plone/volto/issues/4959)
+- Refactor Blocks/Video/Edit component from class to functional component. @Tishasoumya-02 [#4960](https://github.com/plone/volto/issues/4960)
+- Refactor ControlPanel/Users/Aliases component , replacing class component lifecycle methods with functional hooks. @Tishasoumya-02 [#4985](https://github.com/plone/volto/issues/4985)
+- Refactor Controlpanel databaseInformation from class component to functional component. @Tishasoumya-02 [#4986](https://github.com/plone/volto/issues/4986)
+- Refactor ControlPanels/Groups RenderGroups from class components to functional component. @Tishasoumya-02 [#4993](https://github.com/plone/volto/issues/4993)
+- Refactor AddonsControlpanel from class to functional components. @Tishasoumya [#4995](https://github.com/plone/volto/issues/4995)
+- Refactor Preference/Change Password from class to functional component. @Tishasoumya-02 [#5044](https://github.com/plone/volto/issues/5044)
+- Refactor Preference/PersonalPreferences from class to functional component. @Tishasoumya-02 [#5045](https://github.com/plone/volto/issues/5045)
+- Refactor Preference/PersonalInformation from Class to Functional component. @Tishasoumya-02 [#5046](https://github.com/plone/volto/issues/5046)
+- Refactor Contents/ContentsUploadModal & Storybook-@Tishasoumya-02 [#5047](https://github.com/plone/volto/issues/5047)
+- Storybook test for ContentBreadcrumbs component. @Tishasoumya [#5048](https://github.com/plone/volto/issues/5048)
+- Storybook Actions component test. @Tishasoumya-02 [#5049](https://github.com/plone/volto/issues/5049)
+- Refactor Display from class to functional component. @Tishasoumya-02 [#5066](https://github.com/plone/volto/issues/5066)
+- Refactor `ReferenceWidget` from class-based to functional component. @Tishasoumya [#5093](https://github.com/plone/volto/issues/5093)
+- Refactor the table of contents block component from a class component to a functional component. @Prince0906 [#6167](https://github.com/plone/volto/issues/6167)
+- Improved URL regex to allow non-public or intranet URLs, such as `https://intranet/` or `file://server/share`. @mamico [#6186](https://github.com/plone/volto/issues/6186)
+- Refactor the `DatetimeWidget` component from a class component to a functional component. @Raman-Luhach [#6213](https://github.com/plone/volto/issues/6213)
+- The schema for the `ContentsPropertiesModal` can be enhanced using the `contentPropertiesSchemaEnhancer` setting.
+ Also, the properties form is now prepopulated with values if all selected items share the same value. @davisagli [#6248](https://github.com/plone/volto/issues/6248)
+- Pass the `user`, `navRoot` and `contentType` objects to the `restricted` function of the block settings. @wesleybl [#6264](https://github.com/plone/volto/issues/6264)
+
+### Bugfix
+
+- Fix search block showing no option select in sort on property @iRohitSingh [#5055](https://github.com/plone/volto/issues/5055)
+- Displays validation error messages on control panel forms. @wesleybl [#5274](https://github.com/plone/volto/issues/5274)
+- Fix `initialValue` block setting. @wesleybl [#5971](https://github.com/plone/volto/issues/5971)
+- When user changes location, set the `userSession.token` value based on cookie. This fixes the login status not being properly determined by the application. @tiberiu-ichim [#6071](https://github.com/plone/volto/issues/6071)
+- Fix `Teaser` block image override option to render external images and internal images pointing to image scales. @Tishasoumya-02 [#6147](https://github.com/plone/volto/issues/6147)
+- Add Cypress test for search block sort on property. @iRohitSingh [#6226](https://github.com/plone/volto/issues/6226)
+- Remove unused i18n message for SortOn component. @davisagli [#6230](https://github.com/plone/volto/issues/6230)
+- Return a 302 response for server-side rendering of the Link view for unauthenticated users. @davisagli [#6235](https://github.com/plone/volto/issues/6235)
+- Fix loading of .cjs in webpack. @davisagli [#6237](https://github.com/plone/volto/issues/6237)
+- fixed change of form.ui.hovered when editing blocks, because if you have a FormBlock component inside another one,
+ onMouseOver fires for all stacked blocks and you cannot use the nested form. @giuliaghisini [#6240](https://github.com/plone/volto/issues/6240)
+- Changed imports from relative to absolute to avoid type errors in add-on tests. @wesleybl [#6244](https://github.com/plone/volto/issues/6244)
+- In the URL Management control panel, allow external URLs as targets. @davisagli [#6247](https://github.com/plone/volto/issues/6247)
+- Disable save button when loading POST query @sabrina-bongiovanni [#6252](https://github.com/plone/volto/issues/6252)
+- Fix `TypeError: values[0] is undefined` in Contents properties modal. @davisagli [#6258](https://github.com/plone/volto/issues/6258)
+- Fix error in `SortOn` component when no sort is selected. @davisagli [#6273](https://github.com/plone/volto/issues/6273)
+- Place actions storybook in correct path. @Tishasoumya-02 [#6275](https://github.com/plone/volto/issues/6275)
+- Remove `Generate tabs for items other than folders` field from navigation control panel. @wesleybl [#6278](https://github.com/plone/volto/issues/6278)
+- Fix error rendering empty grid blocks. @MAX-786 [#6279](https://github.com/plone/volto/issues/6279)
+- Fixed instant error on click on an item of the "Order" tab @sneridagh [#6284](https://github.com/plone/volto/issues/6284)
+
+### Internal
+
+- Refactored the `SelectWidget` component to a functional component. @lorstenoplo [#5570](https://github.com/plone/volto/issues/5570)
+- Allow setting the language for `make backend-docker-start`. Default is `en`. Added usage documentation in Contributing > Develop Volto core, and reorganized it. Cleaned up Configuration > Multilingual. @stevepiercy [#6231](https://github.com/plone/volto/issues/6231)
+- Revert rename of `.gitkeep`. @stevepiercy [#6232](https://github.com/plone/volto/issues/6232)
+- Upgrade Cypress to 13.13.2. @wesleybl [#6241](https://github.com/plone/volto/issues/6241)
+- Complete upgrade Cypress to 13.13.2. Bump actions/upload-artifact@v1 to v4. @stevepiercy [#6242](https://github.com/plone/volto/issues/6242)
+- Add `make i18n` command. @Faakhir30 [#6274](https://github.com/plone/volto/issues/6274)
+- Update to Plone 6.0.13 @sneridagh [#6285](https://github.com/plone/volto/issues/6285)
+
+### Documentation
+
+- Fix redirect of link in documentation to testing Redux store blog post. @stevepiercy [#6239](https://github.com/plone/volto/issues/6239)
+- Fix redirect to Docker documentation. @stevepiercy [#6262](https://github.com/plone/volto/issues/6262)
+- Added upgrade guide notice about CookiePlone as the recommended way for generating projects and add-on boilerplate @sneridagh [#6286](https://github.com/plone/volto/issues/6286)
+
## 18.0.0-alpha.42 (2024-07-30)
### Breaking
@@ -35,7 +836,7 @@ myst:
- Do not send sorting information in the search block if no sort_on setting is configured @erral [#5338](https://github.com/plone/volto/issues/5338)
- Fixed pagination in search results by passing `pageSize` explicitly to all search API calls. @EshaanAgg [#5464](https://github.com/plone/volto/issues/5464)
- Fix the toolbar handler color for the homepage to match its "published" state. @sabrina-bongiovanni [#6126](https://github.com/plone/volto/issues/6126)
-- Allow `ImageWidget` value to be an object and use the `@id` to get the value if present.
+- Allow `ImageWidget` value to be an object and use the `@id` to get the value if present.
This is useful for fields that were using the `object_browser` widget previously to set values. @ichim-david [#6156](https://github.com/plone/volto/issues/6156)
- Persist data for the `backend-docker-start` Docker container in a Docker volume named `volto-backend-data`.
This way the data is persisted between runs of the container.
@@ -251,7 +1052,7 @@ myst:
### Internal
- Allow `Makefile` options to be modified by a `Makefile.local` file if present. @ichim-david [#5997](https://github.com/plone/volto/issues/5997)
-- Modified `locking` and `block-listing` cypress tests
+- Modified `locking` and `block-listing` cypress tests
to use more `assertions` instead of wait times in order to improve
the reliability of the tests. @ichim-david [#5998](https://github.com/plone/volto/issues/5998)
diff --git a/docs/source/theming/index.md b/docs/source/theming/index.md
index dd0743403d..e2577b2c31 100644
--- a/docs/source/theming/index.md
+++ b/docs/source/theming/index.md
@@ -9,6 +9,10 @@ myst:
# Theming
+This section of the documentation describes theming in Volto.
+
+
+## Conceptual guides
```{toctree}
:maxdepth: 1
@@ -17,6 +21,19 @@ about-semantic
semanticui-theming
theming-engine
theming-strategy
+```
+
+
+## How-to guides
+
+```{toctree}
+:maxdepth: 1
+
custom-styling
using-third-party-themes
+theming-a-base-theme
+```
+
+```{seealso}
+For how to create your theme as an add-on in Volto 18 and later, see {doc}`../development/add-ons/theme`.
```
diff --git a/docs/source/theming/theming-a-base-theme.md b/docs/source/theming/theming-a-base-theme.md
new file mode 100644
index 0000000000..f3730dead1
--- /dev/null
+++ b/docs/source/theming/theming-a-base-theme.md
@@ -0,0 +1,46 @@
+---
+myst:
+ html_meta:
+ "description": "Customize a base theme such as Volto Light Theme via SCSS."
+ "property=og:description": "Customize a base theme such as Volto Light Theme via SCSS."
+ "property=og:title": "Theming a base theme such as Volto Light Theme"
+ "keywords": "Volto, Plone, frontend, React, themes, Volto Light Theme"
+---
+
+# Customize a base theme
+
+You can customize a base theme for your add-on.
+The following examples use [Volto Light Theme](https://github.com/kitconcept/volto-light-theme) as a base theme.
+
+
+## File structure
+
+In your Volto add-on's {file}`src` folder, create a subfolder named {file}`theme`.
+Inside {file}`theme` create two empty files named {file}`_main.scss` and {file}`_variables.scss`.
+Refer to the following file system diagram.
+
+```text
+src/
+├── components
+├── index.js
+└── theme
+ ├── _main.scss
+ └── _variables.scss
+```
+
+
+## `_variables.scss`
+
+{file}`_variables.scss` is where you can override SCSS variables of the base theme.
+
+```scss
+$text-color: #000;
+$font-size: 18px;
+$line-height: 24px;
+```
+
+
+## `_main.scss`
+
+{file}`_main.scss` is where you should put all custom styles.
+You can also include other SCSS or CSS files here.
diff --git a/docs/source/tutorials/index.md b/docs/source/tutorials/index.md
index 5eac15479a..4a1ce15a4e 100644
--- a/docs/source/tutorials/index.md
+++ b/docs/source/tutorials/index.md
@@ -18,6 +18,8 @@ On the [Plone Training website](https://training.plone.org), you'll find Volto-d
- [Mastering Plone 6 Development](https://training.plone.org/mastering-plone/)
The comprehensive training on Plone 6 with best practice tips for developers and integrators.
+- [Customizing Volto Light Theme](https://training.plone.org/customizing-volto-light-theme/index.html)
+- [Volto Customization for JavaScript Beginners](https://training.plone.org/volto-customization/index.html)
- [Volto Hands-On](https://training.plone.org/voltohandson/index.html)
- [Volto Add-ons Development](https://training.plone.org/voltoaddons/index.html)
- [Effective Volto](https://training.plone.org/effective-volto/index.html)
@@ -30,7 +32,11 @@ On the [Plone Training website](https://training.plone.org), you'll find Volto-d
You can watch the talk during the World Plone Day 2021:
-
+```{youtube} kHec4MXH8vo
+:privacy_mode:
+:url_parameters: ?privacy_mode=1
+:width: 100%
+```
## Presentations at Plone Conferences (PloneConf) and other events
@@ -40,43 +46,43 @@ In recent years the React based Volto frontend for Plone has been presented in m
### PloneConf 2023
-- [State of Plone Keynote](https://www.youtube.com/watch?v=jl19wuC0wtw&list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=1)
-- [Piero Nicolli - Theming Volto in 2024](https://www.youtube.com/watch?v=LkPOsIn1jYY&list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=6)
-- [Víctor Fernández de Alba - Breaking boundaries: Plone as headless CMS](https://www.youtube.com/watch?v=43LVtjYyo28&list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=7)
-- [Rob Gietema - How to build a site using Nick](https://www.youtube.com/watch?v=ZbdYvNAnamM&list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=18)
-- [Alok Kumar - Is your Volto add-on developer friendly?](https://www.youtube.com/watch?v=E6fH3NhR2Hc&list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=20)
-- [Dylan Jay and Jefferson Bledsoe - How to implement a Gov Design System in Plone 6](https://www.youtube.com/watch?v=_XmKc7jNIE8&list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=25)
-- [Víctor Fernández de Alba - Volto-light-theme: Volto Theming, Reimagined](https://www.youtube.com/watch?v=t2X2NO62J-8)
+- [State of Plone Keynote](https://www.youtube-nocookie.com/embed/jl19wuC0wtw?list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=1&privacy_mode=1)
+- [Piero Nicolli - Theming Volto in 2024](https://www.youtube-nocookie.com/embed/LkPOsIn1jYY?list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=6&privacy_mode=1)
+- [Víctor Fernández de Alba - Breaking boundaries: Plone as headless CMS](https://www.youtube-nocookie.com/embed/43LVtjYyo28?list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=7&privacy_mode=1)
+- [Rob Gietema - How to build a site using Nick](https://www.youtube-nocookie.com/embed/ZbdYvNAnamM?list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=18&privacy_mode=1)
+- [Alok Kumar - Is your Volto add-on developer friendly?](https://www.youtube-nocookie.com/embed/E6fH3NhR2Hc?list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=20&privacy_mode=1)
+- [Dylan Jay and Jefferson Bledsoe - How to implement a Gov Design System in Plone 6](https://www.youtube-nocookie.com/embed/_XmKc7jNIE8?list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=25&privacy_mode=1)
+- [Víctor Fernández de Alba - Volto-light-theme: Volto Theming, Reimagined](https://www.youtube-nocookie.com/embed/t2X2NO62J-8)
### PloneConf 2022
-[PloneConf 2022 full Playlist on Youtube](https://www.youtube.com/playlist?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
+[PloneConf 2022 full Playlist on Youtube](https://www.youtube-nocookie.com/embed/playlist?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
Plone 6 site presentations:
-- [Rai Way: Plone6 supporting the world of italian information, sports and entertainment](https://www.youtube.com/watch?v=hHHGlSjf5O4&list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
-- [How Plone Powers Hundreds of Websites at one of the Largest Research Institutions in Europe](https://www.youtube.com/watch?v=bxWt-GEmPcc&list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
+- [Rai Way: Plone6 supporting the world of italian information, sports and entertainment](https://www.youtube-nocookie.com/embed/hHHGlSjf5O4?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
+- [How Plone Powers Hundreds of Websites at one of the Largest Research Institutions in Europe](https://www.youtube-nocookie.com/embed/bxWt-GEmPcc?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
Developer/integrator talks:
-- [Anatomy of a Volto project](https://www.youtube.com/watch?v=JtNufyFlgc8&list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
-- [Creating a Volto Theme](https://www.youtube.com/watch?v=AMHN74Jr27Y&list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
-- [A Deep Dive Into Internals Of Volto](https://www.youtube.com/watch?v=sMeTDRgp3uI&list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
-- [DevOps Bird's Eye View on the Plone 6 Backend](https://www.youtube.com/watch?v=L5PvGwWC9P4&list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
+- [Anatomy of a Volto project](https://www.youtube-nocookie.com/embed/JtNufyFlgc8?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
+- [Creating a Volto Theme](https://www.youtube-nocookie.com/embed/AMHN74Jr27Y?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
+- [A Deep Dive Into Internals Of Volto](https://www.youtube-nocookie.com/embed/sMeTDRgp3uI?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
+- [DevOps Bird's Eye View on the Plone 6 Backend](https://www.youtube-nocookie.com/embed/L5PvGwWC9P4?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
### Previous PloneConfs
-- [PloneConf 2021 full Playlist on YouTube](https://www.youtube.com/playlist?list=PLGN9BI-OAQkQDLQinBwdEXpebDTQCwdGi)
-- [PloneConf 2020 full Playlist on YouTube](https://www.youtube.com/playlist?list=PLGN9BI-OAQkTJPayNdKIZ8lLDm5RVOLV3)
+- [PloneConf 2021 full Playlist on YouTube](https://www.youtube-nocookie.com/embed/playlist?list=PLGN9BI-OAQkQDLQinBwdEXpebDTQCwdGi&privacy_mode=1)
+- [PloneConf 2020 full Playlist on YouTube](https://www.youtube-nocookie.com/embed/playlist?list=PLGN9BI-OAQkTJPayNdKIZ8lLDm5RVOLV3&privacy_mode=1)
## World Plone Day 2022
-World Plone Day is a 24-hour streaming event, with the goal was to promote and educate the public about the benefits of using Plone and of being part of the Plone community. [Full World Plone Day 2022 playlist on Youtube](https://www.youtube.com/playlist?list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL)
+World Plone Day is a 24-hour streaming event, with the goal was to promote and educate the public about the benefits of using Plone and of being part of the Plone community. [Full World Plone Day 2022 playlist on Youtube](https://www.youtube-nocookie.com/embed/playlist?list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&privacy_mode=1)
-- [Plone 6 Volto's Seamless Mode](https://www.youtube.com/watch?v=Mj8pHRBls-w&list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL)
-- [Volto add ons separator and carousel](https://www.youtube.com/watch?v=eyTMI5TYcVg&list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL)
-- [Weekly Volto Live – Retrospective](https://www.youtube.com/watch?v=WT6OjkSrB20&list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL)
-- [Migrating from Classic to Volto](https://www.youtube.com/watch?v=09fg456T90s&list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL)
+- [Plone 6 Volto's Seamless Mode](https://www.youtube-nocookie.com/embed/Mj8pHRBls-w?list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&privacy_mode=1)
+- [Volto add ons separator and carousel](https://www.youtube-nocookie.com/embed/eyTMI5TYcVg?list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&privacy_mode=1)
+- [Weekly Volto Live – Retrospective](https://www.youtube-nocookie.com/embed/WT6OjkSrB20?list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&privacy_mode=1)
+- [Migrating from Classic to Volto](https://www.youtube-nocookie.com/embed/09fg456T90s?list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&privacy_mode=1)
diff --git a/docs/source/upgrade-guide/index.md b/docs/source/upgrade-guide/index.md
index b199673aea..23e2903a18 100644
--- a/docs/source/upgrade-guide/index.md
+++ b/docs/source/upgrade-guide/index.md
@@ -28,6 +28,19 @@ Thus it is safe to run it on top of your project and answer the prompts.
## Upgrading to Volto 18.x.x
+### Node.js version support: adding 22, dropping 18
+
+Added support for Node.js 22.
+It is the long-term support (LTS) release effective 2024-10-29.
+
+Long-term support for {term}`Node.js` 18 by the Node.js community will end on 2025-04-30.
+Volto 18 no longer supports Node.js 18, since Volto only supports the latest two LTS versions.
+Volto might continue to work on Node.js 18, but it is not tested in CI.
+
+Please update your projects to a supported Node.js version, either 20 or 22.
+Version 22 is recommended, as the latest LTS version of Node.js.
+
+
### Volto's internal `dependencies` and `devDependencies` are now properly sorted out
Volto internal `dependencies` and `devDependencies` have been correctly sorted out.
@@ -40,7 +53,7 @@ For this purpose, we have developed a {ref}`new utility {
+ const config = modifyWebpackConfig({
+ env: { target, dev },
+ webpackConfig,
+ webpackObject,
+ options,
++ paths,
+ });
+ // add custom code here..
+ return config;
+```
+
+The change involves adding a new `paths` argument to the `customModifyWebpackConfig` function.
+
+### Added rule for ESlint to detect missing key property in iterators.
+
+The `react/jsx-key` rule has been enabled in ESlint for catching missing `key` in JSX iterators.
+You might catch some violations in your project or add-on code after running ESlint.
+Adding the missing `key` property whenever the violation is reported will fix it.
+
+### `@plone/registry` moved to ESM
+
+The `@plone/registry` package has been moved to ESM.
+The add-on registry scripts have also been refactored to TypeScript.
+For maximum compatibility with CommonJS builds, the default exports have been moved to named exports.
+The modules affected are now built, and the import paths have changed, too.
+These changes force some import path changes that you should patch in your Plone project or add-on boilerplates.
+
+```{note}
+As always, when something changes in the boilerplate, you may regenerate one from Cookieplone and move your code into it, instead of fiddling with it.
+```
+
+For example, in your project's {file}`.eslintrc.js`:
+
+```diff
+ const fs = require('fs');
+ const projectRootPath = __dirname;
+-const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry');
++const { AddonRegistry } = require('@plone/registry/addon-registry');
+
+ let voltoPath = './node_modules/@plone/volto';
+
+@@ -17,15 +17,15 @@ if (configFile) {
+ voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`;
+ }
+
+-const reg = new AddonConfigurationRegistry(__dirname);
++const { registry } = AddonRegistry.init(__dirname);
+
+ // Extends ESlint configuration for adding the aliases to `src` directories in Volto addons
+-const addonAliases = Object.keys(reg.packages).map((o) => [
++const addonAliases = Object.keys(registry.packages).map((o) => [
+ o,
+- reg.packages[o].modulePath,
++ registry.packages[o].modulePath,
+ ]);
+
+-const addonExtenders = reg.getEslintExtenders().map((m) => require(m));
++const addonExtenders = registry.getEslintExtenders().map((m) => require(m));
+```
+
+Also in the Storybook configuration {file}`.storybook/main.js`.
+
+```diff
+- const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry');
++ const { AddonRegistry } = require('@plone/registry/addon-registry');
+
+- const registry = new AddonConfigurationRegistry(projectRootPath);
++ const { registry } = AddonRegistry.init(projectRootPath);
+```
+
+```{versionadded} Volto 18.0.0-alpha.47
+```
+
+```{versionadded} @plone/registry 3.0.0-alpha.0
+```
+
+### Add missing overrides to projects in `package.json`
+
+This will fix some issues with Hot Module Reload in projects.
+It's required in Volto `18.0.0-alpha.47` and later, otherwise the site breaks in development mode.
+Add this object to the `pnpm` key in your project {file}`package.json`.
+
+```json
+ "pnpm": {
+ "overrides": {
+ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
+ "react-refresh": "^0.14.2"
+ }
+ },
+```
+
+```{versionadded} Volto 18.0.0-alpha.47
+```
+
+```{versionadded} @plone/registry 3.0.0-alpha.0
+```
+
+### Deprecation notices for Volto 18
+
+#### `@plone/generator-volto`
+
+```{deprecated} Volto 18.0.0
+```
+
+The Node.js-based Volto project boilerplate generator is deprecated from Volto 18 onwards.
+After the release of Volto 18, it will be marked as deprecated, archived, and it won't receive any further updates.
+Although you can still migrate your project to Volto 18 using this boilerplate, you should migrate to using [Cookieplone](https://github.com/plone/cookieplone).
+
+##### Alternative
+
+Migrate your project to use a [Cookieplone](https://github.com/plone/cookieplone) boilerplate.
+
+#### Volto project configurations
+
+```{deprecated} Volto 18.0.0
+```
+
+Configuring Volto using {file}`src/config.js` at the project level is deprecated in Volto 18, and will be removed in Volto 19.
+
+```{seealso}
+See https://github.com/plone/volto/issues/6396 for details.
+```
+
+##### Alternative
+
+You should configure your projects in a policy add-on.
+You can move your project to use [Cookieplone](https://github.com/plone/cookieplone) which provides the necessary boilerplate for it.
+
+#### Semantic UI
+
+```{deprecated} Volto 18.0.0
+```
+
+The Semantic UI library is not maintained anymore, and will be removed in Plone 7.
+You should no longer use Semantic UI in add-ons and projects.
+
+```{seealso}
+Related PLIPs:
+
+- https://github.com/plone/volto/issues/6321
+- https://github.com/plone/volto/issues/6323
+```
+
+##### Alternatives
+
+You can use any supported component framework of your choice for implementing new components, especially in the public theme side.
+If you create new widgets or components for the CMSUI—in other words, the non-public side—you should use the [`@plone/components`](https://github.com/plone/volto/tree/main/packages/components) library as an alternative.
+Even though it's still in the development phase, it will be completed in the next few months, and will be supported in the future.
+
+#### `lodash` library
+
+```{deprecated} Volto 18.0.0
+```
+
+`lodash` is deprecated in Volto 18, and will be removed in Plone 7.
+
+`lodash` has not received any updates since 2021.
+It has performance issues from bloated bundles and it's not prepared for ESM.
+Lots of `lodash` utility helpers can be replaced with vanilla ES.
+These issues cause concern about its future maintainability.
+
+In place of `lodash`, Plone 7 will use both the `lodash-es` library, which is ESM ready, and modern vanilla ES alternatives whenever possible.
+
+##### Alternatives
+
+```{seealso}
+The following links suggest alternatives to `lodash`.
+
+- https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore
+- https://javascript.plainenglish.io/you-dont-need-lodash-how-i-gave-up-lodash-693c8b96a07c
+
+If you still need some of the utilities in `lodash` and cannot use vanilla ES, you can use `lodash-es` instead.
+```
+
+#### `@loadable/component` and Volto `Loadables` framework
+
+```{deprecated} Volto 18.0.0
+```
+
+`@loadable/component` and the Volto `Loadables` framework is deprecated in Volto 18, and will be removed from Plone 7.
+It's a Webpack-only library, and it does not have Vite plugin support.
+Since React 18, this library is no longer necessary, as it has the initial implementation of the "concurrent mode".
+React 19 will further improve it and add more features around it.
+
+##### Alternatives
+
+Use plain React 18 lazy load features and its idioms for lazy load components.
+
+```jsx
+const myLazyComponent = lazy(()=> import('@plone/volto/components/theme/MyLazyComponent/MyLazyComponent'))
+
+const RandomComponent = (props) => (
+
+
+
+)
+```
+
+There's no support for pre-loading or lazy loading entire libraries as in `@loadable/component`.
+With the removal of barrel imports files, as described in the next deprecation notice, it is now unnecessary.
+
+#### Removal of barrel import files
+
+```{deprecated} Volto 18.0.0
+```
+
+Volto previously used barrel imports, which are centralized files where other imports are re-exported, to improve the developer user experience.
+With barrel imports, a developer only needs to remember to import from the re-exported place, not the full path.
+
+Since the barrel imports directly import all the code, a lot of imports ended up in the same main chunk of code.
+It became a bad practice.
+Modern bundlers, such as Vite, rely upon the import path to determine whether to bundle code together or not, reducing the bundle size.
+
+The barrel imports must be removed to increase the natural number of chunks that Volto divides on—especially on routes—resulting in code splitting done the right and natural way.
+This forces us to rewrite all the imports everywhere—including core, projects, and add-ons—once we implement it.
+The barrel imports files include the following in Volto.
+
+- {file}`src/components/index.js`
+- {file}`src/helpers/index.js`
+- {file}`src/actions/index.js`
+- {file}`src/hooks/index.js`
+
+They also include the following in the `@plone/volto-slate` package.
+
+- {file}`src/actions/index.js`
+- {file}`src/editor/ui/index.js`
+- {file}`src/utils/index.js`
+
+##### Alternative
+
+Implement only direct imports in code, preparing now for the upcoming change.
+
+```diff
+-import { BodyClass } from '@plone/volto/helpers';
++import BodyClass from '@plone/volto/helpers/BodyClass/BodyClass';
+```
+
+Once this is implemented, a code modification will be provided for a smooth migration.
+
+
(volto-upgrade-guide-17.x.x)=
## Upgrading to Volto 17.x.x
@@ -501,7 +799,7 @@ Upgrade your local dependencies in projects and add-ons by editing your {file}`p
### TypeScript support in Volto
-```{versionadded} 17.0.0-alpha.27
+```{versionadded} Volto 17.0.0-alpha.27
```
We added full support of TypeScript in Volto core.
@@ -604,7 +902,7 @@ make develop
### Upgrade ESlint and use `@babel/eslint-parser`
-```{versionchanged} 17.0.0-alpha.27
+```{versionchanged} Volto 17.0.0-alpha.27
```
ESlint uses a library to parse the language under analysis.
@@ -617,7 +915,7 @@ Once upgraded, run the linters again to make sure that your code is free of viol
Technically not a breaking, the API nor the component contract has changed, but it's worth noting this change in here.
-```{versionadded} 17.0.0-alpha.1
+```{versionadded} Volto 17.0.0-alpha.1
The `BlockChooser` component now uses `popperjs` library to position itself in the screen.
It spawns at the end of the body instead of inner the block that called it.
This is better from the UI point of view, since any other element can take precedence in the CSS element flow, preventing the block chooser to get overlapped by anything else.
@@ -671,11 +969,11 @@ This is because the overrides that `@testing-library/cypress` introduce can be r
Since there are some commands that can call exports in {file}`cypress/support/commands.js`, this import may be run more than once, and then it errors.
So you have to make sure that import is run only once while the tests are run.
-Check the official [Cypress Migration Guide](https://docs.cypress.io/guides/references/migration-guide) for more information.
+Check the official [Cypress Migration Guide](https://docs.cypress.io/app/references/migration-guide) for more information.
### New Image component
-```{versionadded} 17.0.0-alpha.21
+```{versionadded} Volto 17.0.0-alpha.21
A new image component has been added to core to render optimized images.
It requires the latest version of `plone.restapi` (>=8.42.0) installed in the backend to work properly.
```
@@ -727,11 +1025,11 @@ From Volto 16.0.0-alpha.15 onwards, `volto-slate` is integrated into Volto core
The previous text block `text` based on `draftJS` is now deprecated and is restricted (hidden) in Volto bootstrap.
This is a major change and should be planned in advance before you install 16.0.0-alpha.15 or later.
-```{versionadded} 16.0.0-alpha.15
+```{versionadded} Volto 16.0.0-alpha.15
`volto-slate` added to Volto core as the default text block.
```
-```{deprecated} 16.0.0-alpha.15
+```{deprecated} Volto 16.0.0-alpha.15
`text` text block based on `draftJS` is now deprecated and will be removed from core in Volto 18.
```
@@ -825,7 +1123,7 @@ Version 16 is recommended.
### Upgraded to Razzle 4
-```{versionadded} 16.0.0-alpha.38
+```{versionadded} Volto 16.0.0-alpha.38
Volto has upgraded from Razzle 3 to Razzle 4.
You should perform these steps in case you are upgrading to this version or above.
```
@@ -1063,7 +1361,7 @@ If you have already updated your configuration to use Cypress 10 or later in a p
It is possible that forcing your project to use older versions might still work with old configurations.
```{seealso}
-See https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-version-10-0 for more information.
+See https://docs.cypress.io/app/references/migration-guide#Migrating-to-Cypress-100 for more information.
```
### The complete configuration registry is passed to the add-ons and the project configuration pipeline
@@ -1128,7 +1426,7 @@ It maintains signature compatibility with `registry.resolve`, but introduces new
See documentation for more information.
-````{versionchanged} 16.0.0-alpha.23
+````{versionchanged} Volto 16.0.0-alpha.23
The `component` argument changed in 16.0.0-alpha.23.
The `component` key has been flattened for simplification and now it's mapped directly to the `component` argument of `registerComponent`:
@@ -1207,7 +1505,7 @@ During the alpha stage, we received feedback and determined that it's too diffic
We decided it is best to deal with it as any other schema field and enhance it via schema enhancers.
This improves the developer experience, especially when dealing with variations that can provide their own styles and other schema fields.
-```{deprecated} 16.0.0-alpha.46
+```{deprecated} Volto 16.0.0-alpha.46
The options `enableStyling` and `stylesSchema` no longer work.
You need to provide them using your own block schema.
If you are extending an existing one, you should add it as a normal `schemaEnhancer` modification.
@@ -1224,7 +1522,7 @@ Since then, the add-on story has improved.
It now makes sense to extract this feature into its own add-on.
Integrate Sentry in your app with [`@plone-collective/volto-sentry`](https://www.npmjs.com/package/@plone-collective/volto-sentry).
-```{versionchanged} 16.0.0.alpha.45
+```{versionchanged} Volto 16.0.0.alpha.45
```
### Upgrade `husky` to latest version
@@ -1245,7 +1543,7 @@ You can copy over the Volto ones (take a look in Volto's `.husky` folder).
### Better defaults handling
-````{versionadded} 16.0.0-alpha.51
+````{versionadded} Volto 16.0.0-alpha.51
Prior to this version, we handled default values in schemas for blocks settings in a faulty and buggy manner.
The state inferred was not deterministic and depended on the fields with defaults present.
@@ -1361,7 +1659,7 @@ A new `@root` alias has been set up to replace the `~` alias.
Support for the `~` alias is still in place, but we now mark it as deprecated.
The use of `~` will be removed in Volto 16.
-```{deprecated} 15.0
+```{deprecated} Volto 15.0
```
## Upgrading to Volto 14.x.x
diff --git a/docs/source/user-manual/copy-paste-blocks.md b/docs/source/user-manual/copy-paste-blocks.md
index f15f3b1616..4fbc68e769 100644
--- a/docs/source/user-manual/copy-paste-blocks.md
+++ b/docs/source/user-manual/copy-paste-blocks.md
@@ -28,6 +28,7 @@ This will select all the blocks between the start and end blocks, allowing you t
````{only} not text
```{video} ../_static/user-manual/blocks/block-copy-cut.mp4
+:alt: Copy or cut a block in Volto
```
````
@@ -44,5 +45,6 @@ Also if you hold the {kbd}`ctrl` key while clicking the paste button, it keeps t
````{only} not text
```{video} ../_static/user-manual/blocks/block-paste.mp4
+:alt: Paste a block in Volto
```
````
diff --git a/package.json b/package.json
index c5be31bcc6..1cf8ebd0e3 100644
--- a/package.json
+++ b/package.json
@@ -27,23 +27,25 @@
"lockhook": "node packages/scripts/lockhook.js",
"prepare": "husky",
"husky:uninstall": "husky uninstall",
- "prereleaser": "node packages/scripts/preleaser.js"
+ "prereleaser": "node packages/scripts/preleaser.js",
+ "update:browserlist": "pnpm --filter @plone/volto add caniuse-lite && pnpm --filter @plone/volto remove caniuse-lite"
},
"devDependencies": {
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
"concurrently": "^8.2.2",
"husky": "9.0.11",
"lint-staged": "15.2.2",
"tsconfig": "workspace:*",
- "typescript": "^5.4.5",
- "vitest": "^1.5.0"
+ "typescript": "^5.6.3",
+ "vitest": "^2.1.3"
},
"packageManager": "pnpm@9.1.1",
"pnpm": {
"overrides": {
"@pmmmwh/react-refresh-webpack-plugin": "0.5.11",
"react-refresh": "0.14.0"
+ },
+ "patchedDependencies": {
+ "jest-resolve@26.6.2": "patches/jest-resolve@26.6.2.patch"
}
}
}
diff --git a/packages/blocks/.npmignore b/packages/blocks/.npmignore
index 0d8afd5727..a6d10baa1e 100644
--- a/packages/blocks/.npmignore
+++ b/packages/blocks/.npmignore
@@ -2,7 +2,5 @@ news
towncrier.toml
.changelog.draft
node_modules/
-.parcel-cache
-.parcelrc
.release-it.json
.eslintrc.js
diff --git a/packages/blocks/README.md b/packages/blocks/README.md
index 019efc997b..f5fe18bc4f 100644
--- a/packages/blocks/README.md
+++ b/packages/blocks/README.md
@@ -5,3 +5,8 @@ This package is in alpha stages, experimental, under heavy development, and is s
It's one of the Plone frontend strategic packages and part of the headless CMS story.
`@plone/blocks` is not part of Volto, and thus, is not used by it.
+
+> [!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/blocks/package.json b/packages/blocks/package.json
index 3b945309d6..05d801297d 100644
--- a/packages/blocks/package.json
+++ b/packages/blocks/package.json
@@ -29,11 +29,6 @@
"access": "public"
},
"main": "src/index.ts",
- "targets": {
- "main": {
- "includeNodeModules": false
- }
- },
"scripts": {
"test": "vitest",
"dry-release": "release-it --dry-run",
@@ -52,16 +47,13 @@
},
"dependencies": {},
"devDependencies": {
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
"@plone/registry": "workspace:*",
"@plone/types": "workspace:*",
"@types/react": "^18",
"@types/react-dom": "^18",
- "parcel": "^2.12.0",
"release-it": "17.1.1",
"tsconfig": "workspace:*",
- "typescript": "5.4.2",
- "vitest": "^1.3.1"
+ "typescript": "^5.6.3",
+ "vitest": "^2.1.3"
}
}
diff --git a/packages/client/.parcelrc b/packages/client/.parcelrc
deleted file mode 100644
index db2d15099d..0000000000
--- a/packages/client/.parcelrc
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "extends": "@parcel/config-default",
- "transformers": {
- "*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [
- "@parcel/transformer-js",
- "@parcel/transformer-react-refresh-wrap"
- ]
- }
-}
diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md
index b609e68f8e..57c72746b1 100644
--- a/packages/client/CHANGELOG.md
+++ b/packages/client/CHANGELOG.md
@@ -8,6 +8,38 @@
+## 1.0.0-alpha.20 (2024-11-05)
+
+### Internal
+
+- Bump local `typescript` version. @sneridagh [#6461](https://github.com/plone/volto/pull/6461)
+- Replace `parcel` with `tsup`. @sneridagh [#6466](https://github.com/plone/volto/pull/6466)
+
+## 1.0.0-alpha.19 (2024-10-18)
+
+### Feature
+
+- Add support for the `depth` parameter on `getNavigationQuery`. @ebrehault [#6400](https://github.com/plone/volto/pull/6400)
+
+### Internal
+
+- Upgrade `@tanstack/react-query`. @sneridagh [#6069](https://github.com/plone/volto/pull/6069)
+- Update Vite and vitest versions @sneridagh [#6373](https://github.com/plone/volto/pull/6373)
+- Added watch script to package.json @pnicolli @deodorhunter [#6382](https://github.com/plone/volto/pull/6382)
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/pull/6407)
+
+## 1.0.0-alpha.18 (2024-10-03)
+
+### Bugfix
+
+- Fixed client copy mutation, cleanup up move mutation for consistency @pnicolli [#6349](https://github.com/plone/volto/pull/6349)
+
+## 1.0.0-alpha.17 (2024-09-13)
+
+### Documentation
+
+- Add documentation about optional `token` parameter for `ploneClient` initialization. @MAX-786 [#6076](https://github.com/plone/volto/pull/6076)
+
## 1.0.0-alpha.16 (2024-06-06)
### Bugfix
diff --git a/packages/client/news/6076.documentation b/packages/client/news/6076.documentation
deleted file mode 100644
index 138045af4e..0000000000
--- a/packages/client/news/6076.documentation
+++ /dev/null
@@ -1 +0,0 @@
-Add documentation about optional `token` parameter for `ploneClient` initialization. @MAX-786
\ No newline at end of file
diff --git a/packages/client/package.json b/packages/client/package.json
index e3563d194d..327f15c87c 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -8,7 +8,7 @@
}
],
"license": "MIT",
- "version": "1.0.0-alpha.16",
+ "version": "1.0.0-alpha.20",
"repository": {
"type": "git",
"url": "git@github.com:plone/volto.git"
@@ -19,20 +19,15 @@
"type": "module",
"files": [
"dist",
- "src",
"README.md"
],
- "source": "./src/index.ts",
- "main": "./dist/index.cjs",
- "module": "./dist/index.js",
- "types": "./dist/index.d.ts",
+ "main": "./dist/index.js",
"exports": {
+ "./package.json": "./package.json",
".": {
- "types": "./dist/index.d.ts",
"import": "./dist/index.js",
- "require": "./dist/index.cjs"
- },
- "./src/*": "./src/*"
+ "default": "./dist/index.cjs"
+ }
},
"homepage": "https://plone.org",
"keywords": [
@@ -46,10 +41,11 @@
"react-query"
],
"scripts": {
- "build": "parcel build",
- "build:force": "parcel build --no-cache",
+ "build": "tsup",
+ "build:force": "tsup",
"test": "vitest",
- "check-ts": "tsc --project tsconfig.json",
+ "check:exports": "attw --pack .",
+ "check:ts": "tsc --project tsconfig.json",
"coverage": "vitest run --coverage --no-threads",
"dry-release": "release-it --dry-run",
"release": "release-it",
@@ -60,13 +56,7 @@
"access": "public"
},
"devDependencies": {
- "@parcel/config-default": "^2.12.0",
- "@parcel/core": "^2.12.0",
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-js": "^2.12.0",
- "@parcel/transformer-react-refresh-wrap": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
- "@parcel/optimizer-terser": "2.12.0",
+ "@arethetypeswrong/cli": "^0.16.4",
"@plone/types": "workspace: *",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "13.4.0",
@@ -78,20 +68,19 @@
"@vitest/coverage-v8": "^1.3.1",
"glob": "7.1.6",
"jsdom": "^21.1.1",
- "parcel": "^2.12.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"release-it": "17.1.1",
- "tsup": "^8.0.2",
- "typescript": "5.4.2",
+ "tsup": "^8.3.5",
+ "typescript": "^5.6.3",
"uuid": "^9.0.1",
- "vite": "^5.1.7",
+ "vite": "^5.4.8",
"vite-plugin-dts": "^3.7.3",
- "vitest": "^1.3.1",
+ "vitest": "^2.1.3",
"wait-on": "^7.2.0"
},
"dependencies": {
- "@tanstack/react-query": "^5.37.1",
+ "@tanstack/react-query": "^5.59.0",
"axios": "^1.6.7",
"debug": "4.3.4",
"query-string": "^9.0.0",
diff --git a/packages/client/src/API.ts b/packages/client/src/API.ts
index 5862f3084d..12e53ff42e 100644
--- a/packages/client/src/API.ts
+++ b/packages/client/src/API.ts
@@ -1,5 +1,5 @@
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
-import { PloneClientConfig } from './validation/config';
+import type { PloneClientConfig } from './validation/config';
import qs from 'query-string';
import debugFactory from 'debug';
diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts
index 93fdb7e4b6..b0d5e5289a 100644
--- a/packages/client/src/client.ts
+++ b/packages/client/src/client.ts
@@ -109,7 +109,7 @@ import {
queryWithConfig,
mutationHookFromMutation,
} from './utils/misc';
-import { PloneClientConfig } from './validation/config';
+import type { PloneClientConfig } from './validation/config';
const PLONECLIENT_DEFAULT_CONFIG = { apiPath: 'http://localhost:8080/Plone' };
diff --git a/packages/client/src/restapi/actions/get.ts b/packages/client/src/restapi/actions/get.ts
index 5ad55b06b3..7d9ffd3c8b 100644
--- a/packages/client/src/restapi/actions/get.ts
+++ b/packages/client/src/restapi/actions/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { ActionsResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { ActionsResponse } from '@plone/types';
import { z } from 'zod';
const getActionsSchema = z.object({
diff --git a/packages/client/src/restapi/addons/get.ts b/packages/client/src/restapi/addons/get.ts
index f4ce7c67cb..2fdf5669d3 100644
--- a/packages/client/src/restapi/addons/get.ts
+++ b/packages/client/src/restapi/addons/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetAddonResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetAddonResponse } from '@plone/types';
import { z } from 'zod';
const getAddonSchema = z.object({
diff --git a/packages/client/src/restapi/addons/get_list.ts b/packages/client/src/restapi/addons/get_list.ts
index 16a9b54f59..f283e21c38 100644
--- a/packages/client/src/restapi/addons/get_list.ts
+++ b/packages/client/src/restapi/addons/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { Addons } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { Addons } from '@plone/types';
export type AddonsArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/addons/install.ts b/packages/client/src/restapi/addons/install.ts
index 39d1cd516a..1e0812fe00 100644
--- a/packages/client/src/restapi/addons/install.ts
+++ b/packages/client/src/restapi/addons/install.ts
@@ -1,5 +1,5 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
const installAddonSchema = z.object({
diff --git a/packages/client/src/restapi/addons/install_profile.ts b/packages/client/src/restapi/addons/install_profile.ts
index eff76e0bb8..3be0e2a5bd 100644
--- a/packages/client/src/restapi/addons/install_profile.ts
+++ b/packages/client/src/restapi/addons/install_profile.ts
@@ -1,5 +1,5 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
const installAddonProfileSchema = z.object({
diff --git a/packages/client/src/restapi/addons/unistall.ts b/packages/client/src/restapi/addons/unistall.ts
index ca4033a1a4..e0b480a4a5 100644
--- a/packages/client/src/restapi/addons/unistall.ts
+++ b/packages/client/src/restapi/addons/unistall.ts
@@ -1,5 +1,5 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
const uninstallAddonSchema = z.object({
diff --git a/packages/client/src/restapi/addons/upgrade.ts b/packages/client/src/restapi/addons/upgrade.ts
index f1275b53cb..b75f0400d6 100644
--- a/packages/client/src/restapi/addons/upgrade.ts
+++ b/packages/client/src/restapi/addons/upgrade.ts
@@ -1,5 +1,5 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
const upgradeAddonSchema = z.object({
diff --git a/packages/client/src/restapi/aliases/add.ts b/packages/client/src/restapi/aliases/add.ts
index 07e9eee3ff..2d9bfd80ca 100644
--- a/packages/client/src/restapi/aliases/add.ts
+++ b/packages/client/src/restapi/aliases/add.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createAliasesDataSchema } from '../../validation/aliases';
diff --git a/packages/client/src/restapi/aliases/add_multiple.ts b/packages/client/src/restapi/aliases/add_multiple.ts
index 2cd990bc41..77c1c6a518 100644
--- a/packages/client/src/restapi/aliases/add_multiple.ts
+++ b/packages/client/src/restapi/aliases/add_multiple.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createAliasesMultipleDataSchema } from '../../validation/aliases';
diff --git a/packages/client/src/restapi/aliases/delete.ts b/packages/client/src/restapi/aliases/delete.ts
index af2401daf8..73d408cc7b 100644
--- a/packages/client/src/restapi/aliases/delete.ts
+++ b/packages/client/src/restapi/aliases/delete.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { deleteAliasesDataSchema } from '../../validation/aliases';
diff --git a/packages/client/src/restapi/aliases/get.ts b/packages/client/src/restapi/aliases/get.ts
index 7919d859a4..e535175537 100644
--- a/packages/client/src/restapi/aliases/get.ts
+++ b/packages/client/src/restapi/aliases/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetAliasesResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetAliasesResponse } from '@plone/types';
import { z } from 'zod';
const getAliasesSchema = z.object({
diff --git a/packages/client/src/restapi/aliases/get_list.ts b/packages/client/src/restapi/aliases/get_list.ts
index a19d8e193f..767325feb1 100644
--- a/packages/client/src/restapi/aliases/get_list.ts
+++ b/packages/client/src/restapi/aliases/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetAliasesListResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetAliasesListResponse } from '@plone/types';
export type AliasesListArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/breadcrumbs/get.ts b/packages/client/src/restapi/breadcrumbs/get.ts
index b882abdc7a..65a8e1095a 100644
--- a/packages/client/src/restapi/breadcrumbs/get.ts
+++ b/packages/client/src/restapi/breadcrumbs/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { BreadcrumbsResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { BreadcrumbsResponse } from '@plone/types';
import { z } from 'zod';
const getBreadcrumbsSchema = z.object({
diff --git a/packages/client/src/restapi/comments/add.ts b/packages/client/src/restapi/comments/add.ts
index 601ec9f2a8..961c7c092c 100644
--- a/packages/client/src/restapi/comments/add.ts
+++ b/packages/client/src/restapi/comments/add.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { newCommentDataSchema as createCommentDataSchema } from '../../validation/comments';
diff --git a/packages/client/src/restapi/comments/delete.ts b/packages/client/src/restapi/comments/delete.ts
index 70c0353960..403999308c 100644
--- a/packages/client/src/restapi/comments/delete.ts
+++ b/packages/client/src/restapi/comments/delete.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/comments/get.ts b/packages/client/src/restapi/comments/get.ts
index 3d225e39e8..9495f96354 100644
--- a/packages/client/src/restapi/comments/get.ts
+++ b/packages/client/src/restapi/comments/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetCommentsResponse } from '@plone/types';
+import type { GetCommentsResponse } from '@plone/types';
const getCommentsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/comments/update.ts b/packages/client/src/restapi/comments/update.ts
index a0cde49faa..32a3efd4b5 100644
--- a/packages/client/src/restapi/comments/update.ts
+++ b/packages/client/src/restapi/comments/update.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { newCommentDataSchema as updateCommentDataSchema } from '../../validation/comments';
diff --git a/packages/client/src/restapi/content/add.test.tsx b/packages/client/src/restapi/content/add.test.tsx
index f62c221efe..4d65805d6c 100644
--- a/packages/client/src/restapi/content/add.test.tsx
+++ b/packages/client/src/restapi/content/add.test.tsx
@@ -6,6 +6,7 @@ import { beforeEach } from 'vitest';
import { expect, test } from 'vitest';
import PloneClient from '../../client';
import { CreateContentArgs } from './add';
+import { createContent } from '../content/add';
const cli = PloneClient.initialize({
apiPath: 'http://localhost:55001/plone',
@@ -46,6 +47,40 @@ describe('[POST] Content', () => {
expect(result.current.data?.title).toBe('My Page');
});
+ test('Hook - create content in path', async () => {
+ const myPageData = {
+ '@type': 'Document',
+ title: 'My Page',
+ };
+
+ await createContent({
+ path: '/',
+ data: myPageData,
+ config: cli.config,
+ });
+
+ const path = '/my-page';
+ const data: CreateContentArgs['data'] = {
+ '@type': 'Document',
+ title: 'My nested Page',
+ };
+
+ const { result } = renderHook(() => useMutation(createContentMutation()), {
+ wrapper: createWrapper(),
+ });
+
+ act(() => {
+ result.current.mutate({ path, data });
+ });
+
+ await waitFor(() => expect(result.current.isSuccess).toBe(true));
+
+ expect(result.current.data?.['@id']).toBe(
+ 'http://localhost:55001/plone/my-page/my-nested-page',
+ );
+ expect(result.current.data?.title).toBe('My nested Page');
+ });
+
test('Hook - Successful - setup/tearingDown setup', async () => {
const path = '/';
const data: CreateContentArgs['data'] = {
diff --git a/packages/client/src/restapi/content/add.ts b/packages/client/src/restapi/content/add.ts
index 1cff2e06b3..671b1b1406 100644
--- a/packages/client/src/restapi/content/add.ts
+++ b/packages/client/src/restapi/content/add.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createContentDataSchema } from '../../validation/content';
-import { CreateContentResponse } from '@plone/types';
+import type { CreateContentResponse } from '@plone/types';
export const createContentArgsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/content/delete.ts b/packages/client/src/restapi/content/delete.ts
index fbf8ec0230..95ce268eeb 100644
--- a/packages/client/src/restapi/content/delete.ts
+++ b/packages/client/src/restapi/content/delete.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/content/get.ts b/packages/client/src/restapi/content/get.ts
index 18103e1427..5d5cf8fa96 100644
--- a/packages/client/src/restapi/content/get.ts
+++ b/packages/client/src/restapi/content/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { Content } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { Content } from '@plone/types';
import { z } from 'zod';
const getContentArgsSchema = z.object({
diff --git a/packages/client/src/restapi/content/update.ts b/packages/client/src/restapi/content/update.ts
index fe751e7a68..83ee95571b 100644
--- a/packages/client/src/restapi/content/update.ts
+++ b/packages/client/src/restapi/content/update.ts
@@ -1,11 +1,11 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { z } from 'zod';
import { updateContentDataSchema } from '../../validation/content';
-import { UpdateContentResponse } from '@plone/types';
+import type { UpdateContentResponse } from '@plone/types';
export const updateContentArgsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/contextnavigation/get.ts b/packages/client/src/restapi/contextnavigation/get.ts
index af0c1b7398..ca586a2e8a 100644
--- a/packages/client/src/restapi/contextnavigation/get.ts
+++ b/packages/client/src/restapi/contextnavigation/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { ContextNavigationResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { ContextNavigationResponse } from '@plone/types';
import { z } from 'zod';
const getContextNavigationSchema = z.object({
diff --git a/packages/client/src/restapi/controlpanels/add.ts b/packages/client/src/restapi/controlpanels/add.ts
index 9b25859833..a48fefda63 100644
--- a/packages/client/src/restapi/controlpanels/add.ts
+++ b/packages/client/src/restapi/controlpanels/add.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/controlpanels/delete.ts b/packages/client/src/restapi/controlpanels/delete.ts
index 5abff355f8..34cdfe983b 100644
--- a/packages/client/src/restapi/controlpanels/delete.ts
+++ b/packages/client/src/restapi/controlpanels/delete.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/controlpanels/get.ts b/packages/client/src/restapi/controlpanels/get.ts
index 3cb54124dc..2c18b4a25a 100644
--- a/packages/client/src/restapi/controlpanels/get.ts
+++ b/packages/client/src/restapi/controlpanels/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetControlpanelResponse } from '@plone/types';
+import type { GetControlpanelResponse } from '@plone/types';
const getControlpanelSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/controlpanels/get_list.ts b/packages/client/src/restapi/controlpanels/get_list.ts
index e5c171c8e5..24f9b54cc6 100644
--- a/packages/client/src/restapi/controlpanels/get_list.ts
+++ b/packages/client/src/restapi/controlpanels/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetControlpanelsResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetControlpanelsResponse } from '@plone/types';
export type ControlpanelsArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/controlpanels/update.ts b/packages/client/src/restapi/controlpanels/update.ts
index cdd7815db3..1eb8ff8482 100644
--- a/packages/client/src/restapi/controlpanels/update.ts
+++ b/packages/client/src/restapi/controlpanels/update.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/copymove/copy.test.tsx b/packages/client/src/restapi/copymove/copy.test.tsx
index e278ad3d88..0c79277546 100644
--- a/packages/client/src/restapi/copymove/copy.test.tsx
+++ b/packages/client/src/restapi/copymove/copy.test.tsx
@@ -43,7 +43,7 @@ describe('[POST] Copy', () => {
});
act(() => {
- result.current.mutate({ data: copyData });
+ result.current.mutate({ path: '/', data: copyData });
});
await waitFor(() => expect(result.current.isSuccess).toBe(true));
@@ -80,7 +80,7 @@ describe('[POST] Copy', () => {
});
act(() => {
- result.current.mutate({ data: copyMultipleData });
+ result.current.mutate({ path: '/', data: copyMultipleData });
});
await waitFor(() => expect(result.current.isSuccess).toBe(true));
diff --git a/packages/client/src/restapi/copymove/copy.ts b/packages/client/src/restapi/copymove/copy.ts
index 3935275dd5..e62302a1a7 100644
--- a/packages/client/src/restapi/copymove/copy.ts
+++ b/packages/client/src/restapi/copymove/copy.ts
@@ -1,37 +1,35 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
-import {
- PloneClientConfig,
- PloneClientConfigSchema,
-} from '../../validation/config';
+import { type ApiRequestParams, apiRequest } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { copyMoveDataSchema as copyDataSchema } from '../../validation/copymove';
-import { CopyMoveResponse as CopyResponse } from '@plone/types';
+import type { CopyMoveResponse as CopyResponse } from '@plone/types';
-export const copyArgsSchema = z.object({
- data: copyDataSchema,
- config: PloneClientConfigSchema,
-});
-
-export type CopyArgs = z.infer;
+export type CopyArgs = z.infer & {
+ config: PloneClientConfig;
+};
export const copy = async ({
+ path,
data,
config,
}: CopyArgs): Promise => {
- const validatedArgs = copyArgsSchema.parse({
+ const validatedArgs = copyDataSchema.parse({
+ path,
data,
- config,
});
const options: ApiRequestParams = {
+ config,
data: validatedArgs.data,
- config: validatedArgs.config,
};
- return apiRequest('post', '/@copy', options);
+ const copyPath = `${validatedArgs.path}/@copy`;
+
+ return apiRequest('post', copyPath, options);
};
export const copyMutation = ({ config }: { config: PloneClientConfig }) => ({
mutationKey: ['post', 'copy'],
- mutationFn: ({ data }: Omit) => copy({ data, config }),
+ mutationFn: ({ path, data }: Omit) =>
+ copy({ path, data, config }),
});
diff --git a/packages/client/src/restapi/copymove/move.ts b/packages/client/src/restapi/copymove/move.ts
index 3f9e3ceea7..ebd11e193e 100644
--- a/packages/client/src/restapi/copymove/move.ts
+++ b/packages/client/src/restapi/copymove/move.ts
@@ -1,37 +1,29 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
-import {
- PloneClientConfig,
- PloneClientConfigSchema,
-} from '../../validation/config';
+import { type ApiRequestParams, apiRequest } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { copyMoveDataSchema as moveDataSchema } from '../../validation/copymove';
-import { CopyMoveResponse as MoveResponse } from '@plone/types';
+import type { CopyMoveResponse as MoveResponse } from '@plone/types';
-export const MoveArgsSchema = z.object({
- path: z.string(),
- data: moveDataSchema,
- config: PloneClientConfigSchema,
-});
-
-export type MoveArgs = z.infer;
+export type MoveArgs = z.infer & {
+ config: PloneClientConfig;
+};
export const move = async ({
path,
data,
config,
}: MoveArgs): Promise => {
- const validatedArgs = MoveArgsSchema.parse({
+ const validatedArgs = moveDataSchema.parse({
path,
data,
- config,
});
const options: ApiRequestParams = {
+ config,
data: validatedArgs.data,
- config: validatedArgs.config,
};
- const movePath = `/${validatedArgs.path}/@move`;
+ const movePath = `${validatedArgs.path}/@move`;
return apiRequest('post', movePath, options);
};
diff --git a/packages/client/src/restapi/database/get.ts b/packages/client/src/restapi/database/get.ts
index 53769b3c41..5a10438edd 100644
--- a/packages/client/src/restapi/database/get.ts
+++ b/packages/client/src/restapi/database/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { DatabaseResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { DatabaseResponse } from '@plone/types';
export type DatabaseArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/email-notification/post.ts b/packages/client/src/restapi/email-notification/post.ts
index 54a81b340b..718ab59f7b 100644
--- a/packages/client/src/restapi/email-notification/post.ts
+++ b/packages/client/src/restapi/email-notification/post.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/email-send/post.ts b/packages/client/src/restapi/email-send/post.ts
index f1d5863ed0..23cc727f2e 100644
--- a/packages/client/src/restapi/email-send/post.ts
+++ b/packages/client/src/restapi/email-send/post.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/groups/add.ts b/packages/client/src/restapi/groups/add.ts
index b9dd8fa2cc..27f0fc8d17 100644
--- a/packages/client/src/restapi/groups/add.ts
+++ b/packages/client/src/restapi/groups/add.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createGroupDataSchema } from '../../validation/groups';
-import { CreateGroupResponse } from '@plone/types';
+import type { CreateGroupResponse } from '@plone/types';
export const createGroupArgsSchema = z.object({
data: createGroupDataSchema,
diff --git a/packages/client/src/restapi/groups/delete.ts b/packages/client/src/restapi/groups/delete.ts
index 4d2565ae3d..e135f1b691 100644
--- a/packages/client/src/restapi/groups/delete.ts
+++ b/packages/client/src/restapi/groups/delete.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/groups/get.ts b/packages/client/src/restapi/groups/get.ts
index 7048b40fb9..3d23c1690e 100644
--- a/packages/client/src/restapi/groups/get.ts
+++ b/packages/client/src/restapi/groups/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetGroupResponse } from '@plone/types';
+import type { GetGroupResponse } from '@plone/types';
const getGroupSchema = z.object({
groupId: z.string(),
diff --git a/packages/client/src/restapi/groups/get_list.ts b/packages/client/src/restapi/groups/get_list.ts
index 7c7928bbec..e3b1724492 100644
--- a/packages/client/src/restapi/groups/get_list.ts
+++ b/packages/client/src/restapi/groups/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetGroupsResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetGroupsResponse } from '@plone/types';
export type GroupsArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/groups/update.ts b/packages/client/src/restapi/groups/update.ts
index c189643e2a..d31ca1a680 100644
--- a/packages/client/src/restapi/groups/update.ts
+++ b/packages/client/src/restapi/groups/update.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { updateGroupDataSchema } from '../../validation/groups';
diff --git a/packages/client/src/restapi/history/get.ts b/packages/client/src/restapi/history/get.ts
index 15e2ca1169..70f2d2bfbf 100644
--- a/packages/client/src/restapi/history/get.ts
+++ b/packages/client/src/restapi/history/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetHistoryResponse } from '@plone/types';
+import type { GetHistoryResponse } from '@plone/types';
const getHistorySchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/history/get_versioned.ts b/packages/client/src/restapi/history/get_versioned.ts
index de016b50fd..b6e33c86f9 100644
--- a/packages/client/src/restapi/history/get_versioned.ts
+++ b/packages/client/src/restapi/history/get_versioned.ts
@@ -1,5 +1,5 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
const getHistoryVersionedSchema = z.object({
diff --git a/packages/client/src/restapi/history/revert.ts b/packages/client/src/restapi/history/revert.ts
index 3e0ad44580..616e4053f4 100644
--- a/packages/client/src/restapi/history/revert.ts
+++ b/packages/client/src/restapi/history/revert.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { revertHistoryDataSchema } from '../../validation/history';
diff --git a/packages/client/src/restapi/linkintegrity/get.ts b/packages/client/src/restapi/linkintegrity/get.ts
index 9af2dd5551..8cf9f359eb 100644
--- a/packages/client/src/restapi/linkintegrity/get.ts
+++ b/packages/client/src/restapi/linkintegrity/get.ts
@@ -1,5 +1,5 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { PloneClientConfigSchema } from '../../validation/config';
const getLinkintegriyArgsSchema = z.object({
diff --git a/packages/client/src/restapi/lock/add.ts b/packages/client/src/restapi/lock/add.ts
index 2ba71b85c3..608b3e38a6 100644
--- a/packages/client/src/restapi/lock/add.ts
+++ b/packages/client/src/restapi/lock/add.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createLockDataSchema } from '../../validation/lock';
-import { CreateLockResponse } from '@plone/types';
+import type { CreateLockResponse } from '@plone/types';
export const createLockArgsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/lock/delete.ts b/packages/client/src/restapi/lock/delete.ts
index c18cd46d17..ee06ad8d62 100644
--- a/packages/client/src/restapi/lock/delete.ts
+++ b/packages/client/src/restapi/lock/delete.ts
@@ -1,11 +1,11 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { deleteLockDataSchema } from '../../validation/lock';
-import { LockInfo as DeleteLockResponse } from '@plone/types';
+import type { LockInfo as DeleteLockResponse } from '@plone/types';
export const deleteLockArgsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/lock/get.ts b/packages/client/src/restapi/lock/get.ts
index 9c1962e16a..9c09841968 100644
--- a/packages/client/src/restapi/lock/get.ts
+++ b/packages/client/src/restapi/lock/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { LockInfo as GetLockResponse } from '@plone/types';
+import type { LockInfo as GetLockResponse } from '@plone/types';
const getLockSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/lock/update.ts b/packages/client/src/restapi/lock/update.ts
index d9c0a3553a..42fc26866c 100644
--- a/packages/client/src/restapi/lock/update.ts
+++ b/packages/client/src/restapi/lock/update.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { z } from 'zod';
diff --git a/packages/client/src/restapi/login/post.ts b/packages/client/src/restapi/login/post.ts
index 5d893fef9f..e35f1d87f6 100644
--- a/packages/client/src/restapi/login/post.ts
+++ b/packages/client/src/restapi/login/post.ts
@@ -1,10 +1,10 @@
-import { Login } from '@plone/types';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
+import type { Login } from '@plone/types';
export const loginArgsSchema = z.object({
username: z.string(),
diff --git a/packages/client/src/restapi/navigation/get.test.tsx b/packages/client/src/restapi/navigation/get.test.tsx
index f6a47d68e5..e456738842 100644
--- a/packages/client/src/restapi/navigation/get.test.tsx
+++ b/packages/client/src/restapi/navigation/get.test.tsx
@@ -2,12 +2,24 @@ import { renderHook, waitFor } from '@testing-library/react';
import { createWrapper } from '../../testUtils';
import { useQuery } from '@tanstack/react-query';
import ploneClient from '../../client';
+import { setup, teardown } from '../../resetFixture';
+import { createContent } from '../content/add';
const cli = ploneClient.initialize({
apiPath: 'http://localhost:55001/plone',
});
-const { getNavigationQuery } = cli;
+const { getNavigationQuery, login } = cli;
+
+await login({ username: 'admin', password: 'secret' });
+
+beforeEach(async () => {
+ await setup();
+});
+
+afterEach(async () => {
+ await teardown();
+});
describe('[GET] Navigation', () => {
test('Hook - Successful', async () => {
@@ -39,4 +51,57 @@ describe('[GET] Navigation', () => {
expect(result.current.error).toBeDefined();
});
+
+ test('Depth parameter', async () => {
+ await createContent({
+ path: '/',
+ data: {
+ '@type': 'Document',
+ title: 'Level 1',
+ },
+ config: cli.config,
+ });
+ await createContent({
+ path: '/level-1',
+ data: {
+ '@type': 'Document',
+ title: 'Level 2',
+ },
+ config: cli.config,
+ });
+ await createContent({
+ path: '/level-1/level-2',
+ data: {
+ '@type': 'Document',
+ title: 'Level 3',
+ },
+ config: cli.config,
+ });
+
+ const depth2 = renderHook(
+ () => useQuery(getNavigationQuery({ path: '/', depth: 2 })),
+ {
+ wrapper: createWrapper(),
+ },
+ );
+ await waitFor(() => expect(depth2.result.current.isSuccess).toBe(true));
+ const level1_1 = depth2.result.current.data?.items.find(
+ (item) => item.title === 'Level 1',
+ );
+ expect(level1_1.items[0].title).toBe('Level 2');
+ expect(level1_1.items[0].items.length).toBe(0);
+
+ const depth3 = renderHook(
+ () => useQuery(getNavigationQuery({ path: '/', depth: 3 })),
+ {
+ wrapper: createWrapper(),
+ },
+ );
+ await waitFor(() => expect(depth3.result.current.isSuccess).toBe(true));
+ const level1_2 = depth3.result.current.data?.items.find(
+ (item) => item.title === 'Level 1',
+ );
+ expect(level1_2.items[0].items.length).toBe(1);
+ expect(level1_2.items[0].items[0].title).toBe('Level 3');
+ });
});
diff --git a/packages/client/src/restapi/navigation/get.ts b/packages/client/src/restapi/navigation/get.ts
index 8f4c6b6211..71e64fb94f 100644
--- a/packages/client/src/restapi/navigation/get.ts
+++ b/packages/client/src/restapi/navigation/get.ts
@@ -1,10 +1,11 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { NavigationResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { NavigationResponse } from '@plone/types';
import { z } from 'zod';
const getNavigationSchema = z.object({
path: z.string(),
+ depth: z.number().optional(),
});
export type NavigationArgs = z.infer & {
@@ -13,10 +14,12 @@ export type NavigationArgs = z.infer & {
export const getNavigation = async ({
path,
+ depth,
config,
}: NavigationArgs): Promise => {
const validatedArgs = getNavigationSchema.parse({
path,
+ depth,
});
const options: ApiRequestParams = {
@@ -25,11 +28,18 @@ export const getNavigation = async ({
};
const navigationPath = `${validatedArgs.path}/@navigation`;
+ if (validatedArgs.depth) {
+ options.params['expand.navigation.depth'] = validatedArgs.depth;
+ }
return apiRequest('get', navigationPath, options);
};
-export const getNavigationQuery = ({ path, config }: NavigationArgs) => ({
- queryKey: [path, 'get', 'navigation'],
- queryFn: () => getNavigation({ path, config }),
+export const getNavigationQuery = ({
+ path,
+ depth,
+ config,
+}: NavigationArgs) => ({
+ queryKey: [path, depth, 'get', 'navigation'],
+ queryFn: () => getNavigation({ path, depth, config }),
});
diff --git a/packages/client/src/restapi/navroot/get.ts b/packages/client/src/restapi/navroot/get.ts
index a43d9d1d51..d5dea3d4f2 100644
--- a/packages/client/src/restapi/navroot/get.ts
+++ b/packages/client/src/restapi/navroot/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetNavrootResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetNavrootResponse } from '@plone/types';
import { z } from 'zod';
const getNavrootSchema = z.object({
diff --git a/packages/client/src/restapi/principals/get.ts b/packages/client/src/restapi/principals/get.ts
index d42abc930c..4ae976781e 100644
--- a/packages/client/src/restapi/principals/get.ts
+++ b/packages/client/src/restapi/principals/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetPrincipalsResponse } from '@plone/types';
+import type { GetPrincipalsResponse } from '@plone/types';
const getPrincipalsSchema = z.object({
search: z.string(),
diff --git a/packages/client/src/restapi/querysources/get.ts b/packages/client/src/restapi/querysources/get.ts
index 46949b2d5c..47207b9507 100644
--- a/packages/client/src/restapi/querysources/get.ts
+++ b/packages/client/src/restapi/querysources/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetQuerysourceResponse } from '@plone/types';
+import type { GetQuerysourceResponse } from '@plone/types';
const getQuerysourceSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/querystring-search/get.ts b/packages/client/src/restapi/querystring-search/get.ts
index 926453dfbd..588b50eddc 100644
--- a/packages/client/src/restapi/querystring-search/get.ts
+++ b/packages/client/src/restapi/querystring-search/get.ts
@@ -1,8 +1,8 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
import { querystringSearchDataSchema as getQuerystringSearchSchema } from '../../validation/querystring-search';
-import { QuerystringSearchResponse as GetQuerystringSearchResponse } from '@plone/types';
+import type { QuerystringSearchResponse as GetQuerystringSearchResponse } from '@plone/types';
export type QuerystringSearchArgs = z.infer<
typeof getQuerystringSearchSchema
diff --git a/packages/client/src/restapi/querystring-search/post.ts b/packages/client/src/restapi/querystring-search/post.ts
index 95c07e6be5..decf6dad71 100644
--- a/packages/client/src/restapi/querystring-search/post.ts
+++ b/packages/client/src/restapi/querystring-search/post.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { querystringSearchDataSchema as postQuerystringSearchDataSchema } from '../../validation/querystring-search';
-import { QuerystringSearchResponse as PostQuerystringSearchResponse } from '@plone/types';
+import type { QuerystringSearchResponse as PostQuerystringSearchResponse } from '@plone/types';
export const postQuerystringSearchArgsSchema = z.object({
data: postQuerystringSearchDataSchema,
diff --git a/packages/client/src/restapi/querystring/get.ts b/packages/client/src/restapi/querystring/get.ts
index 008f1747ea..acea020e41 100644
--- a/packages/client/src/restapi/querystring/get.ts
+++ b/packages/client/src/restapi/querystring/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetQueryStringResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetQueryStringResponse } from '@plone/types';
export type QueryStringArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/registry/get.ts b/packages/client/src/restapi/registry/get.ts
index d4642ee39a..90784f77fe 100644
--- a/packages/client/src/restapi/registry/get.ts
+++ b/packages/client/src/restapi/registry/get.ts
@@ -1,5 +1,5 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
const getRegistrySchema = z.object({
diff --git a/packages/client/src/restapi/registry/get_list.ts b/packages/client/src/restapi/registry/get_list.ts
index c3c43faaa9..438a12a771 100644
--- a/packages/client/src/restapi/registry/get_list.ts
+++ b/packages/client/src/restapi/registry/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetRegistriesResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetRegistriesResponse } from '@plone/types';
export type GetRegistriesArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/registry/update.ts b/packages/client/src/restapi/registry/update.ts
index ee24c141e6..0bd30eeff9 100644
--- a/packages/client/src/restapi/registry/update.ts
+++ b/packages/client/src/restapi/registry/update.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { updateRegistryDataSchema } from '../../validation/registry';
diff --git a/packages/client/src/restapi/relations/add.ts b/packages/client/src/restapi/relations/add.ts
index c979571172..70a90b1673 100644
--- a/packages/client/src/restapi/relations/add.ts
+++ b/packages/client/src/restapi/relations/add.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createRelationsDataSchema } from '../../validation/relations';
diff --git a/packages/client/src/restapi/relations/delete.ts b/packages/client/src/restapi/relations/delete.ts
index 6276712db2..97eef66aab 100644
--- a/packages/client/src/restapi/relations/delete.ts
+++ b/packages/client/src/restapi/relations/delete.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { deleteRelationsDataSchema } from '../../validation/relations';
diff --git a/packages/client/src/restapi/relations/fix.ts b/packages/client/src/restapi/relations/fix.ts
index 53daba35be..e52b0306fd 100644
--- a/packages/client/src/restapi/relations/fix.ts
+++ b/packages/client/src/restapi/relations/fix.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { fixRelationsDataSchema } from '../../validation/relations';
diff --git a/packages/client/src/restapi/relations/get.ts b/packages/client/src/restapi/relations/get.ts
index 6ac85f6a14..44e608a276 100644
--- a/packages/client/src/restapi/relations/get.ts
+++ b/packages/client/src/restapi/relations/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetRelationsResponse } from '@plone/types';
+import type { GetRelationsResponse } from '@plone/types';
export const getRelationsSchema = z
.object({
diff --git a/packages/client/src/restapi/relations/get_list.ts b/packages/client/src/restapi/relations/get_list.ts
index e37cdc2381..07490cf19f 100644
--- a/packages/client/src/restapi/relations/get_list.ts
+++ b/packages/client/src/restapi/relations/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetRelationsListResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetRelationsListResponse } from '@plone/types';
export type GetRelationsListArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/roles/get.ts b/packages/client/src/restapi/roles/get.ts
index 96dfd7e18a..6fb5d23504 100644
--- a/packages/client/src/restapi/roles/get.ts
+++ b/packages/client/src/restapi/roles/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetRolesResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetRolesResponse } from '@plone/types';
export type GetRolesArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/rules/add.ts b/packages/client/src/restapi/rules/add.ts
index b187c64b4d..10009142c0 100644
--- a/packages/client/src/restapi/rules/add.ts
+++ b/packages/client/src/restapi/rules/add.ts
@@ -1,10 +1,10 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
-import { RuleRespose as CreateRuleResponse } from '@plone/types';
+import type { RuleRespose as CreateRuleResponse } from '@plone/types';
export const createRuleArgsSchema = z.object({
ruleId: z.string(),
diff --git a/packages/client/src/restapi/rules/delete.ts b/packages/client/src/restapi/rules/delete.ts
index 343e71cd65..9f53b63508 100644
--- a/packages/client/src/restapi/rules/delete.ts
+++ b/packages/client/src/restapi/rules/delete.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { deleteRulesDataSchema } from '../../validation/rules';
diff --git a/packages/client/src/restapi/rules/get.ts b/packages/client/src/restapi/rules/get.ts
index a5b9f930f1..141204c5f8 100644
--- a/packages/client/src/restapi/rules/get.ts
+++ b/packages/client/src/restapi/rules/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetRulesResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetRulesResponse } from '@plone/types';
export type GetRulesArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/rules/update.ts b/packages/client/src/restapi/rules/update.ts
index 302a0d0592..73260fb05c 100644
--- a/packages/client/src/restapi/rules/update.ts
+++ b/packages/client/src/restapi/rules/update.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { updateRulesDataSchema } from '../../validation/rules';
-import { RuleRespose as UpdateRuleRespose } from '@plone/types';
+import type { RuleRespose as UpdateRuleRespose } from '@plone/types';
export const updateRulesArgsSchema = z.object({
data: updateRulesDataSchema,
diff --git a/packages/client/src/restapi/search/get.ts b/packages/client/src/restapi/search/get.ts
index 53e44aadd0..ab75b82fd9 100644
--- a/packages/client/src/restapi/search/get.ts
+++ b/packages/client/src/restapi/search/get.ts
@@ -1,8 +1,8 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
import { getSearchSchema } from '../../validation/search';
-import { GetSearchResponse } from '@plone/types';
+import type { GetSearchResponse } from '@plone/types';
import { flattenToDottedNotation } from '../../utils/misc';
export type SearchArgs = z.infer & {
diff --git a/packages/client/src/restapi/site/get.ts b/packages/client/src/restapi/site/get.ts
index a857df017d..dcfe189eb6 100644
--- a/packages/client/src/restapi/site/get.ts
+++ b/packages/client/src/restapi/site/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetSiteResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetSiteResponse } from '@plone/types';
export type SiteArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/sources/get.ts b/packages/client/src/restapi/sources/get.ts
index 7fc5fa4e0d..fc4a6ecbb5 100644
--- a/packages/client/src/restapi/sources/get.ts
+++ b/packages/client/src/restapi/sources/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetSourceResponse } from '@plone/types';
+import type { GetSourceResponse } from '@plone/types';
const getSourceSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/system/get.ts b/packages/client/src/restapi/system/get.ts
index 41c447bc6e..ed801c6e4d 100644
--- a/packages/client/src/restapi/system/get.ts
+++ b/packages/client/src/restapi/system/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetSystemResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetSystemResponse } from '@plone/types';
export type GetSystemArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/transactions/get.ts b/packages/client/src/restapi/transactions/get.ts
index c9f7f5ffb5..c45b3790ca 100644
--- a/packages/client/src/restapi/transactions/get.ts
+++ b/packages/client/src/restapi/transactions/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetTransactionsResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetTransactionsResponse } from '@plone/types';
export type GetTransactionsArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/transactions/revert.ts b/packages/client/src/restapi/transactions/revert.ts
index e9e5f45fbc..1e9723620c 100644
--- a/packages/client/src/restapi/transactions/revert.ts
+++ b/packages/client/src/restapi/transactions/revert.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { revertTransactionsDataSchema } from '../../validation/transactions';
-import { RevertTransactionsResponse } from '@plone/types';
+import type { RevertTransactionsResponse } from '@plone/types';
export const revertTransactionsArgsSchema = z.object({
data: revertTransactionsDataSchema,
diff --git a/packages/client/src/restapi/translations/get.ts b/packages/client/src/restapi/translations/get.ts
index 726112310a..fb383f0bd2 100644
--- a/packages/client/src/restapi/translations/get.ts
+++ b/packages/client/src/restapi/translations/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetTranslationResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetTranslationResponse } from '@plone/types';
import { z } from 'zod';
const getTranslationSchema = z.object({
diff --git a/packages/client/src/restapi/translations/link.ts b/packages/client/src/restapi/translations/link.ts
index 4b5a34c2b5..468aa23654 100644
--- a/packages/client/src/restapi/translations/link.ts
+++ b/packages/client/src/restapi/translations/link.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { linkTranslationDataSchema } from '../../validation/translations';
diff --git a/packages/client/src/restapi/translations/unlink.ts b/packages/client/src/restapi/translations/unlink.ts
index ab360e7ff0..321ee63a07 100644
--- a/packages/client/src/restapi/translations/unlink.ts
+++ b/packages/client/src/restapi/translations/unlink.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { unlinkTranslationDataSchema } from '../../validation/translations';
diff --git a/packages/client/src/restapi/types/add.ts b/packages/client/src/restapi/types/add.ts
index 7516b00227..6182a97126 100644
--- a/packages/client/src/restapi/types/add.ts
+++ b/packages/client/src/restapi/types/add.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createTypeFieldDataSchema } from '../../validation/types';
-import { CreateTypeFieldResponse } from '@plone/types';
+import type { CreateTypeFieldResponse } from '@plone/types';
export const createTypeFieldArgsSchema = z.object({
contentPath: z.string(),
diff --git a/packages/client/src/restapi/types/get.ts b/packages/client/src/restapi/types/get.ts
index ccbf2278a5..86dd3c5a11 100644
--- a/packages/client/src/restapi/types/get.ts
+++ b/packages/client/src/restapi/types/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetTypeResponse } from '@plone/types';
+import type { GetTypeResponse } from '@plone/types';
const getTypeSchema = z.object({
contentPath: z.string(),
diff --git a/packages/client/src/restapi/types/get_list.ts b/packages/client/src/restapi/types/get_list.ts
index 314b2d273d..31cb883e7b 100644
--- a/packages/client/src/restapi/types/get_list.ts
+++ b/packages/client/src/restapi/types/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetTypesResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetTypesResponse } from '@plone/types';
export type GetTypesArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/types/get_type_field.ts b/packages/client/src/restapi/types/get_type_field.ts
index 32a0f771ba..2891792e44 100644
--- a/packages/client/src/restapi/types/get_type_field.ts
+++ b/packages/client/src/restapi/types/get_type_field.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetTypeFieldResponse } from '@plone/types';
+import type { GetTypeFieldResponse } from '@plone/types';
const getTypeFieldSchema = z.object({
contentFieldPath: z.string(),
diff --git a/packages/client/src/restapi/types/update.ts b/packages/client/src/restapi/types/update.ts
index c3e0eff2ef..bae958ffc0 100644
--- a/packages/client/src/restapi/types/update.ts
+++ b/packages/client/src/restapi/types/update.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { updateTypeFieldDataSchema } from '../../validation/types';
diff --git a/packages/client/src/restapi/upgrade/get.ts b/packages/client/src/restapi/upgrade/get.ts
index 4edb5f08f4..1f05437ada 100644
--- a/packages/client/src/restapi/upgrade/get.ts
+++ b/packages/client/src/restapi/upgrade/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetUpgradeResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetUpgradeResponse } from '@plone/types';
export type GetUpgradeArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/upgrade/run.ts b/packages/client/src/restapi/upgrade/run.ts
index c9cb34faab..3d847045fd 100644
--- a/packages/client/src/restapi/upgrade/run.ts
+++ b/packages/client/src/restapi/upgrade/run.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { runUpgradeDataSchema } from '../../validation/upgrade';
-import { RunUpgradeResponse } from '@plone/types';
+import type { RunUpgradeResponse } from '@plone/types';
export const runUpgradeArgsSchema = z.object({
data: runUpgradeDataSchema,
diff --git a/packages/client/src/restapi/users/add.ts b/packages/client/src/restapi/users/add.ts
index e9896013dc..de76be19d1 100644
--- a/packages/client/src/restapi/users/add.ts
+++ b/packages/client/src/restapi/users/add.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createUserDataSchema } from '../../validation/users';
-import { User as CreateUserResponse } from '@plone/types';
+import type { User as CreateUserResponse } from '@plone/types';
export const createUserArgsSchema = z.object({
data: createUserDataSchema,
diff --git a/packages/client/src/restapi/users/delete.ts b/packages/client/src/restapi/users/delete.ts
index c3f5c175cc..59203b8bc2 100644
--- a/packages/client/src/restapi/users/delete.ts
+++ b/packages/client/src/restapi/users/delete.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/users/get.ts b/packages/client/src/restapi/users/get.ts
index 1f8ab45bfb..dd29774f12 100644
--- a/packages/client/src/restapi/users/get.ts
+++ b/packages/client/src/restapi/users/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { User as GetUserResponse } from '@plone/types';
+import type { User as GetUserResponse } from '@plone/types';
const getUserSchema = z.object({
userId: z.string(),
diff --git a/packages/client/src/restapi/users/get_list.ts b/packages/client/src/restapi/users/get_list.ts
index f441084903..d0319f2e62 100644
--- a/packages/client/src/restapi/users/get_list.ts
+++ b/packages/client/src/restapi/users/get_list.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetUsersResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetUsersResponse } from '@plone/types';
const getUsersSchema = z.object({
query: z.string().optional(),
diff --git a/packages/client/src/restapi/users/reset_password.ts b/packages/client/src/restapi/users/reset_password.ts
index 6b831048e8..ec13bc4f5c 100644
--- a/packages/client/src/restapi/users/reset_password.ts
+++ b/packages/client/src/restapi/users/reset_password.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/users/reset_password_with_token.ts b/packages/client/src/restapi/users/reset_password_with_token.ts
index 65d9ead9e1..8355b11544 100644
--- a/packages/client/src/restapi/users/reset_password_with_token.ts
+++ b/packages/client/src/restapi/users/reset_password_with_token.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { resetPasswordWithTokenDataSchema } from '../../validation/users';
diff --git a/packages/client/src/restapi/users/update.ts b/packages/client/src/restapi/users/update.ts
index 83f71fb396..cc0e7a36ef 100644
--- a/packages/client/src/restapi/users/update.ts
+++ b/packages/client/src/restapi/users/update.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { updateUserDataSchema } from '../../validation/users';
diff --git a/packages/client/src/restapi/users/update_password.ts b/packages/client/src/restapi/users/update_password.ts
index d5a52996df..21b29143b6 100644
--- a/packages/client/src/restapi/users/update_password.ts
+++ b/packages/client/src/restapi/users/update_password.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { updatePasswordDataSchema } from '../../validation/users';
diff --git a/packages/client/src/restapi/userschema/get.ts b/packages/client/src/restapi/userschema/get.ts
index c5cf6c55d3..2d7e0fbf17 100644
--- a/packages/client/src/restapi/userschema/get.ts
+++ b/packages/client/src/restapi/userschema/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetUserschemaResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetUserschemaResponse } from '@plone/types';
export type GetUserschemaArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/vocabularies/get.ts b/packages/client/src/restapi/vocabularies/get.ts
index b25c9b00cf..a8e00488c3 100644
--- a/packages/client/src/restapi/vocabularies/get.ts
+++ b/packages/client/src/restapi/vocabularies/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetVocabulariesResponse } from '@plone/types';
import { z } from 'zod';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetVocabulariesResponse } from '@plone/types';
const getVocabulariesSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/vocabularies/get_list.ts b/packages/client/src/restapi/vocabularies/get_list.ts
index 1a6b18b83a..312be656aa 100644
--- a/packages/client/src/restapi/vocabularies/get_list.ts
+++ b/packages/client/src/restapi/vocabularies/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetVocabulariesListResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetVocabulariesListResponse } from '@plone/types';
export type VocabulariesListArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/workflow/add.ts b/packages/client/src/restapi/workflow/add.ts
index 91b355bff9..86988f021c 100644
--- a/packages/client/src/restapi/workflow/add.ts
+++ b/packages/client/src/restapi/workflow/add.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createWorkflowDataSchema } from '../../validation/workflow';
-import { CreateWorkflowResponse } from '@plone/types';
+import type { CreateWorkflowResponse } from '@plone/types';
export const createWorkflowArgsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/workflow/get.ts b/packages/client/src/restapi/workflow/get.ts
index 7b0e733dfe..1e1f16801d 100644
--- a/packages/client/src/restapi/workflow/get.ts
+++ b/packages/client/src/restapi/workflow/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { WorkflowResponse } from '@plone/types';
import { z } from 'zod';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { WorkflowResponse } from '@plone/types';
const getWorkflowSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/workingcopy/add.ts b/packages/client/src/restapi/workingcopy/add.ts
index b5d3bcfa49..534a00eae0 100644
--- a/packages/client/src/restapi/workingcopy/add.ts
+++ b/packages/client/src/restapi/workingcopy/add.ts
@@ -1,10 +1,10 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
-import { CreateWorkingcopyResponse } from '@plone/types';
+import type { CreateWorkingcopyResponse } from '@plone/types';
export const createWorkingcopyArgsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/workingcopy/check-in.ts b/packages/client/src/restapi/workingcopy/check-in.ts
index d2c91d7c81..36627ac325 100644
--- a/packages/client/src/restapi/workingcopy/check-in.ts
+++ b/packages/client/src/restapi/workingcopy/check-in.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/workingcopy/delete.ts b/packages/client/src/restapi/workingcopy/delete.ts
index 9efab73607..11db8c026c 100644
--- a/packages/client/src/restapi/workingcopy/delete.ts
+++ b/packages/client/src/restapi/workingcopy/delete.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/workingcopy/get.ts b/packages/client/src/restapi/workingcopy/get.ts
index 1149d49863..4426a0314a 100644
--- a/packages/client/src/restapi/workingcopy/get.ts
+++ b/packages/client/src/restapi/workingcopy/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetWorkingcopyResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetWorkingcopyResponse } from '@plone/types';
const getWorkingcopySchema = z.object({
path: z.string(),
diff --git a/packages/client/src/utils/misc.ts b/packages/client/src/utils/misc.ts
index 6486c48f7d..d668d3971e 100644
--- a/packages/client/src/utils/misc.ts
+++ b/packages/client/src/utils/misc.ts
@@ -4,7 +4,7 @@ import {
useMutation,
useQuery,
} from '@tanstack/react-query';
-import { PloneClientConfig } from '../validation/config';
+import type { PloneClientConfig } from '../validation/config';
/*
configGetter is required instead of using the config directly to make sure
diff --git a/packages/client/src/validation/copymove.ts b/packages/client/src/validation/copymove.ts
index ee27382a81..f712bd708a 100644
--- a/packages/client/src/validation/copymove.ts
+++ b/packages/client/src/validation/copymove.ts
@@ -1,5 +1,8 @@
import { z } from 'zod';
export const copyMoveDataSchema = z.object({
- source: z.union([z.string(), z.array(z.string())]),
+ path: z.string(),
+ data: z.object({
+ source: z.union([z.string(), z.array(z.string())]),
+ }),
});
diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json
index dfe27120f2..8d104e76ee 100644
--- a/packages/client/tsconfig.json
+++ b/packages/client/tsconfig.json
@@ -1,21 +1,28 @@
{
"compilerOptions": {
- "target": "ESNext",
- "useDefineForClassFields": true,
- "lib": ["DOM", "DOM.Iterable", "ESNext"],
- "allowJs": false,
- "skipLibCheck": true,
"esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "module": "ESNext",
- "moduleResolution": "Node",
+ "skipLibCheck": true,
+ "target": "es2022",
+ "allowJs": true,
"resolveJsonModule": true,
+ "moduleDetection": "force",
"isolatedModules": true,
+ "verbatimModuleSyntax": true,
+
+ "strict": true,
+ "noImplicitOverride": true,
+
+ "lib": ["es2022", "dom", "dom.iterable"],
+ "module": "preserve",
"noEmit": true,
+
"jsx": "react-jsx",
- "types": ["vite", "vitest/globals"]
+
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true,
+ "strictPropertyInitialization": false,
+
+ "useDefineForClassFields": true
},
"exclude": ["node_modules", "dist", "coverage", "src/**/*.test.{ts,tsx}"],
"include": ["src"],
diff --git a/packages/client/tsup.config.ts b/packages/client/tsup.config.ts
new file mode 100644
index 0000000000..82b88a425c
--- /dev/null
+++ b/packages/client/tsup.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from 'tsup';
+
+export default defineConfig({
+ entryPoints: ['src/index.ts'],
+ format: ['cjs', 'esm'],
+ dts: true,
+ outDir: 'dist',
+ clean: true,
+});
diff --git a/packages/components/.gitignore b/packages/components/.gitignore
index ead174f021..3fbb0086d7 100644
--- a/packages/components/.gitignore
+++ b/packages/components/.gitignore
@@ -12,5 +12,3 @@ lib
!.yarn/releases
!.yarn/sdks
!.yarn/versions
-
-.parcel-cache/
diff --git a/packages/components/.parcelrc b/packages/components/.parcelrc
deleted file mode 100644
index db2d15099d..0000000000
--- a/packages/components/.parcelrc
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "extends": "@parcel/config-default",
- "transformers": {
- "*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [
- "@parcel/transformer-js",
- "@parcel/transformer-react-refresh-wrap"
- ]
- }
-}
diff --git a/packages/components/.release-it.json b/packages/components/.release-it.json
index 7959b21d1c..0069a0a569 100644
--- a/packages/components/.release-it.json
+++ b/packages/components/.release-it.json
@@ -5,7 +5,7 @@
"hooks": {
"after:bump": [
"pipx run towncrier build --draft --yes --version ${version} > .changelog.draft && pipx run towncrier build --yes --version ${version}",
- "pnpm build"
+ "pnpm build:force"
],
"after:release": "rm .changelog.draft"
},
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 09194352a8..451fc012b6 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -8,6 +8,80 @@
+## 2.2.1 (2024-12-05)
+
+### Bugfix
+
+- Fix color picker usability. @sneridagh [#6512](https://github.com/plone/volto/issues/6512)
+
+## 2.2.0 (2024-11-21)
+
+### Feature
+
+- Update RAC to 1.5.0 @sneridagh [#6498](https://github.com/plone/volto/issues/6498)
+
+## 2.1.1 (2024-11-05)
+
+### Internal
+
+- Improve packaging and bring back the export for `src` folder. @sneridagh
+
+## 2.1.0 (2024-11-05)
+
+### Internal
+
+- Replace `parcel` with `tsup`. Better types, better tsconfig. Move to ESM. @sneridagh [#6467](https://github.com/plone/volto/issues/6467)
+
+## 2.0.0 (2024-10-31)
+
+### Internal
+
+- Release 2.0.0 @sneridagh
+
+## 2.0.0-alpha.16 (2024-10-18)
+
+### Breaking
+
+- Removed the `FlattenToAppURLProvider` since it's no longer needed. @sneridagh
+ The components in here need it.
+ Refactored the `Link` component to not use it, since `react-aria-components` uses the React Client Routing facilities that can be injected into the React tree.
+
+ Breaking:
+ - Use the new providers in `@plone/providers` instead to make the new `Link` work with them. [#6069](https://github.com/plone/volto/issues/6069)
+
+### Internal
+
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407)
+- Adjust the path to perform a proper `git diff` between the cached and current commits to determine whether to build the Storybook for the components package on Netlify. @stevepiercy [#6410](https://github.com/plone/volto/issues/6410)
+
+## 2.0.0-alpha.15 (2024-10-14)
+
+### Bugfix
+
+- Fix Select component logic to support `items` use case @sneridagh [#6405](https://github.com/plone/volto/issues/6405)
+
+## 2.0.0-alpha.14 (2024-10-10)
+
+### Bugfix
+
+- Add missing export for new components @sneridagh [#6391](https://github.com/plone/volto/issues/6391)
+
+## 2.0.0-alpha.13 (2024-10-08)
+
+### Bugfix
+
+- Fixed flattenToAppURL types @pnicolli @deodorhunter [#6382](https://github.com/plone/volto/issues/6382)
+
+### Internal
+
+- Update Vite and vitest versions @sneridagh [#6373](https://github.com/plone/volto/issues/6373)
+
+## 2.0.0-alpha.12 (2024-10-03)
+
+### Feature
+
+- Update RAC to 1.4.0 - Added new `Disclosure` component and new Color widgets @sneridagh [#6364](https://github.com/plone/volto/issues/6364)
+
## 2.0.0-alpha.11 (2024-06-06)
### Bugfix
diff --git a/packages/components/netlify.toml b/packages/components/netlify.toml
index dd96154375..dbedbc629a 100644
--- a/packages/components/netlify.toml
+++ b/packages/components/netlify.toml
@@ -1,2 +1,2 @@
[build]
- ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ./src/"
+ ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF -- packages/components/src/"
diff --git a/packages/components/package.json b/packages/components/package.json
index 34e00c6126..2e45ce640a 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -8,7 +8,7 @@
}
],
"license": "MIT",
- "version": "2.0.0-alpha.11",
+ "version": "2.2.1",
"repository": {
"type": "git",
"url": "http://github.com/plone/components.git"
@@ -16,20 +16,18 @@
"bugs": {
"url": "https://github.com/plone/components/issues"
},
+ "type": "module",
"files": [
"dist",
"src",
"README.md"
],
- "source": "./src/index.ts",
- "main": "./dist/main.js",
- "module": "./dist/module.mjs",
- "types": "./dist/index.d.ts",
+ "main": "./dist/index.js",
"exports": {
+ "./package.json": "./package.json",
".": {
- "types": "./dist/index.d.ts",
- "import": "./dist/module.mjs",
- "require": "./dist/main.js"
+ "import": "./dist/index.js",
+ "default": "./dist/index.cjs"
},
"./dist/*.css": "./dist/*.css",
"./src/*": "./src/*"
@@ -45,12 +43,13 @@
"quanta"
],
"scripts": {
- "build": "parcel build && pnpm build:css",
- "build:force": "parcel build --no-cache && pnpm build:css",
+ "build": "tsup && pnpm build:css",
+ "build:force": "tsup && pnpm build:css",
"build:css": "pnpm build:basic && pnpm build:quanta",
"build:basic": "lightningcss --browserslist --bundle --sourcemap src/styles/basic/main.css -o basic.css && mv basic.css* dist/.",
"build:quanta": "lightningcss --browserslist --bundle --sourcemap src/styles/quanta/main.css -o quanta.css && mv quanta.css* dist/.",
- "check-ts": "tsc --project tsconfig.json",
+ "check:exports": "attw --pack .",
+ "check:ts": "tsc --project tsconfig.json",
"test": "vitest --passWithNoTests",
"coverage": "vitest run --coverage --no-threads",
"lint": "pnpm eslint && pnpm prettier && pnpm stylelint && pnpm check-ts",
@@ -77,12 +76,7 @@
"not dead"
],
"devDependencies": {
- "@parcel/config-default": "^2.12.0",
- "@parcel/core": "^2.12.0",
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-js": "^2.12.0",
- "@parcel/transformer-react-refresh-wrap": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
+ "@arethetypeswrong/cli": "^0.16.4",
"@plone/types": "workspace: *",
"@react-types/shared": "^3.22.0",
"@storybook/addon-essentials": "^8.0.4",
@@ -107,20 +101,20 @@
"jsdom": "^22.1.0",
"lightningcss": "^1.24.0",
"lightningcss-cli": "^1.24.0",
- "parcel": "^2.12.0",
"release-it": "17.1.1",
"storybook": "^8.0.4",
- "typescript": "^5.4.5",
- "vite": "^5.1.7",
- "vitest": "^1.3.1",
+ "tsup": "^8.3.5",
+ "typescript": "^5.6.3",
+ "vite": "^5.4.8",
+ "vitest": "^2.1.3",
"vitest-axe": "^0.1.0"
},
"dependencies": {
- "@react-aria/utils": "^3.22.0",
- "@react-spectrum/utils": "^3.11.1",
+ "@react-aria/utils": "^3.26.0",
+ "@react-spectrum/utils": "^3.12.0",
"@storybook/test": "^8.0.4",
- "clsx": "^2.0.0",
- "react-aria-components": "^1.2.0"
+ "clsx": "^2.1.1",
+ "react-aria-components": "^1.5.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
diff --git a/packages/components/src/components/BlockToolbar/BlockToolbar.tsx b/packages/components/src/components/BlockToolbar/BlockToolbar.tsx
index 62f1b47922..f76ba5ef83 100644
--- a/packages/components/src/components/BlockToolbar/BlockToolbar.tsx
+++ b/packages/components/src/components/BlockToolbar/BlockToolbar.tsx
@@ -1,5 +1,8 @@
import React from 'react';
-import { Toolbar as RACToolbar, ToolbarProps } from 'react-aria-components';
+import {
+ Toolbar as RACToolbar,
+ type ToolbarProps,
+} from 'react-aria-components';
export function BlockToolbar(props: ToolbarProps) {
return ;
diff --git a/packages/components/src/components/Button/Button.tsx b/packages/components/src/components/Button/Button.tsx
index 9e8c76b371..c466e8a1dd 100644
--- a/packages/components/src/components/Button/Button.tsx
+++ b/packages/components/src/components/Button/Button.tsx
@@ -1,5 +1,5 @@
-import React, { forwardRef, ForwardedRef } from 'react';
-import { Button as RACButton, ButtonProps } from 'react-aria-components';
+import React, { forwardRef, type ForwardedRef } from 'react';
+import { Button as RACButton, type ButtonProps } from 'react-aria-components';
export const Button = forwardRef(function _Button(
props: ButtonProps,
diff --git a/packages/components/src/components/Calendar/Calendar.tsx b/packages/components/src/components/Calendar/Calendar.tsx
index 37f3b489c4..faf32a12d7 100644
--- a/packages/components/src/components/Calendar/Calendar.tsx
+++ b/packages/components/src/components/Calendar/Calendar.tsx
@@ -4,8 +4,8 @@ import {
Calendar as RACCalendar,
CalendarCell,
CalendarGrid,
- CalendarProps as RACCalendarProps,
- DateValue,
+ type CalendarProps as RACCalendarProps,
+ type DateValue,
Heading,
Text,
} from 'react-aria-components';
diff --git a/packages/components/src/components/Checkbox/Checkbox.tsx b/packages/components/src/components/Checkbox/Checkbox.tsx
index a24407dd5b..47eaca3ef5 100644
--- a/packages/components/src/components/Checkbox/Checkbox.tsx
+++ b/packages/components/src/components/Checkbox/Checkbox.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {
Checkbox as RACCheckbox,
- CheckboxProps as RACCheckboxProps,
+ type CheckboxProps as RACCheckboxProps,
} from 'react-aria-components';
interface CheckboxProps extends RACCheckboxProps {
diff --git a/packages/components/src/components/CheckboxGroup/CheckboxGroup.tsx b/packages/components/src/components/CheckboxGroup/CheckboxGroup.tsx
index 0c29059708..d0106355dd 100644
--- a/packages/components/src/components/CheckboxGroup/CheckboxGroup.tsx
+++ b/packages/components/src/components/CheckboxGroup/CheckboxGroup.tsx
@@ -1,10 +1,10 @@
import React from 'react';
import {
CheckboxGroup as RACCheckboxGroup,
- CheckboxGroupProps as RACCheckboxGroupProps,
+ type CheckboxGroupProps as RACCheckboxGroupProps,
FieldError,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface CheckboxGroupProps
diff --git a/packages/components/src/components/ColorArea/ColorArea.stories.tsx b/packages/components/src/components/ColorArea/ColorArea.stories.tsx
new file mode 100644
index 0000000000..b4ac8b95ea
--- /dev/null
+++ b/packages/components/src/components/ColorArea/ColorArea.stories.tsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+import { ColorArea } from './ColorArea';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorArea.css';
+
+const meta = {
+ title: 'Widgets/ColorArea',
+ component: ColorArea,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => ,
+};
+
+Default.args = {
+ defaultValue: 'hsl(30, 100%, 50%)',
+};
diff --git a/packages/components/src/components/ColorArea/ColorArea.tsx b/packages/components/src/components/ColorArea/ColorArea.tsx
new file mode 100644
index 0000000000..3f060b6721
--- /dev/null
+++ b/packages/components/src/components/ColorArea/ColorArea.tsx
@@ -0,0 +1,14 @@
+import * as React from 'react';
+import {
+ ColorArea as RACColorArea,
+ type ColorAreaProps,
+ ColorThumb,
+} from 'react-aria-components';
+
+export function ColorArea(props: ColorAreaProps) {
+ return (
+
+
+
+ );
+}
diff --git a/packages/components/src/components/ColorField/ColorField.stories.tsx b/packages/components/src/components/ColorField/ColorField.stories.tsx
new file mode 100644
index 0000000000..6621ebec66
--- /dev/null
+++ b/packages/components/src/components/ColorField/ColorField.stories.tsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+import { ColorField } from './ColorField';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorField.css';
+
+const meta = {
+ title: 'Widgets/ColorField',
+ component: ColorField,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => ,
+};
+
+Default.args = {
+ label: 'Color',
+};
diff --git a/packages/components/src/components/ColorField/ColorField.tsx b/packages/components/src/components/ColorField/ColorField.tsx
new file mode 100644
index 0000000000..ac3f00f62e
--- /dev/null
+++ b/packages/components/src/components/ColorField/ColorField.tsx
@@ -0,0 +1,32 @@
+import * as React from 'react';
+import {
+ ColorField as RACColorField,
+ type ColorFieldProps as RACColorFieldProps,
+ FieldError,
+ Input,
+ Label,
+ Text,
+ type ValidationResult,
+} from 'react-aria-components';
+
+export interface ColorFieldProps extends RACColorFieldProps {
+ label?: string;
+ description?: string;
+ errorMessage?: string | ((validation: ValidationResult) => string);
+}
+
+export function ColorField({
+ label,
+ description,
+ errorMessage,
+ ...props
+}: ColorFieldProps) {
+ return (
+
+ {label && }
+
+ {description && {description}}
+ {errorMessage}
+
+ );
+}
diff --git a/packages/components/src/components/ColorPicker/ColorPicker.stories.tsx b/packages/components/src/components/ColorPicker/ColorPicker.stories.tsx
new file mode 100644
index 0000000000..686b2dd859
--- /dev/null
+++ b/packages/components/src/components/ColorPicker/ColorPicker.stories.tsx
@@ -0,0 +1,38 @@
+import * as React from 'react';
+import { ColorPicker } from './ColorPicker';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorPicker.css';
+
+const meta = {
+ title: 'Widgets/ColorPicker',
+ component: ColorPicker,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+const ColorPickerStory = (args: any) => {
+ const [color, setColor] = React.useState(args.defaultValue);
+
+ return (
+ <>
+
+ The color is: {color.toString('hex')}
+ >
+ );
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: ColorPickerStory,
+};
+
+Default.args = {
+ label: 'Fill color',
+ defaultValue: '#f00',
+};
diff --git a/packages/components/src/components/ColorPicker/ColorPicker.tsx b/packages/components/src/components/ColorPicker/ColorPicker.tsx
new file mode 100644
index 0000000000..f8bed1f0bd
--- /dev/null
+++ b/packages/components/src/components/ColorPicker/ColorPicker.tsx
@@ -0,0 +1,53 @@
+import * as React from 'react';
+import {
+ Button,
+ ColorPicker as RACColorPicker,
+ type ColorPickerProps as RACColorPickerProps,
+ Dialog,
+ DialogTrigger,
+ Popover,
+} from 'react-aria-components';
+import { ColorSwatch } from '../ColorSwatch/ColorSwatch';
+import { ColorSlider } from '../ColorSlider/ColorSlider';
+import { ColorArea } from '../ColorArea/ColorArea';
+import { ColorField } from '../ColorField/ColorField';
+
+export interface ColorPickerProps extends RACColorPickerProps {
+ label?: string;
+ children?: React.ReactNode;
+}
+
+export function ColorPicker({ label, children, ...props }: ColorPickerProps) {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/components/src/components/ColorSlider/ColorSlider.stories.tsx b/packages/components/src/components/ColorSlider/ColorSlider.stories.tsx
new file mode 100644
index 0000000000..a0a363489e
--- /dev/null
+++ b/packages/components/src/components/ColorSlider/ColorSlider.stories.tsx
@@ -0,0 +1,28 @@
+import * as React from 'react';
+import { ColorSlider } from './ColorSlider';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorSlider.css';
+
+const meta = {
+ title: 'Widgets/ColorSlider',
+ component: ColorSlider,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => ,
+};
+
+Default.args = {
+ label: 'Red Opacity',
+ defaultValue: '#f00',
+ channel: 'alpha',
+};
diff --git a/packages/components/src/components/ColorSlider/ColorSlider.tsx b/packages/components/src/components/ColorSlider/ColorSlider.tsx
new file mode 100644
index 0000000000..de9afa32c2
--- /dev/null
+++ b/packages/components/src/components/ColorSlider/ColorSlider.tsx
@@ -0,0 +1,30 @@
+import * as React from 'react';
+import {
+ ColorSlider as RACColorSlider,
+ type ColorSliderProps as RACColorSliderProps,
+ ColorThumb,
+ Label,
+ SliderOutput,
+ SliderTrack,
+} from 'react-aria-components';
+
+export interface ColorSliderProps extends RACColorSliderProps {
+ label?: string;
+}
+
+export function ColorSlider({ label, ...props }: ColorSliderProps) {
+ return (
+
+
+
+ ({
+ background: `${defaultStyle.background},
+ repeating-conic-gradient(#CCC 0% 25%, white 0% 50%) 50% / 16px 16px`,
+ })}
+ >
+
+
+
+ );
+}
diff --git a/packages/components/src/components/ColorSwatch/ColorSwatch.stories.tsx b/packages/components/src/components/ColorSwatch/ColorSwatch.stories.tsx
new file mode 100644
index 0000000000..0168fa94f5
--- /dev/null
+++ b/packages/components/src/components/ColorSwatch/ColorSwatch.stories.tsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+import { ColorSwatch } from './ColorSwatch';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorSwatch.css';
+
+const meta = {
+ title: 'Widgets/ColorSwatch',
+ component: ColorSwatch,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => ,
+};
+
+Default.args = {
+ color: '#f00a',
+};
diff --git a/packages/components/src/components/ColorSwatch/ColorSwatch.tsx b/packages/components/src/components/ColorSwatch/ColorSwatch.tsx
new file mode 100644
index 0000000000..1415622497
--- /dev/null
+++ b/packages/components/src/components/ColorSwatch/ColorSwatch.tsx
@@ -0,0 +1,17 @@
+import * as React from 'react';
+import {
+ ColorSwatch as RACColorSwatch,
+ type ColorSwatchProps,
+} from 'react-aria-components';
+
+export function ColorSwatch(props: ColorSwatchProps) {
+ return (
+ ({
+ background: `linear-gradient(${color}, ${color}),
+ repeating-conic-gradient(#CCC 0% 25%, white 0% 50%) 50% / 16px 16px`,
+ })}
+ />
+ );
+}
diff --git a/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.stories.tsx b/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.stories.tsx
new file mode 100644
index 0000000000..cba4b2a099
--- /dev/null
+++ b/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.stories.tsx
@@ -0,0 +1,31 @@
+import * as React from 'react';
+import { ColorSwatchPicker, ColorSwatchPickerItem } from './ColorSwatchPicker';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorSwatchPicker.css';
+
+const meta = {
+ title: 'Widgets/ColorSwatchPicker',
+ component: ColorSwatchPicker,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => (
+
+
+
+
+
+
+
+
+ ),
+};
diff --git a/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.tsx b/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.tsx
new file mode 100644
index 0000000000..2d1598980a
--- /dev/null
+++ b/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.tsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+import {
+ ColorSwatchPicker as AriaColorSwatchPicker,
+ ColorSwatchPickerItem as AriaColorSwatchPickerItem,
+ type ColorSwatchPickerItemProps,
+ type ColorSwatchPickerProps,
+} from 'react-aria-components';
+
+import { ColorSwatch } from '../ColorSwatch/ColorSwatch';
+
+export function ColorSwatchPicker({
+ children,
+ ...props
+}: ColorSwatchPickerProps) {
+ return {children};
+}
+
+export { ColorSwatchPicker as MyColorSwatchPicker };
+
+export function ColorSwatchPickerItem(props: ColorSwatchPickerItemProps) {
+ return (
+
+
+
+ );
+}
diff --git a/packages/components/src/components/ColorWheel/ColorWheel.stories.tsx b/packages/components/src/components/ColorWheel/ColorWheel.stories.tsx
new file mode 100644
index 0000000000..5d5d4278c9
--- /dev/null
+++ b/packages/components/src/components/ColorWheel/ColorWheel.stories.tsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+import { ColorWheel } from './ColorWheel';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorWheel.css';
+
+const meta = {
+ title: 'Widgets/ColorWheel',
+ component: ColorWheel,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => ,
+};
+
+Default.args = {
+ defaultValue: 'hsl(30, 100%, 50%)',
+};
diff --git a/packages/components/src/components/ColorWheel/ColorWheel.tsx b/packages/components/src/components/ColorWheel/ColorWheel.tsx
new file mode 100644
index 0000000000..fd601243bb
--- /dev/null
+++ b/packages/components/src/components/ColorWheel/ColorWheel.tsx
@@ -0,0 +1,21 @@
+import * as React from 'react';
+import {
+ ColorThumb,
+ ColorWheel as AriaColorWheel,
+ type ColorWheelProps as AriaColorWheelProps,
+ ColorWheelTrack,
+} from 'react-aria-components';
+
+export interface ColorWheelProps
+ extends Omit {}
+
+export function ColorWheel(props: ColorWheelProps) {
+ return (
+
+
+
+
+ );
+}
+
+export { ColorWheel as MyColorWheel };
diff --git a/packages/components/src/components/ComboBox/ComboBox.tsx b/packages/components/src/components/ComboBox/ComboBox.tsx
index f3cc54422c..dae5e3767d 100644
--- a/packages/components/src/components/ComboBox/ComboBox.tsx
+++ b/packages/components/src/components/ComboBox/ComboBox.tsx
@@ -2,16 +2,16 @@ import React from 'react';
import {
Button,
ComboBox as RACComboBox,
- ComboBoxProps as RACComboBoxProps,
+ type ComboBoxProps as RACComboBoxProps,
FieldError,
Input,
Label,
ListBox,
ListBoxItem,
- ListBoxItemProps,
+ type ListBoxItemProps,
Popover,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface ComboBoxProps
diff --git a/packages/components/src/components/DateField/DateField.tsx b/packages/components/src/components/DateField/DateField.tsx
index 8f41e0a680..6bbca4b9c8 100644
--- a/packages/components/src/components/DateField/DateField.tsx
+++ b/packages/components/src/components/DateField/DateField.tsx
@@ -1,14 +1,14 @@
import React from 'react';
import {
DateField as RACDateField,
- DateFieldProps as RACDateFieldProps,
+ type DateFieldProps as RACDateFieldProps,
DateInput,
DateSegment,
- DateValue,
+ type DateValue,
FieldError,
Label,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface DateFieldProps
diff --git a/packages/components/src/components/DatePicker/DatePicker.tsx b/packages/components/src/components/DatePicker/DatePicker.tsx
index b49bc7e493..9db14f1278 100644
--- a/packages/components/src/components/DatePicker/DatePicker.tsx
+++ b/packages/components/src/components/DatePicker/DatePicker.tsx
@@ -6,9 +6,9 @@ import {
CalendarGrid,
DateInput,
DatePicker as RACDatePicker,
- DatePickerProps as RACDatePickerProps,
+ type DatePickerProps as RACDatePickerProps,
DateSegment,
- DateValue,
+ type DateValue,
Dialog,
FieldError,
Group,
@@ -16,7 +16,7 @@ import {
Label,
Popover,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface DatePickerProps
diff --git a/packages/components/src/components/DateRangePicker/DateRangePicker.tsx b/packages/components/src/components/DateRangePicker/DateRangePicker.tsx
index 55b76c5fde..9a00fd0a44 100644
--- a/packages/components/src/components/DateRangePicker/DateRangePicker.tsx
+++ b/packages/components/src/components/DateRangePicker/DateRangePicker.tsx
@@ -5,9 +5,9 @@ import {
CalendarGrid,
DateInput,
DateRangePicker as RACDateRangePicker,
- DateRangePickerProps as RACDateRangePickerProps,
+ type DateRangePickerProps as RACDateRangePickerProps,
DateSegment,
- DateValue,
+ type DateValue,
Dialog,
FieldError,
Group,
@@ -16,7 +16,7 @@ import {
Popover,
RangeCalendar,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface DateRangePickerProps
diff --git a/packages/components/src/components/Dialog/Dialog.tsx b/packages/components/src/components/Dialog/Dialog.tsx
index 39de5afd8e..503f87f9d5 100644
--- a/packages/components/src/components/Dialog/Dialog.tsx
+++ b/packages/components/src/components/Dialog/Dialog.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { Dialog as RACDialog, DialogProps } from 'react-aria-components';
+import { Dialog as RACDialog, type DialogProps } from 'react-aria-components';
export function Dialog(props: DialogProps) {
return ;
diff --git a/packages/components/src/components/Disclosure/Disclosure.stories.tsx b/packages/components/src/components/Disclosure/Disclosure.stories.tsx
new file mode 100644
index 0000000000..c318a693b4
--- /dev/null
+++ b/packages/components/src/components/Disclosure/Disclosure.stories.tsx
@@ -0,0 +1,39 @@
+import * as React from 'react';
+import { Disclosure } from './Disclosure';
+import {
+ Button,
+ DisclosurePanel as DisclosurePanel,
+} from 'react-aria-components';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/Disclosure.css';
+
+const meta = {
+ component: Disclosure,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => (
+
+
+
+
+
+
Details about system requirements here.
+
+
+ ),
+};
diff --git a/packages/components/src/components/Disclosure/Disclosure.tsx b/packages/components/src/components/Disclosure/Disclosure.tsx
new file mode 100644
index 0000000000..7322a5818b
--- /dev/null
+++ b/packages/components/src/components/Disclosure/Disclosure.tsx
@@ -0,0 +1,12 @@
+import * as React from 'react';
+import {
+ Disclosure as RACDisclosure,
+ type DisclosureProps,
+} from 'react-aria-components';
+
+/**
+ * A Disclosure is used to show or hide content that is not visible by default.
+ */
+export function Disclosure(props: DisclosureProps) {
+ return ;
+}
diff --git a/packages/components/src/components/DisclosureGroup/DisclosureGroup.stories.tsx b/packages/components/src/components/DisclosureGroup/DisclosureGroup.stories.tsx
new file mode 100644
index 0000000000..c51881e461
--- /dev/null
+++ b/packages/components/src/components/DisclosureGroup/DisclosureGroup.stories.tsx
@@ -0,0 +1,57 @@
+import * as React from 'react';
+import { DisclosureGroup } from './DisclosureGroup';
+import {
+ Button,
+ Disclosure as Disclosure,
+ DisclosurePanel as DisclosurePanel,
+} from 'react-aria-components';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+const meta = {
+ component: DisclosureGroup,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => (
+
+
+
+
+
+
+
Personal information form here.
+
+
+
+
+
+
+
+
Billing address form here.
+
+
+
+ ),
+};
+
+Default.args = {
+ defaultExpandedKeys: ['personal'],
+};
diff --git a/packages/components/src/components/DisclosureGroup/DisclosureGroup.tsx b/packages/components/src/components/DisclosureGroup/DisclosureGroup.tsx
new file mode 100644
index 0000000000..980950ee75
--- /dev/null
+++ b/packages/components/src/components/DisclosureGroup/DisclosureGroup.tsx
@@ -0,0 +1,12 @@
+import * as React from 'react';
+import {
+ DisclosureGroup as RACDisclosureGroup,
+ type DisclosureGroupProps,
+} from 'react-aria-components';
+
+/**
+ * A DisclosureGroup is used to group Disclosures together to create an accordion.
+ */
+export function DisclosureGroup(props: DisclosureGroupProps) {
+ return ;
+}
diff --git a/packages/components/src/components/Form/Form.tsx b/packages/components/src/components/Form/Form.tsx
index f2f2e9b70a..03fd907bef 100644
--- a/packages/components/src/components/Form/Form.tsx
+++ b/packages/components/src/components/Form/Form.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { Form as RACForm, FormProps } from 'react-aria-components';
+import { Form as RACForm, type FormProps } from 'react-aria-components';
export function Form(props: FormProps) {
return ;
diff --git a/packages/components/src/components/GridList/GridList.tsx b/packages/components/src/components/GridList/GridList.tsx
index d195b71cfd..088235641d 100644
--- a/packages/components/src/components/GridList/GridList.tsx
+++ b/packages/components/src/components/GridList/GridList.tsx
@@ -3,8 +3,8 @@ import {
Button,
GridList as RACGridList,
GridListItem as RACGridListItem,
- GridListItemProps,
- GridListProps,
+ type GridListItemProps,
+ type GridListProps,
} from 'react-aria-components';
import { Checkbox } from '../Checkbox/Checkbox';
diff --git a/packages/components/src/components/Icon/Icon.tsx b/packages/components/src/components/Icon/Icon.tsx
index 0056688024..c55a91521e 100644
--- a/packages/components/src/components/Icon/Icon.tsx
+++ b/packages/components/src/components/Icon/Icon.tsx
@@ -1,4 +1,4 @@
-import React, { ReactElement } from 'react';
+import React, { type ReactElement } from 'react';
import type {
AriaLabelingProps,
DOMProps,
@@ -7,7 +7,7 @@ import type {
} from '@react-types/shared';
import {
baseStyleProps,
- StyleHandlers,
+ type StyleHandlers,
useSlotProps,
useStyleProps,
} from '@react-spectrum/utils';
diff --git a/packages/components/src/components/Link/Link.tsx b/packages/components/src/components/Link/Link.tsx
index 307c6528d3..4923f3218c 100644
--- a/packages/components/src/components/Link/Link.tsx
+++ b/packages/components/src/components/Link/Link.tsx
@@ -1,20 +1 @@
-import React from 'react';
-import { forwardRef, ForwardedRef } from 'react';
-import { Link as RACLink, LinkProps } from 'react-aria-components';
-import { useFlattenToAppURL } from '../../providers/flattenToAppURL';
-
-type forwardRefType = typeof forwardRef;
-
-const Link = (props: LinkProps, ref: ForwardedRef) => {
- const { flattenToAppURL } = useFlattenToAppURL();
- const flattenedURL = flattenToAppURL(props.href);
-
- return (
-
- {props.children}
-
- );
-};
-
-const _Link = /*#__PURE__*/ (forwardRef as forwardRefType)(Link);
-export { _Link as Link };
+export { Link } from 'react-aria-components';
diff --git a/packages/components/src/components/ListBox/ListBox.tsx b/packages/components/src/components/ListBox/ListBox.tsx
index ac90dbe68f..244efb16a4 100644
--- a/packages/components/src/components/ListBox/ListBox.tsx
+++ b/packages/components/src/components/ListBox/ListBox.tsx
@@ -2,8 +2,8 @@ import React from 'react';
import {
ListBox as RACListBox,
ListBoxItem as RACListBoxItem,
- ListBoxItemProps,
- ListBoxProps,
+ type ListBoxItemProps,
+ type ListBoxProps,
} from 'react-aria-components';
export function ListBox({
diff --git a/packages/components/src/components/Menu/Menu.tsx b/packages/components/src/components/Menu/Menu.tsx
index db153971a6..14834205cd 100644
--- a/packages/components/src/components/Menu/Menu.tsx
+++ b/packages/components/src/components/Menu/Menu.tsx
@@ -2,12 +2,12 @@ import React from 'react';
import {
Menu as RACMenu,
MenuItem as RACMenuItem,
- MenuItemProps,
- MenuProps,
+ type MenuItemProps,
+ type MenuProps,
MenuTrigger,
- MenuTriggerProps,
+ type MenuTriggerProps,
Popover,
- PressEvent,
+ type PressEvent,
} from 'react-aria-components';
import { Button } from '../Button/Button';
diff --git a/packages/components/src/components/Meter/Meter.tsx b/packages/components/src/components/Meter/Meter.tsx
index fb06f8c57c..71da9815c6 100644
--- a/packages/components/src/components/Meter/Meter.tsx
+++ b/packages/components/src/components/Meter/Meter.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import {
Label,
Meter as RACMeter,
- MeterProps as RACMeterProps,
+ type MeterProps as RACMeterProps,
} from 'react-aria-components';
export interface MeterProps extends RACMeterProps {
diff --git a/packages/components/src/components/Modal/Modal.tsx b/packages/components/src/components/Modal/Modal.tsx
index 83eb2e061c..2ef1c970cb 100644
--- a/packages/components/src/components/Modal/Modal.tsx
+++ b/packages/components/src/components/Modal/Modal.tsx
@@ -1,5 +1,8 @@
import React from 'react';
-import { Modal as RACModal, ModalOverlayProps } from 'react-aria-components';
+import {
+ Modal as RACModal,
+ type ModalOverlayProps,
+} from 'react-aria-components';
export function Modal(props: ModalOverlayProps) {
return ;
diff --git a/packages/components/src/components/NumberField/NumberField.tsx b/packages/components/src/components/NumberField/NumberField.tsx
index 98781966fe..462910c32d 100644
--- a/packages/components/src/components/NumberField/NumberField.tsx
+++ b/packages/components/src/components/NumberField/NumberField.tsx
@@ -5,9 +5,9 @@ import {
Input,
Label,
NumberField as RACNumberField,
- NumberFieldProps as RACNumberFieldProps,
+ type NumberFieldProps as RACNumberFieldProps,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
import { Button } from '../Button/Button';
import { AddIcon } from '../Icons/AddIcon';
diff --git a/packages/components/src/components/Popover/Popover.tsx b/packages/components/src/components/Popover/Popover.tsx
index a79c3d1418..8a0614c9b6 100644
--- a/packages/components/src/components/Popover/Popover.tsx
+++ b/packages/components/src/components/Popover/Popover.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import {
OverlayArrow,
Popover as RACPopover,
- PopoverProps as RACPopoverProps,
+ type PopoverProps as RACPopoverProps,
} from 'react-aria-components';
import { Dialog } from '../Dialog/Dialog';
diff --git a/packages/components/src/components/ProgressBar/ProgressBar.tsx b/packages/components/src/components/ProgressBar/ProgressBar.tsx
index cb72f500f5..2fa1f49ca8 100644
--- a/packages/components/src/components/ProgressBar/ProgressBar.tsx
+++ b/packages/components/src/components/ProgressBar/ProgressBar.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import {
Label,
ProgressBar as RACProgressBar,
- ProgressBarProps as RACProgressBarProps,
+ type ProgressBarProps as RACProgressBarProps,
} from 'react-aria-components';
export interface ProgressBarProps extends RACProgressBarProps {
diff --git a/packages/components/src/components/RadioGroup/RadioGroup.tsx b/packages/components/src/components/RadioGroup/RadioGroup.tsx
index 90fea840d4..7090d0f15a 100644
--- a/packages/components/src/components/RadioGroup/RadioGroup.tsx
+++ b/packages/components/src/components/RadioGroup/RadioGroup.tsx
@@ -3,9 +3,9 @@ import {
FieldError,
Label,
RadioGroup as RACRadioGroup,
- RadioGroupProps as RACRadioGroupProps,
+ type RadioGroupProps as RACRadioGroupProps,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface RadioGroupProps extends Omit {
diff --git a/packages/components/src/components/RangeCalendar/RangeCalendar.tsx b/packages/components/src/components/RangeCalendar/RangeCalendar.tsx
index 7881028477..63e32cdcab 100644
--- a/packages/components/src/components/RangeCalendar/RangeCalendar.tsx
+++ b/packages/components/src/components/RangeCalendar/RangeCalendar.tsx
@@ -3,10 +3,10 @@ import {
Button,
CalendarCell,
CalendarGrid,
- DateValue,
+ type DateValue,
Heading,
RangeCalendar as RACRangeCalendar,
- RangeCalendarProps as RACRangeCalendarProps,
+ type RangeCalendarProps as RACRangeCalendarProps,
Text,
} from 'react-aria-components';
diff --git a/packages/components/src/components/SearchField/SearchField.tsx b/packages/components/src/components/SearchField/SearchField.tsx
index 57f7946d4f..dc5c3964b3 100644
--- a/packages/components/src/components/SearchField/SearchField.tsx
+++ b/packages/components/src/components/SearchField/SearchField.tsx
@@ -5,9 +5,9 @@ import {
Input,
Label,
SearchField as RACSearchField,
- SearchFieldProps as RACSearchFieldProps,
+ type SearchFieldProps as RACSearchFieldProps,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface SearchFieldProps extends RACSearchFieldProps {
diff --git a/packages/components/src/components/Select/Select.stories.tsx b/packages/components/src/components/Select/Select.stories.tsx
index bec5ec99be..7adb2e0afc 100644
--- a/packages/components/src/components/Select/Select.stories.tsx
+++ b/packages/components/src/components/Select/Select.stories.tsx
@@ -4,11 +4,6 @@ import type { Meta, StoryObj } from '@storybook/react';
import '../../styles/basic/Select.css';
-export interface SelectItemObject {
- label: string;
- value: string;
-}
-
// More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction
const meta = {
title: 'Forms/Select',
@@ -51,14 +46,6 @@ export const Default: Story = {
* Select renders options via render props `(item)=> React.ReactNode`
*/
export const Items: Story = {
- render: (args) => (
- // @ts-ignore I assume this is a storybook bug when passing args
-
- ),
args: {
name: 'field-empty',
label: 'field 1 title',
@@ -81,14 +68,6 @@ export const Items: Story = {
};
export const LotsOfItems: Story = {
- render: (args) => (
- // @ts-ignore I assume this is a storybook bug when passing args
-
- ),
args: {
name: 'field-empty',
label: 'field 1 title',
diff --git a/packages/components/src/components/Select/Select.tsx b/packages/components/src/components/Select/Select.tsx
index fdbe84c784..5cb8f71488 100644
--- a/packages/components/src/components/Select/Select.tsx
+++ b/packages/components/src/components/Select/Select.tsx
@@ -5,15 +5,15 @@ import {
Label,
ListBox,
ListBoxItem,
- ListBoxItemProps,
+ type ListBoxItemProps,
Popover,
PopoverContext,
Select as RACSelect,
- SelectProps as RACSelectProps,
+ type SelectProps as RACSelectProps,
SelectValue,
Text,
useContextProps,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
import { ChevrondownIcon } from '../Icons/ChevrondownIcon';
@@ -43,7 +43,7 @@ export interface SelectProps
* the data.
*
*/
-export function Select({
+export function Select({
label,
description,
errorMessage,
@@ -69,7 +69,15 @@ export function Select({
{description && {description}}
{errorMessage}
- {children}
+ {children ? (
+ {children}
+ ) : (
+
+ {(item) => (
+ {item.value}
+ )}
+
+ )}
>
)}
@@ -77,6 +85,11 @@ export function Select({
);
}
+export type SelectItemObject = {
+ label: string;
+ value: string;
+};
+
export function SelectItem(props: ListBoxItemProps) {
return ;
}
diff --git a/packages/components/src/components/Slider/Slider.tsx b/packages/components/src/components/Slider/Slider.tsx
index f09e907195..4fab667edf 100644
--- a/packages/components/src/components/Slider/Slider.tsx
+++ b/packages/components/src/components/Slider/Slider.tsx
@@ -3,7 +3,7 @@ import {
Label,
Slider as RACSlider,
SliderOutput,
- SliderProps as RACSliderProps,
+ type SliderProps as RACSliderProps,
SliderThumb,
SliderTrack,
} from 'react-aria-components';
diff --git a/packages/components/src/components/Switch/Switch.tsx b/packages/components/src/components/Switch/Switch.tsx
index 5f0f2d0b64..0cd187b092 100644
--- a/packages/components/src/components/Switch/Switch.tsx
+++ b/packages/components/src/components/Switch/Switch.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {
Switch as RACSwitch,
- SwitchProps as RACSwitchProps,
+ type SwitchProps as RACSwitchProps,
} from 'react-aria-components';
export interface SwitchProps extends Omit {
diff --git a/packages/components/src/components/Tabs/Tabs.tsx b/packages/components/src/components/Tabs/Tabs.tsx
index a0d4ab8cfd..7190ee2cee 100644
--- a/packages/components/src/components/Tabs/Tabs.tsx
+++ b/packages/components/src/components/Tabs/Tabs.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { Tabs as RACTabs, TabsProps } from 'react-aria-components';
+import { Tabs as RACTabs, type TabsProps } from 'react-aria-components';
export function Tabs(props: TabsProps) {
return ;
diff --git a/packages/components/src/components/TagGroup/TagGroup.tsx b/packages/components/src/components/TagGroup/TagGroup.tsx
index 1424dea383..05230519f1 100644
--- a/packages/components/src/components/TagGroup/TagGroup.tsx
+++ b/packages/components/src/components/TagGroup/TagGroup.tsx
@@ -4,10 +4,10 @@ import {
Label,
Tag as RACTag,
TagGroup as RACTagGroup,
- TagGroupProps as RACTagGroupProps,
+ type TagGroupProps as RACTagGroupProps,
TagList,
- TagListProps,
- TagProps,
+ type TagListProps,
+ type TagProps,
Text,
} from 'react-aria-components';
diff --git a/packages/components/src/components/TextAreaField/TextAreaField.tsx b/packages/components/src/components/TextAreaField/TextAreaField.tsx
index e79f5e78bb..76ee55ea8d 100644
--- a/packages/components/src/components/TextAreaField/TextAreaField.tsx
+++ b/packages/components/src/components/TextAreaField/TextAreaField.tsx
@@ -5,8 +5,8 @@ import {
Label,
Text,
TextField as RACTextField,
- TextFieldProps as RACTextFieldProps,
- ValidationResult,
+ type TextFieldProps as RACTextFieldProps,
+ type ValidationResult,
} from 'react-aria-components';
export interface TextAreaFieldProps extends RACTextFieldProps {
diff --git a/packages/components/src/components/TextField/TextField.tsx b/packages/components/src/components/TextField/TextField.tsx
index 2b29fa3e79..1190aea2a1 100644
--- a/packages/components/src/components/TextField/TextField.tsx
+++ b/packages/components/src/components/TextField/TextField.tsx
@@ -5,8 +5,8 @@ import {
Label,
Text,
TextField as RACTextField,
- TextFieldProps as RACTextFieldProps,
- ValidationResult,
+ type TextFieldProps as RACTextFieldProps,
+ type ValidationResult,
} from 'react-aria-components';
export interface TextFieldProps extends RACTextFieldProps {
diff --git a/packages/components/src/components/TimeField/TimeField.tsx b/packages/components/src/components/TimeField/TimeField.tsx
index 4133c46c1c..c92e531326 100644
--- a/packages/components/src/components/TimeField/TimeField.tsx
+++ b/packages/components/src/components/TimeField/TimeField.tsx
@@ -6,9 +6,9 @@ import {
Label,
Text,
TimeField as RACTimeField,
- TimeFieldProps as RACTimeFieldProps,
- TimeValue,
- ValidationResult,
+ type TimeFieldProps as RACTimeFieldProps,
+ type TimeValue,
+ type ValidationResult,
} from 'react-aria-components';
export interface TimeFieldProps
diff --git a/packages/components/src/components/ToggleButton/ToggleButton.tsx b/packages/components/src/components/ToggleButton/ToggleButton.tsx
index f7599fd6af..546d47fc2b 100644
--- a/packages/components/src/components/ToggleButton/ToggleButton.tsx
+++ b/packages/components/src/components/ToggleButton/ToggleButton.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {
ToggleButton as RACToggleButton,
- ToggleButtonProps,
+ type ToggleButtonProps,
} from 'react-aria-components';
export function ToggleButton(props: ToggleButtonProps) {
diff --git a/packages/components/src/components/Toolbar/Toolbar.tsx b/packages/components/src/components/Toolbar/Toolbar.tsx
index f2b151bcce..b99af182f4 100644
--- a/packages/components/src/components/Toolbar/Toolbar.tsx
+++ b/packages/components/src/components/Toolbar/Toolbar.tsx
@@ -1,5 +1,8 @@
import React from 'react';
-import { Toolbar as RACToolbar, ToolbarProps } from 'react-aria-components';
+import {
+ Toolbar as RACToolbar,
+ type ToolbarProps,
+} from 'react-aria-components';
export function Toolbar(props: ToolbarProps) {
return ;
diff --git a/packages/components/src/components/Tooltip/Tooltip.tsx b/packages/components/src/components/Tooltip/Tooltip.tsx
index 17213e6ce3..b629c5f39e 100644
--- a/packages/components/src/components/Tooltip/Tooltip.tsx
+++ b/packages/components/src/components/Tooltip/Tooltip.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import {
OverlayArrow,
Tooltip as RACTooltip,
- TooltipProps as RACTooltipProps,
+ type TooltipProps as RACTooltipProps,
} from 'react-aria-components';
export interface TooltipProps extends Omit {
diff --git a/packages/components/src/components/quanta/Select/Select.tsx b/packages/components/src/components/quanta/Select/Select.tsx
index 6e43494084..0f446c6b7d 100644
--- a/packages/components/src/components/quanta/Select/Select.tsx
+++ b/packages/components/src/components/quanta/Select/Select.tsx
@@ -1,8 +1,15 @@
import React from 'react';
import { SelectContext, PopoverContext } from 'react-aria-components';
-import { Select, SelectItem, SelectProps } from '../../Select/Select';
+import {
+ Select,
+ SelectItem,
+ type SelectProps,
+ type SelectItemObject,
+} from '../../Select/Select';
-export function QuantaSelect(props: SelectProps) {
+export function QuantaSelect(
+ props: SelectProps,
+) {
return (
diff --git a/packages/components/src/components/quanta/TextAreaField/TextAreaField.tsx b/packages/components/src/components/quanta/TextAreaField/TextAreaField.tsx
index 449dd56d6d..b092fa86aa 100644
--- a/packages/components/src/components/quanta/TextAreaField/TextAreaField.tsx
+++ b/packages/components/src/components/quanta/TextAreaField/TextAreaField.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import { TextFieldContext } from 'react-aria-components';
import {
TextAreaField,
- TextAreaFieldProps,
+ type TextAreaFieldProps,
} from '../../TextAreaField/TextAreaField';
export function QuantaTextAreaField(props: TextAreaFieldProps) {
diff --git a/packages/components/src/components/quanta/TextField/TextField.tsx b/packages/components/src/components/quanta/TextField/TextField.tsx
index eb222e5078..08e8ae7df9 100644
--- a/packages/components/src/components/quanta/TextField/TextField.tsx
+++ b/packages/components/src/components/quanta/TextField/TextField.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { TextFieldContext } from 'react-aria-components';
-import { TextField, TextFieldProps } from '../../TextField/TextField';
+import { TextField, type TextFieldProps } from '../../TextField/TextField';
export function QuantaTextField(props: TextFieldProps) {
return (
diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts
index fd11f012a8..a7e2df464f 100644
--- a/packages/components/src/index.ts
+++ b/packages/components/src/index.ts
@@ -2,6 +2,13 @@ export { BlockToolbar } from './components/BlockToolbar/BlockToolbar';
export { Breadcrumbs } from './components/Breadcrumbs/Breadcrumbs';
export { Button } from './components/Button/Button';
export { Calendar } from './components/Calendar/Calendar';
+export { ColorArea } from './components/ColorArea/ColorArea';
+export { ColorField } from './components/ColorField/ColorField';
+export { ColorPicker } from './components/ColorPicker/ColorPicker';
+export { ColorSlider } from './components/ColorSlider/ColorSlider';
+export { ColorSwatch } from './components/ColorSwatch/ColorSwatch';
+export { ColorSwatchPicker } from './components/ColorSwatchPicker/ColorSwatchPicker';
+export { ColorWheel } from './components/ColorWheel/ColorWheel';
export { Checkbox } from './components/Checkbox/Checkbox';
export { CheckboxField } from './components/CheckboxField/CheckboxField';
export { CheckboxGroup } from './components/CheckboxGroup/CheckboxGroup';
@@ -11,6 +18,8 @@ export { DateField } from './components/DateField/DateField';
export { DatePicker } from './components/DatePicker/DatePicker';
export { DateRangePicker } from './components/DateRangePicker/DateRangePicker';
export { Dialog } from './components/Dialog/Dialog';
+export { Disclosure } from './components/Disclosure/Disclosure';
+export { DisclosureGroup } from './components/DisclosureGroup/DisclosureGroup';
export { Form } from './components/Form/Form';
export { GridList, GridListItem } from './components/GridList/GridList';
export { Icon } from './components/Icon/Icon';
@@ -46,9 +55,6 @@ export { Tooltip } from './components/Tooltip/Tooltip';
export { RenderBlocks } from './views/RenderBlocks/RenderBlocks';
export { DefaultBlockView } from './views/RenderBlocks/DefaultBlockView';
-export { FlattenToAppURLProvider } from './providers/flattenToAppURL';
-export { useFlattenToAppURL } from './providers/flattenToAppURL';
-
// Quanta components
export { QuantaTextField } from './components/quanta/TextField/TextField';
export { QuantaTextAreaField } from './components/quanta/TextAreaField/TextAreaField';
diff --git a/packages/components/src/providers/flattenToAppURL.tsx b/packages/components/src/providers/flattenToAppURL.tsx
deleted file mode 100644
index b1ce32a98d..0000000000
--- a/packages/components/src/providers/flattenToAppURL.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import React from 'react';
-import { createContext, ReactNode, useContext, useMemo } from 'react';
-
-interface FlattenToAppURL {
- flattenToAppURL: (path: string | undefined) => string | undefined;
-}
-
-const FlattenToAppURLContext = createContext({
- flattenToAppURL: (path) => path,
-});
-
-interface FlattenToAppURLProps {
- flattenToAppURL: (path: string | undefined) => string | undefined;
- children: ReactNode;
-}
-
-export function FlattenToAppURLProvider(props: FlattenToAppURLProps) {
- let { children, flattenToAppURL } = props;
-
- let ctx = useMemo(
- () => ({
- flattenToAppURL,
- }),
- [flattenToAppURL],
- );
-
- return (
-
- {children}
-
- );
-}
-
-export function useFlattenToAppURL(): FlattenToAppURL {
- return useContext(FlattenToAppURLContext);
-}
diff --git a/packages/components/src/styles/basic/ColorArea.css b/packages/components/src/styles/basic/ColorArea.css
new file mode 100644
index 0000000000..a241aee10f
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorArea.css
@@ -0,0 +1,34 @@
+@import './ColorSlider.css';
+
+.react-aria-ColorArea {
+ width: 192px;
+ height: 192px;
+ flex-shrink: 0;
+ border-radius: 4px;
+}
+
+.react-aria-ColorThumb {
+ width: 20px;
+ height: 20px;
+ box-sizing: border-box;
+ border: 2px solid white;
+ border-radius: 50%;
+ box-shadow:
+ 0 0 0 1px black,
+ inset 0 0 0 1px black;
+
+ &[data-focus-visible] {
+ width: 24px;
+ height: 24px;
+ }
+}
+
+.react-aria-ColorArea {
+ &[data-disabled] {
+ background: gray !important;
+
+ .react-aria-ColorThumb {
+ background: gray !important;
+ }
+ }
+}
diff --git a/packages/components/src/styles/basic/ColorField.css b/packages/components/src/styles/basic/ColorField.css
new file mode 100644
index 0000000000..bcb29a5273
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorField.css
@@ -0,0 +1,49 @@
+@import './Button.css';
+@import './Form.css';
+@import './theme.css';
+
+.react-aria-ColorField {
+ display: flex;
+ flex-direction: column;
+ color: var(--text-color);
+
+ .react-aria-Input {
+ width: 100%;
+ max-width: 12ch;
+ box-sizing: border-box;
+ padding: 0.286rem;
+ border: 1px solid var(--border-color);
+ border-radius: 6px;
+ margin: 0;
+ background: var(--field-background);
+ color: var(--field-text-color);
+ font-size: 1.143rem;
+
+ &[data-focused] {
+ outline: 2px solid var(--focus-ring-color);
+ outline-offset: -1px;
+ }
+ }
+
+ &[data-invalid] {
+ .react-aria-Input {
+ border-color: var(--color-invalid);
+ }
+ }
+
+ .react-aria-FieldError {
+ color: var(--color-invalid);
+ font-size: 12px;
+ }
+
+ [slot='description'] {
+ font-size: 12px;
+ }
+
+ .react-aria-Input {
+ &[data-disabled] {
+ border-color: var(--border-color-disabled);
+ color: var(--text-color-disabled);
+ }
+ }
+}
diff --git a/packages/components/src/styles/basic/ColorPicker.css b/packages/components/src/styles/basic/ColorPicker.css
new file mode 100644
index 0000000000..e8ccca99b8
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorPicker.css
@@ -0,0 +1,43 @@
+@import './Button.css';
+@import './ColorArea.css';
+@import './ColorSlider.css';
+@import './ColorSwatch.css';
+@import './ColorSwatchPicker.css';
+@import './ColorField.css';
+@import './Popover.css';
+@import './Dialog.css';
+@import './ListBox.css';
+@import './Select.css';
+@import './theme.css';
+
+.color-picker {
+ display: flex;
+ align-items: center;
+ padding: 0;
+ border: none;
+ border-radius: 4px;
+ appearance: none;
+ background: none;
+ color: var(--text-color);
+ font-size: 1rem;
+ gap: 8px;
+ outline: none;
+ vertical-align: middle;
+
+ &[data-focus-visible] {
+ outline: 2px solid var(--focus-ring-color);
+ outline-offset: 2px;
+ }
+}
+
+.color-picker-dialog {
+ display: flex;
+ overflow: auto;
+ min-width: 192px;
+ max-height: inherit;
+ box-sizing: border-box;
+ flex-direction: column;
+ padding: 15px;
+ gap: 8px;
+ outline: none;
+}
diff --git a/packages/components/src/styles/basic/ColorSlider.css b/packages/components/src/styles/basic/ColorSlider.css
new file mode 100644
index 0000000000..63f3d432a1
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorSlider.css
@@ -0,0 +1,80 @@
+.react-aria-ColorSlider {
+ display: grid;
+ max-width: 300px;
+ gap: 4px;
+ grid-template-areas:
+ 'label output'
+ 'track track';
+ grid-template-columns: 1fr auto;
+
+ .react-aria-Label {
+ grid-area: label;
+ }
+
+ .react-aria-SliderOutput {
+ grid-area: output;
+ }
+
+ .react-aria-SliderTrack {
+ border-radius: 4px;
+ grid-area: track;
+ }
+
+ &[data-orientation='horizontal'] {
+ .react-aria-SliderTrack {
+ height: 28px;
+ }
+
+ .react-aria-ColorThumb {
+ top: 50%;
+ }
+ }
+}
+
+.react-aria-ColorThumb {
+ width: 20px;
+ height: 20px;
+ box-sizing: border-box;
+ border: 2px solid white;
+ border-radius: 50%;
+ box-shadow:
+ 0 0 0 1px black,
+ inset 0 0 0 1px black;
+
+ &[data-focus-visible] {
+ width: 24px;
+ height: 24px;
+ }
+}
+
+.react-aria-ColorSlider {
+ &[data-orientation='vertical'] {
+ display: block;
+ height: 150px;
+
+ .react-aria-Label,
+ .react-aria-SliderOutput {
+ display: none;
+ }
+
+ .react-aria-SliderTrack {
+ width: 28px;
+ height: 100%;
+ }
+
+ .react-aria-ColorThumb {
+ left: 50%;
+ }
+ }
+
+ &[data-disabled] {
+ .react-aria-SliderTrack {
+ background: gray !important;
+ }
+
+ .react-aria-ColorThumb {
+ background: gray !important;
+ opacity: 0.5;
+ }
+ }
+}
diff --git a/packages/components/src/styles/basic/ColorSwatch.css b/packages/components/src/styles/basic/ColorSwatch.css
new file mode 100644
index 0000000000..3713b3807b
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorSwatch.css
@@ -0,0 +1,8 @@
+@import './ColorSlider.css';
+
+.react-aria-ColorSwatch {
+ width: 32px;
+ height: 32px;
+ border-radius: 4px;
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
+}
diff --git a/packages/components/src/styles/basic/ColorSwatchPicker.css b/packages/components/src/styles/basic/ColorSwatchPicker.css
new file mode 100644
index 0000000000..657d836443
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorSwatchPicker.css
@@ -0,0 +1,42 @@
+@import './ColorSwatch.css';
+@import './ColorField.css';
+@import './theme.css';
+
+.react-aria-ColorSwatchPicker {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.react-aria-ColorSwatchPickerItem {
+ position: relative;
+ width: fit-content;
+ border-radius: 4px;
+ forced-color-adjust: none;
+ outline: none;
+
+ &[data-focus-visible] {
+ outline: 2px solid var(--focus-ring-color);
+ outline-offset: 2px;
+ }
+
+ &[data-selected]::after {
+ position: absolute;
+ border: 2px solid black;
+ border-radius: inherit;
+ content: '';
+ inset: 0;
+ outline: 2px solid white;
+ outline-offset: -4px;
+ }
+
+ &[data-disabled] {
+ opacity: 0.2;
+ }
+}
+
+.react-aria-ColorSwatchPicker {
+ &[data-layout='stack'] {
+ flex-direction: column;
+ }
+}
diff --git a/packages/components/src/styles/basic/ColorWheel.css b/packages/components/src/styles/basic/ColorWheel.css
new file mode 100644
index 0000000000..429e59dc9c
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorWheel.css
@@ -0,0 +1,28 @@
+.react-aria-ColorThumb {
+ width: 20px;
+ height: 20px;
+ box-sizing: border-box;
+ border: 2px solid white;
+ border-radius: 50%;
+ box-shadow:
+ 0 0 0 1px black,
+ inset 0 0 0 1px black;
+
+ &[data-focus-visible] {
+ width: 24px;
+ height: 24px;
+ }
+}
+
+.react-aria-ColorWheel {
+ &[data-disabled] {
+ .react-aria-ColorWheelTrack {
+ background: gray !important;
+ }
+
+ .react-aria-ColorThumb {
+ background: gray !important;
+ opacity: 0.5;
+ }
+ }
+}
diff --git a/packages/components/src/styles/basic/Disclosure.css b/packages/components/src/styles/basic/Disclosure.css
new file mode 100644
index 0000000000..809a49816e
--- /dev/null
+++ b/packages/components/src/styles/basic/Disclosure.css
@@ -0,0 +1,33 @@
+@import './theme.css';
+@import './Button.css';
+
+.react-aria-Disclosure {
+ .react-aria-Button[slot='trigger'] {
+ display: flex;
+ align-items: center;
+ border: none;
+ background: none;
+ box-shadow: none;
+ font-size: 16px;
+ font-weight: bold;
+ gap: 8px;
+
+ svg {
+ width: 12px;
+ height: 12px;
+ fill: none;
+ rotate: 0deg;
+ stroke: currentColor;
+ stroke-width: 3px;
+ transition: rotate 200ms;
+ }
+ }
+
+ &[data-expanded] .react-aria-Button[slot='trigger'] svg {
+ rotate: 90deg;
+ }
+}
+
+.react-aria-DisclosurePanel {
+ margin-left: 32px;
+}
diff --git a/packages/components/src/styles/basic/DisclosureGroup.css b/packages/components/src/styles/basic/DisclosureGroup.css
new file mode 100644
index 0000000000..2c60061a9e
--- /dev/null
+++ b/packages/components/src/styles/basic/DisclosureGroup.css
@@ -0,0 +1,3 @@
+@import './theme.css';
+@import './Button.css';
+@import './Disclosure.css';
diff --git a/packages/components/src/styles/basic/main.css b/packages/components/src/styles/basic/main.css
index 60173bd34b..066feb263c 100644
--- a/packages/components/src/styles/basic/main.css
+++ b/packages/components/src/styles/basic/main.css
@@ -8,7 +8,15 @@
@import './Label.css';
@import './Checkbox.css';
@import './CheckboxGroup.css';
+@import './ColorArea.css';
+@import './ColorField.css';
+@import './ColorPicker.css';
+@import './ColorSlider.css';
+@import './ColorSwatch.css';
+@import './ColorSwatchPicker.css';
@import './ComboBox.css';
+@import './Disclosure.css';
+@import './DisclosureGroup.css';
@import './NumberField.css';
@import './RadioGroup.css';
@import './Switch.css';
diff --git a/packages/components/src/views/RenderBlocks/RenderBlocks.tsx b/packages/components/src/views/RenderBlocks/RenderBlocks.tsx
index e0489596b8..91d6ba4342 100644
--- a/packages/components/src/views/RenderBlocks/RenderBlocks.tsx
+++ b/packages/components/src/views/RenderBlocks/RenderBlocks.tsx
@@ -44,6 +44,7 @@ export const RenderBlocks = (props: RenderBlocksProps) => {
const Block = blocksConfig[blockType]?.view || DefaultBlockView;
return Block ? (
+ // @ts-ignore It's ok to pass the blockData as is
{
diff --git a/packages/coresandbox/src/components/Blocks/TestBlock/Edit.tsx b/packages/coresandbox/src/components/Blocks/TestBlock/Edit.tsx
index fd858204c7..0d075c9c5d 100644
--- a/packages/coresandbox/src/components/Blocks/TestBlock/Edit.tsx
+++ b/packages/coresandbox/src/components/Blocks/TestBlock/Edit.tsx
@@ -1,4 +1,4 @@
-import { SidebarPortal } from '@plone/volto/components';
+import SidebarPortal from '@plone/volto/components/manage/Sidebar/SidebarPortal';
import Data from './Data';
import type { BlockEditProps } from '@plone/types';
diff --git a/packages/coresandbox/src/components/TestForm.jsx b/packages/coresandbox/src/components/TestForm.jsx
index 23fa15787e..1a916a22d8 100644
--- a/packages/coresandbox/src/components/TestForm.jsx
+++ b/packages/coresandbox/src/components/TestForm.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { Helmet } from '@plone/volto/helpers';
+import { Helmet } from '@plone/volto/helpers/Helmet/Helmet';
import { defineMessages, useIntl } from 'react-intl';
import { Container } from 'semantic-ui-react';
diff --git a/packages/coresandbox/src/components/Views/NewsAndEvents.tsx b/packages/coresandbox/src/components/Views/NewsAndEvents.tsx
index 4ef7d1fc52..acdba48de2 100644
--- a/packages/coresandbox/src/components/Views/NewsAndEvents.tsx
+++ b/packages/coresandbox/src/components/Views/NewsAndEvents.tsx
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
-import { searchContent } from '@plone/volto/actions';
+import { searchContent } from '@plone/volto/actions/search/search';
import { useDispatch, useSelector } from 'react-redux';
import { Container } from 'semantic-ui-react';
diff --git a/packages/coresandbox/src/index.ts b/packages/coresandbox/src/index.ts
index bad373a9b0..a27e22cd2d 100644
--- a/packages/coresandbox/src/index.ts
+++ b/packages/coresandbox/src/index.ts
@@ -4,7 +4,7 @@ import TestBlockView from './components/Blocks/TestBlock/View';
import TestBlockEdit from './components/Blocks/TestBlock/Edit';
import InputBlockView from './components/Blocks/InputBlock/View';
import InputBlockEdit from './components/Blocks/InputBlock/Edit';
-import { flattenToAppURL } from '@plone/volto/helpers';
+import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
import { SliderSchema as TestBlockSchema } from './components/Blocks/TestBlock/schema';
import { inputBlockSchema } from './components/Blocks/InputBlock/schema';
import { multipleFieldsetsSchema } from './components/Blocks/TestBlock/schema';
@@ -13,12 +13,14 @@ import codeSVG from '@plone/volto/icons/code.svg';
import type { BlockConfigBase } from '@plone/types';
import type { ConfigType } from '@plone/registry';
import SlotComponentTest from './components/Slots/SlotTest';
-import { ContentTypeCondition } from '@plone/volto/helpers';
+import { ContentTypeCondition } from '@plone/volto/helpers/Slots';
import { RouteCondition } from '@plone/volto/helpers/Slots';
import TestForm from './components/TestForm';
import FormBlockView from './components/Blocks/FormBlock/View';
import FormBlockEdit from './components/Blocks/FormBlock/Edit';
import { formBlockSchema } from './components/Blocks/FormBlock/schema';
+import Login from '@plone/volto/components/theme/Login/Login';
+
const testBlock: BlockConfigBase = {
id: 'testBlock',
title: 'testBlock',
@@ -207,6 +209,12 @@ const applyConfig = (config: ConfigType) => {
exact: false,
},
];
+
+ config.addonRoutes.push({
+ path: '/fallback_login',
+ component: Login,
+ exact: false,
+ });
config.blocks.blocksConfig.testBlock = testBlock;
config.blocks.blocksConfig.inputBlock = inputBlock;
config.blocks.blocksConfig.testBlockConditional = testBlockConditional;
diff --git a/packages/generator-volto/CHANGELOG.md b/packages/generator-volto/CHANGELOG.md
index fb6cf2607a..b04403e193 100644
--- a/packages/generator-volto/CHANGELOG.md
+++ b/packages/generator-volto/CHANGELOG.md
@@ -8,6 +8,33 @@
+## 9.0.0 (2024-10-31)
+
+### Internal
+
+- Release 9.0.0 @sneridagh
+
+## 9.0.0-alpha.20 (2024-10-27)
+
+### Breaking
+
+- Dropped support for Node.js 18, since Volto only supports the last two LTS versions. @sneridagh
+ Volto might continue to work on it, but it will be no longer tested in CI. [#6371](https://github.com/plone/volto/issues/6371)
+- Breaking changes in `.eslintrc.js` and `.storybook/main.js` because of #6399. @sneridagh
+ Please see the [Upgrade Guide](https://6.docs.plone.org/volto/upgrade-guide/index.html). [#6399](https://github.com/plone/volto/issues/6399)
+
+## 9.0.0-alpha.19 (2024-10-18)
+
+### Internal
+
+- Update to use jest 26 @sneridagh [#6406](https://github.com/plone/volto/issues/6406)
+
+## 9.0.0-alpha.18 (2024-10-04)
+
+### Breaking
+
+- Updated `razzle.config.js` to accommodate new argument `paths` passed down to `customModifyWebpackConfig`. @sneridagh [#6368](https://github.com/plone/volto/issues/6368)
+
## 9.0.0-alpha.17 (2024-06-28)
### Bugfix
diff --git a/packages/generator-volto/generators/app/templates/.eslintrc.js b/packages/generator-volto/generators/app/templates/.eslintrc.js
index 6148e2e62f..a5aaf045b1 100644
--- a/packages/generator-volto/generators/app/templates/.eslintrc.js
+++ b/packages/generator-volto/generators/app/templates/.eslintrc.js
@@ -1,6 +1,6 @@
const fs = require('fs');
const projectRootPath = __dirname;
-const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry');
+const { AddonRegistry } = require('@plone/registry/addon-registry');
let voltoPath = './node_modules/@plone/volto';
@@ -17,15 +17,15 @@ if (configFile) {
voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`;
}
-const reg = new AddonConfigurationRegistry(__dirname);
+const { registry } = AddonRegistry.init(__dirname);
// Extends ESlint configuration for adding the aliases to `src` directories in Volto addons
-const addonAliases = Object.keys(reg.packages).map((o) => [
+const addonAliases = Object.keys(registry.packages).map((o) => [
o,
- reg.packages[o].modulePath,
+ registry.packages[o].modulePath,
]);
-const addonExtenders = reg.getEslintExtenders().map((m) => require(m));
+const addonExtenders = registry.getEslintExtenders().map((m) => require(m));
const defaultConfig = {
extends: `${voltoPath}/.eslintrc`,
diff --git a/packages/generator-volto/generators/app/templates/.storybook/main.js b/packages/generator-volto/generators/app/templates/.storybook/main.js
index aa094cb6d7..da6c799e56 100644
--- a/packages/generator-volto/generators/app/templates/.storybook/main.js
+++ b/packages/generator-volto/generators/app/templates/.storybook/main.js
@@ -104,9 +104,9 @@ module.exports = {
[],
defaultRazzleOptions,
);
- const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry');
+ const { AddonRegistry } = require('@plone/registry/addon-registry');
- const registry = new AddonConfigurationRegistry(projectRootPath);
+ const { registry } = AddonRegistry.init(projectRootPath);
config = lessPlugin({ registry }).modifyWebpackConfig({
env: { target: 'web', dev: 'dev' },
diff --git a/packages/generator-volto/generators/app/templates/razzle.config.js b/packages/generator-volto/generators/app/templates/razzle.config.js
index 6bab4ae583..2b4a6a8f57 100644
--- a/packages/generator-volto/generators/app/templates/razzle.config.js
+++ b/packages/generator-volto/generators/app/templates/razzle.config.js
@@ -27,12 +27,14 @@ const customModifyWebpackConfig = ({
webpackConfig,
webpackObject,
options,
+ paths,
}) => {
const config = modifyWebpackConfig({
env: { target, dev },
webpackConfig,
webpackObject,
options,
+ paths,
});
// add custom code here..
return config;
diff --git a/packages/generator-volto/package.json b/packages/generator-volto/package.json
index f61a14b344..b31a63b5e8 100644
--- a/packages/generator-volto/package.json
+++ b/packages/generator-volto/package.json
@@ -10,7 +10,7 @@
}
],
"license": "MIT",
- "version": "9.0.0-alpha.17",
+ "version": "9.0.0",
"repository": {
"type": "git",
"url": "git+https://github.com/plone/generator-volto.git"
@@ -62,7 +62,7 @@
"testEnvironment": "node"
},
"engines": {
- "node": "^16 || ^18 || ^20"
+ "node": "^16 || ^18 || ^20 || ^22"
},
"dependencies": {
"ansi-escapes": "2.0.0",
@@ -83,7 +83,7 @@
},
"devDependencies": {
"coveralls": "^3.0.7",
- "jest": "^24.8.0",
+ "jest": "^26.6.3",
"release-it": "^16.1.3",
"yeoman-assert": "^3.1.0",
"yeoman-test": "^6.1.0"
diff --git a/packages/helpers/README.md b/packages/helpers/README.md
index 6226029a0f..f9c90bc51b 100644
--- a/packages/helpers/README.md
+++ b/packages/helpers/README.md
@@ -1 +1,6 @@
# @plone/helpers
+
+> [!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/helpers/news/6069.internal b/packages/helpers/news/6069.internal
new file mode 100644
index 0000000000..003eb2470b
--- /dev/null
+++ b/packages/helpers/news/6069.internal
@@ -0,0 +1 @@
+Added experimental notice @sneridagh
diff --git a/packages/helpers/news/6407.internal b/packages/helpers/news/6407.internal
new file mode 100644
index 0000000000..d6fed2b831
--- /dev/null
+++ b/packages/helpers/news/6407.internal
@@ -0,0 +1,2 @@
+Unpin parcel version @sneridagh
+Update `typescript` and `vitest` everywhere @sneridagh
diff --git a/packages/helpers/package.json b/packages/helpers/package.json
index 55e5cd3ff6..33cf0204a5 100644
--- a/packages/helpers/package.json
+++ b/packages/helpers/package.json
@@ -29,14 +29,9 @@
"access": "public"
},
"main": "src/index.ts",
- "targets": {
- "main": {
- "includeNodeModules": false
- }
- },
"scripts": {
- "watch": "parcel watch",
- "build": "parcel build",
+ "watch": "tsup",
+ "build": "tsup",
"test": "vitest",
"dry-release": "release-it --dry-run",
"release": "release-it",
@@ -54,15 +49,13 @@
},
"dependencies": {},
"devDependencies": {
- "@parcel/packager-ts": "2.12.0",
- "@parcel/transformer-typescript-types": "2.12.0",
"@plone/types": "workspace:*",
"@types/react": "^18",
"@types/react-dom": "^18",
- "parcel": "2.12.0",
"release-it": "^17.1.1",
"tsconfig": "workspace:*",
- "typescript": "5.4.2",
- "vitest": "^1.3.1"
+ "tsup": "^8.3.5",
+ "typescript": "^5.6.3",
+ "vitest": "^2.1.3"
}
}
diff --git a/packages/providers/.npmignore b/packages/providers/.npmignore
index 0d8afd5727..a6d10baa1e 100644
--- a/packages/providers/.npmignore
+++ b/packages/providers/.npmignore
@@ -2,7 +2,5 @@ news
towncrier.toml
.changelog.draft
node_modules/
-.parcel-cache
-.parcelrc
.release-it.json
.eslintrc.js
diff --git a/packages/providers/.parcelrc b/packages/providers/.parcelrc
deleted file mode 100644
index db2d15099d..0000000000
--- a/packages/providers/.parcelrc
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "extends": "@parcel/config-default",
- "transformers": {
- "*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [
- "@parcel/transformer-js",
- "@parcel/transformer-react-refresh-wrap"
- ]
- }
-}
diff --git a/packages/providers/.release-it.json b/packages/providers/.release-it.json
index 2087371cdb..78a8734399 100644
--- a/packages/providers/.release-it.json
+++ b/packages/providers/.release-it.json
@@ -5,7 +5,7 @@
"hooks": {
"after:bump": [
"pipx run towncrier build --draft --yes --version ${version} > .changelog.draft && pipx run towncrier build --yes --version ${version}",
- "pnpm build"
+ "pnpm build:force"
],
"after:release": "rm .changelog.draft"
},
diff --git a/packages/providers/CHANGELOG.md b/packages/providers/CHANGELOG.md
index 9aad3658e3..96a8e9b472 100644
--- a/packages/providers/CHANGELOG.md
+++ b/packages/providers/CHANGELOG.md
@@ -8,6 +8,39 @@
+## 1.0.0-alpha.6 (2024-11-21)
+
+### Feature
+
+- Update RAC to 1.5.0 @sneridagh [#6498](https://github.com/plone/volto/issues/6498)
+
+## 1.0.0-alpha.5 (2024-11-05)
+
+### Internal
+
+- Improve packaging. @sneridagh
+
+## 1.0.0-alpha.4 (2024-11-05)
+
+### Internal
+
+- Bump local `typescript` version. @sneridagh [#6461](https://github.com/plone/volto/issues/6461)
+- Replace `parcel` with `tsup`. Better types, better tsconfig. Move to ESM. @sneridagh [#6468](https://github.com/plone/volto/issues/6468)
+
+## 1.0.0-alpha.3 (2024-10-18)
+
+## 1.0.0-alpha.2 (2024-10-18)
+
+### Breaking
+
+- Improve and group providers. @sneridagh
+ Breaking:
+ - The interface of the providers has changed. Please check the new one, and adapt your apps accordingly. [#6069](https://github.com/plone/volto/issues/6069)
+
+### Internal
+
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407)
+
## 1.0.0-alpha.1 (2024-05-23)
### Internal
diff --git a/packages/providers/README.md b/packages/providers/README.md
index 05502673e0..75ba6475a9 100644
--- a/packages/providers/README.md
+++ b/packages/providers/README.md
@@ -1,3 +1,188 @@
# `@plone/providers`
This package contains utility providers for Plone React components.
+The main purpose is to provide dependency injection of common required artifacts needed by any app.
+These artifacts include:
+- Router related
+- Plone Client
+- URL handling methods
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
+
+## `PloneProvider`
+
+It provides all the necessary artifacts that an app can need grouped in a single provider.
+
+```ts
+interface PloneProvider {
+ ploneClient: InstanceType;
+ queryClient: QueryClient;
+ useLocation: () => Location | undefined;
+ useParams: (opts?: any) => Record;
+ navigate: (path: string) => void;
+ useHref: (to: string, options?: any) => string;
+ flattenToAppURL: (path: string | undefined) => string | undefined;
+}
+```
+
+It should be instantiated at the top of your app.
+You have to provide the required props depending on the framework and the router used.
+This is the example for a Next.js app.
+Please refer to the {file}`apps` folder of the Volto repository for more examples of the usage of `PloneProvider` in different React frameworks.
+
+```tsx
+'use client';
+import React from 'react';
+import {
+ useRouter,
+ usePathname,
+ useSearchParams,
+ useParams,
+} from 'next/navigation';
+import { QueryClient } from '@tanstack/react-query';
+import { PloneProvider } from '@plone/providers';
+import PloneClient from '@plone/client';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+import { flattenToAppURL } from './utils';
+import config from './config';
+
+// Custom hook to unify the location object between NextJS and Plone
+function useLocation() {
+ let pathname = usePathname();
+ let search = useSearchParams();
+
+ return {
+ pathname,
+ search,
+ searchStr: '',
+ hash: (typeof window !== 'undefined' && window.location.hash) || '',
+ href: (typeof window !== 'undefined' && window.location.href) || '',
+ };
+}
+
+const Providers: React.FC<{
+ children?: React.ReactNode;
+}> = ({ children }) => {
+ // Creating the clients at the file root level makes the cache shared
+ // between all requests and means _all_ data gets passed to _all_ users.
+ // Besides being bad for performance, this also leaks any sensitive data.
+ // We use this pattern to ensure that every client gets its own clients
+ const [queryClient] = React.useState(
+ () =>
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ // With SSR, we usually want to set some default staleTime
+ // above 0 to avoid refetching immediately on the client
+ staleTime: 60 * 1000,
+ },
+ },
+ }),
+ );
+
+ const [ploneClient] = React.useState(() =>
+ PloneClient.initialize({
+ apiPath: config.settings.apiPath,
+ }),
+ );
+
+ const router = useRouter();
+
+ return (
+ {
+ router.push(to);
+ }}
+ useParams={useParams}
+ useHref={(to) => flattenToAppURL(to)}
+ flattenToAppURL={flattenToAppURL}
+ >
+ {children}
+
+
+ );
+};
+
+export default Providers;
+
+```
+
+You can use it anywhere in your app by using the hook `usePloneProvider`.
+
+```tsx
+import { usePloneProvider } from '@plone/providers';
+
+const { ploneClient } = usePloneProvider()
+```
+
+Alternatively, you can use it in any other context property.
+
+```tsx
+const { navigate } = usePloneProvider()
+```
+
+## `PloneClientProvider`
+
+`PloneProvider` in a group of other smaller providers.
+You can also instantiate and use them as standalone providers.
+However, you should do this only if the framework has some limitation on using the bulk `PloneClientProvider`.
+
+The following snippets show its usage.
+First, instantiate the provider.
+
+```ts
+export type PloneClientProviderProps = {
+ client: InstanceType;
+ queryClient: QueryClient;
+ children?: React.ReactNode;
+};
+```
+
+Second, use its related hook through either of the following examples.
+
+```tsx
+import { usePloneClient } from '@plone/providers';
+
+const client = usePloneClient()
+```
+
+or
+
+```tsx
+const { getContentQuery } = usePloneClient()
+```
+
+## `AppRouterProvider`
+
+This provider is included also in `PloneProvider`.
+You can also instantiate and use it as a standalone provider.
+However, you should do this only if the framework has some limitation on using the bulk `PloneClientProvider`.
+
+The following code example shows its usage.
+
+```ts
+interface AppRouterProps {
+ useLocation: () => Location | undefined;
+ useParams: (opts?: any) => Record;
+ navigate: (path: string) => void;
+ useHref?: (to: string, options?: any) => string;
+ flattenToAppURL: (path: string | undefined) => string | undefined;
+ children: ReactNode;
+}
+```
+
+The following code sample shows its related hook.
+
+```tsx
+import { useAppRouter } from '@plone/providers';
+
+const { useLocation } = useAppRouter()
+```
diff --git a/packages/providers/package.json b/packages/providers/package.json
index d2a84fc790..8a12623112 100644
--- a/packages/providers/package.json
+++ b/packages/providers/package.json
@@ -9,7 +9,7 @@
],
"funding": "https://github.com/sponsors/plone",
"license": "MIT",
- "version": "1.0.0-alpha.1",
+ "version": "1.0.0-alpha.6",
"repository": {
"type": "git",
"url": "https://github.com/plone/volto.git"
@@ -28,22 +28,23 @@
"publishConfig": {
"access": "public"
},
- "source": "./src/index.ts",
- "main": "./dist/main.js",
- "module": "./dist/module.mjs",
- "types": "./dist/index.d.ts",
+ "type": "module",
+ "files": [
+ "dist",
+ "README.md"
+ ],
+ "main": "./dist/index.js",
"exports": {
+ "./package.json": "./package.json",
".": {
- "types": "./dist/index.d.ts",
- "import": "./dist/module.mjs",
- "require": "./dist/main.js"
- },
- "./src/*": "./src/*"
+ "import": "./dist/index.js",
+ "default": "./dist/index.cjs"
+ }
},
"scripts": {
- "watch": "parcel watch",
- "build": "parcel build",
- "build:force": "parcel build --no-cache",
+ "build": "tsup",
+ "build:force": "tsup",
+ "check:exports": "attw --pack .",
"test": "vitest",
"dry-release": "release-it --dry-run",
"release": "release-it",
@@ -62,22 +63,19 @@
"dependencies": {
"@plone/client": "workspace:*",
"@plone/components": "workspace:*",
- "@plone/registry": "workspace:*"
+ "@plone/registry": "workspace:*",
+ "@tanstack/react-query": "^5.59.0",
+ "react-aria-components": "^1.5.0"
},
"devDependencies": {
- "@parcel/config-default": "^2.12.0",
- "@parcel/core": "^2.12.0",
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-js": "^2.12.0",
- "@parcel/transformer-react-refresh-wrap": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
+ "@arethetypeswrong/cli": "^0.16.4",
"@plone/types": "workspace:*",
"@types/react": "^18",
"@types/react-dom": "^18",
- "parcel": "^2.12.0",
"release-it": "17.1.1",
"tsconfig": "workspace:*",
- "typescript": "5.2.2",
- "vitest": "^1.3.1"
+ "tsup": "^8.3.5",
+ "typescript": "^5.6.3",
+ "vitest": "^2.1.3"
}
}
diff --git a/packages/providers/src/AppRouter.tsx b/packages/providers/src/AppRouter.tsx
new file mode 100644
index 0000000000..cf4005fa6b
--- /dev/null
+++ b/packages/providers/src/AppRouter.tsx
@@ -0,0 +1,82 @@
+import React, {
+ createContext,
+ type ReactNode,
+ useContext,
+ useMemo,
+} from 'react';
+import { RouterProvider } from 'react-aria-components';
+import { flattenToAppURL as defaultFlattenToAppURL } from './utils';
+
+export type AnySearchSchema = {};
+export interface Location {
+ href?: string; // TSR
+ searchStr?: string; // TSR
+ pathname: string; // TSR, RR and Remix
+ search: TSearchObj; // TSR, RR and Remix
+ hash: string; // TSR, RR and Remix
+ state?: any; // TSR, RR and Remix
+ key?: string; // RR and Remix
+}
+
+interface AppRouter {
+ useLocation: () => Location | undefined;
+ useParams: (opts?: any) => Record;
+ navigate: (path: string) => void;
+ useHref?: (to: string, options?: any) => string;
+ flattenToAppURL: (path: string | undefined) => string | undefined;
+}
+
+const AppRouterContext = createContext({
+ useLocation: () => ({
+ href: '',
+ pathname: '',
+ search: {},
+ searchStr: '',
+ hash: '',
+ }),
+ useParams: () => ({}),
+ navigate: () => {},
+ useHref: () => '',
+ flattenToAppURL: defaultFlattenToAppURL,
+});
+
+interface AppRouterProps {
+ useLocation: () => Location | undefined;
+ useParams: (opts?: any) => Record;
+ navigate: (path: string) => void;
+ useHref?: (to: string, options?: any) => string;
+ flattenToAppURL: (path: string | undefined) => string | undefined;
+ children: ReactNode;
+}
+
+export function AppRouterProvider(props: AppRouterProps) {
+ let { children, navigate, useLocation, useParams, useHref, flattenToAppURL } =
+ props;
+
+ if (!flattenToAppURL) {
+ flattenToAppURL = defaultFlattenToAppURL;
+ }
+
+ let ctx = useMemo(
+ () => ({
+ useLocation,
+ useParams,
+ navigate,
+ useHref,
+ flattenToAppURL,
+ }),
+ [useLocation, useParams, navigate, useHref, flattenToAppURL],
+ );
+
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+export function useAppRouter() {
+ return useContext(AppRouterContext);
+}
diff --git a/packages/providers/src/PloneClient.tsx b/packages/providers/src/PloneClient.tsx
index 621f797746..50a319a6f4 100644
--- a/packages/providers/src/PloneClient.tsx
+++ b/packages/providers/src/PloneClient.tsx
@@ -1,4 +1,5 @@
import * as React from 'react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import PloneClient from '@plone/client';
@@ -18,16 +19,18 @@ export const usePloneClient = () => {
export type PloneClientProviderProps = {
client: InstanceType;
+ queryClient: QueryClient;
children?: React.ReactNode;
};
export const PloneClientProvider = ({
client,
+ queryClient,
children,
}: PloneClientProviderProps): JSX.Element => {
return (
- {children}
+ {children}
);
};
diff --git a/packages/providers/src/PloneProvider.tsx b/packages/providers/src/PloneProvider.tsx
new file mode 100644
index 0000000000..0912536a22
--- /dev/null
+++ b/packages/providers/src/PloneProvider.tsx
@@ -0,0 +1,104 @@
+import React, {
+ createContext,
+ type ReactNode,
+ useContext,
+ useMemo,
+} from 'react';
+import { QueryClient } from '@tanstack/react-query';
+import { AppRouterProvider, type Location } from './AppRouter';
+import { PloneClientProvider } from './PloneClient';
+import PloneClient from '@plone/client';
+import { flattenToAppURL as defaultFlattenToAppURL } from './utils';
+
+interface PloneProvider {
+ ploneClient?: InstanceType;
+ queryClient?: QueryClient;
+ useLocation: () => Location | undefined;
+ useParams: (opts?: any) => Record;
+ navigate: (path: string) => void;
+ useHref?: (to: string, options?: any) => string;
+ flattenToAppURL: (path: string | undefined) => string | undefined;
+}
+
+const PloneProviderContext = createContext({
+ useLocation: () => ({
+ href: '',
+ pathname: '',
+ search: {},
+ searchStr: '',
+ hash: '',
+ }),
+ useParams: () => ({}),
+ navigate: () => {},
+ useHref: () => '',
+ flattenToAppURL: defaultFlattenToAppURL,
+});
+
+interface PloneProviderProps {
+ ploneClient: InstanceType;
+ queryClient: QueryClient;
+ useLocation: () => Location | undefined;
+ useParams: (opts?: any) => Record;
+ navigate: (path: string) => void;
+ useHref?: (to: string, options?: any) => string;
+ flattenToAppURL?: (path: string | undefined) => string | undefined;
+ children: ReactNode;
+}
+
+export function PloneProvider(props: PloneProviderProps) {
+ let {
+ children,
+ navigate,
+ useLocation,
+ useParams,
+ useHref,
+ ploneClient,
+ queryClient,
+ flattenToAppURL,
+ } = props;
+
+ if (!flattenToAppURL) {
+ flattenToAppURL = defaultFlattenToAppURL;
+ }
+
+ let ctx = useMemo(
+ () => ({
+ ploneClient,
+ queryClient,
+ useLocation,
+ useParams,
+ navigate,
+ useHref,
+ flattenToAppURL,
+ }),
+ [
+ ploneClient,
+ queryClient,
+ useLocation,
+ useParams,
+ navigate,
+ useHref,
+ flattenToAppURL,
+ ],
+ );
+
+ return (
+
+
+
+ {children}
+
+
+
+ );
+}
+
+export function usePloneProvider() {
+ return useContext(PloneProviderContext);
+}
diff --git a/packages/providers/src/RouterLocation.tsx b/packages/providers/src/RouterLocation.tsx
deleted file mode 100644
index f2881957d4..0000000000
--- a/packages/providers/src/RouterLocation.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import React, { createContext, ReactNode, useContext, useMemo } from 'react';
-
-export type AnySearchSchema = {};
-export interface Location {
- href: string;
- pathname: string;
- search: TSearchObj;
- searchStr: string;
- hash: string;
-}
-
-interface RouterLocation {
- useLocation: () => Location | undefined;
-}
-
-const RouterLocationContext = createContext({
- useLocation: () => ({
- href: '',
- pathname: '',
- search: {},
- searchStr: '',
- hash: '',
- }),
-});
-
-interface RouterLocationProps {
- useLocation: () => Location | undefined;
- children: ReactNode;
-}
-
-export function RouterLocationProvider(props: RouterLocationProps) {
- let { children, useLocation } = props;
-
- let ctx = useMemo(
- () => ({
- useLocation,
- }),
- [useLocation],
- );
-
- return (
-
- {children}
-
- );
-}
-
-export function useRouterLocation() {
- const { useLocation } = useContext(RouterLocationContext);
- return useLocation();
-}
diff --git a/packages/providers/src/index.ts b/packages/providers/src/index.ts
index 37d404ef95..5978c8c821 100644
--- a/packages/providers/src/index.ts
+++ b/packages/providers/src/index.ts
@@ -1,6 +1,6 @@
-export * from './RouterLocation';
+export * from './AppRouter';
export * from './PloneClient';
-// FlattenToAppURL provider should live on the components
-// but @plone/components can't depend on other packages
-// we are proxying it here for convenience and centralization
-export { FlattenToAppURLProvider, useFlattenToAppURL } from '@plone/components';
+export * from './PloneProvider';
+
+// Proxying RouterProvider from react-aria-components
+export { RouterProvider } from 'react-aria-components';
diff --git a/packages/providers/tsconfig.json b/packages/providers/tsconfig.json
index 86da1e79f4..24722f2f4c 100644
--- a/packages/providers/tsconfig.json
+++ b/packages/providers/tsconfig.json
@@ -1,6 +1,27 @@
{
- "extends": "tsconfig/react-library.json",
- "include": ["src", "src/**/*.js"],
+ "compilerOptions": {
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "target": "es2022",
+ "allowJs": true,
+ "resolveJsonModule": true,
+ "moduleDetection": "force",
+ "isolatedModules": true,
+ "verbatimModuleSyntax": true,
+
+ "strict": true,
+ "noImplicitOverride": true,
+
+ "lib": ["es2022", "dom", "dom.iterable"],
+ "module": "preserve",
+ "noEmit": true,
+
+ "jsx": "react-jsx",
+
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true
+ },
+ "include": ["src"],
"exclude": [
"node_modules",
"build",
diff --git a/packages/providers/tsup.config.ts b/packages/providers/tsup.config.ts
new file mode 100644
index 0000000000..82b88a425c
--- /dev/null
+++ b/packages/providers/tsup.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from 'tsup';
+
+export default defineConfig({
+ entryPoints: ['src/index.ts'],
+ format: ['cjs', 'esm'],
+ dts: true,
+ outDir: 'dist',
+ clean: true,
+});
diff --git a/packages/registry/.eslintrc.js b/packages/registry/.eslintrc.cjs
similarity index 86%
rename from packages/registry/.eslintrc.js
rename to packages/registry/.eslintrc.cjs
index b60637a973..f7665b4d37 100644
--- a/packages/registry/.eslintrc.js
+++ b/packages/registry/.eslintrc.cjs
@@ -16,6 +16,8 @@ module.exports = {
// Base config
extends: ['eslint:recommended'],
+ ignorePatterns: ['docs/_static/searchtools.js'],
+
overrides: [
// React
{
@@ -61,13 +63,18 @@ module.exports = {
// Node
{
- files: ['.eslintrc.js', 'src/*.js'],
+ files: [
+ '.eslintrc.cjs',
+ 'src/addon-registry/**/*.{js,ts}',
+ '__tests__/**/*.{js,ts}',
+ ],
env: {
node: true,
es6: true,
},
rules: {
'no-prototype-builtins': 0,
+ '@typescript-eslint/ban-ts-comment': 0,
},
},
],
diff --git a/packages/registry/.gitignore b/packages/registry/.gitignore
index d32327c9cd..290e94407a 100644
--- a/packages/registry/.gitignore
+++ b/packages/registry/.gitignore
@@ -1,6 +1,6 @@
-.parcel-cache/
dist
+/bin
+/lib
+/include
-# yarn 3
-.pnp.*
-.yarn/*
+docs/_build/
diff --git a/packages/registry/.npmignore b/packages/registry/.npmignore
index 0d8afd5727..7e2134f27e 100644
--- a/packages/registry/.npmignore
+++ b/packages/registry/.npmignore
@@ -2,7 +2,9 @@ news
towncrier.toml
.changelog.draft
node_modules/
-.parcel-cache
-.parcelrc
.release-it.json
.eslintrc.js
+lib
+bin
+docs
+include
diff --git a/packages/registry/.readthedocs.yaml b/packages/registry/.readthedocs.yaml
new file mode 100644
index 0000000000..b1f7970727
--- /dev/null
+++ b/packages/registry/.readthedocs.yaml
@@ -0,0 +1,34 @@
+# .readthedocs.yaml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Set the OS, Python version and other tools you might need
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3.12"
+ # You can also specify other tool versions:
+ # nodejs: "19"
+ # rust: "1.64"
+ # golang: "1.19"
+ commands:
+ - cd ./packages/registry && make docs-rtd-registry
+
+# Build documentation in the "docs/" directory with Sphinx
+#sphinx:
+# configuration: packages/registry/docs/conf.py
+
+# Optionally build your docs in additional formats such as PDF and ePub
+# formats:
+# - pdf
+# - epub
+
+# Optional but recommended, declare the Python requirements required
+# to build your documentation
+# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
+#python:
+# install:
+# - requirements: requirements-docs.txt
diff --git a/packages/registry/.release-it.json b/packages/registry/.release-it.json
index f2720cb9f0..a63e1ce21e 100644
--- a/packages/registry/.release-it.json
+++ b/packages/registry/.release-it.json
@@ -6,7 +6,7 @@
"after:bump": [
"pipx run towncrier build --draft --yes --version ${version} > .changelog.draft",
"pipx run towncrier build --yes --version ${version}",
- "pnpm build --no-cache"
+ "pnpm build:force"
],
"after:release": "rm .changelog.draft"
},
diff --git a/packages/registry/CHANGELOG.md b/packages/registry/CHANGELOG.md
index bfc4bc4705..da8626074f 100644
--- a/packages/registry/CHANGELOG.md
+++ b/packages/registry/CHANGELOG.md
@@ -8,6 +8,68 @@
+## 2.1.2 (2024-11-05)
+
+### Bugfix
+
+- Fix weird typings issue happening in docker build but not locally. @sneridagh [#6471](https://github.com/plone/volto/issues/6471)
+
+### Internal
+
+- Improve packaging. @sneridagh
+
+## 2.1.1 (2024-11-05)
+
+### Internal
+
+- Repackage registry, the previous build was including the docs. @sneridagh
+
+## 2.1.0 (2024-11-05)
+
+### Feature
+
+- Allow any type `js`, `cjs`, `mjs`, `ts` as configuration for the add-on registry. @sneridagh [#6458](https://github.com/plone/volto/issues/6458)
+
+### Bugfix
+
+- Fix ERR_REQUIRE from ESM module requiring CJS module. @sneridagh [#6458](https://github.com/plone/volto/issues/6458)
+- Fix types for add-on's TypeScript. Fix `.tsconfig` for Node.js side. @sneridagh [#6461](https://github.com/plone/volto/issues/6461)
+
+### Internal
+
+- Replace `parcel` with `tsup` for build. @sneridagh [#6461](https://github.com/plone/volto/issues/6461)
+
+## 2.0.0 (2024-10-31)
+
+### Internal
+
+- Release 2.0.0 @sneridagh
+
+## 2.0.0-alpha.0 (2024-10-27)
+
+### Breaking
+
+- Moved the package to ESM and refactored the add-on registry scripts to TypeScript. @sneridagh
+ Breaking:
+ - For maximum compatibility with CommonJS builds, the default exports have been moved to named exports.
+ - The modules affected are now built, and the import paths have changed, too.
+ - These changes force the modification in imports in a couple of files.
+ Please see the [Upgrade Guide](https://6.docs.plone.org/volto/upgrade-guide/index.html). [#6399](https://github.com/plone/volto/issues/6399)
+
+### Feature
+
+- Added an experimental Vite plugin. @sneridagh [#6399](https://github.com/plone/volto/issues/6399)
+
+### Bugfix
+
+- Return empty array when `getUtilities` does not match anything. @sneridagh [#6422](https://github.com/plone/volto/issues/6422)
+
+### Internal
+
+- Update typescript @sneridagh [#6371](https://github.com/plone/volto/issues/6371)
+- Update Vite and vitest versions @sneridagh [#6373](https://github.com/plone/volto/issues/6373)
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407)
+
## 1.8.0 (2024-07-30)
### Feature
diff --git a/packages/registry/Makefile b/packages/registry/Makefile
new file mode 100644
index 0000000000..3dfbd1f2ef
--- /dev/null
+++ b/packages/registry/Makefile
@@ -0,0 +1,90 @@
+### Defensive settings for make:
+# https://tech.davis-hansson.com/p/make/
+SHELL:=bash
+.ONESHELL:
+.SHELLFLAGS:=-eu -o pipefail -c
+.SILENT:
+.DELETE_ON_ERROR:
+MAKEFLAGS+=--warn-undefined-variables
+MAKEFLAGS+=--no-builtin-rules
+
+# Sphinx variables
+# You can set these variables from the command line.
+SPHINXOPTS ?=
+VALEOPTS ?=
+# Internal variables.
+SPHINXBUILD = "$(realpath bin/sphinx-build)"
+SPHINXAUTOBUILD = "$(realpath bin/sphinx-autobuild)"
+DOCS_DIR = ./docs/
+BUILDDIR = ./_build/
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) .
+VALEFILES := $(shell find $(DOCS_DIR) -type f -name "*.md" -print)
+
+# We like colors
+# From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects
+RED=`tput setaf 1`
+GREEN=`tput setaf 2`
+RESET=`tput sgr0`
+YELLOW=`tput setaf 3`
+
+## Docs
+
+bin/python: ## Create a Python virtual environment with the latest pip, and install documentation requirements
+ python3 -m venv . || virtualenv --clear --python=python3 .
+ bin/python -m pip install --upgrade pip
+ @echo "Python environment created."
+ bin/pip install -r ../../requirements-docs.txt
+ @echo "Requirements installed."
+
+.PHONY: docs-clean
+docs-clean: ## Clean current and legacy docs build directories, and Python virtual environment
+ rm -rf bin include lib
+ rm -rf docs/_build
+ cd $(DOCS_DIR) && rm -rf $(BUILDDIR)/
+
+.PHONY: docs-html
+docs-html: bin/python ## Build html
+ cd $(DOCS_DIR) && $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: docs-livehtml
+docs-livehtml: bin/python ## Rebuild Sphinx documentation on changes, with live-reload in the browser
+ cd "$(DOCS_DIR)" && ${SPHINXAUTOBUILD} \
+ --ignore "*.swp" \
+ -b html . "$(BUILDDIR)/html" $(SPHINXOPTS)
+
+.PHONY: docs-linkcheck
+docs-linkcheck: bin/python ## Run linkcheck
+ cd $(DOCS_DIR) && $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/ ."
+
+.PHONY: docs-linkcheckbroken
+docs-linkcheckbroken: bin/python ## Run linkcheck and show only broken links
+ cd $(DOCS_DIR) && $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck | GREP_COLORS='0;31' grep -wi "broken\|redirect" --color=always | GREP_COLORS='0;31' grep -vi "https://github.com/plone/volto/issues/" --color=always && if test $$? -eq 0; then exit 1; fi || test $$? -ne 0
+
+.PHONY: docs-vale
+docs-vale: bin/python ## Install (once) and run Vale style, grammar, and spell checks
+ bin/vale sync
+ bin/vale --no-wrap $(VALEOPTS) $(VALEFILES)
+ @echo
+ @echo "Vale is finished; look for any errors in the above output."
+
+.PHONY: docs-rtd-pr-preview
+docs-rtd-pr-preview: ## Build previews of pull requests that have documentation changes on Read the Docs via CI
+ pip install -r requirements-docs.txt
+ cd $(DOCS_DIR) && sphinx-build -b html $(ALLSPHINXOPTS) ${READTHEDOCS_OUTPUT}/html/
+
+.PHONY: docs-rtd-registry
+docs-rtd-registry: ## Build Plone Registry docs on RTD
+ pip install -r ../../requirements-docs.txt && cd $(DOCS_DIR) && sphinx-build -b html $(ALLSPHINXOPTS) ${READTHEDOCS_OUTPUT}/html/
+
+## Build
+
+.PHONY: fix-build
+fix-build: ## Rename the built files js -> cjs and fix import.meta.url appearences in cjs
+ mv dist/cjs/addon-registry.js dist/cjs/addon-registry.cjs
+ mv dist/cjs/create-addons-loader.js dist/cjs/create-addons-loader.cjs
+ mv dist/cjs/create-theme-loader.js dist/cjs/create-theme-loader.cjs
diff --git a/packages/registry/README.md b/packages/registry/README.md
index 552133573c..0c4ae8865b 100644
--- a/packages/registry/README.md
+++ b/packages/registry/README.md
@@ -10,198 +10,10 @@ That means you have to build something that has very specific requirements, beha
Sometimes you need to build something generic that is pluggable and extensible.
In the JavaScript and TypeScript ecosystem, this is often quite complex, and the existing frameworks do not provide the means to do this.
-## Add-on registry
+`@plone/registry` provides tools to facilitate pluggability for your app.
-An add-on registry is a facility that allows an app, which was built on an existing framework, to itself be extensible and pluggable.
+## Documentation
-The add-on registry is a store where you can register a number of add-ons that your app consumes.
+You can find the detailed documentation of this package by visiting the following link.
-Add-on packages are just CommonJS packages.
-The only requirement is that they point the `main` key of their `package.json` to a module that exports as a default function, which acts as a configuration loader.
-
-An add-on can be published in an npm registry, just as any other package.
-However, add-ons are meant to not be transpiled.
-They should be released as source packages.
-
-## Register an add-on
-
-You should declare your add-on in your project.
-This is done in your app's `package.json`'s `addons` key:
-
-```json
-{
- "name": "my-app-project",
- "addons": [
- "acme-volto-foo-addon",
- "@plone/some-addon",
- "collective-another-volto-addon"
- ]
-}
-```
-
-The `addons` key ensures the add-on's main default export function is executed, being passed the configuration registry.
-In that function, the add-on can customize the registry.
-The function needs to return the `config` object (the configuration registry), so that it's passed further along to the other add-ons.
-
-The add-ons are registered in the order they are found in the `addons` key.
-The last add-on takes precedence over the others.
-This means that if you configure something in `acme-volto-foo-addon`, then the same thing later in `collective-another-volto-addon`, the latter configured thing will win and its configuration will be applied.
-
-The default export of any add-on main `index.js` file should be a function with the signature `config => config`.
-That is, it should take the configuration registry object and return it, possibly mutated or changed.
-
-## Configuration registry
-
-The configuration registry supplements the add-on registry.
-
-It is a facility that stores app configuration to be shared in the app.
-
-Let's say that your app is the user interface of a content management system (CMS).
-This CMS uses blocks as its main fundamental unit of content.
-The pages that the CMS builds are made up of these blocks.
-The CMS has some basic available blocks, but it's a requirement that integrators are able to register more blocks in a pluggable way.
-
-This app will use the add-on registry to extend the basic CMS capabilities, so an external add-on can supplement their own add-ons to the basic CMS ones.
-
-Let's assume we've defined a key in the registry `config.blocks.blocksConfig`, and defined a way to register the available blocks in the CMS as the keys in that object in the configuration registry:
-
-```js
- config.blocks.blocksConfig.faq_viewer = {
- id: 'faq_viewer',
- title: 'FAQ Viewer',
- edit: FAQBlockEdit,
- view: FAQBlockView,
- icon: chartIcon,
- group: 'common',
- restricted: false,
- mostUsed: true,
- sidebarTab: 1,
- };
-```
-
-The configuration registry will have other keys already set by default, which will compose the initial set of basic blocks used by the CMS.
-Then the CMS will properly populate the available blocks in the user interface.
-
-The add-on is meant to extend the initial configuration.
-From the default export function of our add-on, we should provide the configuration of the new block:
-
-```ts
-export default function applyConfig(config: ConfigData) {
- config.blocks.blocksConfig.faq_viewer = {
- id: 'faq_viewer',
- title: 'FAQ Viewer',
- edit: FAQBlockEdit,
- view: FAQBlockView,
- icon: chartIcon,
- group: 'common',
- restricted: false,
- mostUsed: true,
- sidebarTab: 1,
- };
-
- return config;
-}
-```
-
-Once the app starts, the add-on registry will execute, in order, all the registered add-ons' default export functions, configuring the new block.
-The add-on will then become available to the CMS when it asks the configuration registry for it.
-
-## Accessing the configuration registry
-
-The configuration registry can be accessed by:
-
-```ts
-import config from '@plone/registry'
-
-const blocksConfig = config.blocks.blocksConfig
-```
-
-## Component registry
-
-The configuration registry also stores a components registry in itself.
-The components registry is a mapping of name to component.
-You can look up a name, and receive a component that you can reference in your code.
-This provides an alternative, and more convenient, way to customize components.
-You can override programmatically such registrations from your add-on or projects because it's stored in the configuration registry.
-You can customize a component without using shadowing at all, if the code that uses the component retrieves from the component registry, rather then import it directly.
-You can even have modifiers to the component registrations through dependencies.
-Thus you can adapt the call, given an array of such dependencies.
-
-## Register components by name using `config.registerComponent`
-
-You can register components by name, typically from an add-on or project configuration:
-
-```js
-import MyToolbarComponent from './MyToolbarComponent'
-
-config.registerComponent({
- name: 'Toolbar',
- component: MyToolbarComponent,
-});
-```
-
-## Retrieve a component from the component registry
-
-You can programmatically retrieve a component from the registry using `config.getComponent`:
-
-```js
-const Toolbar = config.getComponent('Toolbar').component
-```
-
-Or you can retrieve a component by using the convenience component `Component`, if you want to use it in JSX code directly.
-
-```jsx
-import Component from '@plone/volto/components/theme/Component/Component';
-
-
-```
-
-Note that you can pass `props` down to the retrieved component.
-
-## Adapt the component using the `dependencies` array
-
-You can register components, then retrieve them, given a list of modifiers using `dependencies`.
-
-```js
-import MyTeaserNewsItemComponent from './MyTeaserNewsItemComponent'
-
-config.registerComponent({
- name: 'Teaser',
- component: MyTeaserNewsItemComponent,
- dependencies: 'News Item',
- });
-```
-
-And then retrieve the component:
-
-```js
-config.getComponent({
- name: 'Teaser',
- dependencies: ['News Item'],
- }).component
-```
-
-You can have both, either with or without dependencies:
-
-```js
-import MyTeaserDefaultComponent from './MyTeaserDefaultComponent'
-import MyTeaserNewsItemComponent from './MyTeaserNewsItemComponent'
-
-config.registerComponent({
- name: 'Teaser',
- component: MyTeaserDefaultComponent,
- });
-
-config.registerComponent({
- name: 'Teaser',
- component: MyTeaserNewsItemComponent,
- dependencies: 'News Item',
- });
-```
-
-Then retrieve them both, depending on the use case.
-In the example, given a content type value coming from the `content` prop, you would retrieve them as shown:
-
-```jsx
-
-```
+https://plone-registry.readthedocs.io
diff --git a/packages/registry/__tests__/addon-registry-app.test.js b/packages/registry/__tests__/addon-registry-app.test.js
new file mode 100644
index 0000000000..7c3d7d7e04
--- /dev/null
+++ b/packages/registry/__tests__/addon-registry-app.test.js
@@ -0,0 +1,18 @@
+import path from 'path';
+import { AddonRegistry } from '../src/addon-registry/addon-registry';
+import { describe, it, expect } from 'vitest';
+
+describe('AddonRegistry - get()', () => {
+ it('Basic information', () => {
+ const base = path.join(import.meta.dirname, 'fixtures', 'test-app');
+ const { addons, shadowAliases, theme } = AddonRegistry.init(base);
+ expect(addons).toStrictEqual(['@plone/slots', 'my-addon']);
+ expect(shadowAliases).toStrictEqual([
+ {
+ find: '@plone/slots/components/Logo/Logo.svg',
+ replacement: `${base}/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg`,
+ },
+ ]);
+ expect(theme).toStrictEqual(undefined);
+ });
+});
diff --git a/packages/registry/__tests__/addon-registry.test.js b/packages/registry/__tests__/addon-registry.test.js
new file mode 100644
index 0000000000..cc21df88fa
--- /dev/null
+++ b/packages/registry/__tests__/addon-registry.test.js
@@ -0,0 +1,366 @@
+import path from 'path';
+import {
+ AddonRegistry,
+ buildDependencyGraph,
+ getAddonsLoaderChain,
+} from '../src/addon-registry/addon-registry';
+import { vi, describe, it, expect, test, beforeEach, afterEach } from 'vitest';
+
+vi.mock(
+ 'fixtures/test-volto-project/node_modules/@plone/volto/package.json',
+ () => ({
+ // TODO: mock the packages folder inside the mocked @plone/volto to work with resolves
+ coreAddons: {},
+ }),
+ { virtual: true },
+);
+
+describe('AddonRegistry - get()', () => {
+ it('Basic information', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { addons, shadowAliases, theme } = AddonRegistry.init(base);
+ expect(addons).toStrictEqual([
+ 'test-released-unmentioned:extra1,extra2',
+ 'test-released-dummy',
+ 'test-addon',
+ 'test-released-addon:extra',
+ 'test-released-source-addon',
+ 'my-volto-config-addon',
+ ]);
+ expect(shadowAliases).toStrictEqual([
+ {
+ find: 'test-released-source-addon/index',
+ replacement: `${base}/addons/test-addon/src/custom-addons/test-released-source-addon/index.js`,
+ },
+ {
+ find: '@plone/volto/server',
+ replacement: `${base}/addons/test-addon/src/custom-addons/volto/server.jsx`,
+ },
+ {
+ find: '@root/marker',
+ replacement: `${base}/node_modules/test-released-source-addon/src/customizations/@root/marker.js`,
+ },
+ {
+ find: '@plone/volto/routes',
+ replacement: `${base}/node_modules/test-released-source-addon/src/customizations/routes.tsx`,
+ },
+ {
+ find: '@plone/volto/client',
+ replacement: `${base}/node_modules/test-released-source-addon/src/customizations/client.js`,
+ },
+ {
+ find: '@plone/volto/TSComponent',
+ replacement: `${base}/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx`,
+ },
+ {
+ find: '@plone/volto/LanguageSwitcher',
+ replacement: `${base}/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js`,
+ },
+ ]);
+ expect(theme).toStrictEqual(undefined);
+ });
+});
+
+describe('AddonRegistry - Project', () => {
+ it('works in a mock project directory', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+
+ const voltoPath = `${base}/node_modules/@plone/volto`;
+
+ expect(registry.projectRootPath).toStrictEqual(base);
+ expect(registry.voltoPath).toStrictEqual(voltoPath);
+
+ expect(registry.addonNames).toStrictEqual([
+ 'test-addon',
+ 'test-released-addon',
+ 'test-released-source-addon',
+ 'my-volto-config-addon',
+ 'test-released-dummy',
+ 'test-released-unmentioned',
+ ]);
+
+ expect(registry.packages).toEqual({
+ 'test-addon': {
+ isPublishedPackage: false,
+ modulePath: `${base}/addons/test-addon/src`,
+ name: 'test-addon',
+ packageJson: `${base}/addons/test-addon/package.json`,
+ addons: ['test-released-dummy'],
+ isRegisteredAddon: true,
+ version: '0.0.0',
+ },
+ 'test-released-addon': {
+ basePath: `${base}/node_modules/test-released-addon`,
+ isPublishedPackage: true,
+ modulePath: `${base}/node_modules/test-released-addon`,
+ name: 'test-released-addon',
+ packageJson: `${base}/node_modules/test-released-addon/package.json`,
+ addons: ['test-released-unmentioned:extra1,extra2'],
+ isRegisteredAddon: true,
+ tsConfigPaths: null,
+ version: '0.0.0',
+ },
+ 'test-released-source-addon': {
+ basePath: `${base}/node_modules/test-released-source-addon`,
+ isPublishedPackage: true,
+ modulePath: `${base}/node_modules/test-released-source-addon/src`,
+ name: 'test-released-source-addon',
+ packageJson: `${base}/node_modules/test-released-source-addon/package.json`,
+ razzleExtender: `${base}/node_modules/test-released-source-addon/razzle.extend.js`,
+ addons: [],
+ isRegisteredAddon: true,
+ tsConfigPaths: null,
+ version: '0.0.0',
+ },
+ 'test-released-unmentioned': {
+ addons: [],
+ basePath: `${base}/node_modules/test-released-unmentioned`,
+ isPublishedPackage: true,
+ modulePath: `${base}/node_modules/test-released-unmentioned`,
+ name: 'test-released-unmentioned',
+ packageJson: `${base}/node_modules/test-released-unmentioned/package.json`,
+ isRegisteredAddon: true,
+ tsConfigPaths: null,
+ version: '0.0.0',
+ },
+ 'my-volto-config-addon': {
+ addons: ['test-released-dummy'],
+ isPublishedPackage: false,
+ modulePath: `${base}/addons/my-volto-config-addon/src`,
+ name: 'my-volto-config-addon',
+ packageJson: `${base}/addons/my-volto-config-addon/package.json`,
+ isRegisteredAddon: true,
+ version: '0.0.0',
+ },
+ 'test-released-dummy': {
+ addons: ['test-released-unmentioned'],
+ isPublishedPackage: false,
+ modulePath: `${base}/addons/test-released-dummy`,
+ name: 'test-released-dummy',
+ packageJson: `${base}/addons/test-released-dummy/package.json`,
+ isRegisteredAddon: true,
+ version: '0.0.0',
+ },
+ });
+ });
+
+ it('provides aliases for addons', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.getResolveAliases()).toStrictEqual({
+ 'my-volto-config-addon': `${base}/addons/my-volto-config-addon/src`,
+ 'test-addon': `${base}/addons/test-addon/src`,
+ 'test-released-addon': `${base}/node_modules/test-released-addon`,
+ 'test-released-dummy': `${base}/addons/test-released-dummy`,
+ 'test-released-source-addon': `${base}/node_modules/test-released-source-addon/src`,
+ 'test-released-unmentioned': `${base}/node_modules/test-released-unmentioned`,
+ });
+ });
+
+ it('provides addon extenders', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.getAddonExtenders().length).toBe(1);
+ });
+
+ it('provides a list of addon records ordered by initial package declaration', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+ const addons = registry.getAddons();
+ expect(addons.map((a) => a.name)).toStrictEqual([
+ 'test-released-unmentioned',
+ 'test-released-dummy',
+ 'test-addon',
+ 'test-released-addon',
+ 'test-released-source-addon',
+ 'my-volto-config-addon',
+ ]);
+ });
+
+ it('provides customization paths declared in a Volto project', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.getProjectCustomizationPaths()).toStrictEqual({
+ '@plone/volto/LanguageSwitcher': `${base}/src/customizations/LanguageSwitcher.js`,
+ '@plone/volto/TSComponent': `${base}/src/customizations/TSComponent.jsx`,
+ '@plone/volto/client': `${base}/src/customizations/client.js`,
+ '@plone/volto/routes': `${base}/src/customizations/routes.tsx`,
+ 'test-addon/testaddon': `${base}/src/custom-addons/test-addon/testaddon.js`,
+ '@plone/volto/server': `${base}/src/customizations/server.jsx`,
+ });
+ });
+
+ it('provides customization paths declared in addons', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.getAddonCustomizationPaths()).toStrictEqual({
+ '@plone/volto/LanguageSwitcher': `${base}/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js`,
+ '@plone/volto/TSComponent': `${base}/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx`,
+ '@plone/volto/client': `${base}/node_modules/test-released-source-addon/src/customizations/client.js`,
+ '@plone/volto/routes': `${base}/node_modules/test-released-source-addon/src/customizations/routes.tsx`,
+ '@plone/volto/server': `${base}/addons/test-addon/src/custom-addons/volto/server.jsx`,
+ '@root/marker': `${base}/node_modules/test-released-source-addon/src/customizations/@root/marker.js`,
+ 'test-released-source-addon/index': `${base}/addons/test-addon/src/custom-addons/test-released-source-addon/index.js`,
+ });
+ });
+});
+
+describe('Addon chain loading dependencies', () => {
+ const depTree = {
+ add0: ['add1'],
+ add1: ['add2:e0', 'add4'],
+ add2: ['add3:e6', 'add5', 'add6'],
+ add3: ['add0'],
+ add4: ['add2:e1,e3'],
+ add5: ['add6'],
+ };
+ const extractor = (name) => depTree[name] || [];
+
+ test('no addons', () => {
+ const graph = buildDependencyGraph([], extractor);
+ const deps = getAddonsLoaderChain(graph);
+ expect(deps).toEqual([]);
+ });
+
+ test('one addon', () => {
+ const graph = buildDependencyGraph(['volto-addon1'], extractor);
+ const deps = getAddonsLoaderChain(graph);
+ expect(deps).toEqual(['volto-addon1']);
+ });
+
+ test('two addons', () => {
+ const graph = buildDependencyGraph(
+ ['volto-addon1', 'volto-addon2'],
+ extractor,
+ );
+ const deps = getAddonsLoaderChain(graph);
+ expect(deps).toEqual(['volto-addon1', 'volto-addon2']);
+ });
+
+ test('one addon with dependency', () => {
+ const graph = buildDependencyGraph(['add5'], extractor);
+ const deps = getAddonsLoaderChain(graph);
+ expect(deps).toEqual(['add6', 'add5']);
+ });
+
+ test('one addon with circular dependencies', () => {
+ const graph = buildDependencyGraph(['add0'], extractor);
+ const deps = getAddonsLoaderChain(graph);
+ expect(deps).toEqual([
+ 'add3:e6',
+ 'add6',
+ 'add5',
+ 'add2:e0,e1,e3',
+ 'add4',
+ 'add1',
+ 'add0',
+ ]);
+ });
+});
+
+describe('Addon via env var - Released addon (same as dev add-on, when resolved via workspaces)', () => {
+ const originalEnv = process.env;
+
+ beforeEach(() => {
+ vi.resetModules();
+ process.env = {
+ ...originalEnv,
+ ADDONS: 'test-released-via-addons-env-var',
+ };
+ });
+
+ afterEach(() => {
+ process.env = originalEnv;
+ });
+
+ it('addons can be specified on the fly using ADDONS env var - Released addon', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+ expect(
+ Object.keys(registry.packages).includes(
+ 'test-released-via-addons-env-var',
+ ),
+ ).toBe(true);
+ });
+});
+
+describe('Add-on via config file provided using an env var', () => {
+ const originalEnv = process.env;
+ const base = path.join(import.meta.dirname, 'fixtures', 'test-volto-project');
+
+ beforeEach(() => {
+ vi.resetModules();
+ });
+
+ afterEach(() => {
+ process.env = originalEnv;
+ });
+
+ it('VOLTOCONFIG - provides a list of addon records ordered using an env var for providing the configuration file', () => {
+ process.env = {
+ ...originalEnv,
+ VOLTOCONFIG: `${base}/volto.config.envvar.js`,
+ };
+ const { registry } = AddonRegistry.init(base);
+ const addons = registry.getAddons();
+ expect(addons.map((a) => a.name)).toStrictEqual([
+ 'test-released-unmentioned',
+ 'test-released-dummy',
+ 'test-addon',
+ 'test-released-addon',
+ 'test-released-source-addon',
+ 'my-volto-config-addon-via-env-var',
+ ]);
+ });
+
+ it('[REGISTRYCONFIG - provides a list of addon records ordered using an env var for providing the configuration file', () => {
+ process.env = {
+ ...originalEnv,
+ REGISTRYCONFIG: `${base}/volto.config.envvar.js`,
+ };
+
+ const { registry } = AddonRegistry.init(base);
+ const addons = registry.getAddons();
+ expect(addons.map((a) => a.name)).toStrictEqual([
+ 'test-released-unmentioned',
+ 'test-released-dummy',
+ 'test-addon',
+ 'test-released-addon',
+ 'test-released-source-addon',
+ 'my-volto-config-addon-via-env-var',
+ ]);
+ });
+});
diff --git a/packages/registry/__tests__/create-addons-loader.test.js b/packages/registry/__tests__/create-addons-loader.test.js
new file mode 100644
index 0000000000..60b611ad67
--- /dev/null
+++ b/packages/registry/__tests__/create-addons-loader.test.js
@@ -0,0 +1,164 @@
+import {
+ getAddonsLoaderCode,
+ nameFromPackage,
+} from '../src/addon-registry/create-addons-loader';
+import { describe, expect, test } from 'vitest';
+
+describe('create-addons-loader code generation', () => {
+ test('no addon creates simple loader, default = no loadProjectConfig', () => {
+ const code = getAddonsLoaderCode([]);
+ expect(code).toBe(`/*
+This file is autogenerated. Don't change it directly.
+Instead, change the "addons" setting in your package.json file.
+*/
+
+
+const addonsInfo = {};
+export { addonsInfo };
+
+const safeWrapper = (func) => (config) => {
+ const res = func(config);
+ if (typeof res === 'undefined') {
+ throw new Error("Configuration function doesn't return config");
+ }
+ return res;
+}
+
+const projectConfigLoader = false;
+const projectConfig = (config) => {
+ return projectConfigLoader && typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config;
+}
+
+const load = (config) => {
+ const addonLoaders = [];
+ if(!addonLoaders.every((el) => typeof el === "function")) {
+ throw new TypeError(
+ 'Each addon has to provide a function applying its configuration to the projects configuration.',
+ );
+ }
+ return projectConfig(addonLoaders.reduce((acc, apply) => safeWrapper(apply)(acc), config));
+};
+export default load;
+`);
+ });
+
+ test('no addon creates simple loader, loadProjectConfig set to true', () => {
+ const code = getAddonsLoaderCode([], {}, true);
+ expect(code).toBe(`/*
+This file is autogenerated. Don't change it directly.
+Instead, change the "addons" setting in your package.json file.
+*/
+
+const projectConfigLoader = require('@root/config');
+
+const addonsInfo = {};
+export { addonsInfo };
+
+const safeWrapper = (func) => (config) => {
+ const res = func(config);
+ if (typeof res === 'undefined') {
+ throw new Error("Configuration function doesn't return config");
+ }
+ return res;
+}
+
+
+const projectConfig = (config) => {
+ return projectConfigLoader && typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config;
+}
+
+const load = (config) => {
+ const addonLoaders = [];
+ if(!addonLoaders.every((el) => typeof el === "function")) {
+ throw new TypeError(
+ 'Each addon has to provide a function applying its configuration to the projects configuration.',
+ );
+ }
+ return projectConfig(addonLoaders.reduce((acc, apply) => safeWrapper(apply)(acc), config));
+};
+export default load;
+`);
+ });
+
+ test('one addon creates loader', () => {
+ const code = getAddonsLoaderCode(['volto-addon1']);
+ expect(code.indexOf("import voltoAddon1 from 'volto-addon1';") > 0).toBe(
+ true,
+ );
+ });
+
+ test('two addons create loaders', () => {
+ const code = getAddonsLoaderCode(['volto-addon1', 'volto-addon2']);
+ expect(
+ code.indexOf(`
+import voltoAddon1 from 'volto-addon1';
+import voltoAddon2 from 'volto-addon2';`) > 0,
+ ).toBe(true);
+ });
+
+ test('one addons plus one extra creates loader', () => {
+ const code = getAddonsLoaderCode(['volto-addon1:loadExtra1']);
+ expect(
+ code.indexOf(`
+import voltoAddon1, { loadExtra1 as loadExtra10 } from 'volto-addon1';
+`) > 0,
+ ).toBe(true);
+ });
+
+ test('one addons plus two extras creates loader', () => {
+ const code = getAddonsLoaderCode(['volto-addon1:loadExtra1,loadExtra2']);
+ expect(
+ code.indexOf(`
+import voltoAddon1, { loadExtra1 as loadExtra10, loadExtra2 as loadExtra21 } from 'volto-addon1';
+`) > 0,
+ ).toBe(true);
+ });
+
+ test('two addons plus extras creates loader', () => {
+ const code = getAddonsLoaderCode([
+ 'volto-addon1:loadExtra1,loadExtra2',
+ 'volto-addon2:loadExtra3,loadExtra4',
+ ]);
+ expect(
+ code.indexOf(`
+import voltoAddon1, { loadExtra1 as loadExtra10, loadExtra2 as loadExtra21 } from 'volto-addon1';
+import voltoAddon2, { loadExtra3 as loadExtra32, loadExtra4 as loadExtra43 } from 'volto-addon2';
+`) > 0,
+ ).toBe(true);
+ });
+});
+
+describe('create-addons-loader default name generation', () => {
+ const getName = nameFromPackage;
+
+ test('passing a simple word returns a word', () => {
+ expect(getName('something')).toBe('something');
+ });
+
+ test('passing a kebab-name returns a word', () => {
+ expect(getName('volto-something-else')).toBe('voltoSomethingElse');
+ });
+
+ test('passing a simple relative path returns random string', () => {
+ const rand = getName('../../');
+ expect(rand.length).toBe(10);
+ expect(new RegExp(/[abcdefghjk]+/).exec(rand)[0].length > 0).toBe(true);
+ });
+ test('passing a tilda relative path with addon strips tilda', () => {
+ const name = getName('~/addons/volto-addon1');
+ expect(name).toBe('addonsvoltoAddon1');
+ });
+ test('passing a namespace package strips @', () => {
+ const name = getName('@plone/volto-addon1');
+ expect(name).toBe('plonevoltoAddon1');
+ });
+ test('passing a tilda relative path strips tilda', () => {
+ const name = getName('~/../');
+ expect(name.length).toBe(10);
+ expect(new RegExp(/[abcdefghjk]+/).exec(name)[0].length > 0).toBe(true);
+ });
+ test('passing a backspaced path strips backspace', () => {
+ const name = getName('c:\\nodeprojects');
+ expect(name).toBe('cnodeprojects');
+ });
+});
diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/components/Logo/Logo.svg b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/components/Logo/Logo.svg
new file mode 100644
index 0000000000..1aa6adfdf3
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/components/Logo/Logo.svg
@@ -0,0 +1 @@
+
diff --git a/packages/volto/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/src/index.js b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/index.js
similarity index 100%
rename from packages/volto/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/src/index.js
rename to packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/index.js
diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/package.json b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/package.json
new file mode 100644
index 0000000000..68a86c4d37
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "@plone/slots",
+ "main": "index.js"
+}
diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg
new file mode 100644
index 0000000000..3e8fcacea6
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg
@@ -0,0 +1 @@
+
diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/index.js b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/index.js
new file mode 100644
index 0000000000..8cb61d300a
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/index.js
@@ -0,0 +1 @@
+export default (config) => config;
diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/package.json b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/package.json
new file mode 100644
index 0000000000..ccf0c501c1
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "my-addon",
+ "main": "index.js"
+}
diff --git a/packages/registry/__tests__/fixtures/test-app/package.json b/packages/registry/__tests__/fixtures/test-app/package.json
new file mode 100644
index 0000000000..8dd7aa1a9c
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "test-app",
+ "type": "module"
+}
diff --git a/packages/registry/__tests__/fixtures/test-app/registry.config.js b/packages/registry/__tests__/fixtures/test-app/registry.config.js
new file mode 100644
index 0000000000..9679e4450c
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/registry.config.js
@@ -0,0 +1,3 @@
+const addons = ['@plone/slots', 'my-addon'];
+
+export { addons };
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/package.json b/packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/package.json
new file mode 100644
index 0000000000..87e8d2ad96
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "test-addon",
+ "version": "0.0.0",
+ "customizationPaths": [
+ "src/custom-addons"
+ ],
+ "addons": [
+ "test-released-dummy"
+ ]
+}
diff --git a/apps/plone/cypress/.gitkeep b/packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/src/testaddon.js
similarity index 100%
rename from apps/plone/cypress/.gitkeep
rename to packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/src/testaddon.js
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/index.js b/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/index.js
new file mode 100644
index 0000000000..8cb61d300a
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/index.js
@@ -0,0 +1 @@
+export default (config) => config;
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/package.json b/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/package.json
new file mode 100644
index 0000000000..c81be50ab7
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "non-volto-addon-lib",
+ "version": "0.0.0",
+ "main": "index.js"
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/package.json b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/package.json
new file mode 100644
index 0000000000..87e8d2ad96
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "test-addon",
+ "version": "0.0.0",
+ "customizationPaths": [
+ "src/custom-addons"
+ ],
+ "addons": [
+ "test-released-dummy"
+ ]
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/test-released-source-addon/index.js b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/test-released-source-addon/index.js
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/test-released-source-addon/index.js
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/volto/server.jsx b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/volto/server.jsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/volto/server.jsx
@@ -0,0 +1 @@
+//
diff --git a/apps/plone/cypress/tests/.gitkeep b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/testaddon.js
similarity index 100%
rename from apps/plone/cypress/tests/.gitkeep
rename to packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/testaddon.js
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/index.js b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/index.js
new file mode 100644
index 0000000000..8cb61d300a
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/index.js
@@ -0,0 +1 @@
+export default (config) => config;
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/package.json b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/package.json
new file mode 100644
index 0000000000..18c940d721
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "test-released-dummy",
+ "version": "0.0.0",
+ "main": "index.js",
+ "addons": [
+ "test-released-unmentioned"
+ ]
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/jsconfig.json b/packages/registry/__tests__/fixtures/test-volto-project/jsconfig.json
new file mode 100644
index 0000000000..4c3beb25ec
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/jsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "paths": {
+ "test-addon": ["test-addon/src"],
+ "test-released-dummy": ["test-released-dummy"],
+ "my-volto-config-addon": ["my-volto-config-addon/src"],
+ "non-volto-addon-lib": ["non-volto-addon-lib/src"]
+ },
+ "baseUrl": "addons"
+ }
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/LanguageSwitcher.jsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/LanguageSwitcher.jsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/LanguageSwitcher.jsx
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/TSComponent.tsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/TSComponent.tsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/TSComponent.tsx
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/client.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/client.js
new file mode 100644
index 0000000000..0dae97301d
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/client.js
@@ -0,0 +1 @@
+//dummy file
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/routes.jsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/routes.jsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/routes.jsx
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/pnpm-lock.yaml b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/server.jsx
similarity index 100%
rename from packages/registry/pnpm-lock.yaml
rename to packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/server.jsx
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/index.js
new file mode 100644
index 0000000000..8cb61d300a
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/index.js
@@ -0,0 +1 @@
+export default (config) => config;
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/package.json
new file mode 100644
index 0000000000..ac0652a9b0
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "my-volto-config-addon-via-env-var",
+ "version": "0.0.0",
+ "main": "index.js",
+ "customizationPaths": [
+ "src/custom-addons"
+ ],
+ "addons": [
+ "test-released-dummy"
+ ]
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/index.js
new file mode 100644
index 0000000000..8cb61d300a
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/index.js
@@ -0,0 +1 @@
+export default (config) => config;
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/package.json
new file mode 100644
index 0000000000..4f1f61b465
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "test-released-addon",
+ "version": "0.0.0",
+ "main": "index.js",
+ "addons": [
+ "test-released-unmentioned:extra1,extra2"
+ ]
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/index.js
new file mode 100644
index 0000000000..53e61140f2
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/index.js
@@ -0,0 +1,2 @@
+export default (config) => config;
+
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/package.json
new file mode 100644
index 0000000000..4640873bbf
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "test-released-dummy",
+ "main": "index.js",
+ "addons": []
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/package.json
new file mode 100644
index 0000000000..3220bd17cc
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "test-released-source-addon",
+ "version": "0.0.0",
+ "main": "src/index.js"
+}
diff --git a/styles/Vocab/Base/accept.txt b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/razzle.extend.js
similarity index 100%
rename from styles/Vocab/Base/accept.txt
rename to packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/razzle.extend.js
diff --git a/styles/Vocab/Base/reject.txt b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/@root/marker.js
similarity index 100%
rename from styles/Vocab/Base/reject.txt
rename to packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/@root/marker.js
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/client.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/client.js
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/client.js
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/routes.tsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/routes.tsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/routes.tsx
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/index.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/index.js
new file mode 100644
index 0000000000..53e61140f2
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/index.js
@@ -0,0 +1,2 @@
+export default (config) => config;
+
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/package.json
new file mode 100644
index 0000000000..b3b3c399f1
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "test-released-unmentioned",
+ "version": "0.0.0",
+ "main": "index.js"
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/index.js
new file mode 100644
index 0000000000..53e61140f2
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/index.js
@@ -0,0 +1,2 @@
+export default (config) => config;
+
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/package.json
new file mode 100644
index 0000000000..c6a4424310
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "test-released-via-addons-env-var",
+ "main": "index.js"
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/package.json b/packages/registry/__tests__/fixtures/test-volto-project/package.json
new file mode 100644
index 0000000000..bb67637bb2
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "test-volto-project",
+ "addons": [
+ "test-addon",
+ "test-released-addon:extra",
+ "test-released-source-addon"
+ ],
+ "customizationPaths": [
+ "src/custom-addons",
+ "src/customizations/"
+ ]
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/test-addon/testaddon.js b/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/test-addon/testaddon.js
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/test-addon/testaddon.js
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/volto/client.js b/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/volto/client.js
new file mode 100644
index 0000000000..4d389677f0
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/volto/client.js
@@ -0,0 +1 @@
+// another customization
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/LanguageSwitcher.js b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/LanguageSwitcher.js
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/LanguageSwitcher.js
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/TSComponent.jsx b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/TSComponent.jsx
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/client.js b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/client.js
new file mode 100644
index 0000000000..b262fa10a3
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/client.js
@@ -0,0 +1 @@
+// dummy file
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/routes.tsx b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/routes.tsx
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/server.jsx b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/server.jsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/server.jsx
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/marker.js b/packages/registry/__tests__/fixtures/test-volto-project/src/marker.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/volto.config.envvar.js b/packages/registry/__tests__/fixtures/test-volto-project/volto.config.envvar.js
new file mode 100644
index 0000000000..2e37cd37c2
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/volto.config.envvar.js
@@ -0,0 +1,3 @@
+module.exports = {
+ addons: ['my-volto-config-addon-via-env-var'],
+};
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/volto.config.js b/packages/registry/__tests__/fixtures/test-volto-project/volto.config.js
new file mode 100644
index 0000000000..ec05e8e085
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/volto.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ addons: ['my-volto-config-addon'],
+};
diff --git a/packages/registry/__tests__/fixtures/volto-addon1.js b/packages/registry/__tests__/fixtures/volto-addon1.js
new file mode 100644
index 0000000000..b406bacbd8
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/volto-addon1.js
@@ -0,0 +1,14 @@
+export default (config) => {
+ const settings = {
+ nonContentRoutes: [],
+ supportedLanguages: ['en'],
+ navDepth: 1,
+ };
+
+ config.settings = {
+ ...config.settings,
+ ...settings,
+ };
+
+ return config;
+};
diff --git a/packages/registry/__tests__/fixtures/volto-addon2.js b/packages/registry/__tests__/fixtures/volto-addon2.js
new file mode 100644
index 0000000000..62ec3d2cf0
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/volto-addon2.js
@@ -0,0 +1,29 @@
+export default (config) => {
+ const settings = {
+ nonContentRoutes: [],
+ supportedLanguages: ['en', 'de'],
+ navDepth: 3,
+ };
+
+ config.settings = {
+ ...config.settings,
+ ...settings,
+ };
+
+ return config;
+};
+
+const additionalConfig = (config) => {
+ const settings = {
+ navDepth: 6,
+ };
+
+ config.settings = {
+ ...config.settings,
+ ...settings,
+ };
+
+ return config;
+};
+
+export { additionalConfig };
diff --git a/packages/registry/__tests__/fixtures/volto-addon3.js b/packages/registry/__tests__/fixtures/volto-addon3.js
new file mode 100644
index 0000000000..2231f8a8b1
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/volto-addon3.js
@@ -0,0 +1,41 @@
+export default (config) => {
+ const settings = {
+ nonContentRoutes: [],
+ supportedLanguages: ['en', 'de'],
+ navDepth: 3,
+ };
+
+ config.settings = {
+ ...config.settings,
+ ...settings,
+ };
+
+ return config;
+};
+
+const additionalConfig = (config) => {
+ const settings = {
+ navDepth: 6,
+ };
+
+ config.settings = {
+ ...config.settings,
+ ...settings,
+ };
+
+ return config;
+};
+
+const alternateAdditionalConfig = (config) => {
+ const settings = {
+ navDepth: 10,
+ };
+
+ config.settings = {
+ ...config.settings,
+ ...settings,
+ };
+
+ return config;
+};
+export { additionalConfig, alternateAdditionalConfig };
diff --git a/packages/registry/docs/_static/Plone_logo_square.png b/packages/registry/docs/_static/Plone_logo_square.png
new file mode 100644
index 0000000000..f42a1220d7
Binary files /dev/null and b/packages/registry/docs/_static/Plone_logo_square.png differ
diff --git a/packages/registry/docs/_static/favicon.ico b/packages/registry/docs/_static/favicon.ico
new file mode 100644
index 0000000000..ae7bb0cd8b
Binary files /dev/null and b/packages/registry/docs/_static/favicon.ico differ
diff --git a/packages/registry/docs/_static/logo.svg b/packages/registry/docs/_static/logo.svg
new file mode 100644
index 0000000000..6bfdc1ee90
--- /dev/null
+++ b/packages/registry/docs/_static/logo.svg
@@ -0,0 +1,48 @@
+
+
+
+
diff --git a/packages/registry/docs/_static/print.css b/packages/registry/docs/_static/print.css
new file mode 100644
index 0000000000..8dbc2d5794
--- /dev/null
+++ b/packages/registry/docs/_static/print.css
@@ -0,0 +1,3 @@
+.tooltip {
+ display: none;
+}
diff --git a/packages/registry/docs/conceptual-guides/add-on-registry.md b/packages/registry/docs/conceptual-guides/add-on-registry.md
new file mode 100644
index 0000000000..0c107a6134
--- /dev/null
+++ b/packages/registry/docs/conceptual-guides/add-on-registry.md
@@ -0,0 +1,38 @@
+---
+myst:
+ html_meta:
+ "description": "An explanation of the add-on registry in @plone/registry"
+ "property=og:description": "An explanation of the add-on registry in @plone/registry"
+ "property=og:title": "Add-on registry"
+ "keywords": "@plone/registry, registry, add-on"
+---
+
+# Add-on registry
+
+An add-on registry is a facility that allows an app, which was built on an existing framework, to itself be extensible and pluggable.
+
+The add-on registry is a store where you can register a number of add-ons that your app consumes.
+
+The add-on registry can be queried, so it can provide a list of add-ons installed in the registry and their properties.
+
+
+## What is an add-on?
+
+Add-on packages are just CommonJS or ESM packages.
+Their main purpose is to encapsulate logic, configuration, and customizations in a reusable way.
+The only requirement is that their primary entry point (the `main` key of their {file}`package.json`) points to a module that exports a default function, which acts as a default configuration loader for that package.
+
+Add-ons are applied in the order they are declared in the `addons` key of {file}`package.json` or programmatically via a provided configuration file.
+Add-ons can override configuration coming from other add-ons, providing a hierarchy of configuration stacks.
+
+An add-on can be published in an npm registry, just as any other package.
+However, add-ons are meant not to be transpiled, but built along with your app code.
+They can be released as "source" packages or used directly in your app as local code.
+
+Add-ons can be chained, where each one can configure the app in some way.
+If needed, each add-on in the chain can override or extend the previous configuration that other add-ons set.
+Thus, the order in which you register add-ons matters.
+
+Add-ons can define shadowed components.
+"Component shadowing" is a technique for overriding modules of other packages at build time.
+This technique builds upon the `resolve.aliases` facilities of bundlers, so modules can be replaced when the app is being built.
diff --git a/packages/registry/docs/conceptual-guides/component-registry.md b/packages/registry/docs/conceptual-guides/component-registry.md
new file mode 100644
index 0000000000..08fb9226d9
--- /dev/null
+++ b/packages/registry/docs/conceptual-guides/component-registry.md
@@ -0,0 +1,20 @@
+---
+myst:
+ html_meta:
+ "description": "An explanation of the component registry in @plone/registry"
+ "property=og:description": "An explanation of the component registry in @plone/registry"
+ "property=og:title": "Component registry"
+ "keywords": "@plone/registry, registry, component"
+---
+
+# Component registry
+
+The configuration registry stores a component registry in itself.
+The component registry is a mapping of names to components.
+You can look up a name, and receive a component that you can reference in your code.
+This provides an alternative, and more convenient, way to customize components in a pluggable way.
+
+You can programmatically override such registrations from your add-on or projects because it's stored in the configuration registry.
+You can customize a component without using shadowing at all, if the code that uses the component retrieves from the component registry, rather than import it directly.
+You can even have modifiers to the component registrations through dependencies.
+Thus you can adapt the call, given an array of such dependencies.
diff --git a/packages/registry/docs/conceptual-guides/configuration-registry.md b/packages/registry/docs/conceptual-guides/configuration-registry.md
new file mode 100644
index 0000000000..c1fd19db20
--- /dev/null
+++ b/packages/registry/docs/conceptual-guides/configuration-registry.md
@@ -0,0 +1,78 @@
+---
+myst:
+ html_meta:
+ "description": "An explanation of the configuration registry in @plone/registry"
+ "property=og:description": "An explanation of the configuration registry in @plone/registry"
+ "property=og:title": "Configuration registry"
+ "keywords": "@plone/registry, registry, configuration"
+---
+
+# Configuration registry
+
+The configuration registry supplements the add-on registry.
+They both work together to provide extensibility and pluggability capabilities.
+The configuration registry is a facility that stores app configuration to share in the app.
+The add-ons save configuration from the registry using their default export function on app bootstrap time.
+They retrieve this configuration as needed by the functionality and components they expose.
+
+## Example use case - Pluggable block system
+
+Let's say that your app is the user interface of a content management system (CMS).
+This CMS uses blocks as its main fundamental unit of content.
+The pages that the CMS builds are made up of these blocks.
+The CMS has some basic available blocks, yet it's a requirement that integrators can register more blocks in a pluggable way.
+This app will use the add-on registry to extend the basic CMS capabilities, so an external add-on can supplement their own add-ons to those in the basic CMS.
+
+Let's assume we've defined a key in the registry `config.blocks.blocksConfig`, and defined a way to register the available blocks in the CMS as the keys in that object in the configuration registry:
+
+```js
+ config.blocks.blocksConfig.faq_viewer = {
+ id: 'faq_viewer',
+ title: 'FAQ Viewer',
+ edit: FAQBlockEdit,
+ view: FAQBlockView,
+ icon: chartIcon,
+ group: 'common',
+ restricted: false,
+ mostUsed: true,
+ sidebarTab: 1,
+ };
+```
+
+The configuration registry will have other keys already set by default, which will compose the initial set of basic blocks used by the CMS.
+Then the CMS will populate the available blocks in the user interface.
+
+The add-on is meant to extend the initial configuration.
+From the default export function of our add-on, you should provide the configuration of the new block:
+
+```ts
+export default function applyConfig(config: ConfigData) {
+ config.blocks.blocksConfig.faq_viewer = {
+ id: 'faq_viewer',
+ title: 'FAQ Viewer',
+ edit: FAQBlockEdit,
+ view: FAQBlockView,
+ icon: chartIcon,
+ group: 'common',
+ restricted: false,
+ mostUsed: true,
+ sidebarTab: 1,
+ };
+
+ return config;
+}
+```
+
+Once the app starts, the add-on registry will execute, in order, all the registered add-ons' default export functions, configuring the new block.
+The add-on will then become available to the CMS when it asks the configuration registry for it.
+
+
+## Configuration registry artifacts
+
+The configuration registry also stores special elements that can be queried and retrieved in a pluggable way.
+
+- Components
+- Slots
+- Utilities
+
+Some of the components are particular to the use case of a CMS, such as slots, but the abstraction can be ported and applied to different scenarios.
diff --git a/packages/registry/docs/conceptual-guides/utility-registry.md b/packages/registry/docs/conceptual-guides/utility-registry.md
new file mode 100644
index 0000000000..5c0d284bae
--- /dev/null
+++ b/packages/registry/docs/conceptual-guides/utility-registry.md
@@ -0,0 +1,14 @@
+---
+myst:
+ html_meta:
+ "description": "An explanation of the utility registry in @plone/registry"
+ "property=og:description": "An explanation of the utility registry in @plone/registry"
+ "property=og:title": "Utility registry"
+ "keywords": "@plone/registry, registry, utility"
+---
+
+# Utility registry
+
+The configuration registry stores a utility registry in itself.
+The component registry is a mapping of a `name` and a `type` to a method or function.
+The utility registry works similarly, but for methods and functions, and with additional query argument `type`.
diff --git a/packages/registry/docs/conf.py b/packages/registry/docs/conf.py
new file mode 100644
index 0000000000..fa39be8168
--- /dev/null
+++ b/packages/registry/docs/conf.py
@@ -0,0 +1,297 @@
+# Configuration file for the Sphinx documentation builder.
+# Plone Documentation build configuration file
+
+
+# -- Path setup --------------------------------------------------------------
+
+from datetime import datetime
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath("."))
+
+import os
+import json
+
+
+# -- Project information -----------------------------------------------------
+
+project = "@plone/registry documentation"
+copyright = "Plone Foundation"
+author = "Plone Community"
+trademark_name = "Plone"
+now = datetime.now()
+year = str(now.year)
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+with open(
+ os.path.join(os.path.abspath("."), "../package.json"), "r"
+) as package_json:
+ data = package_json.read()
+
+version_from_package_json = json.loads(data)["version"]
+
+if version_from_package_json:
+ # The short X.Y version.
+ version = version_from_package_json
+ # The full version, including alpha/beta/rc tags.
+ release = version_from_package_json
+else:
+ version = "1.8.0"
+ release = "1.8.0"
+
+
+# -- General configuration ----------------------------------------------------
+
+# Add any paths that contain templates here, relative to this directory.
+# templates_path = ["_templates"]
+
+# Add any Sphinx extension module names here, as strings.
+# They can be extensions coming with Sphinx (named "sphinx.ext.*")
+# or your custom ones.
+extensions = [
+ "myst_parser",
+ "sphinx.ext.intersphinx",
+ "sphinx.ext.todo",
+ "sphinx_copybutton",
+ "sphinxext.opengraph",
+]
+
+
+# If true, the Docutils Smart Quotes transform, originally based on SmartyPants
+# (limited to English) and currently applying to many languages, will be used
+# to convert quotes and dashes to typographically correct entities.
+# Note to maintainers: setting this to `True` will cause contractions and
+# hyphenated words to be marked as misspelled by spellchecker.
+smartquotes = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+# pygments_style = "sphinx.pygments_styles.PyramidStyle"
+pygments_style = "sphinx"
+
+# Options for the linkcheck builder
+# Ignore localhost
+linkcheck_ignore = [
+ # TODO: Before release, clean up any links to ignore
+ r"http://127.0.0.1",
+ r"http://localhost",
+ # Ignore pages that require authentication
+ r"https://github.com/plone/volto/issues/new/choose", # requires auth
+ # Ignore github.com pages with anchors
+ r"https://github.com/.*#.*",
+ # Ignore other specific anchors
+]
+linkcheck_anchors = True
+linkcheck_timeout = 5
+linkcheck_retries = 1
+
+# The suffix of source filenames.
+source_suffix = {
+ ".md": "markdown",
+ ".bugfix": "markdown",
+ ".breaking": "markdown",
+ ".documentation": "markdown",
+ ".feature": "markdown",
+ ".internal": "markdown",
+}
+
+# The master toctree document.
+master_doc = "index"
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = [
+]
+
+suppress_warnings = [
+ # "toc.excluded", # Suppress `WARNING: document isn't included in any toctree`
+ "toc.not_readable", # Suppress `WARNING: toctree contains reference to nonexisting document 'news*'`
+]
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = "plone_sphinx_theme"
+html_logo = "_static/logo.svg"
+html_favicon = "_static/favicon.ico"
+html_theme_options = {
+ "article_header_start": ["toggle-primary-sidebar"],
+ "extra_footer": """