diff --git a/CHANGELOG.md b/CHANGELOG.md index 6da1b27..b1c1a0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ All notable changes follow Keep a Changelog. confirmed. - Added live pull metrics, lifecycle stress controls, browser guidance, and published-package integration proofs to the documentation site. +- Preserved the upward touch-pan direction at the top boundary so scrollable + content can begin scrolling normally from its first row. ## [0.1.0-alpha.0] - 2026-09-05 diff --git a/docs/INTEGRATIONS.md b/docs/INTEGRATIONS.md index 67313e7..176421b 100644 --- a/docs/INTEGRATIONS.md +++ b/docs/INTEGRATIONS.md @@ -39,15 +39,22 @@ element explicitly: ```tsx -
- - {/* feed */} - -
+ + {/* feed */} +
``` +Make the root itself scrollable. Do not place a default PTR root—with its +mechanical overflow containment—inside a second element that is expected to +receive chained scrolling. + The documentation proof uses the published v5 package. Desktop automation -verifies that the sheet opens, exposes a dedicated scroll root, and closes. +verifies that the sheet opens, scrolls the dedicated root, and closes. Combined drag feel on iOS and Android remains manual pending; the alpha does not claim physical-device verification. diff --git a/e2e/gesture.spec.ts b/e2e/gesture.spec.ts index cf2f254..1c60411 100644 --- a/e2e/gesture.spec.ts +++ b/e2e/gesture.spec.ts @@ -77,6 +77,16 @@ test('documents ownership, native refresh, and truthful device status', async ({ test('renders real sibling primitive integration proofs', async ({ page }) => { await page.goto('/#integrations') + await expect( + page.getByRole('link', { name: 'React Swipe Actions' }), + ).toHaveAttribute('href', 'https://react-swipe-actions.nipesolutions.com/') + await expect( + page.getByRole('link', { name: 'React Spring Bottom Sheet' }), + ).toHaveAttribute( + 'href', + 'https://react-spring-bottom-sheet.nipesolutions.com/', + ) + const swipe = page.getByTestId('swipe-integration') await expect(swipe).toHaveAttribute('data-integration', 'swipe-actions') await expect(swipe.locator('.swipe-actions-root').first()).toBeVisible() @@ -87,6 +97,32 @@ test('renders real sibling primitive integration proofs', async ({ page }) => { await page.getByRole('button', { name: 'Close sheet proof' }).click() }) +test('sheet content keeps the native scroll direction available at its top boundary', async ({ + page, +}) => { + await page.goto('/#integrations') + await page.getByRole('button', { name: 'Open bottom sheet proof' }).click() + + const root = page.getByTestId('sheet-ptr') + await expect(root).toHaveAttribute('data-at-top', 'true') + const touchAction = await root.evaluate( + (node) => window.getComputedStyle(node).touchAction, + ) + expect(['pan-x pan-up', 'pan-x pan-y']).toContain(touchAction) + + const scroll = page.locator('.sheet-scroll') + const dimensions = await scroll.evaluate((node) => ({ + clientHeight: node.clientHeight, + scrollHeight: node.scrollHeight, + })) + expect(dimensions.scrollHeight).toBeGreaterThan(dimensions.clientHeight) + await scroll.hover() + await page.mouse.wheel(0, 180) + await expect + .poll(() => scroll.evaluate((node) => node.scrollTop)) + .toBeGreaterThan(0) +}) + test('Swipe Actions owns a horizontal row trace', async ({ page }) => { await page.goto('/#integrations') const row = page.locator('.swipe-actions-root').first() diff --git a/src/core.css b/src/core.css index 25f3d72..e9c0015 100644 --- a/src/core.css +++ b/src/core.css @@ -10,7 +10,7 @@ } .ptr-root[data-at-top='true'] { - touch-action: pan-x pan-down; + touch-action: pan-x pan-up; } .ptr-indicator { diff --git a/website/src/demos.tsx b/website/src/demos.tsx index 019521b..cd1850f 100644 --- a/website/src/demos.tsx +++ b/website/src/demos.tsx @@ -369,26 +369,26 @@ export function BottomSheetProof() { Close sheet proof -
- - new Promise((resolve) => window.setTimeout(resolve, 400)) - } - > - - - - - {Array.from({ length: 8 }, (_, index) => ( -
- Sheet content {index + 1} -
- ))} -
-
-
+ + new Promise((resolve) => window.setTimeout(resolve, 400)) + } + > + + + + + {Array.from({ length: 8 }, (_, index) => ( +
+ Sheet content {index + 1} +
+ ))} +
+
diff --git a/website/src/main.tsx b/website/src/main.tsx index a38c4e2..09939c6 100644 --- a/website/src/main.tsx +++ b/website/src/main.tsx @@ -183,7 +183,12 @@ function IntegrationProofs() {
-

Swipe Actions inside a feed

+

+ + React Swipe Actions + {' '} + inside a feed +

Horizontal motion reveals a row. Vertical motion stays with scrolling. A downward pull at the top belongs to refresh. @@ -191,7 +196,12 @@ function IntegrationProofs() {

-

Pull to Refresh inside a sheet

+

+ Pull to Refresh inside{' '} + + React Spring Bottom Sheet + +

The sheet handle remains outside the scrolling feed. The feed element is passed explicitly as the pull boundary. diff --git a/website/src/site.css b/website/src/site.css index 16dad9f..b38838d 100644 --- a/website/src/site.css +++ b/website/src/site.css @@ -574,6 +574,15 @@ pre { .integrations .section-heading { margin-bottom: 56px; } +.integrations h3 a { + color: inherit; + text-decoration-color: var(--tension-deep); + text-decoration-thickness: 2px; + text-underline-offset: 5px; +} +.integrations h3 a:hover { + color: var(--tension-deep); +} .integration-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));