From 95b87586b35ccfc8709f8761e8624539d8c5776a Mon Sep 17 00:00:00 2001 From: Avinash Dwarapu Date: Fri, 5 Sep 2025 18:38:39 +0200 Subject: [PATCH] fix: Don't scroll the page unnecessarily when the file input is focused --- pages/file-input/integ.page.tsx | 32 ++++++++++++++ src/file-input/__integ__/file-input.test.ts | 47 +++++++++++++++++++++ src/file-input/internal.tsx | 1 - src/file-input/styles.scss | 22 +++++++--- 4 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 pages/file-input/integ.page.tsx create mode 100644 src/file-input/__integ__/file-input.test.ts diff --git a/pages/file-input/integ.page.tsx b/pages/file-input/integ.page.tsx new file mode 100644 index 0000000000..b7ae0197d2 --- /dev/null +++ b/pages/file-input/integ.page.tsx @@ -0,0 +1,32 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useState } from 'react'; + +import { Box, FileInput, FileInputProps } from '~components'; + +import ScreenshotArea from '../utils/screenshot-area'; + +export default function DateInputScenario() { + const [files, setFiles] = useState([]); + + const ref = React.useRef(null); + + return ( + + +

File input

+ +
+ setFiles(event.detail.value)}> + Choose files + + + + {files.map((file, index) => ( +
{file.name}
+ ))} +
+
+
+ ); +} diff --git a/src/file-input/__integ__/file-input.test.ts b/src/file-input/__integ__/file-input.test.ts new file mode 100644 index 0000000000..2e587c57f8 --- /dev/null +++ b/src/file-input/__integ__/file-input.test.ts @@ -0,0 +1,47 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { BasePageObject } from '@cloudscape-design/browser-test-tools/page-objects'; +import useBrowser from '@cloudscape-design/browser-test-tools/use-browser'; + +import createWrapper from '../../../lib/components/test-utils/selectors'; + +const wrapper = createWrapper(); +const fileInputWrapper = wrapper.findFileInput(); + +const setupTest = (testFn: (page: BasePageObject) => Promise) => { + return useBrowser(async browser => { + const page = new BasePageObject(browser); + await browser.url('#/light/file-input/integ'); + await page.waitForVisible(fileInputWrapper.toSelector()); + await testFn(page); + }); +}; + +describe('FileInput', () => { + test( + 'visible in viewport when input is focused', + setupTest(async page => { + await page.click('#focus-before'); + await page.keys(['Tab']); + + // Check that the file input is still in the viewport + await expect(page.isDisplayedInViewport(fileInputWrapper.findTrigger().toSelector())).resolves.toBe(true); + }) + ); + + test( + 'does not scroll the page if the button is already visible', + setupTest(async page => { + await page.click('#focus-before'); + await page.keys(['Tab']); + const beforeScrollPosition = await page.getWindowScroll(); + + await page.click('#focus-after'); + await page.keys(['Shift', 'Tab']); + const afterScrollPosition = await page.getWindowScroll(); + + // Check that the positions are the same + expect(beforeScrollPosition).toStrictEqual(afterScrollPosition); + }) + ); +}); diff --git a/src/file-input/internal.tsx b/src/file-input/internal.tsx index 7c14453c15..31e1c0b66c 100644 --- a/src/file-input/internal.tsx +++ b/src/file-input/internal.tsx @@ -66,7 +66,6 @@ const InternalFileInput = React.forwardRef( const onUploadButtonClick = () => uploadInputRef.current?.click(); const onUploadInputFocus = () => { setIsFocused(true); - containerRef.current?.scrollIntoView?.(); }; const onUploadInputBlur = () => setIsFocused(false); diff --git a/src/file-input/styles.scss b/src/file-input/styles.scss index 4fd64c68a2..9abde0625d 100644 --- a/src/file-input/styles.scss +++ b/src/file-input/styles.scss @@ -6,13 +6,25 @@ @use '../internal/styles' as styles; @use '@cloudscape-design/component-toolkit/internal/focus-visible' as focus-visible; -.root, -.file-input { - /* used in test-utils */ +.root { + position: relative; } -.hidden { - @include styles.awsui-util-hide; +.file-input { + // Adapted from https://webaim.org/techniques/css/invisiblecontent/ + + position: absolute; + inset-block-start: 0; + inset-inline-start: 0; + clip: rect(1px, 1px, 1px, 1px); + clip-path: inset(50%); + block-size: 1px; + inline-size: 1px; + margin-block: -1px; + margin-inline: -1px; + padding-block: 0; + padding-inline: 0; + overflow: hidden; } .file-input-button {