Skip to content

Commit

Permalink
add forward ref for input
Browse files Browse the repository at this point in the history
  • Loading branch information
Sworzen1 committed Feb 15, 2024
1 parent d41d3bd commit 1e4265d
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 69 deletions.
23 changes: 23 additions & 0 deletions govtool/frontend/src/components/atoms/FormErrorMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Typography } from "@mui/material";

import { FormErrorMessageProps } from "./types";

export const FormErrorMessage = ({
errorMessage,
errorStyles,
}: FormErrorMessageProps) => {
return (
errorMessage && (
<Typography
color="red"
data-testid={`${errorMessage.replace(/\s+/g, "-").toLowerCase()}-error`}
fontSize={12}
fontWeight={400}
sx={{ mt: 0.25 }}
{...errorStyles}
>
{errorMessage}
</Typography>
)
);
};
76 changes: 51 additions & 25 deletions govtool/frontend/src/components/atoms/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,56 @@
import { useId } from "react";
import {
forwardRef,
useCallback,
useId,
useImperativeHandle,
useRef,
} from "react";
import { InputBase } from "@mui/material";

import { InputProps } from "./types";

export const Input = ({
errorMessage,
dataTestId,
sx,
...rest
}: InputProps) => {
const id = useId();
export const Input = forwardRef<HTMLInputElement, InputProps>(
({ errorMessage, dataTestId, onBlur, onFocus, sx, ...rest }, ref) => {
const id = useId();
const inputRef = useRef<HTMLInputElement>(null);

return (
<InputBase
id={id}
inputProps={{ "data-testid": dataTestId }}
sx={{
backgroundColor: errorMessage ? "inputRed" : "transparent",
border: 1,
borderColor: errorMessage ? "red" : "secondaryBlue",
borderRadius: 50,
padding: "8px 16px",
width: "100%",
...sx,
}}
{...rest}
/>
);
};
const handleFocus = useCallback((e: React.FocusEvent<HTMLInputElement>) => {
onFocus?.(e);
inputRef.current?.focus();
}, []);

const handleBlur = useCallback((e: React.FocusEvent<HTMLInputElement>) => {
onBlur?.(e);
inputRef.current?.blur();
}, []);

useImperativeHandle(
ref,
() =>
({
focus: handleFocus,
blur: handleBlur,
...inputRef.current,
} as unknown as HTMLInputElement),
[handleBlur, handleFocus]
);

return (
<InputBase
id={id}
inputRef={inputRef}
inputProps={{ "data-testid": dataTestId }}
sx={{
backgroundColor: errorMessage ? "inputRed" : "transparent",
border: 1,
borderColor: errorMessage ? "red" : "secondaryBlue",
borderRadius: 50,
padding: "8px 16px",
width: "100%",
...sx,
}}
{...rest}
/>
);
}
);
7 changes: 4 additions & 3 deletions govtool/frontend/src/components/atoms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ export * from "./Button";
export * from "./ClickOutside";
export * from "./CopyButton";
export * from "./DrawerLink";
export * from "./FormErrorMessage";
export * from "./HighlightedText";
export * from "./Input";
export * from "./Link";
export * from "./LoadingButton";
export * from "./modal/Modal";
Expand All @@ -13,13 +16,11 @@ export * from "./modal/ModalWrapper";
export * from "./Radio";
export * from "./ScrollToManage";
export * from "./ScrollToTop";
export * from "./Spacer";
export * from "./StakeRadio";
export * from "./Tooltip";
export * from "./Typography";
export * from "./VotePill";
export * from "./VotingPowerChips";
export * from "./Input";
export * from "./Spacer";
export * from "./HighlightedText";

export * from "./types";
5 changes: 5 additions & 0 deletions govtool/frontend/src/components/atoms/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,8 @@ export type SpacerProps = {
x?: number;
y?: number;
};

export type FormErrorMessageProps = {
errorMessage?: string;
errorStyles?: MUITypographyProps;
};
88 changes: 55 additions & 33 deletions govtool/frontend/src/components/molecules/Field/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,60 @@
import { forwardRef, useCallback, useImperativeHandle, useRef } from "react";
import { Box } from "@mui/material";

import { Input as InputBase, Typography } from "@atoms";
import { FormErrorMessage, Input as InputBase, Typography } from "@atoms";

import { InputFieldProps } from "./types";

export const Input = ({
errorMessage,
errorStyle,
label,
labelStyle,
layoutStyle,
...rest
}: InputFieldProps) => {
return (
<Box sx={{ width: "100%", ...layoutStyle }}>
{label && (
<Typography fontWeight={400} variant="body2" {...labelStyle}>
{label}
</Typography>
)}
<InputBase errorMessage={errorMessage} {...rest} />
{errorMessage && (
<Typography
variant="caption"
color="red"
data-testid={`${errorMessage
.replace(/\s+/g, "-")
.toLowerCase()}-error`}
sx={{ mt: 0.25 }}
{...errorStyle}
>
{errorMessage}
</Typography>
)}
</Box>
);
};
export const Input = forwardRef<HTMLInputElement, InputFieldProps>(
(
{
errorMessage,
errorStyles,
label,
labelStyles,
layoutStyles,
onBlur,
onFocus,
...rest
},
ref
) => {
const inputRef = useRef<HTMLInputElement>(null);

const handleFocus = useCallback((e: React.FocusEvent<HTMLInputElement>) => {
onFocus?.(e);
inputRef.current?.focus();
}, []);

const handleBlur = useCallback((e: React.FocusEvent<HTMLInputElement>) => {
onBlur?.(e);
inputRef.current?.blur();
}, []);

useImperativeHandle(
ref,
() =>
({
focus: handleFocus,
blur: handleBlur,
...inputRef.current,
} as unknown as HTMLInputElement),
[handleBlur, handleFocus]
);

return (
<Box sx={{ width: "100%", ...layoutStyles }}>
{label && (
<Typography fontWeight={400} variant="body2" {...labelStyles}>
{label}
</Typography>
)}
<InputBase errorMessage={errorMessage} ref={inputRef} {...rest} />
<FormErrorMessage
errorMessage={errorMessage}
errorStyles={errorStyles}
/>
</Box>
);
}
);
8 changes: 4 additions & 4 deletions govtool/frontend/src/components/molecules/Field/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { BoxProps } from "@mui/material";
import { BoxProps, TypographyProps as MUITypographyProps } from "@mui/material";

import { InputProps, TypographyProps } from "@atoms";

export type InputFieldProps = InputProps & {
errorMessage?: string;
errorStyle?: TypographyProps;
errorStyles?: MUITypographyProps;
label?: string;
labelStyle?: TypographyProps;
layoutStyle?: BoxProps;
labelStyles?: TypographyProps;
layoutStyles?: BoxProps;
};
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,15 @@ export const RegisterAsdRepStepOne = ({ setStep }: Props) => {
<ControlledField.Input
{...{ control, errors }}
dataTestId="url-input"
layoutStyle={{ width: isMobile ? "100%" : "70%" }}
layoutStyles={{ width: isMobile ? "100%" : "70%" }}
name="url"
placeholder={t("forms.urlWithInfoPlaceholder")}
/>
<Spacer y={6} />
<ControlledField.Input
{...{ control, errors }}
dataTestId="hash-input"
layoutStyle={{ width: isMobile ? "100%" : "70%" }}
layoutStyles={{ width: isMobile ? "100%" : "70%" }}
name="hash"
placeholder={t("forms.hashPlaceholder")}
/>
Expand Down
4 changes: 2 additions & 2 deletions govtool/frontend/src/pages/UpdatedRepMetadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const UpdatedRepMetadata = () => {
<ControlledField.Input
{...{ control, errors }}
dataTestId="url-input"
layoutStyle={{ width: isMobile ? "100%" : "70%" }}
layoutStyles={{ width: isMobile ? "100%" : "70%" }}
name="url"
placeholder={t("forms.urlWithInfoPlaceholder")}
/>
Expand All @@ -129,7 +129,7 @@ export const UpdatedRepMetadata = () => {
{...{ control, errors }}
dataTestId="hash-input"
errorMessage={errors.hash?.message}
layoutStyle={{ width: isMobile ? "100%" : "70%" }}
layoutStyles={{ width: isMobile ? "100%" : "70%" }}
name="hash"
placeholder={t("forms.hashPlaceholder")}
/>
Expand Down

0 comments on commit 1e4265d

Please sign in to comment.