Skip to content
Open
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
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x, 22.x]
node-version: [20.x, 22.x, 24.x]
react-version: [^18.2.0, ^19.0.0]
steps:
- uses: actions/checkout@v4

Expand All @@ -84,6 +85,7 @@ jobs:
cache: "yarn"

- run: yarn --frozen-lockfile
- run: yarn add react@${{ matrix.react-version }} react-dom@${{ matrix.react-version }} --dev
- run: yarn lint
- run: yarn build
- run: yarn prettier-check
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ storybook-static
terraform

.npmrc
*storybook.log

# Ignore vs folder
.vs
.vs
16 changes: 0 additions & 16 deletions .storybook/main.js

This file was deleted.

23 changes: 23 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { StorybookConfig } from "@storybook/react-webpack5";
import { TsconfigPathsPlugin } from "tsconfig-paths-webpack-plugin";

const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: ["@storybook/addon-webpack5-compiler-swc", "@storybook/addon-docs"],
framework: {
name: "@storybook/react-webpack5",
options: {},
},
webpackFinal: (config) => {
if (config && config.resolve) {
config.resolve.plugins = [
...(config.resolve.plugins || []),
new TsconfigPathsPlugin({
extensions: config.resolve.extensions,
}),
];
}
return config;
},
};
export default config;
2 changes: 0 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- :boom: `AsyncTypeAheadInput` based on MUI Autocomplete component.
- :boom: renamed `AsyncTypeaheadProps` to `AsyncTypeaheadInputProps`.

1. The component is by default **form** controlled. However, updating values using form methods could lead to an unexpected behavior because MUI library requires consistency between options and value. In order to manually to control the input, a ref is exposed which allows to mutually modify input value and form value.
2. `inputRef` is exposed to clear, reset or set new selected options, as mentioned in point 1.
3. `defaultSelected` options is still needed as in previous version.
Expand All @@ -139,7 +138,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7. `onInputChange` exposes the reason why the input-text changes. Check whether reason is `input` for checking only the typing event.

- :boom: `StaticTypeAheadInput` based on MUI Autocomplete component.

1. The component is fully **form** controlled.
2. In order to control input value, use form methods being sure to be consistent between the set value and available options. Therefore, any input ref is exposed anymore.
3. No need to specify a `defaultSelected` option according to point 1. The input automatically select options based on form value.
Expand Down
7 changes: 4 additions & 3 deletions cypress/cypress/component/AutoSubmit/AutoSumbit.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { yupResolver } from "@hookform/resolvers/yup";
import { Form, Input } from "react-hook-form-components";
import * as yup from "yup";
import { generateOptions } from "../../helpers/typeahead";
import { mount } from "cypress/react";

it("radio button multiple autosave works", () => {
const name1 = faker.random.alpha(10);
Expand All @@ -17,7 +18,7 @@ it("radio button multiple autosave works", () => {
const { objectOptions: options1 } = generateOptions(faker.datatype.number({ min: 1, max: 10 }));
const { objectOptions: options2 } = generateOptions(faker.datatype.number({ min: 1, max: 10 }));

cy.mount(
mount(
<>
<Form onSubmit={cy.spy().as("onSubmitSpy1")} resolver={yupResolver(schema1)} autoSubmitConfig={{ wait: 500 }}>
<Input type="radio" label="hello" name={name1} helpText="help" options={options1} />
Expand Down Expand Up @@ -59,7 +60,7 @@ it("radio button autosave only once", () => {

const { objectOptions: options } = generateOptions(faker.datatype.number({ min: 1, max: 10 }));

cy.mount(
mount(
<Form onSubmit={cy.spy().as("onSubmitSpy")} resolver={yupResolver(schema)} autoSubmitConfig={{ wait: 500 }}>
<Input type="radio" label="hello" name={name} helpText="help" options={options} />
</Form>,
Expand All @@ -83,7 +84,7 @@ it("text input autosave only once", () => {

const randomText = faker.random.alphaNumeric(10);

cy.mount(
mount(
<Form onSubmit={cy.spy().as("onSubmitSpy")} resolver={yupResolver(schema)} autoSubmitConfig={{ wait: 500 }}>
<Input type="text" label={name} name={name} />
</Form>,
Expand Down
2 changes: 1 addition & 1 deletion cypress/cypress/component/ClassName/ClassName.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Form, Input, DatePickerInput, AsyncTypeaheadInput, StaticTypeaheadInput, FormattedInput } from "react-hook-form-components";
import { faker } from "@faker-js/faker";
import { fetchMock, generateOptions } from "../../helpers/typeahead";
import { mount } from "cypress/react18";
import { mount } from "cypress/react";

it("input has correct classname", () => {
const name = faker.random.alpha(10);
Expand Down
2 changes: 1 addition & 1 deletion cypress/cypress/component/ColorPicker/ColorPicker.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Form, ColorPickerInput } from "react-hook-form-components";
import { faker } from "@faker-js/faker";
import { mount } from "cypress/react18";
import { mount } from "cypress/react";

it("select correct color by specific format", () => {
const hexName = faker.random.alpha(10);
Expand Down
32 changes: 17 additions & 15 deletions cypress/cypress/component/Datepicker/DatePickerInput.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/* eslint-disable max-lines */
import { DatePickerInput, Form, getUtcTimeZeroDate } from "react-hook-form-components";
import "react-datepicker/dist/react-datepicker.css";
import "react-hook-form-components/styles.css";
import { faker } from "@faker-js/faker";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { faCalendar } from "@fortawesome/free-solid-svg-icons";
import { faCalendar, faClock } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Button, InputGroupText } from "reactstrap";
import { mount } from "cypress/react";

import { SinonSpy } from "cypress/types/sinon";
import { useRef, useEffect, FC } from "react";
Expand All @@ -19,7 +20,7 @@ it("selecting today works", () => {
[name]: yup.date().required(),
});

cy.mount(
mount(
<Form onSubmit={cy.spy().as("onSubmitSpy")} resolver={yupResolver(schema)}>
<DatePickerInput name={name} label={name} />

Expand Down Expand Up @@ -51,7 +52,7 @@ it("setting intial value as iso string works", () => {
console.log(JSON.stringify(values));
};

cy.mount(
mount(
<Form
onSubmit={cy.spy(x).as("onSubmitSpy")}
resolver={yupResolver(schema)}
Expand Down Expand Up @@ -80,7 +81,7 @@ it("setting intial value as date object works", () => {
[name]: yup.date().required(),
});

cy.mount(
mount(
<Form
onSubmit={cy.spy().as("onSubmitSpy")}
resolver={yupResolver(schema)}
Expand All @@ -104,7 +105,7 @@ it("setting intial value as date object works", () => {
it("is disabled", () => {
const name = faker.random.word();

cy.mount(
mount(
<Form
onSubmit={() => {
// Do nothing
Expand All @@ -123,7 +124,7 @@ it("contains calendar icon if provided in DateInput", () => {
[name]: yup.date(),
});

cy.mount(
mount(
<Form
onSubmit={() => {
// Nothing to do
Expand All @@ -133,21 +134,22 @@ it("contains calendar icon if provided in DateInput", () => {
<DatePickerInput
name={name}
label={name}
addonLeft={
addonRight={
<InputGroupText>
<FontAwesomeIcon icon={faCalendar} />
</InputGroupText>
}
addonRight={
addonLeft={
<InputGroupText>
<FontAwesomeIcon icon={faCalendar} />
<FontAwesomeIcon icon={faClock} />
</InputGroupText>
}
/>
</Form>,
);

cy.get(`label[for=${name}]`).parent().find("svg[data-icon=calendar]");
cy.get(`label[for=${name}]`).parent().find("svg[data-icon=clock]");
});

it("not contains calendar icon if not provided in DateInput", () => {
Expand All @@ -156,7 +158,7 @@ it("not contains calendar icon if not provided in DateInput", () => {
[name]: yup.date(),
});

cy.mount(
mount(
<Form
onSubmit={() => {
// Nothing to do
Expand Down Expand Up @@ -186,7 +188,7 @@ it("passing an IANA timezone works", () => {
const fixedDate = new Date(isoInputDate);
const inputJsonString = JSON.stringify(fixedDate);

cy.mount(
mount(
<Form<FormFields>
defaultValues={{
[name]: fixedDate,
Expand Down Expand Up @@ -225,7 +227,7 @@ it("passing the ref works", () => {
});

const DatePickerWithRef: FC = () => {
const ref = useRef<ReactDatePickers<never, undefined>>(null);
const ref = useRef<ReactDatePickers>(null);

useEffect(() => {
if (ref && ref.current) {
Expand All @@ -248,7 +250,7 @@ it("passing the ref works", () => {
);
};

cy.mount(<DatePickerWithRef />);
mount(<DatePickerWithRef />);

cy.get(".react-datepicker-popper").should("be.visible");
});
Expand All @@ -259,7 +261,7 @@ it("addon works as a function (with onClick)", () => {
[name]: yup.date(),
});

cy.mount(
mount(
<Form
onSubmit={() => {
// Nothing to do
Expand Down
2 changes: 1 addition & 1 deletion cypress/cypress/component/Disabled/Disabled.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
useFormContext,
} from "react-hook-form-components";
import { Input as ReactstrapInput, Label } from "reactstrap";
import { mount } from "cypress/react18";
import { mount } from "cypress/react";

it("disable all fields when readonly attribute is set", () => {
const inputName = faker.random.alpha(10);
Expand Down
5 changes: 3 additions & 2 deletions cypress/cypress/component/Form/Form.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Form, FormattedInput } from "react-hook-form-components";
import { NumericFormatProps, numericFormatter } from "react-number-format";
import { useRef } from "react";
import * as yup from "yup";
import { mount } from "cypress/react";

const numericFormat: NumericFormatProps = {
thousandSeparator: faker.random.alpha(),
Expand All @@ -30,7 +31,7 @@ it("submitting through the ref works", () => {
</Form>
);
};
cy.mount(<FormWithRef />);
mount(<FormWithRef />);

cy.contains("label", name).click();
cy.focused().type(randomNumber.toString());
Expand Down Expand Up @@ -60,7 +61,7 @@ it("submitting through the ref checks for yup errors", () => {
</Form>
);
};
cy.mount(<FormWithRef />);
mount(<FormWithRef />);

cy.contains("label", name).click();
cy.focused().type(randomNumber.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { faker } from "@faker-js/faker";
import { yupResolver } from "@hookform/resolvers/yup";
import { Form, Input, StaticTypeaheadInput, AsyncTypeaheadInput, DatePickerInput, FormattedInput } from "react-hook-form-components";
import * as yup from "yup";
import { mount } from "cypress/react18";
import { mount } from "cypress/react";

it("adding * character if string label works", () => {
const name = faker.random.alpha(10);
Expand Down
15 changes: 8 additions & 7 deletions cypress/cypress/component/Formatted/NumericFormat.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { yupResolver } from "@hookform/resolvers/yup";
import { Form, FormattedInput } from "react-hook-form-components";
import { NumericFormatProps, numericFormatter } from "react-number-format";
import * as yup from "yup";
import { mount } from "cypress/react";

const numericFormat: NumericFormatProps = {
thousandSeparator: faker.random.alpha(),
Expand All @@ -18,7 +19,7 @@ it("numeric format works", () => {
min: 10_000,
});

cy.mount(
mount(
<Form onSubmit={cy.spy().as("onSubmitSpy")} resolver={yupResolver(schema)}>
<FormattedInput name={name} label={name} numericFormat={numericFormat} />

Expand All @@ -36,7 +37,7 @@ it("numeric format works", () => {
it("is disabled", () => {
const name = faker.random.word();

cy.mount(
mount(
<Form
onSubmit={() => {
// Do nothing
Expand All @@ -55,7 +56,7 @@ it("auto mark on focus", () => {
min: 10_000_000,
});

cy.mount(
mount(
<Form
defaultValues={{ [name]: randomNumber }}
onSubmit={() => {
Expand Down Expand Up @@ -94,7 +95,7 @@ it("validation works", () => {
min: 10_000,
});

cy.mount(
mount(
<Form onSubmit={cy.spy().as("onSubmitSpy")} resolver={yupResolver(schema)}>
<FormattedInput name={name} label={name} numericFormat={numericFormat} />

Expand Down Expand Up @@ -132,7 +133,7 @@ it("validation works for nested fields", () => {
min: 10_000,
});

cy.mount(
mount(
<Form onSubmit={cy.spy().as("onSubmitSpy")} resolver={yupResolver(schema)}>
<FormattedInput name={name} label={name} numericFormat={numericFormat} />

Expand All @@ -155,7 +156,7 @@ it("validation works for nested fields", () => {
it("style is correcly applied", () => {
const name = faker.random.word();
const style = "background-color: red;";
cy.mount(
mount(
<Form
onSubmit={() => {
// Do nothing
Expand All @@ -172,7 +173,7 @@ it("has placeholder", () => {
const name = faker.random.word();
const placeholder = faker.random.words(5);

cy.mount(
mount(
<Form
onSubmit={() => {
// Do nothing
Expand Down
Loading
Loading