I would like to wrap radio button group with 200px width. And I expect keyboard navigation to behave the same, which is to move between radio buttons through right/left arrow back and forth. Weird thing is if I implement parent div by wrapping this Container component const Container = ({ children }) => ( <div style={{ width: "200px" }}>{children}</div>);
, after moving to different radio button once, it stops. I can't no longer navigate radio buttons. But if I implement parent div using plain html code <div style={{ width: "200px"}}>
, keyboard navigation works fine. When I inspect using dev tools, semantically, they both appear the same. So I'm having trouble figuring out why Container component impacts keyboard navigation while hard coding in html doesn't. Anybody knows why? https://codesandbox.io/s/radio-button-example-3t8l80?file=/App.js
import { Stack, RadioButton } from "@shopify/polaris";
import { useState, useCallback } from "react";
function RadioButtonExample() {
const [value, setValue] = useState("disabled");
const handleChange = useCallback(
(_checked, newValue) => setValue(newValue),
[]
);
const Container = ({ children }) => (
<div style={{ width: "200px" }}>{children}</div>
);
return (
<Container> // keyboard navigation doesn't work properly with this
{/* <div style={{ width: "200px"}}> */} // keyboard navigation works fine
<Stack vertical>
<RadioButton
label="Accounts are disabled"
helpText="Customers will only be able to check out as guests."
checked={value === "disabled"}
id="disabled"
name="accounts"
onChange={handleChange}
/>
<RadioButton
label="Accounts are optional"
helpText="Customers will be able to check out with a customer account or as a guest."
id="optional"
name="accounts"
checked={value === "optional"}
onChange={handleChange}
/>
</Stack>
{/* </div> */}
</Container>
);
}
export default RadioButtonExample;
CodePudding user response:
The reason for this to happen is because your Container
component is part of the RadioButtonExample
component. Every time a state or prop gets updated, the Container
component gets created as a new component. To prevent this, take your Container
component outside of the RadioButtonExample
like this:
import { Stack, RadioButton } from "@shopify/polaris";
import { useState, useCallback } from "react";
const Container = ({ children }) => (
<div style={{ width: "200px" }}>{children}</div>
);
function RadioButtonExample() {
const [value, setValue] = useState("disabled");
const handleChange = useCallback(
(_checked, newValue) => setValue(newValue),
[]
);
return (
<Container>
<Stack vertical>
<RadioButton
label="Accounts are disabled"
helpText="Customers will only be able to check out as guests."
checked={value === "disabled"}
id="disabled"
name="accounts"
onChange={handleChange}
/>
<RadioButton
label="Accounts are optional"
helpText="Customers will be able to check out with a customer account or as a guest."
id="optional"
name="accounts"
checked={value === "optional"}
onChange={handleChange}
/>
</Stack>
</Container>
);
}
export default RadioButtonExample;
A different solution would be to wrap the container in a useMemo
to make sure it stays the same. This would give you something like this:
const Container = useMemo(() => ({ children }) => (
<div style={{ width: "200px" }}>{children}</div>
), []);
Both will work, but I would suggest going for the first solution.