Skip to content

Commit

Permalink
feat(react-filters): add SimpleDateRange
Browse files Browse the repository at this point in the history
  • Loading branch information
bertho-zero committed Oct 23, 2024
1 parent 591a394 commit 4c6655e
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 2 deletions.
2 changes: 1 addition & 1 deletion react-filters/example/views/index.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@

<div
data-oa-filter="date-range"
data-oa-filter-params="<%= JSON.stringify({ type: 'dateRange', name: 'timings' }) %>"
data-oa-filter-params="<%= JSON.stringify({ type: 'simpleDateRange', name: 'timings' }) %>"
></div>

<div
Expand Down
2 changes: 2 additions & 0 deletions react-filters/src/components/ActiveFilters.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import useActiveFilters from '../hooks/useActiveFilters';
import Filters from './Filters';
import DateRangeFilter from './filters/DateRangeFilter';
import SimpleDateRangeFilter from './filters/SimpleDateRangeFilter';
import NumberRangeFilter from './filters/NumberRangeFilter';
import ChoiceFilter from './filters/ChoiceFilter';
import DefinedRangeFilter from './filters/DefinedRangeFilter';
Expand All @@ -17,6 +18,7 @@ export default function ActiveFilters({ filters, ...rest }) {
filters={activeFilters}
choiceComponent={ChoiceFilter.Preview}
dateRangeComponent={DateRangeFilter.Preview}
simpleDateRangeComponent={SimpleDateRangeFilter.Preview}
numberRangeComponent={NumberRangeFilter.Preview}
definedRangeComponent={DefinedRangeFilter.Preview}
searchComponent={SearchFilter.Preview}
Expand Down
13 changes: 13 additions & 0 deletions react-filters/src/components/Filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function Filters({
withRef = false,
choiceComponent: ChoiceComponent = Noop,
dateRangeComponent: DateRangeComponent = Noop,
simpleDateRangeComponent: SimpleDateRangeComponent = Noop,
definedRangeComponent: DefinedRangeComponent = Noop,
numberRangeComponent: NumberRangeComponent = Noop,
mapComponent: MapComponent = Noop,
Expand Down Expand Up @@ -47,6 +48,18 @@ function Filters({
/>
);
break;
case 'simpleDateRange':
elem = (
<SimpleDateRangeComponent
key={seed(filter)}
ref={withRef ? filter.elemRef : null}
filter={filter}
{...filter}
{...dateRangeProps}
{...additionnalProps}
/>
);
break;
case 'definedRange':
elem = (
<DefinedRangeComponent
Expand Down
3 changes: 3 additions & 0 deletions react-filters/src/components/FiltersManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import FavoriteToggle from './FavoriteToggle';
import Total from './Total';
import ChoiceFilter from './filters/ChoiceFilter';
import DateRangeFilter from './filters/DateRangeFilter';
import SimpleDateRangeFilter from './filters/SimpleDateRangeFilter';
import DefinedRangeFilter from './filters/DefinedRangeFilter';
import NumberRangeFilter from './filters/NumberRangeFilter';
import SearchFilter from './filters/SearchFilter';
Expand All @@ -51,6 +52,7 @@ const FiltersManager = React.forwardRef(function FiltersManager(

choiceComponent = ChoiceFilter,
dateRangeComponent = DateRangeFilter,
simpleDateRangeComponent = SimpleDateRangeFilter,
definedRangeComponent = DefinedRangeFilter,
numberRangeComponent = NumberRangeFilter,
searchComponent = SearchFilter,
Expand Down Expand Up @@ -257,6 +259,7 @@ const FiltersManager = React.forwardRef(function FiltersManager(
// filters
choiceComponent={choiceComponent}
dateRangeComponent={dateRangeComponent}
simpleDateRangeComponent={simpleDateRangeComponent}
definedRangeComponent={definedRangeComponent}
numberRangeComponent={numberRangeComponent}
searchComponent={searchComponent}
Expand Down
1 change: 1 addition & 0 deletions react-filters/src/components/fields/SearchInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function Input({ input, placeholder, onButtonClick }) {
className="form-control"
autoComplete="off"
placeholder={placeholder}
aria-label={placeholder}
{...input}
/>
<div className="input-group-append">
Expand Down
65 changes: 65 additions & 0 deletions react-filters/src/components/fields/SimpleDateRangeField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, { useCallback } from 'react';
import { defineMessages, useIntl } from 'react-intl';

const messages = defineMessages({
min: {
id: 'ReactFilters.fields.SimpleRangeField.gte',
defaultMessage: 'Min',
},
max: {
id: 'ReactFilters.fields.SimpleRangeField.lte',
defaultMessage: 'Max',
},
});

// TODO minDate && maxDate from props

function SimpleDateRangeField({ input }, _ref) {
const intl = useIntl();

const { value, onChange } = input;

const onInputChange = useCallback(
(k, v) => {
if (k === 'gte') {
onChange({
...value,
gte: v,
});
} else {
onChange({
...value,
lte: v,
});
}
},
[onChange, value],
);

return (
<div className="row">
<div className="col-xs-6">
<input
value={value?.gte || ''}
type="date"
className="form-control"
aria-label={intl.formatMessage(messages.min)}
onChange={(e) => onInputChange('gte', e.target.value)}
max={value?.lte || ''}
/>
</div>
<div className="col-xs-6">
<input
value={value?.lte || ''}
type="date"
className="form-control"
aria-label={intl.formatMessage(messages.max)}
onChange={(e) => onInputChange('lte', e.target.value)}
min={value?.gte || ''}
/>
</div>
</div>
);
}

export default React.forwardRef(SimpleDateRangeField);
2 changes: 1 addition & 1 deletion react-filters/src/components/filters/DateRangeFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ function parseValue(value) {
return result;
}

function Preview({
export function Preview({
name,
staticRanges = [],
component = FilterPreviewer,
Expand Down
114 changes: 114 additions & 0 deletions react-filters/src/components/filters/SimpleDateRangeFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import React, { useState } from 'react';
import { Field } from 'react-final-form';
import { endOfDay, startOfDay, format } from 'date-fns';
import { getTimezoneOffset, utcToZonedTime } from 'date-fns-tz';
// import NumberRangeField from '../fields/NumberRangeField';
import Panel from '../Panel';
import Title from '../Title';
import SimpleDateRangeField from '../fields/SimpleDateRangeField';
import { Preview } from './DateRangeFilter';

const subscription = { value: true };

function formatDateValue(value, tz) {
if (!value) return value;

const currentTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
const tzDiff = getTimezoneOffset(tz, value) - getTimezoneOffset(currentTz, value);

let date = new Date(value);

if (tzDiff) {
date = utcToZonedTime(date, tz);
}

return date;
}

// For display (store -> form)
function formatValue(value) {
if (!value) {
return undefined;
}

const gte = formatDateValue(value.gte, value.tz);
const lte = formatDateValue(value.lte, value.tz);

return {
gte: gte ? format(gte, 'yyyy-MM-dd') : null,
lte: lte ? format(lte, 'yyyy-MM-dd') : null,
};
}

// For save (form -> store)
function parseValue(value) {
if (!value) {
return value;
}

const gte = value.gte ? startOfDay(new Date(value.gte)).toISOString() : null;
const lte = value.lte ? endOfDay(new Date(value.lte)).toISOString() : null;

const result = {};
if (gte) result.gte = gte;
if (lte) result.lte = lte;

if (gte || lte) result.tz = Intl.DateTimeFormat().resolvedOptions().timeZone;

return result;
}

const SimpleDateRangeFilter = React.forwardRef(function SimpleDateRangeFilter(
{ name },
ref,
) {
return (
<Field
ref={ref}
name={name}
subscription={subscription}
format={formatValue}
parse={parseValue}
component={SimpleDateRangeField}
/>
);
});

const Collapsable = React.forwardRef(function Collapsable(
{ name, filter, component, disabled, staticRanges, inputRanges, ...rest },
ref,
) {
const [collapsed, setCollapsed] = useState(true);

return (
<Panel
header={(
<Title
name={name}
filter={filter}
component={Preview}
disabled={disabled}
/>
)}
collapsed={collapsed}
setCollapsed={setCollapsed}
>
<SimpleDateRangeFilter
ref={ref}
name={name}
filter={filter}
component={component}
disabled={disabled}
collapsed={collapsed}
{...rest}
/>
</Panel>
);
});

const exported = React.memo(SimpleDateRangeFilter);

exported.Preview = Preview;
exported.Collapsable = Collapsable;

export default exported;
1 change: 1 addition & 0 deletions react-filters/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export { default as ChoiceFilter } from './filters/ChoiceFilter';
export { default as SearchFilter } from './filters/SearchFilter';
export { default as FavoritesFilter } from './filters/FavoritesFilter';
export { default as NumberRangeFilter } from './filters/NumberRangeFilter';
export { default as SimpleDateRangeFilter } from './filters/SimpleDateRangeFilter';

export { default as ActiveFilters } from './ActiveFilters';
export { default as FavoriteToggle } from './FavoriteToggle';
Expand Down
15 changes: 15 additions & 0 deletions react-filters/stories/react.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
DateRangeFilter,
ChoiceFilter,
NumberRangeFilter,
SimpleDateRangeFilter,
ActiveFilters,
} from '../src';

Expand Down Expand Up @@ -123,3 +124,17 @@ export const NumberRange = ({ onSubmit }) => (
</FiltersProvider>
</IntlProvider>
);

export const SimpleDateRange = ({ onSubmit }) => (
<IntlProvider locale={lang}>
<FiltersProvider onSubmit={onSubmit}>
<div className="container">
<div className="row">
<div className="col-sm-4">
<SimpleDateRangeFilter name="timings" />
</div>
</div>
</div>
</FiltersProvider>
</IntlProvider>
);

0 comments on commit 4c6655e

Please sign in to comment.