Skip to content

Commit

Permalink
Merge branch 'main' of github.com:visualize-admin/visualization-tool …
Browse files Browse the repository at this point in the history
…into perf/general-optimizations
  • Loading branch information
bprusinowski committed Oct 9, 2023
2 parents 761ebb6 + a09cdc3 commit f500aad
Show file tree
Hide file tree
Showing 23 changed files with 398 additions and 101 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,26 @@ You can also check the [release page](https://github.com/visualize-admin/visuali

## Unreleased

Nothing yet.

# [3.22.9] - 2023-10-06

- Fixes
- Cascading filters are not stuck anymore in the loading mode in some cases
- Maintenance
- GQL debug panel now includes queries fired through SPARQLClientStream (e.g. hierarchies) and CONSTRUCT queries
- Configurator and interactive filters debug panel is now displayed if `flag__debug` is set to true
- Docs
- Added chart preview via API section to the documentation

# [3.22.8] - 2023-09-29

- 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
- Interactive filters are now aligned correctly (y axis)
- Performance
- Dataset preview should now load quicker as we no longer fetch dimension values along with it

# [3.22.6] - 2023-09-19

Expand Down
17 changes: 9 additions & 8 deletions app/charts/shared/chart-data-filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
useInteractiveFiltersRaw,
} from "@/stores/interactive-filters";
import { hierarchyToOptions } from "@/utils/hierarchy";
import useEvent from "@/utils/use-event";

type ChartDataFiltersProps = {
dataSet: string;
Expand Down Expand Up @@ -227,11 +228,11 @@ const DataFilter = (props: DataFilterProps) => {
const dimension = data?.dataCubeByIri?.dimensionByIri;
const hierarchy = data?.dataCubeByIri?.dimensionByIri?.hierarchy;

const setDataFilter = (
e: SelectChangeEvent<unknown> | { target: { value: string } }
) => {
updateDataFilter(dimensionIri, e.target.value as string);
};
const setDataFilter = useEvent(
(e: SelectChangeEvent<unknown> | { target: { value: string } }) => {
updateDataFilter(dimensionIri, e.target.value as string);
}
);

const configFilter = dimension
? chartConfig.filters[dimension.iri]
Expand Down Expand Up @@ -263,9 +264,9 @@ const DataFilter = (props: DataFilterProps) => {
dimension?.values,
dimensionIri,
fetching,
updateDataFilter,
// Also reload when the config value changes.
setDataFilter,
configFilterValue,
updateDataFilter,
]);

return dimension ? (
Expand Down Expand Up @@ -603,7 +604,7 @@ const useEnsurePossibleInteractiveFilters = (

// We need to get the values dynamically, as they can get updated by
// useSyncInteractiveFilters and this callback runs with old value.
const dataFilters = IFRaw.getState().dataFilters;
const dataFilters = { ...IFRaw.getState().dataFilters };

if (!isEqual(filters, interactiveFilters) && !isEmpty(filters)) {
for (const [k, v] of Object.entries(filters)) {
Expand Down
18 changes: 7 additions & 11 deletions app/components/debug-panel/DebugPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,14 @@ DESCRIBE <${configuratorState.dataSet ?? ""}>`
);
};

const DebugPanel = ({
configurator = false,
interactiveFilters = false,
}: {
export type DebugPanelProps = {
configurator?: Boolean;
interactiveFilters?: Boolean;
}) => {
};

const DebugPanel = (props: DebugPanelProps) => {
const { configurator = false, interactiveFilters = false } = props;

return (
<Box
sx={{
Expand All @@ -199,9 +200,4 @@ const DebugPanel = ({
);
};

const DebugPanelNull = () => null;

const ExportedDebugPanel =
process.env.NODE_ENV === "development" ? DebugPanel : DebugPanelNull;

export default ExportedDebugPanel;
export default DebugPanel;
30 changes: 21 additions & 9 deletions app/components/debug-panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@ import { CircularProgress } from "@mui/material";
import dynamic from "next/dynamic";
import { Suspense } from "react";

const LazyDebugPanel = dynamic(() => import("./DebugPanel"));

export default process.env.NODE_ENV === "development"
? (props: React.ComponentProps<typeof LazyDebugPanel>) => (
<Suspense fallback={<CircularProgress />}>
<LazyDebugPanel {...props} />
</Suspense>
)
: () => null;
import { flag } from "@/flags";

import { DebugPanelProps } from "./DebugPanel";

const LazyDebugPanel = dynamic(() => import("./DebugPanel"), { ssr: false });

const DebugPanel = (props: DebugPanelProps) => {
const shouldShow = flag("debug") || process.env.NODE_ENV === "development";

if (!shouldShow) {
return null;
}

return (
<Suspense fallback={<CircularProgress />}>
<LazyDebugPanel {...props} />
</Suspense>
);
};

export default DebugPanel;
2 changes: 1 addition & 1 deletion app/components/select-tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ function SelectTree({
return (
<div>
{label && (
<Label htmlFor={id!} smaller sx={{ mb: 1 }}>
<Label htmlFor={id!} smaller sx={{ my: 1 }}>
{label} {controls}
</Label>
)}
Expand Down
33 changes: 19 additions & 14 deletions app/configurator/configurator-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<ComponentsQuery, ComponentsQueryVariables>(
ComponentsDocument,
variables
);

return metadataQuery?.data?.dataCubeByIri &&
componentsQuery?.data?.dataCubeByIri
Expand Down
160 changes: 160 additions & 0 deletions app/docs/chart-preview-via-api.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<style>{`
table:not([class]) {
margin-top: 1rem;
font-size: 0.875rem;
cell-spacing: none;
border-spacing: 0;
border-collapse: collapse;
}
table:not([class]) tr:nth-child(2n) {
background: #eee;
}
table:not([class]) td, table:not([class]) th {
border-bottom: #ccc 1px solid;
margin-top: 0;
padding: 0.25rem 0.5rem;
}
table:not([class]) tr {
margin-bottom: 0;
}
li > code {
font-size: 0.875rem;
}
`}</style>

While usually you'll want to publish your chart, sometimes you might want to simply preview it, without going through the publishing process.
This could be especially helpful to programatically generate charts based on many different configuration options.
Visualize offers a way to preview charts without publishing them, by using a custom API.

## iframe

**Demo**: Visit <a href="/_preview" target="_blank">`/_preview`</a> to see a page with an iframe containing a chart preview.

This method works by pointing an iframe to the `/preview` page, and posting a message with the chart state to the iframe window on load.

<CodeSpecimen
lang="js"
raw
rawBody={`const iframe = document.getElementById("chart");
iframe.onload = () => {
const iframeWindow = iframe?.contentWindow;
if (iframeWindow) {
iframeWindow.postMessage(chartState, "*");
}
};
`}
/>

## POST request

**Demo**: Visit <a href="/_preview_post" target="_blank">`/_preview_post`</a> to see a page with two buttons that open a new page with a chart preview after clicking.

This method works by sending a POST request to `/preview_post` page with chart state when clicking on a form button.
The `/preview_post` page retrieves the content of a request in `getServerSideProps` and renders a preview of a chart.

It's important to only use one input inside a form (as we split the string by `=`).

<CodeSpecimen
lang="css"
raw
rawBody={`<form method="post" action="/preview_post" target="_blank">
<input
type="hidden"
name="chartState"
value={JSON.stringify(photovoltaikanlagenState)}
/>
<input type="submit" value="☀️ Preview a Photovoltaikanlagen chart" />
</form>`}
/>

### Chart config schema

As the application constantly evolves, the chart config schema might change.
You can find the latest version of the schema in the [config-types.ts](https://github.com/visualize-admin/visualization-tool/blob/main/app/config-types.ts) file.

An example chart config is shown below.

<CodeSpecimen
lang="json"
raw
rawBody={`{
state: "CONFIGURING_CHART",
dataSet:
"https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/7",
dataSource: {
type: "sparql",
url: "https://lindas.admin.ch/query",
},
meta: {
title: {
de: "",
fr: "",
it: "",
en: "",
},
description: {
de: "",
fr: "",
it: "",
en: "",
},
},
chartConfig: {
version: "1.4.2",
chartType: "column",
filters: {
"https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Kanton":
{
type: "single",
value: "https://ld.admin.ch/canton/1",
},
},
interactiveFiltersConfig: {
legend: {
active: false,
componentIri: "",
},
timeRange: {
active: false,
componentIri:
"https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Jahr",
presets: {
type: "range",
from: "",
to: "",
},
},
dataFilters: {
active: false,
componentIris: [],
},
calculation: {
active: false,
type: "identity",
},
},
fields: {
x: {
componentIri:
"https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Jahr",
sorting: {
sortingType: "byAuto",
sortingOrder: "asc",
},
},
y: {
componentIri:
"https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/AnzahlAnlagen",
},
},
},
}`}
/>

Note that it's encouraged to visit the Visualize application with &flag\_\_debug==true added to the URL to enable
the debug mode, which will show the chart config directly below the chart that is being edited. It also enables
the "Dump to console" button, which will log the chart config to the browser console, for easier re-use.
Loading

0 comments on commit f500aad

Please sign in to comment.