diff --git a/packages/nuxt/src/app/composables/cookie.ts b/packages/nuxt/src/app/composables/cookie.ts index fa93422de9..47a9299673 100644 --- a/packages/nuxt/src/app/composables/cookie.ts +++ b/packages/nuxt/src/app/composables/cookie.ts @@ -103,9 +103,18 @@ export function useCookie (name: string, _opts?: } if (store) { + /* event is of type CookieChangeEvent */ const changeHandler = (event: any) => { - const cookie = event.changed.find((c: any) => c.name === name) - if (cookie) { handleChange({ value: cookie.value }) } + const changedCookie = event.changed.find((c: any) => c.name === name) + const removedCookie = event.deleted.find((c: any) => c.name === name) + + if (changedCookie) { + handleChange({ value: changedCookie.value }) + } + + if (removedCookie) { + handleChange({ value: null }) + } } store.addEventListener('change', changeHandler) if (hasScope) { diff --git a/test/basic.test.ts b/test/basic.test.ts index 0e9b2eb2dc..9cc2d823b7 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -638,7 +638,7 @@ describe('nuxt composables', () => { }, }) const cookies = res.headers.get('set-cookie') - expect(cookies).toMatchInlineSnapshot('"set-in-plugin=true; Path=/, accessed-with-default-value=default; Path=/, set=set; Path=/, browser-set=set; Path=/, browser-set-to-null=; Max-Age=0; Path=/, browser-set-to-null-with-default=; Max-Age=0; Path=/, browser-object-default=%7B%22foo%22%3A%22bar%22%7D; Path=/"') + expect(cookies).toMatchInlineSnapshot('"set-in-plugin=true; Path=/, accessed-with-default-value=default; Path=/, set=set; Path=/, browser-set=set; Path=/, browser-set-to-null=; Max-Age=0; Path=/, browser-set-to-null-with-default=; Max-Age=0; Path=/, browser-object-default=%7B%22foo%22%3A%22bar%22%7D; Path=/, theCookie=show; Path=/"') }) it('updates cookies when they are changed', async () => { const { page } = await renderPage('/cookies') @@ -661,6 +661,26 @@ describe('nuxt composables', () => { await page.close() }) + it('sets cookies in composable to null in all components', async () => { + const { page } = await renderPage('/cookies') + const parentBannerText = await page.locator('#parent-banner').textContent() + expect(parentBannerText).toContain('parent banner') + + const childBannerText = await page.locator('#child-banner').innerText() + expect(childBannerText).toContain('child banner') + + // Clear the composable cookie + await page.getByText('Toggle cookie banner').click() + await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 10))) + + const parentBannerAfterToggle = await page.locator('#parent-banner').isVisible() + expect(parentBannerAfterToggle).toBe(false) + + const childBannerAfterToggle = await page.locator('#child-banner').isVisible() + expect(childBannerAfterToggle).toBe(false) + await page.close() + }) + it('supports onPrehydrate', async () => { const html = await $fetch('/composables/on-prehydrate') as string /** diff --git a/test/fixtures/basic/components/ComponentUsingCookie.vue b/test/fixtures/basic/components/ComponentUsingCookie.vue new file mode 100644 index 0000000000..352a23237b --- /dev/null +++ b/test/fixtures/basic/components/ComponentUsingCookie.vue @@ -0,0 +1,14 @@ + + + diff --git a/test/fixtures/basic/composables/cookie-manager.ts b/test/fixtures/basic/composables/cookie-manager.ts new file mode 100644 index 0000000000..d728fd1054 --- /dev/null +++ b/test/fixtures/basic/composables/cookie-manager.ts @@ -0,0 +1,18 @@ +export function useCookieManager () { + const theCookie = useCookie('theCookie', { + default: () => 'show', + }) + + const showCookieBanner = computed(() => { + return theCookie.value === 'show' + }) + + function toggle () { + theCookie.value = theCookie.value === 'show' ? null : 'show' + } + + return { + showCookieBanner, + toggle, + } +} diff --git a/test/fixtures/basic/pages/cookies.vue b/test/fixtures/basic/pages/cookies.vue index b47ba29102..22a34504cb 100644 --- a/test/fixtures/basic/pages/cookies.vue +++ b/test/fixtures/basic/pages/cookies.vue @@ -1,4 +1,7 @@