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

feat: Add Animation for Column, Pie and Scatter charts #940

Merged
merged 34 commits into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6f713fc
refactor: Time slider -> Animation
bprusinowski Jan 19, 2023
18e7343
feat: Add Animation icon
bprusinowski Jan 19, 2023
cc40a66
feat: Use Animation icon for time slider
bprusinowski Jan 19, 2023
0f2b62b
refactor: Make label optional
bprusinowski Jan 20, 2023
9b5562d
feat: Add animation field to types and migrations
bprusinowski Jan 20, 2023
e7eb303
feat: Handle animation field changes
bprusinowski Jan 20, 2023
c63e799
chore: Remove TimeSlider UI elements
bprusinowski Jan 20, 2023
e6786ec
feat: Hide animation field behind a flag
bprusinowski Jan 20, 2023
4d2d62f
feat: Add label
bprusinowski Jan 20, 2023
2fb1ddb
feat: Consider animation time range filter
bprusinowski Jan 20, 2023
e90d7f3
refactor: Remove interactiveFiltersConfig.timeSlider in favor of Anim…
bprusinowski Jan 20, 2023
a4e56e3
chore: Merge main
bprusinowski Jan 20, 2023
02c7c6b
fix: Lint
bprusinowski Jan 20, 2023
55eee5c
fix: Only migrate timeSlider to charts that support it
bprusinowski Jan 20, 2023
1400d72
feat: Enable Animation field for Pie and Scatterplot
bprusinowski Jan 23, 2023
0b24e50
Merge branch 'main' of github.com:visualize-admin/visualization-tool …
bprusinowski May 8, 2023
76e54d5
fix: Imports
bprusinowski May 8, 2023
c04ceb6
fix: Align optional label with label (field)
bprusinowski May 8, 2023
9121a38
feat: Remove flag from Animation field
bprusinowski May 8, 2023
a753244
feat: Consolidate Add… translations
bprusinowski May 8, 2023
526120e
refactor: Move icons closer to the component that needs them
bprusinowski May 9, 2023
2910e83
refactor: Improve types
bprusinowski May 9, 2023
1f8d086
feat: Add an option to show / hide play button
bprusinowski May 9, 2023
774114b
chore: Remove unused translations
bprusinowski May 9, 2023
68fe8d9
feat: Add an option to control animation duration
bprusinowski May 9, 2023
beb6139
feat: Only show animation duration options when play button is shown
bprusinowski May 9, 2023
48cec8a
feat: Add animation types
bprusinowski May 9, 2023
73ae545
feat: Add animation to ChartConfigAdjusters
bprusinowski May 9, 2023
d0673ea
fix: Type
bprusinowski May 9, 2023
767bf9a
feat: Animate columns in column chart
bprusinowski May 9, 2023
f236df3
docs: Update CHANGELOG
bprusinowski May 9, 2023
6aac288
feat: Add a 10s option to Animation
bprusinowski May 15, 2023
8eb8833
Merge branch 'main' of github.com:visualize-admin/visualization-tool …
bprusinowski May 15, 2023
14df0b1
fix: Lint
bprusinowski May 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/charts/area/areas-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ const useAreasState = (
const { preparedData, scalesData } = useDataAfterInteractiveFilters({
sortedData: plottableSortedData,
interactiveFiltersConfig,
// No animation yet for areas
animationField: undefined,
getX,
getSegment,
});
Expand Down
12 changes: 11 additions & 1 deletion app/charts/chart-config-ui-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ import {
* Related to config-types.ts.
*/

type BaseEncodingFieldType = "animation";
type MapEncodingFieldType = "baseLayer" | "areaLayer" | "symbolLayer";
type XYEncodingFieldType = "x" | "y" | "segment";
export type EncodingFieldType = MapEncodingFieldType | XYEncodingFieldType;
export type EncodingFieldType =
| BaseEncodingFieldType
| MapEncodingFieldType
| XYEncodingFieldType;

export type EncodingOption =
| { field: "chartSubType" }
Expand Down Expand Up @@ -168,6 +172,12 @@ export const chartConfigOptionsUISpec: ChartSpecs = {
{ field: "useAbbreviations" },
],
},
{
field: "animation",
optional: true,
componentTypes: ["TemporalDimension"],
filters: true,
},
],
interactiveFilters: ["legend", "timeRange"],
},
Expand Down
12 changes: 6 additions & 6 deletions app/charts/column/chart-column.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ export const ChartColumns = memo(
}
/>

{interactiveFiltersConfig?.timeSlider.componentIri && (
{fields.animation && (
bprusinowski marked this conversation as resolved.
Show resolved Hide resolved
<TimeSlider
componentIri={interactiveFiltersConfig.timeSlider.componentIri}
componentIri={fields.animation.componentIri}
dimensions={dimensions}
/>
)}
Expand Down Expand Up @@ -148,9 +148,9 @@ export const ChartColumns = memo(
}
/>

{interactiveFiltersConfig?.timeSlider.componentIri && (
{fields.animation && (
<TimeSlider
componentIri={interactiveFiltersConfig.timeSlider.componentIri}
componentIri={fields.animation.componentIri}
dimensions={dimensions}
/>
)}
Expand All @@ -176,9 +176,9 @@ export const ChartColumns = memo(
</ChartSvg>
<Tooltip type="single" />
</ChartContainer>
{interactiveFiltersConfig?.timeSlider.componentIri && (
{fields.animation && (
<TimeSlider
componentIri={interactiveFiltersConfig.timeSlider.componentIri}
componentIri={fields.animation.componentIri}
dimensions={dimensions}
/>
)}
Expand Down
3 changes: 2 additions & 1 deletion app/charts/column/columns-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ import {
} from "@/configurator/components/ui-helpers";
import { isTemporalDimension, Observation } from "@/domain/data";
import {
useFormatNumber,
formatNumberWithUnit,
useFormatNumber,
useTimeFormatUnit,
} from "@/formatters";
import {
Expand Down Expand Up @@ -171,6 +171,7 @@ const useColumnsState = (
const { preparedData, scalesData } = useDataAfterInteractiveFilters({
sortedData: plottableSortedData,
interactiveFiltersConfig,
animationField: fields.animation,
getX: getXAsDate,
});

Expand Down
2 changes: 2 additions & 0 deletions app/charts/line/lines-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ const useLinesState = (
const { preparedData, scalesData } = useDataAfterInteractiveFilters({
sortedData: plottableSortedData,
interactiveFiltersConfig,
// No animation yet for lines
animationField: undefined,
getX,
getSegment,
});
Expand Down
4 changes: 2 additions & 2 deletions app/charts/pie/chart-pie.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ export const ChartPie = memo(
}
/>

{interactiveFiltersConfig?.timeSlider.componentIri && (
{fields.animation && (
<TimeSlider
componentIri={interactiveFiltersConfig.timeSlider.componentIri}
componentIri={fields.animation.componentIri}
dimensions={dimensions}
/>
)}
Expand Down
1 change: 1 addition & 0 deletions app/charts/pie/pie-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ const usePieState = (
const { preparedData } = useDataAfterInteractiveFilters({
sortedData: plottableData,
interactiveFiltersConfig,
animationField: fields.animation,
getSegment,
});

Expand Down
4 changes: 2 additions & 2 deletions app/charts/scatterplot/chart-scatterplot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ export const ChartScatterplot = memo(
}
/>

{interactiveFiltersConfig?.timeSlider.componentIri && (
{fields.animation && (
<TimeSlider
componentIri={interactiveFiltersConfig.timeSlider.componentIri}
componentIri={fields.animation.componentIri}
dimensions={dimensions}
/>
)}
Expand Down
2 changes: 2 additions & 0 deletions app/charts/scatterplot/scatterplot-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ const useScatterplotState = ({
const { preparedData, scalesData } = useDataAfterInteractiveFilters({
sortedData: plottableSortedData,
interactiveFiltersConfig,
// No animation yet for scatterplot
animationField: undefined,
getSegment,
});
const xMeasure = measures.find((d) => d.iri === fields.x.componentIri);
Expand Down
21 changes: 9 additions & 12 deletions app/charts/shared/chart-helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from "@/charts/shared/use-interactive-filters";
import { parseDate } from "@/configurator/components/ui-helpers";
import {
AnimationField,
ChartConfig,
ChartType,
Filters,
Expand All @@ -37,17 +38,13 @@ export const prepareQueryFilters = (
dataFilters: InteractiveFiltersState["dataFilters"]
): Filters => {
let queryFilters = filters;
const { timeSlider } = interactiveFiltersConfig || {};

if (chartType !== "table" && interactiveFiltersConfig?.dataFilters.active) {
queryFilters = { ...queryFilters, ...dataFilters };
}

queryFilters = omitBy(queryFilters, (v, k) => {
return (
(v.type === "single" && v.value === FIELD_VALUE_NONE) ||
k === timeSlider?.componentIri
);
queryFilters = omitBy(queryFilters, (v) => {
return v.type === "single" && v.value === FIELD_VALUE_NONE;
bprusinowski marked this conversation as resolved.
Show resolved Hide resolved
});

return queryFilters;
Expand Down Expand Up @@ -105,11 +102,13 @@ export const usePlottableData = ({
export const useDataAfterInteractiveFilters = ({
sortedData,
interactiveFiltersConfig,
animationField,
getX,
getSegment,
}: {
sortedData: Observation[];
interactiveFiltersConfig: InteractiveFiltersConfig;
animationField: AnimationField | undefined;
getX?: (d: Observation) => Date;
getSegment?: (d: Observation) => string;
}): {
Expand All @@ -129,9 +128,7 @@ export const useDataAfterInteractiveFilters = ({
const toTime = IFState.timeRange.to?.getTime();

// time slider
const getTime = useTemporalVariable(
interactiveFiltersConfig?.timeSlider.componentIri || ""
);
const getTime = useTemporalVariable(animationField?.componentIri ?? "");
const timeSliderValue = IFState.timeSlider.value;

// legend
Expand All @@ -146,7 +143,7 @@ export const useDataAfterInteractiveFilters = ({
}
: null;
const timeSliderFilter =
interactiveFiltersConfig?.timeSlider.componentIri && timeSliderValue
animationField?.componentIri && timeSliderValue
? (d: Observation) => {
return getTime(d).getTime() === timeSliderValue.getTime();
}
Expand All @@ -172,8 +169,8 @@ export const useDataAfterInteractiveFilters = ({
fromTime,
toTime,
interactiveFiltersConfig?.timeRange.active,
interactiveFiltersConfig?.timeSlider.componentIri,
interactiveFiltersConfig?.legend.active,
animationField,
timeSliderValue,
getSegment,
getTime,
Expand All @@ -185,7 +182,7 @@ export const useDataAfterInteractiveFilters = ({
}, [allFilters, sortedData]);

const timeSliderFilterPresent = !!(
interactiveFiltersConfig?.timeSlider.componentIri && timeSliderValue
animationField?.componentIri && timeSliderValue
);

const scalesData = timeSliderFilterPresent ? sortedData : preparedData;
Expand Down
32 changes: 0 additions & 32 deletions app/charts/shared/use-sync-interactive-filters.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,9 @@ import {
import useSyncInteractiveFilters from "@/charts/shared/use-sync-interactive-filters";
import {
ChartConfig,
ConfiguratorStateConfiguringChart,
InteractiveFiltersConfig,
} from "@/configurator/config-types";
import fixture from "@/test/__fixtures/config/dev/4YL1p4QTFQS4.json";
const { handleInteractiveFilterTimeSliderReset } = jest.requireActual(
"@/configurator/configurator-state"
);

const interactiveFiltersConfig: InteractiveFiltersConfig = {
legend: {
Expand All @@ -28,10 +24,6 @@ const interactiveFiltersConfig: InteractiveFiltersConfig = {
"http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1",
],
},
timeSlider: {
componentIri:
"http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/0",
},
timeRange: {
active: false,
componentIri: "https://fake-iri/dimension/2",
Expand All @@ -43,26 +35,6 @@ const interactiveFiltersConfig: InteractiveFiltersConfig = {
},
};

const configuratorState = {
state: "CONFIGURING_CHART",
chartConfig: {
interactiveFiltersConfig,
},
} as unknown as ConfiguratorStateConfiguringChart;

jest.mock("@/configurator/configurator-state", () => {
return {
useConfiguratorState: () => {
return [
configuratorState,
(_: { type: "INTERACTIVE_FILTER_TIME_SLIDER_RESET" }) => {
handleInteractiveFilterTimeSliderReset(configuratorState);
},
];
},
};
});

const chartConfig = {
...fixture.data.chartConfig,
interactiveFiltersConfig,
Expand Down Expand Up @@ -142,10 +114,6 @@ describe("useSyncInteractiveFilters", () => {
"http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/1",
});

expect(
configuratorState.chartConfig.interactiveFiltersConfig?.timeSlider
.componentIri
).toEqual("");
expect(IFState2.timeSlider.value).toBeUndefined();
});
});
30 changes: 0 additions & 30 deletions app/charts/shared/use-sync-interactive-filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ import {
FilterValueSingle,
isSegmentInConfig,
} from "@/configurator/config-types";
import { useConfiguratorState } from "@/configurator/configurator-state";
import { FIELD_VALUE_NONE } from "@/configurator/constants";
import useFilterChanges from "@/configurator/use-filter-changes";

import { getFieldComponentIri } from "..";

/**
* Makes sure interactive filters are in sync with chart config.
*
Expand All @@ -22,7 +19,6 @@ import { getFieldComponentIri } from "..";
*/
const useSyncInteractiveFilters = (chartConfig: ChartConfig) => {
const [IFstate, dispatch] = useInteractiveFilters();
const [_, dispatchConfigurator] = useConfiguratorState();
const { interactiveFiltersConfig } = chartConfig;

// Time range filter
Expand All @@ -46,32 +42,6 @@ const useSyncInteractiveFilters = (chartConfig: ChartConfig) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dispatch, presetFromStr, presetToStr]);

// Time slider filter
const timeSliderFilter = interactiveFiltersConfig?.timeSlider;
const xComponentIri = getFieldComponentIri(chartConfig.fields, "x");
useEffect(() => {
if (
timeSliderFilter?.componentIri === "" &&
IFstate.timeSlider.value !== undefined
) {
dispatch({ type: "RESET_TIME_SLIDER_FILTER" });
}

if (
xComponentIri !== undefined &&
xComponentIri === timeSliderFilter?.componentIri
) {
dispatchConfigurator({ type: "INTERACTIVE_FILTER_TIME_SLIDER_RESET" });
}
}, [
IFstate.timeSlider.value,
timeSliderFilter?.componentIri,
dispatch,
dispatchConfigurator,
interactiveFiltersConfig,
xComponentIri,
]);

// Data Filters
const componentIris = interactiveFiltersConfig?.dataFilters.componentIris;
useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ export const ChartAnnotationsSelector = ({
case "dataFilters":
case "legend":
case "timeRange":
case "timeSlider":
return true;

default:
Expand Down
Loading