summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/stupid-peas-juggle.md10
-rw-r--r--.changeset/three-owls-drop.md10
-rw-r--r--packages/astro/e2e/custom-client-directives.test.js14
-rw-r--r--packages/astro/e2e/fixtures/custom-client-directives/astro.config.mjs18
-rw-r--r--packages/astro/e2e/fixtures/custom-client-directives/client-options.js10
-rw-r--r--packages/astro/e2e/fixtures/custom-client-directives/src/client-directives-types.d.ts5
-rw-r--r--packages/astro/e2e/fixtures/custom-client-directives/src/pages/index.astro3
-rw-r--r--packages/astro/src/@types/astro.ts8
-rw-r--r--packages/astro/src/runtime/client/visible.ts9
9 files changed, 65 insertions, 22 deletions
diff --git a/.changeset/stupid-peas-juggle.md b/.changeset/stupid-peas-juggle.md
deleted file mode 100644
index 1e01c0996..000000000
--- 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
-<!-- Load component when it's within 200px away from entering the viewport -->
-<Component client:visible="200px" />
-```
diff --git a/.changeset/three-owls-drop.md b/.changeset/three-owls-drop.md
new file mode 100644
index 000000000..f6a5f90e9
--- /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
+<!-- Load component when it's within 200px away from entering the viewport -->
+<Component client:visible={{ rootMargin: "200px" }} />
+```
diff --git a/packages/astro/e2e/custom-client-directives.test.js b/packages/astro/e2e/custom-client-directives.test.js
index fec5ef9a1..118a5d53f 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 3790d21b7..ae5514771 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 000000000..70320cf81
--- /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 07399f7bb..6fb3c614d 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 05c28b109..b03042d44 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';
<body>
<Counter id="client-click" client:click>client:click</Counter>
<Counter id="client-password" client:password="hunter2">client:password</Counter>
+ <Counter id="client-has-options" client:options={{message: "Hello! I was passed as an option"}}>client:options</Counter>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index 74cbb4694..08226ff5e 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<IntersectionObserverInit, 'rootMargin'>;
+
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 9e625ca23..9be4d9b31 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) => {