feat: cookie-consent as the first engine plugin (new 'plugin' canonical) - #9
Merged
Conversation
…labels - Added `info.icon` to every engine component JSON to improve the admin "Pick one component" dialog, replacing the generic grid fallback with specific icons for each block. - Introduced a new admin bundle (`./strapi-admin`) to register human-readable category labels for the component picker, resolving titles through react-intl. - Implemented placement-scoped adopter categories for custom blocks, allowing specific components to be admitted into designated Dynamic Zones. - Seeded a "Privacy Policy" page template at bootstrap, ensuring it is created only once and respects existing pages on the same slug. - Updated various component JSON files to include icons for better visual representation in the admin interface.
Introduces the engine plugin family and ships cookie consent as plugin #1. press-web (major): - Entity gains 'plugin' (additive); new PressPlugin<Id> contract with a synthetic urn:plugin:{id} identity, mirroring urn:site-setting:default - ResolvedPressConfig gains the REQUIRED plugins named map (plugins.cookieConsent) — resolved TOTAL even when the CMS is unreachable - mapCookieConsent fails OPEN (banner enabled + default copy on CMS outage); hasConsent stays independently fail-closed (no decision => false) - CookieConsentBanner (first stateful client component), useConsent hook, consent store over a versioned first-party cookie (press_consent, 180d), and buildConsentBootstrapScript() for pre-paint anti-flash — the visitor's decision is never read via cookies() in the RSC tree (static/ISR intact) - Closed consent categories (necessary | analytics | marketing): editors toggle and re-word, never rename keys press-cms (minor): - press.cookie-consent + press.cookie-category config components (injected, never DZ-admitted), cookieConsent attribute on Site Settings, deep populate (nested categories + privacy page slug) - seedCookieConsent: run-once (cookieConsentSeeded flag), booleans only — default copy lives web-side; editor disable respected forever; the flag is not set when the record is missing so a broken bootstrap order self-heals Tests: 8 interactive banner tests via a minimal act()+createRoot jsdom harness — deliberately not @testing-library/react, whose only materialized copy under the Strapi-required node-linker=hoisted layout is the react-19 variant and cannot render this package's react-18 elements. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ships cookie consent (LGPD/GDPR) for press sites as the first member of the engine's plugin family, introducing the
'plugin'canonical entity.The plugin family (press-web, major)
Entitygains'plugin'(additive) and the newPressPlugin<Id>contract (extends Canonical<'plugin'>, syntheticurn:plugin:{id},enabledflag)ResolvedPressConfig.plugins.<key>→ explicit mount in the host layout. A second plugin (consent-gated scripts) costs exactly what the first didResolvedPressConfiggains a REQUIREDpluginsfield — hand-constructed literals (fixtures/mocks) needplugins: { cookieConsent: ... }; runtime is additive viagetSiteConfigCookie consent
press_consent, SameSite=Lax, 180d)hasConsent('analytics')(fail-closed),useConsent(),acceptAll/rejectAll/setConsent/resetConsent,parseConsentCookie(server-side seam for future script gating)<head>script stamps<html data-press-consent="decided">so a decided visitor never sees the banner; never readscookies()in the RSC tree, so static/ISR stays intacthasConsentstays independently fail-closedpress.cookie-consent/press.cookie-categoryconfig components, Site Settings attribute + deep populate, run-once seed writing only theenabledbooleans (default copy lives web-side; editor disable respected forever)Design decisions in
docs/superpowers/specs/2026-07-04-cookie-consent-design.md(cited ascookie-consent Spec §N); CLAUDE.md gained the living "Engine plugins + cookie consent" section.Test plan
pnpm -r --if-present typecheck— all packages greenpnpm -r test— 294 tests green (cms 63, web 210 incl. 8 interactive jsdom banner tests, cli 21)pnpm build && pnpm dev→ check the Cookie Consent component in the admin, banner on first visit, persistence across reloads, pre-paint hide after decidingNotes
act()+createRootjsdom harness instead of the approved@testing-library/react: under the Strapi-requirednode-linker=hoistedlayout, the only materialized RTL copy is Strapi-admin's react-19 variant, incompatible with this package's react 18 (documented in CLAUDE.md)resetConsent()is the seam) and the script plugin (GTM/GA) as plugin Spec 5 deploy guide #2🤖 Generated with Claude Code