diff --git a/index.d.ts b/index.d.ts index 4eae448b9..ef85c2840 100644 --- a/index.d.ts +++ b/index.d.ts @@ -287,6 +287,7 @@ export interface InputProps labelProps?: LabelProps; maxLength?: number; unlimitedChars: boolean; + rejectCharsRegex?: RegExp; } export type LabelProps = { diff --git a/src/components/Input.jsx b/src/components/Input.jsx index 03a115022..002775bc8 100644 --- a/src/components/Input.jsx +++ b/src/components/Input.jsx @@ -3,6 +3,7 @@ import React, { useState, forwardRef } from "react"; import { useId } from "@reach/auto-id"; import classnames from "classnames"; import PropTypes from "prop-types"; +import { replace } from "ramda"; import { hyphenize } from "utils"; @@ -29,6 +30,7 @@ const Input = forwardRef( maxLength, unlimitedChars = false, labelProps, + rejectCharsRegex, ...otherProps }, ref @@ -50,6 +52,14 @@ const Input = forwardRef( const onChange = otherProps.onChange || onChangeInternal; const isMaxLengthPresent = !!maxLength || maxLength === 0; + const handleRegexChange = e => { + const globalRegex = new RegExp(rejectCharsRegex, "g"); + e.target.value = replace(globalRegex, "", e.target.value); + onChange(e); + }; + + const handleChange = rejectCharsRegex ? handleRegexChange : onChange; + return (
@@ -101,7 +111,7 @@ const Input = forwardRef( {...(isMaxLengthPresent && !unlimitedChars && { maxLength })} {...otherProps} value={value} - onChange={onChange} + onChange={handleChange} /> {suffix &&
{suffix}
}
@@ -197,6 +207,11 @@ Input.propTypes = { * To specify whether the Input field is required or not. */ required: PropTypes.bool, + /** + * To specify a regex to be matched against the user input. Any character that matches it + * cannot be input by the user. It will also prevent such characters from being pasted into the input. + */ + rejectCharsRegex: PropTypes.instanceOf(RegExp), }; export default Input; diff --git a/stories/Components/Input.stories.jsx b/stories/Components/Input.stories.jsx index c738d88d1..874a1f67f 100644 --- a/stories/Components/Input.stories.jsx +++ b/stories/Components/Input.stories.jsx @@ -22,6 +22,7 @@ const metadata = { url: "https://www.figma.com/file/zhdsnPzXzr264x1WUeVdmA/02-Components?node-id=104%3A11", }, }, + argTypes: { rejectCharsRegex: { control: "text" } }, }; const Template = args => ; @@ -177,6 +178,20 @@ FormikInputStory.parameters = { }, }; +const RejectCharsInputStory = args => ( + +); + +RejectCharsInputStory.storyName = "Reject specific characters"; +RejectCharsInputStory.parameters = { + docs: { + description: { + story: `The prop \`rejectCharsRegex\` will accept a regex and any character that matches it + cannot be input by the user. It will also prevent such characters from being pasted into the input.`, + }, + }, +}; + export { Default, Sizes, @@ -189,6 +204,7 @@ export { SearchInput, InputWithMaxLength, FormikInputStory, + RejectCharsInputStory, }; export default metadata; diff --git a/tests/Input.test.jsx b/tests/Input.test.jsx index 742fbdbab..519542228 100644 --- a/tests/Input.test.jsx +++ b/tests/Input.test.jsx @@ -27,6 +27,19 @@ describe("Input", () => { expect(onChange).toHaveBeenCalledTimes(4); }); + it("should not show matched regex value", () => { + const { getByLabelText } = render( + + ); + const inputField = getByLabelText("Input Label"); + userEvent.type(inputField, "12345"); + expect(inputField).not.toHaveValue("12345"); + + userEvent.type(inputField, "abc123"); + expect(inputField).toHaveValue("abc"); + expect(inputField).not.toHaveValue("123"); + }); + it("should display error message", () => { const { getByText } = render(); expect(getByText("Error message")).toBeInTheDocument(); diff --git a/tests/formik/Input.test.jsx b/tests/formik/Input.test.jsx index e504e159c..5a5a45808 100644 --- a/tests/formik/Input.test.jsx +++ b/tests/formik/Input.test.jsx @@ -16,11 +16,7 @@ const TestForm = ({ onSubmit }) => {

Sign Up

{ onSubmit: handleSubmit, }} > - + @@ -44,12 +44,7 @@ const TestForm = ({ onSubmit }) => { describe("formik/Input", () => { it("should render without error", () => { render( - {}, - }} - > + {} }}>
); @@ -77,8 +72,19 @@ describe("formik/Input", () => { render(); userEvent.type(screen.getByLabelText("Email"), "john.doemail.com"); userEvent.click(screen.getByText("Submit")); - await waitFor(() => - expect(screen.getByText("Invalid email address")).toBeInTheDocument() - ); + expect( + await screen.findByText("First name is required") + ).toBeInTheDocument(); + }); + + it("should display validation error when string having only rejected characters is provided", async () => { + const onSubmit = jest.fn(); + render(); + userEvent.type(screen.getByLabelText("First Name"), "123"); + userEvent.click(screen.getByText("Submit")); + + expect( + await screen.findByText("First name is required") + ).toBeInTheDocument(); }); });