최근에 MUI(Material-UI)의 Datepicker를 Input만 스타일을 수정해서 사용하는 경우가 있었습니다.
MUI의 Datepicker는 기본적으로 아래와 같은 Input 스타일을 가지고 있습니다.
저는 여기에 우리 팀이 커스텀해둔 TextField를 사용하고, 캘린더 아이콘을 사용하고 싶었습니다.
Datepicker 컴포넌트는 당연하게도 Input에 사용할 컴포넌트를 바꿀 수 있는 방법을 제공합니다.
Datepicker의 prop중에 renderInput prop을 이용하면 됩니다. renderInput prop에 Input으로 사용할 컴포넌트를 return하는 함수를 넣어줍니다. (함수의 매개변수는 공식문서를 참고하시면 좋습니다.)
저는 TextField를 return 하게 넣어줬습니다. 그리고 캘린더 아이콘을 변경하기 위해서 TextField의 InputProps.endAdornment 속성에 캘린더 아이콘을 버튼에 넣었습니다. (TextField의 endAdornment 관련 문서)
<DatePicker
// ...
renderInput={({ inputRef, inputProps }: any) => (
<TextField
InputProps={{
endAdornment: (
<MuiInputAdornment position="end">
<Button>
<CalendarIcon />
</Button>
</MuiInputAdornment>
),
}}
/>
)}
// ...
/>
이렇게 하니깐 Input의 모양은 제가 원하던 모양으로 만들어졌습니다. 그런데, 한가지 이슈가 있었습니다. Datepicker의 기본 캘린더 아이콘을 사용할 때는 아이콘을 눌렀을 때 날짜선택창이 나타나는데, 제가 커스텀으로 넣은 캘린더 아이콘 버튼을 눌렀을 때는 날짜선택창이 나타나지 않았습니다. (지금 생각해보니 MUI 기본 로직에서 제가 넣은 버튼에 알아서 onClick 핸들러를 넣을 수 있는.. 단서가 아무 것도 없네요..ㅎㅎㅎ)
Datepicker의 기본 캘린더 아이콘을 사용하지 않을 것이라면, 날짜선택창이 뜰 수 있게 직접 제어해줘야 합니다. 이를 위해서 Datepicker 컴포넌트에는 open , onOpen , onClose prop이 있습니다.
수동으로 날짜선택창을 제어하려면, 날짜선택창의 열림 상태를 저장하는 state를 하나 만들어서 open prop에 할당하고, Datepicker의 onOpen , onClose prop에 각각 open state를 true, false로 변경하는 핸들러 함수를 할당하면 됩니다.
그리고 커스텀 캘린더 아이콘 버튼의 onClick 이벤트 리스너에도 open state를 true로 변경하는 핸들러 함수를 할당합니다.
// 날짜 선택창을 띄우는 상태 제어
const [isOpen, setIsOpen] = useState<boolean>(false);
const setOpen = useCallback(() => {
setIsOpen(true);
}, [setIsOpen]);
const setClose = useCallback(() => {
setIsOpen(false);
}, [setIsOpen]);
// ...
<DatePicker
open={isOpen}
onOpen={setOpen}
onClose={setClose}
// ...
renderInput={({ inputRef, inputProps }: any) => (
<Box>
<TextField
InputProps={{
endAdornment: (
<MuiInputAdornment position="end">
<Button onClick={setOpen}>
<CalendarIcon />
</Button>
</MuiInputAdornment>
),
}}
/>
</Box>
)}
// ...
/>
여기까지 하면 원하는 Input 스타일을 가진 Datepicker 컴포넌트를 만들 수 있습니다.
그런데, 한가지 아쉬운 점이 있었습니다. 날짜선택창을 열었을 때, 캘린더 아이콘 버튼을 포함한 TextField를 기준으로 아래-가운데 정렬되지 않고 오직 값이 들어가는 input 영역을 기준으로 아래-가운데 정렬되었습니다. 그래서 눈으로 보기에 날짜선택창이 가운데보다 조금 왼쪽으로 이동되어 있어 보였습니다.
이 문제는, Datepicker가 날짜선택창을 띄울 때, 위치를 잡는 기준이 input에 있어서 그렇습니다.
그래서 아래처럼 ref를 이용해서 날짜선택창이 위치를 잡는 Element를 직접 지정해주었습니다. 저는 TextField 전체를 감싸는 Box 컴포넌트를 추가하여 그곳을 기준으로 날짜선택창이 뜨도록 바꿔주었습니다.
// Datepicker 컴포넌트의 prop
PopperProps={{
placement: 'bottom', // 날짜선택창이 뜨는 위치
anchorEl: popperAnchorRef.current,
}}
// Datepicker 컴포넌트의 renderInput에서 return하는 컴포넌트
<Box ref={popperAnchorRef}>
<TextField
// ...
/>
</Box>
여기까지해서 원하는 스타일을 가진 Datepicker를 완성할 수 있었습니다.
Amplify Console에서 환경(브랜치) 제거를 막자 (0) | 2022.10.25 |
---|---|
MSW를 이용해서 쉽고 깔끔하게 API Mocking 하자 (0) | 2022.05.11 |
emotion을 이용해서 MUI(Material-UI)의 Tooltip 컴포넌트에 커스텀 스타일 적용하는 방법 (1) | 2021.11.10 |
Error Handling(에러 핸들링) - (1) 고민 풀어보기 (0) | 2021.10.13 |
AWS S3에 파일을 업로드 하기 위한 Pre-signed URL과 Pre-signed POST (0) | 2021.09.08 |
댓글 영역