From a5d384c1f63908a32ef00e2d78cc2923aab4fd55 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Wed, 8 May 2024 21:04:05 -0700 Subject: [PATCH] docs: add section explaining scrolling (#30719) Fixes #30643. --- docs/src/api/class-elementhandle.md | 2 + docs/src/api/class-locator.md | 2 + docs/src/api/class-mouse.md | 2 +- docs/src/input.md | 103 ++++++++++++++++++++++ packages/playwright-core/types/types.d.ts | 7 +- utils/build/build.js | 1 - 6 files changed, 114 insertions(+), 3 deletions(-) diff --git a/docs/src/api/class-elementhandle.md b/docs/src/api/class-elementhandle.md index df3bd3a164..dbf99eb3a2 100644 --- a/docs/src/api/class-elementhandle.md +++ b/docs/src/api/class-elementhandle.md @@ -789,6 +789,8 @@ completely visible as defined by Throws when `elementHandle` does not point to an element [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot. +See [scrolling](../input.md#scrolling) for alternative ways to scroll. + ### option: ElementHandle.scrollIntoViewIfNeeded.timeout = %%-input-timeout-%% * since: v1.8 diff --git a/docs/src/api/class-locator.md b/docs/src/api/class-locator.md index 3213a9eb06..af7f8754c7 100644 --- a/docs/src/api/class-locator.md +++ b/docs/src/api/class-locator.md @@ -1972,6 +1972,8 @@ This method waits for [actionability](../actionability.md) checks, then tries to completely visible as defined by [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)'s `ratio`. +See [scrolling](../input.md#scrolling) for alternative ways to scroll. + ### option: Locator.scrollIntoViewIfNeeded.timeout = %%-input-timeout-%% * since: v1.14 diff --git a/docs/src/api/class-mouse.md b/docs/src/api/class-mouse.md index 2234eac3f9..78319d9b93 100644 --- a/docs/src/api/class-mouse.md +++ b/docs/src/api/class-mouse.md @@ -147,7 +147,7 @@ Dispatches a `mouseup` event. ## async method: Mouse.wheel * since: v1.15 -Dispatches a `wheel` event. +Dispatches a `wheel` event. This method is usually used to manually scroll the page. See [scrolling](../input.md#scrolling) for alternative ways to scroll. :::note Wheel events may cause scrolling if they are not handled, and this method does not diff --git a/docs/src/input.md b/docs/src/input.md index 7f14d27ec8..1baf49e33d 100644 --- a/docs/src/input.md +++ b/docs/src/input.md @@ -757,3 +757,106 @@ await page.Mouse.UpAsync(); :::note If your page relies on the `dragover` event being dispatched, you need at least two mouse moves to trigger it in all browsers. To reliably issue the second mouse move, repeat your [`method: Mouse.move`] or [`method: Locator.hover`] twice. The sequence of operations would be: hover the drag element, mouse down, hover the drop element, hover the drop element second time, mouse up. ::: + +## Scrolling + +Most of the time, Playwright will automatically scroll for you before doing any actions. Therefore, you do not need to scroll explicitly. + +```js +// Scrolls automatically so that button is visible +await page.getByRole('button').click(); +``` + +```java +// Scrolls automatically so that button is visible +page.getByRole(AriaRole.BUTTON).click(); +``` + +```python async +# Scrolls automatically so that button is visible +await page.get_by_role("button").click() +``` + +```python sync +# Scrolls automatically so that button is visible +page.get_by_role("button").click() +``` + +```csharp +// Scrolls automatically so that button is visible +await page.GetByRole(AriaRole.Button).ClickAsync(); +``` + +However, in rare cases you might need to manually scroll. For example, you might want to force an "infinite list" to load more elements, or position the page for a specific screenshot. In such a case, the most reliable way is to find an element that you want to make visible at the bottom, and scroll it into view. + +```js +// Scroll the footer into view, forcing an "infinite list" to load more content +await page.getByText('Footer text').scrollIntoViewIfNeeded(); +``` + +```java +// Scroll the footer into view, forcing an "infinite list" to load more content +page.getByText("Footer text").scrollIntoViewIfNeeded(); +``` + +```python async +# Scroll the footer into view, forcing an "infinite list" to load more content +await page.get_by_text("Footer text").scroll_into_view_if_needed() +``` + +```python sync +# Scroll the footer into view, forcing an "infinite list" to load more content +page.get_by_text("Footer text").scroll_into_view_if_needed() +``` + +```csharp +// Scroll the footer into view, forcing an "infinite list" to load more content +await page.GetByText("Footer text").ScrollIntoViewIfNeededAsync(); +``` + +If you would like to control the scrolling more precisely, use [`method: Mouse.wheel`] or [`method: Locator.evaluate`]: + +```js +// Position the mouse and scroll with the mouse wheel +await page.getByTestId('scrolling-container').hover(); +await page.mouse.wheel(0, 10); + +// Alternatively, programmatically scroll a specific element +await page.getByTestId('scrolling-container').evaluate(e => e.scrollTop += 100); +``` + +```java +// Position the mouse and scroll with the mouse wheel +page.getByTestId("scrolling-container").hover(); +page.mouse.wheel(0, 10); + +// Alternatively, programmatically scroll a specific element +page.getByTestId("scrolling-container").evaluate("e => e.scrollTop += 100"); +``` + +```python async +# Position the mouse and scroll with the mouse wheel +await page.get_by_test_id("scrolling-container").hover() +await page.mouse.wheel(0, 10) + +# Alternatively, programmatically scroll a specific element +await page.get_by_test_id("scrolling-container").evaluate("e => e.scrollTop += 100") +``` + +```python sync +# Position the mouse and scroll with the mouse wheel +page.get_by_test_id("scrolling-container").hover() +page.mouse.wheel(0, 10) + +# Alternatively, programmatically scroll a specific element +page.get_by_test_id("scrolling-container").evaluate("e => e.scrollTop += 100") +``` + +```csharp +// Position the mouse and scroll with the mouse wheel +await page.GetByTestId("scrolling-container").HoverAsync(); +await page.Mouse.WheelAsync(0, 10); + +// Alternatively, programmatically scroll a specific element +await page.GetByTestId("scrolling-container").EvaluateAsync("e => e.scrollTop += 100"); +``` diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 6fd38100a0..9f36c89152 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -10374,6 +10374,8 @@ export interface ElementHandle extends JSHandle { * * Throws when `elementHandle` does not point to an element * [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot. + * + * See [scrolling](https://playwright.dev/docs/input#scrolling) for alternative ways to scroll. * @param options */ scrollIntoViewIfNeeded(options?: { @@ -12574,6 +12576,8 @@ export interface Locator { * This method waits for [actionability](https://playwright.dev/docs/actionability) checks, then tries to scroll element into view, unless * it is completely visible as defined by * [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)'s `ratio`. + * + * See [scrolling](https://playwright.dev/docs/input#scrolling) for alternative ways to scroll. * @param options */ scrollIntoViewIfNeeded(options?: { @@ -18649,7 +18653,8 @@ export interface Mouse { }): Promise; /** - * Dispatches a `wheel` event. + * Dispatches a `wheel` event. This method is usually used to manually scroll the page. See + * [scrolling](https://playwright.dev/docs/input#scrolling) for alternative ways to scroll. * * **NOTE** Wheel events may cause scrolling if they are not handled, and this method does not wait for the scrolling * to finish before returning. diff --git a/utils/build/build.js b/utils/build/build.js index 8472d47f75..980802735d 100644 --- a/utils/build/build.js +++ b/utils/build/build.js @@ -339,7 +339,6 @@ onChanges.push({ 'packages/playwright-core/src/server/chromium/protocol.d.ts', ], mustExist: [ - 'packages/playwright-core/lib/server/deviceDescriptors.js', 'packages/playwright-core/lib/server/deviceDescriptorsSource.json', ], script: 'utils/generate_types/index.js',