Skip to content

Commit

Permalink
fix(fields): normalize colors between states in DatePicker & DateRang…
Browse files Browse the repository at this point in the history
…ePicker
  • Loading branch information
ivangabriele committed Dec 21, 2022
1 parent 38219de commit 9cebad3
Show file tree
Hide file tree
Showing 14 changed files with 114 additions and 23 deletions.
4 changes: 3 additions & 1 deletion src/elements/Label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import styled from 'styled-components'
import type { LabelHTMLAttributes } from 'react'

export type LabelProps = LabelHTMLAttributes<HTMLLabelElement> & {
isDisabled?: boolean
isHidden?: boolean
}
export const Label = styled.label<{
isDisabled?: boolean
isHidden?: boolean
}>`
color: ${p => p.theme.color.slateGray};
color: ${p => (p.isDisabled ? p.theme.color.lightGray : p.theme.color.slateGray)};
display: ${p => (p.isHidden ? 'none' : 'table')};
font-size: 13px;
line-height: 1.4;
Expand Down
5 changes: 3 additions & 2 deletions src/elements/Legend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { Label } from './Label'
import type { HTMLAttributes } from 'react'

export type LegendProps = HTMLAttributes<HTMLLegendElement> & {
isDisabled?: boolean
isHidden?: boolean
}
export function Legend({ isHidden = false, ...nativeProps }: LegendProps) {
return <StyledLabel as="legend" isHidden={isHidden} {...nativeProps} />
export function Legend({ isDisabled = false, isHidden = false, ...nativeProps }: LegendProps) {
return <StyledLabel as="legend" isDisabled={isDisabled} isHidden={isHidden} {...nativeProps} />
}

const StyledLabel = styled(Label)`
Expand Down
11 changes: 9 additions & 2 deletions src/fields/DatePicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type { Promisable } from 'type-fest'

export type DatePickerProps = Omit<HTMLAttributes<HTMLFieldSetElement>, 'defaultValue' | 'onChange'> & {
defaultValue?: Date
disabled?: boolean
isCompact?: boolean
/** Only allow past dates until today. */
isHistorical?: boolean
Expand All @@ -42,6 +43,8 @@ export type DatePickerProps = Omit<HTMLAttributes<HTMLFieldSetElement>, 'default
}
export function DatePicker({
defaultValue,
// eslint-disable-next-line @typescript-eslint/naming-convention
disabled = false,
isCompact = false,
isHistorical = false,
isLabelHidden = false,
Expand Down Expand Up @@ -198,14 +201,17 @@ export function DatePicker({
}, [handleClickOutside])

return (
<Fieldset {...nativeProps}>
<Legend isHidden={isLabelHidden}>{label}</Legend>
<Fieldset disabled={disabled} {...nativeProps}>
<Legend isDisabled={disabled} isHidden={isLabelHidden}>
{label}
</Legend>

<Box ref={boxRef}>
<Field>
<DateInput
ref={dateInputRef}
defaultValue={selectedDateTupleRef.current}
disabled={disabled}
isCompact={isCompact}
isForcedFocused={isCalendarPickerOpenRef.current}
isLight={isLight}
Expand All @@ -220,6 +226,7 @@ export function DatePicker({
<TimeInput
ref={timeInputRef}
defaultValue={selectedTimeTupleRef.current}
disabled={disabled}
isCompact={isCompact}
isLight={isLight}
minutesRange={minutesRange}
Expand Down
30 changes: 23 additions & 7 deletions src/fields/DateRangePicker/DateInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import type { Promisable } from 'type-fest'

export type DateInputProps = Pick<NumberInputProps, 'onBack' | 'onPrevious' | 'onNext'> & {
defaultValue?: DateTuple
// TODO Check why TS thinks there is no `disabled` prop in `NumberInputProps`.
disabled: boolean
isCompact: boolean
isEndDate?: boolean
isForcedFocused: boolean
Expand All @@ -24,6 +26,8 @@ export type DateInputProps = Pick<NumberInputProps, 'onBack' | 'onPrevious' | 'o
function DateInputWithRef(
{
defaultValue,
// eslint-disable-next-line @typescript-eslint/naming-convention
disabled = false,
isCompact,
isEndDate = false,
isForcedFocused,
Expand Down Expand Up @@ -116,6 +120,7 @@ function DateInputWithRef(
<Box
ref={boxRef}
$hasError={hasFormatError || hasValidationError}
$isDisabled={disabled}
$isFocused={isForcedFocused || isFocused}
$isLight={isLight}
>
Expand All @@ -126,6 +131,8 @@ function DateInputWithRef(
ref={dayInputRef}
aria-label={`Jour${isStartDate ? ' de début' : ''}${isEndDate ? ' de fin' : ''}`}
defaultValue={defaultValue && formatNumberAsDoubleDigit(defaultValue[2])}
disabled={disabled}
isLight={isLight}
max={31}
min={1}
onBack={onBack}
Expand All @@ -143,6 +150,8 @@ function DateInputWithRef(
ref={monthInputRef}
aria-label={`Mois${isStartDate ? ' de début' : ''}${isEndDate ? ' de fin' : ''}`}
defaultValue={defaultValue && formatNumberAsDoubleDigit(defaultValue[1])}
disabled={disabled}
isLight={isLight}
max={12}
min={1}
onBack={() => dayInputRef.current.focus()}
Expand All @@ -160,6 +169,8 @@ function DateInputWithRef(
ref={yearInputRef}
aria-label={`Année${isStartDate ? ' de début' : ''}${isEndDate ? ' de fin' : ''}`}
defaultValue={defaultValue && defaultValue[0]}
disabled={disabled}
isLight={isLight}
max={2030}
min={2020}
onBack={() => monthInputRef.current.focus()}
Expand All @@ -183,6 +194,7 @@ export const DateInput = forwardRef(DateInputWithRef)

const Box = styled.div<{
$hasError: boolean
$isDisabled: boolean
$isFocused: boolean
$isLight: boolean
}>`
Expand All @@ -192,22 +204,26 @@ const Box = styled.div<{
p.$hasError || p.$isFocused
? `inset 0px 0px 0px 1px ${p.$hasError ? p.theme.color.maximumRed : p.theme.color.blueGray[100]}`
: 'none'};
color: ${p => p.theme.color.blueGray[100]};
color: ${p => (p.$isFocused ? p.theme.color.blueGray[100] : p.theme.color.slateGray)};
display: inline-flex;
font-size: inherit;
justify-content: space-between;
padding: 5px 8px 7px;
user-select: none;
:hover {
box-shadow: ${p => `inset 0px 0px 0px 1px ${p.theme.color.blueYonder[100]}`};
box-shadow: ${p =>
`inset 0px 0px 0px 1px ${
// eslint-disable-next-line no-nested-ternary
p.$isDisabled
? p.theme.color.cultured
: p.$isFocused
? p.theme.color.blueGray[100]
: p.theme.color.blueYonder[100]
}`};
color: ${p => (p.$isFocused ? p.theme.color.blueGray[100] : p.theme.color.blueYonder[100])};
}
> div:first-child {
> span {
color: ${p => p.theme.color.slateGray};
}
}
> div:nth-child(2) {
margin: 1px 0 0 32px;
}
Expand Down
6 changes: 5 additions & 1 deletion src/fields/DateRangePicker/NumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type NumberInputProps = Omit<
InputHTMLAttributes<HTMLInputElement>,
'maxLength' | 'onInput' | 'pattern' | 'type'
> & {
isLight: boolean
max: number
min: number
/** Called when the use press backspace key while the input is empty. */
Expand All @@ -25,6 +26,7 @@ export type NumberInputProps = Omit<
function NumberInputWithRef(
{
defaultValue,
isLight,
max,
min,
onBack,
Expand Down Expand Up @@ -134,6 +136,7 @@ function NumberInputWithRef(
<StyledNumberInput
key={String(defaultValue)}
ref={inputRef}
$isLight={isLight}
$size={size}
defaultValue={defaultValue}
maxLength={size}
Expand All @@ -152,6 +155,7 @@ function NumberInputWithRef(
export const NumberInput = forwardRef(NumberInputWithRef)

const StyledNumberInput = styled.input<{
$isLight: boolean
$size: number
}>`
background-color: transparent;
Expand All @@ -165,6 +169,6 @@ const StyledNumberInput = styled.input<{
width: ${p => p.$size * 0.5}rem;
::placeholder {
color: ${p => p.theme.color.blueGray[100]};
color: ${p => (p.$isLight ? p.theme.color.slateGray : p.theme.color.slateGray)};
}
`
30 changes: 27 additions & 3 deletions src/fields/DateRangePicker/TimeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import type { Promisable } from 'type-fest'

export type TimeInputProps = Pick<NumberInputProps, 'onBack' | 'onPrevious' | 'onNext'> & {
defaultValue?: TimeTuple
// TODO Check why TS thinks there is no `disabled` prop in `NumberInputProps`.
disabled: boolean
isCompact: boolean
isEndDate?: boolean
isLight: boolean
Expand All @@ -27,6 +29,8 @@ export type TimeInputProps = Pick<NumberInputProps, 'onBack' | 'onPrevious' | 'o
function TimeInputWithRef(
{
defaultValue,
// eslint-disable-next-line @typescript-eslint/naming-convention
disabled = false,
isCompact,
isEndDate = false,
isLight,
Expand Down Expand Up @@ -165,13 +169,21 @@ function TimeInputWithRef(
}, [closeRangedTimePicker, onChange])

return (
<Box ref={boxRef} $hasError={hasFormatError || hasValidationError} $isFocused={isFocused} $isLight={isLight}>
<Box
ref={boxRef}
$hasError={hasFormatError || hasValidationError}
$isDisabled={disabled}
$isFocused={isFocused}
$isLight={isLight}
>
<InputGroup>
<div>
<NumberInput
ref={hourInputRef}
aria-label={`Heure${isStartDate ? ' de début' : ''}${isEndDate ? ' de fin' : ''}`}
defaultValue={controlledDefaultValue && controlledDefaultValue[0]}
disabled={disabled}
isLight={isLight}
max={23}
min={0}
onBack={handleBack}
Expand All @@ -190,6 +202,8 @@ function TimeInputWithRef(
ref={minuteInputRef}
aria-label={`Minute${isStartDate ? ' de début' : ''}${isEndDate ? ' de fin' : ''}`}
defaultValue={controlledDefaultValue && controlledDefaultValue[1]}
disabled={disabled}
isLight={isLight}
max={59}
min={0}
onBack={() => hourInputRef.current.focus()}
Expand Down Expand Up @@ -222,6 +236,7 @@ export const TimeInput = forwardRef(TimeInputWithRef)

const Box = styled.div<{
$hasError: boolean
$isDisabled: boolean
$isFocused: boolean
$isLight: boolean
}>`
Expand All @@ -230,15 +245,24 @@ const Box = styled.div<{
p.$hasError || p.$isFocused
? `inset 0px 0px 0px 1px ${p.$hasError ? p.theme.color.maximumRed : p.theme.color.blueGray[100]}`
: 'none'};
color: ${p => p.theme.color.blueGray[100]};
color: ${p => (p.$isFocused ? p.theme.color.blueGray[100] : p.theme.color.slateGray)};
display: inline-block;
font-size: inherit;
padding: 5px 8px 7px;
position: relative;
user-select: none;
:hover {
box-shadow: ${p => `inset 0px 0px 0px 1px ${p.theme.color.blueYonder[100]}`};
box-shadow: ${p =>
`inset 0px 0px 0px 1px ${
// eslint-disable-next-line no-nested-ternary
p.$isDisabled
? p.theme.color.cultured
: p.$isFocused
? p.theme.color.blueGray[100]
: p.theme.color.blueYonder[100]
}`};
color: ${p => (p.$isFocused ? p.theme.color.blueGray[100] : p.theme.color.blueYonder[100])};
}
`

Expand Down
29 changes: 25 additions & 4 deletions src/fields/DateRangePicker/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// TODO We should make this component both form- & a11y-compliant with a `name` and proper (aria-)labels.

import { useCallback, useEffect, useMemo, useRef } from 'react'
import styled from 'styled-components'
import styled, { css } from 'styled-components'

import { Fieldset } from '../../elements/Fieldset'
import { Legend } from '../../elements/Legend'
Expand All @@ -20,6 +20,7 @@ import type { Promisable } from 'type-fest'

export type DateRangePickerProps = Omit<HTMLAttributes<HTMLFieldSetElement>, 'defaultValue' | 'onChange'> & {
defaultValue?: DateRange
disabled?: boolean
isCompact?: boolean
/** Only allow past dates until today. */
isHistorical?: boolean
Expand All @@ -43,6 +44,8 @@ export type DateRangePickerProps = Omit<HTMLAttributes<HTMLFieldSetElement>, 'de
}
export function DateRangePicker({
defaultValue,
// eslint-disable-next-line @typescript-eslint/naming-convention
disabled = false,
isCompact = false,
isHistorical = false,
isLabelHidden = false,
Expand Down Expand Up @@ -283,13 +286,16 @@ export function DateRangePicker({

return (
<Fieldset {...nativeProps}>
<Legend isHidden={isLabelHidden}>{label}</Legend>
<Legend isDisabled={disabled} isHidden={isLabelHidden}>
{label}
</Legend>

<Box>
<Box isDisabled={disabled}>
<Field>
<DateInput
ref={startDateInputRef}
defaultValue={selectedStartDateTupleRef.current}
disabled={disabled}
isCompact={isCompact}
isForcedFocused={isRangeCalendarPickerOpenRef.current}
isLight={isLight}
Expand All @@ -307,6 +313,7 @@ export function DateRangePicker({
<TimeInput
ref={startTimeInputRef}
defaultValue={selectedStartTimeTupleRef.current}
disabled={disabled}
isCompact={isCompact}
isLight={isLight}
isStartDate
Expand All @@ -324,6 +331,7 @@ export function DateRangePicker({
<DateInput
ref={endDateInputRef}
defaultValue={selectedEndDateTupleRef.current}
disabled={disabled}
isCompact={isCompact}
isEndDate
isForcedFocused={isRangeCalendarPickerOpenRef.current}
Expand All @@ -343,6 +351,7 @@ export function DateRangePicker({
<TimeInput
ref={endTimeInputRef}
defaultValue={selectedEndTimeTupleRef.current}
disabled={disabled}
isCompact={isCompact}
isEndDate
isLight={isLight}
Expand All @@ -366,7 +375,9 @@ export function DateRangePicker({
)
}

const Box = styled.div`
const Box = styled.div<{
isDisabled: boolean
}>`
* {
font-weight: 500;
line-height: 1;
Expand All @@ -375,6 +386,16 @@ const Box = styled.div`
color: ${p => p.theme.color.gunMetal};
font-size: 13px;
position: relative;
${p =>
p.isDisabled &&
css`
* {
background-color: ${p.theme.color.cultured} !important;
color: ${p.theme.color.lightGray} !important;
cursor: not-allowed;
}
`}
`

const Field = styled.span<{
Expand Down
Loading

0 comments on commit 9cebad3

Please sign in to comment.