기록

React Antd CollapseForm 검색 컴포넌트(1) 본문

React

React Antd CollapseForm 검색 컴포넌트(1)

dev.jung 2021. 7. 28. 18:00

서론

antd를 이용한 form 컴포넌트 제작.
FormItem 하나하나씩 나누어서 만들기.

 

사용 패키지

yarn add antd @ant-design/icons styled-components
yarn add -D @typs/styled-components
yarn add -D craco-antd @craco/craco
yarn add moment

 

폴더구조

 

Craco 이용  antd  테마 커스텀

package.json 에서 start 스크립트를  craco start로 변경해준다.

 

/craco.config.js

module.exports = {
  plugins: [
    {
      plugin: require("craco-antd"),
      options: {
        customizeTheme: {
          "@primary-color": "#797b9e",
          "@text-color": "#131313",
          "@font-family": "IBMPlexSansKr",
          "@btn-shadow": "none",
          "@wave-animation-width": "0px",
        },
      },
    },
  ],
};

 

CollapseSearchForm 컴포넌트

폼에 들어가 컴포넌트들을 감쌀 컴포넌트를 만든다.

FormProps를 확장하여 form 속성들을 받아온다.
기본적으로 들어가는 컴포넌트들은 children 으로 받아오고,

detail로 상세 검색에 해당하는 컴포넌트들은 받아온다.

검색 버튼을 폼에 입력한 데이터를 출력해주기 위해 submit으로 만들고 form의 onFinish에서 콘솔 로그로 확인을 한다.
detailStatus로 상세 검색에 해당하는 컴포넌트들을 제어한다.

 

src/component/Form/CollapseSearchForm/index.tsx

import { ReactNode, useState } from "react";

import { Form, Button, FormProps, Row } from "antd";
import { DownOutlined, UpOutlined } from "@ant-design/icons";
import styled from "styled-components";

const StyledCollapseSearchForm = styled(Form)`
  position: relative;
  width: 100%;
  background: #ffffff;
  .ant-form-item {
    margin-bottom: 0;
    padding: 20px 0;
    border-bottom: 1px solid rgba(0, 0, 0, 0.2);
  }

  .submit-btn {
    background: #000000;
  }

  .reset-btn,
  .submit-btn {
    width: 140px;
    height: 40px;
  }

  .detail-btn {
    position: absolute;
    width: 120px;
    height: 40px;
    right: 0;
  }

  .button-box {
    margin-top: 10px;
  }
`;

interface CollapseSearchFormProps extends FormProps {
  detail: ReactNode;
}

export const CollapseSearchForm = (props: CollapseSearchFormProps) => {
  const { detail, children, ...rest } = props;
  const [detailStatus, setDetailStatus] = useState(false);

  return (
    <StyledCollapseSearchForm {...rest} onFinish={(e) => console.log(e)}>
      {children}
      {detailStatus && detail}
      <Row justify="center" className="button-box">
        <Button className="submit-btn" htmlType="submit" type="primary">
          검색
        </Button>
        <Button
          className="detail-btn"
          onClick={() => setDetailStatus(!detailStatus)}
        >
          상세 검색 {detailStatus ? <UpOutlined /> : <DownOutlined />}
        </Button>
      </Row>
    </StyledCollapseSearchForm>
  );
};

 

App.tsx 에 적용시켜보면 아직 만든 컴포넌트들이 없어서 detail에 빈 엘리먼트를 넘겨주었다. 

src/App.tsx

import { Layout } from "antd";
import { CollapseSearchForm } from "component";
import styled from "styled-components";

const { Content } = Layout;

const StyledApp = styled(Layout)`
  left: 0;
  right: 0;
  margin: 0 auto;
  width: 750px;
  height: 100vh;
`;

const App = () => {
  return (
    <StyledApp>
      <Content>
        <CollapseSearchForm detail={<></>} />
      </Content>
    </StyledApp>
  );
};

export default App;

실행을 시켜보면 요렇게 나온다. 이제 폼 안에 들어갈 추가적인 컴포넌트들을 제작해본다.

 

FormInput 컴포넌트

텍스트 입력창으로 사용할 input 컴포넌트를 만들어본다.
FormItemProps를 확장해서 FormItem 속성들을 받아오고

inputProps로 input의 속성들을 받아온다.

 

src/component/Form/FormInput/index.tsx

import { Form, FormItemProps, Input, InputProps } from "antd";
import styled from "styled-components";

const StyledFormInput = styled(Form.Item)`
  .ant-input {
    width: 400px;
  }
`;

interface FormInputProp extends FormItemProps {
  inputProps?: InputProps;
}

export const FormInput = (props: FormInputProp) => {
  const { inputProps, ...rest } = props;
  return (
    <StyledFormInput {...rest}>
      <Input {...inputProps} />
    </StyledFormInput>
  );
};

 

App.tsx에 적용시키기

src/App.tsx

import { Layout } from "antd";
import { CollapseSearchForm, FormInput } from "component";
import styled from "styled-components";

const { Content } = Layout;

const StyledApp = styled(Layout)`
  left: 0;
  right: 0;
  margin: 0 auto;
  width: 750px;
  height: 100vh;
`;

const App = () => {
  return (
    <StyledApp>
      <Content>
        <CollapseSearchForm detail={<></>}>
          <FormInput />
        </CollapseSearchForm>
      </Content>
    </StyledApp>
  );
};

export default App;

실행

 

이제 입력창이 어떤 입력창인지 알기 위해 label과 어떤 식으로 입력하면 되는지 placeholder를 추가해본다.

label, colon(라벨 콜론 여부), labelCol(라벨 크기 지정), labelAlign(라벨 정렬 지정) 은 antd의 Form.Item의 속성들이고

inputProps는 antd Input의 속성들이다. 

import { Layout } from "antd";
import { CollapseSearchForm, FormInput } from "component";
import styled from "styled-components";

const { Content } = Layout;

const StyledApp = styled(Layout)`
  left: 0;
  right: 0;
  margin: 0 auto;
  width: 750px;
  height: 100vh;
`;

const App = () => {
  return (
    <StyledApp>
      <Content>
        <CollapseSearchForm detail={<></>}>
          <FormInput
            label="가맹점"
            colon={false}
            labelCol={{ span: 4 }}
            labelAlign="left"
            inputProps={{ placeholder: "가맹점을 입력해주세요" }}
          />
        </CollapseSearchForm>
      </Content>
    </StyledApp>
  );
};

export default App;

실행을 하면 이렇게 입력 폼 하나가 완성되었다.
상세 검색 시 펼쳐지는 것을 확인하기 위해 detail에 FormInput을 넘겨보자. 

 

import { Layout } from "antd";
import { CollapseSearchForm, FormInput } from "component";
import styled from "styled-components";

const { Content } = Layout;

const StyledApp = styled(Layout)`
  left: 0;
  right: 0;
  margin: 0 auto;
  width: 750px;
  height: 100vh;
`;

const App = () => {
  return (
    <StyledApp>
      <Content>
        <CollapseSearchForm
          detail={
            <FormInput
              label="전화번호"
              colon={false}
              labelCol={{ span: 4 }}
              labelAlign="left"
              inputProps={{ placeholder: "전화번호를 입력해주세요" }}
            ></FormInput>
          }
        >
          <FormInput
            label="가맹점"
            colon={false}
            labelCol={{ span: 4 }}
            labelAlign="left"
            inputProps={{ placeholder: "가맹점을 입력해주세요" }}
          ></FormInput>
        </CollapseSearchForm>
      </Content>
    </StyledApp>
  );
};

export default App;

실행을 시키면 첫 로드엔 안 보이고 상세검색을 눌렀을 시에 추가된 컴포넌트가 나타나는 걸 확인할 수 있다.

 

같은 방식으로 다른 컴포넌트들도 만들어 본다.

 

 

FormCheckBox 컴포넌트

이번에 만들어 볼 것은 체크박스 컴포넌트이다.

둥근 체크 박스와 네모난 체크박스를 같이 사용할 수 있게 컴포넌트를 만든다.

기본적으로 전체 선택을 넣는다.

option으로 체크할 값들을 받아온다.

 

src/component/Form/FormCheckBox/index.tsx

import { Checkbox, Form, FormItemProps } from "antd";
import styled, { css } from "styled-components";

const StyledFormCheckBox = styled(Form.Item)<{ $circle?: boolean }>`
  .ant-checkbox-checked .ant-checkbox-inner {
    background: #000000 !important;
  }
  ${(props) =>
    props.$circle &&
    css`
      .ant-checkbox-inner,
      .ant-checkbox-inner,
      .ant-checkbox:hover {
        border-radius: 10px;
      }
      .ant-checkbox-checked::after {
        border: none;
      }
    `}
`;

interface FormCheckBoxProps extends FormItemProps {
  option: { label: string; value: string }[];
  circle?: boolean;
}

export const FormCheckBox = (props: FormCheckBoxProps) => {
  const { option, name, circle, ...rest } = props;

  return (
    <StyledFormCheckBox {...rest} $circle={circle}>
      <Checkbox>전체</Checkbox>
      <Form.Item name={name} noStyle>
        <Checkbox.Group options={option} />
      </Form.Item>
    </StyledFormCheckBox>
  );
};

App.tsx 에 두 가지 종류의 체크박스를 넣어본다.

둥근 체크박스는 detail에 넣는다.

 

src/App.tsx

storeStatusOptions와 storeDivisionOptions를 만들어 각각 체크박스에 넣어준다.

둥근 체크박스로 사용할 체크박스는 circle을 넘겨준다.

import { Layout } from "antd";
import { CollapseSearchForm, FormCheckBox, FormInput } from "component";
import styled from "styled-components";

const { Content } = Layout;

const StyledApp = styled(Layout)`
  left: 0;
  right: 0;
  margin: 0 auto;
  width: 750px;
  height: 100vh;
`;

const App = () => {
  const storeStatusOptions = [
    { label: "신청", value: "APPLY" },
    { label: "심사", value: "JUDGE" },
    { label: "보류", value: "HOLD" },
    { label: "승인", value: "OKAY" },
    { label: "해지", value: "TERMINATION" },
    { label: "수정요청", value: "MODIFYINFO" },
  ];

  const storeDivisionOptions = [
    { label: "개인", value: "individual" },
    { label: "법인", value: "corporation" },
  ];
  return (
    <StyledApp>
      <Content>
        <CollapseSearchForm
          detail={
            <>
              <FormInput
                label="전화번호"
                colon={false}
                labelCol={{ span: 4 }}
                labelAlign="left"
                inputProps={{ placeholder: "전화번호를 입력해주세요" }}
              ></FormInput>
              <FormCheckBox
                label="가맹점 구분"
                circle
                labelAlign="left"
                labelCol={{ span: 4 }}
                colon={false}
                option={storeDivisionOptions}
              ></FormCheckBox>
            </>
          }
        >
          <FormInput
            label="가맹점"
            colon={false}
            labelCol={{ span: 4 }}
            labelAlign="left"
            inputProps={{ placeholder: "가맹점을 입력해주세요" }}
          ></FormInput>
          <FormCheckBox
            label="가맹점 처리상태"
            labelAlign="left"
            labelCol={{ span: 4 }}
            colon={false}
            option={storeStatusOptions}
          ></FormCheckBox>
        </CollapseSearchForm>
      </Content>
    </StyledApp>
  );
};

export default App;

 

실행을 시키면 이런 식으로 체크박스가 생성된다 

전체 선택이 안되기 때문에 전체 선택이 되게 만들어본다.

src/component/Form/FormCheckBox/index.tsx

해당하는 폼의 value를 변경해 주기 위해 antd에서 제공하는 formInstant를 받아와 setFiledsValue를 사용했다.

여기서 name은 해당 입력 폼의 데이터 이름이다.

import { Checkbox, Form, FormInstance, FormItemProps } from "antd";
import { CheckboxChangeEvent } from "antd/lib/checkbox";
import { useState } from "react";
import styled, { css } from "styled-components";

const StyledFormCheckBox = styled(Form.Item)<{ $circle?: boolean }>`
  .ant-checkbox-checked .ant-checkbox-inner {
    background: #000000 !important;
  }
  ${(props) =>
    props.$circle &&
    css`
      .ant-checkbox-inner,
      .ant-checkbox-inner,
      .ant-checkbox:hover {
        border-radius: 10px;
      }
      .ant-checkbox-checked::after {
        border: none;
      }
    `}
`;

interface FormCheckBoxProps extends FormItemProps {
  option: { label: string; value: string }[];
  circle?: boolean;
  form: FormInstance;
}

export const FormCheckBox = (props: FormCheckBoxProps) => {
  const { option, name, form, circle, ...rest } = props;
  const [checkAll, setCheckAll] = useState(false);

  const onCheckAllChange = (e: CheckboxChangeEvent) => {
    form.setFieldsValue({
      [`${name}`]: e.target.checked ? option.map((arr) => arr.value) : [],
    });
    setCheckAll(e.target.checked);
  };

  return (
    <StyledFormCheckBox {...rest} $circle={circle}>
      <Checkbox onChange={onCheckAllChange} checked={checkAll}>
        전체
      </Checkbox>
      <Form.Item name={name} noStyle>
        <Checkbox.Group options={option} />
      </Form.Item>
    </StyledFormCheckBox>
  );
};

 

src/App.tsx

antd에서 제공하는 form의 useForm을 사용하여 collapseForm에 넘겨준다.

collapseForm에 있는 입력 폼들의 값들을 제어할 수 있게 한다.

form을 활용하여 value를 변경시키기 위해 FormCheckBox에도 form을 넘겨주고 추가적으로 name을 넘겨준다.

import { Layout, Form } from "antd";
import { CollapseSearchForm, FormCheckBox, FormInput } from "component";
import styled from "styled-components";

const { Content } = Layout;

const StyledApp = styled(Layout)`
  left: 0;
  right: 0;
  margin: 0 auto;
  width: 750px;
  height: 100vh;
`;

const App = () => {
  const [form] = Form.useForm();
  const storeStatusOptions = [
    { label: "신청", value: "APPLY" },
    { label: "심사", value: "JUDGE" },
    { label: "보류", value: "HOLD" },
    { label: "승인", value: "OKAY" },
    { label: "해지", value: "TERMINATION" },
    { label: "수정요청", value: "MODIFYINFO" },
  ];

  const storeDivisionOptions = [
    { label: "개인", value: "individual" },
    { label: "법인", value: "corporation" },
  ];

  return (
    <StyledApp>
      <Content>
        <CollapseSearchForm
          form={form}
          detail={
           <>
              <FormInput
                label="전화번호"
                colon={false}
                labelCol={{ span: 4 }}
                labelAlign="left"
                inputProps={{ placeholder: "전화번호를 입력해주세요" }}
              ></FormInput>
              <FormCheckBox
                label="가맹점 구분"
                circle
                form={form}
                name="storeDivision"
                labelAlign="left"
                labelCol={{ span: 4 }}
                colon={false}
                option={storeDivisionOptions}
              ></FormCheckBox>
           </>
        >
          <FormInput
            label="가맹점"
            colon={false}
            labelCol={{ span: 4 }}
            labelAlign="left"
            inputProps={{ placeholder: "가맹점을 입력해주세요" }}
          ></FormInput>
          <FormCheckBox
            form={form}
            label="가맹점 처리상태"
            name="storeStatus"
            labelAlign="left"
            labelCol={{ span: 4 }}
            colon={false}
            option={storeStatusOptions}
          ></FormCheckBox>
        </CollapseSearchForm>
      </Content>
    </StyledApp>
  );
};

export default App;

 

실행화면

 

체크박스까지 완성해봤다.

다음 포스트에서 추가적인 컴포넌트 셀렉트와 기간 설정하는 컴포넌트를 만들어본다.

반응형

'React' 카테고리의 다른 글

React Antd CollapseForm 검색 컴포넌트(2)  (0) 2021.07.29
useState, useEffect  (0) 2021.06.03
state, props  (0) 2021.06.03
함수 컴포넌트, 클래스 컴포넌트  (0) 2021.06.03
Simple Collapse  (0) 2021.06.03
Comments