From d7db551c69928edb4e2415b97c3c00aecb09a819 Mon Sep 17 00:00:00 2001 From: Cola Date: Wed, 12 Jul 2023 20:58:18 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20Name=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20props=20=EA=B0=84=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/SsafyIcon/Track.stories.tsx | 7 +- src/components/Common/SsafyIcon/Track.tsx | 21 ++--- src/components/Name/Name.stories.tsx | 93 +++++++++++++------ src/components/Name/index.tsx | 40 ++++---- src/mocks/handlers/member/data.ts | 21 ++++- src/mocks/handlers/member/index.ts | 7 +- src/services/member/utils/types.ts | 4 +- 7 files changed, 114 insertions(+), 79 deletions(-) diff --git a/src/components/Common/SsafyIcon/Track.stories.tsx b/src/components/Common/SsafyIcon/Track.stories.tsx index 67ba80537..8297c8da0 100644 --- a/src/components/Common/SsafyIcon/Track.stories.tsx +++ b/src/components/Common/SsafyIcon/Track.stories.tsx @@ -16,11 +16,6 @@ type TrackIconStory = StoryObj; export const TrackIcon: TrackIconStory = { args: { name: SsafyTrack.MOBILE, size: TrackSize.SM1 }, argTypes: { - label: { - table: { - disable: true, - }, - }, style: { table: { disable: true, @@ -43,7 +38,7 @@ export const AllTrackIcons: TrackIconStory = {
{sizes.map((size) => (
- +
))}
diff --git a/src/components/Common/SsafyIcon/Track.tsx b/src/components/Common/SsafyIcon/Track.tsx index f9074c741..622d9cd8c 100644 --- a/src/components/Common/SsafyIcon/Track.tsx +++ b/src/components/Common/SsafyIcon/Track.tsx @@ -12,36 +12,27 @@ import PythonTrack from '~/assets/images/track-python.svg'; import Uncertified from '~/assets/images/track-uncertified.svg'; import { SsafyTrack } from '~/services/member/utils'; import { inlineFlex } from '~/styles/utils'; +import { defaultify } from '~/utils'; -// MajorType을 ~/services/member 에서 가져오면 참조 오류가 발생하는데 이유를 잘 모르겠습니다. +// SsafyTrack을 ~/services/member 에서 가져오면 참조 오류가 발생하는데 이유를 잘 모르겠습니다. // import 구문 옆에다 주석 달면 린트 오류가 발생해서 여기 적어둡니다. export interface TrackProps { - /** - * API 명세에 `null`이 들어올 수 있다고 되어있는데 - * 만약 그렇다면 `fetcher`에서 `null`을 `undefined`로 변환할 예정입니다. - */ name?: Track; - label?: string; size?: TrackSize; style?: CSSProperties; theme?: Track extends 'fallback' | undefined ? FallbackTheme : undefined; } const Track = (props: TrackProps) => { - const { - name = 'fallback', - label = name, - size = TrackSize.SM1, - style = {}, - theme, - } = props; + const { name = 'fallback', size = TrackSize.SM1, style = {}, theme } = props; + const safeName = defaultify(name, [null]).to('fallback') as NonNullable; - const TrackComponent = tracks[name]; + const TrackComponent = tracks[safeName]; return (
- +
diff --git a/src/components/Name/Name.stories.tsx b/src/components/Name/Name.stories.tsx index e61c24f5e..81eb5f5e7 100644 --- a/src/components/Name/Name.stories.tsx +++ b/src/components/Name/Name.stories.tsx @@ -1,6 +1,8 @@ +import type { NameProps } from './index'; import type { Meta, StoryObj } from '@storybook/react'; -import { SsafyTrack, CertificationState } from '~/services/member/utils'; +import { userInfo } from '~/mocks/handlers/member/data'; +import { CertificationState, SsafyTrack } from '~/services/member'; import Name from './index'; @@ -8,43 +10,76 @@ const meta: Meta = { title: 'Name', component: Name, tags: ['autodocs'], - argTypes: {}, + argTypes: { + userInfo: { + table: { + disable: true, + }, + }, + }, }; export default meta; -type NameStory = StoryObj; +type NameStory = StoryObj; +interface NameStoryArgs { + nickname: string; + isMajor: boolean; + certificationState: CertificationState; + track: SsafyTrack; + size: NameProps['size']; +} -export const NameStory: NameStory = { - name: 'Name', +export const Default: NameStory = { + args: { + nickname: 'Kimee', + isMajor: false, + certificationState: CertificationState.UNCERTIFIED, + track: SsafyTrack.EMBEDDED, + size: 'sm', + }, + argTypes: { + certificationState: { + control: 'radio', + options: Object.values(CertificationState), + }, + track: { + control: 'radio', + options: Object.values(SsafyTrack), + }, + }, + + render: (args: NameStoryArgs) => { + const { nickname, isMajor, certificationState, track, size } = args; + const ssafyInfo = + certificationState === CertificationState.CERTIFIED + ? { + certificationState, + majorTrack: track, + } + : { + certificationState, + majorTrack: null, + }; + + const user = { + ...userInfo.certifiedSsafyUserInfo, + nickname, + isMajor, + ssafyInfo: { + ...userInfo.certifiedSsafyUserInfo.ssafyInfo, + ...ssafyInfo, + }, + }; + + return ; + }, }; export const Certified = () => { - return ( - - ); + return ; }; export const UnCertified = () => { - return ( - - ); + return ; }; diff --git a/src/components/Name/index.tsx b/src/components/Name/index.tsx index 8e90a2026..6337efd3a 100644 --- a/src/components/Name/index.tsx +++ b/src/components/Name/index.tsx @@ -1,44 +1,44 @@ import type { SerializedStyles } from '@emotion/react'; -import type { UserSsafyInfo, UserBasicInfo } from '~/services/member/utils'; +import type { UserInfo } from '~/services/member/utils'; import { css } from '@emotion/react'; -import { SsafyTrack } from '~/services/member/utils'; +import { Avatar, SsafyIcon, TrackSize } from '~/components/Common'; +import { CertificationState } from '~/services/member/utils'; import { fontCss, inlineFlex } from '~/styles/utils'; -import { Avatar, SsafyIcon, TrackSize } from '../Common'; - -type BasicInfo = Pick; export type NameProps = { - userSsafyInfo: UserSsafyInfo; + userInfo: UserInfo; size?: NameSize; -} & BasicInfo; +}; type NameSize = 'sm' | 'md' | 'lg'; const Name = (props: NameProps) => { + const { size = 'sm', userInfo } = props; const { - size = 'lg', - nickname = '쌒사운드', + // basic info isMajor, - userSsafyInfo = { - ssafyMember: true, - ssafyInfo: { - majorTrack: SsafyTrack['MOBILE'], - certificationState: true, - }, - }, - } = props; + nickname, + ssafyMember, + + // ssafy info + ssafyInfo, + } = userInfo; + + const showBadge = + ssafyMember && + ssafyInfo?.certificationState === CertificationState.CERTIFIED; - const { ssafyMember, ssafyInfo } = userSsafyInfo; + // case 1: ssafyMember && uncertified ( return ( {nickname} - {ssafyMember && ( + {showBadge && ( )} diff --git a/src/mocks/handlers/member/data.ts b/src/mocks/handlers/member/data.ts index ce4a73c2c..a445ead99 100644 --- a/src/mocks/handlers/member/data.ts +++ b/src/mocks/handlers/member/data.ts @@ -1,6 +1,6 @@ import type { UserInfo } from '~/services/member'; -import { CertificationState } from '~/services/member'; +import { CertificationState, SsafyTrack } from '~/services/member'; const initialUserInfo: UserInfo = { memberId: 434, @@ -9,15 +9,27 @@ const initialUserInfo: UserInfo = { ssafyMember: null, isMajor: false, }; -const ssafyUserInfo: UserInfo = { +const certifiedSsafyUserInfo: UserInfo = { + ...initialUserInfo, + ssafyMember: true, + isMajor: false, + ssafyInfo: { + semester: 1, + campus: '구미', + certificationState: CertificationState.CERTIFIED, + majorTrack: SsafyTrack.EMBEDDED, + }, +}; + +const uncertifiedSsafyUserInfo: UserInfo = { ...initialUserInfo, ssafyMember: true, isMajor: false, ssafyInfo: { semester: 1, campus: '구미', - majorTrack: undefined, certificationState: CertificationState.UNCERTIFIED, + majorTrack: null, }, }; @@ -29,6 +41,7 @@ const nonSsafyUserInfo: UserInfo = { export const userInfo = { initialUserInfo, - ssafyUserInfo, + certifiedSsafyUserInfo, + uncertifiedSsafyUserInfo, nonSsafyUserInfo, }; diff --git a/src/mocks/handlers/member/index.ts b/src/mocks/handlers/member/index.ts index 10fc715c0..7fc7d50cd 100644 --- a/src/mocks/handlers/member/index.ts +++ b/src/mocks/handlers/member/index.ts @@ -2,7 +2,8 @@ import type { GetMyInfoApiData, UpdateMyInfoParams, UserInfo, - CertifyStudentApiData } from '~/services/member'; + CertifyStudentApiData, +} from '~/services/member'; import type { ApiErrorResponse } from '~/types'; import { rest } from 'msw'; @@ -18,7 +19,7 @@ const getMyInfo = rest.get( return res( ctx.delay(500), // ...mockSuccess(ctx, userInfo.initialUserInfo) - ...mockSuccess(ctx, userInfo.ssafyUserInfo) + ...mockSuccess(ctx, userInfo.certifiedSsafyUserInfo) // ...mockSuccess(ctx, userInfo.nonSsafyUserInfo) // ...mockError(ctx, 'code', 'message', 404), ); @@ -35,7 +36,7 @@ const updateMyInfo = rest.patch< let response; if (body.ssafyMember) { - response = userInfo.ssafyUserInfo; + response = userInfo.certifiedSsafyUserInfo; } else { response = userInfo.nonSsafyUserInfo; } diff --git a/src/services/member/utils/types.ts b/src/services/member/utils/types.ts index b30085ad2..b41e5c71b 100644 --- a/src/services/member/utils/types.ts +++ b/src/services/member/utils/types.ts @@ -17,12 +17,12 @@ export interface SsafyBasicInfo { export type SsafyInfo = | (SsafyBasicInfo & { - majorTrack?: undefined; certificationState: CertificationState.UNCERTIFIED; + majorTrack: null; }) | (SsafyBasicInfo & { - majorTrack?: SsafyTrack; certificationState: CertificationState.CERTIFIED; + majorTrack: SsafyTrack; }); export enum CertificationState { From 5e20d812d2575480454574c747402564fbafbb74 Mon Sep 17 00:00:00 2001 From: Cola Date: Wed, 12 Jul 2023 21:41:41 +0900 Subject: [PATCH 2/8] =?UTF-8?q?refactor:=20Name,=20Avatar=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 스타일, 기본값, 타입 리팩토링 --- src/components/Common/Avatar/AvatarGroup.tsx | 2 +- src/components/Common/Avatar/SingleAvatar.tsx | 51 +++++++------------ src/components/Name/index.tsx | 8 ++- src/services/member/utils/types.ts | 2 +- 4 files changed, 24 insertions(+), 39 deletions(-) diff --git a/src/components/Common/Avatar/AvatarGroup.tsx b/src/components/Common/Avatar/AvatarGroup.tsx index 1b09d2349..d04061235 100644 --- a/src/components/Common/Avatar/AvatarGroup.tsx +++ b/src/components/Common/Avatar/AvatarGroup.tsx @@ -1,4 +1,4 @@ -import type { ReactNode, ComponentPropsWithoutRef, ReactElement } from 'react'; +import type { ReactNode, ComponentPropsWithoutRef } from 'react'; import { css } from '@emotion/react'; import { Children, isValidElement } from 'react'; diff --git a/src/components/Common/Avatar/SingleAvatar.tsx b/src/components/Common/Avatar/SingleAvatar.tsx index 7f64782d8..b4213290e 100644 --- a/src/components/Common/Avatar/SingleAvatar.tsx +++ b/src/components/Common/Avatar/SingleAvatar.tsx @@ -4,39 +4,32 @@ import type { ComponentPropsWithoutRef } from 'react'; import { css } from '@emotion/react'; import React from 'react'; -import { flex, fontCss } from '~/styles/utils'; +import { flex, fontCss, palettes } from '~/styles/utils'; export interface AvatarProps extends ComponentPropsWithoutRef<'div'> { - size?: AvatarSize; - major?: boolean; nickName?: string; + major?: boolean; + size?: AvatarSize; isEmpty?: boolean; } type AvatarSize = 'sm' | 'md' | 'lg'; -type BackgroundColor = 'major' | 'nonMajor'; const SingleAvatar = (props: AvatarProps) => { const { + nickName = '', major = false, size = 'sm', - nickName = '샆사운드', isEmpty = false, ...rest } = props; - // 현재 설계상 major라는 이름으로 전공여부를 가지고오게되어 그대로 사용하기 위해 major라는 명칭을 사용하게 됨. return (
{isEmpty || ( - + {getFirstText(nickName)} )} @@ -44,22 +37,22 @@ const SingleAvatar = (props: AvatarProps) => { ); }; -const getFirstText = (str: string) => str.at(0); +const getFirstText = (str: string) => str.at(0) || ''; const selfCss = css( { borderRadius: 100, - color: '#000', - border: '0.6px solid #fff', + color: palettes.black, + border: `0.6px solid ${palettes.white}`, + backgroundColor: palettes.nonMajor, }, - flex('center', 'center', 'row') + flex('center', 'center', 'row'), + fontCss.family.auto ); -const fontStyleCss = fontCss.family.manrope; - const sizeCss: Record = { - sm: css({ width: 12, height: 12 }), - md: css({ width: 18, height: 18 }), + sm: css({ width: 16, height: 16 }), + md: css({ width: 20, height: 20 }), lg: css({ width: 40, height: 40 }), }; @@ -73,19 +66,13 @@ const textCapitalizeCss = css({ textTransform: 'capitalize', }); -const backgroundCss: Record = { - major: css({ - backgroundColor: '#71E498', - // todo 팔레트로 이관 - }), - nonMajor: css({ - backgroundColor: '#FFBF75', - }), -}; +const majorCss = css({ + backgroundColor: palettes.major, +}); const emptyCss = css({ - backgroundColor: '#F0F0F0', - border: '1px dotted #292929', + backgroundColor: palettes.white, + border: `1px dotted ${palettes.grey0}`, }); export default SingleAvatar; diff --git a/src/components/Name/index.tsx b/src/components/Name/index.tsx index 6337efd3a..94409bb70 100644 --- a/src/components/Name/index.tsx +++ b/src/components/Name/index.tsx @@ -30,8 +30,6 @@ const Name = (props: NameProps) => { ssafyMember && ssafyInfo?.certificationState === CertificationState.CERTIFIED; - // case 1: ssafyMember && uncertified ( - return ( @@ -67,9 +65,9 @@ const textCss: Record = { }; const trackSize: Record = { - sm: TrackSize['SM1'], - md: TrackSize['SM2'], - lg: TrackSize['SM3'], + sm: TrackSize.SM1, + md: TrackSize.SM2, + lg: TrackSize.SM3, }; export default Name; diff --git a/src/services/member/utils/types.ts b/src/services/member/utils/types.ts index b41e5c71b..d68ff5fc0 100644 --- a/src/services/member/utils/types.ts +++ b/src/services/member/utils/types.ts @@ -18,7 +18,7 @@ export interface SsafyBasicInfo { export type SsafyInfo = | (SsafyBasicInfo & { certificationState: CertificationState.UNCERTIFIED; - majorTrack: null; + majorTrack?: null | undefined; }) | (SsafyBasicInfo & { certificationState: CertificationState.CERTIFIED; From 7b33d01f29e8bda9bc10fec85a3e70bcc9c0d2af Mon Sep 17 00:00:00 2001 From: Cola Date: Wed, 12 Jul 2023 21:52:37 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20Name=20maxwidth,=20wordbreak=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Name/Name.stories.tsx | 8 ++++++++ src/components/Name/index.tsx | 9 ++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/components/Name/Name.stories.tsx b/src/components/Name/Name.stories.tsx index 81eb5f5e7..541523b38 100644 --- a/src/components/Name/Name.stories.tsx +++ b/src/components/Name/Name.stories.tsx @@ -83,3 +83,11 @@ export const Certified = () => { export const UnCertified = () => { return ; }; + +export const LongName = () => { + const user = { + ...userInfo.certifiedSsafyUserInfo, + nickname: '한글한글한글한글한글한', + }; + return ; +}; diff --git a/src/components/Name/index.tsx b/src/components/Name/index.tsx index 94409bb70..d38de1c30 100644 --- a/src/components/Name/index.tsx +++ b/src/components/Name/index.tsx @@ -33,7 +33,9 @@ const Name = (props: NameProps) => { return ( - {nickname} + + {nickname} + {showBadge && ( = { }), }; +const textBaseCss = css({ + maxWidth: 230, + wordBreak: 'break-word', +}); + const textCss: Record = { sm: css(fontCss.style.B12), md: css(fontCss.style.B18), From e11d96ceeb588d6e2f73868b4b415fc8fd9cc58b Mon Sep 17 00:00:00 2001 From: Cola Date: Wed, 12 Jul 2023 22:32:06 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20css=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Common/Avatar/SingleAvatar.tsx | 6 ++++-- src/components/Name/index.tsx | 16 ++-------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/components/Common/Avatar/SingleAvatar.tsx b/src/components/Common/Avatar/SingleAvatar.tsx index b4213290e..e2a9e3aac 100644 --- a/src/components/Common/Avatar/SingleAvatar.tsx +++ b/src/components/Common/Avatar/SingleAvatar.tsx @@ -29,7 +29,7 @@ const SingleAvatar = (props: AvatarProps) => { {...rest} > {isEmpty || ( - + {getFirstText(nickName)} )} @@ -56,10 +56,12 @@ const sizeCss: Record = { lg: css({ width: 40, height: 40 }), }; +const lineHeightCss = css({ lineHeight: 1 }); + const textCss: Record = { sm: css(fontCss.style.B12), md: css(fontCss.style.B14), - lg: css(fontCss.style.B24), + lg: css(fontCss.style.B28), }; const textCapitalizeCss = css({ diff --git a/src/components/Name/index.tsx b/src/components/Name/index.tsx index d38de1c30..d9be2d546 100644 --- a/src/components/Name/index.tsx +++ b/src/components/Name/index.tsx @@ -31,7 +31,7 @@ const Name = (props: NameProps) => { ssafyInfo?.certificationState === CertificationState.CERTIFIED; return ( - + {nickname} @@ -46,19 +46,7 @@ const Name = (props: NameProps) => { ); }; -const selfCss = css(inlineFlex('center', '', 'row')); - -const gapCss: Record = { - sm: css({ - gap: 2, - }), - md: css({ - gap: 4, - }), - lg: css({ - gap: 5, - }), -}; +const selfCss = css(inlineFlex('center', '', 'row', 2)); const textBaseCss = css({ maxWidth: 230, From 799869d59e611fb50b25bd27b933c25ed86b06b9 Mon Sep 17 00:00:00 2001 From: Cola Date: Thu, 13 Jul 2023 02:17:27 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20AvatarGroup=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/Avatar/Avatar.stories.tsx | 89 +++++++++++-------- src/components/Common/Avatar/AvatarGroup.tsx | 42 +++++---- src/components/Common/Avatar/SingleAvatar.tsx | 30 +++---- 3 files changed, 93 insertions(+), 68 deletions(-) diff --git a/src/components/Common/Avatar/Avatar.stories.tsx b/src/components/Common/Avatar/Avatar.stories.tsx index 676f92fe9..c3d5df1d5 100644 --- a/src/components/Common/Avatar/Avatar.stories.tsx +++ b/src/components/Common/Avatar/Avatar.stories.tsx @@ -1,53 +1,72 @@ import type { Meta, StoryObj } from '@storybook/react'; +import type { SingleAvatarProps } from '~/components/Common/Avatar/SingleAvatar'; + +import { userInfo } from '~/mocks/handlers/member/data'; import Avatar from './index'; -const meta: Meta = { +const meta: Meta = { title: 'Avatar', component: Avatar, + argTypes: {}, }; export default meta; -type AvatarStory = StoryObj; +interface AvatarStoryProps { + size: SingleAvatarProps['size']; + empty: boolean; +} + +type AvatarStory = StoryObj; + export const SingleAvatar: AvatarStory = { name: 'Avatar', args: { size: 'sm', - major: true, - nickName: '전공자', - isEmpty: false, + empty: false, + }, + render: (args: AvatarStoryProps) => { + return ( + + ); }, }; -export const AvatarGroup = () => { - const data = [ - { - major: true, - nickName: '전공자', - }, - { - major: false, - nickName: '비전공자', - }, - { - major: true, - nickName: 'Eng', - }, - { - major: false, - nickName: 'Test', - }, - { - major: true, - nickName: 'Extra', - }, - ]; - return ( - - {data.map((d) => ( - - ))} - - ); +interface AvatarGroupStoryProps { + avatarCount: number; + maxCount: number; + visibleCount: number; + size: SingleAvatarProps['size']; +} +type AvatarGroupStory = StoryObj; + +export const AvatarGroup: AvatarGroupStory = { + name: 'AvatarGroup', + args: { + avatarCount: 2, + maxCount: 4, + visibleCount: 4, + size: 'sm', + }, + render: (args: AvatarGroupStoryProps) => { + const { avatarCount, maxCount, visibleCount, size } = args; + const data = Array(avatarCount) + .fill(undefined) + .map(() => userInfo.certifiedSsafyUserInfo); + + return ( + <> +
+ + {data.map((d) => ( + + ))} + +
+ + ); + }, }; diff --git a/src/components/Common/Avatar/AvatarGroup.tsx b/src/components/Common/Avatar/AvatarGroup.tsx index d04061235..5f1576981 100644 --- a/src/components/Common/Avatar/AvatarGroup.tsx +++ b/src/components/Common/Avatar/AvatarGroup.tsx @@ -1,3 +1,4 @@ +import type { SingleAvatarProps } from './SingleAvatar'; import type { ReactNode, ComponentPropsWithoutRef } from 'react'; import { css } from '@emotion/react'; @@ -10,37 +11,44 @@ import SingleAvatar from './SingleAvatar'; export interface AvatarGroupProps extends ComponentPropsWithoutRef<'div'> { children: ReactNode; visibleCount?: number; + maxCount: number; } const AvatarGroup = (props: AvatarGroupProps) => { - const { children, visibleCount = 4, ...rest } = props; + const { children, maxCount, visibleCount = 4, ...rest } = props; - const validAvatars = Children.toArray(children).filter(isValidElement); + const validAvatars = Children.toArray(children).filter( + isValidElement + ); const visibleAvatars = validAvatars.slice(0, visibleCount); - - const restAvatarsNumber = validAvatars.length - visibleCount; - const emptyAvatarsNumber = visibleCount - validAvatars.length; + const emptyAvatarsCount = visibleCount - validAvatars.length; + const restAvatarsCount = maxCount - visibleCount; + const avatarSize = validAvatars[0].props?.size || 'sm'; return (
{visibleAvatars} - {Array.from({ length: emptyAvatarsNumber }).map((_, i) => ( - + {Array.from({ length: emptyAvatarsCount }).map((_, i) => ( + ))} - {restAvatarsNumber > 0 && +{restAvatarsNumber}} + {restAvatarsCount > 0 && ( + + +{restAvatarsCount} + + )}
); }; -const selfCss = css( - { - '> div': { - marginLeft: -2, - }, - }, - flex('center', 'center', 'row') -); +const selfCss = css(flex('center', 'center', 'row'), fontCss.family.auto); -const textCss = css(fontCss.style.R12); +const avatarCss = css({ marginLeft: -4 }); + +const textCss = css({ marginLeft: 4 }); +const textSizeCss = { + sm: css(fontCss.style.B12), + md: css(fontCss.style.B14), + lg: css(fontCss.style.B28), +}; export default AvatarGroup; diff --git a/src/components/Common/Avatar/SingleAvatar.tsx b/src/components/Common/Avatar/SingleAvatar.tsx index e2a9e3aac..d6f226292 100644 --- a/src/components/Common/Avatar/SingleAvatar.tsx +++ b/src/components/Common/Avatar/SingleAvatar.tsx @@ -1,36 +1,34 @@ import type { SerializedStyles } from '@emotion/react'; import type { ComponentPropsWithoutRef } from 'react'; +import type { UserInfo } from '~/services/member'; import { css } from '@emotion/react'; import React from 'react'; import { flex, fontCss, palettes } from '~/styles/utils'; -export interface AvatarProps extends ComponentPropsWithoutRef<'div'> { - nickName?: string; - major?: boolean; +export interface SingleAvatarProps extends ComponentPropsWithoutRef<'div'> { size?: AvatarSize; - isEmpty?: boolean; + userInfo?: UserInfo; } type AvatarSize = 'sm' | 'md' | 'lg'; -const SingleAvatar = (props: AvatarProps) => { - const { - nickName = '', - major = false, - size = 'sm', - isEmpty = false, - ...rest - } = props; +const SingleAvatar = (props: SingleAvatarProps) => { + const { size = 'sm', userInfo, ...restProps } = props; return (
- {isEmpty || ( + {userInfo && ( - {getFirstText(nickName)} + {getFirstText(userInfo.nickname)} )}
From ae86944381b3742c6a97f7a07d4b473b19a2ab38 Mon Sep 17 00:00:00 2001 From: Cola Date: Thu, 13 Jul 2023 02:26:55 +0900 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20Avatar=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC,=20Name=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Common/Avatar/Avatar.stories.tsx | 16 +++++++++++++++- src/components/Name/index.tsx | 3 +-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/components/Common/Avatar/Avatar.stories.tsx b/src/components/Common/Avatar/Avatar.stories.tsx index c3d5df1d5..25f377b8d 100644 --- a/src/components/Common/Avatar/Avatar.stories.tsx +++ b/src/components/Common/Avatar/Avatar.stories.tsx @@ -5,10 +5,22 @@ import { userInfo } from '~/mocks/handlers/member/data'; import Avatar from './index'; +const disableArgs = ['userInfo']; +const disableArgsTypes = disableArgs.reduce((acc, cur) => { + (acc as Record)[cur] = { + table: { + disable: true, + }, + }; + return acc; +}, {}); + const meta: Meta = { title: 'Avatar', component: Avatar, - argTypes: {}, + argTypes: { + ...disableArgsTypes, + }, }; export default meta; @@ -45,12 +57,14 @@ type AvatarGroupStory = StoryObj; export const AvatarGroup: AvatarGroupStory = { name: 'AvatarGroup', + argTypes: {}, args: { avatarCount: 2, maxCount: 4, visibleCount: 4, size: 'sm', }, + render: (args: AvatarGroupStoryProps) => { const { avatarCount, maxCount, visibleCount, size } = args; const data = Array(avatarCount) diff --git a/src/components/Name/index.tsx b/src/components/Name/index.tsx index d9be2d546..c53d34cc0 100644 --- a/src/components/Name/index.tsx +++ b/src/components/Name/index.tsx @@ -18,7 +18,6 @@ const Name = (props: NameProps) => { const { size = 'sm', userInfo } = props; const { // basic info - isMajor, nickname, ssafyMember, @@ -32,7 +31,7 @@ const Name = (props: NameProps) => { return ( - + {nickname} From 029a3927922c27e862fe72430dd77e77946e9487 Mon Sep 17 00:00:00 2001 From: Cola Date: Thu, 13 Jul 2023 02:53:56 +0900 Subject: [PATCH 7/8] =?UTF-8?q?feat:=20Avatar=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8,=20Avatar=20Gr?= =?UTF-8?q?oup=20css=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Common/Avatar/Avatar.stories.tsx | 4 +++- src/components/Common/Avatar/AvatarGroup.tsx | 12 +++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/Common/Avatar/Avatar.stories.tsx b/src/components/Common/Avatar/Avatar.stories.tsx index 25f377b8d..a1148e085 100644 --- a/src/components/Common/Avatar/Avatar.stories.tsx +++ b/src/components/Common/Avatar/Avatar.stories.tsx @@ -39,9 +39,11 @@ export const SingleAvatar: AvatarStory = { empty: false, }, render: (args: AvatarStoryProps) => { + const { size, empty } = args; return ( ); }, diff --git a/src/components/Common/Avatar/AvatarGroup.tsx b/src/components/Common/Avatar/AvatarGroup.tsx index 5f1576981..7b41f1ec1 100644 --- a/src/components/Common/Avatar/AvatarGroup.tsx +++ b/src/components/Common/Avatar/AvatarGroup.tsx @@ -4,7 +4,7 @@ import type { ReactNode, ComponentPropsWithoutRef } from 'react'; import { css } from '@emotion/react'; import { Children, isValidElement } from 'react'; -import { flex, fontCss } from '~/styles/utils'; +import { fontCss, inlineFlex } from '~/styles/utils'; import SingleAvatar from './SingleAvatar'; @@ -29,7 +29,7 @@ const AvatarGroup = (props: AvatarGroupProps) => {
{visibleAvatars} {Array.from({ length: emptyAvatarsCount }).map((_, i) => ( - + ))} {restAvatarsCount > 0 && ( @@ -40,9 +40,11 @@ const AvatarGroup = (props: AvatarGroupProps) => { ); }; -const selfCss = css(flex('center', 'center', 'row'), fontCss.family.auto); - -const avatarCss = css({ marginLeft: -4 }); +const selfCss = css( + { '> div': { marginLeft: -4 } }, + inlineFlex('center', 'center', 'row'), + fontCss.family.auto +); const textCss = css({ marginLeft: 4 }); const textSizeCss = { From 362463261287f89609241c46505a14a7fbf2f118 Mon Sep 17 00:00:00 2001 From: Cola Date: Thu, 13 Jul 2023 03:04:13 +0900 Subject: [PATCH 8/8] =?UTF-8?q?feat:=20withAvatar=20prop=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Name/Name.stories.tsx | 5 +++-- src/components/Name/index.tsx | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/Name/Name.stories.tsx b/src/components/Name/Name.stories.tsx index 541523b38..f8aac97bd 100644 --- a/src/components/Name/Name.stories.tsx +++ b/src/components/Name/Name.stories.tsx @@ -50,7 +50,8 @@ export const Default: NameStory = { }, render: (args: NameStoryArgs) => { - const { nickname, isMajor, certificationState, track, size } = args; + const { nickname, isMajor, certificationState, track, size, ...restArgs } = + args; const ssafyInfo = certificationState === CertificationState.CERTIFIED ? { @@ -72,7 +73,7 @@ export const Default: NameStory = { }, }; - return ; + return ; }, }; diff --git a/src/components/Name/index.tsx b/src/components/Name/index.tsx index c53d34cc0..3109bc52e 100644 --- a/src/components/Name/index.tsx +++ b/src/components/Name/index.tsx @@ -9,13 +9,14 @@ import { fontCss, inlineFlex } from '~/styles/utils'; export type NameProps = { userInfo: UserInfo; + withAvatar?: boolean; size?: NameSize; }; type NameSize = 'sm' | 'md' | 'lg'; const Name = (props: NameProps) => { - const { size = 'sm', userInfo } = props; + const { size = 'sm', withAvatar = true, userInfo } = props; const { // basic info nickname, @@ -31,7 +32,7 @@ const Name = (props: NameProps) => { return ( - + {withAvatar && } {nickname}