From f6ce7e4f2eba9aeddc29033c2142ead5c0e291b6 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Fri, 24 May 2024 13:35:28 +0200 Subject: [PATCH 01/31] fix: Bad SVG props triggered warning in console --- app/icons/components/IcInfoOutline.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/icons/components/IcInfoOutline.tsx b/app/icons/components/IcInfoOutline.tsx index ff4901058..9259eb968 100644 --- a/app/icons/components/IcInfoOutline.tsx +++ b/app/icons/components/IcInfoOutline.tsx @@ -9,14 +9,14 @@ function SvgIcInfoOutline(props: React.SVGProps) { {...props} > From aeed5c2edda54374eb88bb8b1b56922353804349 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Wed, 22 May 2024 16:37:35 +0200 Subject: [PATCH 02/31] refactor: Clarify name --- app/configurator/components/chart-type-selector.tsx | 8 ++++---- app/configurator/config-form.tsx | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/configurator/components/chart-type-selector.tsx b/app/configurator/components/chart-type-selector.tsx index 9d46f29ea..5d6aaaa91 100644 --- a/app/configurator/components/chart-type-selector.tsx +++ b/app/configurator/components/chart-type-selector.tsx @@ -16,7 +16,7 @@ import { } from "@/config-types"; import { ControlSectionSkeleton } from "@/configurator/components/chart-controls/section"; import { IconButton } from "@/configurator/components/icon-button"; -import { useChartType } from "@/configurator/config-form"; +import { useAddOrEditChartType } from "@/configurator/config-form"; import { ConfiguratorStateWithChartConfigs } from "@/configurator/configurator-state"; import { useDataCubesComponentsQuery } from "@/graphql/hooks"; import { useLocale } from "@/locales/use-locale"; @@ -48,7 +48,7 @@ export const ChartTypeSelector = (props: ChartTypeSelectorProps) => { }); const dimensions = data?.dataCubesComponents?.dimensions ?? []; const measures = data?.dataCubesComponents?.measures ?? []; - const { value: chartType, onChange: onChangeChartType } = useChartType( + const { value: chartType, addOrEditChartType } = useAddOrEditChartType( chartKey, type, dimensions, @@ -63,10 +63,10 @@ export const ChartTypeSelector = (props: ChartTypeSelectorProps) => { // only in edit mode; we should be able to add any possible chart type // in add mode. if (type === "edit" ? newChartType !== chartType : true) { - onChangeChartType(newChartType); + addOrEditChartType(newChartType); } }, - [chartType, onChangeChartType, type] + [chartType, addOrEditChartType, type] ); if (!data?.dataCubesComponents) { diff --git a/app/configurator/config-form.tsx b/app/configurator/config-form.tsx index df7d1e99a..6dffcf9a6 100644 --- a/app/configurator/config-form.tsx +++ b/app/configurator/config-form.tsx @@ -3,10 +3,10 @@ import get from "lodash/get"; import React, { ChangeEvent, InputHTMLAttributes, + createContext, useCallback, useContext, useMemo, - createContext, } from "react"; import { getFieldComponentIri, getInitialConfig } from "@/charts"; @@ -398,19 +398,19 @@ export const getNewChartConfig = ({ }); }; -export const useChartType = ( +export const useAddOrEditChartType = ( chartKey: string, type: "add" | "edit" = "edit", dimensions: Dimension[], measures: Measure[] ): { value: ChartType; - onChange: (chartType: ChartType) => void; + addOrEditChartType: (chartType: ChartType) => void; } => { const locale = useLocale(); const [state, dispatch] = useConfiguratorState(); const chartConfig = getChartConfig(state, chartKey); - const onChange = useEvent((chartType: ChartType) => { + const addOrEditChartType = useEvent((chartType: ChartType) => { if (type === "edit") { dispatch({ type: "CHART_TYPE_CHANGED", @@ -440,7 +440,7 @@ export const useChartType = ( const value = get(chartConfig, "chartType"); return { - onChange, + addOrEditChartType, value, }; }; From a54bac0b11cdecb56c4da4967da0d0ef7c7a1353 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Wed, 8 May 2024 21:19:56 +0200 Subject: [PATCH 03/31] feat: Show a button in tabs to open select dataset step in drawer (WIP) --- app/components/chart-selection-tabs.tsx | 16 +++++--- .../components/FromNewDatasetUI.tsx | 39 +++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 app/configurator/components/FromNewDatasetUI.tsx diff --git a/app/components/chart-selection-tabs.tsx b/app/components/chart-selection-tabs.tsx index 67cfc52e0..ba87adbca 100644 --- a/app/components/chart-selection-tabs.tsx +++ b/app/components/chart-selection-tabs.tsx @@ -39,6 +39,7 @@ import { useConfiguratorState, } from "@/configurator"; import { ChartTypeSelector } from "@/configurator/components/chart-type-selector"; +import FromNewDatasetUI from "@/configurator/components/FromNewDatasetUI"; import { getIconName } from "@/configurator/components/ui-helpers"; import { useUserConfig } from "@/domain/user-configs"; import { useDataCubesComponentsQuery } from "@/graphql/hooks"; @@ -183,6 +184,7 @@ const TabsEditable = (props: TabsEditableProps) => { }} /> + { onClose={handleClose} > {tabsState.popoverType === "add" ? ( - + <> + + ) : ( + + + Choose a new dataset + + close()} /> + + + + + + + + + ); +}; + +export default FromNewDatasetUI; From cbc2543504c9bbf39d16cdb1d91193c5e1f0df7a Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Wed, 22 May 2024 16:53:21 +0200 Subject: [PATCH 04/31] feat: Use a zustand store to share if add new dataset panel is opened --- app/components/chart-selection-tabs.tsx | 17 +++++++- .../components/FromNewDatasetUI.tsx | 39 ----------------- .../components/add-new-dataset-panel.tsx | 42 +++++++++++++++++++ app/pages/create/[chartId].tsx | 4 +- 4 files changed, 60 insertions(+), 42 deletions(-) delete mode 100644 app/configurator/components/FromNewDatasetUI.tsx create mode 100644 app/configurator/components/add-new-dataset-panel.tsx diff --git a/app/components/chart-selection-tabs.tsx b/app/components/chart-selection-tabs.tsx index ba87adbca..6182dd901 100644 --- a/app/components/chart-selection-tabs.tsx +++ b/app/components/chart-selection-tabs.tsx @@ -7,6 +7,7 @@ import { Popover, Theme, Tooltip, + Typography, useEventCallback, } from "@mui/material"; import { makeStyles } from "@mui/styles"; @@ -38,8 +39,8 @@ import { saveChartLocally, useConfiguratorState, } from "@/configurator"; +import { useSearchDatasetPanelStore } from "@/configurator/components/add-new-dataset-panel"; import { ChartTypeSelector } from "@/configurator/components/chart-type-selector"; -import FromNewDatasetUI from "@/configurator/components/FromNewDatasetUI"; import { getIconName } from "@/configurator/components/ui-helpers"; import { useUserConfig } from "@/domain/user-configs"; import { useDataCubesComponentsQuery } from "@/graphql/hooks"; @@ -153,6 +154,8 @@ const TabsEditable = (props: TabsEditableProps) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [state.activeChartKey]); + const { open: openAddDatasetPanel } = useSearchDatasetPanelStore(); + return ( <> { }} /> - { chartKey={tabsState.activeChartKey ?? chartConfig.key} sx={{ width: 320, px: 3, pb: 3 }} /> + + From new dataset + + ) : ( diff --git a/app/configurator/components/FromNewDatasetUI.tsx b/app/configurator/components/FromNewDatasetUI.tsx deleted file mode 100644 index 68aeaa433..000000000 --- a/app/configurator/components/FromNewDatasetUI.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Box, Button, Drawer, Typography } from "@mui/material"; - -import { SelectDatasetStep } from "@/browser/select-dataset-step"; -import { useTabsState } from "@/components/chart-selection-tabs"; -import { DialogCloseButton } from "@/components/dialog-close-button"; -import useDisclosure from "@/components/use-disclosure"; -import { ConfiguratorStateProvider } from "@/configurator/configurator-state"; - -const FromNewDatasetUI = () => { - const { open, close, isOpen } = useDisclosure(); - const [tabsState, setTabsState] = useTabsState(); - return ( - <> - - - - Choose a new dataset - - close()} /> - - - - - - - - - ); -}; - -export default FromNewDatasetUI; diff --git a/app/configurator/components/add-new-dataset-panel.tsx b/app/configurator/components/add-new-dataset-panel.tsx new file mode 100644 index 000000000..49bec88de --- /dev/null +++ b/app/configurator/components/add-new-dataset-panel.tsx @@ -0,0 +1,42 @@ +import { Box, Drawer, Typography } from "@mui/material"; +import createStore from "zustand"; + +import { SelectDatasetStep } from "@/browser/select-dataset-step"; +import { DialogCloseButton } from "@/components/dialog-close-button"; +import { ConfiguratorStateProvider } from "@/configurator/configurator-state"; + +export const useSearchDatasetPanelStore = createStore<{ + isOpen: boolean; + toggle: () => void; + close: () => void; + open: () => void; +}>((set) => ({ + isOpen: false, + toggle: () => { + set((s) => ({ isOpen: !s.isOpen })); + }, + close: () => { + set({ isOpen: false }); + }, + open: () => { + set({ isOpen: true }); + }, +})); + +export const AddNewDatasetPanel = () => { + const { isOpen, close } = useSearchDatasetPanelStore(); + return ( + + + Choose a new dataset + + close()} /> + + + + + + + + ); +}; diff --git a/app/pages/create/[chartId].tsx b/app/pages/create/[chartId].tsx index 9e9f0ad7b..7891dbb2f 100644 --- a/app/pages/create/[chartId].tsx +++ b/app/pages/create/[chartId].tsx @@ -1,6 +1,6 @@ import { GetServerSideProps, NextPage } from "next"; import Head from "next/head"; -import React, { useMemo } from "react"; +import { useMemo } from "react"; import { AppLayout } from "@/components/layout"; import { @@ -8,6 +8,7 @@ import { MetadataPanelStoreContext, } from "@/components/metadata-panel-store"; import { Configurator, ConfiguratorStateProvider } from "@/configurator"; +import { AddNewDatasetPanel } from "@/configurator/components/add-new-dataset-panel"; type PageProps = { locale: string; @@ -41,6 +42,7 @@ const ChartConfiguratorPage: NextPage = ({ chartId }) => { + From 947fcbf6fcb9d2c2f8e6129ce76a773b405192b1 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Thu, 23 May 2024 09:49:25 +0200 Subject: [PATCH 05/31] feat: Ability to adapt the select dataset step for usage in the drawer --- app/browser/dataset-browse.tsx | 95 ++++++++++------ app/browser/dataset-preview.tsx | 14 ++- app/browser/select-dataset-step.tsx | 103 ++++++++++++++---- app/components/form.tsx | 8 +- .../components/add-new-dataset-panel.tsx | 36 ++++-- 5 files changed, 188 insertions(+), 68 deletions(-) diff --git a/app/browser/dataset-browse.tsx b/app/browser/dataset-browse.tsx index 6768ecff6..a18a8d598 100644 --- a/app/browser/dataset-browse.tsx +++ b/app/browser/dataset-browse.tsx @@ -11,6 +11,7 @@ import { Typography, } from "@mui/material"; import { makeStyles } from "@mui/styles"; +import clsx from "clsx"; import { AnimatePresence, Reorder } from "framer-motion"; import keyBy from "lodash/keyBy"; import orderBy from "lodash/orderBy"; @@ -23,7 +24,12 @@ import { stringify } from "qs"; import React, { ComponentProps, useMemo, useState } from "react"; import Flex, { FlexProps } from "@/components/flex"; -import { Checkbox, MinimalisticSelect, SearchField } from "@/components/form"; +import { + Checkbox, + MinimalisticSelect, + SearchField, + SearchFieldProps, +} from "@/components/form"; import { Loading, LoadingDataError } from "@/components/hint"; import { accordionPresenceProps, @@ -59,11 +65,51 @@ import { } from "./context"; import { BrowseFilter } from "./filters"; +const useStyles = makeStyles(() => ({ + navChip: { + minWidth: 32, + height: 24, + justifyContent: "center", + alignItems: "center", + borderRadius: 2, + }, + removeFilterButton: { + minWidth: 16, + minHeight: 16, + height: "auto", + alignItems: "center", + display: "flex", + width: "auto", + padding: 0, + borderRadius: 2, + marginRight: 2, + "&:hover": { + background: "rgba(0, 0, 0, 0.25)", + }, + }, + navItem: { + justifyContent: "space-between", + alignItems: "center", + gap: 6, + borderRadius: 2, + width: "100%", + display: "flex", + transition: "background 0.1s ease", + }, + searchInput: { + width: "100%", + maxWidth: 350, + }, +})); + export const SearchDatasetInput = ({ browseState, + searchFieldProps, }: { browseState: BrowseState; + searchFieldProps?: Partial; }) => { + const classes = useStyles(); const [_, setShowDraftCheckbox] = useState(false); const { inputRef, search, onSubmitSearch, onReset } = browseState; @@ -106,7 +152,8 @@ export const SearchDatasetInput = ({ onFocus: () => setShowDraftCheckbox(true), }} placeholder={placeholderLabel} - sx={{ width: "100%", maxWidth: 350 }} + {...searchFieldProps} + className={clsx(classes.searchInput, searchFieldProps?.className)} /> - + ) : ( + + + + )} {dataCubeMetadata.description && ( diff --git a/app/configurator/components/add-new-dataset-panel.tsx b/app/configurator/components/add-new-dataset-panel.tsx index d56a000e4..1ee7af164 100644 --- a/app/configurator/components/add-new-dataset-panel.tsx +++ b/app/configurator/components/add-new-dataset-panel.tsx @@ -1,10 +1,18 @@ import { Box, Drawer } from "@mui/material"; import { useState } from "react"; +import { useClient } from "urql"; import createStore from "zustand"; import { SelectDatasetStep } from "@/browser/select-dataset-step"; import { DialogCloseButton } from "@/components/dialog-close-button"; -import { ConfiguratorStateProvider } from "@/configurator/configurator-state"; +import { + ConfiguratorStateProvider, + initChartStateFromCube, + useConfiguratorState, +} from "@/configurator/configurator-state"; +import { useLocale } from "@/locales/use-locale"; +import { useDataSourceStore } from "@/stores/data-source"; +import { assert } from "@/utils/assert"; export const useSearchDatasetPanelStore = createStore<{ isOpen: boolean; @@ -27,6 +35,35 @@ export const useSearchDatasetPanelStore = createStore<{ export const AddNewDatasetPanel = () => { const { isOpen, close } = useSearchDatasetPanelStore(); const [dataSetIri, setDataSetIri] = useState(""); + const [state, dispatch] = useConfiguratorState(); + const locale = useLocale(); + const client = useClient(); + const { dataSource } = useDataSourceStore(); + + const handleAddNewDataset = async (datasetIri: string) => { + const chartState = await initChartStateFromCube( + client, + datasetIri, + dataSource, + locale + ); + + if (!chartState) { + return; + } + + assert( + chartState?.state === "CONFIGURING_CHART", + "After init, chart state should be in CONFIGURING_CHART state" + ); + dispatch({ + type: "CHART_CONFIG_ADD", + value: { + chartConfig: chartState.chartConfigs[0], + locale, + }, + }); + }; return ( { dataSetIri, onClickCreate: (ev, datasetIri) => { ev.preventDefault(); - alert("Create new dataset" + datasetIri); + handleAddNewDataset(datasetIri); }, }} /> From cd1e494f329f87a3bdd30bde97a9d1a0662ee780 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Fri, 24 May 2024 13:35:12 +0200 Subject: [PATCH 09/31] feat: Create a new specific action to add a new chart config from scratch --- .../components/add-new-dataset-panel.tsx | 32 +++++++++++++------ app/configurator/configurator-state.tsx | 23 +++++++++++-- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/app/configurator/components/add-new-dataset-panel.tsx b/app/configurator/components/add-new-dataset-panel.tsx index 1ee7af164..69dd208a3 100644 --- a/app/configurator/components/add-new-dataset-panel.tsx +++ b/app/configurator/components/add-new-dataset-panel.tsx @@ -1,4 +1,4 @@ -import { Box, Drawer } from "@mui/material"; +import { Box, Drawer, useEventCallback } from "@mui/material"; import { useState } from "react"; import { useClient } from "urql"; import createStore from "zustand"; @@ -32,15 +32,15 @@ export const useSearchDatasetPanelStore = createStore<{ }, })); -export const AddNewDatasetPanel = () => { - const { isOpen, close } = useSearchDatasetPanelStore(); - const [dataSetIri, setDataSetIri] = useState(""); - const [state, dispatch] = useConfiguratorState(); +/** + * Inits a new chart state based on a new dataset and adds it to the configurator state + */ +export const useAddChartConfigBasedOnNewDataset = () => { + const [, dispatch] = useConfiguratorState(); const locale = useLocale(); const client = useClient(); const { dataSource } = useDataSourceStore(); - - const handleAddNewDataset = async (datasetIri: string) => { + const handleAddNewDataset = useEventCallback(async (datasetIri: string) => { const chartState = await initChartStateFromCube( client, datasetIri, @@ -56,14 +56,28 @@ export const AddNewDatasetPanel = () => { chartState?.state === "CONFIGURING_CHART", "After init, chart state should be in CONFIGURING_CHART state" ); + dispatch({ - type: "CHART_CONFIG_ADD", + type: "CHART_CONFIG_ADD_NEW_DATASET", value: { chartConfig: chartState.chartConfigs[0], locale, }, }); - }; + }); + return handleAddNewDataset; +}; + +/** + * Used when adding a new chart based on a new dataset + * Shows the dataset search, but overrides its default behavior to + * keep the state as a local state instead of inside the URL + */ +export const AddNewDatasetPanel = () => { + const { isOpen, close } = useSearchDatasetPanelStore(); + const [dataSetIri, setDataSetIri] = useState(""); + const handleAddNewDataset = useAddChartConfigBasedOnNewDataset(); + return ( = ( case "CHART_CONFIG_ADD": if (isConfiguring(draft)) { - const chartConfig = getChartConfig(draft); + const chartConfig = + createDraft(action.value.chartConfig) ?? getChartConfig(draft); const dataCubesComponents = getCachedComponents( draft.dataSource, chartConfig.cubes.map((cube) => ({ @@ -1415,11 +1423,12 @@ const reducer_: Reducer = ( ); if (dataCubesComponents) { + const cubes = current(chartConfig.cubes); const newConfig = deriveFiltersFromFields( { ...action.value.chartConfig, // Need to finalize the cubes draft to avoid revoked proxy errors - cubes: current(chartConfig.cubes), + cubes, }, { dimensions: dataCubesComponents.dimensions } ); @@ -1430,6 +1439,13 @@ const reducer_: Reducer = ( return draft; + case "CHART_CONFIG_ADD_NEW_DATASET": + if (isConfiguring(draft)) { + draft.chartConfigs.push(action.value.chartConfig); + draft.activeChartKey = action.value.chartConfig.key; + } + return draft; + case "DATASET_ADD": if (isConfiguring(draft)) { addDatasetInConfig(draft, action.value); @@ -1639,6 +1655,7 @@ export const initChartStateFromCube = async ( client, dataSource, }); + const { data: components } = await executeDataCubesComponentsQuery(client, { sourceType: dataSource.type, sourceUrl: dataSource.url, From fb023ebfc0acb1fda3855fb7bc5e3aba651b4035 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Fri, 24 May 2024 13:39:19 +0200 Subject: [PATCH 10/31] fix: Dataset section show only the cubes for the current chart config --- app/configurator/components/dataset-control-section.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/configurator/components/dataset-control-section.tsx b/app/configurator/components/dataset-control-section.tsx index 6d2f5069a..85a9c6023 100644 --- a/app/configurator/components/dataset-control-section.tsx +++ b/app/configurator/components/dataset-control-section.tsx @@ -162,12 +162,12 @@ export const DatasetsControlSection = () => { }; const { setOpen, setActiveSection } = useMetadataPanelStoreActions(); const cubes = useMemo(() => { - const cubes = uniqBy( - state.chartConfigs.flatMap((x) => x.cubes), - (x) => x.iri + const activeChartConfig = state.chartConfigs.find( + (x) => x.key === state.activeChartKey ); + const cubes = uniqBy(activeChartConfig?.cubes ?? [], (x) => x.iri); return cubes; - }, [state.chartConfigs]); + }, [state.activeChartKey, state.chartConfigs]); const [metadataQuery] = useDataCubesMetadataQuery({ variables: { ...commonQueryVariables, From a47b84067dd737b922604a3097cab38bc7f03a3f Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Fri, 24 May 2024 13:59:47 +0200 Subject: [PATCH 11/31] refactor: Allow creation of useBrowseParams passing the underlying hook --- app/browser/context.tsx | 194 +++++++++++++++------------- app/browser/select-dataset-step.tsx | 2 +- 2 files changed, 104 insertions(+), 92 deletions(-) diff --git a/app/browser/context.tsx b/app/browser/context.tsx index 51e7fcce7..df32cfd2b 100644 --- a/app/browser/context.tsx +++ b/app/browser/context.tsx @@ -3,15 +3,16 @@ import { ParsedUrlQuery } from "querystring"; import mapValues from "lodash/mapValues"; import pick from "lodash/pick"; import pickBy from "lodash/pickBy"; +import { Url } from "next/dist/shared/lib/router/router"; import Link from "next/link"; import { Router, useRouter } from "next/router"; import React, { + createContext, useContext, useEffect, useMemo, useRef, useState, - createContext, } from "react"; import { SearchCubeResultOrder } from "@/graphql/query-hooks"; @@ -20,7 +21,9 @@ import useEvent from "@/utils/use-event"; import { getFiltersFromParams } from "./filters"; -export const getBrowseParamsFromQuery = (query: Router["query"]) => { +export const getBrowseParamsFromQuery = ( + query: Router["query"] +): BrowseParams => { const rawValues = mapValues( pick(query, [ "type", @@ -75,16 +78,18 @@ export const buildURLFromBrowseState = (browseState: BrowseParams) => { }; const extractParamFromPath = (path: string, param: string) => path.match(new RegExp(`[&?]${param}=(.*?)(&|$)`)); -const useQueryParamsState = ( - initialState: T, - { - serialize, - parse, - }: { - serialize: (s: T) => Parameters[0]; - parse: (s: ParsedUrlQuery) => T; - } -) => { + +type BrowseParamsCodec = { + parse: (query: ParsedUrlQuery) => BrowseParams; + serialize: (browseState: BrowseParams) => Url; +}; + +const urlCodec: BrowseParamsCodec = { + parse: getBrowseParamsFromQuery, + serialize: buildURLFromBrowseState, +}; + +const useQueryParamsState = (initialState: T) => { const router = useRouter(); const [state, rawSetState] = useState(() => { @@ -101,19 +106,19 @@ const useQueryParamsState = ( query.dataset = dataset[0]; } - return query ? parse(query) : initialState; + return query ? urlCodec.parse(query) : initialState; }); useEffect(() => { if (router.isReady) { - rawSetState(parse(router.query)); + rawSetState(urlCodec.parse(router.query)); } - }, [parse, router.isReady, router.query]); + }, [urlCodec.parse, router.isReady, router.query]); const setState = useEvent((stateUpdate: T) => { rawSetState((curState) => { const newState = { ...curState, ...stateUpdate } as T; - router.replace(serialize(newState), undefined, { + router.replace(urlCodec.serialize(newState), undefined, { shallow: true, }); return newState; @@ -122,92 +127,99 @@ const useQueryParamsState = ( return [state, setState] as const; }; -export const useBrowseState = () => { - const inputRef = useRef(null); - const [browseParams, setParams] = useQueryParamsState( - {}, - { - parse: getBrowseParamsFromQuery, - serialize: buildURLFromBrowseState, - } - ); - const { - search, - type, - order, - iri, - includeDrafts, - dataset: paramDataset, - } = browseParams; - - // Support /browse?dataset= and legacy /browse/dataset/ - const dataset = type === "dataset" ? iri : paramDataset; - const filters = getFiltersFromParams(browseParams); - - const setSearch = useEvent((v: string) => setParams({ search: v })); - const setIncludeDrafts = useEvent((v: boolean) => - setParams({ includeDrafts: v }) - ); - const setOrder = useEvent((v: SearchCubeResultOrder) => - setParams({ order: v }) - ); - const setDataset = useEvent((v: string) => setParams({ dataset: v })); - - const previousOrderRef = useRef( - SearchCubeResultOrder.Score - ); +export const noopCodec: BrowseParamsCodec = { + parse: () => ({}), + serialize: () => "", +}; - return useMemo( - () => ({ - inputRef, - includeDrafts: !!includeDrafts, - setIncludeDrafts, - onReset: () => { - setParams({ search: "", order: SearchCubeResultOrder.CreatedDesc }); - }, - onSubmitSearch: (newSearch: string) => { - setParams({ - search: newSearch, - order: - newSearch === "" - ? SearchCubeResultOrder.CreatedDesc - : previousOrderRef.current, - }); - }, +const makeUseBrowseState = + ( + useParamsHook: ( + initialState: BrowseParams + ) => readonly [BrowseParams, (state: BrowseParams) => void] + ) => + () => { + const inputRef = useRef(null); + const [browseParams, setParams] = useParamsHook({}); + const { search, + type, order, - onSetOrder: (order: SearchCubeResultOrder) => { - previousOrderRef.current = order; - setOrder(order); - }, - setSearch, - setOrder, - dataset, - setDataset, - filters, - }), - [ + iri, includeDrafts, - setIncludeDrafts, - search, - order, - setSearch, - setOrder, - dataset, - setDataset, - filters, - setParams, - ] - ); -}; + dataset: paramDataset, + } = browseParams; + + // Support /browse?dataset= and legacy /browse/dataset/ + const dataset = type === "dataset" ? iri : paramDataset; + const filters = getFiltersFromParams(browseParams); + + const setSearch = useEvent((v: string) => setParams({ search: v })); + const setIncludeDrafts = useEvent((v: boolean) => + setParams({ includeDrafts: v }) + ); + const setOrder = useEvent((v: SearchCubeResultOrder) => + setParams({ order: v }) + ); + const setDataset = useEvent((v: string) => setParams({ dataset: v })); + + const previousOrderRef = useRef( + SearchCubeResultOrder.Score + ); + + return useMemo( + () => ({ + inputRef, + includeDrafts: !!includeDrafts, + setIncludeDrafts, + onReset: () => { + setParams({ search: "", order: SearchCubeResultOrder.CreatedDesc }); + }, + onSubmitSearch: (newSearch: string) => { + setParams({ + search: newSearch, + order: + newSearch === "" + ? SearchCubeResultOrder.CreatedDesc + : previousOrderRef.current, + }); + }, + search, + order, + onSetOrder: (order: SearchCubeResultOrder) => { + previousOrderRef.current = order; + setOrder(order); + }, + setSearch, + setOrder, + dataset, + setDataset, + filters, + }), + [ + includeDrafts, + setIncludeDrafts, + search, + order, + setSearch, + setOrder, + dataset, + setDataset, + filters, + setParams, + ] + ); + }; + +export const useBrowseState = makeUseBrowseState(useQueryParamsState); export type BrowseState = ReturnType; const BrowseContext = createContext(undefined); + /** * Provides browse context to children below * Responsible for connecting the router to the browsing state */ - export const BrowseStateProvider = ({ children, }: { diff --git a/app/browser/select-dataset-step.tsx b/app/browser/select-dataset-step.tsx index 8d794bc1d..c92922584 100644 --- a/app/browser/select-dataset-step.tsx +++ b/app/browser/select-dataset-step.tsx @@ -23,11 +23,11 @@ import { DatasetMetadata } from "@/components/dataset-metadata"; import Flex from "@/components/flex"; import { Footer } from "@/components/footer"; import { + bannerPresenceProps, BANNER_HEIGHT, BANNER_MARGIN_TOP, DURATION, MotionBox, - bannerPresenceProps, navPresenceProps, smoothPresenceProps, } from "@/components/presence"; From f9dd22b974a56a69978eff985282f2a695cf3584 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Fri, 24 May 2024 14:03:11 +0200 Subject: [PATCH 12/31] refactor: Rename --- app/browser/context.tsx | 8 ++++---- app/browser/select-dataset-step.tsx | 6 +++--- app/docs/form.stories.tsx | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/browser/context.tsx b/app/browser/context.tsx index df32cfd2b..691efd0fe 100644 --- a/app/browser/context.tsx +++ b/app/browser/context.tsx @@ -211,21 +211,21 @@ const makeUseBrowseState = ); }; -export const useBrowseState = makeUseBrowseState(useQueryParamsState); +export const useBrowseStateSavedToURL = makeUseBrowseState(useQueryParamsState); -export type BrowseState = ReturnType; +export type BrowseState = ReturnType; const BrowseContext = createContext(undefined); /** * Provides browse context to children below * Responsible for connecting the router to the browsing state */ -export const BrowseStateProvider = ({ +export const BrowseStateURLSyncedProvider = ({ children, }: { children: React.ReactNode; }) => { - const browseState = useBrowseState(); + const browseState = useBrowseStateSavedToURL(); return ( {children} diff --git a/app/browser/select-dataset-step.tsx b/app/browser/select-dataset-step.tsx index c92922584..9c8c099f4 100644 --- a/app/browser/select-dataset-step.tsx +++ b/app/browser/select-dataset-step.tsx @@ -49,7 +49,7 @@ import { Icon } from "@/icons"; import { useConfiguratorState, useLocale } from "@/src"; import { - BrowseStateProvider, + BrowseStateURLSyncedProvider, buildURLFromBrowseState, useBrowseContext, } from "./context"; @@ -535,8 +535,8 @@ export const SelectDatasetStep = ( props: React.ComponentProps ) => { return ( - + - + ); }; diff --git a/app/docs/form.stories.tsx b/app/docs/form.stories.tsx index ea70ba1a3..6432b346e 100644 --- a/app/docs/form.stories.tsx +++ b/app/docs/form.stories.tsx @@ -3,7 +3,7 @@ import { DatePicker, PickersDay } from "@mui/lab"; import { Stack, TextField } from "@mui/material"; import { useState } from "react"; -import { BrowseStateProvider } from "@/browser/context"; +import { BrowseStateURLSyncedProvider } from "@/browser/context"; import { Checkbox, Input, @@ -162,13 +162,13 @@ const SearchFieldStory = { return (
- + - +
- + alert("reset search"), }} /> - +
); From 08020f4dd8e93a7b5d8e89c6115a859caad85b53 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Fri, 24 May 2024 14:11:19 +0200 Subject: [PATCH 13/31] feat: Make a variant of SelectStep which uses a state that does not sync to URL --- app/browser/context.tsx | 19 +++++++++++++++++++ app/browser/select-dataset-step.tsx | 11 +++++++++++ .../components/add-new-dataset-panel.tsx | 4 ++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/app/browser/context.tsx b/app/browser/context.tsx index 691efd0fe..423d33f00 100644 --- a/app/browser/context.tsx +++ b/app/browser/context.tsx @@ -211,6 +211,8 @@ const makeUseBrowseState = ); }; +export const useBrowseState = makeUseBrowseState(useState); + export const useBrowseStateSavedToURL = makeUseBrowseState(useQueryParamsState); export type BrowseState = ReturnType; @@ -233,6 +235,23 @@ export const BrowseStateURLSyncedProvider = ({ ); }; +/** + * Provides browse context to children below + * Responsible for connecting the router to the browsing state + */ +export const BrowseStateProvider = ({ + children, +}: { + children: React.ReactNode; +}) => { + const browseState = useBrowseState(); + return ( + + {children} + + ); +}; + export const useBrowseContext = () => { const ctx = useContext(BrowseContext); if (!ctx) { diff --git a/app/browser/select-dataset-step.tsx b/app/browser/select-dataset-step.tsx index 9c8c099f4..5890f05dd 100644 --- a/app/browser/select-dataset-step.tsx +++ b/app/browser/select-dataset-step.tsx @@ -49,6 +49,7 @@ import { Icon } from "@/icons"; import { useConfiguratorState, useLocale } from "@/src"; import { + BrowseStateProvider, BrowseStateURLSyncedProvider, buildURLFromBrowseState, useBrowseContext, @@ -540,3 +541,13 @@ export const SelectDatasetStep = ( ); }; + +export const SelectDatasetStepDrawer = ( + props: React.ComponentProps +) => { + return ( + + + + ); +}; diff --git a/app/configurator/components/add-new-dataset-panel.tsx b/app/configurator/components/add-new-dataset-panel.tsx index 69dd208a3..4b26d4cc4 100644 --- a/app/configurator/components/add-new-dataset-panel.tsx +++ b/app/configurator/components/add-new-dataset-panel.tsx @@ -3,7 +3,7 @@ import { useState } from "react"; import { useClient } from "urql"; import createStore from "zustand"; -import { SelectDatasetStep } from "@/browser/select-dataset-step"; +import { SelectDatasetStepDrawer } from "@/browser/select-dataset-step"; import { DialogCloseButton } from "@/components/dialog-close-button"; import { ConfiguratorStateProvider, @@ -95,7 +95,7 @@ export const AddNewDatasetPanel = () => { - { From 0b11fb59042a9e947b0d529da4faf74867b74d7b Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Fri, 24 May 2024 14:44:18 +0200 Subject: [PATCH 14/31] feat: Adapt design for "Add chart" menu --- app/components/chart-selection-tabs.tsx | 64 ++++++++++++++++++------- app/locales/de/messages.po | 17 +++++++ app/locales/en/messages.po | 17 +++++++ app/locales/fr/messages.po | 17 +++++++ app/locales/it/messages.po | 17 +++++++ 5 files changed, 115 insertions(+), 17 deletions(-) diff --git a/app/components/chart-selection-tabs.tsx b/app/components/chart-selection-tabs.tsx index 6182dd901..5ab2cef2f 100644 --- a/app/components/chart-selection-tabs.tsx +++ b/app/components/chart-selection-tabs.tsx @@ -3,8 +3,10 @@ import { TabContext } from "@mui/lab"; import { Box, Button, + Divider, Grow, Popover, + Stack, Theme, Tooltip, Typography, @@ -199,23 +201,51 @@ const TabsEditable = (props: TabsEditableProps) => { > {tabsState.popoverType === "add" ? ( <> - - - From new dataset - - + } gap="0.5rem" direction="column"> + + + + Add chart based on the same dataset + + + + + + + + Add chart based on a different dataset + + + + + ) : ( diff --git a/app/locales/de/messages.po b/app/locales/de/messages.po index 0f9524c03..b33914b71 100644 --- a/app/locales/de/messages.po +++ b/app/locales/de/messages.po @@ -56,6 +56,7 @@ msgstr "Kategorien" msgid "browse.dataset.all" msgstr "Alle Datensätze durchsuchen" +#: app/browser/dataset-preview.tsx #: app/browser/dataset-preview.tsx msgid "browse.dataset.create-visualization" msgstr "Visualisierung von diesem Datensatz erstellen" @@ -150,6 +151,18 @@ msgstr "Diese Visualisierung aktualisieren" msgid "button.update.warning" msgstr "Denken Sie daran, dass sich die Aktualisierung dieser Visualisierung auf alle Stellen auswirkt, an denen sie bereits eingebettet ist!" +#: app/components/chart-selection-tabs.tsx +msgid "chart-selection-tabs.add-chart-different-dataset.button" +msgstr "Wählen Sie einen anderen Datensatz" + +#: app/components/chart-selection-tabs.tsx +msgid "chart-selection-tabs.add-chart-different-dataset.caption" +msgstr "Fügen Sie ein Diagramm basierend auf einem anderen Datensatz hinzu" + +#: app/components/chart-selection-tabs.tsx +msgid "chart-selection-tabs.add-chart-same-dataset.caption" +msgstr "Fügen Sie ein Diagramm basierend auf demselben Datensatz hinzu" + #: app/configurator/components/dataset-control-section.tsx msgid "chart.datasets.add" msgstr "Hinzufügen" @@ -162,6 +175,10 @@ msgstr "Sie können nur Datensätze kombinieren, die Dimensionen mit derselben E msgid "chart.datasets.add-dataset-dialog.title" msgstr "Wählen Sie einen Datensatz mit gemeinsamen Dimensionen aus" +#: app/browser/select-dataset-step.tsx +msgid "chart.datasets.add-dataset-drawer.title" +msgstr "Datensatz auswählen" + #: app/configurator/components/field-i18n.ts msgid "chart.map.layers.area" msgstr "Flächen" diff --git a/app/locales/en/messages.po b/app/locales/en/messages.po index 3e1975a8e..368e7e126 100644 --- a/app/locales/en/messages.po +++ b/app/locales/en/messages.po @@ -56,6 +56,7 @@ msgstr "Categories" msgid "browse.dataset.all" msgstr "Browse all datasets" +#: app/browser/dataset-preview.tsx #: app/browser/dataset-preview.tsx msgid "browse.dataset.create-visualization" msgstr "Start a visualization" @@ -150,6 +151,18 @@ msgstr "Update this visualization" msgid "button.update.warning" msgstr "Keep in mind that updating this visualization will affect all the places where it might be already embedded!" +#: app/components/chart-selection-tabs.tsx +msgid "chart-selection-tabs.add-chart-different-dataset.button" +msgstr "Choose another dataset" + +#: app/components/chart-selection-tabs.tsx +msgid "chart-selection-tabs.add-chart-different-dataset.caption" +msgstr "Add chart based on a different dataset" + +#: app/components/chart-selection-tabs.tsx +msgid "chart-selection-tabs.add-chart-same-dataset.caption" +msgstr "Add chart based on the same dataset" + #: app/configurator/components/dataset-control-section.tsx msgid "chart.datasets.add" msgstr "Add dataset" @@ -162,6 +175,10 @@ msgstr "You can only combine datasets that share dimensions with the same unit a msgid "chart.datasets.add-dataset-dialog.title" msgstr "Select dataset with shared dimensions" +#: app/browser/select-dataset-step.tsx +msgid "chart.datasets.add-dataset-drawer.title" +msgstr "Select dataset" + #: app/configurator/components/field-i18n.ts msgid "chart.map.layers.area" msgstr "Areas" diff --git a/app/locales/fr/messages.po b/app/locales/fr/messages.po index 4b0ddeedb..4c7d53028 100644 --- a/app/locales/fr/messages.po +++ b/app/locales/fr/messages.po @@ -56,6 +56,7 @@ msgstr "Catégories" msgid "browse.dataset.all" msgstr "Parcourir tous les ensembles de données" +#: app/browser/dataset-preview.tsx #: app/browser/dataset-preview.tsx msgid "browse.dataset.create-visualization" msgstr "Démarrer une visualisation" @@ -150,6 +151,18 @@ msgstr "Actualiser cette visualisation" msgid "button.update.warning" msgstr "Gardez à l'esprit que la mise à jour de cette visualisation affectera tous les endroits où elle est déjà intégrée !" +#: app/components/chart-selection-tabs.tsx +msgid "chart-selection-tabs.add-chart-different-dataset.button" +msgstr "Choisir un autre ensemble de données" + +#: app/components/chart-selection-tabs.tsx +msgid "chart-selection-tabs.add-chart-different-dataset.caption" +msgstr "Ajouter un graphique basé sur un ensemble de données différent" + +#: app/components/chart-selection-tabs.tsx +msgid "chart-selection-tabs.add-chart-same-dataset.caption" +msgstr "Ajouter un graphique basé sur le même ensemble de données" + #: app/configurator/components/dataset-control-section.tsx msgid "chart.datasets.add" msgstr "Ajouter" @@ -162,6 +175,10 @@ msgstr "Vous ne pouvez combiner que des ensembles de données qui partagent des msgid "chart.datasets.add-dataset-dialog.title" msgstr "Sélectionner un ensemble de données avec des dimensions partagées" +#: app/browser/select-dataset-step.tsx +msgid "chart.datasets.add-dataset-drawer.title" +msgstr "Sélectionner un ensemble de données" + #: app/configurator/components/field-i18n.ts msgid "chart.map.layers.area" msgstr "Zones" diff --git a/app/locales/it/messages.po b/app/locales/it/messages.po index 91b2baf54..10171d7fb 100644 --- a/app/locales/it/messages.po +++ b/app/locales/it/messages.po @@ -56,6 +56,7 @@ msgstr "Categorie" msgid "browse.dataset.all" msgstr "Sfoglia tutti i set di dati" +#: app/browser/dataset-preview.tsx #: app/browser/dataset-preview.tsx msgid "browse.dataset.create-visualization" msgstr "Iniziare una visualizzazione" @@ -150,6 +151,18 @@ msgstr "Aggiornare questa visualizzazione" msgid "button.update.warning" msgstr "Tenete presente che l'aggiornamento di questa visualizzazione avrà effetto su tutti i luoghi in cui potrebbe essere già incorporata!" +#: app/components/chart-selection-tabs.tsx +msgid "chart-selection-tabs.add-chart-different-dataset.button" +msgstr "Scegli un altro set di dati" + +#: app/components/chart-selection-tabs.tsx +msgid "chart-selection-tabs.add-chart-different-dataset.caption" +msgstr "Aggiungi grafico basato su un set di dati diverso" + +#: app/components/chart-selection-tabs.tsx +msgid "chart-selection-tabs.add-chart-same-dataset.caption" +msgstr "Aggiungi grafico basato sullo stesso set di dati" + #: app/configurator/components/dataset-control-section.tsx msgid "chart.datasets.add" msgstr "Aggiungere" @@ -162,6 +175,10 @@ msgstr "Puoi combinare solo set di dati che condividono dimensioni con la stessa msgid "chart.datasets.add-dataset-dialog.title" msgstr "Seleziona il set di dati con dimensioni condivise" +#: app/browser/select-dataset-step.tsx +msgid "chart.datasets.add-dataset-drawer.title" +msgstr "Seleziona il set di dati" + #: app/configurator/components/field-i18n.ts msgid "chart.map.layers.area" msgstr "Aree" From 40e93f4487aa19d6d28d54f3011309c939892362 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Sun, 26 May 2024 10:44:51 +0200 Subject: [PATCH 15/31] feat: Alignment of button and dialog title --- app/locales/de/messages.po | 2 +- app/locales/en/messages.po | 2 +- app/locales/fr/messages.po | 2 +- app/locales/it/messages.po | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/locales/de/messages.po b/app/locales/de/messages.po index b33914b71..c726d75b0 100644 --- a/app/locales/de/messages.po +++ b/app/locales/de/messages.po @@ -153,7 +153,7 @@ msgstr "Denken Sie daran, dass sich die Aktualisierung dieser Visualisierung auf #: app/components/chart-selection-tabs.tsx msgid "chart-selection-tabs.add-chart-different-dataset.button" -msgstr "Wählen Sie einen anderen Datensatz" +msgstr "Datensatz auswählen" #: app/components/chart-selection-tabs.tsx msgid "chart-selection-tabs.add-chart-different-dataset.caption" diff --git a/app/locales/en/messages.po b/app/locales/en/messages.po index 368e7e126..ccc9ee715 100644 --- a/app/locales/en/messages.po +++ b/app/locales/en/messages.po @@ -153,7 +153,7 @@ msgstr "Keep in mind that updating this visualization will affect all the places #: app/components/chart-selection-tabs.tsx msgid "chart-selection-tabs.add-chart-different-dataset.button" -msgstr "Choose another dataset" +msgstr "Select dataset" #: app/components/chart-selection-tabs.tsx msgid "chart-selection-tabs.add-chart-different-dataset.caption" diff --git a/app/locales/fr/messages.po b/app/locales/fr/messages.po index 4c7d53028..86a32a066 100644 --- a/app/locales/fr/messages.po +++ b/app/locales/fr/messages.po @@ -153,7 +153,7 @@ msgstr "Gardez à l'esprit que la mise à jour de cette visualisation affectera #: app/components/chart-selection-tabs.tsx msgid "chart-selection-tabs.add-chart-different-dataset.button" -msgstr "Choisir un autre ensemble de données" +msgstr "Sélectionner un ensemble de données" #: app/components/chart-selection-tabs.tsx msgid "chart-selection-tabs.add-chart-different-dataset.caption" diff --git a/app/locales/it/messages.po b/app/locales/it/messages.po index 10171d7fb..3c06a8ace 100644 --- a/app/locales/it/messages.po +++ b/app/locales/it/messages.po @@ -153,7 +153,7 @@ msgstr "Tenete presente che l'aggiornamento di questa visualizzazione avrà effe #: app/components/chart-selection-tabs.tsx msgid "chart-selection-tabs.add-chart-different-dataset.button" -msgstr "Scegli un altro set di dati" +msgstr "Seleziona il set di dati" #: app/components/chart-selection-tabs.tsx msgid "chart-selection-tabs.add-chart-different-dataset.caption" From 8d2dd1ed715a31487805cc6797bdd67e20da9016 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Sun, 26 May 2024 10:48:56 +0200 Subject: [PATCH 16/31] feat: Hide comparison chart when adding a new dataset --- app/components/chart-selection-tabs.tsx | 16 ++++-- .../components/chart-type-selector.tsx | 50 ++++++++++++------- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/app/components/chart-selection-tabs.tsx b/app/components/chart-selection-tabs.tsx index 5ab2cef2f..a8f9af431 100644 --- a/app/components/chart-selection-tabs.tsx +++ b/app/components/chart-selection-tabs.tsx @@ -198,10 +198,19 @@ const TabsEditable = (props: TabsEditableProps) => { vertical: "bottom", }} onClose={handleClose} + PaperProps={{ + sx: { + py: "1rem", + }, + }} > {tabsState.popoverType === "add" ? ( <> - } gap="0.5rem" direction="column"> + } + gap="0.5rem" + direction="column" + > { state={state} type="add" showHelp={false} + showComparisonCharts={false} chartKey={tabsState.activeChartKey ?? chartConfig.key} sx={{ width: 320, px: 3, pb: 3 }} /> - + { }} > - Choose another dataset + Select dataset diff --git a/app/configurator/components/chart-type-selector.tsx b/app/configurator/components/chart-type-selector.tsx index 5d6aaaa91..1ee7151f3 100644 --- a/app/configurator/components/chart-type-selector.tsx +++ b/app/configurator/components/chart-type-selector.tsx @@ -27,11 +27,19 @@ type ChartTypeSelectorProps = { state: Exclude; type?: "add" | "edit"; showHelp?: boolean; + showComparisonCharts?: boolean; chartKey: string; } & BoxProps; export const ChartTypeSelector = (props: ChartTypeSelectorProps) => { - const { state, type = "edit", showHelp, chartKey, ...rest } = props; + const { + state, + type = "edit", + showHelp, + showComparisonCharts = true, + chartKey, + ...rest + } = props; const locale = useLocale(); const chartConfig = getChartConfig(state); const [{ data }] = useDataCubesComponentsQuery({ @@ -120,24 +128,28 @@ export const ChartTypeSelector = (props: ChartTypeSelectorProps) => { onClick={handleClick} testId="chart-type-selector-regular" /> - - + {showComparisonCharts ? ( + <> + + + + ) : null} )} From 9e2dc2da1b35049ec83809c3154ef0b13c110ccb Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Sun, 26 May 2024 10:57:18 +0200 Subject: [PATCH 17/31] refactor: Clarification on usage --- app/browser/select-dataset-step.tsx | 2 +- app/configurator/components/configurator.tsx | 4 ++-- app/pages/browse/index.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/browser/select-dataset-step.tsx b/app/browser/select-dataset-step.tsx index 5890f05dd..f09f737ee 100644 --- a/app/browser/select-dataset-step.tsx +++ b/app/browser/select-dataset-step.tsx @@ -532,7 +532,7 @@ const DatasetMetadataSingleCubeAdapter = ({ ); }; -export const SelectDatasetStep = ( +export const SelectDatasetStepPage = ( props: React.ComponentProps ) => { return ( diff --git a/app/configurator/components/configurator.tsx b/app/configurator/components/configurator.tsx index 79cfc27af..f5c9dcc2c 100644 --- a/app/configurator/components/configurator.tsx +++ b/app/configurator/components/configurator.tsx @@ -4,7 +4,7 @@ import Button, { ButtonProps } from "@mui/material/Button"; import { useRouter } from "next/router"; import React, { useEffect } from "react"; -import { SelectDatasetStep } from "@/browser/select-dataset-step"; +import { SelectDatasetStepPage } from "@/browser/select-dataset-step"; import { META } from "@/charts"; import { ChartPreview } from "@/components/chart-preview"; import { @@ -448,7 +448,7 @@ export const Configurator = () => { return isLoadingConfigureChartStep ? ( ) : configuratorState.state === "SELECTING_DATASET" ? ( - + ) : configuratorState.state === "INITIAL" ? null : ( {configuratorState.state === "CONFIGURING_CHART" && ( diff --git a/app/pages/browse/index.tsx b/app/pages/browse/index.tsx index a0a6e1f18..17e1bbd28 100644 --- a/app/pages/browse/index.tsx +++ b/app/pages/browse/index.tsx @@ -1,4 +1,4 @@ -import { SelectDatasetStep } from "@/browser/select-dataset-step"; +import { SelectDatasetStepPage } from "@/browser/select-dataset-step"; import { AppLayout } from "@/components/layout"; import { ConfiguratorStateProvider } from "@/configurator/configurator-state"; import { SearchCubeResultOrder } from "@/graphql/query-hooks"; @@ -19,7 +19,7 @@ export function DatasetBrowser() { return ( - + ); From 04fe0b2db6863e373813cca2bedcef4ea1160f65 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Sun, 26 May 2024 11:18:39 +0200 Subject: [PATCH 18/31] refactor: UseQueryParams can receive a setState function --- app/browser/context.tsx | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/app/browser/context.tsx b/app/browser/context.tsx index 423d33f00..738b2b19f 100644 --- a/app/browser/context.tsx +++ b/app/browser/context.tsx @@ -8,6 +8,8 @@ import Link from "next/link"; import { Router, useRouter } from "next/router"; import React, { createContext, + Dispatch, + SetStateAction, useContext, useEffect, useMemo, @@ -89,7 +91,7 @@ const urlCodec: BrowseParamsCodec = { serialize: buildURLFromBrowseState, }; -const useQueryParamsState = (initialState: T) => { +const useQueryParamsState = (initialState: BrowseParams) => { const router = useRouter(); const [state, rawSetState] = useState(() => { @@ -115,15 +117,24 @@ const useQueryParamsState = (initialState: T) => { } }, [urlCodec.parse, router.isReady, router.query]); - const setState = useEvent((stateUpdate: T) => { - rawSetState((curState) => { - const newState = { ...curState, ...stateUpdate } as T; - router.replace(urlCodec.serialize(newState), undefined, { - shallow: true, + const setState = useEvent( + ( + stateUpdate: BrowseParams | ((prevState: BrowseParams) => BrowseParams) + ) => { + rawSetState((curState) => { + const newState = { + ...curState, + ...(stateUpdate instanceof Function + ? stateUpdate(curState) + : stateUpdate), + } as BrowseParams; + router.replace(urlCodec.serialize(newState), undefined, { + shallow: true, + }); + return newState; }); - return newState; - }); - }); + } + ); return [state, setState] as const; }; @@ -136,7 +147,7 @@ const makeUseBrowseState = ( useParamsHook: ( initialState: BrowseParams - ) => readonly [BrowseParams, (state: BrowseParams) => void] + ) => readonly [BrowseParams, Dispatch>] ) => () => { const inputRef = useRef(null); From e5df03cc80d018a6545283545c8b0772e64c5f93 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Sun, 26 May 2024 11:18:53 +0200 Subject: [PATCH 19/31] docs: Add comments --- app/browser/select-dataset-step.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/browser/select-dataset-step.tsx b/app/browser/select-dataset-step.tsx index f09f737ee..0a6ccdb78 100644 --- a/app/browser/select-dataset-step.tsx +++ b/app/browser/select-dataset-step.tsx @@ -532,6 +532,10 @@ const DatasetMetadataSingleCubeAdapter = ({ ); }; +/** + * This is the select dataset step component for use directly in a page. + * It uses the URL to sync the state. + */ export const SelectDatasetStepPage = ( props: React.ComponentProps ) => { @@ -542,6 +546,10 @@ export const SelectDatasetStepPage = ( ); }; +/** + * This is the select dataset step component for use in a drawer, where the state is not + * synced with the URL. + */ export const SelectDatasetStepDrawer = ( props: React.ComponentProps ) => { From 2a057eae5d28afec4b37664aa0bab6d3e73ba4ec Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Sun, 26 May 2024 13:21:24 +0200 Subject: [PATCH 20/31] refactor: Extract to useStyles --- app/browser/dataset-browse.tsx | 60 ++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/app/browser/dataset-browse.tsx b/app/browser/dataset-browse.tsx index a18a8d598..604ccb192 100644 --- a/app/browser/dataset-browse.tsx +++ b/app/browser/dataset-browse.tsx @@ -65,7 +65,7 @@ import { } from "./context"; import { BrowseFilter } from "./filters"; -const useStyles = makeStyles(() => ({ +const useStyles = makeStyles(() => ({ navChip: { minWidth: 32, height: 24, @@ -73,7 +73,32 @@ const useStyles = makeStyles(() => ({ alignItems: "center", borderRadius: 2, }, - removeFilterButton: { + + searchInput: { + width: "100%", + maxWidth: 350, + }, +})); + +const useNavItemStyles = makeStyles< + Theme, + { level: number; navItemTheme: NavItemTheme } +>((theme) => ({ + navItem: { + justifyContent: "space-between", + alignItems: "center", + gap: 6, + borderRadius: 2, + width: "100%", + display: "flex", + transition: "background 0.1s ease", + }, + link: { + cursor: "pointer", + flexGrow: 1, + padding: theme.spacing(1, 0), + }, + removeFilterButton: ({ level, navItemTheme }) => ({ minWidth: 16, minHeight: 16, height: "auto", @@ -86,20 +111,13 @@ const useStyles = makeStyles(() => ({ "&:hover": { background: "rgba(0, 0, 0, 0.25)", }, - }, - navItem: { - justifyContent: "space-between", - alignItems: "center", - gap: 6, - borderRadius: 2, - width: "100%", - display: "flex", - transition: "background 0.1s ease", - }, - searchInput: { - width: "100%", - maxWidth: 350, - }, + + backgroundColor: level === 1 ? navItemTheme.activeBg : "transparent", + color: + level === 1 + ? navItemTheme.closeColor ?? navItemTheme.activeTextColor + : navItemTheme.activeBg, + }), })); export const SearchDatasetInput = ({ @@ -410,19 +428,11 @@ const NavItem = ({ .join("/")}?${extraURLParams}`; }, [includeDrafts, search, filters, next.iri]); - const classes = useStyles(); - const removeFilterButton = ( @@ -467,7 +477,7 @@ const NavItem = ({ <> From 17344ed70cee7bd913fb1849a9ab3005bfc9598c Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Sun, 26 May 2024 13:21:42 +0200 Subject: [PATCH 21/31] fix: Add keys --- app/browser/select-dataset-step.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/browser/select-dataset-step.tsx b/app/browser/select-dataset-step.tsx index 0a6ccdb78..09f5b1b6a 100644 --- a/app/browser/select-dataset-step.tsx +++ b/app/browser/select-dataset-step.tsx @@ -409,7 +409,7 @@ const SelectDatasetStepContent = ({ {variant === "drawer" ? ( - + Select dataset @@ -429,6 +429,7 @@ const SelectDatasetStepContent = ({ ) : null} {queryFilters.length > 0 && ( Date: Sun, 26 May 2024 13:29:38 +0200 Subject: [PATCH 22/31] feat: Search filters inside drawer work --- app/browser/context.tsx | 13 ++++++- app/browser/dataset-browse.tsx | 58 ++++++++++++++++++++++------- app/browser/filters.tsx | 28 ++++++++++++++ app/browser/select-dataset-step.tsx | 1 + app/components/maybe-link.tsx | 16 ++++++++ 5 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 app/components/maybe-link.tsx diff --git a/app/browser/context.tsx b/app/browser/context.tsx index 738b2b19f..a41c7b427 100644 --- a/app/browser/context.tsx +++ b/app/browser/context.tsx @@ -21,7 +21,11 @@ import { SearchCubeResultOrder } from "@/graphql/query-hooks"; import { BrowseParams } from "@/pages/browse"; import useEvent from "@/utils/use-event"; -import { getFiltersFromParams } from "./filters"; +import { + BrowseFilter, + getFiltersFromParams, + getParamsFromFilters, +} from "./filters"; export const getBrowseParamsFromQuery = ( query: Router["query"] @@ -115,7 +119,7 @@ const useQueryParamsState = (initialState: BrowseParams) => { if (router.isReady) { rawSetState(urlCodec.parse(router.query)); } - }, [urlCodec.parse, router.isReady, router.query]); + }, [router.isReady, router.query]); const setState = useEvent( ( @@ -206,6 +210,11 @@ const makeUseBrowseState = dataset, setDataset, filters, + setFilters: (filters: BrowseFilter[]) => + setParams((params) => ({ + ...params, + ...getParamsFromFilters(filters), + })), }), [ includeDrafts, diff --git a/app/browser/dataset-browse.tsx b/app/browser/dataset-browse.tsx index 604ccb192..7f4da314b 100644 --- a/app/browser/dataset-browse.tsx +++ b/app/browser/dataset-browse.tsx @@ -31,6 +31,7 @@ import { SearchFieldProps, } from "@/components/form"; import { Loading, LoadingDataError } from "@/components/hint"; +import MaybeLink from "@/components/maybe-link"; import { accordionPresenceProps, MotionBox, @@ -371,6 +372,7 @@ const NavItem = ({ active, theme = defaultNavItemTheme, level = 1, + disableLink, }: { children: React.ReactNode; filters: BrowseFilter[]; @@ -380,10 +382,12 @@ const NavItem = ({ theme: typeof defaultNavItemTheme; /** Level is there to differentiate between organizations and organization subtopics */ level?: number; + disableLink?: boolean; } & MUILinkProps) => { - const { includeDrafts, search } = useBrowseContext(); + const { includeDrafts, search, setFilters } = useBrowseContext(); + const classes = useNavItemStyles({ level, navItemTheme: theme }); - const path = useMemo(() => { + const [newFiltersAdd, path] = useMemo(() => { const extraURLParams = stringify( pickBy( { @@ -404,12 +408,13 @@ const NavItem = ({ newFilters.push(next); } - return `/browse/${newFilters - .map(encodeFilter) - .join("/")}?${extraURLParams}`; + return [ + newFilters, + `/browse/${newFilters.map(encodeFilter).join("/")}?${extraURLParams}`, + ] as const; }, [includeDrafts, search, level, next, filters]); - const removeFilterPath = useMemo(() => { + const [newFiltersRemove, removeFilterPath] = useMemo(() => { const extraURLParams = stringify( pickBy( { @@ -423,21 +428,31 @@ const NavItem = ({ (f) => f.__typename !== "DataCubeAbout" && f.iri !== next.iri ); - return `/browse/${newFilters - .map(encodeFilter) - .join("/")}?${extraURLParams}`; + return [ + newFilters, + `/browse/${newFilters.map(encodeFilter).join("/")}?${extraURLParams}`, + ] as const; }, [includeDrafts, search, filters, next.iri]); const removeFilterButton = ( - + { + ev.preventDefault(); + console.log(newFiltersRemove); + setFilters(newFiltersRemove); }} > - + ); const countChip = @@ -475,15 +490,25 @@ const NavItem = ({ ) : ( <> - + { + ev.preventDefault(); + setFilters(newFiltersAdd); + }} > {children} - + {countChip} )} @@ -568,6 +593,7 @@ const NavSection = ({ filters, counts, extra, + disableLinks, }: { label: React.ReactNode; icon: React.ReactNode; @@ -578,6 +604,7 @@ const NavSection = ({ filters: BrowseFilter[]; counts: Record; extra?: React.ReactNode; + disableLinks?: boolean; }) => { const topItems = useMemo(() => { return sortBy( @@ -616,6 +643,7 @@ const NavSection = ({ next={item} count={counts[item.iri]} theme={navItemTheme} + disableLink={disableLinks} > {item.label} @@ -710,11 +738,13 @@ export const SearchFilters = ({ themes, orgs, termsets: termsetCounts, + disableNavLinks = false, }: { cubes: SearchCubeResult[]; themes: DataCubeTheme[]; orgs: DataCubeOrganization[]; termsets: TermsetCount[]; + disableNavLinks?: boolean; }) => { const { filters } = useBrowseContext(); const counts = useMemo(() => { @@ -797,6 +827,7 @@ export const SearchFilters = ({ icon={} label={Themes} extra={null} + disableLinks={disableNavLinks} /> ) : null; @@ -834,6 +865,7 @@ export const SearchFilters = ({ /> ) : null } + disableLinks={disableNavLinks} /> ) : null; diff --git a/app/browser/filters.tsx b/app/browser/filters.tsx index 5d6e5fde2..bf72cc5c9 100644 --- a/app/browser/filters.tsx +++ b/app/browser/filters.tsx @@ -53,3 +53,31 @@ export const getFiltersFromParams = (params: BrowseParams) => { return filters; }; + +export const getParamsFromFilters = (filters: BrowseFilter[]) => { + const params: BrowseParams = { + type: undefined, + subtype: undefined, + iri: undefined, + subiri: undefined, + topic: undefined, + }; + let i = 0; + for (const filter of filters) { + const typeAttr = i === 0 ? "type" : ("subtype" as const); + const iriAttr = i === 0 ? "iri" : ("subiri" as const); + if (filter.__typename === SearchCubeFilterType.DataCubeTheme) { + params[typeAttr] = "theme"; + params[iriAttr] = filter.iri; + } else if ( + filter.__typename === SearchCubeFilterType.DataCubeOrganization + ) { + params[typeAttr] = "organization"; + params[iriAttr] = filter.iri; + } else if (filter.__typename === SearchCubeFilterType.DataCubeAbout) { + params.topic = filter.iri; + } + i++; + } + return params; +}; diff --git a/app/browser/select-dataset-step.tsx b/app/browser/select-dataset-step.tsx index 09f5b1b6a..607302a4a 100644 --- a/app/browser/select-dataset-step.tsx +++ b/app/browser/select-dataset-step.tsx @@ -386,6 +386,7 @@ const SelectDatasetStepContent = ({ themes={themes} orgs={orgs} termsets={termsets} + disableNavLinks /> )} diff --git a/app/components/maybe-link.tsx b/app/components/maybe-link.tsx new file mode 100644 index 000000000..175422580 --- /dev/null +++ b/app/components/maybe-link.tsx @@ -0,0 +1,16 @@ +import { Link } from "@mui/material"; +import { LinkProps } from "next/link"; +import React from "react"; + +/** A link where the default link behavior can be disabled */ +const MaybeLink = ({ + disabled, + children, + ...props +}: LinkProps & { disabled: boolean; children: React.ReactNode }) => { + const Wrapper = disabled ? React.Fragment : Link; + const wrapperProps = disabled ? {} : props; + return {children}; +}; + +export default MaybeLink; From 455b9384d7f695bef899dd6a9ed915cd87f4c147 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Sun, 26 May 2024 13:46:10 +0200 Subject: [PATCH 23/31] feat: Close drawer after adding dataset --- app/configurator/components/add-new-dataset-panel.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/configurator/components/add-new-dataset-panel.tsx b/app/configurator/components/add-new-dataset-panel.tsx index 4b26d4cc4..bda4e0e94 100644 --- a/app/configurator/components/add-new-dataset-panel.tsx +++ b/app/configurator/components/add-new-dataset-panel.tsx @@ -76,7 +76,7 @@ export const useAddChartConfigBasedOnNewDataset = () => { export const AddNewDatasetPanel = () => { const { isOpen, close } = useSearchDatasetPanelStore(); const [dataSetIri, setDataSetIri] = useState(""); - const handleAddNewDataset = useAddChartConfigBasedOnNewDataset(); + const addNewDataset = useAddChartConfigBasedOnNewDataset(); return ( { }} datasetPreviewProps={{ dataSetIri, - onClickCreate: (ev, datasetIri) => { + onClickCreate: async (ev, datasetIri) => { ev.preventDefault(); - handleAddNewDataset(datasetIri); + await addNewDataset(datasetIri); + close(); }, }} /> From 174002bb29df4b717dfba9fde7512d72caea75cc Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Tue, 28 May 2024 13:47:12 +0200 Subject: [PATCH 24/31] refactor: Remove unneeded --- app/browser/dataset-browse.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/browser/dataset-browse.tsx b/app/browser/dataset-browse.tsx index 7f4da314b..47b903767 100644 --- a/app/browser/dataset-browse.tsx +++ b/app/browser/dataset-browse.tsx @@ -442,11 +442,9 @@ const NavItem = ({ disabled={!!disableLink} > { ev.preventDefault(); - console.log(newFiltersRemove); setFilters(newFiltersRemove); }} > From b51a09b2937fcec38936592e77fdc8c4b24b83d0 Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Tue, 28 May 2024 13:47:37 +0200 Subject: [PATCH 25/31] refactor: Renamed props for clarity --- app/browser/dataset-preview.tsx | 12 ++++-------- .../components/add-new-dataset-panel.tsx | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app/browser/dataset-preview.tsx b/app/browser/dataset-preview.tsx index e3fa30653..565523508 100644 --- a/app/browser/dataset-preview.tsx +++ b/app/browser/dataset-preview.tsx @@ -89,15 +89,11 @@ export interface Preview { export const DataSetPreview = ({ dataSetIri, dataSource, - onClickCreate, + onCreateChartFromDataset, }: { dataSetIri: string; dataSource: DataSource; - onClickDataset?: ( - ev: React.MouseEvent, - datasetIri: string - ) => void; - onClickCreate?: ( + onCreateChartFromDataset?: ( ev: React.MouseEvent, datasetIri: string ) => void; @@ -156,9 +152,9 @@ export const DataSetPreview = ({ {dataCubeMetadata.title} - {onClickCreate ? ( + {onCreateChartFromDataset ? (