Skip to content

Commit

Permalink
Merge pull request #56 from kakao-tech-campus-2nd-step3/weekly/10th-week
Browse files Browse the repository at this point in the history
Weekly/10th week
  • Loading branch information
harugi7 authored Nov 9, 2024
2 parents 03d0081 + c3e6e45 commit eaedd83
Show file tree
Hide file tree
Showing 55 changed files with 1,481 additions and 104 deletions.
1 change: 1 addition & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Preview } from "@storybook/react";
import {ThemeProvider} from '@emotion/react';
import defaultTheme from '../src/styles/theme';
import 'reset-css/reset.css';
import '../public/index.css';
const preview: Preview = {
parameters: {
controls: {
Expand Down
7 changes: 6 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="preload" as="style" crossorigin
href="https://cdn.jsdelivr.net/gh/orioncactus/[email protected]/dist/web/static/pretendard-dynamic-subset.min.css" />
<link rel="stylesheet" crossorigin
href="https://cdn.jsdelivr.net/gh/orioncactus/[email protected]/dist/web/static/pretendard-dynamic-subset.min.css" />
<link rel="stylesheet" href='/index.css' />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>Ditto</title>
</head>
<body>
<div id="root"></div>
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"react-datepicker": "^7.5.0",
"react-dom": "^18.3.1",
"react-hook-form": "^7.53.0",
"react-hot-toast": "^2.4.1",
"react-router-dom": "^6.26.2",
"reset-css": "^5.0.2"
},
Expand Down
3 changes: 3 additions & 0 deletions public/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:root {
font-family: 'Pretendard', sans-serif;
}
10 changes: 10 additions & 0 deletions src/api/study/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import endpoints from '@constants/endpoints';
import { StudySearchRequestQuery, StudySearchResponse } from '@/types/study';
import axiosInstance from '@/utils/network';

export async function searchStudies(requestQuery: StudySearchRequestQuery) {
const response = await axiosInstance.get<StudySearchResponse>(endpoints.searchStudy, {
params: requestQuery,
});
return response.data;
}
Binary file added src/assets/banner-background.webp
Binary file not shown.
11 changes: 11 additions & 0 deletions src/assets/banner-image.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions src/components/boundary/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Component, ReactNode } from 'react';
import { isAxiosError } from 'axios';
import {
defaultErrorMessage, defaultFetchErrorMessage,
fetchErrorMessages,
} from '@constants/errorMessages';
import Container from '@components/container';

interface FetchErrorBoundaryProps {
children?: ReactNode;
fallback?: ReactNode;
}

interface FetchErrorBoundaryState {
hasError: boolean;
errorMessage?: string;
}

class ErrorBoundary extends Component<FetchErrorBoundaryProps, FetchErrorBoundaryState> {
constructor(props: FetchErrorBoundaryProps) {
super(props);
this.state = {
hasError: false,
errorMessage: undefined,
};
}

componentDidCatch(error: Error) {
console.error(error);

Check warning on line 29 in src/components/boundary/ErrorBoundary.tsx

View workflow job for this annotation

GitHub Actions / init

Unexpected console statement
}

static getDerivedStateFromError(error: Error) {
if (!isAxiosError(error)) {
return {
hasError: true,
errorMessage: defaultErrorMessage,
};
}
const errorStatus = error.response?.status;
const errorMessage = !errorStatus || !(errorStatus in fetchErrorMessages)
? defaultFetchErrorMessage
: fetchErrorMessages[errorStatus];
return {
hasError: true,
errorMessage,
};
}

render() {
const { props, state } = this;
const fallback = props.fallback || <Container>{state.errorMessage}</Container>;
return state.hasError ? fallback : props.children;
}
}

export default ErrorBoundary;
25 changes: 25 additions & 0 deletions src/components/boundary/SuspenseErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ReactNode, Suspense } from 'react';
import ErrorBoundary from '@components/boundary/ErrorBoundary';
import Spinner from '@components/fallback/Spinner';

interface SuspenseErrorBoundaryProps {
suspenseFallback?: ReactNode;
errorFallback?: ReactNode;
children?: ReactNode;
}

function SuspenseErrorBoundary({
suspenseFallback,
errorFallback,
children,
}: SuspenseErrorBoundaryProps) {
return (
<ErrorBoundary fallback={errorFallback}>
<Suspense fallback={suspenseFallback || <Spinner />}>
{children}
</Suspense>
</ErrorBoundary>
);
}

export default SuspenseErrorBoundary;
21 changes: 6 additions & 15 deletions src/components/container/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { HTMLAttributes, ReactNode } from 'react';
import { css, CSSObject } from '@emotion/react';
import { CSSObject } from '@emotion/react';
import useContainerStyle from '@components/container/useContainerStyle';

export interface ContainerProps extends HTMLAttributes<HTMLDivElement> {
children?: ReactNode;
Expand All @@ -12,25 +13,15 @@ export interface ContainerProps extends HTMLAttributes<HTMLDivElement> {
padding?: string;
boxSizing?: 'border-box' | 'content-box';
cssOverride?: CSSObject;
css?: CSSObject;
}

function Container({
children, direction, justify, align, width, height, gap, padding, boxSizing, cssOverride, ...rest
cssOverride, css, children, ...rest
}: ContainerProps) {
const style = css`
display: flex;
flex-direction: ${direction || 'row'};
justify-content: ${justify || 'center'};
align-items: ${align || 'center'};
width: ${width || '100%'};
height: ${height || 'auto'};
gap: ${gap || '0'};
padding: ${padding || '0'};
box-sizing: ${boxSizing || 'border-box'};
`;

const { containerStyle } = useContainerStyle(rest);
return (
<div css={[style, cssOverride]} {...rest}>
<div css={[containerStyle, cssOverride, css]} {...rest}>
{children}
</div>
);
Expand Down
23 changes: 23 additions & 0 deletions src/components/container/useContainerStyle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { css } from '@emotion/react';
import type { ContainerProps } from '@components/container/index';

function useContainerStyle({
direction, justify, align, width, height, gap, padding, boxSizing,
}: ContainerProps) {
const containerStyle = css`
display: flex;
flex-direction: ${direction || 'row'};
justify-content: ${justify || 'center'};
align-items: ${align || 'center'};
width: ${width || '100%'};
height: ${height || 'auto'};
gap: ${gap || '0'};
padding: ${padding || '0'};
box-sizing: ${boxSizing || 'border-box'};
`;
return {
containerStyle,
};
}

export default useContainerStyle;
4 changes: 2 additions & 2 deletions src/components/container/variants/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface DefaultPaddedContainerProps extends ContainerProps {

// eslint-disable-next-line import/prefer-default-export
export function DefaultPaddedContainer(
{ children, ...rest }: DefaultPaddedContainerProps,
{ children, cssOverride, ...rest }: DefaultPaddedContainerProps,
) {
const paddedContainerStyle = css`
box-sizing: border-box;
Expand All @@ -18,7 +18,7 @@ export function DefaultPaddedContainer(
`;

return (
<Container {...rest} cssOverride={paddedContainerStyle}>
<Container cssOverride={css([paddedContainerStyle, cssOverride])} {...rest}>
{children}
</Container>
);
Expand Down
17 changes: 17 additions & 0 deletions src/components/fallback/Spinner.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Meta, StoryObj } from '@storybook/react';
import Spinner from '@components/fallback/Spinner';

const meta: Meta<typeof Spinner> = {
title: 'Components/Spinner',
component: Spinner,
argTypes: {
wrapperCss: { control: 'object' },
circleCss: { control: 'object' },
},
};

export default meta;

type Story = StoryObj<typeof Spinner>;

export const Default: Story = {};
34 changes: 34 additions & 0 deletions src/components/fallback/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/** @jsxImportSource @emotion/react */
import { css, keyframes, useTheme } from '@emotion/react';
import Container from '@components/container';
import { Paragraph } from '@components/text';

const spin = keyframes`
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
`;

function Spinner() {
const theme = useTheme();
const spinnerStyle = css`
width: 30px;
height: 30px;
border: 5px solid ${theme.colors.border.subtle};
border-top: 5px solid ${theme.colors.primary.main};
border-radius: 50%;
margin-bottom: 15px;
animation: ${spin} 1s cubic-bezier(0.42, 0, 0.58, 1) infinite;
`;
return (
<Container direction="column">
<div css={spinnerStyle} />
<Paragraph variant="small" color={theme.colors.text.moderate}>로드 중</Paragraph>
</Container>
);
}

export default Spinner;
38 changes: 19 additions & 19 deletions src/components/header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ function Header() {
width="auto"
padding="0"
gap="10px"
cssOverride={{ position: 'relative' }}
>
<Avatar
size="small"
Expand All @@ -61,7 +62,7 @@ function Header() {
role="presentation"
/>
{isDropdownOpen && (
<Dropdown />
<Dropdown />
)}
</Container>
</Container>
Expand All @@ -71,23 +72,10 @@ function Header() {
}

function Dropdown() {
return (
<div css={dropdownStyle}>
<ul css={menuStyle}>
<li>내 스터디</li>
<li>설정</li>
<li>로그아웃</li>
</ul>
</div>
);
}

export default Header;

const dropdownStyle = css`
const dropdownStyle = css`
position: absolute;
right: 50px;
top: 65px;
right: 0;
top: 50px;
width: 180px;
background: white;
font-size: 13px;
Expand All @@ -96,7 +84,7 @@ const dropdownStyle = css`
z-index: 1000;
padding: 5px 5px;
`;
const menuStyle = css`
const menuStyle = css`
list-style: none;
padding: 0;
margin: 0;
Expand All @@ -107,4 +95,16 @@ const menuStyle = css`
background-color: #f0f0f0;
}
}
`;
`;
return (
<div css={dropdownStyle}>
<ul css={menuStyle}>
<li>내 스터디</li>
<li>설정</li>
<li>로그아웃</li>
</ul>
</div>
);
}

export default Header;
6 changes: 3 additions & 3 deletions src/components/input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const Input = forwardRef<HTMLInputElement, InputProps>(({
throw new Error('Cannot enable toggle while the type of input is not password');
}

const inputType = type === 'password' && isHidden ? 'password' : type;

return (
<>
{
Expand All @@ -54,9 +56,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(({
<DynamicIcon icon={icon} css={inputIconStyle()} />
<input
css={[inputStyle, css]}
type={type === 'password' && isHidden
? 'password'
: 'text'}
type={inputType}
id={inputId.current}
ref={ref}
{...rest}
Expand Down
4 changes: 1 addition & 3 deletions src/components/select/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
children?: ReactNode;
icon?: string | ReactNode;
label?: string;
placeholder?: string;
css?: CSSObject;
}

function Select({
children, icon, label, placeholder, css, ...rest
children, icon, label, css, ...rest
}: SelectProps) {
const selectId = useRef(generateRandomId());
const { selectStyle, selectContainerStyle } = useSelectStyle();
Expand All @@ -22,7 +21,6 @@ function Select({
<div css={selectContainerStyle}>
{label ? <Label>{label}</Label> : null}
<select id={selectId.current} css={[selectStyle, css]} {...rest}>
<option value="" selected disabled hidden>{placeholder}</option>
{children}
</select>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/select/select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type Story = StoryObj<typeof Select>;

export const Default: Story = {
render: () => (
<Select placeholder="옵션을 선택하세요." label="label" icon={eye}>
<Select label="label" icon={eye}>
<option value="asdf">asdf</option>
<option value="asdf">asdf</option>
<option value="asdf">asdf</option>
Expand Down
12 changes: 12 additions & 0 deletions src/components/span/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ReactNode } from 'react';
import { CSSObject, useTheme } from '@emotion/react';

interface SpanProps {
children?: ReactNode;
css?: CSSObject;
}

export function PrimarySpan({ children, css }: SpanProps) {
const theme = useTheme();
return <span css={[{ color: theme.colors.primary.main }, css]}>{children}</span>;
}
Loading

0 comments on commit eaedd83

Please sign in to comment.