🐛(react) fix props not recognized on the input element
The props propagation was adding unknown props to the input element, which was causing an error warning to be displayed in the console.
This commit is contained in:
committed by
Anthony LC
parent
8086a48672
commit
c7000f37d2
5
.changeset/red-dragons-yell.md
Normal file
5
.changeset/red-dragons-yell.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@openfun/cunningham-react": minor
|
||||
---
|
||||
|
||||
fix props not recognized on the input element
|
||||
@@ -1,9 +1,20 @@
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { Checkbox, CheckboxGroup } from ":/components/Forms/Checkbox/index";
|
||||
import { FieldProps } from "../Field";
|
||||
import {
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
CheckboxOnlyProps,
|
||||
} from ":/components/Forms/Checkbox/index";
|
||||
|
||||
const spyError = vi.spyOn(global.console, "error");
|
||||
|
||||
describe("<Checkbox/>", () => {
|
||||
afterAll(() => {
|
||||
spyError.mockRestore();
|
||||
});
|
||||
|
||||
it("renders and can be checked", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<Checkbox label="Agree" />);
|
||||
@@ -106,4 +117,21 @@ describe("<Checkbox/>", () => {
|
||||
document.querySelector(".c__checkbox__group.c__field.c__field--error"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("checks the props doesn't create error warning", async () => {
|
||||
const propsInput: Required<FieldProps & CheckboxOnlyProps> = {
|
||||
label: "First name",
|
||||
fullWidth: true,
|
||||
className: "c__field--full-width",
|
||||
compact: false,
|
||||
state: "default",
|
||||
text: "my text",
|
||||
textItems: ["my text item 1", "my text item 2"],
|
||||
rightText: "my right text",
|
||||
indeterminate: true,
|
||||
};
|
||||
|
||||
render(<Checkbox {...propsInput} />);
|
||||
expect(spyError).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,11 +9,14 @@ import React, {
|
||||
import classNames from "classnames";
|
||||
import { Field, FieldProps } from ":/components/Forms/Field";
|
||||
|
||||
export type CheckboxOnlyProps = {
|
||||
indeterminate?: boolean;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
export type CheckboxProps = InputHTMLAttributes<HTMLInputElement> &
|
||||
FieldProps & {
|
||||
indeterminate?: boolean;
|
||||
label?: string;
|
||||
};
|
||||
FieldProps &
|
||||
CheckboxOnlyProps;
|
||||
|
||||
export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
||||
(
|
||||
@@ -33,6 +36,16 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
||||
}
|
||||
}, [indeterminate]);
|
||||
|
||||
const {
|
||||
compact,
|
||||
fullWidth,
|
||||
rightText,
|
||||
state,
|
||||
text,
|
||||
textItems,
|
||||
...inputProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<label
|
||||
className={classNames("c__checkbox", {
|
||||
@@ -46,7 +59,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
||||
<input
|
||||
type="checkbox"
|
||||
className={className}
|
||||
{...props}
|
||||
{...inputProps}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.checked);
|
||||
props.onChange?.(e);
|
||||
|
||||
@@ -2,10 +2,17 @@ import { render, screen, waitFor } from "@testing-library/react";
|
||||
import React, { useRef } from "react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { expect } from "vitest";
|
||||
import { Input } from ":/components/Forms/Input/index";
|
||||
import { FieldProps } from "../Field";
|
||||
import { Input, InputOnlyProps } from ":/components/Forms/Input/index";
|
||||
import { Button } from ":/components/Button";
|
||||
|
||||
const spyError = vi.spyOn(global.console, "error");
|
||||
|
||||
describe("<Input/>", () => {
|
||||
afterAll(() => {
|
||||
spyError.mockRestore();
|
||||
});
|
||||
|
||||
it("renders and can type", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<Input label="First name" />);
|
||||
@@ -208,4 +215,24 @@ describe("<Input/>", () => {
|
||||
await user.clear(input);
|
||||
screen.getByText("Value: .");
|
||||
});
|
||||
|
||||
it("checks the props doesn't create error warning", async () => {
|
||||
const propsInput: Required<FieldProps & InputOnlyProps> = {
|
||||
label: "First name",
|
||||
fullWidth: true,
|
||||
charCounter: true,
|
||||
charCounterMax: 15,
|
||||
className: "c__field--full-width",
|
||||
compact: false,
|
||||
state: "default",
|
||||
icon: "my icon",
|
||||
rightIcon: "my right icon",
|
||||
text: "my text",
|
||||
textItems: ["my text item 1", "my text item 2"],
|
||||
rightText: "my right text",
|
||||
};
|
||||
|
||||
render(<Input {...propsInput} />);
|
||||
expect(spyError).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,14 +11,17 @@ import { randomString } from ":/utils";
|
||||
import { Field, FieldProps } from ":/components/Forms/Field";
|
||||
import { LabelledBox } from ":/components/Forms/LabelledBox";
|
||||
|
||||
export type InputOnlyProps = {
|
||||
label?: string;
|
||||
icon?: ReactNode;
|
||||
rightIcon?: ReactNode;
|
||||
charCounter?: boolean;
|
||||
charCounterMax?: number;
|
||||
};
|
||||
|
||||
export type InputProps = InputHTMLAttributes<HTMLInputElement> &
|
||||
FieldProps & {
|
||||
label?: string;
|
||||
icon?: ReactNode;
|
||||
rightIcon?: ReactNode;
|
||||
charCounter?: boolean;
|
||||
charCounterMax?: number;
|
||||
};
|
||||
FieldProps &
|
||||
InputOnlyProps;
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
(
|
||||
@@ -68,6 +71,16 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
setValue(props.value || "");
|
||||
}, [props.value]);
|
||||
|
||||
const {
|
||||
compact,
|
||||
fullWidth,
|
||||
rightText,
|
||||
state,
|
||||
text,
|
||||
textItems,
|
||||
...inputProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Field {...props} rightText={rightTextToUse}>
|
||||
{/* We disabled linting for this specific line because we consider that the onClick props is only used for */}
|
||||
@@ -76,7 +89,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
<div
|
||||
className={classNames(
|
||||
"c__input__wrapper",
|
||||
"c__input__wrapper--" + props.state,
|
||||
props.state && "c__input__wrapper--" + props.state,
|
||||
{
|
||||
"c__input__wrapper--disabled": props.disabled,
|
||||
},
|
||||
@@ -95,7 +108,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
<input
|
||||
type="text"
|
||||
className={classes.join(" ")}
|
||||
{...props}
|
||||
{...inputProps}
|
||||
id={idToUse.current}
|
||||
value={value}
|
||||
onFocus={(e) => {
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { Radio, RadioGroup } from ":/components/Forms/Radio/index";
|
||||
import { FieldProps } from "../Field";
|
||||
import {
|
||||
Radio,
|
||||
RadioGroup,
|
||||
RadioOnlyProps,
|
||||
} from ":/components/Forms/Radio/index";
|
||||
|
||||
const spyError = vi.spyOn(global.console, "error");
|
||||
|
||||
describe("<Radio/>", () => {
|
||||
afterAll(() => {
|
||||
spyError.mockRestore();
|
||||
});
|
||||
|
||||
it("should render", async () => {
|
||||
render(<Radio label="Yes" />);
|
||||
expect(screen.getByLabelText("Yes")).toBeInTheDocument();
|
||||
@@ -111,4 +122,20 @@ describe("<Radio/>", () => {
|
||||
document.querySelector(".c__radio__group.c__field.c__field--error"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("checks the props doesn't create error warning", async () => {
|
||||
const propsInput: Required<FieldProps & RadioOnlyProps> = {
|
||||
label: "First name",
|
||||
fullWidth: true,
|
||||
className: "c__field--full-width",
|
||||
compact: false,
|
||||
state: "default",
|
||||
text: "my text",
|
||||
textItems: ["my text item 1", "my text item 2"],
|
||||
rightText: "my right text",
|
||||
};
|
||||
|
||||
render(<Radio {...propsInput} />);
|
||||
expect(spyError).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,13 +6,26 @@ import React, {
|
||||
import classNames from "classnames";
|
||||
import { Field, FieldProps } from ":/components/Forms/Field";
|
||||
|
||||
export type RadioOnlyProps = {
|
||||
label?: string;
|
||||
};
|
||||
|
||||
export type RadioProps = InputHTMLAttributes<HTMLInputElement> &
|
||||
FieldProps & {
|
||||
label?: string;
|
||||
};
|
||||
FieldProps &
|
||||
RadioOnlyProps;
|
||||
|
||||
export const Radio = forwardRef<HTMLInputElement, RadioProps>(
|
||||
({ label, ...props }: RadioProps, ref) => {
|
||||
const {
|
||||
compact,
|
||||
fullWidth,
|
||||
rightText,
|
||||
state,
|
||||
text,
|
||||
textItems,
|
||||
...inputProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<label
|
||||
className={classNames("c__checkbox", "c__radio", {
|
||||
@@ -22,7 +35,7 @@ export const Radio = forwardRef<HTMLInputElement, RadioProps>(
|
||||
>
|
||||
<Field compact={true} {...props}>
|
||||
<div className="c__checkbox__container">
|
||||
<input type="radio" {...props} ref={ref} />
|
||||
<input type="radio" {...inputProps} ref={ref} />
|
||||
{label && <div className="c__checkbox__label">{label}</div>}
|
||||
</div>
|
||||
</Field>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import React from "react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { Switch } from ":/components/Forms/Switch/index";
|
||||
import { FieldProps } from "../Field";
|
||||
import { Switch, SwitchOnlyProps } from ":/components/Forms/Switch/index";
|
||||
|
||||
const spyError = vi.spyOn(global.console, "error");
|
||||
|
||||
describe("<Switch/>", () => {
|
||||
afterAll(() => {
|
||||
spyError.mockRestore();
|
||||
});
|
||||
|
||||
it("renders and can be checked", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<Switch label="Newsletter" />);
|
||||
@@ -132,4 +139,21 @@ describe("<Switch/>", () => {
|
||||
expect(input.checked).toEqual(false);
|
||||
screen.queryByText("Value: false.");
|
||||
});
|
||||
|
||||
it("checks the props doesn't create error warning", async () => {
|
||||
const propsInput: Required<FieldProps & SwitchOnlyProps> = {
|
||||
label: "First name",
|
||||
fullWidth: true,
|
||||
className: "c__field--full-width",
|
||||
compact: false,
|
||||
state: "default",
|
||||
text: "my text",
|
||||
textItems: ["my text item 1", "my text item 2"],
|
||||
rightText: "my right text",
|
||||
labelSide: "right",
|
||||
};
|
||||
|
||||
render(<Switch {...propsInput} />);
|
||||
expect(spyError).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,14 +2,27 @@ import React, { InputHTMLAttributes, forwardRef } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Field, FieldProps } from ":/components/Forms/Field";
|
||||
|
||||
export type SwitchOnlyProps = {
|
||||
label?: string;
|
||||
labelSide?: "left" | "right";
|
||||
};
|
||||
|
||||
export type SwitchProps = InputHTMLAttributes<HTMLInputElement> &
|
||||
FieldProps & {
|
||||
label?: string;
|
||||
labelSide?: "left" | "right";
|
||||
};
|
||||
FieldProps &
|
||||
SwitchOnlyProps;
|
||||
|
||||
export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
||||
({ label, labelSide = "left", ...props }: SwitchProps, ref) => {
|
||||
const {
|
||||
compact,
|
||||
fullWidth,
|
||||
rightText,
|
||||
state,
|
||||
text,
|
||||
textItems,
|
||||
...inputProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<label
|
||||
className={classNames(
|
||||
@@ -26,7 +39,7 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
||||
<div className="c__checkbox__container">
|
||||
{label && <div className="c__checkbox__label">{label}</div>}
|
||||
<div className="c__switch__rail__wrapper">
|
||||
<input type="checkbox" {...props} ref={ref} />
|
||||
<input type="checkbox" {...inputProps} ref={ref} />
|
||||
<div className="c__switch__rail" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user