Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ const config: StorybookConfig = {
options: {},
},
staticDirs: ['../public'],
typescript: {
reactDocgen: 'react-docgen-typescript',
},
webpackFinal: async (config) => {
if (!config.module || !config.module.rules) {
return config;
Expand Down
37 changes: 37 additions & 0 deletions src/shared/styles/components/surface.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@keyframes surface-overlay-fade-in {
from {
opacity: 0;
}

to {
opacity: 1;
}
}

@keyframes surface-modal-slide-up {
from {
opacity: 0;
transform: translate3d(0, 40px, 0);
}

to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}

.surface-modal-overlay-animation {
animation: surface-overlay-fade-in 350ms ease-out both;
}

.surface-modal-animation {
will-change: transform, opacity;
animation: surface-modal-slide-up 350ms ease-out both;
}

@media (prefers-reduced-motion: reduce) {
.surface-modal-overlay-animation,
.surface-modal-animation {
animation: none;
}
}
1 change: 1 addition & 0 deletions src/shared/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@import './base/colors.css';
@import './base/z-index.css';
@import './base/scroll.css';
@import './components/surface.css';

button {
cursor: pointer;
Expand Down
2 changes: 1 addition & 1 deletion src/shared/ui/accordion/AccordionLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { cn } from '@/shared/styles/utils/cn';
import { cloneSlot, getSingleSlotChild } from '@/shared/ui/utils/Slot';
import { cloneSlot, getSingleSlotChild } from '@/shared/utils/slot';

import { useAccordionContext } from './AccordionContext';

Expand Down
6 changes: 3 additions & 3 deletions src/shared/ui/button/buttonVariants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ const buttonVariants = cva(
primary:
'bg-primary-500 text-white data-[disabled=false]:hover:bg-primary-600 data-[disabled=false]:active:bg-primary-700 disabled:bg-gray-700 aria-disabled:cursor-not-allowed aria-disabled:bg-gray-700',
secondary:
'border-2 border-gray-300 bg-black text-white data-[disabled=false]:hover:bg-gray-900 data-[disabled=false]:active:bg-gray-800 disabled:border-gray-600 disabled:text-gray-500 aria-disabled:cursor-not-allowed aria-disabled:border-gray-600 aria-disabled:text-gray-500',
'border border-gray-300 bg-black text-white data-[disabled=false]:hover:bg-gray-900 data-[disabled=false]:active:bg-gray-800 disabled:border-gray-600 disabled:text-gray-500 aria-disabled:cursor-not-allowed aria-disabled:border-gray-600 aria-disabled:text-gray-500',
ghost:
'border-2 border-transparent bg-transparent text-white data-[disabled=false]:hover:text-primary-500 data-[disabled=false]:active:bg-primary-600 disabled:text-gray-500 aria-disabled:cursor-not-allowed aria-disabled:text-gray-500',
'border border-transparent bg-transparent text-white data-[disabled=false]:hover:text-primary-500 data-[disabled=false]:active:bg-primary-600 disabled:text-gray-500 aria-disabled:cursor-not-allowed aria-disabled:text-gray-500',
outline:
'border-2 border-primary-500 bg-transparent text-primary-500 data-[disabled=false]:hover:bg-primary-300/50 data-[disabled=false]:active:bg-primary-300/60 disabled:border-gray-500 disabled:text-gray-500 aria-disabled:cursor-not-allowed aria-disabled:border-gray-500 aria-disabled:text-gray-500',
'border border-primary-500 bg-transparent text-primary-500 data-[disabled=false]:hover:bg-primary-300/50 data-[disabled=false]:active:bg-primary-300/60 disabled:border-gray-500 disabled:text-gray-500 aria-disabled:cursor-not-allowed aria-disabled:border-gray-500 aria-disabled:text-gray-500',
},
size: {
sm: 'h-7 w-16 body-14',
Expand Down
202 changes: 202 additions & 0 deletions src/shared/ui/confirm-modal/ConfirmModal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import { useEffect, useRef, useState } from 'react';
import type { ComponentProps } from 'react';

import { Button } from '@/shared/ui/button';

import { ConfirmModal } from './index';

import type { Meta, StoryObj } from '@storybook/nextjs';

type ConfirmModalStoryProps = ComponentProps<typeof ConfirmModal>;

function ButtonOpenConfirmModalExample(args: ConfirmModalStoryProps) {
const [isOpen, setIsOpen] = useState(false);

const handleConfirm = () => {
args.onConfirm();
setIsOpen(false);
};

const handleCancel = () => {
args.onCancel?.();
};

return (
<div className="flex min-h-80 items-center justify-center">
<Button onClick={() => setIsOpen(true)}>ConfirmModal 열기</Button>
<ConfirmModal
{...args}
onCancel={handleCancel}
onConfirm={handleConfirm}
onOpenChange={setIsOpen}
open={isOpen}
/>
</div>
);
}

function ControlledConfirmModalExample() {
const [isOpen, setIsOpen] = useState(false);
const [message, setMessage] = useState('대기 중');

const handleConfirm = () => {
setMessage('확인 이벤트가 실행되었습니다.');
setIsOpen(false);
};

const handleCancel = () => {
setMessage('취소 이벤트가 실행되었습니다.');
};

return (
<div className="flex min-h-80 flex-col items-center justify-center gap-4">
<Button onClick={() => setIsOpen(true)}>ConfirmModal 열기</Button>
<p className="m-0 body-14 text-gray-100">{message}</p>
<ConfirmModal
cancelLabel="아니오"
confirmLabel="저장하기"
description="버전 V.0.1로 저장됩니다."
onCancel={handleCancel}
onConfirm={handleConfirm}
onOpenChange={setIsOpen}
open={isOpen}
title="해당 자기소개서를 저장하시겠습니까?"
/>
</div>
);
}

function LoadingConfirmModalExample() {
const [isOpen, setIsOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

useEffect(() => {
return () => {
if (timerRef.current) {
clearTimeout(timerRef.current);
}
};
}, []);

const handleConfirm = () => {
setIsLoading(true);

timerRef.current = setTimeout(() => {
setIsLoading(false);
setIsOpen(false);
timerRef.current = null;
}, 1200);
};

return (
<div className="flex min-h-80 items-center justify-center">
<Button onClick={() => setIsOpen(true)}>Loading ConfirmModal 열기</Button>
<ConfirmModal
cancelLabel="아니오"
confirmLabel="저장하기"
description="저장 중에는 모달을 닫을 수 없습니다."
isLoading={isLoading}
onConfirm={handleConfirm}
onOpenChange={setIsOpen}
open={isOpen}
title="해당 자기소개서를 저장하시겠습니까?"
/>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</div>
);
}

const meta = {
title: 'Shared/ConfirmModal',
component: ConfirmModal,
tags: ['autodocs'],
args: {
cancelLabel: '아니오',
confirmLabel: '저장하기',
defaultOpen: false,
description: '버전 V.0.1로 저장됩니다.',
isLoading: false,
onConfirm: () => undefined,
title: '해당 자기소개서를 저장하시겠습니까?',
},
argTypes: {
cancelLabel: {
control: 'text',
},
closeOnEscape: {
control: 'boolean',
},
closeOnOutsideClick: {
control: 'boolean',
},
confirmLabel: {
control: 'text',
},
defaultOpen: {
control: 'boolean',
},
description: {
control: 'text',
},
focusTrap: {
control: 'boolean',
},
isLoading: {
control: 'boolean',
},
onCancel: {
action: 'cancel',
},
onClosePrevented: {
action: 'closePrevented',
},
onConfirm: {
action: 'confirm',
},
onOpenChange: {
action: 'openChange',
},
open: {
control: false,
},
restoreFocus: {
control: 'boolean',
},
scrollLock: {
control: 'boolean',
},
title: {
control: 'text',
},
},
parameters: {
backgrounds: {
default: 'black',
},
},
} satisfies Meta<typeof ConfirmModal>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
render: (args) => <ButtonOpenConfirmModalExample {...args} />,
};

export const Loading: Story = {
render: () => <LoadingConfirmModalExample />,
};

export const Controlled: Story = {
render: () => <ControlledConfirmModalExample />,
};

export const LongDescription: Story = {
args: {
description:
'현재 작성 중인 내용은 새 버전으로 저장되며, 저장이 완료된 뒤에는 버전 목록에서 다시 확인할 수 있습니다.',
title: '현재 내용을 새 버전으로 저장하시겠습니까?',
},
render: (args) => <ButtonOpenConfirmModalExample {...args} />,
};
Loading
Loading