Skip to content

Commit

Permalink
Introduce react hook form macaw bindings (#469)
Browse files Browse the repository at this point in the history
* Add components and update the configuration

* Export components to be used in apps
  • Loading branch information
krzysztofwolski authored May 22, 2023
1 parent ce8d9de commit 8a339fc
Show file tree
Hide file tree
Showing 20 changed files with 7,082 additions and 2,213 deletions.
5 changes: 5 additions & 0 deletions .changeset/gentle-poems-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@saleor/react-hook-form-macaw": patch
---

Introduction of the library integrating Macaw with the React Hook Forms.
15 changes: 15 additions & 0 deletions packages/react-hook-form-macaw/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"root": true,
"extends": ["saleor"],
"overrides": [
{
"files": ["*stories.tsx"],
"rules": {
// Stories require default export for storybook
"import/no-default-export": "off",
// Story wrapper is an exception to the rule
"react-hooks/rules-of-hooks": "off"
}
}
]
}
2 changes: 2 additions & 0 deletions packages/react-hook-form-macaw/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ignore storybook build artifacts
storybook-static
29 changes: 29 additions & 0 deletions packages/react-hook-form-macaw/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { mergeConfig } from "vite";

export default {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/react-vite",
options: {},
},
staticDirs: [
{
from: "../public",
to: "/assets",
},
"./public",
],
features: {
storyStoreV7: true,
},
async viteFinal(config) {
return mergeConfig(config, {
plugins: [require("@vanilla-extract/vite-plugin").vanillaExtractPlugin()],
});
},
};
47 changes: 47 additions & 0 deletions packages/react-hook-form-macaw/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import "@saleor/macaw-ui/next/style";
import "./styles.css";

import React from "react";
import { Preview } from "@storybook/react";
import { Box, DefaultTheme, ThemeProvider, useTheme } from "@saleor/macaw-ui/next";

const ThemeSwitcher = ({ children, theme }) => {
const { setTheme } = useTheme();
React.useEffect(() => {
setTheme(theme);
}, [theme]);

return (
<Box display="flex" justifyContent="center" __backgroundColor="white">
{children}
</Box>
);
};

const themes: DefaultTheme[] = ["defaultLight", "defaultDark"];

const preview: Preview = {
globalTypes: {
theme: {
name: "Theme",
description: "Global theme for components",
defaultValue: themes[0],
toolbar: {
icon: "mirror",
items: themes,
dynamicTitle: true,
},
},
},
decorators: [
(Story, context) => (
<ThemeProvider defaultTheme={context.globals.theme}>
<ThemeSwitcher theme={context.globals.theme}>
<Story />
</ThemeSwitcher>
</ThemeProvider>
),
],
};

export default preview;
4 changes: 4 additions & 0 deletions packages/react-hook-form-macaw/.storybook/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.sbdocs-wrapper {
width: 100%;
}

1 change: 1 addition & 0 deletions packages/react-hook-form-macaw/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @saleor/react-hook-form-macaw
1 change: 1 addition & 0 deletions packages/react-hook-form-macaw/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# react-hook-form-macaw
3 changes: 3 additions & 0 deletions packages/react-hook-form-macaw/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./src/components/Input";
export * from "./src/components/Combobox";
export * from "./src/components/Multiselect";
5 changes: 5 additions & 0 deletions packages/react-hook-form-macaw/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
38 changes: 38 additions & 0 deletions packages/react-hook-form-macaw/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "@saleor/react-hook-form-macaw",
"version": "0.0.1",
"devDependencies": {
"@babel/core": "^7.21.8",
"@saleor/macaw-ui": "0.8.0-pre.84",
"@storybook/addon-actions": "^7.0.12",
"@storybook/addon-essentials": "^7.0.12",
"@storybook/addon-interactions": "^7.0.12",
"@storybook/addon-links": "^7.0.12",
"@storybook/blocks": "^7.0.12",
"@storybook/react": "^7.0.12",
"@storybook/react-vite": "^7.0.12",
"@storybook/testing-library": "^0.0.14-next.2",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@vanilla-extract/vite-plugin": "^3.8.1",
"eslint-config-saleor": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.9",
"storybook": "^7.0.12",
"typescript": "^5.0.4",
"vite": "^4.3.6",
"webpack": "^5.82.1"
},
"peerDependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"scripts": {
"lint:fix": "eslint --fix .",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"start-storybook-build": "pnpm dlx http-server ./storybook-static"
},
"main": "index.ts"
}
70 changes: 70 additions & 0 deletions packages/react-hook-form-macaw/src/components/Combobox.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useEffect } from "react";
import { Meta, StoryObj } from "@storybook/react";
import { Combobox } from "./Combobox";
import { useForm } from "react-hook-form";
import { action } from "@storybook/addon-actions";

const meta: Meta<typeof Combobox> = {
title: "Components / Combobox",
component: Combobox,
};

export default meta;

type Story = StoryObj<typeof Combobox>;

const ComboboxTemplate: Story = {
render: (args) => {
const { control, watch, setError } = useForm();
const name = "comboboxField";

if (args.error) {
setError(name, { message: "Error message" });
}

useEffect(() => {
const subscription = watch((value) => action("[React Hooks Form] Form value changed")(value));

return () => subscription.unsubscribe();
}, [watch]);

return (
<Combobox
{...args}
control={control}
label="Combobox field"
name={name}
options={[
{ value: "1", label: "One" },
{ value: "2", label: "Two" },
{ value: "3", label: "Three" },
]}
/>
);
},
};

export const Default: Story = {
...ComboboxTemplate,
};

export const Errored: Story = {
...ComboboxTemplate,
args: {
error: true,
},
};

export const Disabled: Story = {
...ComboboxTemplate,
args: {
disabled: true,
},
};

export const WithHelpText: Story = {
...ComboboxTemplate,
args: {
helperText: "Helper text",
},
};
36 changes: 36 additions & 0 deletions packages/react-hook-form-macaw/src/components/Combobox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Combobox as $Combobox, type ComboboxProps as $ComboboxProps } from "@saleor/macaw-ui/next";
import { Control, Controller, FieldPath, FieldValues } from "react-hook-form";

export type ComboboxProps<T extends FieldValues = FieldValues> = Omit<$ComboboxProps, "name"> & {
name: FieldPath<T>;
control: Control<T>;
};

export function Combobox<TFieldValues extends FieldValues = FieldValues>({
type,
required,
name,
control,
options,
...rest
}: ComboboxProps<TFieldValues>): JSX.Element {
return (
<Controller
name={name}
control={control}
render={({ field: { value, ...field }, fieldState: { error } }) => (
<$Combobox
{...rest}
{...field}
options={options}
value={value || ""}
name={name}
required={required}
type={type}
error={!!error}
helperText={rest.helperText}
/>
)}
/>
);
}
58 changes: 58 additions & 0 deletions packages/react-hook-form-macaw/src/components/Input.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useEffect } from "react";
import { Meta, StoryObj } from "@storybook/react";
import { Input } from "./Input";
import { useForm } from "react-hook-form";
import { action } from "@storybook/addon-actions";

const meta: Meta<typeof Input> = {
title: "Components / Input",
component: Input,
};

export default meta;

type Story = StoryObj<typeof Input>;

const InputTemplate: Story = {
render: (args) => {
const { control, watch, setError } = useForm();
const name = "inputField";

if (args.error) {
setError(name, { message: "Error message" });
}

useEffect(() => {
const subscription = watch((value) => action("[React Hooks Form] Form value changed")(value));

return () => subscription.unsubscribe();
}, [watch]);

return <Input {...args} control={control} label="Input field" name={name} />;
},
};

export const Default: Story = {
...InputTemplate,
};

export const Errored: Story = {
...InputTemplate,
args: {
error: true,
},
};

export const Disabled: Story = {
...InputTemplate,
args: {
disabled: true,
},
};

export const WithHelpText: Story = {
...InputTemplate,
args: {
helperText: "Helper text",
},
};
36 changes: 36 additions & 0 deletions packages/react-hook-form-macaw/src/components/Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Input as $Input, type InputProps as $InputProps } from "@saleor/macaw-ui/next";
import { Control, Controller, FieldPath, FieldValues } from "react-hook-form";

export type TextFieldElementProps<T extends FieldValues = FieldValues> = Omit<
$InputProps,
"name"
> & {
name: FieldPath<T>;
control: Control<T>;
};

export function Input<TFieldValues extends FieldValues = FieldValues>({
type,
required,
name,
control,
...rest
}: TextFieldElementProps<TFieldValues>): JSX.Element {
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<$Input
{...rest}
{...field}
name={name}
required={required}
type={type}
error={!!error}
helperText={rest.helperText}
/>
)}
/>
);
}
Loading

0 comments on commit 8a339fc

Please sign in to comment.