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
5 changes: 5 additions & 0 deletions .changeset/slow-mice-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@croquiscom/monolith': minor
---

Stack 컴포넌트 추가
4 changes: 2 additions & 2 deletions docusaurus/developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ export const useMount = (effect_callback: EffectCallback) => {
`Monolith` 패키지의 버전관리는 `changeset` 통한 자동화가 되어있습니다.
작업이 마무리되었다면 아래 명령어를 입력하여 _작업한 변경사항을 적어주도록 합니다._

> yarn changeset
> pnpm changeset

이후 아래 이미지처럼 배포내역을 작성한위 확인을 눌러주도록 합니다.

![기여_1번이미지.png](./images/기여_2번이미지.png)
![기여_1번이미지.png](./images/기여_2번이미지v2.png)

그러면 아래와 같이 `.changeset`에 버전반영된 파일이 추가된것을 볼수 있습니다. 해당파일까지 같이 commit 후 `main` 에 PR 요청을 합니다.

Expand Down
Binary file added docusaurus/images/기여_2번이미지v2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"docs:build": "docusaurus build",
"test:type": "tsc --skipLibCheck",
"test": "vitest run",
"test:watch": "vitest watch",
"release:package": "pnpm run build && changeset publish"
},
"peerDependencies": {
Expand Down
2 changes: 2 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
* @packageDocumentation Monolith에서 구성하는 공통 컴포넌트들이 구성되어있는 영역 입니다.
*/

export { Stack } from './stack/Stack';
export type { StackProps } from './stack/Stack';
export { SwitchCase } from './switch-case/SwitchCase';
38 changes: 38 additions & 0 deletions src/components/stack/Stack.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.flex {
display: flex;
}

/* flex-direction */
.flex-column { flex-direction: column; }
.flex-column-reverse { flex-direction: column-reverse; }
.flex-row { flex-direction: row; }
.flex-row-reverse { flex-direction: row-reverse; }

/* flex-wrap */
.flex-nowrap { flex-wrap: nowrap; }
.flex-wrap { flex-wrap: wrap; }
.flex-wrap-reverse { flex-wrap: wrap-reverse; }

/* align-items */
.align-center { align-items: center; }
.align-start { align-items: start; }
.align-end { align-items: end; }
.align-flex-start { align-items: flex-start; }
.align-flex-end { align-items: flex-end; }
.align-self-start { align-items: self-start; }
.align-self-end { align-items: self-end; }
.align-baseline { align-items: baseline; }
.align-stretch { align-items: stretch; }

/* justify-content */
.justify-center { justify-content: center; }
.justify-start { justify-content: start; }
.justify-end { justify-content: end; }
.justify-flex-start { justify-content: flex-start; }
.justify-flex-end { justify-content: flex-end; }
.justify-left { justify-content: left; }
.justify-right { justify-content: right; }
.justify-space-between { justify-content: space-between; }
.justify-space-around { justify-content: space-around; }
.justify-space-evenly { justify-content: space-evenly; }
.justify-stretch { justify-content: stretch; }
133 changes: 133 additions & 0 deletions src/components/stack/Stack.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { render } from '@testing-library/react';
import { Stack } from './Stack';
import '@testing-library/jest-dom';

describe('Stack', () => {
it('자식 컴포넌트들을 렌더링합니다', () => {
const { getByText } = render(
<Stack>
<div>1번</div>
<div>2번</div>
<div>3번</div>
</Stack>,
);
expect(getByText('1번')).toBeInTheDocument();
expect(getByText('2번')).toBeInTheDocument();
expect(getByText('3번')).toBeInTheDocument();
});

it('direction prop이 정상적으로 적용됩니다', () => {
const { container } = render(
<Stack direction='column'>
<div>1번</div>
</Stack>,
);
expect(container.firstChild).toHaveClass('flex flex-column');
});

it('wrap prop이 정상적으로 적용됩니다', () => {
const { container } = render(
<Stack wrap='wrap'>
<div>1번</div>
</Stack>,
);
expect(container.firstChild).toHaveClass('flex flex-wrap');
});

it('align prop이 정상적으로 적용됩니다', () => {
const { container } = render(
<Stack align='center'>
<div>1번</div>
</Stack>,
);
expect(container.firstChild).toHaveClass('flex align-center');
});

it('justify prop이 정상적으로 적용됩니다', () => {
const { container } = render(
<Stack justify='center'>
<div>1번</div>
</Stack>,
);
expect(container.firstChild).toHaveClass('flex justify-center');
});

it('gap prop이 정상적으로 적용됩니다', () => {
const { container } = render(
<Stack gap={10}>
<div>1번</div>
</Stack>,
);
expect(container.firstChild).toHaveStyle({ gap: '10px' });
});

it('width prop이 정상적으로 적용됩니다', () => {
const { container } = render(
<Stack width={100}>
<div>1번</div>
</Stack>,
);
expect(container.firstChild).toHaveStyle({ width: '100px' });
});

it('height prop이 정상적으로 적용됩니다', () => {
const { container } = render(
<Stack height={100}>
<div>1번</div>
</Stack>,
);
expect(container.firstChild).toHaveStyle({ height: '100px' });
});

it('margin prop이 정상적으로 적용됩니다', () => {
const { container } = render(
<Stack m={24}>
<div>1번</div>
</Stack>,
);
expect(container.firstChild).toHaveStyle({ margin: '24px' });
});

it('padding prop이 정상적으로 적용됩니다', () => {
const { container } = render(
<Stack p={16}>
<div>1번</div>
</Stack>,
);
expect(container.firstChild).toHaveStyle({ padding: '16px' });
});

it('모든 props가 정상적으로 적용됩니다', () => {
const { container } = render(
<Stack
direction='column'
wrap='nowrap'
align='center'
justify='center'
gap={10}
width={100}
height={100}
m={24}
p={16}
mt={16}
ml={24}
mr={24}
mb={16}
pt={16}
pl={24}
pr={24}
pb={16}
>
<div>1번</div>
</Stack>,
);
expect(container.firstChild).toHaveClass('flex flex-column flex-nowrap align-center justify-center');
expect(container.firstChild).toHaveStyle({
gap: '10px',
width: '100px',
height: '100px',
margin: '16px 24px 16px 24px',
padding: '16px 24px 16px 24px',
});
});
});
140 changes: 140 additions & 0 deletions src/components/stack/Stack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { PropsWithChildren, CSSProperties, HTMLAttributes, forwardRef } from 'react';
import './Stack.css';
import { buildFlexClassNames } from './utils/buildFlexClassNames';

/** The props type of {@link Stack | 'Stack'}. */
export interface StackProps extends PropsWithChildren {
/** flex-direction 속성을 지정합니다. */
direction?: 'row' | 'row-reverse' | 'column' | 'column-reverse';
/** flex-wrap 속성을 지정합니다. */
wrap?: 'wrap' | 'nowrap' | 'wrap-reverse';
/** align-items 속성을 지정합니다. */
align?: 'center' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'baseline' | 'stretch';
/** justify-content 속성을 지정합니다. */
justify?:
| 'center'
| 'start'
| 'end'
| 'flex-start'
| 'flex-end'
| 'left'
| 'right'
| 'space-between'
| 'space-around'
| 'space-evenly'
| 'stretch';
/** gap 속성을 지정합니다. */
gap?: CSSProperties['gap'];
/** width 속성을 지정합니다. */
width?: number | string;
/** height 속성을 지정합니다. */
height?: number | string;
/** margin 속성을 지정합니다. */
m?: number | string;
/** margin-top 속성을 지정합니다. */
mt?: number | string;
/** margin-left 속성을 지정합니다. */
ml?: number | string;
/** margin-right 속성을 지정합니다. */
mr?: number | string;
/** margin-bottom 속성을 지정합니다. */
mb?: number | string;
/** padding 속성을 지정합니다. */
p?: number | string;
/** padding-top 속성을 지정합니다. */
pt?: number | string;
/** padding-left 속성을 지정합니다. */
pl?: number | string;
/** padding-right 속성을 지정합니다. */
pr?: number | string;
/** padding-bottom 속성을 지정합니다. */
pb?: number | string;
}

/**
* 자식 컴포넌트를 스택으로 렌더링하는 컴포넌트입니다.
*
* @category Component
* @param props - {@link StackProps}를 참조하세요.
* @returns 해당하는 컴포넌트를 반환합니다.
*
* @example
* ```tsx
* // 중앙 정렬, 10px 간격
* <Stack justify='center' align='center' gap={10}>
* {children}
* </Stack>
*
* // 양끝 정렬
* <Stack justify='space-between'>
* {children}
* </Stack>
*
* // 상단 정렬
* <Stack align='flex-start'>
* {children}
* </Stack>
*
* // 가로 방향 정렬
* <Stack direction='row'>
* {children}
* </Stack>
*
* // 세로 방향 정렬
* <Stack direction='column'>
* {children}
* </Stack>
* ```
*/
export const Stack = forwardRef<HTMLDivElement, StackProps & HTMLAttributes<HTMLDivElement>>(
(
{
children,
className,
direction,
wrap,
align,
justify,
gap,
width,
height,
m,
mt,
ml,
mr,
mb,
p,
pt,
pl,
pr,
pb,
...props
},
ref,
) => {
const flex_classes = buildFlexClassNames({ direction, wrap, align, justify });
const combined_classes = [flex_classes, className].filter(Boolean).join(' ');

const spacing_style: CSSProperties = {
...(gap !== undefined && { gap }),
...(width !== undefined && { width }),
...(height !== undefined && { height }),
...(m !== undefined && { margin: m }),
...(mt !== undefined && { marginTop: mt }),
...(ml !== undefined && { marginLeft: ml }),
...(mr !== undefined && { marginRight: mr }),
...(mb !== undefined && { marginBottom: mb }),
...(p !== undefined && { padding: p }),
...(pt !== undefined && { paddingTop: pt }),
...(pl !== undefined && { paddingLeft: pl }),
...(pr !== undefined && { paddingRight: pr }),
...(pb !== undefined && { paddingBottom: pb }),
};

return (
<div ref={ref} className={combined_classes} style={spacing_style} {...props}>
{children}
</div>
);
},
);
33 changes: 33 additions & 0 deletions src/components/stack/utils/buildFlexClassNames.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { buildFlexClassNames } from './buildFlexClassNames';

describe('buildFlexClassNames', () => {
it('파라미터가 없는 경우 flex만 반환합니다.', () => {
const classNames = buildFlexClassNames({});
expect(classNames).toEqual('flex');
});

it('flex-direction 파라미터가 있는 경우 flex-direction 클래스를 반환합니다.', () => {
const classNames = buildFlexClassNames({ direction: 'row' });
expect(classNames).toEqual('flex flex-row');
});

it('flex-wrap 파라미터가 있는 경우 flex-wrap 클래스를 반환합니다.', () => {
const classNames = buildFlexClassNames({ wrap: 'wrap' });
expect(classNames).toEqual('flex flex-wrap');
});

it('align-items 파라미터가 있는 경우 align-items 클래스를 반환합니다.', () => {
const classNames = buildFlexClassNames({ align: 'center' });
expect(classNames).toEqual('flex align-center');
});

it('justify-content 파라미터가 있는 경우 justify-content 클래스를 반환합니다.', () => {
const classNames = buildFlexClassNames({ justify: 'center' });
expect(classNames).toEqual('flex justify-center');
});

it('모든 파라미터가 있는 경우 모든 클래스를 반환합니다.', () => {
const classNames = buildFlexClassNames({ direction: 'row', wrap: 'wrap', align: 'center', justify: 'center' });
expect(classNames).toEqual('flex flex-row flex-wrap align-center justify-center');
});
});
Loading