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
1 change: 1 addition & 0 deletions packages/design-system/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export { WheelPicker, WheelPickerWrapper } from './wheelPicker/WheelPicker';
export type { WheelPickerOption } from './wheelPicker/WheelPicker';
export { default as Popup } from './popup/Popup';
export { default as PopupContainer } from './popup/PopupContainer';
export { default as InfoBox } from './infobox/InfoBox';
48 changes: 48 additions & 0 deletions packages/design-system/src/components/infobox/InfoBox.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import InfoBox from './InfoBox';

const meta: Meta<typeof InfoBox> = {
title: 'Components/InfoBox',
component: InfoBox,
tags: ['autodocs'],
parameters: {
layout: 'centered',
},
argTypes: {
title: {
control: 'text',
description: '메인 제목 (180px 넘어가면 … 처리)',
},
source: {
control: 'text',
description: '출처 (예: 블로그, 뉴스 등)',
},
imgUrl: {
control: 'text',
description: '썸네일 이미지 URL',
},
},
};
export default meta;

type Story = StoryObj<typeof InfoBox>;

// 기본 예시
export const Default: Story = {
args: {
imgUrl:
'https://previews.123rf.com/images/latkun/latkun1712/latkun171200130/92172856-empty-transparent-background-seamless-pattern.jpg',
title: '집에서 할 수 있는 것들은 무엇이 있을까요구르트',
source: '네이버 블로그',
},
};

export const LongTitle: Story = {
args: {
imgUrl:
'https://previews.123rf.com/images/latkun/latkun1712/latkun171200130/92172856-empty-transparent-background-seamless-pattern.jpg',
title:
'이건 엄청엄청엄청 길어서 180px 넘어가면 자동으로 ... 처리되는 긴 제목 테스트용 텍스트입니다',
source: '브런치',
},
};
17 changes: 17 additions & 0 deletions packages/design-system/src/components/infobox/InfoBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
interface InfoBoxProps {
title: string;
source: string;
imgUrl?: string;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imgUrl이 필수라고 생각이 드는데 혹시 선택으로 두신 이유가 있으신가요?? 혹은 이미지가 없다면 따로 나타나는 아이콘이 있을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 요건 썸네일이 비어있거나 안넘겨줄 시에, 기본 default 이미지로 보여야 하는 경우를 대비해서 선택사항으로 설정해두긴 했습니다!

}
const InfoBox = ({ title, source, imgUrl }: InfoBoxProps) => {
return (
<div className="border-main400 flex h-[6.8rem] w-[24.8rem] items-center justify-between gap-[0.8rem] rounded-[4px] border bg-white px-[0.8rem] py-[1.2rem]">
<img className="h-[4.4rem] w-[4.4rem] rounded-[0.4rem]" src={imgUrl} />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

접근성을 위해 img태그에 alt 속성 추가!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넹! 추가해두었습니다!

<div className="items-left flex flex-col justify-center gap-[0.2rem] text-left">
<p className="sub3-sb w-[180px] truncate">{title}</p>
<p className="caption2-m text-font-gray-3">{source}</p>
</div>
</div>
);
};
Comment on lines +6 to +16
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

imgUrl 미지정/로딩 실패 시 처리 보완

imgUrl이 선택 prop인데도 항상 img를 렌더링합니다. 비어있을 때는 플레이스홀더로 대체하거나 렌더링을 생략해 레이아웃 깨짐을 방지하는 편이 안전합니다.

-  return (
+  return (
     <div className="border-main400 flex h-[6.8rem] w-[24.8rem] items-center justify-between gap-[0.8rem] rounded-[4px] border bg-white px-[0.8rem] py-[1.2rem]">
-      <img className="h-[4.4rem] w-[4.4rem] rounded-[0.4rem]" src={imgUrl} />
+      {imgUrl ? (
+        <img
+          className="h-[4.4rem] w-[4.4rem] rounded-[0.4rem]"
+          src={imgUrl}
+          alt={imgAlt ?? `${source} 썸네일`}
+          loading="lazy"
+          decoding="async"
+        />
+      ) : (
+        <div
+          className="h-[4.4rem] w-[4.4rem] rounded-[0.4rem] bg-gray-100"
+          aria-hidden="true"
+        />
+      )}
       <div className="items-left flex flex-col justify-center gap-[0.2rem] text-left">
         <p className="sub3-sb w-[180px] truncate">{title}</p>
         <p className="caption2-m text-font-gray-3">{source}</p>
       </div>
     </div>
   );

원하시면 디자인 토큰에 맞는 플레이스홀더(아이콘/이미지)도 함께 제안드릴게요.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/design-system/src/components/infobox/InfoBox.tsx around lines 6 to
16, the component always renders an <img> even when the optional imgUrl is
missing or fails to load; update the component to conditionally render the image
only if imgUrl is truthy, add an onError handler to swap to a placeholder state
when loading fails, and ensure the image includes an appropriate alt text; when
imgUrl is absent or error occurs render a placeholder element (same dimensions
and border-radius as the image, using a background color or icon from design
tokens) to preserve layout and accessibility.

export default InfoBox;
Loading