Skip to content

Commit

Permalink
feat: Ability to update temporal shared filter
Browse files Browse the repository at this point in the history
  • Loading branch information
ptbrowne committed Jun 3, 2024
1 parent ef4e34a commit 6a035b1
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 46 deletions.
8 changes: 5 additions & 3 deletions app/charts/shared/chart-data-filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -341,15 +341,17 @@ const DataFilter = (props: DataFilterProps) => {
);
};

type DataFilterGenericDimensionProps = {
export type DataFilterGenericDimensionProps = {
dimension: Dimension;
value: string;
onChange: (e: SelectChangeEvent<unknown>) => void;
options?: Array<{ label: string; value: string }>;
disabled: boolean;
};

const DataFilterGenericDimension = (props: DataFilterGenericDimensionProps) => {
export const DataFilterGenericDimension = (
props: DataFilterGenericDimensionProps
) => {
const { dimension, value, onChange, options: propOptions, disabled } = props;
const { label, isKeyDimension } = dimension;
const noneLabel = t({
Expand Down Expand Up @@ -466,7 +468,7 @@ const DataFilterTemporalDimension = ({
) => void;
disabled: boolean;
}) => {
const { label, values, timeUnit, timeFormat } = dimension;
const { label, timeUnit, timeFormat } = dimension;
const formatLocale = useTimeFormatLocale();
const formatDate = formatLocale.format(timeFormat);
const parseDate = formatLocale.parse(timeFormat);
Expand Down
2 changes: 1 addition & 1 deletion app/configurator/components/field-date-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Label } from "@/components/form";
import { TimeUnit } from "@/graphql/resolver-types";
import { Icon } from "@/icons";

type DatePickerFieldProps = {
export type DatePickerFieldProps = {
name: string;
label?: React.ReactNode;
value: Date;
Expand Down
263 changes: 221 additions & 42 deletions app/configurator/components/layout-configurator.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Trans } from "@lingui/macro";
import { Trans, t } from "@lingui/macro";
import {
Box,
FormControlLabel,
Expand All @@ -14,6 +14,8 @@ import omit from "lodash/omit";
import uniqBy from "lodash/uniqBy";
import { useMemo } from "react";

import { DataFilterGenericDimensionProps } from "@/charts/shared/chart-data-filters";
import { Select } from "@/components/form";
import { generateLayout } from "@/components/react-grid";
import { ChartConfig, LayoutDashboard } from "@/config-types";
import { LayoutAnnotator } from "@/configurator/components/annotators";
Expand All @@ -22,17 +24,32 @@ import {
ControlSectionContent,
SubsectionTitle,
} from "@/configurator/components/chart-controls/section";
import {
DatePickerField,
DatePickerFieldProps,
canRenderDatePickerField,
} from "@/configurator/components/field-date-picker";
import { IconButton } from "@/configurator/components/icon-button";
import { timeUnitToFormatter } from "@/configurator/components/ui-helpers";
import {
extractDataPickerOptionsFromDimension,
timeUnitToFormatter,
} from "@/configurator/components/ui-helpers";
import {
isLayouting,
useConfiguratorState,
} from "@/configurator/configurator-state";
import { isTemporalDimension } from "@/domain/data";
import {
Dimension,
TemporalDimension,
isTemporalDimension,
} from "@/domain/data";
import { useTimeFormatLocale, useTimeFormatUnit } from "@/formatters";
import { useDataCubesComponentsQuery } from "@/graphql/hooks";
import { useLocale } from "@/src";
import { useDashboardInteractiveFilters } from "@/stores/interactive-filters";
import {
SharedFilter,
useDashboardInteractiveFilters,
} from "@/stores/interactive-filters";
import { getTimeFilterOptions } from "@/utils/time-filter-options";

export const LayoutConfigurator = () => {
Expand Down Expand Up @@ -187,49 +204,50 @@ const LayoutSharedFiltersConfigurator = () => {
{potentialSharedFilters.map((filter) => {
const dimension = dimensionsByIri[filter.componentIri];
const sharedFilter = sharedFiltersByIri[filter.componentIri];
console.log(
dimension,
sharedFilter,
filter.componentIri,
sharedFiltersByIri
);

if (!dimension || !isTemporalDimension(dimension)) {
return null;
}
return (
<Box
display="flex"
alignItems="center"
justifyContent="space-between"
width="100%"
key={filter.componentIri}
>
<Typography variant="body2" flexGrow={1}>
{dimension.label || filter.componentIri}
</Typography>
<FormControlLabel
sx={{ mr: 0 }}
labelPlacement="start"
disableTypography
label={
<Typography variant="body2">
<Trans id="controls.section.shared-filters.shared-switch">
Shared
</Trans>
</Typography>
}
control={
<Switch
checked={!!sharedFilter ?? false}
onChange={handleToggle}
inputProps={{
// @ts-expect-error ts(2322) - data-component-iri is not considered a valid attribute, while it is
"data-component-iri": filter.componentIri,
}}
/>
}
<>
<Box
display="flex"
alignItems="center"
justifyContent="space-between"
width="100%"
key={filter.componentIri}
>
<Typography variant="body2" flexGrow={1}>
{dimension.label || filter.componentIri}
</Typography>
<FormControlLabel
sx={{ mr: 0 }}
labelPlacement="start"
disableTypography
label={
<Typography variant="body2">
<Trans id="controls.section.shared-filters.shared-switch">
Shared
</Trans>
</Typography>
}
control={
<Switch
checked={!!sharedFilter ?? false}
onChange={handleToggle}
inputProps={{
// @ts-expect-error ts(2322) - data-component-iri is not considered a valid attribute, while it is
"data-component-iri": filter.componentIri,
}}
/>
}
/>
</Box>
<SharedFilterOptions
sharedFilter={sharedFilter}
dimension={dimension}
/>
</Box>
</>
);
})}
</Stack>
Expand All @@ -240,6 +258,167 @@ const LayoutSharedFiltersConfigurator = () => {
}
};

const SharedFilterOptions = ({
sharedFilter,
dimension,
}: {
sharedFilter: SharedFilter;
dimension: Dimension;
}) => {
if (!sharedFilter) {
return null;
}

if (sharedFilter.type !== "timeRange") {
return null;
}
if (
!isTemporalDimension(dimension) ||
!canRenderDatePickerField(dimension.timeUnit)
) {
return null;
}

return (
<SharedFilterOptionsTimeRange
sharedFilter={sharedFilter}
dimension={dimension}
/>
);
};

const SharedFilterOptionsTimeRange = ({
sharedFilter,
dimension,
}: {
sharedFilter: SharedFilter;
dimension: TemporalDimension;
}) => {
const { timeUnit, timeFormat } = dimension;
const formatLocale = useTimeFormatLocale();
const formatDate = formatLocale.format(timeFormat);
const parseDate = formatLocale.parse(timeFormat);
const [, dispatch] = useConfiguratorState();

const { minDate, maxDate, optionValues, options } = useMemo(() => {
return extractDataPickerOptionsFromDimension({
dimension,
parseDate,
});
}, [dimension, parseDate]);

const handleChangeFromDate: DatePickerFieldProps["onChange"] = (ev) =>
dispatch({
type: "DASHBOARD_FILTER_UPDATE",
value: {
...sharedFilter,
presets: {
...sharedFilter.presets,
from: formatDate(new Date(ev.target.value)),
},
},
});

const handleChangeFromGeneric: DataFilterGenericDimensionProps["onChange"] = (
ev
) =>
dispatch({
type: "DASHBOARD_FILTER_UPDATE",
value: {
...sharedFilter,
presets: {
...sharedFilter.presets,
from: ev.target.value as string,
},
},
});

const handleChangeToDate: DatePickerFieldProps["onChange"] = (ev) =>
dispatch({
type: "DASHBOARD_FILTER_UPDATE",
value: {
...sharedFilter,
presets: {
...sharedFilter.presets,
to: formatDate(new Date(ev.target.value)),
},
},
});

const handleChangeToGeneric: DataFilterGenericDimensionProps["onChange"] = (
ev
) =>
dispatch({
type: "DASHBOARD_FILTER_UPDATE",
value: {
...sharedFilter,
presets: {
...sharedFilter.presets,
to: ev.target.value as string,
},
},
});

return (
<Stack spacing={1} direction="column" gap="0.25rem">
<Stack
direction="row"
gap="0.5rem"
alignItems="center"
divider={
<Box position="relative" top="0.5rem">
-
</Box>
}
>
{canRenderDatePickerField(timeUnit) ? (
<DatePickerField
name={`interactive-date-picker-${dimension.iri}`}
value={parseDate(sharedFilter.presets.from) as Date}
onChange={handleChangeFromDate}
isDateDisabled={(d) => !optionValues.includes(formatDate(d))}
timeUnit={timeUnit}
dateFormat={formatDate}
minDate={minDate}
maxDate={maxDate}
disabled={false}
label={t({ id: "controls.filters.select.from", message: "From" })}
/>
) : (
<Select
id={`shared-filter-${sharedFilter.componentIri}-from`}
label={t({ id: "controls.filters.select.from", message: "From" })}
options={options}
value={sharedFilter.presets.from}
onChange={handleChangeFromGeneric}
/>
)}
{canRenderDatePickerField(timeUnit) ? (
<DatePickerField
name={`interactive-date-picker-${dimension.iri}`}
value={parseDate(sharedFilter.presets.to) as Date}
onChange={handleChangeToDate}
isDateDisabled={(d) => !optionValues.includes(formatDate(d))}
timeUnit={timeUnit}
dateFormat={formatDate}
minDate={minDate}
maxDate={maxDate}
label={t({ id: "controls.filters.select.to", message: "To" })}
/>
) : (
<Select
id={`shared-filter-${sharedFilter.componentIri}-to`}
label={t({ id: "controls.filters.select.to", message: "To" })}
options={options}
value={sharedFilter.presets.to}
onChange={handleChangeToGeneric}
/>
)}
</Stack>
</Stack>
);
};

type LayoutButtonProps = {
type: LayoutDashboard["layout"];
layout: LayoutDashboard;
Expand Down
4 changes: 4 additions & 0 deletions app/configurator/configurator-state/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -276,4 +276,8 @@ export type ConfiguratorStateAction =
| {
type: "DASHBOARD_FILTER_REMOVE";
value: DashboardFiltersConfig["filters"][number]["componentIri"];
}
| {
type: "DASHBOARD_FILTER_UPDATE";
value: DashboardFiltersConfig["filters"][number];
};
14 changes: 14 additions & 0 deletions app/configurator/configurator-state/reducer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,20 @@ export const reducer_: Reducer<ConfiguratorState, ConfiguratorStateAction> = (

return draft;

case "DASHBOARD_FILTER_UPDATE":
if (isLayouting(draft)) {
const idx = draft.dashboardFilters?.filters.findIndex(
(f) => f.componentIri === action.value.componentIri
);

if (idx !== undefined && idx > -1) {
const newFilters = [...(draft.dashboardFilters?.filters ?? [])];
newFilters.splice(idx, 1, action.value);
setWith(draft, "dashboardFilters.filters", newFilters, Object);
}
}
return draft;

case "DASHBOARD_FILTER_REMOVE":
if (isLayouting(draft)) {
const newFilters = draft.dashboardFilters?.filters.filter(
Expand Down

0 comments on commit 6a035b1

Please sign in to comment.