Skip to content

Commit

Permalink
[Feature] Toast 컴포넌트 (#157)
Browse files Browse the repository at this point in the history
* feat: 토스트 컴포넌트 구현

* feat: 토스트 컴포넌트 스토리북 작성

* chore: package.json 및 rollup 추가

* chore: displayName 추가 및 export 형식 변경

* design: 두 줄인 경우 줄바꿈

* fix: type 옵셔널로 처리

* feat: 토스트 컴포넌트를 사라지게 하는 효과 추가

* feat: 아이콘 클릭 시 호출할 함수 추가, 배경 추가

* feat: 아이콘 클릭 함수 스토리 추가, state 활용 추가

* feat: 토스트 위치조정 및 배경 스타일 prop 추가

* feat: toast 함수로 렌더링 가능하도록 로직 추가

* fix: 토스트가 여러개일 때 레이아웃 조정

* chore: useToast 빌드 포함

* fix: color contrast 검사 통과

* feat: 토스트 duration 받을 수 있도록 추가

* feat: overlay zIndex 토큰 추가

* refactor: zIndex 토큰 사용하도록 변경

* feat: 공통 타입 파일 내보내도록 스크립트 수정

* refactor: TypeIconComponent 네이밍 변경 및 공통 prop 빼기

* fix: 토스트 트리거 존재하는 경우 스토리 추가, 토스트가 없을 때 오버레이 공간 차지하지 않도록 조건부 렌더링

* docs: ToastProvider 내용 안내

* docs: 스토리북 radio 옵션 추가 및 기본 컴포넌트 변경

* feat: 토스트 컴포넌트 제거 이후 실행되는 함수 추가

* feat: docs에도overlay 토큰 추가

* fix: type -> rightIcon 네이밍 변경

* docs: 스토리북 설명 및 타이틀 변경

* fix: 스크립트 content 변수 네이밍 변경

* fix: icon prop 불리언으로 수정

* refactor: 현재 시각 대신 uuid로 토스트 id 지정

* feat: 정규식 입력 시 해당 정규식을 만족하는 아이디를 가진 토스트만 보여주기

* chore: changeset 작성
  • Loading branch information
hamo-o authored Oct 12, 2024
1 parent 3682ddd commit 185475a
Show file tree
Hide file tree
Showing 22 changed files with 483 additions and 281 deletions.
6 changes: 6 additions & 0 deletions .changeset/green-feet-teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"wowds-tokens": patch
"wowds-theme": patch
---

zIndex 토큰을 추가합니다.
5 changes: 5 additions & 0 deletions .changeset/pretty-cooks-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wowds-icons": patch
---

Icon 공통 타입을 내보내기합니다.
5 changes: 5 additions & 0 deletions .changeset/seven-eggs-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wowds-ui": patch
---

Toast 컴포넌트를 추가합니다.
4 changes: 4 additions & 0 deletions apps/wow-docs/styled-system/tokens/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ const tokens = {
value: 10,
variable: "var(--z-index-dropdown)",
},
"zIndex.overlay": {
value: 9999,
variable: "var(--z-index-overlay)",
},
"shadows.blue": {
value: "0px 4px 8px 0px rgba(16, 43, 74, 0.2)",
variable: "var(--shadows-blue)",
Expand Down
3 changes: 2 additions & 1 deletion apps/wow-docs/styled-system/tokens/tokens.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export type Token =
| "borderWidths.button"
| "borderWidths.arrow"
| "zIndex.dropdown"
| "zIndex.overlay"
| "shadows.blue"
| "shadows.mono"
| "breakpoints.xs"
Expand Down Expand Up @@ -323,7 +324,7 @@ export type RadiusToken = "sm" | "md" | "full";

export type BorderWidthToken = "button" | "arrow";

export type ZIndexToken = "dropdown";
export type ZIndexToken = "dropdown" | "overlay";

export type ShadowToken = "blue" | "mono";

Expand Down
13 changes: 9 additions & 4 deletions packages/scripts/generateBuildConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,22 @@ const excludedComponents = [
"CollectionContext",
"DropDownOptionList",
"pickerComponents",
"ToastContext",
];

// 추가할 컴포넌트 목록
const includedComponents = ["useToast"];

const getFilteredComponentFiles = async (directoryPath: string) => {
const files = await fs.readdir(directoryPath, { recursive: true });

return files.filter(
(file) =>
file.endsWith(".tsx") &&
!file.includes("test") &&
!file.includes("stories") &&
!excludedComponents.some((excluded) => file.includes(excluded))
(file.endsWith(".tsx") &&
!file.includes("test") &&
!file.includes("stories") &&
!excludedComponents.some((excluded) => file.includes(excluded))) ||
includedComponents.some((included) => file.includes(included))
);
};

Expand Down
4 changes: 3 additions & 1 deletion packages/scripts/generateReactComponentFromSvg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ const generateExportFile = async (components: string[]) => {
)
.join("\n");

await fs.writeFile(EXPORT_FILE_PATH, exportFileContent);
const resolvedExportFileContent = `export * from "../types/Icon.ts";\n${exportFileContent}`;

await fs.writeFile(EXPORT_FILE_PATH, resolvedExportFileContent);
};

(async () => {
Expand Down
1 change: 0 additions & 1 deletion packages/wow-icons/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"package.json"
],
"type": "module",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/component/index.d.ts",
Expand Down
1 change: 1 addition & 0 deletions packages/wow-icons/src/component/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "../types/Icon.ts";
export { default as BlueAvatar } from "./BlueAvatar.tsx";
export { default as Calendar } from "./Calendar.tsx";
export { default as Check } from "./Check.tsx";
Expand Down
3 changes: 3 additions & 0 deletions packages/wow-theme/src/tokens/zIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ export const zIndex = defineTokens.zIndex({
dropdown: {
value: wowZIndex.dropdown,
},
overlay: {
value: wowZIndex.overlay,
},
});
1 change: 1 addition & 0 deletions packages/wow-tokens/src/zIndex.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const dropdown = 10;
export const overlay = 9999;
19 changes: 18 additions & 1 deletion packages/wow-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@
"type": "module",
"exports": {
"./styles.css": "./dist/styles.css",
"./ToastProvider": {
"types": "./dist/components/Toast/ToastProvider.d.ts",
"require": "./dist/ToastProvider.cjs",
"import": "./dist/ToastProvider.js"
},
"./Toast": {
"types": "./dist/components/Toast/index.d.ts",
"require": "./dist/Toast.cjs",
"import": "./dist/Toast.js"
},
"./useToast": {
"types": "./dist/components/Toast/useToast.d.ts",
"require": "./dist/useToast.cjs",
"import": "./dist/useToast.js"
},
"./TextField": {
"types": "./dist/components/TextField/index.d.ts",
"require": "./dist/TextField.cjs",
Expand Down Expand Up @@ -179,12 +194,14 @@
"plop": "^4.0.1",
"rollup-plugin-peer-deps-external": "^2.2.4",
"storybook": "^8.1.9",
"typescript": "^5.3.3"
"typescript": "^5.3.3",
"@types/uuid": "^10.0.0"
},
"dependencies": {
"clsx": "^2.1.1",
"lottie-react": "^2.4.0",
"react-day-picker": "^9.0.8",
"uuid": "^10.0.0",
"wowds-icons": "workspace:^"
},
"peerDependencies": {
Expand Down
3 changes: 3 additions & 0 deletions packages/wow-ui/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ process.env.BABEL_ENV = "production";

export default {
input: {
ToastProvider: "./src/components/Toast/ToastProvider",
Toast: "./src/components/Toast",
useToast: "./src/components/Toast/useToast",
TextField: "./src/components/TextField",
TextButton: "./src/components/TextButton",
Tag: "./src/components/Tag",
Expand Down
173 changes: 173 additions & 0 deletions packages/wow-ui/src/components/Toast/Toast.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useEffect } from "react";
import { Warn } from "wowds-icons";

import Button from "@/components/Button";

import Toast from ".";
import ToastProvider from "./ToastProvider";
import useToast from "./useToast";

const meta: Meta<typeof Toast> = {
title: "UI/Toast",
component: Toast,
tags: ["autodocs"],
parameters: {
componentSubtitle: "Toast 컴포넌트",
a11y: {
config: {
rules: [{ id: "color-contrast", enabled: false }],
},
},
docs: {
description: {
component:
"토스트가 필요한 레이아웃에서 children을 ToastProvider로 감싸 사용합니다.",
},
},
},
decorators: [
(Story) => (
<ToastProvider>
<Story />
</ToastProvider>
),
],
argTypes: {
text: {
description: "Toast에 들어갈 메인 텍스트를 나타냅니다.",
control: { type: "text" },
},
subText: {
description: "Toast에 들어갈 보조 텍스트를 나타냅니다.",
control: { type: "text" },
},
rightIcon: {
description: "Toast의 우측에 들어갈 아이콘을 나타냅니다.",
table: {
type: { summary: "none | close | arrow" },
defaultValue: { summary: "none" },
},
control: "radio",
options: ["none", "close", "arrow"],
},
id: {
description: "Toast 컴포넌트의 id를 나타냅니다.",
control: false,
},
onClickArrowIcon: {
description:
"Toast 컴포넌트의 화살표 아이콘을 클릭했을 때 호출되는 함수를 나타냅니다.",
control: false,
},
onRemove: {
description: "Toast 컴포넌트가 닫힌 이후 호출되는 함수를 나타냅니다.",
control: false,
},
showLeftIcon: {
description: "Toast 좌측에 들어갈 아이콘의 노출 여부를 나타냅니다.",
control: "boolean",
},
toastDuration: {
description: "Toast가 보여지는 시간(ms)을 나타냅니다.",
control: { type: "number" },
},
style: {
description: "Toast에 커스텀 스타일을 적용하기 위한 객체를 나타냅니다.",
control: false,
},
className: {
description: "Toast에 커스텀 클래스를 적용하기 위한 문자열을 나타냅니다.",
control: false,
},
},
} satisfies Meta<typeof Toast>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
id: "1",
text: "Text",
subText: "subtext",
toastDuration: 60 * 60 * 1000,
},
};

export const WithTrigger = () => {
const { toast } = useToast();

return (
<Button onClick={() => toast({ text: "Text", subText: "subtext" })}>
토스트 열기
</Button>
);
};

export const WithCloseIcon = () => {
const { toast } = useToast();
useEffect(() => {
toast({
text: "Text",
subText: "subtext",
rightIcon: "close",
});
}, []);
};

export const WithArrowIcon = () => {
const { toast } = useToast();
useEffect(() => {
toast({
text: "Text",
subText: "subtext",
rightIcon: "arrow",
});
}, []);
};

export const WithLeftIcon = () => {
const { toast } = useToast();
useEffect(() => {
toast({
text: "Text",
subText: "subtext",
showLeftIcon: true,
});
}, []);
};

export const WithLeftAndArrowIcons = () => {
const { toast } = useToast();
useEffect(() => {
toast({
text: "Text",
subText: "subtext",
showLeftIcon: true,
rightIcon: "arrow",
});
}, []);
};

export const TwoLines = () => {
const { toast } = useToast();
useEffect(() => {
toast({
showLeftIcon: true,
text: "TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText",
});
}, []);
};

export const Slow = () => {
const { toast } = useToast();
useEffect(() => {
toast({
text: "Text",
subText: "subtext",
toastDuration: 5000,
});
}, []);
};
22 changes: 22 additions & 0 deletions packages/wow-ui/src/components/Toast/ToastContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createContext } from "react";

import useSafeContext from "@/hooks/useSafeContext";

import type { ToastProps } from ".";

interface ToastContextProps {
toasts: ToastProps[];
addToast: (
toast: Omit<ToastProps, "id"> & Partial<Pick<ToastProps, "id">>
) => void;
removeToast: (id: string) => void;
}

export const ToastContext = createContext<ToastContextProps | undefined>(
undefined
);

export const useToastContext = () => {
const context = useSafeContext(ToastContext);
return context;
};
Loading

0 comments on commit 185475a

Please sign in to comment.