feat: ui v2 - Date Range Component (#1537)

* feat: Date Range wrapper component

* fix yarn issues

* add calendar icon

* date range css styles

* date range fixes

* fix propTypes warning

* fix clear method, defaultProps & onChange method

* update inline styles to use tailwind classes

* date range documentation page

* minor refactor
This commit is contained in:
Rodrigo Antinarelli authored and James A. Petts committed 2020-05-15 13:38:05 +02:00
1 parent 0ec277dd56
commit b183e78f47
13 files changed
+407 -38

No files matched your search

@@ -0,0 +1,98 @@
.DateRangePicker {
}
/** CONTAINER STYLES **/
.DateRangePickerInput {
@apply bg-transparent border-0 flex;
}
.DateRangePicker_picker {
@apply -mt-1;
}
/** INPUT DIV STYLES **/
.DateInput {
background: transparent;
@apply flex flex-1 w-auto;
}
/** INPUT FIELD COMMON STYLES **/
.DateInput_input {
/* used data:image as background-image because svg import with relative url didn't work */
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="%236b6b6b" fill-rule="evenodd"><path d="M20 20h-4v-4h4v4zm-6-10h-4v4h4v-4zm6 0h-4v4h4v-4zm-12 6h-4v4h4v-4zm6 0h-4v4h4v-4zm-6-6h-4v4h4v-4zm16-8v22h-24v-22h3v1c0 1.103.897 2 2 2s2-.897 2-2v-1h10v1c0 1.103.897 2 2 2s2-.897 2-2v-1h3zm-2 6h-20v14h20v-14zm-2-7c0-.552-.447-1-1-1s-1 .448-1 1v2c0 .552.447 1 1 1s1-.448 1-1v-2zm-14 2c0 .552-.447 1-1 1s-1-.448-1-1v-2c0-.552.447-1 1-1s1 .448 1 1v2z"/></g></svg>');
background-size: 14px;
background-position: 10px center;
@apply bg-no-repeat;
}
.DateInput_input {
@apply cursor-pointer border-custom-blue mt-2 bg-black shadow transition duration-300 appearance-none border-t border-l border-r border-b border-solid rounded w-full py-2 px-3 text-sm text-gray-700 leading-tight pl-8;
}
.DateInput_input:hover {
@apply border-gray-500;
}
.DateInput_input:focus {
@apply border-gray-500 outline-none;
}
/** FIRST INPUT STYLES **/
.DateInput:first-child .DateInput_input {
@apply rounded-r-none;
}
.DateInput:first-child .DateInput_input:hover,
.DateInput:first-child .DateInput_input:focus {
@apply relative z-10;
}
/** SECOND INPUT STYLES **/
.DateInput:last-child .DateInput_input {
@apply rounded-l-none;
margin-left: -1px;
}
/** ARROW STYLES **/
.DateRangePickerInput_arrow {
@apply hidden;
}
/* SELECT MONTH PICKER */
.DateRangePicker_select {
@apply bg-white text-custom-dark border rounded border-custom-gray appearance-none cursor-pointer text-base pl-2 pr-5 py-1; /* NEEDED FOR ARROW DOWN */
background-image: linear-gradient(45deg, transparent 50%, gray 50%),
linear-gradient(135deg, gray 50%, transparent 50%);
background-position: calc(100% - 11px) 11px, calc(100% - 6px) calc(11px);
background-size: 5px 5px, 5px 5px;
background-repeat: no-repeat;
}
/* CALENDAR DAYS */
.CalendarDay {
@apply border-0 rounded-full;
}
.CalendarDay:hover,
.CalendarDay__selected,
.CalendarDay__selected:active,
.CalendarDay__selected:hover {
@apply bg-custom-blue text-white border-0 border-custom-blue;
}
.CalendarDay__blocked_out_of_range:hover {
@apply border-0 bg-white cursor-not-allowed text-custom-gray;
}
.CalendarDay__selected_span {
@apply border-0 bg-custom-aquaBright;
}
/* MONTH NAVIGATION BUTTONS */
.DayPickerNavigation_button__horizontalDefault,
.DayPickerNavigation_button__horizontalDefault:hover {
@apply border-custom-gray text-custom-gray;
top: 24px;
padding: 3px 9px;
}
.DayPickerNavigation_svg__horizontal {
@apply fill-current;
}
@@ -0,0 +1,156 @@
/** REACT DATES */
import 'react-dates/initialize';
import 'react-dates/lib/css/_datepicker.css';
import { DateRangePicker, isInclusivelyBeforeDay } from 'react-dates';
import './DateRange.css';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
const DateRange = props => {
const { onChange, startDate, endDate } = props;
const [focusedInput, setFocusedInput] = useState(null);
const today = moment();
const lastWeek = moment().subtract(7, 'day');
const lastMonth = moment().subtract(1, 'month');
const studyDatePresets = [
{
text: 'Today',
start: today,
end: today,
},
{
text: 'Last 7 days',
start: lastWeek,
end: today,
},
{
text: 'Last 30 days',
start: lastMonth,
end: today,
},
];
const renderDatePresets = () => {
return (
<div className="PresetDateRangePicker_panel flex justify-between">
{studyDatePresets.map(({ text, start, end }) => {
return (
<button
key={text}
type="button"
className={`m-0 py-2 px-3 bg-custom-blue border-0 rounded text-white text-base transition duration-300 hover:opacity-80`}
onClick={() =>
onChange({
startDate: start,
endDate: end,
preset: true,
})
}
>
{text}
</button>
);
})}
</div>
);
};
const renderMonthElement = ({ month, onMonthSelect, onYearSelect }) => {
const renderYearsOptions = () => {
const yearsRange = 20;
const options = [];
for (let i = 0; i < yearsRange; i++) {
const year = moment().year() - i;
options.push(
<option key={year} value={year}>
{year}
</option>
);
}
return options;
};
renderMonthElement.propTypes = {
month: PropTypes.object,
onMonthSelect: PropTypes.func,
onYearSelect: PropTypes.func,
};
return (
<div className="flex justify-center">
<div className="my-0 mx-1">
<select
className="DateRangePicker_select"
value={month.month()}
onChange={e => onMonthSelect(month, e.target.value)}
>
{moment.months().map((label, value) => (
<option key={value} value={value}>
{label}
</option>
))}
</select>
</div>
<div className="my-0 mx-1">
{}
<select
className="DateRangePicker_select"
value={month.year()}
onChange={e => onYearSelect(month, e.target.value)}
>
{renderYearsOptions()}
</select>
</div>
</div>
);
};
return (
<DateRangePicker
/** REQUIRED */
startDate={startDate}
startDateId={'startDateId'}
endDate={endDate}
endDateId={'endDateId'}
onDatesChange={onChange}
focusedInput={focusedInput}
onFocusChange={updatedVal => setFocusedInput(updatedVal)}
/** OPTIONAL */
renderCalendarInfo={renderDatePresets}
renderMonthElement={renderMonthElement}
startDatePlaceholderText={'Start Date'}
endDatePlaceholderText={'End Date'}
phrases={{
closeDatePicker: 'Close',
clearDates: 'Clear dates',
}}
isOutsideRange={day => !isInclusivelyBeforeDay(day, moment())}
hideKeyboardShortcutsPanel={true}
numberOfMonths={1}
showClearDates={false}
anchorDirection="left"
/>
);
};
DateRange.defaultProps = {
startDate: null,
endDate: null,
};
DateRange.propTypes = {
/** Start date moment object */
startDate: PropTypes.object, // moment date is an object
/** End date moment object */
endDate: PropTypes.object, // moment date is an object
/** Callback that returns on object with selected dates */
onChange: PropTypes.func.isRequired,
};
export default DateRange;
@@ -0,0 +1,45 @@
---
name: Date Range
menu: Components
route: components/date-range
---
import { useState } from 'react';
import { Playground, Props } from 'docz';
import DateRange from '../DateRange';
# Date Range
Date Range is used to select a range of dates.
## Import
```javascript
import { DateRange } from '@ohfi/ui';
```
## Date Range
<Playground>
{() => {
const [dates, setDates] = useState({
startDate: null,
endDate: null,
});
return (
<div className="p-4 flex flex-col items-center">
<DateRange
startDate={dates.startDate}
endDate={dates.endDate}
onChange={({ startDate, endDate }) =>
setDates({ startDate, endDate })
}
/>
</div>
);
}}
</Playground>
## Properties
<Props of={DateRange} />
@@ -0,0 +1,3 @@
import DateRange from './DateRange';
export default DateRange;