Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UX improvements date range picker #847

Merged
merged 9 commits into from
May 21, 2019
3 changes: 2 additions & 1 deletion apps/finance/app/src/components/DateRange/DatePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,12 @@ const DayView = styled.li`
props.disabled &&
css`
pointer-events: none;
color: ${theme.disabled};
color: #fff;
`}

${props =>
props.selected &&
!props.disabled &&
css`
&&& {
background: ${mainColor};
Expand Down
189 changes: 140 additions & 49 deletions apps/finance/app/src/components/DateRange/DateRangeInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ import {
startOfDay,
endOfDay,
} from 'date-fns'
import { breakpoint, font, theme } from '@aragon/ui'
import { Button, breakpoint, font, theme, useViewport } from '@aragon/ui'
import IconCalendar from './Calendar'
import TextInput from './TextInput'
import DatePicker from './DatePicker'

const DATE_PLACEHOLDER = '--/--/----'
const START_DATE = 'Start date'
const END_DATE = 'End date'

class DateRangeInput extends React.PureComponent {
state = {
Expand Down Expand Up @@ -47,6 +48,7 @@ class DateRangeInput extends React.PureComponent {

componentDidUpdate(prevProps, prevState) {
if (this.state.showPicker !== prevState.showPicker) {
this.setState({ startDateSelected: false, endDateSelected: false })
if (this.state.showPicker) {
document.addEventListener('mousedown', this.handleClickOutside)
} else {
Expand All @@ -62,15 +64,7 @@ class DateRangeInput extends React.PureComponent {

handleClickOutside = event => {
if (this.rootRef && !this.rootRef.contains(event.target)) {
this.setState({ showPicker: false }, () => {
const { startDate, endDate } = this.state
if (startDate && endDate) {
this.props.onChange({
start: startOfDay(startDate),
end: endOfDay(endDate),
})
}
})
this.setState({ showPicker: false })
}
}

Expand All @@ -92,27 +86,69 @@ class DateRangeInput extends React.PureComponent {
}
}

handleApply = e => {
e.preventDefault()
e.stopPropagation()
bpierre marked this conversation as resolved.
Show resolved Hide resolved
this.setState({ showPicker: false }, () => {
AquiGorka marked this conversation as resolved.
Show resolved Hide resolved
const { startDate, endDate } = this.state
if (startDate && endDate) {
this.props.onChange({
start: startOfDay(startDate),
end: endOfDay(endDate),
})
}
})
}

handleClear = e => {
e.preventDefault()
e.stopPropagation()
this.setState({ showPicker: false, startDate: null, endDate: null }, () =>
this.props.onChange({
start: null,
end: null,
})
)
}

render() {
const { startDate, endDate } = this.state
const {
startDate,
endDate,
startDateSelected,
endDateSelected,
showPicker,
} = this.state
const {
compactMode,
format,
startDate: startDateProps,
endDate: endDateProps,
} = this.props

const icon = this.state.showPicker ? (
<IconCalendarSelected />
) : (
<IconCalendar />
)
const value =
this.formattedStartDate && this.formattedEndDate
? `${this.formattedStartDate} - ${this.formattedEndDate}`
// closed: shows props, if props null then placeholder
// opened:
// compact: shows constants, till dates selected
// not compact: shows props, changes with selection
const value = !showPicker
AquiGorka marked this conversation as resolved.
Show resolved Hide resolved
? startDateProps && endDateProps
? `${formatDate(startDateProps, format)} | ${formatDate(
endDateProps,
format
)}`
: ''
const placeholder = `${
this.formattedStartDate ? this.formattedStartDate : DATE_PLACEHOLDER
} - ${
this.formattedEndDate
? this.formattedEndDate
: this.formattedStartDate
? DATE_PLACEHOLDER
: formatDate(new Date(), this.props.format)
}`
: compactMode
? `${startDateSelected ? this.formattedStartDate : START_DATE} | ${
endDateSelected ? this.formattedEndDate : END_DATE
}`
: `${this.formattedStartDate ? this.formattedStartDate : START_DATE} | ${
this.formattedEndDate ? this.formattedEndDate : END_DATE
}`

return (
<StyledContainer
Expand All @@ -125,25 +161,58 @@ class DateRangeInput extends React.PureComponent {
adornment={icon}
adornmentPosition="end"
height={39}
placeholder={placeholder}
placeholder={`${START_DATE} | ${END_DATE}`}
/>
{this.state.showPicker && (
<StyledDatePickersContainer>
<DatePicker
key={`start-picker-${startDate}`}
name="start-date-picker"
currentDate={startDate}
onSelect={this.handleSelectStartDate}
overlay={false}
/>
<DatePicker
key={`end-picker-${endDate}`}
name="end-date-picker"
currentDate={endDate}
onSelect={this.handleSelectEndDate}
overlay={false}
/>
</StyledDatePickersContainer>
<Container>
<Wrap>
{(!compactMode || !startDateSelected) && (
<DatePicker
key={`start-picker-${startDate}`}
name="start-date-picker"
currentDate={startDate}
onSelect={this.handleSelectStartDate}
overlay={false}
/>
)}
{(!compactMode || startDateSelected) && (
<DatePicker
key={`end-picker-${endDate}`}
name="end-date-picker"
currentDate={endDate}
onSelect={this.handleSelectEndDate}
overlay={false}
/>
)}
</Wrap>

<Controls>
<Button
css={'width: 124px;'}
AquiGorka marked this conversation as resolved.
Show resolved Hide resolved
mode="secondary"
onClick={this.handleClear}
>
Clear
</Button>
<Button
css={`
width: 124px;

${breakpoint(
'medium',
`
margin-left: 19px;
`
)}
`}
mode="strong"
onClick={this.handleApply}
disabled={!startDateSelected || !endDateSelected}
>
Apply
</Button>
</Controls>
</Container>
)}
</StyledContainer>
)
Expand All @@ -162,6 +231,22 @@ DateRangeInput.defaultProps = {
onChange: () => {},
}

const Controls = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
margin: 16px 0;
padding: 0 8px;

${breakpoint(
'medium',
`
display: block;
text-align: right;
`
)}
`

const StyledContainer = styled.div`
position: relative;
`
Expand All @@ -171,13 +256,7 @@ const StyledTextInput = styled(TextInput)`
${font({ monospace: true })};
`

const StyledDatePickersContainer = styled.div`
position: absolute;
z-index: 10;
border: 1px solid ${theme.contentBorder};
border-radius: 3px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);

const Wrap = styled.div`
> div {
border: 0;
box-shadow: none;
Expand All @@ -193,8 +272,20 @@ const StyledDatePickersContainer = styled.div`
)}
`

const Container = styled.div`
position: absolute;
z-index: 10;
border: 1px solid ${theme.contentBorder};
border-radius: 3px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
background: #fff;
`

const IconCalendarSelected = styled(IconCalendar)`
color: ${theme.accent};
`

export default DateRangeInput
export default props => {
const { below } = useViewport()
return <DateRangeInput {...props} compactMode={below('medium')} />
}