Skip to content
This repository has been archived by the owner on Jan 20, 2022. It is now read-only.

AR-2129 Allow all form control children to be removed on subsequent renders #325

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 29 additions & 18 deletions src/FormControl/FormControl.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { FormLabel } from "../FormLabel";
import { FormHelperText } from "../FormHelperText";
import { Input } from "../Input";
import { FormErrorMessage } from "../FormErrorMessage";
import { FormEndAdornment } from "../FormEndAdornment";
import { FormStartAdornment } from "../FormStartAdornment";
import { FormDescription } from "../FormDescription";

test("when passed a label, renders it", () => {
render(
Expand Down Expand Up @@ -88,24 +91,6 @@ test("when passed `<FormErrorMessage />`, renders error and svg", () => {
expect(container.querySelector("svg")).toBeInTheDocument();
});

test("when passed `<FormErrorMessage />` that is removed, removes the error message", () => {
const Component: React.FC<{ errorText?: string }> = ({ errorText }) => (
<FormControl id="test">
<Input />
{errorText && <FormErrorMessage>{errorText}</FormErrorMessage>}
</FormControl>
);

const { container, rerender } = render(<Component errorText="error text" />);

expect(screen.getByText("error text")).toBeInTheDocument();
expect(container.querySelector("svg")).toBeInTheDocument();

rerender(<Component />);

expect(screen.queryByText("error text")).not.toBeInTheDocument();
});

test("when passed `<HelperText>` witout `showIcon` prop, renders no svg", () => {
const { container } = render(
<FormControl id="test">
Expand Down Expand Up @@ -143,3 +128,29 @@ test("when not passed `autoFocus` prop, should not have focus after mounting", (

expect(formField).not.toHaveFocus();
});

test.each([
["FormDescription", FormDescription],
["FormEndAdornment", FormEndAdornment],
["FormErrorMessage", FormErrorMessage],
["FormHelperText", FormHelperText],
["FormLabel", FormLabel],
["FormStartAdornment", FormStartAdornment],
])("%s can be removed on subsequent renders", (_, Component) => {
const { rerender } = render(
<FormControl id="test">
<Component>text</Component>
<Input />
</FormControl>,
);

expect(screen.getByText("text")).toBeInTheDocument();

rerender(
<FormControl id="test">
<Input />
</FormControl>,
);

expect(screen.queryByText("text")).not.toBeInTheDocument();
});
2 changes: 2 additions & 0 deletions src/FormDescription/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export const FormDescription: React.FC<Props> = ({

React.useLayoutEffect(() => {
setDescription?.(element);

return () => setDescription?.(null);
}, [element, setDescription]);

return setDescription ? null : element;
Expand Down
11 changes: 5 additions & 6 deletions src/FormEndAdornment/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,10 @@ import { useFormControlInternalContext } from "../shared/FormControlContext";
* This is intended to be rendered below `<FormControl>`. If this is rendered on
* it's own; it will render `children` without any modification.
*/
export function FormEndAdornment({
export const FormEndAdornment: React.FC<{ className?: string }> = ({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}): React.ReactNode {
}) => {
const { setEndAdornment } = useFormControlInternalContext();

const element = React.useMemo(
Expand All @@ -40,7 +37,9 @@ export function FormEndAdornment({

React.useLayoutEffect(() => {
setEndAdornment?.(element);

return () => setEndAdornment?.(null);
}, [element, setEndAdornment]);

return setEndAdornment ? null : element;
}
};
2 changes: 2 additions & 0 deletions src/FormHelperText/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export const FormHelperText: React.FC<Props> = ({
React.useLayoutEffect(() => {
// This will cause a bug if you change the `error` prop
setHelper?.(element);

return () => setHelper?.(null);
}, [element, setHelper]);

// If `setHelper` exists then we're rendering this under the form control
Expand Down
2 changes: 2 additions & 0 deletions src/FormLabel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export const FormLabel: React.FC<Props> = ({

React.useLayoutEffect(() => {
setLabel?.(element);

return () => setLabel?.(null);
}, [element, setLabel]);

return setLabel ? null : element;
Expand Down
11 changes: 5 additions & 6 deletions src/FormStartAdornment/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,10 @@ import { useFormControlInternalContext } from "../shared/FormControlContext";
* This is intended to be rendered below `<FormControl>`. If this is rendered on
* it's own; it will render `children` without any modification.
*/
export function FormStartAdornment({
export const FormStartAdornment: React.FC<{ className?: string }> = ({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}): React.ReactNode {
}) => {
const { setStartAdornment } = useFormControlInternalContext();

const element = React.useMemo(
Expand All @@ -39,7 +36,9 @@ export function FormStartAdornment({

React.useLayoutEffect(() => {
setStartAdornment?.(element);

return () => setStartAdornment?.(null);
}, [element, setStartAdornment]);

return setStartAdornment ? null : element;
}
};