Skip to content

Commit

Permalink
docs: add examples, fix imports in stories
Browse files Browse the repository at this point in the history
  • Loading branch information
te6-in committed Jan 11, 2025
1 parent ad31322 commit d3182f7
Show file tree
Hide file tree
Showing 18 changed files with 590 additions and 91 deletions.
66 changes: 66 additions & 0 deletions docs/components/example/checkbox-react-hook-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"use client";

import { useController, useForm } from "react-hook-form";
import { Checkbox } from "seed-design/ui/checkbox";
import { ActionButton } from "seed-design/ui/action-button";
import { useCallback, type FormEvent } from "react";

const POSSIBLE_FRUIT_VALUES = ["apple", "melon", "mango"] as const;

type FormValues = Record<(typeof POSSIBLE_FRUIT_VALUES)[number], boolean>;

export default function CheckboxReactHookForm() {
const { handleSubmit, reset, setValue, control } = useForm<FormValues>({
defaultValues: {
apple: false,
melon: true,
mango: false,
},
});

const onValid = useCallback((data: FormValues) => {
window.alert(JSON.stringify(data, null, 2));
}, []);

const onReset = useCallback(
(event: FormEvent) => {
event.preventDefault();
reset();
},
[reset],
);

return (
<form className="flex flex-col gap-3 w-96" onSubmit={handleSubmit(onValid)} onReset={onReset}>
<div className="flex flex-col">
{POSSIBLE_FRUIT_VALUES.map((name) => {
const {
field: { value, ...restProps },
fieldState: { invalid },
} = useController({ name, control });

return (
<Checkbox
key={name}
label={name}
checked={value}
inputProps={restProps}
invalid={invalid}
/>
);
})}
</div>
<div className="flex gap-2">
<ActionButton type="submit" className="grow">
제출
</ActionButton>
<ActionButton type="button" variant="neutralWeak" onClick={() => setValue("mango", true)}>
mango 선택
</ActionButton>
<ActionButton type="reset" variant="neutralWeak">
초기화
</ActionButton>
</div>
</form>
);
}
117 changes: 117 additions & 0 deletions docs/components/example/multiline-text-field-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"use client";

import { ActionButton } from "seed-design/ui/action-button";
import { TextField, TextFieldTextarea } from "seed-design/ui/text-field";
import { useState, useCallback, type FormEvent } from "react";

interface FormValues {
bio: string;
address: string;
}

type FieldErrors = Record<keyof FormValues, string | null>;

export default function MultilineTextFieldForm() {
const [formValues, setFormValues] = useState<FormValues>({
bio: "",
address: "",
});

const [fieldErrors, setFieldStates] = useState<FieldErrors>({
bio: null,
address: null,
});

const validateForm = useCallback((): boolean => {
let isValid = true;

const newFieldErrors: FieldErrors = {
bio: null,
address: null,
};

// Name validation
if (!formValues.bio) {
newFieldErrors.bio = "필수 입력 항목입니다";
isValid = false;
}

if (!formValues.address.startsWith("대한민국")) {
newFieldErrors.address = "대한민국으로 시작해주세요";
isValid = false;
}

if (!formValues.address) {
newFieldErrors.address = "필수 입력 항목입니다";
isValid = false;
}

setFieldStates(newFieldErrors);

return isValid;
}, [formValues]);

const handleSubmit = useCallback(
(event: FormEvent) => {
event.preventDefault();

if (validateForm()) {
window.alert(JSON.stringify(formValues, null, 2));
}
},
[formValues, validateForm],
);

const handleReset = useCallback((event: FormEvent) => {
event.preventDefault();

setFormValues({ bio: "", address: "" });
setFieldStates({ bio: null, address: null });
}, []);

const handleNameChange = (value: string) => {
setFormValues((prev) => ({ ...prev, bio: value }));
setFieldStates((prev) => ({ ...prev, name: null }));
};

const handleAddressChange = (value: string) => {
setFormValues((prev) => ({ ...prev, address: value }));
setFieldStates((prev) => ({ ...prev, address: null }));
};

return (
<form className="grid grid-cols-2 gap-3 w-full" onSubmit={handleSubmit} onReset={handleReset}>
<TextField
label="자기소개"
indicator="(필수)"
description="자기소개를 써주세요"
required
value={formValues.bio}
onValueChange={({ value }) => handleNameChange(value)}
{...(fieldErrors.bio && { invalid: true, errorMessage: fieldErrors.bio })}
>
<TextFieldTextarea placeholder="저는…" />
</TextField>
<TextField
label="주소"
indicator="(필수)"
description="주소를 써주세요"
maxGraphemeCount={30}
required
value={formValues.address}
onValueChange={({ slicedValue }) => handleAddressChange(slicedValue)}
{...(fieldErrors.address && { invalid: true, errorMessage: fieldErrors.address })}
>
<TextFieldTextarea placeholder="대한민국" />
</TextField>
<div className="col-span-2 flex gap-2">
<ActionButton type="submit" className="grow">
제출
</ActionButton>
<ActionButton type="reset" variant="neutralWeak">
초기화
</ActionButton>
</div>
</form>
);
}
11 changes: 3 additions & 8 deletions docs/components/example/multiline-text-field-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,9 @@ import { useState } from "react";
import { TextField, TextFieldTextarea } from "seed-design/ui/text-field";

export default function MultilineTextFieldPreview() {
const [value, setValue] = useState("");

return (
<div className="flex flex-col items-center w-full">
<TextField value={value} onValueChange={({ value }) => setValue(value)}>
<TextFieldTextarea autoFocus />
</TextField>
<p className="text-center">현재 값: {value}</p>
</div>
<TextField>
<TextFieldTextarea autoFocus />
</TextField>
);
}
96 changes: 65 additions & 31 deletions docs/components/example/multiline-text-field-react-hook-form.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,100 @@
"use client";

import { ActionButton } from "seed-design/ui/action-button";
import { IconHouseLine } from "@daangn/react-monochrome-icon";
import { useForm } from "react-hook-form";
import { useController, useForm } from "react-hook-form";
import { TextField, TextFieldTextarea } from "seed-design/ui/text-field";
import { useCallback, type FormEvent, type KeyboardEvent } from "react";

interface FormValues {
bio: string;
address: string;
}

export default function MultilineTextFieldReactHookForm() {
const { handleSubmit, reset, control } = useForm<FormValues>({
defaultValues: {
bio: "",
address: "",
},
shouldFocusError: true,
});

const { field: bioField, fieldState: bioFieldState } = useController({
name: "bio",
control,
rules: {
required: "필수 입력 항목입니다",
},
});
const {
register,
handleSubmit,
clearErrors,
formState: { errors },
} = useForm<FormValues>();
field: { onChange: addressOnChange, ...addressField },
fieldState: addressFieldState,
} = useController({
name: "address",
control,
rules: {
required: "필수 입력 항목입니다",
pattern: { value: /^/, message: "대한민국으로 시작해주세요" },
},
});

const onValid = (data: FormValues) => {
const onValid = useCallback((data: FormValues) => {
window.alert(JSON.stringify(data, null, 2));
};
}, []);

const onReset = useCallback(
(event: FormEvent) => {
event.preventDefault();
reset();
},
[reset],
);

const onMetaReturn = useCallback(
(event: KeyboardEvent) => {
if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
event.preventDefault();
handleSubmit(onValid)();
}
},
[handleSubmit, onValid],
);

return (
<form className="grid grid-cols-2 gap-3 w-full" onSubmit={handleSubmit(onValid)}>
<form
className="grid grid-cols-2 gap-3 w-full"
onSubmit={handleSubmit(onValid)}
onReset={onReset}
>
<TextField
{...register("bio", { required: "필수 입력 항목입니다" })}
label="자기소개"
description="자기소개를 써주세요"
invalid={!!errors.bio}
errorMessage={errors.bio?.message}
indicator="(필수)"
description="자기소개를 써주세요"
invalid={bioFieldState.invalid}
errorMessage={bioFieldState.error?.message}
required
{...bioField}
>
<TextFieldTextarea placeholder="저는…" />
<TextFieldTextarea placeholder="저는…" onKeyDown={onMetaReturn} />
</TextField>
<TextField
{...register("address", {
required: "필수 입력 항목입니다",
pattern: {
value: /^/,
message: "대한민국으로 시작해주세요",
},
})}
label="주소"
description="주소를 써주세요"
invalid={!!errors.address}
errorMessage={errors.address?.message}
indicator="(필수)"
description="주소를 써주세요"
invalid={addressFieldState.invalid}
errorMessage={addressFieldState.error?.message}
maxGraphemeCount={30}
onValueChange={({ slicedValue }) => addressOnChange(slicedValue)}
required
{...addressField}
>
<TextFieldTextarea placeholder="대한민국 서울특별시 은평구" />
<TextFieldTextarea placeholder="대한민국" onKeyDown={onMetaReturn} />
</TextField>
<div className="col-span-2 flex gap-2">
<ActionButton type="submit" className="grow">
제출
</ActionButton>
<ActionButton
type="reset"
onClick={() => clearErrors(["bio", "address"])}
variant="neutralWeak"
>
<ActionButton type="reset" variant="neutralWeak">
초기화
</ActionButton>
</div>
Expand Down
9 changes: 3 additions & 6 deletions docs/components/example/select-box-check-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ import { SelectBoxCheck, SelectBoxCheckGroup } from "seed-design/ui/select-box";
export default function SelectBoxCheckPreview() {
return (
<SelectBoxCheckGroup>
<SelectBoxCheck label="Culpa" defaultChecked />
<SelectBoxCheck
label="Voluptate"
description="Elit cupidatat dolore fugiat enim veniam culpa."
/>
<SelectBoxCheck label="Eiusmod" />
<SelectBoxCheck label="Apple" defaultChecked />
<SelectBoxCheck label="Melon" description="Elit cupidatat dolore fugiat enim veniam culpa." />
<SelectBoxCheck label="Mango" />
</SelectBoxCheckGroup>
);
}
Loading

0 comments on commit d3182f7

Please sign in to comment.