mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
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 769826edbd
.
* feat: update extended `client:visible` to use an object instead of a string
* Apply suggestions from code review
Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
* test: add a test
* nit: comment
* test: write the test some other way to try to convince playwright
---------
Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
This commit is contained in:
parent
da307e4a08
commit
fbc2697653
9 changed files with 65 additions and 22 deletions
|
@ -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" />
|
||||
```
|
10
.changeset/three-owls-drop.md
Normal file
10
.changeset/three-owls-drop.md
Normal file
|
@ -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" }} />
|
||||
```
|
|
@ -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"}'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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 { }
|
||||
|
|
|
@ -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>
|
||||
</html>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) => {
|
||||
|
|
Loading…
Reference in a new issue