From 8583f603abfccd296e5fdd3f78162ba1ac4d4fe0 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Fri, 8 Sep 2023 14:25:49 +0200 Subject: [PATCH 01/21] chore: Improve langString error message --- CHANGELOG.md | 3 ++- app/rdf/queries.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f3269dd5..ba56e65ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ You can also check the [release page](https://github.com/visualize-admin/visuali ## Unreleased -Nothing yet. +- Misc + - Improved `langString` error message # [3.22.4] - 2023-09-06 diff --git a/app/rdf/queries.ts b/app/rdf/queries.ts index e2ac3670a..eeafe9469 100644 --- a/app/rdf/queries.ts +++ b/app/rdf/queries.ts @@ -780,8 +780,8 @@ const buildFilters = ({ const { dataType } = parsedCubeDimension.data; if (ns.rdf.langString.value === dataType) { - console.warn( - `WARNING: Dimension <${iri}> has dataType 'langString'. Filtering won't work.` + throw new Error( + `Dimension <${iri}> has dataType 'langString', which is not supported by Visualize. In order to fix it, change the dataType to 'string' in the cube definition.` ); } From 0c6dbbddb5fa69e84f2952dd462acbeaf2e52be6 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 12 Sep 2023 13:02:45 +0200 Subject: [PATCH 02/21] fix: Initialize color mapping with all values --- CHANGELOG.md | 2 ++ app/charts/chart-config-ui-options.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba56e65ff..d66e9f9a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ You can also check the [release page](https://github.com/visualize-admin/visuali ## Unreleased +- Fixes + - It's now again possible to map all colors for hierarchical dimensions used as segmentation - Misc - Improved `langString` error message diff --git a/app/charts/chart-config-ui-options.ts b/app/charts/chart-config-ui-options.ts index 39a5656f1..746f48605 100644 --- a/app/charts/chart-config-ui-options.ts +++ b/app/charts/chart-config-ui-options.ts @@ -448,7 +448,7 @@ const defaultSegmentOnChange: OnEncodingChange< ); const colorMapping = mapValueIrisToColor({ palette, - dimensionValues: selectedValues, + dimensionValues: component ? component.values : selectedValues, }); const multiFilter = makeMultiFilter(selectedValues.map((d) => d.value)); From c64756bb6199a314a71dc6ca749c97818f9ba49f Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 12 Sep 2023 13:11:47 +0200 Subject: [PATCH 03/21] docs: Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d66e9f9a4..a6c2cec72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ You can also check the [release page](https://github.com/visualize-admin/visuali ## Unreleased +Nothing yet. + +# [3.22.5] - 2023-09-12 + - Fixes - It's now again possible to map all colors for hierarchical dimensions used as segmentation - Misc From 615675d4afa804ab9fdcb6cb67d6886180439441 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 12 Sep 2023 13:12:00 +0200 Subject: [PATCH 04/21] v3.22.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index acd3a7970..877b17e03 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@visualize-admin/visualization-tool", - "version": "3.22.4", + "version": "3.22.5", "repository": { "type": "git", "url": "https://github.com/visualize-admin/visualization-tool.git" From 6570c8f67b8fc23b9d259813e70eda0495f9e855 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 12 Sep 2023 13:34:57 +0200 Subject: [PATCH 05/21] fix: Table docs --- CHANGELOG.md | 3 ++- app/docs/data-table.docs.tsx | 39 +++++++++++++++++++----------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6c2cec72..60c9d99aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ You can also check the [release page](https://github.com/visualize-admin/visuali ## Unreleased -Nothing yet. +- Fixes + - Table docs now work correctly again # [3.22.5] - 2023-09-12 diff --git a/app/docs/data-table.docs.tsx b/app/docs/data-table.docs.tsx index 5f3ae5bf1..2488b7aab 100644 --- a/app/docs/data-table.docs.tsx +++ b/app/docs/data-table.docs.tsx @@ -11,6 +11,7 @@ import { tableObservations, } from "@/docs/fixtures"; import { DimensionMetadataFragment } from "@/graphql/query-hooks"; +import { InteractiveFiltersProvider } from "@/stores/interactive-filters"; export const Docs = () => markdown` @@ -18,24 +19,26 @@ export const Docs = () => markdown` ${( - d.iri - )} - measures={tableMeasures as DimensionMetadataFragment[]} - measuresByIri={keyBy( - tableMeasures as DimensionMetadataFragment[], - (d) => d.iri - )} - chartConfig={tableConfig} - > - -
-
-
+ + d.iri + )} + measures={tableMeasures as DimensionMetadataFragment[]} + measuresByIri={keyBy( + tableMeasures as DimensionMetadataFragment[], + (d) => d.iri + )} + chartConfig={tableConfig} + > + + + + + )} `; From 49153eadc8a2711aa3f1cd649bef9fe21f31c6f7 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 12 Sep 2023 15:40:14 +0200 Subject: [PATCH 06/21] refactor: Clean up --- app/utils/l10n-provider.tsx | 63 +++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/app/utils/l10n-provider.tsx b/app/utils/l10n-provider.tsx index 913577cbb..c3d52b343 100644 --- a/app/utils/l10n-provider.tsx +++ b/app/utils/l10n-provider.tsx @@ -1,37 +1,52 @@ import { LocalizationProvider } from "@mui/lab"; import DateAdapter from "@mui/lab/AdapterDateFns"; -import React, { useEffect, useState } from "react"; - -const AsyncLocalizationProvider = ({ - locale, - children, -}: { - locale: string; - children: React.ReactNode; -}) => { - const [dateFnsModule, setDateFnsModule] = useState<{ default: object }>(); - useEffect(() => { +import React from "react"; + +import { Locale } from "@/locales/locales"; + +type AsyncLocalizationProviderProps = { + locale: Locale; +}; + +const AsyncLocalizationProvider = ( + props: React.PropsWithChildren +) => { + const { locale, children } = props; + const [dateFnsLocale, setDateFnsLocale] = React.useState(); + + React.useEffect(() => { const run = async () => { - if (locale === "fr") { - setDateFnsModule(await import("date-fns/locale/fr")); - } else if (locale === "de") { - setDateFnsModule(await import("date-fns/locale/de")); - } else if (locale === "it") { - setDateFnsModule(await import("date-fns/locale/it")); - } else if (locale === "en") { - setDateFnsModule(await import("date-fns/locale/en-GB")); + switch (locale) { + case "en": { + const importedLocale = await import("date-fns/locale/en-GB"); + setDateFnsLocale(importedLocale.default); + break; + } + case "de": + case "fr": + case "it": { + const importedLocale = await import( + /* webpackMode: "lazy", webpackChunkName: "date-fns-[index]", webpackExclude: /_lib/ */ + `date-fns/locale/${locale}/index.js` + ); + setDateFnsLocale(importedLocale.default); + break; + } + default: + const _exhaustiveCheck: never = locale; + return _exhaustiveCheck; } }; + run(); }, [locale]); - if (!dateFnsModule) { + + if (!dateFnsLocale) { return null; } + return ( - + {children} ); From 8f270980c240587cb30f12d6100ee25d83e1fb51 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 12 Sep 2023 16:07:52 +0200 Subject: [PATCH 07/21] fix: Overwriting of locale when redirecting to versioned cube ...there is no need to specify the locale in pathname. Using this led to overwiting the locale, as we used the old locale, while the router was setting a new one on route change. --- CHANGELOG.md | 1 + app/components/use-redirect-to-versioned-cube.spec.tsx | 4 ++-- app/components/use-redirect-to-versioned-cube.tsx | 2 +- app/pages/_app.tsx | 3 +-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60c9d99aa..20927259a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ You can also check the [release page](https://github.com/visualize-admin/visuali - Fixes - Table docs now work correctly again + - Changing the locale when previewing a larger cube no longer triggers multiple locale switches # [3.22.5] - 2023-09-12 diff --git a/app/components/use-redirect-to-versioned-cube.spec.tsx b/app/components/use-redirect-to-versioned-cube.spec.tsx index 9c05ddf10..ef3cee332 100644 --- a/app/components/use-redirect-to-versioned-cube.spec.tsx +++ b/app/components/use-redirect-to-versioned-cube.spec.tsx @@ -87,7 +87,7 @@ describe("use redirect to versioned cube", () => { versionedCube: { iri: "https://versioned-cube" }, }); expect(router.replace).toHaveBeenCalledWith({ - pathname: "/de/browse", + pathname: "/browse", query: { dataset: "https://versioned-cube", }, @@ -100,7 +100,7 @@ describe("use redirect to versioned cube", () => { versionedCube: { iri: "https://versioned-cube2" }, }); expect(router.replace).toHaveBeenCalledWith({ - pathname: "/de/browse", + pathname: "/browse", query: { dataset: "https://versioned-cube2", }, diff --git a/app/components/use-redirect-to-versioned-cube.tsx b/app/components/use-redirect-to-versioned-cube.tsx index d6a9618f7..a5902f1f7 100644 --- a/app/components/use-redirect-to-versioned-cube.tsx +++ b/app/components/use-redirect-to-versioned-cube.tsx @@ -48,7 +48,7 @@ export const useRedirectToVersionedCube = ({ if (resp) { router.replace({ - pathname: `/${locale}/browse`, + pathname: `/browse`, query: { ...router.query, ...(router.query.iri ? { iri: resp.iri } : { dataset: resp.iri }), diff --git a/app/pages/_app.tsx b/app/pages/_app.tsx index 0c877b157..dfc60d377 100644 --- a/app/pages/_app.tsx +++ b/app/pages/_app.tsx @@ -26,11 +26,10 @@ export default function App({ pageProps: { session, ...pageProps }, }: AppProps) { const { events: routerEvents, asPath, locale: routerLocale } = useRouter(); + const locale = parseLocaleString(routerLocale ?? ""); useNProgress(); - const locale = parseLocaleString(routerLocale ?? ""); - // Immediately activate locale to avoid re-render if (i18n.locale !== locale) { i18n.activate(locale); From 08699cd758f06d4ecb8055f9c5a8bd4d0377e5ca Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 12 Sep 2023 16:36:54 +0200 Subject: [PATCH 08/21] fix: Check fetching condition first ...to avoid showing stale data and updating the localised content once the data fetches in the background. This is needed as we do not reload the page via changing pathname when redirecting to versioned cube. --- app/browse/datatable.tsx | 2 +- app/browser/dataset-preview.tsx | 14 +++++++------- app/components/use-redirect-to-versioned-cube.tsx | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/browse/datatable.tsx b/app/browse/datatable.tsx index 0afcfa5d5..133e31c72 100644 --- a/app/browse/datatable.tsx +++ b/app/browse/datatable.tsx @@ -216,7 +216,7 @@ export const DataSetPreviewTable = ({ title: string; dimensions: DimensionMetadataFragment[]; measures: DimensionMetadataFragment[]; - observations: Observation[]; + observations: Observation[] | undefined; }) => { const headers = useMemo(() => { return getSortedColumns([...dimensions, ...measures]); diff --git a/app/browser/dataset-preview.tsx b/app/browser/dataset-preview.tsx index 700cad991..dad4d2d72 100644 --- a/app/browser/dataset-preview.tsx +++ b/app/browser/dataset-preview.tsx @@ -108,7 +108,13 @@ export const DataSetPreview = ({ window.scrollTo({ top: 0 }); }, []); - if (metadata?.dataCubeByIri) { + if (fetching) { + return ( + + + + ); + } else if (metadata?.dataCubeByIri) { const { dataCubeByIri } = metadata; return ( @@ -188,12 +194,6 @@ export const DataSetPreview = ({ ); - } else if (fetching) { - return ( - - - - ); } else { return ( diff --git a/app/components/use-redirect-to-versioned-cube.tsx b/app/components/use-redirect-to-versioned-cube.tsx index a5902f1f7..78689fc2f 100644 --- a/app/components/use-redirect-to-versioned-cube.tsx +++ b/app/components/use-redirect-to-versioned-cube.tsx @@ -48,7 +48,7 @@ export const useRedirectToVersionedCube = ({ if (resp) { router.replace({ - pathname: `/browse`, + pathname: "/browse", query: { ...router.query, ...(router.query.iri ? { iri: resp.iri } : { dataset: resp.iri }), From 2805e3b34e562ff24fc6cbd6cade65a0c765abcb Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Wed, 13 Sep 2023 09:02:46 +0200 Subject: [PATCH 09/21] fix: Filter hierarchies in interactive filters --- CHANGELOG.md | 1 + app/charts/shared/chart-data-filters.tsx | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60c9d99aa..21d1fd415 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ You can also check the [release page](https://github.com/visualize-admin/visuali - Fixes - Table docs now work correctly again + - Cascading mode now works correctly for hierarchical dimensions used as interactive filters # [3.22.5] - 2023-09-12 diff --git a/app/charts/shared/chart-data-filters.tsx b/app/charts/shared/chart-data-filters.tsx index fc4bc58bd..fcf5518a0 100644 --- a/app/charts/shared/chart-data-filters.tsx +++ b/app/charts/shared/chart-data-filters.tsx @@ -380,7 +380,10 @@ const DataFilterHierarchyDimension = ( const options = React.useMemo(() => { let opts = [] as { label: string; value: string; isNoneValue?: boolean }[]; if (hierarchy) { - opts = hierarchyToOptions(hierarchy); + opts = hierarchyToOptions( + hierarchy, + dimensionValues.map((d) => d.value) + ); } else { // @ts-ignore opts = dimensionValues; From 6b1354ed5ea64c231fa13696dc71de7799adbe4e Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Wed, 13 Sep 2023 09:03:10 +0200 Subject: [PATCH 10/21] refactor: Make it necessary to pass possible values when converting hierarchy to options --- app/utils/hierarchy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/utils/hierarchy.ts b/app/utils/hierarchy.ts index d495fb6d2..0a222aa66 100644 --- a/app/utils/hierarchy.ts +++ b/app/utils/hierarchy.ts @@ -1,7 +1,7 @@ import { ascending } from "d3"; import { SelectTreeProps } from "@/components/select-tree"; -import { OptionGroup, Option } from "@/configurator"; +import { Option, OptionGroup } from "@/configurator"; import { HierarchyParents } from "@/configurator/components/use-hierarchy-parents"; import { DimensionValue } from "@/domain/data"; import { truthy } from "@/domain/types"; @@ -36,7 +36,7 @@ export const makeOptionGroups = ( export const hierarchyToOptions = ( hierarchy: HierarchyValue[], - possibleValues?: DimensionValue["value"][] + possibleValues: DimensionValue["value"][] ) => { const possibleValuesSet = possibleValues ? new Set(possibleValues) From e09be944ab691c4ab1cae287c319cd45f51c0256 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Thu, 14 Sep 2023 13:41:12 +0200 Subject: [PATCH 11/21] docs: Update testing docs --- app/docs/testing.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/docs/testing.mdx b/app/docs/testing.mdx index 17f9492fc..539ead9cc 100644 --- a/app/docs/testing.mdx +++ b/app/docs/testing.mdx @@ -23,9 +23,9 @@ To prevent regressions between releases, snapshot **tests are run automatically ## Query performance A developer tool to monitor resolver performance and SPARQL queries that are made by the server -is available if a page is accessed with the `debug=true` query parameter. +is available if a page is accessed with the `flag__debug=true` query parameter. -See for example https://test.visualize.admin.ch/en?debug=true +See for example https://test.visualize.admin.ch/en?flag__debug=true A small 🛠 should be present in the lower corner of the screen and clicking it will display the GraphQL debug panel. This panel shows every GraphQL query that is made to the server and queries From 16d30685c0d252bc0e113be91fc1613197655ec3 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Thu, 14 Sep 2023 16:38:32 +0200 Subject: [PATCH 12/21] fix: Cut Y axis labels --- app/charts/area/areas-state.tsx | 23 +++++++++---- app/charts/column/columns-grouped-state.tsx | 35 +++++++++++++------- app/charts/column/columns-stacked-state.tsx | 19 +++++++---- app/charts/column/columns-state.tsx | 28 +++++++++------- app/charts/line/lines-state.tsx | 20 +++++++---- app/charts/scatterplot/scatterplot-state.tsx | 13 ++++---- app/charts/shared/chart-dimensions.tsx | 12 +++---- app/charts/shared/chart-state.ts | 11 ++++++ 8 files changed, 107 insertions(+), 54 deletions(-) diff --git a/app/charts/area/areas-state.tsx b/app/charts/area/areas-state.tsx index 972186a92..5b677fc58 100644 --- a/app/charts/area/areas-state.tsx +++ b/app/charts/area/areas-state.tsx @@ -91,7 +91,14 @@ const useAreasState = ( getSegmentAbbreviationOrLabel, } = variables; const getIdentityY = useGetIdentityY(yMeasure.iri); - const { chartData, scalesData, segmentData, timeRangeData, allData } = data; + const { + chartData, + scalesData, + segmentData, + timeRangeData, + paddingData, + allData, + } = data; const { fields, interactiveFiltersConfig } = chartConfig; const width = useWidth(); @@ -281,12 +288,12 @@ const useAreasState = ( }); }, [scalesData, normalize, getXAsString, getY]); - const allYScale = useMemo(() => { + const paddingYScale = useMemo(() => { // When the user can toggle between absolute and relative values, we use the // absolute values to calculate the yScale domain, so that the yScale doesn't // change when the user toggles between absolute and relative values. if (interactiveFiltersConfig?.calculation.active) { - const scale = getStackedYScale(allData, { + const scale = getStackedYScale(paddingData, { normalize: false, getX: getXAsString, getY, @@ -299,9 +306,13 @@ const useAreasState = ( return scale; } - return getStackedYScale(allData, { normalize, getX: getXAsString, getY }); + return getStackedYScale(paddingData, { + normalize, + getX: getXAsString, + getY, + }); }, [ - allData, + paddingData, getXAsString, getY, interactiveFiltersConfig?.calculation.active, @@ -310,7 +321,7 @@ const useAreasState = ( /** Dimensions */ const { left, bottom } = useChartPadding({ - allYScale, + yScale: paddingYScale, width, aspectRatio, interactiveFiltersConfig, diff --git a/app/charts/column/columns-grouped-state.tsx b/app/charts/column/columns-grouped-state.tsx index 0bf3438f3..c26cb5598 100644 --- a/app/charts/column/columns-grouped-state.tsx +++ b/app/charts/column/columns-grouped-state.tsx @@ -90,7 +90,14 @@ const useColumnsGroupedState = ( getSegment, getSegmentAbbreviationOrLabel, } = variables; - const { chartData, scalesData, segmentData, timeRangeData, allData } = data; + const { + chartData, + scalesData, + segmentData, + timeRangeData, + paddingData, + allData, + } = data; const { fields, interactiveFiltersConfig } = chartConfig; const width = useWidth(); @@ -170,7 +177,7 @@ const useColumnsGroupedState = ( xTimeRangeDomainLabels, colors, yScale, - allYScale, + paddingYScale, interactiveXTimeRangeScale, xScale, xScaleIn, @@ -247,22 +254,26 @@ const useColumnsGroupedState = ( ); const yScale = scaleLinear().domain([minValue, maxValue]).nice(); - const allMinValue = Math.min( - min(allData, (d) => (getYErrorRange ? getYErrorRange(d)[0] : getY(d))) ?? - 0, + const minPaddingValue = Math.min( + min(paddingData, (d) => + getYErrorRange ? getYErrorRange(d)[0] : getY(d) + ) ?? 0, 0 ); - const allMaxValue = Math.max( - max(allData, (d) => (getYErrorRange ? getYErrorRange(d)[1] : getY(d))) ?? - 0, + const maxPaddingValue = Math.max( + max(paddingData, (d) => + getYErrorRange ? getYErrorRange(d)[1] : getY(d) + ) ?? 0, 0 ); - const allYScale = scaleLinear().domain([allMinValue, allMaxValue]).nice(); + const paddingYScale = scaleLinear() + .domain([minPaddingValue, maxPaddingValue]) + .nice(); return { colors, yScale, - allYScale, + paddingYScale, interactiveXTimeRangeScale, xScale, xScaleIn, @@ -282,7 +293,7 @@ const useColumnsGroupedState = ( getXLabel, segments, timeRangeData, - allData, + paddingData, allSegments, segmentsByAbbreviationOrLabel, segmentsByValue, @@ -320,7 +331,7 @@ const useColumnsGroupedState = ( }, [getSegment, getX, chartData, segmentSortingOrder, segments, xScale]); const { left, bottom } = useChartPadding({ - allYScale, + yScale: paddingYScale, width, aspectRatio, interactiveFiltersConfig, diff --git a/app/charts/column/columns-stacked-state.tsx b/app/charts/column/columns-stacked-state.tsx index b869adac7..f28526093 100644 --- a/app/charts/column/columns-stacked-state.tsx +++ b/app/charts/column/columns-stacked-state.tsx @@ -99,7 +99,14 @@ const useColumnsStackedState = ( getSegmentAbbreviationOrLabel, } = variables; const getIdentityY = useGetIdentityY(yMeasure.iri); - const { chartData, scalesData, segmentData, timeRangeData, allData } = data; + const { + chartData, + scalesData, + segmentData, + timeRangeData, + paddingData, + allData, + } = data; const { fields, interactiveFiltersConfig } = chartConfig; const width = useWidth(); @@ -312,12 +319,12 @@ const useColumnsStackedState = ( }); }, [scalesData, normalize, getX, getY, getAnimation]); - const allYScale = useMemo(() => { + const paddingYScale = useMemo(() => { // When the user can toggle between absolute and relative values, we use the // absolute values to calculate the yScale domain, so that the yScale doesn't // change when the user toggles between absolute and relative values. if (interactiveFiltersConfig?.calculation.active) { - const scale = getStackedYScale(allData, { + const scale = getStackedYScale(paddingData, { normalize: false, getX, getY, @@ -331,7 +338,7 @@ const useColumnsStackedState = ( return scale; } - return getStackedYScale(allData, { + return getStackedYScale(paddingData, { normalize, getX, getY, @@ -339,7 +346,7 @@ const useColumnsStackedState = ( }); }, [ interactiveFiltersConfig?.calculation.active, - allData, + paddingData, normalize, getX, getY, @@ -373,7 +380,7 @@ const useColumnsStackedState = ( /** Chart dimensions */ const { left, bottom } = useChartPadding({ - allYScale, + yScale: paddingYScale, width, aspectRatio, interactiveFiltersConfig, diff --git a/app/charts/column/columns-state.tsx b/app/charts/column/columns-state.tsx index 5090c2830..5358d5dce 100644 --- a/app/charts/column/columns-state.tsx +++ b/app/charts/column/columns-state.tsx @@ -78,7 +78,7 @@ const useColumnsState = ( getYError, getYErrorRange, } = variables; - const { chartData, scalesData, timeRangeData, allData } = data; + const { chartData, scalesData, timeRangeData, paddingData, allData } = data; const { fields, interactiveFiltersConfig } = chartConfig; const width = useWidth(); @@ -99,7 +99,7 @@ const useColumnsState = ( const { xScale, yScale, - allYScale, + paddingYScale, interactiveXTimeRangeScale, xScaleInteraction, xTimeRangeDomainLabels, @@ -151,22 +151,26 @@ const useColumnsState = ( ); const yScale = scaleLinear().domain([minValue, maxValue]).nice(); - const allMinValue = Math.min( - min(allData, (d) => (getYErrorRange ? getYErrorRange(d)[0] : getY(d))) ?? - 0, + const paddingMinValue = Math.min( + min(paddingData, (d) => + getYErrorRange ? getYErrorRange(d)[0] : getY(d) + ) ?? 0, 0 ); - const allMaxValue = Math.max( - max(allData, (d) => (getYErrorRange ? getYErrorRange(d)[1] : getY(d))) ?? - 0, + const paddingMaxValue = Math.max( + max(paddingData, (d) => + getYErrorRange ? getYErrorRange(d)[1] : getY(d) + ) ?? 0, 0 ); - const allYScale = scaleLinear().domain([allMinValue, allMaxValue]).nice(); + const paddingYScale = scaleLinear() + .domain([paddingMinValue, paddingMaxValue]) + .nice(); return { xScale, yScale, - allYScale, + paddingYScale, interactiveXTimeRangeScale, xScaleInteraction, xTimeRangeDomainLabels, @@ -178,7 +182,7 @@ const useColumnsState = ( getY, getYErrorRange, scalesData, - allData, + paddingData, timeRangeData, fields.x.sorting, fields.x.useAbbreviations, @@ -188,7 +192,7 @@ const useColumnsState = ( ]); const { left, bottom } = useChartPadding({ - allYScale, + yScale: paddingYScale, width, aspectRatio, interactiveFiltersConfig, diff --git a/app/charts/line/lines-state.tsx b/app/charts/line/lines-state.tsx index 623cb3ed5..ea329bd64 100644 --- a/app/charts/line/lines-state.tsx +++ b/app/charts/line/lines-state.tsx @@ -81,7 +81,14 @@ const useLinesState = ( getSegment, getSegmentAbbreviationOrLabel, } = variables; - const { chartData, scalesData, segmentData, timeRangeData, allData } = data; + const { + chartData, + scalesData, + segmentData, + timeRangeData, + paddingData, + allData, + } = data; const { fields, interactiveFiltersConfig } = chartConfig; const width = useWidth(); @@ -132,10 +139,11 @@ const useLinesState = ( const yDomain = [minValue, maxValue]; const yScale = scaleLinear().domain(yDomain).nice(); - const allMinValue = Math.min(min(allData, getY) ?? 0, 0); - const allMaxValue = max(allData, getY) ?? 0; - const allYDomain = [allMinValue, allMaxValue]; - const allYScale = scaleLinear().domain(allYDomain).nice(); + const paddingMinValue = Math.min(min(paddingData, getY) ?? 0, 0); + const paddingMaxValue = max(paddingData, getY) ?? 0; + const paddingYScale = scaleLinear() + .domain([paddingMinValue, paddingMaxValue]) + .nice(); // segments const segmentFilter = segmentDimension?.iri @@ -195,7 +203,7 @@ const useLinesState = ( // Dimensions const { left, bottom } = useChartPadding({ - allYScale, + yScale: paddingYScale, width, aspectRatio, interactiveFiltersConfig, diff --git a/app/charts/scatterplot/scatterplot-state.tsx b/app/charts/scatterplot/scatterplot-state.tsx index ad365f27f..c534aa75f 100644 --- a/app/charts/scatterplot/scatterplot-state.tsx +++ b/app/charts/scatterplot/scatterplot-state.tsx @@ -64,7 +64,7 @@ const useScatterplotState = ( getSegment, getSegmentAbbreviationOrLabel, } = variables; - const { chartData, scalesData, segmentData, allData } = data; + const { chartData, scalesData, segmentData, paddingData, allData } = data; const { fields, interactiveFiltersConfig } = chartConfig; const width = useWidth(); @@ -86,10 +86,11 @@ const useScatterplotState = ( const yDomain = [yMinValue, yMaxValue]; const yScale = scaleLinear().domain(yDomain).nice(); - const allYMinValue = Math.min(min(allData, (d) => getY(d)) ?? 0, 0); - const allYMaxValue = max(allData, getY) ?? 0; - const allYDomain = [allYMinValue, allYMaxValue]; - const allYScale = scaleLinear().domain(allYDomain).nice(); + const paddingYMinValue = Math.min(min(paddingData, (d) => getY(d)) ?? 0, 0); + const paddingYMaxValue = max(paddingData, getY) ?? 0; + const paddingYScale = scaleLinear() + .domain([paddingYMinValue, paddingYMaxValue]) + .nice(); const hasSegment = fields.segment ? true : false; const segmentFilter = segmentDimension?.iri @@ -144,7 +145,7 @@ const useScatterplotState = ( } // Dimensions const { left, bottom } = useChartPadding({ - allYScale, + yScale: paddingYScale, width, aspectRatio, interactiveFiltersConfig, diff --git a/app/charts/shared/chart-dimensions.tsx b/app/charts/shared/chart-dimensions.tsx index 87ba23321..d5c85e6ab 100644 --- a/app/charts/shared/chart-dimensions.tsx +++ b/app/charts/shared/chart-dimensions.tsx @@ -10,7 +10,7 @@ import { ChartConfig } from "@/configurator"; import { getTextWidth } from "@/utils/get-text-width"; type ComputeChartPaddingProps = { - allYScale: d3.ScaleLinear; + yScale: d3.ScaleLinear; width: number; aspectRatio: number; interactiveFiltersConfig: ChartConfig["interactiveFiltersConfig"]; @@ -22,7 +22,7 @@ type ComputeChartPaddingProps = { const computeChartPadding = (props: ComputeChartPaddingProps) => { const { - allYScale, + yScale, width, aspectRatio, interactiveFiltersConfig, @@ -36,7 +36,7 @@ const computeChartPadding = (props: ComputeChartPaddingProps) => { // with decimals have greater text length than the extremes. // Width * aspectRatio is taken as an approximation of chartHeight // since we do not have access to chartHeight yet. - const fakeTicks = allYScale.ticks(getTickNumber(width * aspectRatio)); + const fakeTicks = yScale.ticks(getTickNumber(width * aspectRatio)); const minLeftTickWidth = !!interactiveFiltersConfig?.calculation.active || normalize ? getTextWidth("100%", { fontSize: TICK_FONT_SIZE }) + TICK_PADDING @@ -66,7 +66,7 @@ const computeChartPadding = (props: ComputeChartPaddingProps) => { export const useChartPadding = (props: ComputeChartPaddingProps) => { const { - allYScale, + yScale, width, aspectRatio, interactiveFiltersConfig, @@ -78,7 +78,7 @@ export const useChartPadding = (props: ComputeChartPaddingProps) => { return useMemo(() => { return computeChartPadding({ - allYScale, + yScale, width, aspectRatio, interactiveFiltersConfig, @@ -88,7 +88,7 @@ export const useChartPadding = (props: ComputeChartPaddingProps) => { normalize, }); }, [ - allYScale, + yScale, width, aspectRatio, interactiveFiltersConfig, diff --git a/app/charts/shared/chart-state.ts b/app/charts/shared/chart-state.ts index 0d33edbe3..f1acd7f70 100644 --- a/app/charts/shared/chart-state.ts +++ b/app/charts/shared/chart-state.ts @@ -350,6 +350,8 @@ export type ChartStateData = { segmentData: Observation[]; /** Data to be used for interactive time range slider domain. */ timeRangeData: Observation[]; + /** Data used to compute the axes padding. */ + paddingData: Observation[]; /** Full dataset, needed to show the timeline when using time slider. * We can't use `scalesData` here, due to the fact the the `useChartData` hook gets * re-rendered and prevents the timeline from working it such case. */ @@ -505,11 +507,20 @@ export const useChartData = ( return observations.filter(overEvery(timeRangeFilters)); }, [observations, timeRangeFilters]); + const paddingData = React.useMemo(() => { + if (dynamicScales) { + return chartData; + } else { + return observations.filter(overEvery(interactiveLegendFilters)); + } + }, [dynamicScales, chartData, observations, interactiveLegendFilters]); + return { chartData, scalesData, segmentData, timeRangeData, + paddingData, }; }; From 7a77c28d7843cc695e813fab98d5df48bcf658a6 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Thu, 14 Sep 2023 16:38:41 +0200 Subject: [PATCH 13/21] style: Increase bottom margin --- app/charts/shared/chart-dimensions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/charts/shared/chart-dimensions.tsx b/app/charts/shared/chart-dimensions.tsx index d5c85e6ab..c4dc1f43b 100644 --- a/app/charts/shared/chart-dimensions.tsx +++ b/app/charts/shared/chart-dimensions.tsx @@ -53,7 +53,7 @@ const computeChartPadding = (props: ComputeChartPaddingProps) => { let bottom = interactiveFiltersConfig?.timeRange.active || animationPresent ? BRUSH_BOTTOM_SPACE - : 40; + : 48; if (bandDomain?.length) { bottom += From 82a1eeac79fc2e468d2bdfd2eeaea78ee2166cf5 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Thu, 14 Sep 2023 16:48:19 +0200 Subject: [PATCH 14/21] feat: Show latest data when animation is enabled --- CHANGELOG.md | 2 ++ app/utils/observables.ts | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 765a953d0..b58d279ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ You can also check the [release page](https://github.com/visualize-admin/visuali ## Unreleased +- Features + - Animated charts now show latest data as default - Fixes - Table docs now work correctly again - Cascading mode now works correctly for hierarchical dimensions used as interactive filters diff --git a/app/utils/observables.ts b/app/utils/observables.ts index 3bebf5f34..7eb3ea0bb 100644 --- a/app/utils/observables.ts +++ b/app/utils/observables.ts @@ -53,7 +53,7 @@ export class Timeline extends Observable { // Animation state. public playing = false; /** Animation progress (0-1). */ - private animationProgress = 0; + private animationProgress = 1; /** Duration of the animation in miliseconds. */ private animationDuration: number; private requestAnimationFrameId: number | undefined; @@ -106,7 +106,7 @@ export class Timeline extends Observable { this.msValues[0], this.msValues[this.msValues.length - 1], ]; - this.msValue = min; + this.msValue = max; this.minMsValue = min; this.maxMsValue = max; this.msValueScale = this.msValueScale.range([min, max]); From 6a95c40d1a5e27ee0ad46db712ca2b2c41cfa2f1 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 19 Sep 2023 13:20:35 +0200 Subject: [PATCH 15/21] docs: Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b58d279ed..555d8a8ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ You can also check the [release page](https://github.com/visualize-admin/visuali ## Unreleased +Nothing yet. + +# [3.22.6] - 2023-09-19 + - Features - Animated charts now show latest data as default - Fixes From 929ead09eb95022258352e95728e649a5ed77fa5 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 19 Sep 2023 13:20:54 +0200 Subject: [PATCH 16/21] v3.22.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 877b17e03..b2b7ca438 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@visualize-admin/visualization-tool", - "version": "3.22.5", + "version": "3.22.6", "repository": { "type": "git", "url": "https://github.com/visualize-admin/visualization-tool.git" From 9e9daeb946a57bbd81bd10158615da5b5fee67cb Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Fri, 22 Sep 2023 10:01:55 +0200 Subject: [PATCH 17/21] v3.22.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b2b7ca438..42c8244ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@visualize-admin/visualization-tool", - "version": "3.22.6", + "version": "3.22.7", "repository": { "type": "git", "url": "https://github.com/visualize-admin/visualization-tool.git" From f4ec715038baaedb82704aaec586080f339e2e5e Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Fri, 22 Sep 2023 11:43:05 +0200 Subject: [PATCH 18/21] fix: Cube check on dimensions --- CHANGELOG.md | 3 ++- app/pages/_cube-checker.tsx | 14 +++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 555d8a8ac..e6c57eed0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ You can also check the [release page](https://github.com/visualize-admin/visuali ## Unreleased -Nothing yet. +- Fixes + - Cube checker now correctly checks if dimensions are present # [3.22.6] - 2023-09-19 diff --git a/app/pages/_cube-checker.tsx b/app/pages/_cube-checker.tsx index faa480179..74aeb8689 100644 --- a/app/pages/_cube-checker.tsx +++ b/app/pages/_cube-checker.tsx @@ -6,7 +6,6 @@ import DataLoader from "dataloader"; import omit from "lodash/omit"; import { GetServerSideProps, NextPage } from "next"; import rdf from "rdf-ext"; -import React from "react"; import StreamClient from "sparql-http-client"; import ParsingClient from "sparql-http-client/ParsingClient"; @@ -148,8 +147,17 @@ const checks: Check[] = [ description: "Should have a number of dimensions", run: async ({ cubeIri, loaders }) => { const dimensions = await loaders.getCubeDimensions.load(cubeIri); - console.log(dimensions); - return { ok: true, message: `Has ${dimensions?.length} dimensions` }; + if (dimensions) { + return { + ok: true, + message: `Has ${dimensions.length} dimensions`, + }; + } else { + return { + ok: false, + message: "No dimensions", + }; + } }, }, { From 5caec402cceaf6426526565f9baa8f01947d4089 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Fri, 22 Sep 2023 16:00:05 +0200 Subject: [PATCH 19/21] fix: Retrieve either ComponentsWithHierarchies or Components (cache) --- CHANGELOG.md | 1 + app/configurator/configurator-state.tsx | 33 ++++++++++++++----------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6c57eed0..b874b9dc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ You can also check the [release page](https://github.com/visualize-admin/visuali - Fixes - Cube checker now correctly checks if dimensions are present + - It's now possible to change the chart type for copied, non-hierarchical charts without having to open an options panel first # [3.22.6] - 2023-09-19 diff --git a/app/configurator/configurator-state.tsx b/app/configurator/configurator-state.tsx index ea01a42a8..8b36811f5 100644 --- a/app/configurator/configurator-state.tsx +++ b/app/configurator/configurator-state.tsx @@ -58,6 +58,9 @@ import { DimensionValue, isGeoDimension } from "@/domain/data"; import { DEFAULT_DATA_SOURCE } from "@/domain/datasource"; import { client } from "@/graphql/client"; import { + ComponentsDocument, + ComponentsQuery, + ComponentsQueryVariables, ComponentsWithHierarchiesDocument, ComponentsWithHierarchiesQuery, ComponentsWithHierarchiesQueryVariables, @@ -329,24 +332,26 @@ const getCachedMetadata = ( draft: ConfiguratorStateConfiguringChart, locale: Locale ): DataCubeMetadataWithHierarchies | null => { - const metadataQuery = client.readQuery< - DataCubeMetadataQuery, - DataCubeMetadataQueryVariables - >(DataCubeMetadataDocument, { - iri: draft.dataSet, - locale, - sourceType: draft.dataSource.type, - sourceUrl: draft.dataSource.url, - }); - const componentsQuery = client.readQuery< - ComponentsWithHierarchiesQuery, - ComponentsWithHierarchiesQueryVariables - >(ComponentsWithHierarchiesDocument, { + const variables = { iri: draft.dataSet, locale, sourceType: draft.dataSource.type, sourceUrl: draft.dataSource.url, - }); + }; + const metadataQuery = client.readQuery< + DataCubeMetadataQuery, + DataCubeMetadataQueryVariables + >(DataCubeMetadataDocument, variables); + // Some charts use hierarchical query, so we need to check for both. + const componentsQuery = + client.readQuery< + ComponentsWithHierarchiesQuery, + ComponentsWithHierarchiesQueryVariables + >(ComponentsWithHierarchiesDocument, variables) ?? + client.readQuery( + ComponentsDocument, + variables + ); return metadataQuery?.data?.dataCubeByIri && componentsQuery?.data?.dataCubeByIri From 22a456547ccae48e1044e60b24fbf70678e37673 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Wed, 27 Sep 2023 12:01:18 +0200 Subject: [PATCH 20/21] fix: Use the same y padding across different filters --- app/components/select-tree.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/select-tree.tsx b/app/components/select-tree.tsx index a2538bc4b..7aa454776 100644 --- a/app/components/select-tree.tsx +++ b/app/components/select-tree.tsx @@ -481,7 +481,7 @@ function SelectTree({ return (
{label && ( -