diff --git a/app/browser/dataset-preview.tsx b/app/browser/dataset-preview.tsx index d1f2e53cc..b8466f682 100644 --- a/app/browser/dataset-preview.tsx +++ b/app/browser/dataset-preview.tsx @@ -8,7 +8,6 @@ import * as React from "react"; import { DataSetPreviewTable } from "@/browse/datatable"; import { useFootnotesStyles } from "@/components/chart-footnotes"; import { DataDownloadMenu, RunSparqlQuery } from "@/components/data-download"; -import DebugPanel from "@/components/debug-panel"; import Flex from "@/components/flex"; import { HintRed, Loading, LoadingDataError } from "@/components/hint"; import { DataSource } from "@/config-types"; @@ -194,7 +193,6 @@ export const DataSetPreview = ({ - ); diff --git a/app/charts/index.spec.ts b/app/charts/index.spec.ts index 2d0e0f1f5..47b385616 100644 --- a/app/charts/index.spec.ts +++ b/app/charts/index.spec.ts @@ -15,6 +15,7 @@ describe("initial config", () => { it("should create an initial table config with column order based on dimension order", () => { const config = getInitialConfig({ chartType: "table", + dataSet: "https://environment.ld.admin.ch/foen/nfi", dimensions: forestAreaData.data.dataCubeByIri.dimensions as NonNullable< ComponentsQuery["dataCubeByIri"] >["dimensions"], @@ -81,6 +82,7 @@ describe("chart type switch", () => { key: "column", version: "1.4.0", chartType: "column", + dataSet: "https://environment.ld.admin.ch/foen/ubd0104", filters: {}, meta: { title: { diff --git a/app/charts/index.ts b/app/charts/index.ts index 556b34af5..b790c0775 100644 --- a/app/charts/index.ts +++ b/app/charts/index.ts @@ -327,6 +327,7 @@ const META: Meta = { type GetInitialConfigOptions = { key?: string; + dataSet: string; chartType: ChartType; dimensions: DataCubeMetadataWithHierarchies["dimensions"]; measures: DataCubeMetadataWithHierarchies["measures"]; @@ -335,16 +336,18 @@ type GetInitialConfigOptions = { export const getInitialConfig = ( options: GetInitialConfigOptions ): ChartConfig => { - const { key, chartType, dimensions, measures } = options; + const { key, dataSet, chartType, dimensions, measures } = options; const genericConfigProps: { key: string; version: string; meta: Meta; + dataSet: string; activeField: string | undefined; } = { key: key ?? createChartId(), version: CHART_CONFIG_VERSION, meta: META, + dataSet, activeField: undefined, }; const numericalMeasures = measures.filter(isNumericalMeasure); @@ -667,6 +670,7 @@ export const getChartConfigAdjustedToChartType = ({ const initialConfig = getInitialConfig({ key: chartConfig.key, chartType: newChartType, + dataSet: chartConfig.dataSet, dimensions, measures, }); diff --git a/app/charts/shared/legend-color.tsx b/app/charts/shared/legend-color.tsx index 1659fb76a..87da7d3d9 100644 --- a/app/charts/shared/legend-color.tsx +++ b/app/charts/shared/legend-color.tsx @@ -171,9 +171,9 @@ const useLegendGroups = ({ const segmentValues = segmentFilters?.type === "multi" ? segmentFilters.values : emptyObj; - const { dataSet: dataset, dataSource } = configState; + const { dataSource } = configState; const segmentDimension = useDimension({ - dataset, + dataset: chartConfig.dataSet, dataSource, locale, dimensionIri: segmentField?.componentIri, @@ -181,7 +181,7 @@ const useLegendGroups = ({ const [hierarchyResp] = useDimensionValuesQuery({ variables: { - dataCubeIri: dataset, + dataCubeIri: chartConfig.dataSet, dimensionIri: segmentDimension?.iri!, sourceType: dataSource.type, sourceUrl: dataSource.url, diff --git a/app/charts/shared/use-sync-interactive-filters.spec.tsx b/app/charts/shared/use-sync-interactive-filters.spec.tsx index 5c4b364c7..0d4fe1812 100644 --- a/app/charts/shared/use-sync-interactive-filters.spec.tsx +++ b/app/charts/shared/use-sync-interactive-filters.spec.tsx @@ -45,6 +45,7 @@ const chartConfig = migrateChartConfig( { migrationProps: { meta: {}, + dataSet: "foo", }, } ) as ChartConfig; diff --git a/app/components/chart-published.tsx b/app/components/chart-published.tsx index c448d27ab..264a4eada 100644 --- a/app/components/chart-published.tsx +++ b/app/components/chart-published.tsx @@ -51,13 +51,12 @@ type ChartPublishedProps = { export const ChartPublished = (props: ChartPublishedProps) => { const { configKey } = props; const [state] = useConfiguratorState(isPublished); - const { dataSet, dataSource } = state; + const { dataSource } = state; const chartConfig = getChartConfig(state); return ( ((theme) => ({ })); type ChartPublishInnerProps = { - dataSet: string; dataSource: DataSource | undefined; state: ConfiguratorStatePublished; chartConfig: ChartConfig; @@ -92,7 +90,6 @@ type ChartPublishInnerProps = { const ChartPublishedInner = (props: ChartPublishInnerProps) => { const { - dataSet, dataSource = DEFAULT_DATA_SOURCE, state, chartConfig, @@ -136,7 +133,7 @@ const ChartPublishedInner = (props: ChartPublishInnerProps) => { const locale = useLocale(); const isTrustedDataSource = useIsTrustedDataSource(dataSource); const commonQueryVariables = { - iri: dataSet, + iri: chartConfig.dataSet, sourceType: dataSource.type, sourceUrl: dataSource.url, locale, @@ -225,7 +222,7 @@ const ChartPublishedInner = (props: ChartPublishInnerProps) => { { {isTablePreview ? ( ) : ( { )} { const PublishChartButton = () => { const locale = useLocale(); const [state, dispatch] = useConfiguratorState(hasChartConfigs); - const { dataSet } = state; + const chartConfig = getChartConfig(state); const variables = { - iri: dataSet ?? "", + iri: chartConfig.dataSet, sourceType: state.dataSource.type, sourceUrl: state.dataSource.url, locale, }; - const [{ data: metadata }] = useDataCubeMetadataQuery({ - variables, - pause: !dataSet, - }); - const [{ data: components }] = useComponentsQuery({ - variables, - pause: !dataSet, - }); + const [{ data: metadata }] = useDataCubeMetadataQuery({ variables }); + const [{ data: components }] = useComponentsQuery({ variables }); const goNext = useEvent(() => { if (metadata?.dataCubeByIri && components?.dataCubeByIri) { dispatch({ diff --git a/app/components/debug-panel/DebugPanel.tsx b/app/components/debug-panel/DebugPanel.tsx index f3b010e0c..f87bcf9f7 100644 --- a/app/components/debug-panel/DebugPanel.tsx +++ b/app/components/debug-panel/DebugPanel.tsx @@ -12,7 +12,11 @@ import { import { useState } from "react"; import { Inspector } from "react-inspector"; -import { DataSource, useConfiguratorState } from "@/configurator"; +import { + DataSource, + getChartConfig, + useConfiguratorState, +} from "@/configurator"; import { dataSourceToSparqlEditorUrl } from "@/domain/datasource"; import { useComponentsWithHierarchiesQuery } from "@/graphql/query-hooks"; import { Icon } from "@/icons"; @@ -86,6 +90,7 @@ const CubeMetadata = ({ const DebugConfigurator = () => { const [configuratorState] = useConfiguratorState(); + const chartConfig = getChartConfig(configuratorState); const sparqlEditorUrl = dataSourceToSparqlEditorUrl( configuratorState.dataSource ); @@ -96,51 +101,43 @@ const DebugConfigurator = () => { Cube Tools - {configuratorState.dataSet ? ( - - ) : ( - Please select a dataset first - )} - { - + - } - {configuratorState.dataSet ? ( - - ) : null} +DESCRIBE <${chartConfig.dataSet}>` + )}&requestMethod=POST`} + target="_blank" + rel="noopener noreferrer" + startIcon={} + > + Cube Metadata Query + + ; const Config = t.type( { version: t.string, - dataSet: t.string, dataSource: DataSource, meta: Meta, chartConfigs: t.array(ChartConfig), @@ -1097,7 +1097,6 @@ export const decodeConfig = (config: unknown) => { const ConfiguratorStateInitial = t.type({ version: t.string, state: t.literal("INITIAL"), - dataSet: t.undefined, dataSource: DataSource, }); export type ConfiguratorStateInitial = t.TypeOf< @@ -1107,7 +1106,6 @@ export type ConfiguratorStateInitial = t.TypeOf< const ConfiguratorStateSelectingDataSet = t.type({ version: t.string, state: t.literal("SELECTING_DATASET"), - dataSet: t.union([t.string, t.undefined]), dataSource: DataSource, meta: Meta, chartConfigs: t.undefined, diff --git a/app/configurator/components/chart-configurator.tsx b/app/configurator/components/chart-configurator.tsx index 74f753e27..6e576a39a 100644 --- a/app/configurator/components/chart-configurator.tsx +++ b/app/configurator/components/chart-configurator.tsx @@ -190,7 +190,7 @@ const useEnsurePossibleFilters = ({ .query( PossibleFiltersDocument, { - iri: state.dataSet, + iri: chartConfig.dataSet, sourceType: state.dataSource.type, sourceUrl: state.dataSource.url, filters: unmappedFilters, @@ -234,7 +234,7 @@ const useEnsurePossibleFilters = ({ client, dispatch, chartConfig, - state.dataSet, + chartConfig.dataSet, state.dataSource.type, state.dataSource.url, ]); @@ -263,7 +263,7 @@ const useFilterReorder = ({ const variables = useMemo(() => { const hasUnmappedFilters = Object.keys(unmappedFilters).length > 0; const vars = { - iri: state.dataSet, + iri: chartConfig.dataSet, sourceType: state.dataSource.type, sourceUrl: state.dataSource.url, locale, @@ -279,7 +279,7 @@ const useFilterReorder = ({ return omitBy(vars, (x) => x === undefined) as typeof vars; }, [ - state.dataSet, + chartConfig.dataSet, state.dataSource.type, state.dataSource.url, locale, diff --git a/app/configurator/components/chart-options-selector.tsx b/app/configurator/components/chart-options-selector.tsx index bc19333bd..fc1b6955e 100644 --- a/app/configurator/components/chart-options-selector.tsx +++ b/app/configurator/components/chart-options-selector.tsx @@ -93,31 +93,24 @@ export const ChartOptionsSelector = ({ state: ConfiguratorStateConfiguringChart; }) => { const chartConfig = getChartConfig(state); - const { dataSet, dataSource } = state; + const { dataSource } = state; const { activeField } = chartConfig; const locale = useLocale(); + const commonVariables = { + iri: chartConfig.dataSet, + sourceType: dataSource.type, + sourceUrl: dataSource.url, + locale, + }; const [{ data: metadataData }] = useDataCubeMetadataQuery({ - variables: { - iri: dataSet, - sourceType: dataSource.type, - sourceUrl: dataSource.url, - locale, - }, + variables: commonVariables, }); const [{ data: componentsData }] = useComponentsWithHierarchiesQuery({ - variables: { - iri: dataSet, - sourceType: dataSource.type, - sourceUrl: dataSource.url, - locale, - }, + variables: commonVariables, }); const [{ data: observationsData }] = useDataCubeObservationsQuery({ variables: { - iri: dataSet, - sourceType: dataSource.type, - sourceUrl: dataSource.url, - locale, + ...commonVariables, filters: chartConfig.filters, }, }); @@ -146,7 +139,6 @@ export const ChartOptionsSelector = ({ ) : ( { - const { state, metadata, chartConfig, observations } = props; + const { metadata, chartConfig, observations } = props; const { dimensions, measures } = metadata; const activeField = chartConfig.activeField as EncodingFieldType | undefined; @@ -204,7 +195,6 @@ const ActiveFieldSwitch = (props: ActiveFieldSwitchProps) => { return ( { type EncodingOptionsPanelProps = { encoding: EncodingSpec; - state: ConfiguratorStateConfiguringChart; chartConfig: ChartConfig; field: EncodingFieldType; component: DimensionMetadataFragment | undefined; @@ -229,7 +218,6 @@ type EncodingOptionsPanelProps = { const EncodingOptionsPanel = (props: EncodingOptionsPanelProps) => { const { encoding, - state, field, chartConfig, component, @@ -409,7 +397,6 @@ const EncodingOptionsPanel = (props: EncodingOptionsPanelProps) => { {encoding.options?.colorComponent && component && ( { )} { }; const ChartFieldMultiFilter = ({ - state, chartConfig, component, encoding, @@ -1203,7 +1188,6 @@ const ChartFieldMultiFilter = ({ dimensions, measures, }: { - state: ConfiguratorStateConfiguringChart; chartConfig: ChartConfig; component: DimensionMetadataFragment | undefined; encoding: EncodingSpec; @@ -1246,7 +1230,7 @@ const ChartFieldMultiFilter = ({ { const { - state, chartConfig, encoding, component, @@ -1660,7 +1642,7 @@ const ChartFieldColorComponent = (props: ChartFieldColorComponentProps) => { colorComponentIri && component.iri !== colorComponentIri ? ( { const locale = useLocale(); + const chartConfig = getChartConfig(state); const [{ data }] = useComponentsWithHierarchiesQuery({ variables: { - iri: state.dataSet, + iri: chartConfig.dataSet, sourceType: state.dataSource.type, sourceUrl: state.dataSource.url, locale, diff --git a/app/configurator/components/configurator.tsx b/app/configurator/components/configurator.tsx index efd0ed4c0..935eed748 100644 --- a/app/configurator/components/configurator.tsx +++ b/app/configurator/components/configurator.tsx @@ -70,6 +70,7 @@ const isAnnotationField = (field: string | undefined) => { const ConfigureChartStep = () => { const [state, dispatch] = useConfiguratorState(); + const chartConfig = getChartConfig(state); const { dataSource, setDataSource } = useDataSourceStore(); const handleClosePanel = useEvent(() => { @@ -85,11 +86,12 @@ const ConfigureChartStep = () => { if (state.state !== "CONFIGURING_CHART") { return; } + router.push( { pathname: `/browse`, query: { - dataset: state.dataSet, + dataset: chartConfig.dataSet, }, }, undefined, @@ -110,8 +112,6 @@ const ConfigureChartStep = () => { return null; } - const chartConfig = getChartConfig(state); - return ( { @@ -173,6 +173,7 @@ const ConfigureChartStep = () => { const PublishStep = () => { const [state] = useConfiguratorState(); + const chartConfig = getChartConfig(state); if (state.state !== "PUBLISHING") { return null; @@ -183,7 +184,7 @@ const PublishStep = () => { diff --git a/app/configurator/config-form.tsx b/app/configurator/config-form.tsx index fabdcfdab..5be4a01d3 100644 --- a/app/configurator/config-form.tsx +++ b/app/configurator/config-form.tsx @@ -95,6 +95,7 @@ export const useChartFieldField = ({ const unmountedRef = useRef(false); const [fetching, setFetching] = useState(false); const [state, dispatch] = useConfiguratorState(); + const chartConfig = getChartConfig(state); const client = useClient(); const locale = useLocale(); @@ -105,10 +106,6 @@ export const useChartFieldField = ({ }, []); const handleChange = useEvent(async (e: SelectChangeEvent) => { - if (!state.dataSet) { - return; - } - if (e.target.value !== FIELD_VALUE_NONE) { setFetching(true); const dimensionIri = e.target.value as string; @@ -117,7 +114,7 @@ export const useChartFieldField = ({ DimensionHierarchyDocument, { locale, - cubeIri: state.dataSet, + cubeIri: chartConfig.dataSet, dimensionIri, sourceUrl: state.dataSource.url, sourceType: state.dataSource.type, @@ -435,6 +432,10 @@ export const useChartType = ( value: { chartConfig: getInitialConfig({ chartType, + dataSet: + state.state === "CONFIGURING_CHART" + ? getChartConfig(state, state.activeChartKey).dataSet + : chartConfig.dataSet, dimensions, measures, }), diff --git a/app/configurator/configurator-state.tsx b/app/configurator/configurator-state.tsx index 7bf66cb1e..6a58b9c99 100644 --- a/app/configurator/configurator-state.tsx +++ b/app/configurator/configurator-state.tsx @@ -108,10 +108,6 @@ export type ConfiguratorStateAction = "INITIAL" | "PUBLISHING" | "PUBLISHED" >; } - | { - type: "DATASET_SELECTED"; - dataSet: string | undefined; - } | { type: "DATASOURCE_CHANGED"; value: DataSource; @@ -329,14 +325,13 @@ const getStateWithCurrentDataSource = (state: ConfiguratorState) => { return { ...state, - dataSource: dataSource || DEFAULT_DATA_SOURCE, + dataSource: dataSource ?? DEFAULT_DATA_SOURCE, }; }; const INITIAL_STATE: ConfiguratorState = { version: CONFIGURATOR_STATE_VERSION, state: "INITIAL", - dataSet: undefined, dataSource: DEFAULT_DATA_SOURCE, }; @@ -344,7 +339,6 @@ const EMPTY_STATE: ConfiguratorStateSelectingDataSet = { ...INITIAL_STATE, version: CONFIGURATOR_STATE_VERSION, state: "SELECTING_DATASET", - dataSet: undefined, dataSource: DEFAULT_DATA_SOURCE, chartConfigs: undefined, meta: { @@ -366,10 +360,11 @@ const EMPTY_STATE: ConfiguratorStateSelectingDataSet = { const getCachedMetadata = ( draft: ConfiguratorStateConfiguringChart, + dataSet: string, locale: Locale ): DataCubeMetadataWithHierarchies | null => { const variables = { - iri: draft.dataSet, + iri: dataSet, locale, sourceType: draft.dataSource.type, sourceUrl: draft.dataSource.url, @@ -610,11 +605,16 @@ export const applyNonTableDimensionToFilters = ({ const transitionStepNext = ( draft: ConfiguratorState, - dataSetMetadata: DataCubeMetadataWithHierarchies + options: { + dataSet?: string; + dataSetMetadata: DataCubeMetadataWithHierarchies; + } ): ConfiguratorState => { + const { dataSet, dataSetMetadata } = options; + switch (draft.state) { case "SELECTING_DATASET": - if (draft.dataSet) { + if (dataSet) { const possibleChartTypes = getPossibleChartTypes({ dimensions: dataSetMetadata.dimensions, measures: dataSetMetadata.measures, @@ -622,6 +622,7 @@ const transitionStepNext = ( const chartConfig = deriveFiltersFromFields( getInitialConfig({ chartType: possibleChartTypes[0], + dataSet, dimensions: dataSetMetadata.dimensions, measures: dataSetMetadata.measures, }), @@ -631,7 +632,6 @@ const transitionStepNext = ( return { version: CONFIGURATOR_STATE_VERSION, state: "CONFIGURING_CHART", - dataSet: draft.dataSet, dataSource: draft.dataSource, meta: draft.meta, chartConfigs: [chartConfig], @@ -773,7 +773,7 @@ export const handleChartFieldChanged = ( } = action.value; const f = get(chartConfig.fields, field); const { dimensions = [], measures = [] } = - getCachedMetadata(draft, locale) ?? {}; + getCachedMetadata(draft, chartConfig.dataSet, locale) ?? {}; const components = [...dimensions, ...measures]; const component = components.find((d) => d.iri === componentIri); const selectedValues = actionSelectedValues ?? component?.values ?? []; @@ -822,7 +822,7 @@ export const handleChartOptionChanged = ( const chartConfig = getChartConfig(draft); const updatePath = field === null ? path : `fields["${field}"].${path}`; const { dimensions = [], measures = [] } = - getCachedMetadata(draft, locale) ?? {}; + getCachedMetadata(draft, chartConfig.dataSet, locale) ?? {}; if (field) { const sideEffect = getChartFieldOptionChangeSideEffect( @@ -920,11 +920,6 @@ const reducer: Reducer = ( return action.value.state === "INITIAL" ? getStateWithCurrentDataSource(EMPTY_STATE) : action.value; - case "DATASET_SELECTED": - if (draft.state === "SELECTING_DATASET") { - draft.dataSet = action.dataSet; - } - return draft; case "DATASOURCE_CHANGED": draft.dataSource = action.value; @@ -933,11 +928,11 @@ const reducer: Reducer = ( case "CHART_TYPE_CHANGED": if (draft.state === "CONFIGURING_CHART") { const { locale, chartKey, chartType } = action.value; - const metadata = getCachedMetadata(draft, locale); + const chartConfig = getChartConfig(draft, chartKey); + const metadata = getCachedMetadata(draft, chartConfig.dataSet, locale); if (metadata) { const { dimensions, measures } = metadata; - const chartConfig = getChartConfig(draft, chartKey); const newConfig = deriveFiltersFromFields( getChartConfigAdjustedToChartType({ chartConfig: current(chartConfig), @@ -972,10 +967,12 @@ const reducer: Reducer = ( if (draft.state === "CONFIGURING_CHART") { const chartConfig = getChartConfig(draft); delete (chartConfig.fields as GenericFields)[action.value.field]; - - const metadata = getCachedMetadata(draft, action.value.locale); + const metadata = getCachedMetadata( + draft, + chartConfig.dataSet, + action.value.locale + ); const dimensions = metadata?.dimensions ?? []; - deriveFiltersFromFields(chartConfig, dimensions); if ( @@ -1243,7 +1240,9 @@ const reducer: Reducer = ( // State transitions case "STEP_NEXT": - return transitionStepNext(draft, action.dataSetMetadata); + return transitionStepNext(draft, { + dataSetMetadata: action.dataSetMetadata, + }); case "STEP_PREVIOUS": return transitionStepPrevious(draft, action.to); @@ -1260,7 +1259,12 @@ const reducer: Reducer = ( case "CHART_CONFIG_ADD": if (draft.state === "CONFIGURING_CHART") { - const metadata = getCachedMetadata(draft, action.value.locale); + const chartConfig = getChartConfig(draft); + const metadata = getCachedMetadata( + draft, + chartConfig.dataSet, + action.value.locale + ); if (metadata) { draft.chartConfigs.push( @@ -1374,10 +1378,13 @@ export const initChartStateFromCube = async ( .toPromise(); if (metadata?.dataCubeByIri && components?.dataCubeByIri) { - return transitionStepNext( - getStateWithCurrentDataSource({ ...EMPTY_STATE, dataSet: datasetIri }), - { ...metadata.dataCubeByIri, ...components.dataCubeByIri } - ); + return transitionStepNext(getStateWithCurrentDataSource(EMPTY_STATE), { + dataSetMetadata: { + ...metadata.dataCubeByIri, + ...components.dataCubeByIri, + }, + dataSet: datasetIri, + }); } console.warn(`Could not fetch cube with iri ${datasetIri}`); @@ -1391,27 +1398,30 @@ export const initChartStateFromLocalStorage = async ( chartId: string ): Promise => { const storedState = window.localStorage.getItem(getLocalStorageKey(chartId)); - if (storedState) { - let parsedState; - try { - const rawParsedState = JSON.parse(storedState); - const migratedState = migrateConfiguratorState(rawParsedState); - parsedState = decodeConfiguratorState(migratedState); - } catch (e) { - console.error("Error while parsing stored state", e); - // Ignore errors since we are returning undefined and removing bad state from localStorage - } - if (parsedState) { - return parsedState; - } + if (!storedState) { + return; + } - console.warn( - "Attempted to restore invalid state. Removing from localStorage.", - parsedState - ); - window.localStorage.removeItem(getLocalStorageKey(chartId)); + let parsedState; + try { + const rawParsedState = JSON.parse(storedState); + const migratedState = migrateConfiguratorState(rawParsedState); + parsedState = decodeConfiguratorState(migratedState); + } catch (e) { + console.error("Error while parsing stored state", e); + // Ignore errors since we are returning undefined and removing bad state from localStorage + } + + if (parsedState) { + return parsedState; } + + console.warn( + "Attempted to restore invalid state. Removing from localStorage.", + parsedState + ); + window.localStorage.removeItem(getLocalStorageKey(chartId)); }; const ConfiguratorStateProviderInternal = ( diff --git a/app/configurator/interactive-filters/interactive-filters-configurator.tsx b/app/configurator/interactive-filters/interactive-filters-configurator.tsx index 9a7a77159..02e17e89e 100644 --- a/app/configurator/interactive-filters/interactive-filters-configurator.tsx +++ b/app/configurator/interactive-filters/interactive-filters-configurator.tsx @@ -31,13 +31,13 @@ export const InteractiveFiltersConfigurator = ({ }: { state: ConfiguratorStateConfiguringChart; }) => { - const { dataSet, dataSource } = state; + const { dataSource } = state; const chartConfig = getChartConfig(state); const { fields } = chartConfig; const locale = useLocale(); const [{ data }] = useComponentsQuery({ variables: { - iri: dataSet, + iri: chartConfig.dataSet, sourceType: dataSource.type, sourceUrl: dataSource.url, locale, diff --git a/app/configurator/table/table-chart-configurator.tsx b/app/configurator/table/table-chart-configurator.tsx index 9679f67a6..5cca10704 100644 --- a/app/configurator/table/table-chart-configurator.tsx +++ b/app/configurator/table/table-chart-configurator.tsx @@ -63,21 +63,17 @@ export const ChartConfiguratorTable = ({ state: ConfiguratorStateConfiguringChart; }) => { const locale = useLocale(); - const [{ data: metadata }] = useDataCubeMetadataQuery({ - variables: { - iri: state.dataSet, - sourceType: state.dataSource.type, - sourceUrl: state.dataSource.url, - locale, - }, - }); + const [, dispatch] = useConfiguratorState(isConfiguring); + const chartConfig = getChartConfig(state); + const variables = { + iri: chartConfig.dataSet, + sourceType: state.dataSource.type, + sourceUrl: state.dataSource.url, + locale, + }; + const [{ data: metadata }] = useDataCubeMetadataQuery({ variables }); const [{ data: components }] = useComponentsWithHierarchiesQuery({ - variables: { - iri: state.dataSet, - sourceType: state.dataSource.type, - sourceUrl: state.dataSource.url, - locale, - }, + variables, }); const metaData = useMemo(() => { @@ -89,9 +85,6 @@ export const ChartConfiguratorTable = ({ : null; }, [metadata?.dataCubeByIri, components?.dataCubeByIri]); - const [, dispatch] = useConfiguratorState(isConfiguring); - const chartConfig = getChartConfig(state); - const [currentDraggableId, setCurrentDraggableId] = useState( null ); diff --git a/app/db/config.ts b/app/db/config.ts index 659619c47..9eb140c03 100644 --- a/app/db/config.ts +++ b/app/db/config.ts @@ -120,8 +120,9 @@ const parseDbConfig = ( ...d, data: { ...migratedData, - dataSet: migrateDataSet(migratedData.dataSet), - chartConfigs: migratedData.chartConfigs.map(ensureFiltersOrder), + chartConfigs: migratedData.chartConfigs + .map(ensureFiltersOrder) + .map((d: any) => ({ ...d, dataSet: migrateDataSet(d.dataSet) })), }, }; }; diff --git a/app/docs/annotations.docs.tsx b/app/docs/annotations.docs.tsx index 326cca3b2..cdc10cc1e 100644 --- a/app/docs/annotations.docs.tsx +++ b/app/docs/annotations.docs.tsx @@ -57,6 +57,7 @@ ${( it: "", }, }, + dataSet: "", chartType: "column", fields, interactiveFiltersConfig: { @@ -221,6 +222,7 @@ ${( it: "", }, }, + dataSet: "", chartType: "column", fields, interactiveFiltersConfig: { @@ -284,6 +286,7 @@ ${( it: "", }, }, + dataSet: "", chartType: "column", fields, interactiveFiltersConfig: { diff --git a/app/docs/columns.docs.tsx b/app/docs/columns.docs.tsx index 3f68e83ed..31cf05aa0 100644 --- a/app/docs/columns.docs.tsx +++ b/app/docs/columns.docs.tsx @@ -32,7 +32,6 @@ ${( description: { en: "", de: "", fr: "", it: "" }, }, dataSource: { type: "sparql", url: "" }, - dataSet: "", chartConfigs: [chartConfig], activeChartKey: "scatterplot", }} @@ -94,6 +93,7 @@ const chartConfig: ColumnConfig = { it: "", }, }, + dataSet: "", filters: {}, fields: columnFields, interactiveFiltersConfig: { diff --git a/app/docs/fixtures.ts b/app/docs/fixtures.ts index bd7e1108d..91772956b 100644 --- a/app/docs/fixtures.ts +++ b/app/docs/fixtures.ts @@ -8,7 +8,6 @@ export const states: ConfiguratorState[] = [ { state: "SELECTING_DATASET", version: CONFIGURATOR_STATE_VERSION, - dataSet: undefined, dataSource: DEFAULT_DATA_SOURCE, chartConfigs: undefined, meta: { @@ -30,7 +29,6 @@ export const states: ConfiguratorState[] = [ { state: "CONFIGURING_CHART", version: CONFIGURATOR_STATE_VERSION, - dataSet: "foo", dataSource: DEFAULT_DATA_SOURCE, chartConfigs: [ { @@ -50,6 +48,7 @@ export const states: ConfiguratorState[] = [ it: "", }, }, + dataSet: "", chartType: "column", fields: { x: { @@ -827,6 +826,7 @@ export const tableConfig: TableConfig = { it: "", }, }, + dataSet: "", chartType: "table", filters: {}, interactiveFiltersConfig: undefined, diff --git a/app/docs/lines.docs.tsx b/app/docs/lines.docs.tsx index 7576e541e..b13b58832 100644 --- a/app/docs/lines.docs.tsx +++ b/app/docs/lines.docs.tsx @@ -34,7 +34,6 @@ ${( description: { en: "", de: "", fr: "", it: "" }, }, dataSource: { type: "sparql", url: "" }, - dataSet: "", chartConfigs: [chartConfig], activeChartKey: "line", }} @@ -144,6 +143,7 @@ const chartConfig: LineConfig = { it: "", }, }, + dataSet: "", chartType: "line", interactiveFiltersConfig, fields, diff --git a/app/docs/scatterplot.docs.tsx b/app/docs/scatterplot.docs.tsx index 5d20eac58..8fb2fa41f 100644 --- a/app/docs/scatterplot.docs.tsx +++ b/app/docs/scatterplot.docs.tsx @@ -37,7 +37,6 @@ ${( description: { en: "", de: "", fr: "", it: "" }, }, dataSource: { type: "sparql", url: "" }, - dataSet: "", chartConfigs: [chartConfig], activeChartKey: "scatterplot", }} @@ -145,6 +144,7 @@ const chartConfig: ScatterPlotConfig = { it: "", }, }, + dataSet: "", chartType: "scatterplot", filters: {}, interactiveFiltersConfig, diff --git a/app/login/components/profile-tables.tsx b/app/login/components/profile-tables.tsx index 1caa295ec..315b43066 100644 --- a/app/login/components/profile-tables.tsx +++ b/app/login/components/profile-tables.tsx @@ -166,16 +166,26 @@ type ProfileVisualizationsRowProps = { const ProfileVisualizationsRow = (props: ProfileVisualizationsRowProps) => { const { userId, config, onRemoveSuccess } = props; - const { dataSet, dataSource } = config.data; + const { dataSource } = config.data; + const dataSets = Array.from( + new Set(config.data.chartConfigs.map((d) => d.dataSet)) + ); + const dataSet = dataSets.length === 1 ? dataSets[0] : null; const locale = useLocale(); - const [{ data, fetching }] = useDataCubeMetadataQuery({ - variables: { - iri: dataSet, - sourceType: dataSource.type, - sourceUrl: dataSource.url, - locale, - }, - }); + const [{ data, fetching }] = useDataCubeMetadataQuery( + dataSet + ? { + variables: { + iri: dataSet, + sourceType: dataSource.type, + sourceUrl: dataSource.url, + locale, + }, + } + : { + pause: true, + } + ); const actions = React.useMemo(() => { const actions: ActionProps[] = [ { @@ -260,11 +270,11 @@ const ProfileVisualizationsRow = (props: ProfileVisualizationsRowProps) => { {fetching ? ( - ) : ( + ) : dataSet ? ( @@ -274,6 +284,13 @@ const ProfileVisualizationsRow = (props: ProfileVisualizationsRowProps) => { + ) : ( + + {t({ + id: "login.profile.my-visualizations.multiple-datasets", + message: "Multiple datasets", + })} + )} diff --git a/app/utils/chart-config/api.ts b/app/utils/chart-config/api.ts index cd15ded75..e39072be9 100644 --- a/app/utils/chart-config/api.ts +++ b/app/utils/chart-config/api.ts @@ -19,7 +19,6 @@ export const createConfig = async (state: ConfiguratorStatePublishing) => { // used by a chart that has been published. key: createChartId(), version: state.version, - dataSet: state.dataSet, dataSource: state.dataSource, meta: state.meta, chartConfigs: state.chartConfigs, @@ -51,7 +50,6 @@ export const updateConfig = async ( data: { key, version: state.version, - dataSet: state.dataSet, dataSource: state.dataSource, meta: state.meta, chartConfigs: state.chartConfigs, diff --git a/app/utils/chart-config/versioning.spec.ts b/app/utils/chart-config/versioning.spec.ts index f26cebba2..2fb16b846 100644 --- a/app/utils/chart-config/versioning.spec.ts +++ b/app/utils/chart-config/versioning.spec.ts @@ -22,6 +22,7 @@ const CONFIGURATOR_STATE = { en: "", }, }, + dataSet: "foo", } as unknown as ConfiguratorStateConfiguringChart; describe("config migrations", () => { diff --git a/app/utils/chart-config/versioning.ts b/app/utils/chart-config/versioning.ts index 64d0257ad..dfb116b19 100644 --- a/app/utils/chart-config/versioning.ts +++ b/app/utils/chart-config/versioning.ts @@ -12,7 +12,7 @@ type Migration = { down: (config: any, migrationProps?: any) => any; }; -export const CHART_CONFIG_VERSION = "2.2.0"; +export const CHART_CONFIG_VERSION = "2.3.0"; const chartConfigMigrations: Migration[] = [ { @@ -287,12 +287,15 @@ const chartConfigMigrations: Migration[] = [ ...(colorScaleType === "discrete" ? { scaleType: colorScaleType, - interpolationType: colorScaleInterpolationType, + interpolationType: + colorScaleInterpolationType === "linear" + ? "quantize" + : colorScaleInterpolationType, nbClass, } : { scaleType: colorScaleType, - interpolationType: colorScaleInterpolationType, + interpolationType: "linear", }), }, }; @@ -750,20 +753,42 @@ const chartConfigMigrations: Migration[] = [ }); }, }, + { + description: `ALL { + + dataSet + }`, + from: "2.2.0", + to: "2.3.0", + up: (config, configuratorState) => { + const newConfig = { ...config, version: "2.3.0" }; + const { dataSet } = configuratorState; + + return produce(newConfig, (draft: any) => { + draft.dataSet = dataSet; + }); + }, + down: (config) => { + const newConfig = { ...config, version: "2.2.0" }; + + return produce(newConfig, (draft: any) => { + delete draft.dataSet; + }); + }, + }, ]; export const migrateChartConfig = makeMigrate(chartConfigMigrations, { defaultToVersion: CHART_CONFIG_VERSION, }); -export const CONFIGURATOR_STATE_VERSION = "2.0.0"; +export const CONFIGURATOR_STATE_VERSION = "3.0.0"; const configuratorStateMigrations: Migration[] = [ { - description: `ALL`, + description: "ALL", from: "1.0.0", to: "2.0.0", - up: (config: any) => { + up: (config) => { const newConfig = { ...config, version: "2.0.0" }; return produce(newConfig, (draft: any) => { @@ -793,6 +818,58 @@ const configuratorStateMigrations: Migration[] = [ }); }, }, + { + description: "ALL", + from: "2.0.0", + to: "3.0.0", + up: (config) => { + const newConfig = { ...config, version: "3.0.0" }; + + return produce(newConfig, (draft: any) => { + const chartConfigs: any[] = []; + + for (const chartConfig of draft.chartConfigs) { + const migratedChartConfig = migrateChartConfig(chartConfig, { + migrationProps: draft, + toVersion: "2.3.0", + }); + chartConfigs.push(migratedChartConfig); + } + + delete draft.dataSet; + draft.chartConfigs = chartConfigs; + }); + }, + down: (config) => { + const newConfig = { ...config, version: "2.0.0" }; + + return produce(newConfig, (draft: any) => { + let dataSet: string | undefined; + const chartConfigs: any[] = []; + + for (const chartConfig of draft.chartConfigs) { + if (!dataSet) { + dataSet = chartConfig.dataSet; + } + + // Only migrate chartConfigs with the same dataSet as configuratorState. + if (chartConfig.dataSet === dataSet) { + const migratedChartConfig = migrateChartConfig(chartConfig, { + migrationProps: draft, + toVersion: "2.2.0", + }); + chartConfigs.push(migratedChartConfig); + } else { + console.warn( + "Cannot migrate chartConfig dataSet to configuratorState dataSet because they are not the same." + ); + } + } + + draft.dataSet = dataSet; + }); + }, + }, ]; export const migrateConfiguratorState = makeMigrate(