From cb40a7f98ffc849e22d41f5ae3c8e16e73c90c1c Mon Sep 17 00:00:00 2001 From: trsergey Date: Thu, 4 Dec 2025 02:05:01 +0200 Subject: [PATCH 1/6] feat: add cookie consent functionality --- .../content/full/component/cookieConsent.yaml | 128 ++++++++++++++++++ apps/csk/src/proxy.ts | 74 +++++++++- apps/csk/uniform.server.config.js | 1 - package-lock.json | 9 ++ packages/csk-components/package.json | 2 + .../canvas/CookieConsent/cookie-consent.tsx | 126 +++++++++++++++++ .../CookieConsent/empty-placeholder.tsx | 12 ++ .../components/canvas/CookieConsent/index.tsx | 26 ++++ .../src/components/canvas/index.ts | 2 + .../csk-components/src/constants/index.ts | 1 + .../src/utils/useCookiesConsent.ts | 17 +++ 11 files changed, 393 insertions(+), 5 deletions(-) create mode 100644 apps/csk/content/full/component/cookieConsent.yaml create mode 100644 packages/csk-components/src/components/canvas/CookieConsent/cookie-consent.tsx create mode 100644 packages/csk-components/src/components/canvas/CookieConsent/empty-placeholder.tsx create mode 100644 packages/csk-components/src/components/canvas/CookieConsent/index.tsx create mode 100644 packages/csk-components/src/utils/useCookiesConsent.ts diff --git a/apps/csk/content/full/component/cookieConsent.yaml b/apps/csk/content/full/component/cookieConsent.yaml new file mode 100644 index 000000000..040432776 --- /dev/null +++ b/apps/csk/content/full/component/cookieConsent.yaml @@ -0,0 +1,128 @@ +# yaml-language-server: $schema=https://uniform.app/schemas/json-schema/component-definition/v1.json +$schema: https://uniform.app/schemas/json-schema/component-definition/v1.json +id: cookieConsent +name: Cookie Consent +icon: image-text +parameters: + - id: displayName + name: Display Name + type: text + guidance: Used as a display name for the canvas; not rendered in the markup. + typeConfig: null + localizable: true + - id: 186c770f-d86f-40f7-93ba-0e7c72cc3fcb + name: Allow Button + type: group + typeConfig: + collapsed: true + childrenParams: + - allowButtonText + - allowTextColor + - allowButtonColor + - allowButtonHoverColor + - id: allowButtonText + name: Button Text + type: text + typeConfig: null + localizable: true + - id: allowTextColor + name: Text Color + type: dex-color-palette-parameter + typeConfig: + allowColors: [] + selectedGroup: text + - id: allowButtonColor + name: Button Color + type: dex-color-palette-parameter + typeConfig: + allowColors: [] + selectedGroup: button + - id: allowButtonHoverColor + name: Button Hover Color + type: dex-color-palette-parameter + typeConfig: + allowColors: [] + selectedGroup: button + - id: 40d53061-3591-4436-a82b-27131d6f82fb + name: Decline Button + type: group + typeConfig: + collapsed: true + childrenParams: + - declineButtonText + - declineTextColor + - declineButtonColor + - declineButtonHoverColor + - id: declineButtonText + name: Button Text + type: text + typeConfig: null + localizable: true + - id: declineTextColor + name: Text Color + type: dex-color-palette-parameter + typeConfig: + allowColors: [] + selectedGroup: text + - id: declineButtonColor + name: Button Color + type: dex-color-palette-parameter + typeConfig: + allowColors: [] + selectedGroup: button + - id: declineButtonHoverColor + name: Button Hover Color + type: dex-color-palette-parameter + typeConfig: + allowColors: [] + selectedGroup: button + - id: 9f8178c3-539f-4a1e-8a4e-71bfcedfe223 + name: Presentation Settings + type: group + typeConfig: + collapsed: true + childrenParams: + - backgroundColor + - border + - fluidContent + - id: backgroundColor + name: Background Color + type: dex-color-palette-parameter + guidance: Use the Background Color parameter to control component background color. + typeConfig: null + - id: border + name: Border + type: dex-token-selector-parameter + guidance: Use the Border parameter to control the component border. + typeConfig: + selectedTokenType: border + - id: fluidContent + name: Fluid Content + type: checkbox + guidance: >- + Use the Fluid Content parameter to control the component width. + + This parameter restricts the component within the standard centered + container by default, or can enable it to render without restrictions + across the full screen width. + typeConfig: null +categoryId: 096fd5ed-5e2a-4bfa-834b-fb805d1d1ce9 +previewImageUrl: >- + https://res.cloudinary.com/uniform-demos/image/upload/csk-v-next/baseline/preview-images/banner-default.jpg +useTeamPermissions: true +slots: + - id: cookieConsentContent + name: Cookie Consent Content + allowedComponents: [] + allowAllComponents: true + inheritAllowedComponents: false + patternsInAllowedComponents: false +titleParameter: displayName +canBeComposition: false +created: '2025-12-03T14:42:40.816541+00:00' +updated: '2025-12-03T20:37:43.04187+00:00' +variants: + - id: top + name: Top + - id: bottom + name: Bottom diff --git a/apps/csk/src/proxy.ts b/apps/csk/src/proxy.ts index 2c92b1b44..679daf5d8 100644 --- a/apps/csk/src/proxy.ts +++ b/apps/csk/src/proxy.ts @@ -1,10 +1,76 @@ -import { uniformMiddleware } from '@uniformdev/canvas-next-rsc-v2/middleware'; +import { NextRequest } from 'next/server'; +import { handleUniformRoute } from '@uniformdev/canvas-next-rsc-v2/middleware'; import locales from '@/i18n/locales.json'; +import { geolocation } from '@vercel/functions'; import { formatPath } from './utils/formatPath'; -export default uniformMiddleware({ - rewriteRequestPath: async ({ url }) => ({ path: formatPath(url.pathname, locales.defaultLocale) }), -}); +const GDPR_COUNTRIES = [ + 'AL', + 'AD', + 'AM', + 'AT', + 'AZ', + 'BE', + 'BA', + 'BG', + 'HR', + 'CY', + 'CZ', + 'DK', + 'EE', + 'FI', + 'FR', + 'GE', + 'DE', + 'GR', + 'HU', + 'IS', + 'IE', + 'IT', + 'KZ', + 'LV', + 'LI', + 'LT', + 'LU', + 'MT', + 'MD', + 'MC', + 'ME', + 'MK', + 'NL', + 'NO', + 'PL', + 'PT', + 'RO', + 'SM', + 'RS', + 'SK', + 'SI', + 'ES', + 'SE', + 'CH', + 'TR', + 'UA', + 'GB', + 'VA', + 'CN', + 'BR', + 'ZA', + 'AE', +]; + +export default function proxy(request: NextRequest) { + const { country } = geolocation(request); + + const isGDPRCountry = GDPR_COUNTRIES.includes(country || ''); + const defaultConsent = !isGDPRCountry; + + return handleUniformRoute({ + request, + defaultConsent, + rewriteRequestPath: async ({ url }) => ({ path: formatPath(url.pathname, locales.defaultLocale) }), + }); +} export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)'], diff --git a/apps/csk/uniform.server.config.js b/apps/csk/uniform.server.config.js index fb6974bf5..e262d4c7c 100644 --- a/apps/csk/uniform.server.config.js +++ b/apps/csk/uniform.server.config.js @@ -1,6 +1,5 @@ /** @type {import('@uniformdev/canvas-next-rsc-v2/config').UniformServerConfig} */ module.exports = { - defaultConsent: true, playgroundPath: '/playground', experimental: { middlewareRuntimeCache: true, diff --git a/package-lock.json b/package-lock.json index b1bc75492..b9f990f15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6046,6 +6046,13 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/js-cookie": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -21839,6 +21846,7 @@ "commander": "^9.0.0", "cookies-next": "^4.3.0", "dotenv": "16.4.7", + "js-cookie": "^3.0.5", "next-themes": "^0.4.6", "ora": "^8.2.0", "prettier": "3.6.2", @@ -21852,6 +21860,7 @@ "devDependencies": { "@repo/eslint-config": "*", "@repo/typescript-config": "*", + "@types/js-cookie": "^3.0.6", "@uniformdev/assets": "20.7.1-alpha.97", "@uniformdev/canvas": "20.7.1-alpha.97", "@uniformdev/canvas-next-rsc-client-v2": "20.7.1-alpha.97", diff --git a/packages/csk-components/package.json b/packages/csk-components/package.json index 0aab76a67..b50eee8e9 100644 --- a/packages/csk-components/package.json +++ b/packages/csk-components/package.json @@ -91,6 +91,7 @@ "commander": "^9.0.0", "cookies-next": "^4.3.0", "dotenv": "16.4.7", + "js-cookie": "^3.0.5", "next-themes": "^0.4.6", "ora": "^8.2.0", "prettier": "3.6.2", @@ -101,6 +102,7 @@ "devDependencies": { "@repo/eslint-config": "*", "@repo/typescript-config": "*", + "@types/js-cookie": "^3.0.6", "@uniformdev/assets": "20.7.1-alpha.97", "@uniformdev/canvas": "20.7.1-alpha.97", "@uniformdev/canvas-next-rsc-client-v2": "20.7.1-alpha.97", diff --git a/packages/csk-components/src/components/canvas/CookieConsent/cookie-consent.tsx b/packages/csk-components/src/components/canvas/CookieConsent/cookie-consent.tsx new file mode 100644 index 000000000..0d5d93b5c --- /dev/null +++ b/packages/csk-components/src/components/canvas/CookieConsent/cookie-consent.tsx @@ -0,0 +1,126 @@ +'use client'; + +import { FC, useCallback, useEffect, useState } from 'react'; +import Cookies from 'js-cookie'; +import { ComponentParameter, UniformSlot, UniformText } from '@uniformdev/canvas-next-rsc-v2/component'; +import BaseBanner, { BannerVariants } from '@/components/ui/Banner'; +import BaseButton from '@/components/ui/Button'; +import useCookiesConsent from '@/utils/useCookiesConsent'; +import { withFlattenParameters } from '@/utils/withFlattenParameters'; +import { CookieConsentProps, CookieConsentParameters } from '.'; + +const CLIENT_COOKIE_NAME = 'hasAcceptedCookies'; + +const CookieConsent: FC = ({ + backgroundColor, + spacing, + border, + fluidContent, + allowTextColor, + allowButtonColor, + allowButtonHoverColor, + declineTextColor, + declineButtonColor, + declineButtonHoverColor, + slots, + variant, + parameters, + component, + context, +}) => { + const [showCookieConsent, setShowCookieConsent] = useState(false); + const { consent, updateConsent } = useCookiesConsent(); + const { defaultConsent } = context?.pageState || {}; + + useEffect(() => { + // get user has accepted cookies from cookie + const cookieValue = Cookies.get(CLIENT_COOKIE_NAME); + const userHasAcceptedCookies = cookieValue === undefined ? undefined : cookieValue === 'true'; + + if (userHasAcceptedCookies === undefined) { + // if user has not accepted cookies and default consent is false, show cookie consent + if (!defaultConsent) { + // eslint-disable-next-line react-hooks/set-state-in-effect + setShowCookieConsent(true); + + // if consent is true, but user has not accepted cookies, update consent to false + if (consent) { + updateConsent(false); + } + } + return; + } + + // if user has accepted cookies and consent is not the same, update consent + if (consent !== userHasAcceptedCookies) { + updateConsent(userHasAcceptedCookies); + } + }, [defaultConsent, consent, updateConsent, setShowCookieConsent]); + + const handleAllowCookiesButtonClick = useCallback(() => { + updateConsent(true); + Cookies.set(CLIENT_COOKIE_NAME, 'true'); + setShowCookieConsent(false); + }, [updateConsent]); + + const handleDeclineCookiesButtonClick = useCallback(() => { + updateConsent(false); + Cookies.set(CLIENT_COOKIE_NAME, 'false'); + setShowCookieConsent(false); + }, [updateConsent]); + + if (!showCookieConsent) return null; + + return ( + +
+ +
+ + } + component={component} + /> + + + } + component={component} + /> + +
+
+
+ ); +}; + +export default withFlattenParameters(CookieConsent); diff --git a/packages/csk-components/src/components/canvas/CookieConsent/empty-placeholder.tsx b/packages/csk-components/src/components/canvas/CookieConsent/empty-placeholder.tsx new file mode 100644 index 000000000..9e98af802 --- /dev/null +++ b/packages/csk-components/src/components/canvas/CookieConsent/empty-placeholder.tsx @@ -0,0 +1,12 @@ +import { ResolveEmptyPlaceholderOptions } from '@/types/cskTypes'; +import { DEFAULT_EMPTY_PLACEHOLDER } from '@/utils/createEmptyPlaceholderResolver'; +import { CookieConsentSlots } from '.'; + +export const CookieConsentEmptyPlaceholder = (props: ResolveEmptyPlaceholderOptions) => { + switch (props.slotName) { + case CookieConsentSlots.CookieConsentContent: + return { component: () =>
}; + default: + return DEFAULT_EMPTY_PLACEHOLDER; + } +}; diff --git a/packages/csk-components/src/components/canvas/CookieConsent/index.tsx b/packages/csk-components/src/components/canvas/CookieConsent/index.tsx new file mode 100644 index 000000000..a2316cd9f --- /dev/null +++ b/packages/csk-components/src/components/canvas/CookieConsent/index.tsx @@ -0,0 +1,26 @@ +import dynamic from 'next/dynamic'; +import { ContainerParameters } from '@/components/canvas/Container/parameters'; +import { ContentAlignment } from '@/components/ui/Banner'; +import { ComponentProps } from '@/types/cskTypes'; + +export enum CookieConsentSlots { + CookieConsentContent = 'cookieConsentContent', +} + +export type CookieConsentParameters = ContainerParameters & { + allowButtonText?: string; + allowTextColor?: string; + allowButtonColor?: string; + allowButtonHoverColor?: string; + declineButtonText?: string; + declineTextColor?: string; + declineButtonColor?: string; + declineButtonHoverColor?: string; +}; + +export type CookieConsentProps = ComponentProps; + +export { ContentAlignment }; + +export default dynamic(() => import('./cookie-consent').then(mod => mod.default)); +export { CookieConsentEmptyPlaceholder } from './empty-placeholder'; diff --git a/packages/csk-components/src/components/canvas/index.ts b/packages/csk-components/src/components/canvas/index.ts index 1b220dc7f..f24385df2 100644 --- a/packages/csk-components/src/components/canvas/index.ts +++ b/packages/csk-components/src/components/canvas/index.ts @@ -9,6 +9,7 @@ import Button from './Button'; import Card from './Card'; import Carousel from './Carousel'; import Container from './Container'; +import CookieConsent from './CookieConsent'; import Countdown from './Countdown'; import DemoHero from './DemoHero'; import Divider from './Divider'; @@ -55,6 +56,7 @@ export const cskComponentsMapping: ComponentMapping = { [cskComponentsNames.Carousel]: Carousel, [cskComponentsNames.Container]: Container, [cskComponentsNames.Countdown]: Countdown, + [cskComponentsNames.CookieConsent]: CookieConsent, [cskComponentsNames.Divider]: Divider, [cskComponentsNames.Grid]: Grid, [cskComponentsNames.GridItem]: GridItem, diff --git a/packages/csk-components/src/constants/index.ts b/packages/csk-components/src/constants/index.ts index 5d1b7deee..3abd8f59f 100644 --- a/packages/csk-components/src/constants/index.ts +++ b/packages/csk-components/src/constants/index.ts @@ -108,6 +108,7 @@ export enum cskComponentsNames { Carousel = 'carousel', Container = 'container', Countdown = 'countdown', + CookieConsent = 'cookieConsent', Divider = 'divider', Grid = 'grid', GridItem = 'gridItem', diff --git a/packages/csk-components/src/utils/useCookiesConsent.ts b/packages/csk-components/src/utils/useCookiesConsent.ts new file mode 100644 index 000000000..051a5439f --- /dev/null +++ b/packages/csk-components/src/utils/useCookiesConsent.ts @@ -0,0 +1,17 @@ +import { useCallback, useMemo } from 'react'; +import { useUniformContext } from '@uniformdev/canvas-next-rsc-client-v2'; + +const useCookiesConsent = () => { + const { context } = useUniformContext(); + + const consent = useMemo(() => context?.storage?.data?.consent, [context?.storage?.data?.consent]); + + const updateConsent = useCallback( + (value: boolean) => context?.storage.updateData([{ type: 'consent', data: value }]), + [context?.storage] + ); + + return { consent, updateConsent }; +}; + +export default useCookiesConsent; From 170989bd9b76c4b0b34e866c0e751e8c479b598e Mon Sep 17 00:00:00 2001 From: trsergey Date: Fri, 5 Dec 2025 13:39:23 +0200 Subject: [PATCH 2/6] chore: add cookie consent recipe --- packages/csk-recipes/src/commands/init/types.ts | 2 +- packages/csk-recipes/src/commands/init/utils.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/csk-recipes/src/commands/init/types.ts b/packages/csk-recipes/src/commands/init/types.ts index 298a36ef4..af3f689a6 100644 --- a/packages/csk-recipes/src/commands/init/types.ts +++ b/packages/csk-recipes/src/commands/init/types.ts @@ -1,5 +1,5 @@ export type Template = 'baseline' | 'coffee-shop' | string; -export type Recipe = 'localization' | 'ga' | 'uniform-insights' | 'ai-assistant' | 'ai-assistant-localized'; +export type Recipe = 'localization' | 'ga' | 'uniform-insights' | 'ai-assistant' | 'ai-assistant-localized' | 'cookie-consent'; export type EnvVariable = | 'UNIFORM_PROJECT_ID' diff --git a/packages/csk-recipes/src/commands/init/utils.ts b/packages/csk-recipes/src/commands/init/utils.ts index 75da33d35..1ad15ab09 100644 --- a/packages/csk-recipes/src/commands/init/utils.ts +++ b/packages/csk-recipes/src/commands/init/utils.ts @@ -170,6 +170,7 @@ export const selectRecipes = async (template?: Template): Promise => { { name: 'Multi-market Localization', value: 'localization' }, { name: 'Google Analytics', value: 'ga' }, { name: 'Uniform Insights', value: 'uniform-insights' }, + { name: 'Cookie Consent', value: 'cookie-consent' }, ...templateRecipes, ]; From 96bd8aab373254a165d6f1de2a1e70b827e70848 Mon Sep 17 00:00:00 2001 From: trsergey Date: Fri, 5 Dec 2025 13:54:40 +0200 Subject: [PATCH 3/6] chore: switche to middleware-based composition --- apps/csk/src/proxy.ts | 74 ++----------------------------- apps/csk/uniform.server.config.js | 1 + 2 files changed, 5 insertions(+), 70 deletions(-) diff --git a/apps/csk/src/proxy.ts b/apps/csk/src/proxy.ts index 679daf5d8..2c92b1b44 100644 --- a/apps/csk/src/proxy.ts +++ b/apps/csk/src/proxy.ts @@ -1,76 +1,10 @@ -import { NextRequest } from 'next/server'; -import { handleUniformRoute } from '@uniformdev/canvas-next-rsc-v2/middleware'; +import { uniformMiddleware } from '@uniformdev/canvas-next-rsc-v2/middleware'; import locales from '@/i18n/locales.json'; -import { geolocation } from '@vercel/functions'; import { formatPath } from './utils/formatPath'; -const GDPR_COUNTRIES = [ - 'AL', - 'AD', - 'AM', - 'AT', - 'AZ', - 'BE', - 'BA', - 'BG', - 'HR', - 'CY', - 'CZ', - 'DK', - 'EE', - 'FI', - 'FR', - 'GE', - 'DE', - 'GR', - 'HU', - 'IS', - 'IE', - 'IT', - 'KZ', - 'LV', - 'LI', - 'LT', - 'LU', - 'MT', - 'MD', - 'MC', - 'ME', - 'MK', - 'NL', - 'NO', - 'PL', - 'PT', - 'RO', - 'SM', - 'RS', - 'SK', - 'SI', - 'ES', - 'SE', - 'CH', - 'TR', - 'UA', - 'GB', - 'VA', - 'CN', - 'BR', - 'ZA', - 'AE', -]; - -export default function proxy(request: NextRequest) { - const { country } = geolocation(request); - - const isGDPRCountry = GDPR_COUNTRIES.includes(country || ''); - const defaultConsent = !isGDPRCountry; - - return handleUniformRoute({ - request, - defaultConsent, - rewriteRequestPath: async ({ url }) => ({ path: formatPath(url.pathname, locales.defaultLocale) }), - }); -} +export default uniformMiddleware({ + rewriteRequestPath: async ({ url }) => ({ path: formatPath(url.pathname, locales.defaultLocale) }), +}); export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)'], diff --git a/apps/csk/uniform.server.config.js b/apps/csk/uniform.server.config.js index e262d4c7c..fb6974bf5 100644 --- a/apps/csk/uniform.server.config.js +++ b/apps/csk/uniform.server.config.js @@ -1,5 +1,6 @@ /** @type {import('@uniformdev/canvas-next-rsc-v2/config').UniformServerConfig} */ module.exports = { + defaultConsent: true, playgroundPath: '/playground', experimental: { middlewareRuntimeCache: true, From 0d99cea60ca20e3de03ff2ba0c02ca9ed6082572 Mon Sep 17 00:00:00 2001 From: trsergey Date: Fri, 5 Dec 2025 14:00:06 +0200 Subject: [PATCH 4/6] fix: lint --- apps/csk-marketing-site/package.json | 2 +- apps/csk-storybook/package.json | 2 +- apps/csk/package.json | 2 +- package-lock.json | 24 +++++++++---------- package.json | 2 +- packages/csk-cli/package.json | 2 +- packages/csk-components/package.json | 2 +- packages/csk-recipes/package.json | 2 +- .../csk-recipes/src/commands/init/types.ts | 8 ++++++- packages/design-extensions-tools/package.json | 2 +- packages/eslint-config/package.json | 2 +- packages/internal-scripts/package.json | 2 +- packages/typescript-config/package.json | 2 +- 13 files changed, 30 insertions(+), 24 deletions(-) diff --git a/apps/csk-marketing-site/package.json b/apps/csk-marketing-site/package.json index f785478d1..c676897e5 100644 --- a/apps/csk-marketing-site/package.json +++ b/apps/csk-marketing-site/package.json @@ -1,6 +1,6 @@ { "name": "@uniformdev/csk-marketing-site", - "version": "6.1.50", + "version": "6.1.51", "private": true, "engines": { "yarn": "please-use-npm", diff --git a/apps/csk-storybook/package.json b/apps/csk-storybook/package.json index 9b1ba8ec5..4c575810e 100644 --- a/apps/csk-storybook/package.json +++ b/apps/csk-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@uniformdev/csk-storybook", - "version": "6.1.50", + "version": "6.1.51", "description": "CSK vNext Storybook is an interactive Storybook build showcasing components from the CSK vNext component starter kit. It provides detailed documentation, live previews, and testing capabilities for easy integration into your projects.", "main": "index.js", "scripts": { diff --git a/apps/csk/package.json b/apps/csk/package.json index 3f173bed1..fb2edb88b 100644 --- a/apps/csk/package.json +++ b/apps/csk/package.json @@ -1,6 +1,6 @@ { "name": "@uniformdev/component-starter-kit", - "version": "6.1.50", + "version": "6.1.51", "private": true, "engines": { "yarn": "please-use-npm", diff --git a/package-lock.json b/package-lock.json index b9f990f15..731c6fbab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "csk-packages", - "version": "6.1.50", + "version": "6.1.51", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "csk-packages", - "version": "6.1.50", + "version": "6.1.51", "workspaces": [ "apps/*", "packages/*" @@ -27,7 +27,7 @@ }, "apps/csk": { "name": "@uniformdev/component-starter-kit", - "version": "6.1.50", + "version": "6.1.51", "dependencies": { "@uniformdev/canvas-next-rsc-client-v2": "20.7.1-alpha.97", "@uniformdev/canvas-next-rsc-shared-v2": "20.7.1-alpha.97", @@ -69,7 +69,7 @@ }, "apps/csk-marketing-site": { "name": "@uniformdev/csk-marketing-site", - "version": "6.1.50", + "version": "6.1.51", "dependencies": { "@uniformdev/canvas-next-rsc-client-v2": "20.7.1-alpha.97", "@uniformdev/canvas-next-rsc-shared-v2": "20.7.1-alpha.97", @@ -143,7 +143,7 @@ }, "apps/csk-storybook": { "name": "@uniformdev/csk-storybook", - "version": "6.1.50", + "version": "6.1.51", "devDependencies": { "@chromatic-com/storybook": "^4.1.3", "@repo/eslint-config": "*", @@ -21792,7 +21792,7 @@ }, "packages/csk-cli": { "name": "@uniformdev/csk-cli", - "version": "6.1.50", + "version": "6.1.51", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@inquirer/prompts": "^7.1.0", @@ -21837,7 +21837,7 @@ }, "packages/csk-components": { "name": "@uniformdev/csk-components", - "version": "6.1.50", + "version": "6.1.51", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@inquirer/prompts": "^7.1.0", @@ -21901,7 +21901,7 @@ }, "packages/csk-recipes": { "name": "@uniformdev/csk-recipes", - "version": "6.1.50", + "version": "6.1.51", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@inquirer/prompts": "^7.1.0", @@ -21947,7 +21947,7 @@ }, "packages/design-extensions-tools": { "name": "@uniformdev/design-extensions-tools", - "version": "6.1.50", + "version": "6.1.51", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@inquirer/prompts": "^7.1.0", @@ -21990,7 +21990,7 @@ }, "packages/eslint-config": { "name": "@repo/eslint-config", - "version": "6.1.50", + "version": "6.1.51", "devDependencies": { "@eslint/js": "^9.31.0", "@next/eslint-plugin-next": "^16.0.7", @@ -22024,12 +22024,12 @@ }, "packages/internal-scripts": { "name": "@repo/internal-scripts", - "version": "6.1.50", + "version": "6.1.51", "license": "ISC" }, "packages/typescript-config": { "name": "@repo/typescript-config", - "version": "6.1.50", + "version": "6.1.51", "license": "MIT" } } diff --git a/package.json b/package.json index 025901df8..a389b82df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csk-packages", - "version": "6.1.50", + "version": "6.1.51", "private": true, "scripts": { "build": "turbo build", diff --git a/packages/csk-cli/package.json b/packages/csk-cli/package.json index c28f8b9fe..ff161752b 100644 --- a/packages/csk-cli/package.json +++ b/packages/csk-cli/package.json @@ -1,6 +1,6 @@ { "name": "@uniformdev/csk-cli", - "version": "6.1.50", + "version": "6.1.51", "description": "Command-line interface (CLI) tool designed to streamline the development workflow within Uniform projects. It provides commands for pulling additional data and generating components based on Canvas data", "license": "SEE LICENSE IN LICENSE.txt", "engines": { diff --git a/packages/csk-components/package.json b/packages/csk-components/package.json index b50eee8e9..bd1eb994c 100644 --- a/packages/csk-components/package.json +++ b/packages/csk-components/package.json @@ -1,6 +1,6 @@ { "name": "@uniformdev/csk-components", - "version": "6.1.50", + "version": "6.1.51", "description": "Components Starter Kit that provides a set of basic components for building websites within a Uniform project", "license": "SEE LICENSE IN LICENSE.txt", "engines": { diff --git a/packages/csk-recipes/package.json b/packages/csk-recipes/package.json index 64eaf13ad..78409fda2 100644 --- a/packages/csk-recipes/package.json +++ b/packages/csk-recipes/package.json @@ -1,6 +1,6 @@ { "name": "@uniformdev/csk-recipes", - "version": "6.1.50", + "version": "6.1.51", "description": "command-line interface (CLI) and utility functions to help you work with recipes in a CSK project. It simplifies project initialization by allowing you to choose templates and include specific recipes", "license": "SEE LICENSE IN LICENSE.txt", "engines": { diff --git a/packages/csk-recipes/src/commands/init/types.ts b/packages/csk-recipes/src/commands/init/types.ts index af3f689a6..1843f9f2f 100644 --- a/packages/csk-recipes/src/commands/init/types.ts +++ b/packages/csk-recipes/src/commands/init/types.ts @@ -1,5 +1,11 @@ export type Template = 'baseline' | 'coffee-shop' | string; -export type Recipe = 'localization' | 'ga' | 'uniform-insights' | 'ai-assistant' | 'ai-assistant-localized' | 'cookie-consent'; +export type Recipe = + | 'localization' + | 'ga' + | 'uniform-insights' + | 'ai-assistant' + | 'ai-assistant-localized' + | 'cookie-consent'; export type EnvVariable = | 'UNIFORM_PROJECT_ID' diff --git a/packages/design-extensions-tools/package.json b/packages/design-extensions-tools/package.json index 8c1e9d161..40e500361 100644 --- a/packages/design-extensions-tools/package.json +++ b/packages/design-extensions-tools/package.json @@ -1,6 +1,6 @@ { "name": "@uniformdev/design-extensions-tools", - "version": "6.1.50", + "version": "6.1.51", "description": "Command-line interface (CLI) tool and a set of utilities for working with design extension integrations", "license": "SEE LICENSE IN LICENSE.txt", "engines": { diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 39474b030..7715d5053 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@repo/eslint-config", - "version": "6.1.50", + "version": "6.1.51", "type": "module", "private": true, "exports": { diff --git a/packages/internal-scripts/package.json b/packages/internal-scripts/package.json index 2a5a742a0..fcd9a8edf 100644 --- a/packages/internal-scripts/package.json +++ b/packages/internal-scripts/package.json @@ -1,6 +1,6 @@ { "name": "@repo/internal-scripts", - "version": "6.1.50", + "version": "6.1.51", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index 8711a63b0..f0ff65102 100644 --- a/packages/typescript-config/package.json +++ b/packages/typescript-config/package.json @@ -1,6 +1,6 @@ { "name": "@repo/typescript-config", - "version": "6.1.50", + "version": "6.1.51", "private": true, "license": "MIT", "publishConfig": { From 82cc869bd35d78c00d31b06905f7387113f54e91 Mon Sep 17 00:00:00 2001 From: trsergey Date: Fri, 5 Dec 2025 16:31:53 +0200 Subject: [PATCH 5/6] chore: add cookie consent component pattern --- .../content/full/component/cookieConsent.yaml | 10 +- apps/csk/content/full/component/page.yaml | 3 +- .../9ff29417-67bd-441f-b2ad-d5ac1fc096c6.yaml | 109 ++++++++++++++++++ .../canvas/CookieConsent/cookie-consent.tsx | 2 +- 4 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 apps/csk/content/full/componentPattern/9ff29417-67bd-441f-b2ad-d5ac1fc096c6.yaml diff --git a/apps/csk/content/full/component/cookieConsent.yaml b/apps/csk/content/full/component/cookieConsent.yaml index 040432776..afe242ca3 100644 --- a/apps/csk/content/full/component/cookieConsent.yaml +++ b/apps/csk/content/full/component/cookieConsent.yaml @@ -113,14 +113,16 @@ useTeamPermissions: true slots: - id: cookieConsentContent name: Cookie Consent Content - allowedComponents: [] - allowAllComponents: true + allowedComponents: + - richText + - text + allowAllComponents: false inheritAllowedComponents: false patternsInAllowedComponents: false titleParameter: displayName canBeComposition: false -created: '2025-12-03T14:42:40.816541+00:00' -updated: '2025-12-03T20:37:43.04187+00:00' +created: '2025-12-05T14:17:32.572562+00:00' +updated: '2025-12-05T14:21:22.817867+00:00' variants: - id: top name: Top diff --git a/apps/csk/content/full/component/page.yaml b/apps/csk/content/full/component/page.yaml index 018cb178b..bbe8bb5db 100644 --- a/apps/csk/content/full/component/page.yaml +++ b/apps/csk/content/full/component/page.yaml @@ -201,6 +201,7 @@ slots: allowedComponents: - footer - simpleFooter + - cookieConsent allowAllComponents: false inheritAllowedComponents: false patternsInAllowedComponents: false @@ -208,4 +209,4 @@ titleParameter: pageTitle thumbnailParameter: openGraphImage canBeComposition: true created: '2025-01-29T10:47:02.333016+00:00' -updated: '2025-10-09T10:53:48.289674+00:00' +updated: '2025-12-05T14:24:06.350518+00:00' diff --git a/apps/csk/content/full/componentPattern/9ff29417-67bd-441f-b2ad-d5ac1fc096c6.yaml b/apps/csk/content/full/componentPattern/9ff29417-67bd-441f-b2ad-d5ac1fc096c6.yaml new file mode 100644 index 000000000..edbdb327f --- /dev/null +++ b/apps/csk/content/full/componentPattern/9ff29417-67bd-441f-b2ad-d5ac1fc096c6.yaml @@ -0,0 +1,109 @@ +composition: + _name: Cookie Consent pattern + _id: 9ff29417-67bd-441f-b2ad-d5ac1fc096c6 + type: cookieConsent + variant: bottom + parameters: + displayName: + type: text + locales: + en: Cookie Consent + allowTextColor: + type: dex-color-palette-parameter + value: text-secondary + allowButtonText: + type: text + locales: + en: Accept + backgroundColor: + type: dex-color-palette-parameter + value: general-color-2 + allowButtonColor: + type: dex-color-palette-parameter + value: button-primary + declineTextColor: + type: dex-color-palette-parameter + value: text-secondary + declineButtonText: + type: text + locales: + en: Decline + declineButtonColor: + type: dex-color-palette-parameter + value: button-secondary + allowButtonHoverColor: + type: dex-color-palette-parameter + value: button-primary-hover + declineButtonHoverColor: + type: dex-color-palette-parameter + value: button-secondary-hover + slots: + cookieConsentContent: + - _id: e0d19c06-57ff-446a-8725-f18233ead8b9 + type: richText + parameters: + size: + type: dex-segmented-control-parameter + value: + mobile: base + tablet: base + desktop: base + text: + type: richText + locales: + en: + root: + type: root + format: '' + indent: 0 + version: 1 + children: + - type: paragraph + format: '' + indent: 0 + version: 1 + children: + - mode: normal + text: >- + We use necessary cookies to make our site work. We’d + like to set additional cookies to understand site + usage, make site improvements and to remember your + settings. We also use cookies set by other sites to + help deliver content from their services. View our + type: text + style: '' + detail: 0 + format: 0 + version: 1 + - link: + path: https://www.uniform.dev/cookie-policy + type: url + type: link + format: '' + indent: 0 + version: 1 + children: + - mode: normal + text: Cookie Notice + type: text + style: '' + detail: 0 + format: 0 + version: 1 + direction: ltr + direction: ltr + textStyle: '' + textFormat: 0 + direction: ltr + color: + type: dex-color-palette-parameter + value: text-secondary + _overridability: + hideLockedParameters: true + _overridability: + hideLockedParameters: true + _locales: + - en +created: '2025-12-05T14:18:15.038176+00:00' +modified: '2025-12-05T14:27:22.504678+00:00' +pattern: true diff --git a/packages/csk-components/src/components/canvas/CookieConsent/cookie-consent.tsx b/packages/csk-components/src/components/canvas/CookieConsent/cookie-consent.tsx index 0d5d93b5c..b328d7633 100644 --- a/packages/csk-components/src/components/canvas/CookieConsent/cookie-consent.tsx +++ b/packages/csk-components/src/components/canvas/CookieConsent/cookie-consent.tsx @@ -84,7 +84,7 @@ const CookieConsent: FC = ({ >
-
+
Date: Fri, 5 Dec 2025 17:37:26 +0200 Subject: [PATCH 6/6] chore: add cookie consent recipe support --- .../content/full/component/cookieConsent.yaml | 4 +- .../src/commands/init/code-changer.ts | 2 + .../csk-recipes/src/commands/init/index.ts | 51 ++++++++++++------- packages/csk-recipes/src/constants.ts | 33 +++++++++--- 4 files changed, 64 insertions(+), 26 deletions(-) diff --git a/apps/csk/content/full/component/cookieConsent.yaml b/apps/csk/content/full/component/cookieConsent.yaml index afe242ca3..cd9eb3bab 100644 --- a/apps/csk/content/full/component/cookieConsent.yaml +++ b/apps/csk/content/full/component/cookieConsent.yaml @@ -108,7 +108,7 @@ parameters: typeConfig: null categoryId: 096fd5ed-5e2a-4bfa-834b-fb805d1d1ce9 previewImageUrl: >- - https://res.cloudinary.com/uniform-demos/image/upload/csk-v-next/baseline/preview-images/banner-default.jpg + https://res.cloudinary.com/uniform-demos/image/upload/csk-v-next/baseline/preview-images/cookie-consent useTeamPermissions: true slots: - id: cookieConsentContent @@ -122,7 +122,7 @@ slots: titleParameter: displayName canBeComposition: false created: '2025-12-05T14:17:32.572562+00:00' -updated: '2025-12-05T14:21:22.817867+00:00' +updated: '2025-12-07T16:57:32.889175+00:00' variants: - id: top name: Top diff --git a/packages/csk-recipes/src/commands/init/code-changer.ts b/packages/csk-recipes/src/commands/init/code-changer.ts index 32c9bc7a5..cbcf9731f 100644 --- a/packages/csk-recipes/src/commands/init/code-changer.ts +++ b/packages/csk-recipes/src/commands/init/code-changer.ts @@ -72,12 +72,14 @@ export const proceedCodeChange = async (filePath: string, recipes: Recipe[], isM const isGAEnabled = recipes.includes('ga'); const isUniformInsightsEnabled = recipes.includes('uniform-insights'); const isAiAssistantEnabled = recipes.includes('ai-assistant'); + const isCookieConsentEnabled = recipes.includes('cookie-consent'); const transformedCode = new MetaScript(metaProgram).transform({ localization: isLocalizationEnabled, ga: isGAEnabled, uniformInsights: isUniformInsightsEnabled, aiAssistant: isAiAssistantEnabled, monorepo: isMonorepo, + cookieConsent: isCookieConsentEnabled, }); const fileExtension = path.extname(filePath); diff --git a/packages/csk-recipes/src/commands/init/index.ts b/packages/csk-recipes/src/commands/init/index.ts index 3e1d6fdde..f609b07d3 100644 --- a/packages/csk-recipes/src/commands/init/index.ts +++ b/packages/csk-recipes/src/commands/init/index.ts @@ -157,27 +157,28 @@ const init = async ({ isPushedToUniform = true; } } - const hasMissingEnv = missingEnvKeys.length > 0; - if (!hasMissingEnv && (!isNeedToPushCanvasData || isPushedToUniform)) { - const startPrompt = 'Would you like to start dev server now?'; - const buildAndStart = await confirm({ message: startPrompt }); - if (buildAndStart) { - runStartDevInteractive(); - return; - } - } - - const recipesNotes = recipes - .flatMap(recipe => RECIPE_SPECIFIC_NOTES[recipe as keyof typeof RECIPE_SPECIFIC_NOTES] || []) - .filter(Boolean); + const { importantRecipesNotes, nonImportantRecipesNotes } = recipes.reduce<{ + importantRecipesNotes: string[]; + nonImportantRecipesNotes: string[]; + }>( + (acc, recipe) => { + const noteObj = RECIPE_SPECIFIC_NOTES[recipe as keyof typeof RECIPE_SPECIFIC_NOTES]; + if (!noteObj || !noteObj.Notes) return acc; + if (noteObj.required) { + acc.importantRecipesNotes.push(...noteObj.Notes); + } else { + acc.nonImportantRecipesNotes.push(...noteObj.Notes); + } + return acc; + }, + { importantRecipesNotes: [], nonImportantRecipesNotes: [] } + ); const templatesNotes = TEMPLATE_SPECIFIC_NOTES[template as keyof typeof TEMPLATE_SPECIFIC_NOTES] || []; - - const notes = [...recipesNotes, ...templatesNotes]; - - const shouldShowNotes = notes.length > 0 && !isPushedToUniform; + const importantNotes = [...importantRecipesNotes, ...templatesNotes]; + const shouldShowNotes = importantNotes.length > 0 && !isPushedToUniform; if (hasMissingEnv || shouldShowNotes) { spinner.warn('⚠️ IMPORTANT NOTES BEFORE STARTING THE APPLICATION ⚠️'); @@ -189,7 +190,21 @@ const init = async ({ } if (shouldShowNotes) { - console.info(`⚠️ Additional Setup Required:\n${notes.join('\n')}\n`); + console.info(`⚠️ Additional Setup Required:\n${importantNotes.join('\n')}\n`); + } + } + + if (nonImportantRecipesNotes.length > 0) { + console.info(`\n⚠️ Additional Setup Required:\n${nonImportantRecipesNotes.join('\n')}\n`); + } + + if (!hasMissingEnv && (!isNeedToPushCanvasData || isPushedToUniform)) { + const startPrompt = 'Would you like to start dev server now?'; + const buildAndStart = await confirm({ message: startPrompt }); + + if (buildAndStart) { + runStartDevInteractive(); + return; } } diff --git a/packages/csk-recipes/src/constants.ts b/packages/csk-recipes/src/constants.ts index 81346d89d..591ea507e 100644 --- a/packages/csk-recipes/src/constants.ts +++ b/packages/csk-recipes/src/constants.ts @@ -80,6 +80,7 @@ export const REQUIRED_ENV_VARIABLES: { 'ai-assistant-localized': ['OPENAI_API_KEY', 'DATABASE_URL'], localization: [], ga: ['GOOGLE_ANALYTICS_ID'], + 'cookie-consent': [], }; export const ENV_VARIABLES_VARIANTS: Partial<{ @@ -106,22 +107,41 @@ export const REQUIRED_UNIFORM_ENV_VARIABLES: EnvVariable[] = [ ]; export const RECIPE_SPECIFIC_NOTES = { - 'ai-assistant': [ - `🔧 Please create a Data Source with the following settings: + 'ai-assistant': { + Notes: [ + `🔧 Please create a Data Source with the following settings: • Type: HTTP Request • Base URL: ${process.env.UNIFORM_CLI_BASE_URL || ENV_VARIABLES_DEFAULT_VALUES.UNIFORM_CLI_BASE_URL || 'https://uniform.app'} • Query Param: 'projectId' — set this to your Uniform project's ID • Header: 'X-Api-Key' — set this to your Uniform API key • Public ID: 'uniformApp'`, - ], - 'ai-assistant-localized': [ - `🔧 Please create a Data Source with the following settings: + ], + required: true, + }, + 'ai-assistant-localized': { + Notes: [ + `🔧 Please create a Data Source with the following settings: • Type: HTTP Request • Base URL: ${process.env.UNIFORM_CLI_BASE_URL || ENV_VARIABLES_DEFAULT_VALUES.UNIFORM_CLI_BASE_URL || 'https://uniform.app'} • Query Param: 'projectId' — set this to your Uniform project's ID • Header: 'X-Api-Key' — set this to your Uniform API key • Public ID: 'uniformApp'`, - ], + ], + required: true, + }, + 'cookie-consent': { + Notes: [ + `🔧 To complete the cookie consent setup, you need to add the cookie consent banner to your website.: + ${ + process.env.UNIFORM_PROJECT_ID + ? `• Visit Global Page Template using this link: https://uniform.app/projects/${process.env.UNIFORM_PROJECT_ID}/dashboards/canvas/edit/f9c058ea-c40d-4435-ac5a-53423cf654dc + add the Cookie Consent pattern to the page footer section` + : '• Visit Global Page Template of your project add the Cookie Consent pattern to the page footer section.' + } + The banner is automatically presented in the vercel deployment for users located in GDPR-compliant regions`, + ], + required: false, + }, }; export const TEMPLATE_SPECIFIC_NOTES = { @@ -179,6 +199,7 @@ const DataSource = { export const REQUIRED_DATA_SOURCES = { 'ai-assistant': [DataSource.UniformApp], 'ai-assistant-localized': [DataSource.UniformApp], + 'cookie-consent': [], }; export const TEMPLATE_SPECIFIC_DATA_SOURCES = {