Repository navigation
Add "Customize cookie settings" button and preferences panel - #13
Open
Castellon-ACM wants to merge 4 commits into
Open
Castellon-ACM wants to merge 4 commits into
Castellon-ACM wants to merge 4 commits into
Conversation
Adds a real <button> to the left of Reject/Accept, showing the existing cookie icon, printed through the existing frcn_cookie_notice_before_actions action so a PRO add-on can hook the same extension point. Clicking it (or the persistent reopen trigger) opens a modal preferences panel (role="dialog", aria-modal="true") with a static "Strictly necessary" section and Accept all / Reject all / Save changes actions, all funneled through the exact same client-side decision-recording function the banner's own Accept/Reject buttons already use, and the same AJAX log_consent_callback() server-side. PRO-ready extension points, per the issue: - New frcn_cookie_preferences_categories action, fired inside the panel where a PRO add-on renders its own per-category toggles. - New frcn_cookie_consent_categories filter, applied around the optional per-category map in log_consent_callback()'s response — purely additive, the binary accepted/rejected cookie format is unchanged. - New frcn_cookie_customize_button_label / frcn_cookie_customize_button_enabled filters for the button's copy and visibility. The panel is printed unconditionally (including on the policy page) and hidden by default via the `hidden` attribute, keeping the output cache-neutral like the rest of the banner. Without JS the button is hidden via the existing <noscript> fallback; Accept/Reject keep working through the no-JS <form> fallback as before. Also updates the reopen trigger to open this same panel instead of clearing the consent cookie and reloading the page — now that the panel renders even on the policy page, there's no need to navigate away to reach it. Documented in docs/preferences-panel-hooks.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…rketing/frontconsent into feature/customize-preferences-panel # Conflicts: # assets/cookie-notice/frontconsent-cookie-notice.css # assets/cookie-notice/frontconsent-cookie-notice.min.css
…ions The panel had no visible feedback: Accept all/Reject all/Save changes all recorded a decision, but the panel's own markup looked identical regardless of what was clicked, since the only category shown was the static, always-on "Strictly necessary" block. A visitor had no way to tell whether their action had any effect. Add a real "Analytics & Marketing" checkbox (data-frcn-category="optional") after the necessary block. frontconsent-cookie-notice.js already had the plumbing to read panel toggles on Save changes/Accept all (collectPreferencesCategories()) — it only lacked an actual toggle to read. Added the missing half: syncPreferencesToggles(), called every time the panel opens, reflects the checkbox from the real consent cookie (accepted/rejected), which stays the single source of truth — the toggle is presentation, not a second stored state. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Analytics and Marketing gated the exact same binary accepted/rejected
decision despite showing as separate concepts ("analytics y marketing
son diferentes"). Replace the single "Analytics & Marketing" checkbox
with two independent ones (data-frcn-category="analytics"/"marketing")
and make the split functionally meaningful end to end:
- frontconsent-cookie-notice.js persists the visitor's actual
per-category selection into a new frontconsent_categories cookie
(JSON, e.g. {"analytics":true,"marketing":false}) alongside the
existing binary consent cookie, and syncs each toggle's checked
state from it independently when the panel opens.
- CookieNotice::get_config_callback() reads that cookie server-side
and builds a real allowedCategories map (still filterable via
frcn_cookie_notice_allowed_tracking_categories for PRO), instead of
always returning null. frcnCookieNoticeInject() already gated GTM/
GA4 ('analytics') separately from Clientify/Brevo/OpenAI ads
('marketing') on this map — it just needed a real value to gate on.
- Backward compatible: a visitor who accepted before this cookie
existed has no frontconsent_categories cookie yet; both categories
default to allowed for them (matching the previous allow-all
behavior) until they make an explicit per-category choice.
Updates existing PHPUnit/JS tests for the new markup and cookie logic,
and adds coverage for the malformed-cookie fallback and the backward-
compat default.
Co-Authored-By: Claude Sonnet 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
Closes #11.
Implements the "Customize cookie settings" button and cookie preferences panel described in the issue, built on top of PR #12 (
fix/reopen-trigger-and-link-a11y, not yet merged) since both touch the exact same banner markup/JS (the icon-only reopen trigger and policy-page-title link PR #12 just added). This PR's base branch isfix/reopen-trigger-and-link-a11y, notmain— once #12 merges, this should be re-based/re-targeted atmain.Acceptance criteria (from the issue)
bar,boxandpopuplayouts, on desktop and mobile. Printed through the existingfrcn_cookie_notice_before_actionsaction; reuses the same CSS-mask icon technique as.frcn-cookie-notice__icon.#frcn-cookie-preferencesdialog and restore focus to whichever one opened it.handleDecision()in JS and the samelog_consent_callback()AJAX endpoint server-side. No parallel consent logic.hiddenattribute, identical HTML for every visitor — open/closed state is entirely client-side.<noscript>fallback (print_noscript_style()); Accept/Reject keep working via the no-JS<form>fallback.docs/preferences-panel-hooks.md:frcn_cookie_notice_before_actions(existing, reused for the button)frcn_cookie_preferences_categories(new action, fires inside the panel)frcn_cookie_consent_categories(new filter, applied around the per-category payload inlog_consent_callback())frcn_cookie_customize_button_label/frcn_cookie_customize_button_enabled(new filters)frcn_cookie_preferences_categories), no category UI is rendered — verified intest_no_category_ui_is_rendered_without_an_add_on. With a test add-on hooked in (seetest_preferences_categories_action_fires_in_the_right_placeandCookieNoticeConsentCategoriesTest), categories show up and are read back through the filter.frontconsent) and in English; button/panel are keyboard and screen-reader friendly (dialog ARIA, focus trap, live-region announcements reusing the existing announcer).composer lintandcomposer phpstanpass;readme.txt/readme.mdupdated.What was built
includes/Frontend/CookieNotice.php):render_customize_button()(hooked onfrcn_cookie_notice_before_actions),render_preferences_panel()(called fromrender_banner()next to the reopen trigger, unconditionally), the new action/filters above, andget_consent_categories_payload()wiring the categories filter intolog_consent_callback()'s JSON response.#frcn-cookie-preferences.handleDecision,setConsentCookie,updateConsentMode,logDecision,dispatchConsentEvent) out from under the "only if banner exists" gate so it also works on the policy page; added the preferences panel's own focus trap/Escape handling (guarding the banner's own popup trap so only one dialog reacts to Escape/Tab at a time); changed the reopen trigger to open this panel instead of clearing the cookie and reloading.docs/preferences-panel-hooks.mddocuments every hook/filter, when it fires, and what data is available, plus a minimal PRO-style example add-on.tests/Unit/CookieNoticePreferencesPanelTest.php(button/panel markup, ARIA, the categories action firing, the label/enabled filters, no-JS hiding),tests/Unit/CookieNoticeConsentCategoriesTest.php(the categories filter applied around the AJAX payload), plus updates toCookieNoticeAccessibilityTest.php(scoped an existing assertion now that the panel's own dialog markup is always present) and bothtests/js/*.test.jsfiles covering the new panel's open/close/focus-trap/decision-routing behavior.Test results
composer test(PHPUnit): 96 tests, 187 assertions, all passing — ran locally against a throwaway MySQL 8.0 instance (Local by Flywheel's bundled binaries, scratch datadir, non-default port), torn down afterward.composer lint(phpcs): clean.composer phpstan: clean, no errors.npm run test:js(node --test): 39 tests, all passing.Not done / out of scope
🤖 Generated with Claude Code