From fbc26976533bbcf2de9d6dba1aa3ea3dc6ce0853 Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Wed, 3 Jan 2024 19:44:04 -0500 Subject: [PATCH] feat: update extended client:visible to use an object instead of a string (#9596) * Revert "feat: support setting rootMargin for `client:visible` (#9363)" This reverts commit 769826edbd9ee2144e9272656dfbe6155b24d67d. * feat: update extended `client:visible` to use an object instead of a string * Apply suggestions from code review Co-authored-by: Nate Moore * test: add a test * nit: comment * test: write the test some other way to try to convince playwright --------- Co-authored-by: Nate Moore --- .changeset/stupid-peas-juggle.md | 10 ---------- .changeset/three-owls-drop.md | 10 ++++++++++ .../astro/e2e/custom-client-directives.test.js | 14 +++++++++++++- .../custom-client-directives/astro.config.mjs | 18 ++++++++++++++++-- .../custom-client-directives/client-options.js | 10 ++++++++++ .../src/client-directives-types.d.ts | 5 +++-- .../src/pages/index.astro | 3 ++- packages/astro/src/@types/astro.ts | 8 +++++--- packages/astro/src/runtime/client/visible.ts | 9 ++++++--- 9 files changed, 65 insertions(+), 22 deletions(-) delete mode 100644 .changeset/stupid-peas-juggle.md create mode 100644 .changeset/three-owls-drop.md create mode 100644 packages/astro/e2e/fixtures/custom-client-directives/client-options.js diff --git a/.changeset/stupid-peas-juggle.md b/.changeset/stupid-peas-juggle.md deleted file mode 100644 index 1e01c09966..0000000000 --- a/.changeset/stupid-peas-juggle.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'astro': minor ---- - -Extends the `client:visible` directive by adding an optional `rootMargin` property. This allows a component to be hydrated when it is close to the viewport instead of waiting for it to become visible. - -```html - - -``` diff --git a/.changeset/three-owls-drop.md b/.changeset/three-owls-drop.md new file mode 100644 index 0000000000..f6a5f90e9b --- /dev/null +++ b/.changeset/three-owls-drop.md @@ -0,0 +1,10 @@ +--- +"astro": minor +--- + +Adds the ability to set a [`rootMargin`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin) setting when using the `client:visible` directive. This allows a component to be hydrated when it is _near_ the viewport, rather than hydrated when it has _entered_ the viewport. + +```astro + + +``` diff --git a/packages/astro/e2e/custom-client-directives.test.js b/packages/astro/e2e/custom-client-directives.test.js index fec5ef9a11..118a5d53f5 100644 --- a/packages/astro/e2e/custom-client-directives.test.js +++ b/packages/astro/e2e/custom-client-directives.test.js @@ -1,6 +1,6 @@ import { expect } from '@playwright/test'; -import { testFactory, waitForHydrate } from './test-utils.js'; import testAdapter from '../test/test-adapter.js'; +import { testFactory, waitForHydrate } from './test-utils.js'; const test = testFactory({ root: './fixtures/custom-client-directives/', @@ -89,4 +89,16 @@ function testClientDirectivesShared() { // Hydrated, this should be 1 await expect(counterValue).toHaveText('1'); }); + + test('Client directives should be passed options correctly', async ({ astro, page }) => { + await page.goto(astro.resolveUrl('/')); + + const optionsContent = page.locator('#client-has-options pre'); + await waitForHydrate(page, optionsContent); + + const clientOptions = page.locator('#options'); + await expect(clientOptions).toHaveText( + 'Passed options are: {"message":"Hello! I was passed as an option"}' + ); + }); } diff --git a/packages/astro/e2e/fixtures/custom-client-directives/astro.config.mjs b/packages/astro/e2e/fixtures/custom-client-directives/astro.config.mjs index 3790d21b79..ae55147712 100644 --- a/packages/astro/e2e/fixtures/custom-client-directives/astro.config.mjs +++ b/packages/astro/e2e/fixtures/custom-client-directives/astro.config.mjs @@ -1,9 +1,9 @@ -import { defineConfig } from 'astro/config'; import react from "@astrojs/react"; +import { defineConfig } from 'astro/config'; import { fileURLToPath } from 'node:url'; export default defineConfig({ - integrations: [astroClientClickDirective(), astroClientPasswordDirective(), react()], + integrations: [astroClientClickDirective(), astroClientPasswordDirective(), astroHasOptionsDirective(), react()], }); function astroClientClickDirective() { @@ -33,3 +33,17 @@ function astroClientPasswordDirective() { } }; } + +function astroHasOptionsDirective() { + return { + name: 'astro-options', + hooks: { + 'astro:config:setup': (opts) => { + opts.addClientDirective({ + name: 'options', + entrypoint: fileURLToPath(new URL('./client-options.js', import.meta.url)) + }); + } + } + }; +} diff --git a/packages/astro/e2e/fixtures/custom-client-directives/client-options.js b/packages/astro/e2e/fixtures/custom-client-directives/client-options.js new file mode 100644 index 0000000000..70320cf818 --- /dev/null +++ b/packages/astro/e2e/fixtures/custom-client-directives/client-options.js @@ -0,0 +1,10 @@ +// Hydrate directly and write the passed options to the DOM +export default async (load, options) => { + const hydrate = await load(); + + const div = document.createElement('div'); + div.id = 'options'; + div.textContent = `Passed options are: ${JSON.stringify(options.value)}`; + document.body.appendChild(div); + await hydrate(); +} diff --git a/packages/astro/e2e/fixtures/custom-client-directives/src/client-directives-types.d.ts b/packages/astro/e2e/fixtures/custom-client-directives/src/client-directives-types.d.ts index 07399f7bb0..6fb3c614d4 100644 --- a/packages/astro/e2e/fixtures/custom-client-directives/src/client-directives-types.d.ts +++ b/packages/astro/e2e/fixtures/custom-client-directives/src/client-directives-types.d.ts @@ -1,9 +1,10 @@ declare module 'astro' { interface AstroClientDirectives { 'client:click'?: boolean - 'client:password'?: string + 'client:password'?: string + 'client:options'?: { message: string } } } // Make d.ts a module to similate common packaging setups where the entry `index.d.ts` would augment the types -export {} +export { } diff --git a/packages/astro/e2e/fixtures/custom-client-directives/src/pages/index.astro b/packages/astro/e2e/fixtures/custom-client-directives/src/pages/index.astro index 05c28b109e..b03042d446 100644 --- a/packages/astro/e2e/fixtures/custom-client-directives/src/pages/index.astro +++ b/packages/astro/e2e/fixtures/custom-client-directives/src/pages/index.astro @@ -6,5 +6,6 @@ import Counter from '../components/Counter.jsx'; client:click client:password + client:options - \ No newline at end of file + diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 74cbb4694d..08226ff5e0 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -65,19 +65,21 @@ export type { export type { RemotePattern } from '../assets/utils/remotePattern.js'; export type { SSRManifest } from '../core/app/types.js'; export type { - AstroCookies, - AstroCookieSetOptions, AstroCookieGetOptions, + AstroCookieSetOptions, + AstroCookies, } from '../core/cookies/index.js'; export interface AstroBuiltinProps { 'client:load'?: boolean; 'client:idle'?: boolean; 'client:media'?: string; - 'client:visible'?: string | boolean; + 'client:visible'?: ClientVisibleOptions | boolean; 'client:only'?: boolean | string; } +export type ClientVisibleOptions = Pick; + export interface TransitionAnimation { name: string; // The name of the keyframe delay?: number | string; diff --git a/packages/astro/src/runtime/client/visible.ts b/packages/astro/src/runtime/client/visible.ts index 9e625ca23d..9be4d9b318 100644 --- a/packages/astro/src/runtime/client/visible.ts +++ b/packages/astro/src/runtime/client/visible.ts @@ -1,4 +1,4 @@ -import type { ClientDirective } from '../../@types/astro.js'; +import type { ClientDirective, ClientVisibleOptions } from '../../@types/astro.js'; /** * Hydrate this component when one of it's children becomes visible @@ -11,8 +11,11 @@ const visibleDirective: ClientDirective = (load, options, el) => { await hydrate(); }; - const ioOptions = { - rootMargin: typeof options.value === 'string' ? options.value : undefined, + const rawOptions = + typeof options.value === 'object' ? (options.value as ClientVisibleOptions) : undefined; + + const ioOptions: IntersectionObserverInit = { + rootMargin: rawOptions?.rootMargin, }; const io = new IntersectionObserver((entries) => {