✨(react) add InputPassword
We had the need to have a built-in password input able to show or hide the password. Closes #301
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import { Meta } from "@storybook/react";
|
||||
import { InputPassword } from ":/components/Forms/Input/InputPassword";
|
||||
|
||||
export default {
|
||||
title: "Components/Forms/Input",
|
||||
component: InputPassword,
|
||||
} as Meta<typeof InputPassword>;
|
||||
|
||||
export const Password = {
|
||||
args: {
|
||||
label: "Your most secret password",
|
||||
},
|
||||
};
|
||||
41
packages/react/src/components/Forms/Input/InputPassword.tsx
Normal file
41
packages/react/src/components/Forms/Input/InputPassword.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { Input, InputProps } from ":/components/Forms/Input/index";
|
||||
import { Button } from ":/components/Button";
|
||||
import { useCunningham } from ":/components/Provider";
|
||||
|
||||
export const InputPassword = forwardRef<
|
||||
HTMLInputElement,
|
||||
Omit<InputProps, "rightIcon">
|
||||
>((props: InputProps, ref) => {
|
||||
const [showPassword, setShowPassword] = React.useState(false);
|
||||
const { className, ...otherProps } = props;
|
||||
const customClassName = "c__input--password";
|
||||
const { t } = useCunningham();
|
||||
return (
|
||||
<Input
|
||||
{...otherProps}
|
||||
ref={ref}
|
||||
className={className + " " + customClassName}
|
||||
type={showPassword ? "text" : "password"}
|
||||
rightIcon={
|
||||
showPassword ? (
|
||||
<Button
|
||||
onClick={() => setShowPassword(false)}
|
||||
icon={<span className="material-icons">visibility_off</span>}
|
||||
color="tertiary-text"
|
||||
size="small"
|
||||
aria-label={t("components.forms.input.password.hide_password")}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
onClick={() => setShowPassword(true)}
|
||||
icon={<span className="material-icons">visibility</span>}
|
||||
color="tertiary-text"
|
||||
size="small"
|
||||
aria-label={t("components.forms.input.password.show_password")}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@@ -133,3 +133,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c__input--password {
|
||||
.c__input__icon-right .material-icons {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Canvas, Meta, Story, Source, ArgTypes } from '@storybook/blocks';
|
||||
import { Input } from "./index";
|
||||
import * as Stories from './index.stories';
|
||||
import * as InputPasswordStories from './InputPassword.stories';
|
||||
|
||||
<Meta of={Stories}/>
|
||||
|
||||
@@ -65,6 +66,12 @@ in mind to also define `charCounterMax`.
|
||||
|
||||
<Canvas of={Stories.CharCounter} sourceState="shown"/>
|
||||
|
||||
## Password
|
||||
|
||||
You can also use a built-in password input that includes a button to show or hide the password.
|
||||
|
||||
<Canvas of={InputPasswordStories.Password} sourceState="shown"/>
|
||||
|
||||
## Controlled / Non Controlled
|
||||
|
||||
Like a native input, you can use the Input component in a controlled or non controlled way. You can see the example below
|
||||
|
||||
@@ -4,6 +4,8 @@ import userEvent from "@testing-library/user-event";
|
||||
import { expect } from "vitest";
|
||||
import { Input, InputOnlyProps } from ":/components/Forms/Input/index";
|
||||
import { Button } from ":/components/Button";
|
||||
import { InputPassword } from ":/components/Forms/Input/InputPassword";
|
||||
import { CunninghamProvider } from ":/components/Provider";
|
||||
import { FieldProps } from "../Field";
|
||||
|
||||
const spyError = vi.spyOn(global.console, "error");
|
||||
@@ -242,4 +244,32 @@ describe("<Input/>", () => {
|
||||
document.querySelector(".c__field.my-custom-class"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders with className", async () => {
|
||||
render(<Input label="First name" className="my-custom-class" />);
|
||||
expect(
|
||||
document.querySelector(".c__field.my-custom-class"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("allows to show/hide password", async () => {
|
||||
render(
|
||||
<CunninghamProvider>
|
||||
<InputPassword label="Password" />
|
||||
</CunninghamProvider>,
|
||||
);
|
||||
const user = userEvent.setup();
|
||||
const input: HTMLInputElement = screen.getByLabelText("Password");
|
||||
|
||||
await user.type(input, "azerty");
|
||||
expect(input.type).toEqual("password");
|
||||
|
||||
let button = screen.getByRole("button", { name: "Show password" });
|
||||
await user.click(button);
|
||||
expect(input.type).toEqual("text");
|
||||
|
||||
button = screen.getByRole("button", { name: "Hide password" });
|
||||
await user.click(button);
|
||||
expect(input.type).toEqual("password");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ export * from "./components/Forms/DatePicker";
|
||||
export * from "./components/Forms/Field";
|
||||
export * from "./components/Forms/FileUploader";
|
||||
export * from "./components/Forms/Input";
|
||||
export * from "./components/Forms/Input/InputPassword";
|
||||
export * from "./components/Forms/Radio";
|
||||
export * from "./components/Forms/Select";
|
||||
export * from "./components/Forms/Switch";
|
||||
|
||||
@@ -24,6 +24,12 @@
|
||||
"test": "This is a test: {name}"
|
||||
},
|
||||
"forms": {
|
||||
"input": {
|
||||
"password": {
|
||||
"show_password": "Show password",
|
||||
"hide_password": "Hide password"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"toggle_button_aria_label": "Toggle dropdown",
|
||||
"clear_button_aria_label": "Clear selection",
|
||||
|
||||
Reference in New Issue
Block a user