Skip to content

Commit

Permalink
Merge pull request #206 from mash-up-kr/release/v1.1.0
Browse files Browse the repository at this point in the history
Main Release/v1.1.0
  • Loading branch information
mango906 authored Sep 1, 2022
2 parents bb266c1 + 1164488 commit 13d1292
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 131 deletions.
100 changes: 76 additions & 24 deletions src/components/common/SearchOptionBar/SearchOptionBar.component.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { Dispatch, SetStateAction, useLayoutEffect, useMemo, useRef } from 'react';
import React, { FormEvent, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { Input, Select } from '@/components';
import { ButtonSize, ButtonShape } from '@/components/common/Button/Button.component';
import * as Styled from './SearchOptionBar.styled';
Expand All @@ -17,42 +18,71 @@ export interface ApplicationFilterValuesType {

interface SearchOptionBarProps {
placeholder?: string;
searchWord: { value: string };
handleSubmit: React.FormEventHandler<HTMLFormElement>;
filterValues?: ApplicationFilterValuesType;
setFilterValues?: Dispatch<SetStateAction<ApplicationFilterValuesType>>;
showFilterValues?: boolean;
}

const DEFAULT = { label: '전체', value: '' };

const SearchOptionBar = ({
placeholder,
searchWord,
handleSubmit,
filterValues,
setFilterValues,
}: SearchOptionBarProps) => {
const SearchOptionBar = ({ placeholder, showFilterValues = true }: SearchOptionBarProps) => {
const [searchParams, setSearchParams] = useSearchParams();
const ref = useRef<HTMLInputElement>(null);

const confirmStatus = searchParams.get('confirmStatus') || '';
const resultStatus = searchParams.get('resultStatus') || '';
const searchWord = searchParams.get('searchWord') || '';

const [filterValues, setFilterValues] = useState<ApplicationFilterValuesType>({
confirmStatus: { label: '', value: '' },
resultStatus: { label: '', value: '' },
});

const [searchKeyword, setSearchKeyword] = useState<{ value: string }>({ value: '' });

const handleApplicationConfirmStatus = (option: SelectOption) => {
setFilterValues?.((prev) => ({
...prev,
confirmStatus: option,
}));
if (option.value === DEFAULT.value) {
searchParams.delete('confirmStatus');
setSearchParams(searchParams);
return;
}

searchParams.set('confirmStatus', option.value);
setSearchParams(searchParams);
};

const handleApplicationResultStatus = (option: SelectOption) => {
setFilterValues?.((prev) => ({
...prev,
resultStatus: option,
}));
if (option.value === DEFAULT.value) {
searchParams.delete('resultStatus');
setSearchParams(searchParams);
return;
}

searchParams.set('resultStatus', option.value);
setSearchParams(searchParams);
};

const handleSearch = (
e: { target: { searchWord: { value: string } } } & FormEvent<HTMLFormElement>,
) => {
const { value } = e.target.searchWord;

e.preventDefault();

if (!value) {
searchParams.delete('searchWord');
setSearchParams(searchParams);

return;
}

searchParams.set('searchWord', value);
setSearchParams(searchParams);
};

useLayoutEffect(() => {
if (ref.current) {
ref.current.value = searchWord.value;
ref.current.value = searchKeyword.value;
}
}, [searchWord]);
}, [searchKeyword]);

const applicationConfirmStatusOptions = useMemo(
() => [
Expand Down Expand Up @@ -88,9 +118,31 @@ const SearchOptionBar = ({
[],
);

useEffect(() => {
const confirmStatusLabel =
applicationConfirmStatusOptions.find((option) => option.value === confirmStatus)?.label ?? '';

const resultStatusLabel =
applicationResultStatusOptions.find((option) => option.value === resultStatus)?.label ?? '';

setFilterValues({
confirmStatus: { label: confirmStatusLabel, value: confirmStatus },
resultStatus: { label: resultStatusLabel, value: resultStatus },
});
}, [
applicationConfirmStatusOptions,
applicationResultStatusOptions,
confirmStatus,
resultStatus,
]);

useEffect(() => {
setSearchKeyword({ value: searchWord });
}, [searchWord]);

return (
<Styled.BarContainer onSubmit={handleSubmit}>
{filterValues && (
<Styled.BarContainer onSubmit={handleSearch}>
{showFilterValues && (
<Styled.SelectContainer>
<div>
<div>합격여부</div>
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/useConvertToXlsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ interface UseExportXlsxProps<T> {
}

const useConvertToXlsx = <T>({ workSheet, teamName, isLoading }: UseExportXlsxProps<T>) => {
const workBookRef = useRef<WorkBook>(utils.book_new());
const workBookRef = useRef<WorkBook>();

useEffect(() => {
if (workSheet) {
workBookRef.current = utils.book_new();
Expand All @@ -17,7 +18,7 @@ const useConvertToXlsx = <T>({ workSheet, teamName, isLoading }: UseExportXlsxPr
}
}, [teamName, workSheet, isLoading]);

return { workBook: workBookRef.current };
return { getWorkBook: () => workBookRef.current };
};

export default useConvertToXlsx;
76 changes: 76 additions & 0 deletions src/hooks/useHistory.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { act } from '@testing-library/react';
import { renderHook } from '@testing-library/react-hooks';

import useHistory from './useHistory';

const mockNavigate = jest.fn();

const mockUseLocation = jest.fn().mockImplementation(() => ({ state: {} }));

jest.mock('react-router-dom', () => ({
useLocation: () => mockUseLocation(),
useNavigate: jest.fn().mockImplementation(() => mockNavigate),
}));

describe('useHistory', () => {
it('from 값이 존재한다면 뒤로가기 클릭 시 from으로 이동한다', () => {
// Given
const from = 'from';
mockUseLocation.mockReturnValueOnce({
state: {
from,
},
});

// When
const { result } = renderHook(() => useHistory());
act(() => result.current.handleGoBack());

// Then
expect(mockNavigate).toBeCalledWith(from);
});

it('from 값이 존재하지 않고 defaultPath가 있다면 defaultPath로 이동한다', () => {
// Given
const defaultPath = 'default-path';

// When
const { result } = renderHook(() => useHistory());
act(() => result.current.handleGoBack(defaultPath));

// Then
expect(mockNavigate).toBeCalledWith(defaultPath);
});

it('from 값과 defaultPath값 모두 존재하지 않는다면 뒤로 이동한다', () => {
// Given

// When
const { result } = renderHook(() => useHistory());
act(() => result.current.handleGoBack());

// Then
expect(mockNavigate).toBeCalledWith(-1);
});

it('shouldClearQueryString값이 false라면 from값에 쿼리스트링도 함께 저장된다.', () => {
// Given
const from = '/from';
const queryString = '?queryString=queryString';
const fromWithQueryString = `${from}${queryString}`;
const to = '/to';

mockUseLocation.mockReturnValueOnce({
pathname: from,
search: queryString,
});

// When
const { result } = renderHook(() => useHistory(false));
act(() => result.current.handleNavigate(to));

// Then

expect(mockNavigate).toBeCalledWith(to, { state: { from: fromWithQueryString } });
});
});
9 changes: 7 additions & 2 deletions src/hooks/useHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@ import { useLocation, useNavigate } from 'react-router-dom';

type HistoryLocationFromState = { from: string } | undefined;

const useHistory = () => {
const useHistory = (shouldClearQueryString = true) => {
const navigate = useNavigate();
const location = useLocation();
const { search } = location ?? {};
const { from } = (location.state as HistoryLocationFromState) ?? {};

const handleNavigate = (to: string) => {
const currentPath = location.pathname;
const currentPathWithQueryString = `${currentPath}${search}`;
const fromPath = shouldClearQueryString ? currentPath : currentPathWithQueryString;

navigate(to, {
state: {
from: location.pathname,
from: fromPath,
},
});
};
Expand Down
27 changes: 8 additions & 19 deletions src/pages/ApplicationFormList/ApplicationFormList.page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FormEvent, useEffect, useLayoutEffect, useMemo, useState } from 'react';
import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react';
import { useRecoilStateLoadable, useRecoilValue } from 'recoil';
import { useLocation, useSearchParams } from 'react-router-dom';

Expand Down Expand Up @@ -42,14 +42,13 @@ const ApplicationFormPreview = ({ questions }: { questions: Question[] }) => {
};

const ApplicationFormList = () => {
const [searchParams] = useSearchParams();
const [searchParams, setSearchParams] = useSearchParams();
const teamName = searchParams.get('team');
const teamId = useRecoilValue($teamIdByName(teamName));

const page = searchParams.get('page') || '1';
const size = searchParams.get('size') || '20';

const [searchWord, setSearchWord] = useState<{ value: string }>({ value: '' });
const searchWord = searchParams.get('searchWord') || '';

const { pathname, search } = useLocation();

Expand All @@ -72,7 +71,7 @@ const ApplicationFormList = () => {
page: parseInt(page, 10) - 1,
size: parseInt(size, 10),
teamId: parseInt(teamId, 10) || undefined,
searchWord: searchWord.value,
searchWord,
sort: sortParam,
}),
[page, size, teamId, searchWord, sortParam],
Expand All @@ -94,13 +93,6 @@ const ApplicationFormList = () => {

const { makeDirty, isDirty } = useDirty(1);

const handleSubmit = (
e: { target: { searchWord: { value: string } } } & FormEvent<HTMLFormElement>,
) => {
e.preventDefault();
setSearchWord({ value: e.target.searchWord.value });
};

const columns: TableColumn<ApplicationFormResponse>[] = [
{
title: '플랫폼',
Expand Down Expand Up @@ -167,13 +159,14 @@ const ApplicationFormList = () => {
}, [isLoading, tableRows]);

useEffect(() => {
setSearchWord({ value: '' });
searchParams.delete('searchWord');
setSearchParams(searchParams);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [teamName]);

useLayoutEffect(() => {
if (isDirty && !isLoading) {
window.scrollTo(0, 179);
window.scrollTo({ top: 179, left: 0, behavior: 'smooth' });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loadedTableRows]);
Expand All @@ -183,11 +176,7 @@ const ApplicationFormList = () => {
<Styled.Heading>지원서 설문지 내역</Styled.Heading>
<Styled.StickyContainer>
<TeamNavigationTabs />
<SearchOptionBar
placeholder="지원서 설문지 문서명 검색"
searchWord={searchWord}
handleSubmit={handleSubmit}
/>
<SearchOptionBar placeholder="지원서 설문지 문서명 검색" showFilterValues={false} />
</Styled.StickyContainer>
<Table
prefix="application-form"
Expand Down
Loading

0 comments on commit 13d1292

Please sign in to comment.