Skip to content
Snippets Groups Projects
Unverified Commit 18030359 authored by Dalton's avatar Dalton Committed by GitHub
Browse files

use preventScroll in focus call in ListSearchField (#17692)

* use preventScroll in focus call in ListSearchField

* add a few ListSearchField unit tests
parent 786a4b7e
No related branches found
No related tags found
No related merge requests found
import React, { useEffect, useRef } from "react";
import React, { useRef, useEffect } from "react";
import Icon from "metabase/components/Icon";
import TextInput from "metabase/components/TextInput";
export default function ListSearchField(props) {
export default function ListSearchField({ autoFocus, ...props }) {
const inputRef = useRef();
useEffect(() => {
if (!props.autoFocus) {
return;
// this component is used within virtualized lists
// rerendering an input with autoFocus causes the list to be scrolled to the top
// so we override an autoFocus prop here to prevent any scrolling
if (inputRef.current && autoFocus) {
inputRef.current.focus({
preventScroll: true,
});
}
// Call focus() with a small delay because instant input focus causes an abrupt scroll to top of page
// when ListSearchField is used inside a popover. It seems that it takes a while for Tether library
// to correctly position the popover.
const timerId = setTimeout(
() => inputRef.current && inputRef.current.focus(),
50,
);
return () => clearTimeout(timerId);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<TextInput
{...props}
ref={inputRef}
{...props}
padding="sm"
borderRadius="md"
icon={<Icon name="search" size={16} />}
......
import React from "react";
import ListSearchField from "./ListSearchField";
import { render } from "@testing-library/react";
describe("ListSearchField", () => {
let input;
beforeEach(() => {
render(<ListSearchField autoFocus type="number" foo />);
input = document.querySelector("input");
});
it("should render", async () => {
expect(input).toBeInTheDocument();
});
it("should have focused the input field", () => {
expect(document.activeElement).toBe(input);
});
it("should pass through any additional input properties", () => {
expect(input.getAttribute("type")).toBe("number");
});
});
......@@ -25,20 +25,23 @@ TextInput.propTypes = {
colorScheme: PropTypes.oneOf(["default", "admin"]),
};
function TextInput({
value = "",
className,
placeholder = t`Find...`,
onChange,
hasClearButton = false,
icon,
type = "text",
colorScheme = "default",
autoFocus = false,
padding = "md",
borderRadius = "md",
...rest
}) {
function TextInput(
{
value = "",
className,
placeholder = t`Find...`,
onChange,
hasClearButton = false,
icon,
type = "text",
colorScheme = "default",
autoFocus = false,
padding = "md",
borderRadius = "md",
...rest
},
ref,
) {
const handleClearClick = () => {
onChange("");
};
......@@ -49,6 +52,7 @@ function TextInput({
<TextInputRoot className={className}>
{icon && <IconWrapper>{icon}</IconWrapper>}
<Input
innerRef={ref}
colorScheme={colorScheme}
autoFocus={autoFocus}
hasClearButton={hasClearButton}
......@@ -71,6 +75,4 @@ function TextInput({
);
}
export default forwardRef(function TextInputWithForwardedRef(props, ref) {
return <TextInput forwardedRef={ref} {...props} />;
});
export default forwardRef(TextInput);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment