Skip to content

Commit

Permalink
feat: Add dynamic temporal dimension filters (most recent date)
Browse files Browse the repository at this point in the history
  • Loading branch information
bprusinowski committed Dec 5, 2023
1 parent e23bdf0 commit 058f82d
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 94 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ You can also check the [release page](https://github.com/visualize-admin/visuali

- Features
- Localized cube landing pages are now supported (dcat:landingPage) 🌎
- Temporal dimension filters can now be pinned to dynamically use the most recent value (so that published charts automatically switch to it when the cube is updated) 📅

# [3.24.2] - 2023-11-28

Expand Down
10 changes: 7 additions & 3 deletions app/components/chart-filters-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
FilterValue,
getAnimationField,
} from "@/configurator";
import { isDynamicMaxValue } from "@/configurator/components/field";
import {
Dimension,
Measure,
Expand Down Expand Up @@ -74,12 +75,15 @@ export const ChartFiltersList = (props: ChartFiltersListProps) => {
return [];
}

const filterValue = isDynamicMaxValue(f.value)
? dimension.values[dimension.values.length - 1].value
: f.value;
const value = isTemporalDimension(dimension)
? {
value: f.value,
label: timeFormatUnit(`${f.value}`, dimension.timeUnit),
value: filterValue,
label: timeFormatUnit(`${filterValue}`, dimension.timeUnit),
}
: dimension.values.find((d) => d.value === f.value);
: dimension.values.find((d) => d.value === filterValue);

return [{ dimension, value }];
});
Expand Down
15 changes: 15 additions & 0 deletions app/configurator/components/chart-configurator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import {
ControlTabField,
DataFilterSelect,
DataFilterTemporal,
isDynamicMaxValue,
OnOffControlTabField,
} from "@/configurator/components/field";
import { canRenderDatePickerField } from "@/configurator/components/field-date-picker";
Expand Down Expand Up @@ -222,6 +223,20 @@ const useEnsurePossibleFilters = ({

const oldFilters = getChartConfigFilters(chartConfig.cubes, cube.iri);

// Replace potential dynamic max values with resolved values
for (const [key, value] of Object.entries(oldFilters)) {
if (
value.type === "single" &&
isDynamicMaxValue(value.value) &&
filters[key]
) {
filters[key] = {
type: "single",
value: `${value.value}`,
};
}
}

if (!isEqual(filters, oldFilters) && !isEmpty(filters)) {
dispatch({
type: "CHART_CONFIG_FILTERS_UPDATE",
Expand Down
68 changes: 59 additions & 9 deletions app/configurator/components/field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Trans, t } from "@lingui/macro";
import {
CircularProgress,
FormControlLabel,
FormGroup,
Switch as MUISwitch,
Theme,
Typography,
} from "@mui/material";
Expand Down Expand Up @@ -74,6 +76,7 @@ import {
Component,
Dimension,
HierarchyValue,
ObservationValue,
TemporalDimension,
} from "@/domain/data";
import { useTimeFormatLocale } from "@/formatters";
Expand Down Expand Up @@ -255,6 +258,21 @@ export const DataFilterSelect = ({
);
};

/** We can pin some filters' values to max value dynamically, so that when a new
* value is added to the dataset, it will be automatically used as default filter
* value for published charts.
*/
const VISUALIZE_MAX_VALUE = "VISUALIZE_MAX_VALUE";

/** Checks if a given filter value is supposed to be dynamiaclly pinned to max
* value.
*/
export const isDynamicMaxValue = (
value: ObservationValue
): value is "VISUALIZE_MAX_VALUE" => {
return value === VISUALIZE_MAX_VALUE;
};

type DataFilterTemporalProps = {
dimension: TemporalDimension;
timeUnit: DatePickerTimeUnit;
Expand All @@ -273,6 +291,7 @@ export const DataFilterTemporal = (props: DataFilterTemporalProps) => {
cubeIri: dimension.cubeIri,
dimensionIri: dimension.iri,
});
const usesMostRecentDate = isDynamicMaxValue(fieldProps.value);
const label = isOptional ? (
<>
{_label}{" "}
Expand Down Expand Up @@ -317,22 +336,53 @@ export const DataFilterTemporal = (props: DataFilterTemporalProps) => {
<DatePickerField
name={`date-picker-${dimension.iri}`}
label={
<FieldLabel
label={
<OpenMetadataPanelWrapper dim={dimension}>
{label}
</OpenMetadataPanelWrapper>
}
/>
<Flex
sx={{
width: "100%",
justifyContent: "space-between",
alignItems: "center",
}}
>
<FieldLabel
label={
<OpenMetadataPanelWrapper dim={dimension}>
{label}
</OpenMetadataPanelWrapper>
}
/>
{/* FIXME: adapt to design */}
<FormGroup>
<FormControlLabel
control={
<MUISwitch
checked={usesMostRecentDate}
onChange={() =>
fieldProps.onChange({
target: {
value: usesMostRecentDate
? formatDate(maxDate)
: VISUALIZE_MAX_VALUE,
},
})
}
/>
}
// FIXME: adapt to design, translate
label={<Typography variant="caption">Use most recent</Typography>}
/>
</FormGroup>
</Flex>
}
value={
usesMostRecentDate ? maxDate : (parseDate(fieldProps.value) as Date)
}
value={parseDate(fieldProps.value) as Date}
onChange={fieldProps.onChange}
isDateDisabled={isDateDisabled}
timeUnit={timeUnit}
dateFormat={formatDate}
minDate={minDate}
maxDate={maxDate}
disabled={disabled}
disabled={disabled || usesMostRecentDate}
controls={controls}
/>
);
Expand Down
Loading

0 comments on commit 058f82d

Please sign in to comment.