feat: add label element to DateField and adjust styles

This commit is contained in:
jinhojang6 2023-04-10 22:00:51 +09:00 committed by Jon
parent 5f623c8700
commit 2537acf89d
11 changed files with 190 additions and 49 deletions

View File

@ -2,7 +2,7 @@ import React from 'react'
export type CalendarContextType = {
focusedDate: Date | null
size?: 'large' | 'medium'
size?: 'large' | 'medium' | 'small'
isDateFocused: (date: Date) => boolean
isDateSelected: (date: Date) => boolean
isDateHovered: (date: Date) => boolean

View File

@ -9,7 +9,7 @@ export default {
size: {
type: {
name: 'enum',
value: ['medium', 'large'],
value: ['small', 'medium', 'large'],
},
defaultValue: 'large',
},

View File

@ -20,7 +20,7 @@ export type CalendarProps = Omit<
value?: string
onChange: (data: Date) => void
handleRef: React.RefObject<HTMLElement>
size?: 'large' | 'medium'
size?: 'large' | 'medium' | 'small'
onClose?: () => void
}

View File

@ -1,10 +1,11 @@
export const dateFieldClasses = {
root: `lsd-date-field`,
label: 'lsd-date-field__label',
inputContainer: `lsd-date-field-input-container`,
input: `lsd-date-field-input-container__input`,
icon: `lsd-date-field-input-container__icon`,
iconButton: `lsd-date-field-input-container__icon-button`,
inputContainer: `lsd-date-field__input-container`,
input: `lsd-date-field__input-container__input`,
icon: `lsd-date-field__input-container__icon`,
iconButton: `lsd-date-field__input-container__icon-button`,
supportingText: 'lsd-date-field__supporting-text',
@ -13,4 +14,8 @@ export const dateFieldClasses = {
large: `lsd-date-field--large`,
medium: `lsd-date-field--medium`,
small: `lsd-date-field--small`,
outlined: `lsd-date-field--outlined`,
outlinedBottom: `lsd-date-field--outlined-bottom`,
}

View File

@ -8,7 +8,14 @@ export default {
size: {
type: {
name: 'enum',
value: ['medium', 'large'],
value: ['small', 'medium', 'large'],
},
defaultValue: 'large',
},
variant: {
type: {
name: 'enum',
value: ['outlined', 'outlined-bottom'],
},
defaultValue: 'large',
},
@ -24,6 +31,7 @@ export const Controlled: Story<DateFieldProps> = ({ ...args }) => {
}
Uncontrolled.args = {
id: 'label',
size: 'large',
supportingText: 'Supporting text',
disabled: false,
@ -32,9 +40,12 @@ Uncontrolled.args = {
error: false,
errorIcon: false,
clearButton: true,
variant: 'outlined-bottom',
label: 'Label',
}
Controlled.args = {
id: 'label',
size: 'large',
supportingText: 'Supporting text',
disabled: false,
@ -43,4 +54,6 @@ Controlled.args = {
error: false,
errorIcon: false,
clearButton: true,
variant: 'outlined-bottom',
label: 'Label',
}

View File

@ -4,10 +4,27 @@ import { dateFieldClasses } from './DateField.classes'
export const DateFieldStyles = css`
.${dateFieldClasses.root} {
width: auto;
border-bottom: 1px solid rgb(var(--lsd-border-primary));
box-sizing: border-box;
}
.${dateFieldClasses.label} {
display: block;
}
.${dateFieldClasses.icon} {
cursor: pointer;
display: flex;
align-items: center;
}
.${dateFieldClasses.outlined} {
border: 1px solid rgb(var(--lsd-border-primary));
}
.${dateFieldClasses.outlinedBottom} {
border-bottom: 1px solid rgb(var(--lsd-border-primary));
}
.${dateFieldClasses.inputContainer} {
display: flex;
align-items: center;
@ -48,32 +65,64 @@ export const DateFieldStyles = css`
}
.${dateFieldClasses.supportingText} {
width: fit-content;
margin-top: 20px;
position: absolute;
}
.${dateFieldClasses.large} {
width: 208px;
.${dateFieldClasses.label} {
margin: 0 0 6px 18px;
}
.${dateFieldClasses.inputContainer} {
height: 40px;
padding: 10px 14px;
}
.${dateFieldClasses.input} {
padding: 9px 13px 9px 17px;
}
.${dateFieldClasses.icon} {
padding: 12px 13px;
}
.${dateFieldClasses.supportingText} {
margin: 6px 18px 0 18px;
}
}
.${dateFieldClasses.medium} {
width: 188px;
.${dateFieldClasses.label} {
margin: 0 0 6px 14px;
}
.${dateFieldClasses.inputContainer} {
height: 32px;
padding: 6px 12px;
}
.${dateFieldClasses.input} {
padding: 5px 11px 5px 13px;
}
.${dateFieldClasses.icon} {
padding: 8px 11px;
}
.${dateFieldClasses.supportingText} {
margin: 6px 14px 0 14px;
}
}
.${dateFieldClasses.iconButton} {
padding: 0;
width: auto;
height: auto;
margin: 0;
border: 0;
svg {
width: 100%;
height: auto;
.${dateFieldClasses.small} {
width: 164px;
.${dateFieldClasses.label} {
margin: 0 0 6px 12px;
}
.${dateFieldClasses.inputContainer} {
height: 28px;
}
.${dateFieldClasses.input} {
padding: 5px 9px 5px 11px;
}
.${dateFieldClasses.icon} {
padding: 6px 9px;
}
.${dateFieldClasses.supportingText} {
margin: 6px 12px 0 12px;
}
}
`

View File

@ -1,7 +1,6 @@
import clsx from 'clsx'
import React, { useRef } from 'react'
import { useInput } from '../../utils/useInput'
import { IconButton } from '../IconButton'
import { CloseIcon, ErrorIcon } from '../Icons'
import { Typography } from '../Typography'
import { dateFieldClasses } from './DateField.classes'
@ -11,7 +10,8 @@ export type DateFieldProps = Omit<
'onChange' | 'value'
> &
Pick<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> & {
size?: 'large' | 'medium'
label?: React.ReactNode
size?: 'large' | 'medium' | 'small'
error?: boolean
errorIcon?: boolean
clearButton?: boolean
@ -23,11 +23,13 @@ export type DateFieldProps = Omit<
icon?: React.ReactNode
onIconClick?: () => void
inputProps?: React.InputHTMLAttributes<HTMLInputElement>
variant?: 'outlined' | 'outlined-bottom'
}
export const DateField: React.FC<DateFieldProps> & {
classes: typeof dateFieldClasses
} = ({
label,
size = 'large',
error = false,
errorIcon = false,
@ -42,6 +44,7 @@ export const DateField: React.FC<DateFieldProps> & {
icon,
onIconClick,
inputProps = {},
variant = 'outlined-bottom',
...props
}) => {
const ref = useRef<HTMLInputElement>(null)
@ -49,6 +52,8 @@ export const DateField: React.FC<DateFieldProps> & {
const onCancel = () => input.setValue('')
const inputId = inputProps?.id ?? (props.id || 'date-field') + '-input'
return (
<div
aria-disabled={disabled ? 'true' : 'false'}
@ -61,8 +66,26 @@ export const DateField: React.FC<DateFieldProps> & {
error && dateFieldClasses.error,
)}
>
<div className={dateFieldClasses.inputContainer}>
{label && (
<Typography
htmlFor={inputId}
className={dateFieldClasses.label}
variant="label2"
component="label"
>
{label}
</Typography>
)}
<div
className={clsx(
dateFieldClasses.inputContainer,
variant === 'outlined'
? dateFieldClasses.outlined
: dateFieldClasses.outlinedBottom,
)}
>
<input
id={inputId}
type="date"
placeholder={placeholder}
{...inputProps}
@ -72,32 +95,28 @@ export const DateField: React.FC<DateFieldProps> & {
className={clsx(inputProps.className, dateFieldClasses.input)}
/>
{icon ? (
<IconButton
disabled={disabled}
className={dateFieldClasses.iconButton}
<span
className={dateFieldClasses.icon}
onClick={() => !disabled && onIconClick && onIconClick()}
>
{icon}
</IconButton>
</span>
) : error && errorIcon ? (
<ErrorIcon color="primary" className={dateFieldClasses.icon} />
<span className={dateFieldClasses.icon}>
<ErrorIcon color="primary" />
</span>
) : clearButton && input.filled ? (
<IconButton
disabled={disabled}
<span
onClick={() => !disabled && onCancel()}
aria-label="clear"
className={dateFieldClasses.iconButton}
className={dateFieldClasses.icon}
>
<CloseIcon color="primary" className={dateFieldClasses.icon} />
</IconButton>
<CloseIcon color="primary" />
</span>
) : null}
</div>
{supportingText && (
<div className={clsx(dateFieldClasses.supportingText)}>
<Typography
variant={size === 'large' ? 'label1' : 'label2'}
component="p"
>
<Typography variant={'label2'} component="p">
{supportingText}
</Typography>
</div>

View File

@ -1,5 +1,9 @@
export const datePickerClasses = {
root: `lsd-date-picker`,
calendar: `lsd-date-picker-calendar`,
calendar: `lsd-date-picker__calendar`,
large: `lsd-date-picker--large`,
medium: `lsd-date-picker--medium`,
small: `lsd-date-picker--small`,
}

View File

@ -8,7 +8,14 @@ export default {
size: {
type: {
name: 'enum',
value: ['medium', 'large'],
value: ['small', 'medium', 'large'],
},
defaultValue: 'large',
},
variant: {
type: {
name: 'enum',
value: ['outlined', 'outlined-bottom'],
},
defaultValue: 'large',
},
@ -24,6 +31,7 @@ export const Controlled: Story<DatePickerProps> = ({ ...args }) => {
}
Uncontrolled.args = {
id: 'label',
supportingText: 'Supporting text',
disabled: false,
error: false,
@ -33,9 +41,12 @@ Uncontrolled.args = {
clearButton: true,
withCalendar: true,
size: 'large',
variant: 'outlined-bottom',
label: 'Label',
}
Controlled.args = {
id: 'label',
supportingText: 'Supporting text',
disabled: false,
error: false,
@ -45,4 +56,6 @@ Controlled.args = {
clearButton: true,
withCalendar: true,
size: 'large',
variant: 'outlined-bottom',
label: 'Label',
}

View File

@ -1,5 +1,6 @@
import { css } from '@emotion/react'
import { datePickerClasses } from './DatePicker.classes'
import { dateFieldClasses } from '../DateField/DateField.classes'
export const DatePickerStyles = css`
.${datePickerClasses.root} {
@ -9,4 +10,22 @@ export const DatePickerStyles = css`
.${datePickerClasses.calendar} {
border-top: none !important;
}
.${datePickerClasses.large} {
.${dateFieldClasses.large} {
width: 318px;
}
}
.${datePickerClasses.medium} {
.${dateFieldClasses.medium} {
width: 290px;
}
}
.${datePickerClasses.small} {
.${dateFieldClasses.small} {
width: 262px;
}
}
`

View File

@ -16,6 +16,7 @@ export type DatePickerProps = Omit<
'onChange' | 'value'
> &
Pick<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> & {
label?: React.ReactNode
error?: boolean
errorIcon?: boolean
clearButton?: boolean
@ -25,13 +26,22 @@ export type DatePickerProps = Omit<
value?: string
defaultValue?: string
placeholder?: string
size?: 'large' | 'medium'
size?: 'large' | 'medium' | 'small'
variant?: 'outlined' | 'outlined-bottom'
inputProps?: React.InputHTMLAttributes<HTMLInputElement>
}
export const DatePicker: React.FC<DatePickerProps> & {
classes: typeof datePickerClasses
} = ({ value: valueProp, onChange, withCalendar = true, ...props }) => {
} = ({
label,
size = 'large',
value: valueProp,
onChange,
withCalendar = true,
variant = 'outlined-bottom',
...props
}) => {
const ref = useRef<HTMLDivElement>(null)
const [openCalendar, setOpenCalendar] = useState(false)
@ -48,18 +58,27 @@ export const DatePicker: React.FC<DatePickerProps> & {
const handleDateChange = (date: Date) =>
input.setValue(dateToISODateString(removeDateTimezoneOffset(date)))
const inputId = (props.id || 'date-picker') + '-input'
return (
<div
id={inputId}
ref={ref}
{...props}
className={clsx(props.className, datePickerClasses.root)}
className={clsx(
props.className,
datePickerClasses.root,
datePickerClasses[size],
)}
>
<DateField
label={label}
size={size}
variant={variant}
icon={withCalendar && <CalendarIcon color="primary" />}
onIconClick={() => setOpenCalendar((prev) => !prev)}
value={input.value}
onChange={input.onChange}
style={{ width: '310px' }}
{...props}
>
<Portal id="calendar">