From 5005c2a0c2acf4b741d9d47d26dd1d60409f12ca Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Tue, 28 Nov 2023 13:19:55 -0500 Subject: [PATCH 01/17] Stub in collection bar map type component --- .../eda/src/lib/map/analysis/MapAnalysis.tsx | 38 +++++++++++++++++++ .../src/lib/map/analysis/mapTypes/index.ts | 1 + .../plugins/CollectionBarMarkerType.tsx | 27 +++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/CollectionBarMarkerType.tsx diff --git a/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx b/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx index c9799717d8..c2a82f77f2 100755 --- a/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx +++ b/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx @@ -74,6 +74,7 @@ import { SideNavigationItems } from './MapSideNavigation'; import { barMarkerPlugin, bubbleMarkerPlugin, + collectionBarMarkerPlugin, donutMarkerPlugin, } from './mapTypes'; @@ -86,6 +87,7 @@ import { FetchClientError } from '@veupathdb/http-utils'; import { Page } from '@veupathdb/wdk-client/lib/Components'; import { Link } from 'react-router-dom'; import { AnalysisError } from '../../core/components/AnalysisError'; +import { EmotionJSX } from '@emotion/react/types/jsx-namespace'; enum MapSideNavItemLabels { Download = 'Download', @@ -504,6 +506,42 @@ function MapAnalysisImpl(props: ImplProps) { }, ], }, + { + type: 'subheading', + labelText: MapSideNavItemLabels.GroupedVariableMaps, + children: [ + { + type: 'item', + id: 'collection-var-bar', + labelText: collectionBarMarkerPlugin.displayName, + leftIcon: + activeMarkerConfigurationType === 'collection-barplot' ? ( + + ) : null, + rightIcon: , + onActive: () => { + setActiveMarkerConfigurationType('collection-barplot'); + }, + renderSidePanelDrawer(apps): EmotionJSX.Element { + return ( + + ); + }, + }, + ], + }, ], }, { diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/index.ts b/packages/libs/eda/src/lib/map/analysis/mapTypes/index.ts index 715596ebd1..122da94c5e 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/index.ts +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/index.ts @@ -1,3 +1,4 @@ export { plugin as donutMarkerPlugin } from './plugins/DonutMarkerMapType'; export { plugin as barMarkerPlugin } from './plugins/BarMarkerMapType'; export { plugin as bubbleMarkerPlugin } from './plugins/BubbleMarkerMapType'; +export { plugin as collectionBarMarkerPlugin } from './plugins/CollectionBarMarkerType'; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/CollectionBarMarkerType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/CollectionBarMarkerType.tsx new file mode 100644 index 0000000000..4266919acf --- /dev/null +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/CollectionBarMarkerType.tsx @@ -0,0 +1,27 @@ +import { MapTypePlugin } from '../types'; + +const displayName = 'Bar plots'; + +export const plugin: MapTypePlugin = { + displayName, + ConfigPanelComponent, + MapLayerComponent, + MapOverlayComponent, + MapTypeHeaderDetails, +}; + +function ConfigPanelComponent() { + return
I am a config component
; +} + +function MapLayerComponent() { + return
I am a map layer component
; +} + +function MapOverlayComponent() { + return
I am a map overlay component
; +} + +function MapTypeHeaderDetails() { + return
I am a map type header details component
; +} From e68b2842c54648606145333b26421ec3c5a0e7bd Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Fri, 1 Dec 2023 13:10:33 -0500 Subject: [PATCH 02/17] Add missing return statement --- packages/libs/eda/src/lib/core/hooks/workspace.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/libs/eda/src/lib/core/hooks/workspace.ts b/packages/libs/eda/src/lib/core/hooks/workspace.ts index 6e925c722b..fb999d16d2 100755 --- a/packages/libs/eda/src/lib/core/hooks/workspace.ts +++ b/packages/libs/eda/src/lib/core/hooks/workspace.ts @@ -181,6 +181,7 @@ export function useStudyEntities(filters?: Filter[]) { // Otherwise if >= 12 months, use month bins, etc return diff >= 12; } + return false; } ) ?? 'day') as TimeUnit; From 0b0f6aa5bd65f164930cdc522027afe5c275beed Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Fri, 1 Dec 2023 13:11:43 -0500 Subject: [PATCH 03/17] Some more refactoring to make marker configuration more generic --- .../eda/src/lib/map/analysis/MapAnalysis.tsx | 42 ++++----- .../libs/eda/src/lib/map/analysis/appState.ts | 85 ++++--------------- .../mapTypes/plugins/BarMarkerMapType.tsx | 29 +++++-- .../mapTypes/plugins/BubbleMarkerMapType.tsx | 25 ++++-- .../plugins/CollectionBarMarkerType.tsx | 38 ++++++++- .../mapTypes/plugins/DonutMarkerMapType.tsx | 27 ++++-- .../src/lib/map/analysis/mapTypes/types.ts | 30 ++++--- 7 files changed, 157 insertions(+), 119 deletions(-) diff --git a/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx b/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx index c2a82f77f2..5035e3de11 100755 --- a/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx +++ b/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx @@ -83,9 +83,7 @@ import { useToggleStarredVariable } from '../../core/hooks/starredVariables'; import { MapTypeMapLayerProps } from './mapTypes/types'; import { defaultViewport } from '@veupathdb/components/lib/map/config/map'; import AnalysisNameDialog from '../../workspace/AnalysisNameDialog'; -import { FetchClientError } from '@veupathdb/http-utils'; import { Page } from '@veupathdb/wdk-client/lib/Components'; -import { Link } from 'react-router-dom'; import { AnalysisError } from '../../core/components/AnalysisError'; import { EmotionJSX } from '@emotion/react/types/jsx-namespace'; @@ -439,7 +437,7 @@ function MapAnalysisImpl(props: ImplProps) { studyEntities={studyEntities} geoConfigs={geoConfigs} configuration={activeMarkerConfiguration} - updateConfiguration={updateMarkerConfigurations as any} + updateConfiguration={updateMarkerConfigurations} hideVizInputsAndControls={hideVizInputsAndControls} setHideVizInputsAndControls={setHideVizInputsAndControls} /> @@ -469,7 +467,7 @@ function MapAnalysisImpl(props: ImplProps) { studyEntities={studyEntities} geoConfigs={geoConfigs} configuration={activeMarkerConfiguration} - updateConfiguration={updateMarkerConfigurations as any} + updateConfiguration={updateMarkerConfigurations} hideVizInputsAndControls={hideVizInputsAndControls} setHideVizInputsAndControls={setHideVizInputsAndControls} /> @@ -497,7 +495,7 @@ function MapAnalysisImpl(props: ImplProps) { studyEntities={studyEntities} geoConfigs={geoConfigs} configuration={activeMarkerConfiguration} - updateConfiguration={updateMarkerConfigurations as any} + updateConfiguration={updateMarkerConfigurations} hideVizInputsAndControls={hideVizInputsAndControls} setHideVizInputsAndControls={setHideVizInputsAndControls} /> @@ -802,6 +800,7 @@ function MapAnalysisImpl(props: ImplProps) { const toggleStarredVariable = useToggleStarredVariable(analysisState); + // TODO Add `type` to plugin def and use to look up, so we can remove hard coded strings. const activeMapTypePlugin = activeMarkerConfiguration?.type === 'barplot' ? barMarkerPlugin @@ -818,22 +817,23 @@ function MapAnalysisImpl(props: ImplProps) { const activeSideNavigationItemMenu = activePanelItem?.renderSidePanelDrawer(apps) ?? null; - const mapTypeMapLayerProps: MapTypeMapLayerProps = { - apps, - analysisState, - appState, - studyId, - filters: filtersIncludingTimeSlider, - studyEntities, - geoConfigs, - configuration: activeMarkerConfiguration, - updateConfiguration: updateMarkerConfigurations as any, - filtersIncludingViewport: filtersIncludingViewportAndTimeSlider, - totalCounts, - filteredCounts, - hideVizInputsAndControls, - setHideVizInputsAndControls, - }; + const mapTypeMapLayerProps: MapTypeMapLayerProps = + { + apps, + analysisState, + appState, + studyId, + filters: filtersIncludingTimeSlider, + studyEntities, + geoConfigs, + configuration: activeMarkerConfiguration, + updateConfiguration: updateMarkerConfigurations, + filtersIncludingViewport: filtersIncludingViewportAndTimeSlider, + totalCounts, + filteredCounts, + hideVizInputsAndControls, + setHideVizInputsAndControls, + }; return ( diff --git a/packages/libs/eda/src/lib/map/analysis/appState.ts b/packages/libs/eda/src/lib/map/analysis/appState.ts index e37e0f5cc1..1a7a72334b 100644 --- a/packages/libs/eda/src/lib/map/analysis/appState.ts +++ b/packages/libs/eda/src/lib/map/analysis/appState.ts @@ -3,19 +3,18 @@ import { pipe } from 'fp-ts/lib/function'; import * as t from 'io-ts'; import { isEqual } from 'lodash'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { useAnalysis, useGetDefaultVariableDescriptor } from '../../core'; +import { + useAnalysis, + useGetDefaultVariableDescriptor, + useStudyMetadata, +} from '../../core'; import { VariableDescriptor } from '../../core/types/variable'; import { useGetDefaultTimeVariableDescriptor } from './hooks/eztimeslider'; import { defaultViewport } from '@veupathdb/components/lib/map/config/map'; +import * as plugins from './mapTypes'; const LatLngLiteral = t.type({ lat: t.number, lng: t.number }); -const MarkerType = t.keyof({ - barplot: null, - pie: null, - bubble: null, -}); - // user-specified selection export type SelectedValues = t.TypeOf; // eslint-disable-next-line @typescript-eslint/no-redeclare @@ -39,42 +38,11 @@ const SelectedCountsOption = t.union([ ]); export type MarkerConfiguration = t.TypeOf; +// TODO Make `uknown` and use plugin-specific decoder // eslint-disable-next-line @typescript-eslint/no-redeclare -export const MarkerConfiguration = t.intersection([ - t.type({ - type: MarkerType, - selectedVariable: VariableDescriptor, - }), - t.partial({ - activeVisualizationId: t.string, - }), - t.union([ - t.type({ - type: t.literal('barplot'), - selectedValues: SelectedValues, - selectedPlotMode: t.union([t.literal('count'), t.literal('proportion')]), - binningMethod: BinningMethod, - dependentAxisLogScale: t.boolean, - selectedCountsOption: SelectedCountsOption, - }), - t.type({ - type: t.literal('pie'), - selectedValues: SelectedValues, - binningMethod: BinningMethod, - selectedCountsOption: SelectedCountsOption, - }), - t.intersection([ - t.type({ - type: t.literal('bubble'), - }), - t.partial({ - aggregator: t.union([t.literal('mean'), t.literal('median')]), - numeratorValues: t.union([t.array(t.string), t.undefined]), - denominatorValues: t.union([t.array(t.string), t.undefined]), - }), - ]), - ]), -]); +export const MarkerConfiguration = t.type({ + type: t.string, +}); export const AppState = t.intersection([ t.type({ @@ -82,7 +50,7 @@ export const AppState = t.intersection([ center: t.tuple([t.number, t.number]), zoom: t.number, }), - activeMarkerConfigurationType: MarkerType, + activeMarkerConfigurationType: t.string, markerConfigurations: t.array(MarkerConfiguration), isSidePanelExpanded: t.boolean, }), @@ -141,6 +109,7 @@ export function useAppState( const getDefaultVariableDescriptor = useGetDefaultVariableDescriptor(); const defaultVariable = getDefaultVariableDescriptor(); + const studyMetadata = useStudyMetadata(); const getDefaultTimeVariableDescriptor = useGetDefaultTimeVariableDescriptor(); @@ -157,33 +126,11 @@ export function useAppState( active: true, selectedRange: undefined, }, - markerConfigurations: [ - { - type: 'pie', - selectedVariable: defaultVariable, - selectedValues: undefined, - binningMethod: undefined, - selectedCountsOption: 'filtered', - }, - { - type: 'barplot', - selectedPlotMode: 'count', - selectedVariable: defaultVariable, - selectedValues: undefined, - binningMethod: undefined, - dependentAxisLogScale: false, - selectedCountsOption: 'filtered', - }, - { - type: 'bubble', - selectedVariable: defaultVariable, - aggregator: 'mean', - numeratorValues: undefined, - denominatorValues: undefined, - }, - ], + markerConfigurations: Object.values(plugins).map((plugin) => + plugin.getDefaultConfig({ defaultVariable, study: studyMetadata }) + ), }), - [defaultVariable, defaultTimeVariable] + [defaultTimeVariable, defaultVariable, studyMetadata] ); useEffect(() => { diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BarMarkerMapType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BarMarkerMapType.tsx index 86e7e4755e..e52fa272ad 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BarMarkerMapType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BarMarkerMapType.tsx @@ -64,8 +64,19 @@ import { MapFloatingErrorDiv } from '../../MapFloatingErrorDiv'; import { MapTypeHeaderCounts } from '../MapTypeHeaderCounts'; const displayName = 'Bar plots'; -export const plugin: MapTypePlugin = { +export const plugin: MapTypePlugin = { displayName, + getDefaultConfig({ defaultVariable }) { + return { + type: 'barplot', + selectedPlotMode: 'count', + selectedVariable: defaultVariable, + selectedValues: undefined, + binningMethod: undefined, + dependentAxisLogScale: false, + selectedCountsOption: 'filtered', + }; + }, ConfigPanelComponent, MapLayerComponent, MapOverlayComponent, @@ -80,7 +91,9 @@ type ChartMarkerPropsWithCounts = Omit & { data: ChartMarkerDataWithCounts[]; }; -function ConfigPanelComponent(props: MapTypeConfigPanelProps) { +function ConfigPanelComponent( + props: MapTypeConfigPanelProps +) { const { apps, analysisState, @@ -322,7 +335,9 @@ function ConfigPanelComponent(props: MapTypeConfigPanelProps) { ); } -function MapLayerComponent(props: MapTypeMapLayerProps) { +function MapLayerComponent( + props: MapTypeMapLayerProps +) { const { studyEntities, studyId, @@ -375,7 +390,9 @@ function MapLayerComponent(props: MapTypeMapLayerProps) { ); } -function MapOverlayComponent(props: MapTypeMapLayerProps) { +function MapOverlayComponent( + props: MapTypeMapLayerProps +) { const { studyEntities, studyId, @@ -454,7 +471,9 @@ function MapOverlayComponent(props: MapTypeMapLayerProps) { ); } -function MapTypeHeaderDetails(props: MapTypeMapLayerProps) { +function MapTypeHeaderDetails( + props: MapTypeMapLayerProps +) { const { selectedVariable, binningMethod, diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BubbleMarkerMapType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BubbleMarkerMapType.tsx index 557b4fbd1f..41ef2bf14d 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BubbleMarkerMapType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BubbleMarkerMapType.tsx @@ -59,15 +59,26 @@ import { MapTypeHeaderCounts } from '../MapTypeHeaderCounts'; const displayName = 'Bubbles'; -export const plugin: MapTypePlugin = { +export const plugin: MapTypePlugin = { displayName, + getDefaultConfig({ defaultVariable }) { + return { + type: 'bubble', + selectedVariable: defaultVariable, + aggregator: 'mean', + numeratorValues: undefined, + denominatorValues: undefined, + }; + }, ConfigPanelComponent: BubbleMapConfigurationPanel, MapLayerComponent: BubbleMapLayer, MapOverlayComponent: BubbleLegends, MapTypeHeaderDetails, }; -function BubbleMapConfigurationPanel(props: MapTypeConfigPanelProps) { +function BubbleMapConfigurationPanel( + props: MapTypeConfigPanelProps +) { const { apps, analysisState, @@ -177,7 +188,9 @@ function BubbleMapConfigurationPanel(props: MapTypeConfigPanelProps) { /** * Renders marker and legend components */ -function BubbleMapLayer(props: MapTypeMapLayerProps) { +function BubbleMapLayer( + props: MapTypeMapLayerProps +) { const { studyId, filters, appState, configuration, geoConfigs } = props; const markersData = useMarkerData({ boundsZoomLevel: appState.boundsZoomLevel, @@ -211,7 +224,7 @@ function BubbleMapLayer(props: MapTypeMapLayerProps) { ); } -function BubbleLegends(props: MapTypeMapLayerProps) { +function BubbleLegends(props: MapTypeMapLayerProps) { const { studyId, filters, geoConfigs, appState, updateConfiguration } = props; const configuration = props.configuration as BubbleMarkerConfiguration; const findEntityAndVariable = useFindEntityAndVariable(); @@ -295,7 +308,9 @@ function BubbleLegends(props: MapTypeMapLayerProps) { ); } -function MapTypeHeaderDetails(props: MapTypeMapLayerProps) { +function MapTypeHeaderDetails( + props: MapTypeMapLayerProps +) { const configuration = props.configuration as BubbleMarkerConfiguration; const markerDataResponse = useMarkerData({ studyId: props.studyId, diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/CollectionBarMarkerType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/CollectionBarMarkerType.tsx index 4266919acf..bc4cec53f4 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/CollectionBarMarkerType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/CollectionBarMarkerType.tsx @@ -1,17 +1,47 @@ -import { MapTypePlugin } from '../types'; +import { preorder } from '@veupathdb/wdk-client/lib/Utils/TreeUtils'; +import { VariableCollectionDescriptor } from '../../../../core/types/variable'; +import { MapTypeConfigPanelProps, MapTypePlugin } from '../types'; const displayName = 'Bar plots'; -export const plugin: MapTypePlugin = { +interface CollectionBarMarkerConfiguration { + type: 'collection-barplot'; + selectedCollection: VariableCollectionDescriptor; +} + +export const plugin: MapTypePlugin = { displayName, + getDefaultConfig({ study }) { + const firstCollection = Array.from( + preorder(study.rootEntity, (e) => e.children ?? []) + ) + .flatMap((e) => e.collections?.map((c) => [e, c]) ?? []) + .at(0); + if (firstCollection == null) + throw new Error('This study does not have any collections.'); + return { + type: 'collection-barplot', + selectedCollection: { + entityId: firstCollection[0].id, + collectionId: firstCollection[1].id, + }, + }; + }, ConfigPanelComponent, MapLayerComponent, MapOverlayComponent, MapTypeHeaderDetails, }; -function ConfigPanelComponent() { - return
I am a config component
; +function ConfigPanelComponent( + props: MapTypeConfigPanelProps +) { + return ( +
+

I am a config component

+

Selected collection: {JSON.stringify(props.configuration)}

+
+ ); } function MapLayerComponent() { diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/DonutMarkerMapType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/DonutMarkerMapType.tsx index 44b02baaa3..b349819163 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/DonutMarkerMapType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/DonutMarkerMapType.tsx @@ -59,15 +59,26 @@ import { MapTypeHeaderCounts } from '../MapTypeHeaderCounts'; const displayName = 'Donuts'; -export const plugin: MapTypePlugin = { +export const plugin: MapTypePlugin = { displayName, + getDefaultConfig({ defaultVariable }) { + return { + type: 'pie', + selectedVariable: defaultVariable, + selectedValues: undefined, + binningMethod: undefined, + selectedCountsOption: 'filtered', + }; + }, ConfigPanelComponent, MapLayerComponent, MapOverlayComponent, MapTypeHeaderDetails, }; -function ConfigPanelComponent(props: MapTypeConfigPanelProps) { +function ConfigPanelComponent( + props: MapTypeConfigPanelProps +) { const { apps, analysisState, @@ -286,7 +297,9 @@ function ConfigPanelComponent(props: MapTypeConfigPanelProps) { ); } -function MapLayerComponent(props: MapTypeMapLayerProps) { +function MapLayerComponent( + props: MapTypeMapLayerProps +) { const { selectedVariable, binningMethod, selectedValues } = props.configuration as PieMarkerConfiguration; const markerDataResponse = useMarkerData({ @@ -325,7 +338,9 @@ function MapLayerComponent(props: MapTypeMapLayerProps) { ); } -function MapOverlayComponent(props: MapTypeMapLayerProps) { +function MapOverlayComponent( + props: MapTypeMapLayerProps +) { const { studyId, filters, @@ -407,7 +422,9 @@ function MapOverlayComponent(props: MapTypeMapLayerProps) { ); } -function MapTypeHeaderDetails(props: MapTypeMapLayerProps) { +function MapTypeHeaderDetails( + props: MapTypeMapLayerProps +) { const { selectedVariable, binningMethod, selectedValues } = props.configuration as PieMarkerConfiguration; const markerDataResponse = useMarkerData({ diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts b/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts index 66fd2bd865..1d188f6a1d 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts @@ -4,13 +4,15 @@ import { Filter, PromiseHookState, StudyEntity, + StudyMetadata, } from '../../../core'; import { GeoConfig } from '../../../core/types/geoConfig'; import { ComputationAppOverview } from '../../../core/types/visualization'; -import { AppState } from '../appState'; +import { AppState, MarkerConfiguration } from '../appState'; import { EntityCounts } from '../../../core/hooks/entityCounts'; +import { VariableDescriptor } from '../../../core/types/variable'; -export interface MapTypeConfigPanelProps { +export interface MapTypeConfigPanelProps { apps: ComputationAppOverview[]; analysisState: AnalysisState; appState: AppState; @@ -19,12 +21,12 @@ export interface MapTypeConfigPanelProps { studyEntities: StudyEntity[]; geoConfigs: GeoConfig[]; configuration: unknown; - updateConfiguration: (configuration: unknown) => void; + updateConfiguration: (configuration: T) => void; hideVizInputsAndControls: boolean; setHideVizInputsAndControls: (hide: boolean) => void; } -export interface MapTypeMapLayerProps { +export interface MapTypeMapLayerProps { apps: ComputationAppOverview[]; analysisState: AnalysisState; appState: AppState; @@ -33,7 +35,7 @@ export interface MapTypeMapLayerProps { studyEntities: StudyEntity[]; geoConfigs: GeoConfig[]; configuration: unknown; - updateConfiguration: (configuration: unknown) => void; + updateConfiguration: (configuration: T) => void; totalCounts: PromiseHookState; filteredCounts: PromiseHookState; filtersIncludingViewport: Filter[]; @@ -45,25 +47,33 @@ export interface MapTypeMapLayerProps { * A plugin containing the pieces needed to render * and configure a map type */ -export interface MapTypePlugin { +export interface MapTypePlugin { /** * Display name of map type used for menu, etc. */ displayName: string; + /** + * Returns a default configuration for this MapType. This is used to + * create a set of default configurations for new analyses. + */ + getDefaultConfig(props: { + defaultVariable: VariableDescriptor; + study: StudyMetadata; + }): T; /** * Returns a ReactNode used for configuring the map type */ - ConfigPanelComponent: ComponentType; + ConfigPanelComponent: ComponentType>; /** * Returns a ReactNode that is rendered as a leaflet map layer */ - MapLayerComponent?: ComponentType; + MapLayerComponent?: ComponentType>; /** * Returns a ReactNode that is rendered on top of the map */ - MapOverlayComponent?: ComponentType; + MapOverlayComponent?: ComponentType>; /** * Returns a ReactNode that is rendered in the map header */ - MapTypeHeaderDetails?: ComponentType; + MapTypeHeaderDetails?: ComponentType>; } From 93957d9e586b975d8ee528867e11f119ddffe04f Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Thu, 29 Feb 2024 17:07:31 -0500 Subject: [PATCH 04/17] other merge fixes --- packages/libs/eda/src/lib/map/analysis/appState.ts | 12 +++++++++--- .../eda/src/lib/map/analysis/mapTypes/shared.tsx | 4 +++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/libs/eda/src/lib/map/analysis/appState.ts b/packages/libs/eda/src/lib/map/analysis/appState.ts index 143041d061..f1c9aa6090 100644 --- a/packages/libs/eda/src/lib/map/analysis/appState.ts +++ b/packages/libs/eda/src/lib/map/analysis/appState.ts @@ -41,9 +41,15 @@ const SelectedCountsOption = t.union([ export type MarkerConfiguration = t.TypeOf; // TODO Make `uknown` and use plugin-specific decoder // eslint-disable-next-line @typescript-eslint/no-redeclare -export const MarkerConfiguration = t.type({ - type: t.string, -}); +export const MarkerConfiguration = t.intersection([ + t.type({ + type: t.string, + }), + t.partial({ + selectedMarkers: t.array(t.string), + selectedVariable: VariableDescriptor, + }), +]); const PanelConfig = t.type({ isVisble: t.boolean, diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/shared.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/shared.tsx index a8c9449290..1d285fd1d9 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/shared.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/shared.tsx @@ -39,6 +39,8 @@ import Banner from '@veupathdb/coreui/lib/components/banners/Banner'; import { NoDataError } from '../../../core/api/DataClient/NoDataError'; import { useCallback, useState } from 'react'; import useSnackbar from '@veupathdb/coreui/lib/components/notifications/useSnackbar'; +import { PieMarkerConfiguration } from '../MarkerConfiguration/PieMarkerConfigurationMenu'; +import { BarPlotMarkerConfiguration } from '../MarkerConfiguration/BarPlotMarkerConfigurationMenu'; export const defaultAnimation = { method: 'geohash', @@ -487,7 +489,7 @@ export function pieOrBarMarkerConfigLittleFilter( const activeMarkerConfiguration = markerConfigurations.find( (markerConfig) => markerConfig.type === activeMarkerConfigurationType - ); + ) as PieMarkerConfiguration | BarPlotMarkerConfiguration; // This doesn't seem ideal. Do we ever have no active config? if (activeMarkerConfiguration == null) return []; From 78440bb333495445dd7a5388be3fb4644a996d7a Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Fri, 1 Mar 2024 11:09:37 -0500 Subject: [PATCH 05/17] Colocate configuration components with maptype components --- .../eda/src/lib/map/analysis/MapAnalysis.tsx | 2 +- .../map/analysis/MarkerConfiguration/index.ts | 11 ----- .../CategoricalMarkerConfigurationTable.tsx | 8 ++-- .../CategoricalMarkerPreview.tsx | 6 +-- .../MapTypeConfigurationMenu.tsx | 2 +- .../MarkerConfigurationSelector.tsx | 2 +- .../icons/BarPlotMarkerIcon.tsx | 0 .../icons/BarPlotMarkersIcon.tsx | 0 .../icons/BubbleMarkerIcon.tsx | 0 .../icons/DonutMarkerIcon.tsx | 0 .../icons/DonutMarkersIcon.tsx | 0 .../MarkerConfiguration/icons/index.ts | 0 .../src/lib/map/analysis/mapTypes/index.ts | 8 ++-- .../{ => barplot}/BarMarkerMapType.tsx | 40 +++++++++--------- .../BarPlotMarkerConfigurationMenu.tsx | 18 ++++---- .../bubble}/BubbleMarkerConfigurationMenu.tsx | 20 ++++----- .../{ => bubble}/BubbleMarkerMapType.tsx | 42 ++++++++++--------- .../CollectionBarMarkerType.tsx | 4 +- .../{ => donut}/DonutMarkerMapType.tsx | 34 +++++++-------- .../donut}/PieMarkerConfigurationMenu.tsx | 18 ++++---- .../src/lib/map/analysis/mapTypes/shared.tsx | 4 +- .../analysis/utils/defaultOverlayConfig.ts | 2 +- 22 files changed, 106 insertions(+), 115 deletions(-) delete mode 100644 packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/index.ts rename packages/libs/eda/src/lib/map/analysis/{ => mapTypes}/MarkerConfiguration/CategoricalMarkerConfigurationTable.tsx (97%) rename packages/libs/eda/src/lib/map/analysis/{ => mapTypes}/MarkerConfiguration/CategoricalMarkerPreview.tsx (96%) rename packages/libs/eda/src/lib/map/analysis/{ => mapTypes}/MarkerConfiguration/MapTypeConfigurationMenu.tsx (95%) rename packages/libs/eda/src/lib/map/analysis/{ => mapTypes}/MarkerConfiguration/MarkerConfigurationSelector.tsx (97%) rename packages/libs/eda/src/lib/map/analysis/{ => mapTypes}/MarkerConfiguration/icons/BarPlotMarkerIcon.tsx (100%) rename packages/libs/eda/src/lib/map/analysis/{ => mapTypes}/MarkerConfiguration/icons/BarPlotMarkersIcon.tsx (100%) rename packages/libs/eda/src/lib/map/analysis/{ => mapTypes}/MarkerConfiguration/icons/BubbleMarkerIcon.tsx (100%) rename packages/libs/eda/src/lib/map/analysis/{ => mapTypes}/MarkerConfiguration/icons/DonutMarkerIcon.tsx (100%) rename packages/libs/eda/src/lib/map/analysis/{ => mapTypes}/MarkerConfiguration/icons/DonutMarkersIcon.tsx (100%) rename packages/libs/eda/src/lib/map/analysis/{ => mapTypes}/MarkerConfiguration/icons/index.ts (100%) rename packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/{ => barplot}/BarMarkerMapType.tsx (94%) rename packages/libs/eda/src/lib/map/analysis/{MarkerConfiguration => mapTypes/plugins/barplot}/BarPlotMarkerConfigurationMenu.tsx (94%) rename packages/libs/eda/src/lib/map/analysis/{MarkerConfiguration => mapTypes/plugins/bubble}/BubbleMarkerConfigurationMenu.tsx (88%) rename packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/{ => bubble}/BubbleMarkerMapType.tsx (96%) rename packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/{ => collection-barplot}/CollectionBarMarkerType.tsx (90%) rename packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/{ => donut}/DonutMarkerMapType.tsx (95%) rename packages/libs/eda/src/lib/map/analysis/{MarkerConfiguration => mapTypes/plugins/donut}/PieMarkerConfigurationMenu.tsx (92%) diff --git a/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx b/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx index 3705355cf0..a280f708a3 100755 --- a/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx +++ b/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx @@ -55,7 +55,7 @@ import { BarPlotMarkerIcon, DonutMarkerIcon, BubbleMarkerIcon, -} from './MarkerConfiguration/icons'; +} from './mapTypes/MarkerConfiguration/icons'; import { AllAnalyses } from '../../workspace/AllAnalyses'; import { getStudyId } from '@veupathdb/study-data-access/lib/shared/studies'; import { isSavedAnalysis } from '../../core/utils/analysis'; diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/index.ts b/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/index.ts deleted file mode 100644 index 3b67e0b8c1..0000000000 --- a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BarPlotMarkerConfigurationMenu } from './BarPlotMarkerConfigurationMenu'; -import { PieMarkerConfigurationMenu } from './PieMarkerConfigurationMenu'; -import { MarkerConfigurationSelector } from './MarkerConfigurationSelector'; -import { BubbleMarkerConfigurationMenu } from './BubbleMarkerConfigurationMenu'; - -export { - MarkerConfigurationSelector, - PieMarkerConfigurationMenu, - BarPlotMarkerConfigurationMenu, - BubbleMarkerConfigurationMenu, -}; diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/CategoricalMarkerConfigurationTable.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/CategoricalMarkerConfigurationTable.tsx similarity index 97% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/CategoricalMarkerConfigurationTable.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/CategoricalMarkerConfigurationTable.tsx index efd58abf53..d1a3aa762c 100644 --- a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/CategoricalMarkerConfigurationTable.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/CategoricalMarkerConfigurationTable.tsx @@ -4,15 +4,15 @@ import { MesaSortObject, MesaStateProps, } from '@veupathdb/coreui/lib/components/Mesa/types'; -import { AllValuesDefinition } from '../../../core'; +import { AllValuesDefinition } from '../../../../core'; import { Tooltip } from '@veupathdb/components/lib/components/widgets/Tooltip'; import { ColorPaletteDefault } from '@veupathdb/components/lib/types/plots'; import RadioButtonGroup from '@veupathdb/components/lib/components/widgets/RadioButtonGroup'; -import { UNSELECTED_TOKEN } from '../../constants'; +import { UNSELECTED_TOKEN } from '../../../constants'; import { orderBy } from 'lodash'; -import { SelectedCountsOption } from '../appState'; +import { SelectedCountsOption } from '../../appState'; import Spinner from '@veupathdb/components/lib/components/Spinner'; -import { SharedMarkerConfigurations } from '../mapTypes/shared'; +import { SharedMarkerConfigurations } from '../../mapTypes/shared'; type Props = { overlayValues: string[]; diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/CategoricalMarkerPreview.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/CategoricalMarkerPreview.tsx similarity index 96% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/CategoricalMarkerPreview.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/CategoricalMarkerPreview.tsx index f8095eed43..aa9e380685 100644 --- a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/CategoricalMarkerPreview.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/CategoricalMarkerPreview.tsx @@ -1,16 +1,16 @@ -import { AllValuesDefinition, OverlayConfig } from '../../../core'; +import { AllValuesDefinition, OverlayConfig } from '../../../../core'; import { ColorPaletteDefault } from '@veupathdb/components/lib/types/plots'; import { ChartMarkerStandalone, getChartMarkerDependentAxisRange, } from '@veupathdb/components/lib/map/ChartMarker'; import { DonutMarkerStandalone } from '@veupathdb/components/lib/map/DonutMarker'; -import { UNSELECTED_TOKEN } from '../../constants'; +import { UNSELECTED_TOKEN } from '../../../constants'; import Banner from '@veupathdb/coreui/lib/components/banners/Banner'; import { kFormatter, mFormatter, -} from '../../../core/utils/big-number-formatters'; +} from '../../../../core/utils/big-number-formatters'; import { MAXIMUM_ALLOWABLE_VALUES } from './CategoricalMarkerConfigurationTable'; type Props = { diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/MapTypeConfigurationMenu.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MapTypeConfigurationMenu.tsx similarity index 95% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/MapTypeConfigurationMenu.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MapTypeConfigurationMenu.tsx index 327cbc9fee..153d51c11d 100644 --- a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/MapTypeConfigurationMenu.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MapTypeConfigurationMenu.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { H5 } from '@veupathdb/coreui'; -import { MarkerConfiguration } from '../appState'; +import { MarkerConfiguration } from '../../appState'; import TabbedDisplay, { TabbedDisplayProps, } from '@veupathdb/coreui/lib/components/grids/TabbedDisplay'; diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/MarkerConfigurationSelector.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MarkerConfigurationSelector.tsx similarity index 97% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/MarkerConfigurationSelector.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MarkerConfigurationSelector.tsx index 02d43d584d..7eed0d366a 100644 --- a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/MarkerConfigurationSelector.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MarkerConfigurationSelector.tsx @@ -1,6 +1,6 @@ import { H6 } from '@veupathdb/coreui'; import { useUITheme } from '@veupathdb/coreui/lib/components/theming'; -import { MarkerConfiguration } from '../appState'; +import { MarkerConfiguration } from '../../appState'; export interface MarkerConfigurationOption { displayName: string; diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/icons/BarPlotMarkerIcon.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/icons/BarPlotMarkerIcon.tsx similarity index 100% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/icons/BarPlotMarkerIcon.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/icons/BarPlotMarkerIcon.tsx diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/icons/BarPlotMarkersIcon.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/icons/BarPlotMarkersIcon.tsx similarity index 100% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/icons/BarPlotMarkersIcon.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/icons/BarPlotMarkersIcon.tsx diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/icons/BubbleMarkerIcon.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/icons/BubbleMarkerIcon.tsx similarity index 100% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/icons/BubbleMarkerIcon.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/icons/BubbleMarkerIcon.tsx diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/icons/DonutMarkerIcon.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/icons/DonutMarkerIcon.tsx similarity index 100% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/icons/DonutMarkerIcon.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/icons/DonutMarkerIcon.tsx diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/icons/DonutMarkersIcon.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/icons/DonutMarkersIcon.tsx similarity index 100% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/icons/DonutMarkersIcon.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/icons/DonutMarkersIcon.tsx diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/icons/index.ts b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/icons/index.ts similarity index 100% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/icons/index.ts rename to packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/icons/index.ts diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/index.ts b/packages/libs/eda/src/lib/map/analysis/mapTypes/index.ts index 122da94c5e..e1fa390702 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/index.ts +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/index.ts @@ -1,4 +1,4 @@ -export { plugin as donutMarkerPlugin } from './plugins/DonutMarkerMapType'; -export { plugin as barMarkerPlugin } from './plugins/BarMarkerMapType'; -export { plugin as bubbleMarkerPlugin } from './plugins/BubbleMarkerMapType'; -export { plugin as collectionBarMarkerPlugin } from './plugins/CollectionBarMarkerType'; +export { plugin as donutMarkerPlugin } from './plugins/donut/DonutMarkerMapType'; +export { plugin as barMarkerPlugin } from './plugins/barplot/BarMarkerMapType'; +export { plugin as bubbleMarkerPlugin } from './plugins/bubble/BubbleMarkerMapType'; +export { plugin as collectionBarMarkerPlugin } from './plugins/collection-barplot/CollectionBarMarkerType'; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BarMarkerMapType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarMarkerMapType.tsx similarity index 94% rename from packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BarMarkerMapType.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarMarkerMapType.tsx index 4b7c40e97d..de505fc51a 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BarMarkerMapType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarMarkerMapType.tsx @@ -1,22 +1,22 @@ import { useCallback, useMemo } from 'react'; -import { Variable } from '../../../../core/types/study'; -import { findEntityAndVariable } from '../../../../core/utils/study-metadata'; +import { Variable } from '../../../../../core/types/study'; +import { findEntityAndVariable } from '../../../../../core/utils/study-metadata'; import { BarPlotMarkerConfiguration, BarPlotMarkerConfigurationMenu, -} from '../../MarkerConfiguration/BarPlotMarkerConfigurationMenu'; +} from './BarPlotMarkerConfigurationMenu'; import { MapTypeConfigPanelProps, MapTypeMapLayerProps, MapTypePlugin, -} from '../types'; +} from '../../types'; import { OverlayConfig, StandaloneMapMarkersResponse, -} from '../../../../core/api/DataClient/types'; -import { getDefaultAxisRange } from '../../../../core/utils/computeDefaultAxisRange'; +} from '../../../../../core/api/DataClient/types'; +import { getDefaultAxisRange } from '../../../../../core/utils/computeDefaultAxisRange'; import { NumberRange } from '@veupathdb/components/lib/types/general'; -import { mFormatter } from '../../../../core/utils/big-number-formatters'; +import { mFormatter } from '../../../../../core/utils/big-number-formatters'; import ChartMarker, { BaseMarkerData, ChartMarkerProps, @@ -36,7 +36,7 @@ import { STUDY_ID_VARIABLE_ID, UNSELECTED_DISPLAY_TEXT, UNSELECTED_TOKEN, -} from '../../../constants'; +} from '../../../../constants'; import SemanticMarkers from '@veupathdb/components/lib/map/SemanticMarkers'; import { DistributionMarkerDataProps, @@ -56,29 +56,29 @@ import { getErrorOverlayComponent, getLegendErrorMessage, selectedMarkersLittleFilter, -} from '../shared'; +} from '../../shared'; import { useFindEntityAndVariable, useSubsettingClient, -} from '../../../../core/hooks/workspace'; -import { DraggableLegendPanel } from '../../DraggableLegendPanel'; -import { MapLegend } from '../../MapLegend'; +} from '../../../../../core/hooks/workspace'; +import { DraggableLegendPanel } from '../../../DraggableLegendPanel'; +import { MapLegend } from '../../../MapLegend'; import { sharedStandaloneMarkerProperties } from '../../MarkerConfiguration/CategoricalMarkerPreview'; -import { useToggleStarredVariable } from '../../../../core/hooks/starredVariables'; -import DraggableVisualization from '../../DraggableVisualization'; -import { useStandaloneVizPlugins } from '../../hooks/standaloneVizPlugins'; +import { useToggleStarredVariable } from '../../../../../core/hooks/starredVariables'; +import DraggableVisualization from '../../../DraggableVisualization'; +import { useStandaloneVizPlugins } from '../../../hooks/standaloneVizPlugins'; import { MapTypeConfigurationMenu, MarkerConfigurationOption, } from '../../MarkerConfiguration/MapTypeConfigurationMenu'; import { BarPlotMarkerIcon } from '../../MarkerConfiguration/icons'; import { TabbedDisplayProps } from '@veupathdb/coreui/lib/components/grids/TabbedDisplay'; -import MapVizManagement from '../../MapVizManagement'; +import MapVizManagement from '../../../MapVizManagement'; import Spinner from '@veupathdb/components/lib/components/Spinner'; -import { useLittleFilters } from '../../littleFilters'; -import TimeSliderQuickFilter from '../../TimeSliderQuickFilter'; -import { MapTypeHeaderStudyDetails } from '../MapTypeHeaderStudyDetails'; -import { SubStudies } from '../../SubStudies'; +import { useLittleFilters } from '../../../littleFilters'; +import TimeSliderQuickFilter from '../../../TimeSliderQuickFilter'; +import { MapTypeHeaderStudyDetails } from '../../MapTypeHeaderStudyDetails'; +import { SubStudies } from '../../../SubStudies'; const displayName = 'Bar plots'; export const plugin: MapTypePlugin = { diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/BarPlotMarkerConfigurationMenu.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarPlotMarkerConfigurationMenu.tsx similarity index 94% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/BarPlotMarkerConfigurationMenu.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarPlotMarkerConfigurationMenu.tsx index e50d686716..08f7660c6f 100644 --- a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/BarPlotMarkerConfigurationMenu.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarPlotMarkerConfigurationMenu.tsx @@ -2,29 +2,29 @@ import { useCallback } from 'react'; import { InputVariables, Props as InputVariablesProps, -} from '../../../core/components/visualizations/InputVariables'; +} from '../../../../../core/components/visualizations/InputVariables'; import RadioButtonGroup from '@veupathdb/components/lib/components/widgets/RadioButtonGroup'; -import { VariablesByInputName } from '../../../core/utils/data-element-constraints'; +import { VariablesByInputName } from '../../../../../core/utils/data-element-constraints'; import { usePromise, AllValuesDefinition, OverlayConfig, Variable, Filter, -} from '../../../core'; -import { CategoricalMarkerConfigurationTable } from './CategoricalMarkerConfigurationTable'; -import { CategoricalMarkerPreview } from './CategoricalMarkerPreview'; +} from '../../../../../core'; +import { CategoricalMarkerConfigurationTable } from '../../MarkerConfiguration/CategoricalMarkerConfigurationTable'; +import { CategoricalMarkerPreview } from '../../MarkerConfiguration/CategoricalMarkerPreview'; import Barplot from '@veupathdb/components/lib/plots/Barplot'; -import { SubsettingClient } from '../../../core/api'; +import { SubsettingClient } from '../../../../../core/api'; import { Toggle } from '@veupathdb/coreui'; -import { useUncontrolledSelections } from '../hooks/uncontrolledSelections'; +import { useUncontrolledSelections } from '../../../hooks/uncontrolledSelections'; import { BinningMethod, SelectedCountsOption, SelectedValues, -} from '../appState'; +} from '../../../appState'; import { gray } from '@veupathdb/coreui/lib/definitions/colors'; -import { SharedMarkerConfigurations } from '../mapTypes/shared'; +import { SharedMarkerConfigurations } from '../../shared'; interface MarkerConfiguration { type: T; diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/BubbleMarkerConfigurationMenu.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerConfigurationMenu.tsx similarity index 88% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/BubbleMarkerConfigurationMenu.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerConfigurationMenu.tsx index 3c3c7db317..44643ed408 100644 --- a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/BubbleMarkerConfigurationMenu.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerConfigurationMenu.tsx @@ -1,20 +1,20 @@ import { InputVariables, Props as InputVariablesProps, -} from '../../../core/components/visualizations/InputVariables'; -import { VariableTreeNode } from '../../../core/types/study'; -import { VariablesByInputName } from '../../../core/utils/data-element-constraints'; -import { findEntityAndVariable } from '../../../core/utils/study-metadata'; +} from '../../../../../core/components/visualizations/InputVariables'; +import { VariableTreeNode } from '../../../../../core/types/study'; +import { VariablesByInputName } from '../../../../../core/utils/data-element-constraints'; +import { findEntityAndVariable } from '../../../../../core/utils/study-metadata'; import HelpIcon from '@veupathdb/wdk-client/lib/Components/Icon/HelpIcon'; -import { BubbleOverlayConfig } from '../../../core'; -import PluginError from '../../../core/components/visualizations/PluginError'; +import { BubbleOverlayConfig } from '../../../../../core'; +import PluginError from '../../../../../core/components/visualizations/PluginError'; import { aggregationHelp, AggregationInputs, -} from '../../../core/components/visualizations/implementations/LineplotVisualization'; -import { DataElementConstraint } from '../../../core/types/visualization'; // TO DO for dates: remove -import { SharedMarkerConfigurations } from '../mapTypes/shared'; -import { invalidProportionText } from '../utils/defaultOverlayConfig'; +} from '../../../../../core/components/visualizations/implementations/LineplotVisualization'; +import { DataElementConstraint } from '../../../../../core/types/visualization'; // TO DO for dates: remove +import { SharedMarkerConfigurations } from '../../shared'; +import { invalidProportionText } from '../../../utils/defaultOverlayConfig'; type AggregatorOption = typeof aggregatorOptions[number]; const aggregatorOptions = ['mean', 'median'] as const; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BubbleMarkerMapType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerMapType.tsx similarity index 96% rename from packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BubbleMarkerMapType.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerMapType.tsx index 99dd05c7a8..bbdb7d2e55 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/BubbleMarkerMapType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerMapType.tsx @@ -15,26 +15,28 @@ import { Filter, useDataClient, useStudyEntities, -} from '../../../../core'; +} from '../../../../../core'; import { BubbleOverlayConfig, StandaloneMapBubblesLegendRequestParams, StandaloneMapBubblesRequestParams, StandaloneMapBubblesResponse, -} from '../../../../core/api/DataClient/types'; -import { useToggleStarredVariable } from '../../../../core/hooks/starredVariables'; -import { DraggableLegendPanel } from '../../DraggableLegendPanel'; -import { MapLegend } from '../../MapLegend'; -import MapVizManagement from '../../MapVizManagement'; -import { BubbleMarkerConfigurationMenu } from '../../MarkerConfiguration'; -import { BubbleMarkerConfiguration } from '../../MarkerConfiguration/BubbleMarkerConfigurationMenu'; +} from '../../../../../core/api/DataClient/types'; +import { useToggleStarredVariable } from '../../../../../core/hooks/starredVariables'; +import { DraggableLegendPanel } from '../../../DraggableLegendPanel'; +import { MapLegend } from '../../../MapLegend'; +import MapVizManagement from '../../../MapVizManagement'; +import { + BubbleMarkerConfiguration, + BubbleMarkerConfigurationMenu, +} from './BubbleMarkerConfigurationMenu'; import { MapTypeConfigurationMenu, MarkerConfigurationOption, } from '../../MarkerConfiguration/MapTypeConfigurationMenu'; import { BubbleMarkerIcon } from '../../MarkerConfiguration/icons'; -import { useStandaloneVizPlugins } from '../../hooks/standaloneVizPlugins'; -import { getDefaultBubbleOverlayConfig } from '../../utils/defaultOverlayConfig'; +import { useStandaloneVizPlugins } from '../../../hooks/standaloneVizPlugins'; +import { getDefaultBubbleOverlayConfig } from '../../../utils/defaultOverlayConfig'; import { MAX_FILTERSET_VALUES, defaultAnimation, @@ -47,26 +49,26 @@ import { getErrorOverlayComponent, useSelectedMarkerSnackbars, selectedMarkersLittleFilter, -} from '../shared'; +} from '../../shared'; import { MapTypeConfigPanelProps, MapTypeMapLayerProps, MapTypePlugin, -} from '../types'; -import DraggableVisualization from '../../DraggableVisualization'; -import { VariableDescriptor } from '../../../../core/types/variable'; +} from '../../types'; +import DraggableVisualization from '../../../DraggableVisualization'; +import { VariableDescriptor } from '../../../../../core/types/variable'; import { useQuery } from '@tanstack/react-query'; import { BoundsViewport } from '@veupathdb/components/lib/map/Types'; -import { GeoConfig } from '../../../../core/types/geoConfig'; +import { GeoConfig } from '../../../../../core/types/geoConfig'; import Spinner from '@veupathdb/components/lib/components/Spinner'; import { useLittleFilters, UseLittleFiltersFuncProps, -} from '../../littleFilters'; -import TimeSliderQuickFilter from '../../TimeSliderQuickFilter'; -import { SubStudies } from '../../SubStudies'; -import { MapTypeHeaderStudyDetails } from '../MapTypeHeaderStudyDetails'; -import { STUDIES_ENTITY_ID, STUDY_ID_VARIABLE_ID } from '../../../constants'; +} from '../../../littleFilters'; +import TimeSliderQuickFilter from '../../../TimeSliderQuickFilter'; +import { SubStudies } from '../../../SubStudies'; +import { MapTypeHeaderStudyDetails } from '../../MapTypeHeaderStudyDetails'; +import { STUDIES_ENTITY_ID, STUDY_ID_VARIABLE_ID } from '../../../../constants'; const displayName = 'Bubbles'; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/CollectionBarMarkerType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx similarity index 90% rename from packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/CollectionBarMarkerType.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx index bc4cec53f4..74a2e0ee16 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/CollectionBarMarkerType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx @@ -1,6 +1,6 @@ import { preorder } from '@veupathdb/wdk-client/lib/Utils/TreeUtils'; -import { VariableCollectionDescriptor } from '../../../../core/types/variable'; -import { MapTypeConfigPanelProps, MapTypePlugin } from '../types'; +import { VariableCollectionDescriptor } from '../../../../../core/types/variable'; +import { MapTypeConfigPanelProps, MapTypePlugin } from '../../types'; const displayName = 'Bar plots'; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/DonutMarkerMapType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/DonutMarkerMapType.tsx similarity index 95% rename from packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/DonutMarkerMapType.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/DonutMarkerMapType.tsx index dab1104c77..a2346e33ce 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/DonutMarkerMapType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/DonutMarkerMapType.tsx @@ -17,23 +17,23 @@ import { STUDY_ID_VARIABLE_ID, UNSELECTED_DISPLAY_TEXT, UNSELECTED_TOKEN, -} from '../../../constants'; +} from '../../../../constants'; import { StandaloneMapMarkersResponse, Variable, useFindEntityAndVariable, useSubsettingClient, -} from '../../../../core'; -import { useToggleStarredVariable } from '../../../../core/hooks/starredVariables'; -import { kFormatter } from '../../../../core/utils/big-number-formatters'; -import { findEntityAndVariable } from '../../../../core/utils/study-metadata'; -import { DraggableLegendPanel } from '../../DraggableLegendPanel'; -import { MapLegend } from '../../MapLegend'; +} from '../../../../../core'; +import { useToggleStarredVariable } from '../../../../../core/hooks/starredVariables'; +import { kFormatter } from '../../../../../core/utils/big-number-formatters'; +import { findEntityAndVariable } from '../../../../../core/utils/study-metadata'; +import { DraggableLegendPanel } from '../../../DraggableLegendPanel'; +import { MapLegend } from '../../../MapLegend'; import { sharedStandaloneMarkerProperties } from '../../MarkerConfiguration/CategoricalMarkerPreview'; import { PieMarkerConfiguration, PieMarkerConfigurationMenu, -} from '../../MarkerConfiguration/PieMarkerConfigurationMenu'; +} from './PieMarkerConfigurationMenu'; import { DistributionMarkerDataProps, defaultAnimation, @@ -52,26 +52,26 @@ import { getLegendErrorMessage, useSelectedMarkerSnackbars, selectedMarkersLittleFilter, -} from '../shared'; +} from '../../shared'; import { MapTypeConfigPanelProps, MapTypeMapLayerProps, MapTypePlugin, -} from '../types'; -import DraggableVisualization from '../../DraggableVisualization'; -import { useStandaloneVizPlugins } from '../../hooks/standaloneVizPlugins'; +} from '../../types'; +import DraggableVisualization from '../../../DraggableVisualization'; +import { useStandaloneVizPlugins } from '../../../hooks/standaloneVizPlugins'; import { MapTypeConfigurationMenu, MarkerConfigurationOption, } from '../../MarkerConfiguration/MapTypeConfigurationMenu'; import { DonutMarkersIcon } from '../../MarkerConfiguration/icons'; import { TabbedDisplayProps } from '@veupathdb/coreui/lib/components/grids/TabbedDisplay'; -import MapVizManagement from '../../MapVizManagement'; +import MapVizManagement from '../../../MapVizManagement'; import Spinner from '@veupathdb/components/lib/components/Spinner'; -import { MapTypeHeaderStudyDetails } from '../MapTypeHeaderStudyDetails'; -import { SubStudies } from '../../SubStudies'; -import { useLittleFilters } from '../../littleFilters'; -import TimeSliderQuickFilter from '../../TimeSliderQuickFilter'; +import { MapTypeHeaderStudyDetails } from '../../MapTypeHeaderStudyDetails'; +import { SubStudies } from '../../../SubStudies'; +import { useLittleFilters } from '../../../littleFilters'; +import TimeSliderQuickFilter from '../../../TimeSliderQuickFilter'; const displayName = 'Donuts'; diff --git a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/PieMarkerConfigurationMenu.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/PieMarkerConfigurationMenu.tsx similarity index 92% rename from packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/PieMarkerConfigurationMenu.tsx rename to packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/PieMarkerConfigurationMenu.tsx index bccba741f5..ad252f4749 100644 --- a/packages/libs/eda/src/lib/map/analysis/MarkerConfiguration/PieMarkerConfigurationMenu.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/PieMarkerConfigurationMenu.tsx @@ -2,27 +2,27 @@ import { useCallback } from 'react'; import { InputVariables, Props as InputVariablesProps, -} from '../../../core/components/visualizations/InputVariables'; -import { VariablesByInputName } from '../../../core/utils/data-element-constraints'; +} from '../../../../../core/components/visualizations/InputVariables'; +import { VariablesByInputName } from '../../../../../core/utils/data-element-constraints'; import { usePromise, AllValuesDefinition, OverlayConfig, Variable, Filter, -} from '../../../core'; -import { CategoricalMarkerConfigurationTable } from './CategoricalMarkerConfigurationTable'; -import { CategoricalMarkerPreview } from './CategoricalMarkerPreview'; +} from '../../../../../core'; +import { CategoricalMarkerConfigurationTable } from '../../MarkerConfiguration/CategoricalMarkerConfigurationTable'; +import { CategoricalMarkerPreview } from '../../MarkerConfiguration/CategoricalMarkerPreview'; import Barplot from '@veupathdb/components/lib/plots/Barplot'; -import { SubsettingClient } from '../../../core/api'; +import { SubsettingClient } from '../../../../../core/api'; import RadioButtonGroup from '@veupathdb/components/lib/components/widgets/RadioButtonGroup'; -import { useUncontrolledSelections } from '../hooks/uncontrolledSelections'; +import { useUncontrolledSelections } from '../../../hooks/uncontrolledSelections'; import { BinningMethod, SelectedCountsOption, SelectedValues, -} from '../appState'; -import { SharedMarkerConfigurations } from '../mapTypes/shared'; +} from '../../../appState'; +import { SharedMarkerConfigurations } from '../../shared'; interface MarkerConfiguration { type: T; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/shared.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/shared.tsx index 1d285fd1d9..d865f3e103 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/shared.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/shared.tsx @@ -39,8 +39,8 @@ import Banner from '@veupathdb/coreui/lib/components/banners/Banner'; import { NoDataError } from '../../../core/api/DataClient/NoDataError'; import { useCallback, useState } from 'react'; import useSnackbar from '@veupathdb/coreui/lib/components/notifications/useSnackbar'; -import { PieMarkerConfiguration } from '../MarkerConfiguration/PieMarkerConfigurationMenu'; -import { BarPlotMarkerConfiguration } from '../MarkerConfiguration/BarPlotMarkerConfigurationMenu'; +import { PieMarkerConfiguration } from './plugins/donut/PieMarkerConfigurationMenu'; +import { BarPlotMarkerConfiguration } from './plugins/barplot/BarPlotMarkerConfigurationMenu'; export const defaultAnimation = { method: 'geohash', diff --git a/packages/libs/eda/src/lib/map/analysis/utils/defaultOverlayConfig.ts b/packages/libs/eda/src/lib/map/analysis/utils/defaultOverlayConfig.ts index b29372e413..303b74613a 100644 --- a/packages/libs/eda/src/lib/map/analysis/utils/defaultOverlayConfig.ts +++ b/packages/libs/eda/src/lib/map/analysis/utils/defaultOverlayConfig.ts @@ -12,7 +12,7 @@ import { } from '../../../core'; import { DataClient, SubsettingClient } from '../../../core/api'; import { BinningMethod } from '../appState'; -import { BubbleMarkerConfiguration } from '../MarkerConfiguration/BubbleMarkerConfigurationMenu'; +import { BubbleMarkerConfiguration } from '../mapTypes/plugins/bubble/BubbleMarkerConfigurationMenu'; // This async function fetches the default overlay config. // For continuous variables, this involves calling the filter-aware-metadata/continuous-variable From 2c2877460f9b7e632747722458f2ecfb93551a90 Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Fri, 1 Mar 2024 11:10:21 -0500 Subject: [PATCH 06/17] Add missing hook dependency --- .../mapTypes/plugins/donut/PieMarkerConfigurationMenu.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/PieMarkerConfigurationMenu.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/PieMarkerConfigurationMenu.tsx index ad252f4749..b2cbd0467c 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/PieMarkerConfigurationMenu.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/PieMarkerConfigurationMenu.tsx @@ -129,6 +129,7 @@ export function PieMarkerConfigurationMenu({ color: '#333', }; }, [ + studyId, overlayVariable, overlayConfiguration?.overlayType, subsettingClient, From 2e104bf4505c2b6b952441c717e9430372536cd3 Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Wed, 6 Mar 2024 17:05:51 -0500 Subject: [PATCH 07/17] Remove some hard coding from menu --- .../eda/src/lib/map/analysis/MapAnalysis.tsx | 201 ++++++------------ .../plugins/barplot/BarMarkerMapType.tsx | 2 + .../plugins/bubble/BubbleMarkerMapType.tsx | 2 + .../plugins/donut/DonutMarkerMapType.tsx | 7 +- .../src/lib/map/analysis/mapTypes/types.ts | 10 +- 5 files changed, 88 insertions(+), 134 deletions(-) diff --git a/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx b/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx index a280f708a3..f3b2ba8590 100755 --- a/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx +++ b/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx @@ -51,11 +51,6 @@ import { uniq } from 'lodash'; import Path from 'path'; import DownloadTab from '../../workspace/DownloadTab'; import { RecordController } from '@veupathdb/wdk-client/lib/Controllers'; -import { - BarPlotMarkerIcon, - DonutMarkerIcon, - BubbleMarkerIcon, -} from './mapTypes/MarkerConfiguration/icons'; import { AllAnalyses } from '../../workspace/AllAnalyses'; import { getStudyId } from '@veupathdb/study-data-access/lib/shared/studies'; import { isSavedAnalysis } from '../../core/utils/analysis'; @@ -67,10 +62,10 @@ import { } from './Types'; import { SideNavigationItems } from './MapSideNavigation'; import { + donutMarkerPlugin, barMarkerPlugin, bubbleMarkerPlugin, collectionBarMarkerPlugin, - donutMarkerPlugin, } from './mapTypes'; import { MapTypeMapLayerProps } from './mapTypes/types'; @@ -78,10 +73,17 @@ import { defaultViewport } from '@veupathdb/components/lib/map/config/map'; import AnalysisNameDialog from '../../workspace/AnalysisNameDialog'; import { Page } from '@veupathdb/wdk-client/lib/Components'; import { AnalysisError } from '../../core/components/AnalysisError'; -import { EmotionJSX } from '@emotion/react/types/jsx-namespace'; import useSnackbar from '@veupathdb/coreui/lib/components/notifications/useSnackbar'; import SettingsButton from '@veupathdb/coreui/lib/components/containers/DraggablePanel/SettingsButton'; +const singleVariablePlugins = [ + donutMarkerPlugin, + barMarkerPlugin, + bubbleMarkerPlugin, +]; + +const groupedVariablePlugins = [collectionBarMarkerPlugin]; + enum MapSideNavItemLabels { Download = 'Download', Filter = 'Filter', @@ -339,130 +341,66 @@ function MapAnalysisImpl(props: ImplProps) { { type: 'subheading', labelText: MapSideNavItemLabels.SingleVariableMaps, - children: [ - { - type: 'item', - id: 'single-variable-pie', - labelText: donutMarkerPlugin.displayName, - rightIcon: , - leftIcon: - activeMarkerConfigurationType === 'pie' ? : null, - onActive: () => { - setActiveMarkerConfigurationType('pie'); - }, - renderSidePanelDrawer(apps) { - return ( - - ); - }, - }, - { - type: 'item', - id: 'single-variable-bar', - labelText: barMarkerPlugin.displayName, - leftIcon: - activeMarkerConfigurationType === 'barplot' ? ( - - ) : null, - rightIcon: , - onActive: () => { - setActiveMarkerConfigurationType('barplot'); - }, - renderSidePanelDrawer(apps) { - return ( - - ); - }, - }, - { - type: 'item', - id: 'single-variable-bubble', - labelText: bubbleMarkerPlugin.displayName, - rightIcon: , - leftIcon: - activeMarkerConfigurationType === 'bubble' ? ( - - ) : null, - onActive: () => setActiveMarkerConfigurationType('bubble'), - renderSidePanelDrawer(apps) { - return ( - - ); - }, + children: singleVariablePlugins.map((plugin) => ({ + type: 'item', + id: plugin.type, + labelText: plugin.displayName, + rightIcon: , + leftIcon: + activeMarkerConfigurationType === plugin.type ? ( + + ) : null, + onActive: () => { + setActiveMarkerConfigurationType(plugin.type); }, - ], + renderSidePanelDrawer: (apps) => ( + + ), + })), }, { type: 'subheading', labelText: MapSideNavItemLabels.GroupedVariableMaps, - children: [ - { - type: 'item', - id: 'collection-var-bar', - labelText: collectionBarMarkerPlugin.displayName, - leftIcon: - activeMarkerConfigurationType === 'collection-barplot' ? ( - - ) : null, - rightIcon: , - onActive: () => { - setActiveMarkerConfigurationType('collection-barplot'); - }, - renderSidePanelDrawer(apps): EmotionJSX.Element { - return ( - - ); - }, + children: groupedVariablePlugins.map((plugin) => ({ + type: 'item', + id: `grouped-variable-${plugin.type}`, + labelText: plugin.displayName, + rightIcon: , + leftIcon: + activeMarkerConfigurationType === plugin.type ? ( + + ) : null, + onActive: () => { + setActiveMarkerConfigurationType(plugin.type); }, - ], + renderSidePanelDrawer: (apps) => ( + + ), + })), }, ], }, @@ -775,13 +713,12 @@ function MapAnalysisImpl(props: ImplProps) { // TODO Add `type` to plugin def and use to look up, so we can remove hard coded strings. const activeMapTypePlugin = - activeMarkerConfiguration?.type === 'barplot' - ? barMarkerPlugin - : activeMarkerConfiguration?.type === 'bubble' - ? bubbleMarkerPlugin - : activeMarkerConfiguration?.type === 'pie' - ? donutMarkerPlugin - : undefined; + singleVariablePlugins.find( + (plugin) => plugin.type === activeMarkerConfigurationType + ) ?? + groupedVariablePlugins.find( + (plugin) => plugin.type === activeMarkerConfigurationType + ); return ( diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarMarkerMapType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarMarkerMapType.tsx index de505fc51a..b3ba30b388 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarMarkerMapType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarMarkerMapType.tsx @@ -82,6 +82,8 @@ import { SubStudies } from '../../../SubStudies'; const displayName = 'Bar plots'; export const plugin: MapTypePlugin = { + type: 'barplot', + IconComponent: BarPlotMarkerIcon, displayName, getDefaultConfig({ defaultVariable }) { return { diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerMapType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerMapType.tsx index bbdb7d2e55..43397c370c 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerMapType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerMapType.tsx @@ -73,6 +73,8 @@ import { STUDIES_ENTITY_ID, STUDY_ID_VARIABLE_ID } from '../../../../constants'; const displayName = 'Bubbles'; export const plugin: MapTypePlugin = { + type: 'bubble', + IconComponent: BubbleMarkerIcon, displayName, getDefaultConfig({ defaultVariable }) { return { diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/DonutMarkerMapType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/DonutMarkerMapType.tsx index a2346e33ce..aac37bd106 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/DonutMarkerMapType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/DonutMarkerMapType.tsx @@ -64,7 +64,10 @@ import { MapTypeConfigurationMenu, MarkerConfigurationOption, } from '../../MarkerConfiguration/MapTypeConfigurationMenu'; -import { DonutMarkersIcon } from '../../MarkerConfiguration/icons'; +import { + DonutMarkerIcon, + DonutMarkersIcon, +} from '../../MarkerConfiguration/icons'; import { TabbedDisplayProps } from '@veupathdb/coreui/lib/components/grids/TabbedDisplay'; import MapVizManagement from '../../../MapVizManagement'; import Spinner from '@veupathdb/components/lib/components/Spinner'; @@ -76,6 +79,8 @@ import TimeSliderQuickFilter from '../../../TimeSliderQuickFilter'; const displayName = 'Donuts'; export const plugin: MapTypePlugin = { + type: 'pie', + IconComponent: DonutMarkerIcon, displayName, getDefaultConfig({ defaultVariable }) { return { diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts b/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts index 70cbff8841..a9eeb30e39 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts @@ -1,4 +1,4 @@ -import { ComponentType } from 'react'; +import { ComponentType, SVGProps } from 'react'; import { AnalysisState, Filter, @@ -57,10 +57,18 @@ export interface MapTypeMapLayerProps { * and configure a map type */ export interface MapTypePlugin { + /** + * Unique identifier for the map type + */ + type: T['type']; /** * Display name of map type used for menu, etc. */ displayName: string; + /** + * Icon component + */ + IconComponent: ComponentType>; /** * Returns a default configuration for this MapType. This is used to * create a set of default configurations for new analyses. From 19de54e309975c0874c0cb71e4d2e6a15c986a65 Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Wed, 6 Mar 2024 17:06:29 -0500 Subject: [PATCH 08/17] Use item.display for button label --- .../libs/coreui/src/components/inputs/SelectList.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/libs/coreui/src/components/inputs/SelectList.tsx b/packages/libs/coreui/src/components/inputs/SelectList.tsx index 1de1366795..ffd5cd57d1 100644 --- a/packages/libs/coreui/src/components/inputs/SelectList.tsx +++ b/packages/libs/coreui/src/components/inputs/SelectList.tsx @@ -42,9 +42,14 @@ export default function SelectList({ useEffect(() => { setSelected(value); setButtonDisplayContent( - value.length ? value.join(', ') : defaultButtonDisplayContent + value.length + ? items + .filter((item) => value.includes(item.value)) + .map((item) => item.display) + .join(', ') + : defaultButtonDisplayContent ); - }, [value, defaultButtonDisplayContent]); + }, [value, defaultButtonDisplayContent, items]); const buttonLabel = ( Date: Wed, 6 Mar 2024 17:06:48 -0500 Subject: [PATCH 09/17] WIP collection bar marker config --- .../CollectionBarMarkerType.tsx | 137 ++++++- ...llectionBarPlotMarkerConfigurationMenu.tsx | 336 ++++++++++++++++++ 2 files changed, 462 insertions(+), 11 deletions(-) create mode 100644 packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarPlotMarkerConfigurationMenu.tsx diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx index 74a2e0ee16..7b45796d7f 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx @@ -1,30 +1,57 @@ import { preorder } from '@veupathdb/wdk-client/lib/Utils/TreeUtils'; -import { VariableCollectionDescriptor } from '../../../../../core/types/variable'; -import { MapTypeConfigPanelProps, MapTypePlugin } from '../../types'; +import { + MapTypeConfigPanelProps, + MapTypeMapLayerProps, + MapTypePlugin, +} from '../../types'; +import { + CollectionVariableTreeNode, + StudyEntity, + useFindEntityAndVariableCollection, + useStudyEntities, +} from '../../../../../core'; +import { VariableCollectionSelectList } from '../../../../../core/components/variableSelectors/VariableCollectionSingleSelect'; +import { CheckboxList, SelectList } from '@veupathdb/coreui'; +import { Item } from '@veupathdb/coreui/lib/components/inputs/checkboxes/CheckboxList'; +import { DraggablePanel } from '@veupathdb/coreui/lib/components/containers'; +import { DraggableLegendPanel } from '../../../DraggableLegendPanel'; +import { MapLegend } from '../../../MapLegend'; +import { LegendItemsProps } from '@veupathdb/components/lib/components/plotControls/PlotListLegend'; +import { BarPlotMarkerIcon } from '../../MarkerConfiguration/icons'; const displayName = 'Bar plots'; interface CollectionBarMarkerConfiguration { type: 'collection-barplot'; - selectedCollection: VariableCollectionDescriptor; + entityId: string; + collectionId: string; + selectedVariableIds: string[]; } export const plugin: MapTypePlugin = { + type: 'collection-barplot', + IconComponent: BarPlotMarkerIcon, displayName, getDefaultConfig({ study }) { const firstCollection = Array.from( preorder(study.rootEntity, (e) => e.children ?? []) ) - .flatMap((e) => e.collections?.map((c) => [e, c]) ?? []) + .flatMap( + (e) => + e.collections?.map((c): [StudyEntity, CollectionVariableTreeNode] => [ + e, + c, + ]) ?? [] + ) .at(0); if (firstCollection == null) throw new Error('This study does not have any collections.'); + const [entity, collection] = firstCollection; return { type: 'collection-barplot', - selectedCollection: { - entityId: firstCollection[0].id, - collectionId: firstCollection[1].id, - }, + entityId: entity.id, + collectionId: collection.id, + selectedVariableIds: collection.memberVariableIds, }; }, ConfigPanelComponent, @@ -36,10 +63,59 @@ export const plugin: MapTypePlugin = { function ConfigPanelComponent( props: MapTypeConfigPanelProps ) { + const configuration = props.configuration as CollectionBarMarkerConfiguration; + const { updateConfiguration } = props; + const findEntityAndCollection = useFindEntityAndVariableCollection(); + const { entity, variableCollection } = + findEntityAndCollection({ + entityId: configuration.entityId, + collectionId: configuration.collectionId, + }) ?? {}; + const memberVariableIdSet = new Set(variableCollection?.memberVariableIds); + const variableCheckboxListItems = entity?.variables + .filter((v) => memberVariableIdSet.has(v.id)) + .map( + (variable): Item => ({ + display: variable.displayName, + value: variable.id, + }) + ); return (

I am a config component

-

Selected collection: {JSON.stringify(props.configuration)}

+ + +

+ Selected collection:{' '} +

{JSON.stringify(props.configuration, null, 2)}
+

); } @@ -48,8 +124,47 @@ function MapLayerComponent() { return
I am a map layer component
; } -function MapOverlayComponent() { - return
I am a map overlay component
; +function MapOverlayComponent( + props: MapTypeMapLayerProps +) { + const configuration = props.configuration as CollectionBarMarkerConfiguration; + const { headerButtons } = props; + + const findEntityAndCollection = useFindEntityAndVariableCollection(); + const { entity, variableCollection } = + findEntityAndCollection(configuration) ?? {}; + + const noDataError = null; + + const legendItems = entity?.variables + .filter((variable) => + configuration.selectedVariableIds.includes(variable.id) + ) + .map( + (variable): LegendItemsProps => ({ + label: variable.displayName, + marker: 'square', + hasData: true, + }) + ); + + return ( + +
+ {noDataError ?? ( + + )} +
+
+ ); } function MapTypeHeaderDetails() { diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarPlotMarkerConfigurationMenu.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarPlotMarkerConfigurationMenu.tsx new file mode 100644 index 0000000000..164dd23256 --- /dev/null +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarPlotMarkerConfigurationMenu.tsx @@ -0,0 +1,336 @@ +import { useCallback } from 'react'; +import { + InputVariables, + Props as InputVariablesProps, +} from '../../../../../core/components/visualizations/InputVariables'; +import RadioButtonGroup from '@veupathdb/components/lib/components/widgets/RadioButtonGroup'; +import { VariablesByInputName } from '../../../../../core/utils/data-element-constraints'; +import { + usePromise, + AllValuesDefinition, + OverlayConfig, + Variable, + Filter, +} from '../../../../../core'; +import { CategoricalMarkerConfigurationTable } from '../../MarkerConfiguration/CategoricalMarkerConfigurationTable'; +import { CategoricalMarkerPreview } from '../../MarkerConfiguration/CategoricalMarkerPreview'; +import Barplot from '@veupathdb/components/lib/plots/Barplot'; +import { SubsettingClient } from '../../../../../core/api'; +import { Toggle } from '@veupathdb/coreui'; +import { useUncontrolledSelections } from '../../../hooks/uncontrolledSelections'; +import { + BinningMethod, + SelectedCountsOption, + SelectedValues, +} from '../../../appState'; +import { gray } from '@veupathdb/coreui/lib/definitions/colors'; +import { SharedMarkerConfigurations } from '../../shared'; + +interface MarkerConfiguration { + type: T; +} + +export interface BarPlotMarkerConfiguration + extends MarkerConfiguration<'collection-barplot'>, + SharedMarkerConfigurations { + selectedPlotMode: 'count' | 'proportion'; + dependentAxisLogScale: boolean; + binningMethod: BinningMethod; + selectedValues: SelectedValues; + selectedCountsOption: SelectedCountsOption; +} + +interface Props + extends Omit< + InputVariablesProps, + 'onChange' | 'selectedVariables' | 'selectedPlotMode' | 'onPlotSelected' + > { + onChange: (configuration: BarPlotMarkerConfiguration) => void; + configuration: BarPlotMarkerConfiguration; + overlayConfiguration: OverlayConfig | undefined; + overlayVariable: Variable | undefined; + subsettingClient: SubsettingClient; + studyId: string; + filters: Filter[] | undefined; + continuousMarkerPreview: JSX.Element | undefined; + /** + * Always used for categorical marker preview. Also used in categorical table if selectedCountsOption is 'filtered' + */ + allFilteredCategoricalValues: AllValuesDefinition[] | undefined; + /** + * Only defined and used in categorical table if selectedCountsOption is 'visible' + */ + allVisibleCategoricalValues: AllValuesDefinition[] | undefined; +} + +// TODO: generalize this and PieMarkerConfigMenu into MarkerConfigurationMenu. Lots of code repetition... + +export function BarPlotMarkerConfigurationMenu({ + entities, + onChange, + starredVariables, + toggleStarredVariable, + configuration, + constraints, + overlayConfiguration, + overlayVariable, + subsettingClient, + studyId, + filters, + continuousMarkerPreview, + allFilteredCategoricalValues, + allVisibleCategoricalValues, +}: Props) { + /** + * Used to track the CategoricalMarkerConfigurationTable's selection state, which allows users to + * select more than the allowable limit. Doing so results in a message to the user that they've selected + * too many values. The state is lifted up (versus living in CategoricalMarkerConfigurationTable) in order + * to pass its length to CategoricalMarkerPreview. + */ + const { uncontrolledSelections, setUncontrolledSelections } = + useUncontrolledSelections( + overlayConfiguration?.overlayType === 'categorical' + ? overlayConfiguration?.overlayValues + : undefined + ); + + const barplotData = usePromise( + useCallback(async () => { + if ( + !overlayVariable || + overlayConfiguration?.overlayType !== 'continuous' || + !('distributionDefaults' in overlayVariable) + ) + return; + const binSpec = { + displayRangeMin: + overlayVariable.distributionDefaults.rangeMin + + (overlayVariable.type === 'date' ? 'T00:00:00Z' : ''), + displayRangeMax: + overlayVariable.distributionDefaults.rangeMax + + (overlayVariable.type === 'date' ? 'T00:00:00Z' : ''), + binWidth: overlayVariable.distributionDefaults.binWidth ?? 1, + binUnits: + 'binUnits' in overlayVariable.distributionDefaults + ? overlayVariable.distributionDefaults.binUnits + : undefined, + }; + const distributionResponse = await subsettingClient.getDistribution( + studyId, + configuration.selectedVariable.entityId, + configuration.selectedVariable.variableId, + { + valueSpec: 'count', + filters: filters ?? [], + binSpec, + } + ); + return { + name: '', + value: distributionResponse.histogram.map((d) => d.value), + label: distributionResponse.histogram.map((d) => d.binLabel), + showValues: false, + color: '#333', + }; + }, [ + studyId, + overlayVariable, + overlayConfiguration?.overlayType, + subsettingClient, + filters, + configuration.selectedVariable, + ]) + ); + + function handleInputVariablesOnChange(selection: VariablesByInputName) { + if (!selection.overlayVariable) { + console.error( + `Expected overlay to defined but got ${typeof selection.overlayVariable}` + ); + return; + } + + onChange({ + ...configuration, + selectedVariable: selection.overlayVariable, + selectedValues: undefined, + }); + } + function handlePlotModeSelection(option: string) { + onChange({ + ...configuration, + selectedPlotMode: + option as BarPlotMarkerConfiguration['selectedPlotMode'], + }); + } + function handleBinningMethodSelection(option: string) { + onChange({ + ...configuration, + binningMethod: option as BarPlotMarkerConfiguration['binningMethod'], + }); + } + function handleLogScaleChange(option: boolean) { + onChange({ + ...configuration, + dependentAxisLogScale: option, + }); + } + + return ( +
+

+ Color: +

+ {/* limit inputVariables width */} +
+ +
+
+
+ Summary marker (all filtered data) +
+ {overlayConfiguration?.overlayType === 'categorical' ? ( + <> + + + ) : ( + continuousMarkerPreview + )} +
+
+
+ Marker X-axis controls +
+ +
+
+
+ Marker Y-axis controls +
+ + +
+ {overlayConfiguration?.overlayType === 'categorical' && ( +
+ +
+ )} + {overlayConfiguration?.overlayType === 'continuous' && barplotData.value && ( +
+ + Raw distribution of overall filtered data + + +
+ )} +
+ ); +} From 6ba9fd0e6321a9e7be2d180fe6e2681cbca8392e Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Tue, 12 Mar 2024 13:48:51 -0400 Subject: [PATCH 10/17] Convenience exports --- packages/libs/coreui/src/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/libs/coreui/src/index.ts b/packages/libs/coreui/src/index.ts index 66c9e6bdd8..90641df52b 100644 --- a/packages/libs/coreui/src/index.ts +++ b/packages/libs/coreui/src/index.ts @@ -17,6 +17,10 @@ export { Card, ExpandablePanel, Modal } from './components/containers'; // Grids export { DataGrid, TabbedDisplay } from './components/grids'; +// Mesa table +export { Mesa } from './components/Mesa'; +export * from './components/Mesa/types'; + // Forms export { FormField, MultilineTextField } from './components/forms'; From 2772780a5f6a775d4371817f09ad991351264c19 Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Tue, 12 Mar 2024 13:49:05 -0400 Subject: [PATCH 11/17] WIP config menu --- .../CollectionBarMarkerType.tsx | 198 ++++++++++++++---- 1 file changed, 155 insertions(+), 43 deletions(-) diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx index 7b45796d7f..477af967a9 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx @@ -7,17 +7,18 @@ import { import { CollectionVariableTreeNode, StudyEntity, + Variable, useFindEntityAndVariableCollection, - useStudyEntities, } from '../../../../../core'; import { VariableCollectionSelectList } from '../../../../../core/components/variableSelectors/VariableCollectionSingleSelect'; -import { CheckboxList, SelectList } from '@veupathdb/coreui'; +import { SelectList } from '@veupathdb/coreui'; import { Item } from '@veupathdb/coreui/lib/components/inputs/checkboxes/CheckboxList'; -import { DraggablePanel } from '@veupathdb/coreui/lib/components/containers'; import { DraggableLegendPanel } from '../../../DraggableLegendPanel'; import { MapLegend } from '../../../MapLegend'; import { LegendItemsProps } from '@veupathdb/components/lib/components/plotControls/PlotListLegend'; import { BarPlotMarkerIcon } from '../../MarkerConfiguration/icons'; +import { difference, noop, union, uniq } from 'lodash'; +import { Mesa } from '@veupathdb/coreui'; const displayName = 'Bar plots'; @@ -26,6 +27,7 @@ interface CollectionBarMarkerConfiguration { entityId: string; collectionId: string; selectedVariableIds: string[]; + selectedValues: string[]; } export const plugin: MapTypePlugin = { @@ -47,11 +49,17 @@ export const plugin: MapTypePlugin = { if (firstCollection == null) throw new Error('This study does not have any collections.'); const [entity, collection] = firstCollection; + const selectedValues = entity.variables + .filter((variable): variable is Variable => + collection.memberVariableIds.includes(variable.id) + ) + .flatMap((variable) => variable.vocabulary ?? []); return { type: 'collection-barplot', entityId: entity.id, collectionId: collection.id, - selectedVariableIds: collection.memberVariableIds, + selectedVariableIds: collection.memberVariableIds.slice(0, 8), + selectedValues: union(selectedValues), }; }, ConfigPanelComponent, @@ -72,50 +80,154 @@ function ConfigPanelComponent( collectionId: configuration.collectionId, }) ?? {}; const memberVariableIdSet = new Set(variableCollection?.memberVariableIds); - const variableCheckboxListItems = entity?.variables - .filter((v) => memberVariableIdSet.has(v.id)) - .map( - (variable): Item => ({ - display: variable.displayName, - value: variable.id, - }) - ); + const variables = + entity?.variables.filter((v): v is Variable => + memberVariableIdSet.has(v.id) + ) ?? []; + const valueCheckboxListItems = union( + variables?.flatMap((v) => v.vocabulary ?? []) + ).map( + (value): Item => ({ + display: value, + value, + }) + ); return (
-

I am a config component

- - + Marker preview: +

+
TBD
+ +

-

- Selected collection:{' '} -

{JSON.stringify(props.configuration, null, 2)}
+ > + Color:

+
+
+ + + selectedVariableIds.includes(v.id) + ) + .flatMap((v) => v.vocabulary ?? []) + ); + + updateConfiguration({ + type: 'collection-barplot', + entityId: entity.id, + collectionId: variableCollection.id, + selectedVariableIds, + selectedValues, + }); + }} + /> +
+
+ + configuration.selectedVariableIds.includes(row.id), + }, + eventHandlers: { + onSearch: noop, + onSort: noop, + onRowSelect: (row) => { + updateConfiguration({ + ...configuration, + selectedVariableIds: union( + configuration.selectedVariableIds, + [row.id] + ), + }); + }, + onRowDeselect: (row) => { + updateConfiguration({ + ...configuration, + selectedVariableIds: difference( + configuration.selectedVariableIds, + [row.id] + ), + }); + }, + onMultipleRowDeselect: (rows) => { + updateConfiguration({ + ...configuration, + selectedVariableIds: difference( + configuration.selectedVariableIds, + rows.map((r) => r.id) + ), + }); + }, + onMultipleRowSelect: (rows) => { + updateConfiguration({ + ...configuration, + selectedVariableIds: union( + configuration.selectedVariableIds, + rows.map((r) => r.id) + ), + }); + }, + }, + }} + /> +
+
+ + +
+
); } From 7b9a0387c92bdc1a1df339a470e2ab75c4989a32 Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Mon, 8 Apr 2024 17:44:51 -0400 Subject: [PATCH 12/17] Add type argument --- packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx b/packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx index fce812b002..a72f618a44 100644 --- a/packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx +++ b/packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx @@ -29,7 +29,7 @@ interface Props { geoConfigs: GeoConfig[]; mapType?: MarkerConfiguration['type']; setHideVizInputsAndControls: (value: boolean) => void; - setIsSidePanelExpanded: MapTypeConfigPanelProps['setIsSidePanelExpanded']; + setIsSidePanelExpanded: MapTypeConfigPanelProps['setIsSidePanelExpanded']; } const mapVizManagementClassName = makeClassNameHelper('MapVizManagement'); From 3ab6db2b0e8374cb82563b9165d3d6e2cc28637a Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Tue, 30 Apr 2024 15:36:18 -0400 Subject: [PATCH 13/17] post merge changes --- packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx | 1 - packages/libs/eda/src/lib/map/analysis/appState.ts | 4 ++-- packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx b/packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx index 7643d9cfdc..05dbef032e 100644 --- a/packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx +++ b/packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx @@ -28,7 +28,6 @@ interface Props { // visualizationPlugins: Partial>; geoConfigs: GeoConfig[]; mapType?: MarkerConfiguration['type']; - setHideVizInputsAndControls: (value: boolean) => void; setIsSidePanelExpanded: MapTypeConfigPanelProps['setIsSidePanelExpanded']; } diff --git a/packages/libs/eda/src/lib/map/analysis/appState.ts b/packages/libs/eda/src/lib/map/analysis/appState.ts index af1a944ccc..8757f26420 100644 --- a/packages/libs/eda/src/lib/map/analysis/appState.ts +++ b/packages/libs/eda/src/lib/map/analysis/appState.ts @@ -27,12 +27,12 @@ export const defaultVisualizationPanelConfig = { const LatLngLiteral = t.type({ lat: t.number, lng: t.number }); -const PanelPositionConfig = t.type({ +export const PanelPositionConfig = t.type({ x: t.number, y: t.number, }); -const PanelConfig = t.intersection([ +export const PanelConfig = t.intersection([ t.type({ isVisible: t.boolean, position: PanelPositionConfig, diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts b/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts index 3c58ea6041..207c401924 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts @@ -23,7 +23,7 @@ export interface MapTypeConfigPanelProps { studyEntities: StudyEntity[]; geoConfigs: GeoConfig[]; configuration: unknown; - updateConfiguration: (configuration: unknown) => void; + updateConfiguration: (configuration: T) => void; setIsSidePanelExpanded: (isExpanded: boolean) => void; } From fa065973b7d6b33a0603b6d0a68da94fce57aa9c Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Wed, 1 May 2024 12:17:10 -0400 Subject: [PATCH 14/17] Remove circular dependencies --- .../lib/map/analysis/DraggableLegendPanel.tsx | 2 +- .../map/analysis/DraggableVisualization.tsx | 12 +- .../eda/src/lib/map/analysis/MapAnalysis.tsx | 78 +++--- .../src/lib/map/analysis/MapVizManagement.tsx | 2 +- .../eda/src/lib/map/analysis/SubStudies.tsx | 2 +- .../map/analysis/TimeSliderQuickFilter.tsx | 2 +- .../libs/eda/src/lib/map/analysis/Types.ts | 95 +++++++ .../libs/eda/src/lib/map/analysis/appState.ts | 235 +----------------- .../eda/src/lib/map/analysis/littleFilters.ts | 2 +- .../CategoricalMarkerConfigurationTable.tsx | 3 +- .../MapTypeConfigurationMenu.tsx | 2 +- .../MarkerConfigurationSelector.tsx | 2 +- .../plugins/barplot/BarMarkerMapType.tsx | 2 +- .../BarPlotMarkerConfigurationMenu.tsx | 5 +- .../bubble/BubbleMarkerConfigurationMenu.tsx | 2 +- .../plugins/bubble/BubbleMarkerMapType.tsx | 6 +- .../CollectionBarMarkerType.tsx | 7 +- ...llectionBarPlotMarkerConfigurationMenu.tsx | 6 +- .../plugins/donut/DonutMarkerMapType.tsx | 2 +- .../donut/PieMarkerConfigurationMenu.tsx | 7 +- .../src/lib/map/analysis/mapTypes/shared.tsx | 41 ++- .../src/lib/map/analysis/mapTypes/types.ts | 14 +- .../analysis/utils/defaultOverlayConfig.ts | 2 +- .../controllers/LegacyMapRedirectHandler.tsx | 2 +- 24 files changed, 211 insertions(+), 322 deletions(-) diff --git a/packages/libs/eda/src/lib/map/analysis/DraggableLegendPanel.tsx b/packages/libs/eda/src/lib/map/analysis/DraggableLegendPanel.tsx index 3fa28d493a..bdafca810f 100644 --- a/packages/libs/eda/src/lib/map/analysis/DraggableLegendPanel.tsx +++ b/packages/libs/eda/src/lib/map/analysis/DraggableLegendPanel.tsx @@ -1,7 +1,7 @@ import DraggablePanel, { DraggablePanelCoordinatePair, } from '@veupathdb/coreui/lib/components/containers/DraggablePanel'; -import { PanelConfig } from './appState'; +import { PanelConfig } from './Types'; export const DEFAULT_DRAGGABLE_LEGEND_POSITION = { x: window.innerWidth, diff --git a/packages/libs/eda/src/lib/map/analysis/DraggableVisualization.tsx b/packages/libs/eda/src/lib/map/analysis/DraggableVisualization.tsx index 7fea560a37..7d3de91a7c 100644 --- a/packages/libs/eda/src/lib/map/analysis/DraggableVisualization.tsx +++ b/packages/libs/eda/src/lib/map/analysis/DraggableVisualization.tsx @@ -12,17 +12,7 @@ import { Filter } from '../../core/types/filter'; import { FilledButton } from '@veupathdb/coreui'; import { DraggablePanel } from '@veupathdb/coreui/lib/components/containers'; import { ComputationPlugin } from '../../core/components/computations/Types'; -import { PanelConfig } from './appState'; - -export const DEFAULT_DRAGGABLE_VIZ_POSITION = { - x: 535, - y: 220, -}; - -export const DEFAULT_DRAGGABLE_VIZ_DIMENSIONS = { - width: 'auto', - height: 'auto', -}; +import { PanelConfig } from './Types'; interface Props { analysisState: AnalysisState; diff --git a/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx b/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx index f9387c3682..36a7573171 100755 --- a/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx +++ b/packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx @@ -1,5 +1,20 @@ import { useCallback, useMemo, useState } from 'react'; +import { EditLocation, InfoOutlined, Notes, Share } from '@material-ui/icons'; +import MapVEuMap from '@veupathdb/components/lib/map/MapVEuMap'; +import { + CheckIcon, + Download, + FilledButton, + Filter as FilterIcon, + FloatingButton, + H5, + Modal, + Plus, + Table, +} from '@veupathdb/coreui'; +import { useWdkService } from '@veupathdb/wdk-client/lib/Hooks/WdkServiceHook'; +import { useHistory, useLocation, useRouteMatch } from 'react-router'; import { AnalysisState, DEFAULT_ANALYSIS_NAME, @@ -16,72 +31,53 @@ import { useStudyRecord, useSubsettingClient, } from '../../core'; -import MapVEuMap from '@veupathdb/components/lib/map/MapVEuMap'; -import { useGeoConfig } from '../../core/hooks/geoConfig'; +import FilterChipList from '../../core/components/FilterChipList'; +import { VariableLinkConfig } from '../../core/components/VariableLink'; import { DocumentationContainer } from '../../core/components/docs/DocumentationContainer'; -import { - CheckIcon, - Download, - Plus, - FilledButton, - Filter as FilterIcon, - FloatingButton, - H5, - Table, - Modal, -} from '@veupathdb/coreui'; import { useEntityCounts } from '../../core/hooks/entityCounts'; +import { useGeoConfig } from '../../core/hooks/geoConfig'; +import { ComputationAppOverview } from '../../core/types/visualization'; import ShowHideVariableContextProvider from '../../core/utils/show-hide-variable-context'; -import { - AppState, - MarkerConfiguration, - useAppState, - LegacyRedirectState, -} from './appState'; +import NotesTab from '../../workspace/NotesTab'; import Subsetting from '../../workspace/Subsetting'; -import { MapHeader } from './MapHeader'; -import FilterChipList from '../../core/components/FilterChipList'; -import { VariableLinkConfig } from '../../core/components/VariableLink'; -import { MapSidePanel } from './MapSidePanel'; -import { EditLocation, InfoOutlined, Notes, Share } from '@material-ui/icons'; -import { ComputationAppOverview } from '../../core/types/visualization'; -import { useWdkService } from '@veupathdb/wdk-client/lib/Hooks/WdkServiceHook'; +import ConfirmShareAnalysis from '../../workspace/sharing/ConfirmShareAnalysis'; import Login from '../../workspace/sharing/Login'; -import { useLoginCallbacks } from '../../workspace/sharing/hooks'; import NameAnalysis from '../../workspace/sharing/NameAnalysis'; -import NotesTab from '../../workspace/NotesTab'; -import ConfirmShareAnalysis from '../../workspace/sharing/ConfirmShareAnalysis'; -import { useHistory, useLocation, useRouteMatch } from 'react-router'; +import { useLoginCallbacks } from '../../workspace/sharing/hooks'; +import { MapHeader } from './MapHeader'; +import { MapSidePanel } from './MapSidePanel'; +import { AppState, LegacyRedirectState, MarkerConfiguration } from './Types'; +import { useAppState } from './appState'; +import { getStudyId } from '@veupathdb/study-data-access/lib/shared/studies'; +import { RecordController } from '@veupathdb/wdk-client/lib/Controllers'; import { uniq } from 'lodash'; import Path from 'path'; -import DownloadTab from '../../workspace/DownloadTab'; -import { RecordController } from '@veupathdb/wdk-client/lib/Controllers'; -import { AllAnalyses } from '../../workspace/AllAnalyses'; -import { getStudyId } from '@veupathdb/study-data-access/lib/shared/studies'; -import { isSavedAnalysis } from '../../core/utils/analysis'; import { GeoConfig } from '../../core/types/geoConfig'; +import { isSavedAnalysis } from '../../core/utils/analysis'; +import { AllAnalyses } from '../../workspace/AllAnalyses'; +import DownloadTab from '../../workspace/DownloadTab'; +import { SideNavigationItems } from './MapSideNavigation'; import { SidePanelItem, SidePanelMenuEntry, SiteInformationProps, } from './Types'; -import { SideNavigationItems } from './MapSideNavigation'; import { - donutMarkerPlugin, barMarkerPlugin, bubbleMarkerPlugin, collectionBarMarkerPlugin, + donutMarkerPlugin, } from './mapTypes'; -import { MapTypeMapLayerProps } from './mapTypes/types'; import { defaultViewport } from '@veupathdb/components/lib/map/config/map'; -import AnalysisNameDialog from '../../workspace/AnalysisNameDialog'; +import SettingsButton from '@veupathdb/coreui/lib/components/containers/DraggablePanel/SettingsButton'; +import useSnackbar from '@veupathdb/coreui/lib/components/notifications/useSnackbar'; import { Page } from '@veupathdb/wdk-client/lib/Components'; import { AnalysisError } from '../../core/components/AnalysisError'; -import useSnackbar from '@veupathdb/coreui/lib/components/notifications/useSnackbar'; -import SettingsButton from '@veupathdb/coreui/lib/components/containers/DraggablePanel/SettingsButton'; import { getGeoConfig } from '../../core/utils/geoVariables'; +import AnalysisNameDialog from '../../workspace/AnalysisNameDialog'; +import { MapTypeMapLayerProps } from './mapTypes/types'; const singleVariablePlugins = [ donutMarkerPlugin, diff --git a/packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx b/packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx index 05dbef032e..6d4f858996 100644 --- a/packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx +++ b/packages/libs/eda/src/lib/map/analysis/MapVizManagement.tsx @@ -13,7 +13,7 @@ import { useVizIconColors } from '../../core/components/visualizations/implement import { GeoConfig } from '../../core/types/geoConfig'; import { ComputationAppOverview } from '../../core/types/visualization'; import './MapVizManagement.scss'; -import { MarkerConfiguration } from './appState'; +import { MarkerConfiguration } from './Types'; import { ComputationPlugin } from '../../core/components/computations/Types'; import { VisualizationPlugin } from '../../core/components/visualizations/VisualizationPlugin'; import { StartPage } from '../../core/components/computations/StartPage'; diff --git a/packages/libs/eda/src/lib/map/analysis/SubStudies.tsx b/packages/libs/eda/src/lib/map/analysis/SubStudies.tsx index e2954b8465..53a17891ff 100644 --- a/packages/libs/eda/src/lib/map/analysis/SubStudies.tsx +++ b/packages/libs/eda/src/lib/map/analysis/SubStudies.tsx @@ -4,10 +4,10 @@ import { usePermissions } from '@veupathdb/study-data-access/lib/data-restrictio import { useMemo } from 'react'; import { Link } from 'react-router-dom'; import { DraggablePanel } from '@veupathdb/coreui/lib/components/containers'; -import { PanelConfig } from './appState'; import { useDebouncedCallback } from '../../core/hooks/debouncing'; import Spinner from '@veupathdb/components/lib/components/Spinner'; import Banner from '@veupathdb/coreui/lib/components/banners/Banner'; +import { PanelConfig } from './Types'; interface Props { studyId: string; diff --git a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx index c4a46cb075..b922251a19 100755 --- a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx +++ b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx @@ -12,7 +12,7 @@ import { VariableDescriptor } from '../../core/types/variable'; import Spinner from '@veupathdb/components/lib/components/Spinner'; import { useFindEntityAndVariable, Filter } from '../../core'; import { zip } from 'lodash'; -import { AppState } from './appState'; +import { AppState } from './Types'; import { timeSliderVariableConstraints } from './config/eztimeslider'; import { useUITheme } from '@veupathdb/coreui/lib/components/theming'; import HelpIcon from '@veupathdb/wdk-client/lib/Components/Icon/HelpIcon'; diff --git a/packages/libs/eda/src/lib/map/analysis/Types.ts b/packages/libs/eda/src/lib/map/analysis/Types.ts index 80d375cc25..e0d87854e2 100644 --- a/packages/libs/eda/src/lib/map/analysis/Types.ts +++ b/packages/libs/eda/src/lib/map/analysis/Types.ts @@ -1,5 +1,7 @@ +import * as t from 'io-ts'; import { ReactNode } from 'react'; import { ComputationAppOverview } from '../../core/types/visualization'; +import { VariableDescriptor } from '../../core/types/variable'; export type SidePanelMenuEntry = | SidePanelItem @@ -35,3 +37,96 @@ export interface SiteInformationProps { siteName: string; siteLogoSrc: string; } + +export const PanelPositionConfig = t.type({ + x: t.number, + y: t.number, +}); + +export const PanelConfig = t.intersection([ + t.type({ + isVisible: t.boolean, + position: PanelPositionConfig, + dimensions: t.type({ + height: t.union([t.number, t.string]), + width: t.union([t.number, t.string]), + }), + }), + t.partial({ + hideVizControl: t.boolean, + }), +]); + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type PanelConfig = t.TypeOf; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type PanelPositionConfig = t.TypeOf; + +const LatLngLiteral = t.type({ lat: t.number, lng: t.number }); + +export type MarkerConfiguration = t.TypeOf; +// TODO Make `uknown` and use plugin-specific decoder +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const MarkerConfiguration = t.intersection([ + t.type({ + type: t.string, + }), + t.partial({ + selectedMarkers: t.array(t.string), + selectedVariable: VariableDescriptor, + activeVisualizationId: t.string, + geoEntityId: t.string, + }), +]); + +export type LegacyRedirectState = t.TypeOf; +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const LegacyRedirectState = t.union([ + t.undefined, + t.type({ + projectId: t.union([t.string, t.undefined]), + showLegacyMapRedirectModal: t.boolean, + }), +]); + +export const AppState = t.intersection([ + t.type({ + viewport: t.type({ + center: t.tuple([t.number, t.number]), + zoom: t.number, + }), + activeMarkerConfigurationType: t.string, + markerConfigurations: t.array(MarkerConfiguration), + isSidePanelExpanded: t.boolean, + }), + t.partial({ + studyDetailsPanelConfig: PanelConfig, + boundsZoomLevel: t.type({ + zoomLevel: t.number, + bounds: t.type({ + southWest: LatLngLiteral, + northEast: LatLngLiteral, + }), + }), + subsetVariableAndEntity: t.partial({ + entityId: t.string, + variableId: t.string, + }), + isSubsetPanelOpen: t.boolean, + timeSliderConfig: t.type({ + variable: t.union([VariableDescriptor, t.undefined]), + selectedRange: t.union([ + t.type({ + start: t.string, + end: t.string, + }), + t.undefined, + ]), + active: t.boolean, + }), + }), +]); + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type AppState = t.TypeOf; diff --git a/packages/libs/eda/src/lib/map/analysis/appState.ts b/packages/libs/eda/src/lib/map/analysis/appState.ts index 8757f26420..ca21d49847 100644 --- a/packages/libs/eda/src/lib/map/analysis/appState.ts +++ b/packages/libs/eda/src/lib/map/analysis/appState.ts @@ -8,205 +8,11 @@ import { useGetDefaultVariableDescriptor, useStudyMetadata, } from '../../core'; -import { VariableDescriptor } from '../../core/types/variable'; import { useGetDefaultTimeVariableDescriptor } from './hooks/eztimeslider'; import { defaultViewport } from '@veupathdb/components/lib/map/config/map'; import * as plugins from './mapTypes'; import { STUDIES_ENTITY_ID, STUDY_ID_VARIABLE_ID } from '../constants'; -import { - DEFAULT_DRAGGABLE_VIZ_DIMENSIONS, - DEFAULT_DRAGGABLE_VIZ_POSITION, -} from './DraggableVisualization'; - -export const defaultVisualizationPanelConfig = { - isVisible: false, - hideVizControl: false, - position: DEFAULT_DRAGGABLE_VIZ_POSITION, - dimensions: DEFAULT_DRAGGABLE_VIZ_DIMENSIONS, -}; - -const LatLngLiteral = t.type({ lat: t.number, lng: t.number }); - -export const PanelPositionConfig = t.type({ - x: t.number, - y: t.number, -}); - -export const PanelConfig = t.intersection([ - t.type({ - isVisible: t.boolean, - position: PanelPositionConfig, - dimensions: t.type({ - height: t.union([t.number, t.string]), - width: t.union([t.number, t.string]), - }), - }), - t.partial({ - hideVizControl: t.boolean, - }), -]); - -// eslint-disable-next-line @typescript-eslint/no-redeclare -export type PanelConfig = t.TypeOf; - -// eslint-disable-next-line @typescript-eslint/no-redeclare -export type PanelPositionConfig = t.TypeOf; - -/* -const MarkerType = t.keyof({ - barplot: null, - pie: null, - bubble: null, -}); - - -const BubbleLegendPositionConfig = t.type({ - variable: PanelPositionConfig, - count: PanelPositionConfig, -}); - -// eslint-disable-next-line @typescript-eslint/no-redeclare -export type BubbleLegendPositionConfig = t.TypeOf< - typeof BubbleLegendPositionConfig ->; -*/ - -// user-specified selection -export type SelectedValues = t.TypeOf; -// eslint-disable-next-line @typescript-eslint/no-redeclare -const SelectedValues = t.union([t.array(t.string), t.undefined]); - -export type BinningMethod = t.TypeOf; -// eslint-disable-next-line @typescript-eslint/no-redeclare -const BinningMethod = t.union([ - t.literal('equalInterval'), - t.literal('quantile'), - t.literal('standardDeviation'), - t.undefined, -]); - -export type SelectedCountsOption = t.TypeOf; -// eslint-disable-next-line @typescript-eslint/no-redeclare -const SelectedCountsOption = t.union([ - t.literal('filtered'), - t.literal('visible'), - t.undefined, -]); - -export type MarkerConfiguration = t.TypeOf; -// TODO Make `uknown` and use plugin-specific decoder -// eslint-disable-next-line @typescript-eslint/no-redeclare -export const MarkerConfiguration = t.intersection([ - t.type({ - type: t.string, - }), - t.partial({ - selectedMarkers: t.array(t.string), - selectedVariable: VariableDescriptor, - activeVisualizationId: t.string, - geoEntityId: t.string, - }), - /* - t.union([ - t.intersection([ - t.type({ - type: t.literal('barplot'), - selectedValues: SelectedValues, - selectedPlotMode: t.union([ - t.literal('count'), - t.literal('proportion'), - ]), - binningMethod: BinningMethod, - dependentAxisLogScale: t.boolean, - selectedCountsOption: SelectedCountsOption, - legendPanelConfig: PanelPositionConfig, - visualizationPanelConfig: PanelConfig, - }), - t.partial({ - // yes all the modes have selectedMarkers but maybe in the future one won't - selectedMarkers: t.array(t.string), - }), - ]), - t.intersection([ - t.type({ - type: t.literal('pie'), - selectedValues: SelectedValues, - binningMethod: BinningMethod, - selectedCountsOption: SelectedCountsOption, - legendPanelConfig: PanelPositionConfig, - visualizationPanelConfig: PanelConfig, - }), - t.partial({ - selectedMarkers: t.array(t.string), - }), - ]), - t.intersection([ - t.type({ - type: t.literal('bubble'), - }), - t.partial({ - aggregator: t.union([t.literal('mean'), t.literal('median')]), - numeratorValues: t.union([t.array(t.string), t.undefined]), - denominatorValues: t.union([t.array(t.string), t.undefined]), - selectedMarkers: t.array(t.string), - legendPanelConfig: BubbleLegendPositionConfig, - visualizationPanelConfig: PanelConfig, - }), - ]), - ]), - */ -]); - -export type LegacyRedirectState = t.TypeOf; -// eslint-disable-next-line @typescript-eslint/no-redeclare -export const LegacyRedirectState = t.union([ - t.undefined, - t.type({ - projectId: t.union([t.string, t.undefined]), - showLegacyMapRedirectModal: t.boolean, - }), -]); - -export const AppState = t.intersection([ - t.type({ - viewport: t.type({ - center: t.tuple([t.number, t.number]), - zoom: t.number, - }), - activeMarkerConfigurationType: t.string, - markerConfigurations: t.array(MarkerConfiguration), - isSidePanelExpanded: t.boolean, - }), - t.partial({ - studyDetailsPanelConfig: PanelConfig, - boundsZoomLevel: t.type({ - zoomLevel: t.number, - bounds: t.type({ - southWest: LatLngLiteral, - northEast: LatLngLiteral, - }), - }), - subsetVariableAndEntity: t.partial({ - entityId: t.string, - variableId: t.string, - }), - isSubsetPanelOpen: t.boolean, - timeSliderConfig: t.type({ - variable: t.union([VariableDescriptor, t.undefined]), - selectedRange: t.union([ - t.type({ - start: t.string, - end: t.string, - }), - t.undefined, - ]), - active: t.boolean, - }), - }), -]); - -// eslint-disable-next-line @typescript-eslint/no-redeclare -export type AppState = t.TypeOf; +import { AppState } from './Types'; export function useAppState( uiStateKey: string, @@ -272,45 +78,6 @@ export function useAppState( }, } : {}), - /* - markerConfigurations: [ - { - type: 'pie', - selectedVariable: defaultVariable, - selectedValues: undefined, - binningMethod: undefined, - selectedCountsOption: 'filtered', - legendPanelConfig: DEFAULT_DRAGGABLE_LEGEND_POSITION, - visualizationPanelConfig: defaultVisualizationPanelConfig, - }, - { - type: 'barplot', - selectedPlotMode: 'count', - selectedVariable: defaultVariable, - selectedValues: undefined, - binningMethod: undefined, - dependentAxisLogScale: false, - selectedCountsOption: 'filtered', - legendPanelConfig: DEFAULT_DRAGGABLE_LEGEND_POSITION, - visualizationPanelConfig: defaultVisualizationPanelConfig, - }, - { - type: 'bubble', - selectedVariable: defaultVariable, - aggregator: 'mean', - numeratorValues: undefined, - denominatorValues: undefined, - legendPanelConfig: { - variable: DEFAULT_DRAGGABLE_LEGEND_POSITION, - count: { - x: window.innerWidth, - y: 420, - }, - }, - visualizationPanelConfig: defaultVisualizationPanelConfig, - }, - ], - */ }), [defaultTimeVariable, isMegaStudy, defaultVariable, studyMetadata] ); diff --git a/packages/libs/eda/src/lib/map/analysis/littleFilters.ts b/packages/libs/eda/src/lib/map/analysis/littleFilters.ts index 87a0adb5b2..b8a0ab7486 100644 --- a/packages/libs/eda/src/lib/map/analysis/littleFilters.ts +++ b/packages/libs/eda/src/lib/map/analysis/littleFilters.ts @@ -5,7 +5,7 @@ import { useFindEntityAndVariable, } from '../../core'; import { useMemo } from 'react'; -import { AppState } from './appState'; +import { AppState } from './Types'; import { GeoConfig } from '../../core/types/geoConfig'; import { useDeepValue } from '../../core/hooks/immutability'; import { VariableDescriptor } from '../../core/types/variable'; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/CategoricalMarkerConfigurationTable.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/CategoricalMarkerConfigurationTable.tsx index 9abc4ec87b..9c66280c3e 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/CategoricalMarkerConfigurationTable.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/CategoricalMarkerConfigurationTable.tsx @@ -10,9 +10,8 @@ import { ColorPaletteDefault } from '@veupathdb/components/lib/types/plots'; import RadioButtonGroup from '@veupathdb/components/lib/components/widgets/RadioButtonGroup'; import { UNSELECTED_TOKEN } from '../../../constants'; import { orderBy } from 'lodash'; -import { SelectedCountsOption } from '../../appState'; import Spinner from '@veupathdb/components/lib/components/Spinner'; -import { SharedMarkerConfigurations } from '../../mapTypes/shared'; +import { SelectedCountsOption, SharedMarkerConfigurations } from '../shared'; type Props = { overlayValues: string[]; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MapTypeConfigurationMenu.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MapTypeConfigurationMenu.tsx index 153d51c11d..0394dfae11 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MapTypeConfigurationMenu.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MapTypeConfigurationMenu.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { H5 } from '@veupathdb/coreui'; -import { MarkerConfiguration } from '../../appState'; +import { MarkerConfiguration } from '../../Types'; import TabbedDisplay, { TabbedDisplayProps, } from '@veupathdb/coreui/lib/components/grids/TabbedDisplay'; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MarkerConfigurationSelector.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MarkerConfigurationSelector.tsx index 7eed0d366a..38d279713c 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MarkerConfigurationSelector.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/MarkerConfiguration/MarkerConfigurationSelector.tsx @@ -1,6 +1,6 @@ import { H6 } from '@veupathdb/coreui'; import { useUITheme } from '@veupathdb/coreui/lib/components/theming'; -import { MarkerConfiguration } from '../../appState'; +import { MarkerConfiguration } from '../../Types'; export interface MarkerConfigurationOption { displayName: string; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarMarkerMapType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarMarkerMapType.tsx index fa7a5632ff..1fb856e38b 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarMarkerMapType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarMarkerMapType.tsx @@ -57,6 +57,7 @@ import { getLegendErrorMessage, selectedMarkersLittleFilter, useFloatingPanelHandlers, + defaultVisualizationPanelConfig, } from '../../shared'; import { useFindEntityAndVariable, @@ -83,7 +84,6 @@ import { useLittleFilters } from '../../../littleFilters'; import TimeSliderQuickFilter from '../../../TimeSliderQuickFilter'; import { MapTypeHeaderStudyDetails } from '../../MapTypeHeaderStudyDetails'; import { SubStudies } from '../../../SubStudies'; -import { defaultVisualizationPanelConfig } from '../../../appState'; const displayName = 'Bar plots'; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarPlotMarkerConfigurationMenu.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarPlotMarkerConfigurationMenu.tsx index 410da035f2..6432961ef5 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarPlotMarkerConfigurationMenu.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/barplot/BarPlotMarkerConfigurationMenu.tsx @@ -20,15 +20,14 @@ import { Toggle } from '@veupathdb/coreui'; import { useUncontrolledSelections } from '../../../hooks/uncontrolledSelections'; import { BinningMethod, - PanelConfig, - PanelPositionConfig, SelectedCountsOption, SelectedValues, -} from '../../../appState'; +} from '../../shared'; import { gray } from '@veupathdb/coreui/lib/definitions/colors'; import { SharedMarkerConfigurations } from '../../shared'; import { GeoConfig } from '../../../../../core/types/geoConfig'; import { findLeastAncestralGeoConfig } from '../../../../../core/utils/geoVariables'; +import { PanelConfig, PanelPositionConfig } from '../../../Types'; interface MarkerConfiguration { type: T; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerConfigurationMenu.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerConfigurationMenu.tsx index 5c19394364..85b96852ec 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerConfigurationMenu.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerConfigurationMenu.tsx @@ -15,9 +15,9 @@ import { import { DataElementConstraint } from '../../../../../core/types/visualization'; // TO DO for dates: remove import { SharedMarkerConfigurations } from '../../shared'; import { invalidProportionText } from '../../../utils/defaultOverlayConfig'; -import { PanelConfig, PanelPositionConfig } from '../../../appState'; import { GeoConfig } from '../../../../../core/types/geoConfig'; import { findLeastAncestralGeoConfig } from '../../../../../core/utils/geoVariables'; +import { PanelConfig, PanelPositionConfig } from '../../../Types'; type AggregatorOption = typeof aggregatorOptions[number]; const aggregatorOptions = ['mean', 'median'] as const; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerMapType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerMapType.tsx index 999b624190..71e32a00a3 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerMapType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/bubble/BubbleMarkerMapType.tsx @@ -53,6 +53,7 @@ import { useSelectedMarkerSnackbars, selectedMarkersLittleFilter, useFloatingPanelHandlers, + defaultVisualizationPanelConfig, } from '../../shared'; import { MapTypeConfigPanelProps, @@ -73,10 +74,7 @@ import TimeSliderQuickFilter from '../../../TimeSliderQuickFilter'; import { SubStudies } from '../../../SubStudies'; import { MapTypeHeaderStudyDetails } from '../../MapTypeHeaderStudyDetails'; import { STUDIES_ENTITY_ID, STUDY_ID_VARIABLE_ID } from '../../../../constants'; -import { - PanelConfig, - defaultVisualizationPanelConfig, -} from '../../../appState'; +import { PanelConfig } from '../../../Types'; const displayName = 'Bubbles'; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx index 477af967a9..33e693aceb 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx @@ -121,9 +121,12 @@ function ConfigPanelComponent( collectionId: configuration.collectionId, }} onSelect={function ( - value?: { entityId: string; collectionId: string } | undefined + value?: + | { entityId: string; collectionId: string } + | string + | undefined ): void { - if (value == null) return; + if (value == null || typeof value === 'string') return; const { entity, variableCollection } = findEntityAndCollection(value) ?? {}; if (entity == null || variableCollection == null) return; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarPlotMarkerConfigurationMenu.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarPlotMarkerConfigurationMenu.tsx index 164dd23256..bc87fda65d 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarPlotMarkerConfigurationMenu.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarPlotMarkerConfigurationMenu.tsx @@ -18,13 +18,13 @@ import Barplot from '@veupathdb/components/lib/plots/Barplot'; import { SubsettingClient } from '../../../../../core/api'; import { Toggle } from '@veupathdb/coreui'; import { useUncontrolledSelections } from '../../../hooks/uncontrolledSelections'; +import { gray } from '@veupathdb/coreui/lib/definitions/colors'; import { BinningMethod, SelectedCountsOption, SelectedValues, -} from '../../../appState'; -import { gray } from '@veupathdb/coreui/lib/definitions/colors'; -import { SharedMarkerConfigurations } from '../../shared'; + SharedMarkerConfigurations, +} from '../../shared'; interface MarkerConfiguration { type: T; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/DonutMarkerMapType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/DonutMarkerMapType.tsx index 5a5120e373..31e35d1253 100755 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/DonutMarkerMapType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/DonutMarkerMapType.tsx @@ -56,6 +56,7 @@ import { useSelectedMarkerSnackbars, selectedMarkersLittleFilter, useFloatingPanelHandlers, + defaultVisualizationPanelConfig, } from '../../shared'; import { MapTypeConfigPanelProps, @@ -79,7 +80,6 @@ import { MapTypeHeaderStudyDetails } from '../../MapTypeHeaderStudyDetails'; import { SubStudies } from '../../../SubStudies'; import { useLittleFilters } from '../../../littleFilters'; import TimeSliderQuickFilter from '../../../TimeSliderQuickFilter'; -import { defaultVisualizationPanelConfig } from '../../../appState'; const displayName = 'Donuts'; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/PieMarkerConfigurationMenu.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/PieMarkerConfigurationMenu.tsx index a9a54a2d84..1e6ca89373 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/PieMarkerConfigurationMenu.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/donut/PieMarkerConfigurationMenu.tsx @@ -19,14 +19,13 @@ import RadioButtonGroup from '@veupathdb/components/lib/components/widgets/Radio import { useUncontrolledSelections } from '../../../hooks/uncontrolledSelections'; import { BinningMethod, - PanelConfig, - PanelPositionConfig, SelectedCountsOption, SelectedValues, -} from '../../../appState'; -import { SharedMarkerConfigurations } from '../../shared'; + SharedMarkerConfigurations, +} from '../../shared'; import { GeoConfig } from '../../../../../core/types/geoConfig'; import { findLeastAncestralGeoConfig } from '../../../../../core/utils/geoVariables'; +import { PanelConfig, PanelPositionConfig } from '../../../Types'; interface MarkerConfiguration { type: T; diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/shared.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/shared.tsx index f683f9d940..3916c97713 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/shared.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/shared.tsx @@ -42,11 +42,11 @@ import { useCallback, useState } from 'react'; import useSnackbar from '@veupathdb/coreui/lib/components/notifications/useSnackbar'; import { PieMarkerConfiguration } from './plugins/donut/PieMarkerConfigurationMenu'; import { BarPlotMarkerConfiguration } from './plugins/barplot/BarPlotMarkerConfigurationMenu'; -import { PanelConfig, PanelPositionConfig } from '../appState'; import { findLeastAncestralGeoConfig, getGeoConfig, } from '../../../core/utils/geoVariables'; +import { PanelConfig, PanelPositionConfig } from '../Types'; export const defaultAnimation = { method: 'geohash', @@ -62,6 +62,45 @@ const BubbleLegendPositionConfig = t.type({ // eslint-disable-next-line @typescript-eslint/no-redeclare type BubbleLegendPositionConfig = t.TypeOf; +export const DEFAULT_DRAGGABLE_VIZ_POSITION = { + x: 535, + y: 220, +}; + +export const DEFAULT_DRAGGABLE_VIZ_DIMENSIONS = { + width: 'auto', + height: 'auto', +}; + +export const defaultVisualizationPanelConfig = { + isVisible: false, + hideVizControl: false, + position: DEFAULT_DRAGGABLE_VIZ_POSITION, + dimensions: DEFAULT_DRAGGABLE_VIZ_DIMENSIONS, +}; + +// user-specified selection +export type SelectedValues = t.TypeOf; +// eslint-disable-next-line @typescript-eslint/no-redeclare +const SelectedValues = t.union([t.array(t.string), t.undefined]); + +export type BinningMethod = t.TypeOf; +// eslint-disable-next-line @typescript-eslint/no-redeclare +const BinningMethod = t.union([ + t.literal('equalInterval'), + t.literal('quantile'), + t.literal('standardDeviation'), + t.undefined, +]); + +export type SelectedCountsOption = t.TypeOf; +// eslint-disable-next-line @typescript-eslint/no-redeclare +const SelectedCountsOption = t.union([ + t.literal('filtered'), + t.literal('visible'), + t.undefined, +]); + export const markerDataFilterFuncs = [timeSliderLittleFilter]; export const floaterFilterFuncs = [ timeSliderLittleFilter, diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts b/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts index 207c401924..b3150ae005 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/types.ts @@ -1,3 +1,4 @@ +import { Bounds as BoundsProp } from '@veupathdb/components/lib/map/Types'; import { ComponentType, SVGProps } from 'react'; import { AnalysisState, @@ -6,13 +7,16 @@ import { StudyEntity, StudyMetadata, } from '../../../core'; -import { GeoConfig } from '../../../core/types/geoConfig'; -import { ComputationAppOverview } from '../../../core/types/visualization'; -import { AppState, MarkerConfiguration, PanelConfig } from '../appState'; import { EntityCounts } from '../../../core/hooks/entityCounts'; +import { GeoConfig } from '../../../core/types/geoConfig'; import { VariableDescriptor } from '../../../core/types/variable'; -import { SiteInformationProps } from '../Types'; -import { Bounds as BoundsProp } from '@veupathdb/components/lib/map/Types'; +import { ComputationAppOverview } from '../../../core/types/visualization'; +import { + AppState, + MarkerConfiguration, + PanelConfig, + SiteInformationProps, +} from '../Types'; export interface MapTypeConfigPanelProps { apps: ComputationAppOverview[]; diff --git a/packages/libs/eda/src/lib/map/analysis/utils/defaultOverlayConfig.ts b/packages/libs/eda/src/lib/map/analysis/utils/defaultOverlayConfig.ts index 0c7b98ac81..5d82e6ae67 100644 --- a/packages/libs/eda/src/lib/map/analysis/utils/defaultOverlayConfig.ts +++ b/packages/libs/eda/src/lib/map/analysis/utils/defaultOverlayConfig.ts @@ -11,8 +11,8 @@ import { Variable, } from '../../../core'; import { DataClient, SubsettingClient } from '../../../core/api'; -import { BinningMethod } from '../appState'; import { BubbleMarkerConfiguration } from '../mapTypes/plugins/bubble/BubbleMarkerConfigurationMenu'; +import { BinningMethod } from '../mapTypes/shared'; // This async function fetches the default overlay config. // For continuous variables, this involves calling the filter-aware-metadata/continuous-variable diff --git a/packages/libs/web-common/src/controllers/LegacyMapRedirectHandler.tsx b/packages/libs/web-common/src/controllers/LegacyMapRedirectHandler.tsx index d39ab124e5..34e99d1b82 100644 --- a/packages/libs/web-common/src/controllers/LegacyMapRedirectHandler.tsx +++ b/packages/libs/web-common/src/controllers/LegacyMapRedirectHandler.tsx @@ -5,7 +5,7 @@ import { useConfiguredAnalysisClient } from '@veupathdb/eda/lib/core/hooks/clien import { createComputation } from '@veupathdb/eda/lib/core/components/computations/Utils'; import { makeNewAnalysis } from '@veupathdb/eda/lib/core'; import { RouteComponentProps } from 'react-router'; -import { LegacyRedirectState } from '@veupathdb/eda/lib/map/analysis/appState'; +import { LegacyRedirectState } from '@veupathdb/eda/lib/map/analysis/Types'; // Define constants to create new computations and analyses const MEGA_STUDY_ID = 'DS_480c976ef9'; From e3a20536d0a30ab6900079afd6ba95c19201e5c0 Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Mon, 20 May 2024 14:36:27 -0400 Subject: [PATCH 15/17] Add API method and types for collection markers --- .../eda/src/lib/core/api/DataClient/index.ts | 14 ++++ .../eda/src/lib/core/api/DataClient/types.ts | 64 ++++++++++++++++++- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/packages/libs/eda/src/lib/core/api/DataClient/index.ts b/packages/libs/eda/src/lib/core/api/DataClient/index.ts index 49a0cfca07..cd1f2007c4 100644 --- a/packages/libs/eda/src/lib/core/api/DataClient/index.ts +++ b/packages/libs/eda/src/lib/core/api/DataClient/index.ts @@ -34,6 +34,8 @@ import { ContinousVariableMetadataResponse, StandaloneMapBubblesLegendRequestParams, StandaloneMapBubblesLegendResponse, + StandaloneCollectionsMarkerDataRequest, + StandaloneCollectionsMarkerDataResponse, } from './types'; import { NoDataError } from './NoDataError'; @@ -219,6 +221,18 @@ export default class DataClient extends FetchClientWithCredentials { ); } + getStandaloneCollectionsMarkerData( + computationName: string, + params: StandaloneCollectionsMarkerDataRequest + ): Promise { + return this.getVisualizationData( + computationName, + 'map-markers/collections', + params, + StandaloneCollectionsMarkerDataResponse + ); + } + // filter-aware continuous overlay variable metadata getContinousVariableMetadata( params: ContinousVariableMetadataRequestParams diff --git a/packages/libs/eda/src/lib/core/api/DataClient/types.ts b/packages/libs/eda/src/lib/core/api/DataClient/types.ts index 4547887010..59f3684220 100755 --- a/packages/libs/eda/src/lib/core/api/DataClient/types.ts +++ b/packages/libs/eda/src/lib/core/api/DataClient/types.ts @@ -23,8 +23,13 @@ import { TimeUnit, NumberOrNull, } from '../../types/general'; -import { VariableDescriptor, StringVariableValue } from '../../types/variable'; +import { + VariableDescriptor, + StringVariableValue, + VariableCollectionDescriptor, +} from '../../types/variable'; import { ComputationAppOverview } from '../../types/visualization'; +import { DerivedVariable } from '../../types/analysis'; export const AppsResponse = type({ apps: array(ComputationAppOverview), @@ -970,6 +975,63 @@ export const StandaloneMapBubblesLegendResponse = type({ maxSizeValue: number, }); +export interface StandaloneCollectionsMarkerDataRequest { + studyId: string; + filters?: Filter[]; + derivedVariables?: DerivedVariable[]; + config: { + outputEntityId: string; + geoAggregateVariable: VariableDescriptor; + longitudeVariable: VariableDescriptor; + latitudeVariable: VariableDescriptor; + viewport: LatLonViewport; + collectionOverlay: { + collection: VariableCollectionDescriptor; + selectedMembers: string[]; + }; + aggregatorConfig: + | { + overlayType: 'continuous'; + aggregator: 'mean' | 'median'; + } + | { + overlayType: 'categorical'; + numeratorValues: string[]; + denominatorValues: string[]; + }; + }; +} + +export type StandaloneCollectionsMarkerDataResponse = TypeOf< + typeof StandaloneCollectionsMarkerDataResponse +>; + +export const StandaloneCollectionsMarkerDataResponse = type({ + markers: array( + type({ + geoAggregateValue: string, + entityCount: number, + avgLat: number, + avgLon: number, + minLat: number, + minLon: number, + maxLat: number, + maxLon: number, + overlayValues: array( + type({ + variableId: string, + value: union([number, string]), + confidenceInterval: type({ + min: union([number, string]), + max: union([number, string]), + }), + n: number, + }) + ), + }) + ), +}); + export interface ContinousVariableMetadataRequestParams { studyId: string; filters: Filter[]; From 11f9f6cf9566402fa15fc7383e9b3522b661020d Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Mon, 20 May 2024 14:36:40 -0400 Subject: [PATCH 16/17] Add optional vocabulary property --- packages/libs/eda/src/lib/core/types/study.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/libs/eda/src/lib/core/types/study.ts b/packages/libs/eda/src/lib/core/types/study.ts index 6eff7c782e..42ed334f6c 100644 --- a/packages/libs/eda/src/lib/core/types/study.ts +++ b/packages/libs/eda/src/lib/core/types/study.ts @@ -232,6 +232,7 @@ export const CollectionVariableTreeNode = t.intersection([ distributionDefaults: NumberDistributionDefaults, member: t.string, memberPlural: t.string, + vocabulary: t.array(t.string), }), ]); From 8740857cadbde8938ad6ccf345b7fb5511003b51 Mon Sep 17 00:00:00 2001 From: Dave Falke Date: Mon, 20 May 2024 14:47:14 -0400 Subject: [PATCH 17/17] WIP collection marker --- .../CollectionBarMarkerType.tsx | 191 +++++++++++++++--- 1 file changed, 167 insertions(+), 24 deletions(-) diff --git a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx index 33e693aceb..452f840589 100644 --- a/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx +++ b/packages/libs/eda/src/lib/map/analysis/mapTypes/plugins/collection-barplot/CollectionBarMarkerType.tsx @@ -6,9 +6,13 @@ import { } from '../../types'; import { CollectionVariableTreeNode, + Filter, + StandaloneCollectionsMarkerDataRequest, StudyEntity, Variable, + useDataClient, useFindEntityAndVariableCollection, + useStudyEntities, } from '../../../../../core'; import { VariableCollectionSelectList } from '../../../../../core/components/variableSelectors/VariableCollectionSingleSelect'; import { SelectList } from '@veupathdb/coreui'; @@ -19,6 +23,19 @@ import { LegendItemsProps } from '@veupathdb/components/lib/components/plotContr import { BarPlotMarkerIcon } from '../../MarkerConfiguration/icons'; import { difference, noop, union, uniq } from 'lodash'; import { Mesa } from '@veupathdb/coreui'; +import { ColorPaletteDefault } from '@veupathdb/components/lib/types/plots'; +import { useQuery } from '@tanstack/react-query'; +import { BoundsViewport } from '@veupathdb/components/lib/map/Types'; +import { GeoConfig } from '../../../../../core/types/geoConfig'; +import { defaultAnimation, useCommonData } from '../../shared'; +import SemanticMarkers from '@veupathdb/components/lib/map/SemanticMarkers'; +import ChartMarker, { + BaseMarkerData, +} from '@veupathdb/components/lib/map/ChartMarker'; +import { BoundsDriftMarkerProps } from '@veupathdb/components/lib/map/BoundsDriftMarker'; +import { MapFloatingErrorDiv } from '../../../MapFloatingErrorDiv'; +import { mFormatter } from '../../../../../core/utils/big-number-formatters'; +import Spinner from '@veupathdb/components/lib/components/Spinner'; const displayName = 'Bar plots'; @@ -35,31 +52,25 @@ export const plugin: MapTypePlugin = { IconComponent: BarPlotMarkerIcon, displayName, getDefaultConfig({ study }) { - const firstCollection = Array.from( + const firstCollectionWithEntity = Array.from( preorder(study.rootEntity, (e) => e.children ?? []) ) .flatMap( (e) => - e.collections?.map((c): [StudyEntity, CollectionVariableTreeNode] => [ - e, - c, - ]) ?? [] + e.collections + ?.filter((c) => c.dataShape === 'categorical') + .map((c): [StudyEntity, CollectionVariableTreeNode] => [e, c]) ?? [] ) .at(0); - if (firstCollection == null) + if (firstCollectionWithEntity == null) throw new Error('This study does not have any collections.'); - const [entity, collection] = firstCollection; - const selectedValues = entity.variables - .filter((variable): variable is Variable => - collection.memberVariableIds.includes(variable.id) - ) - .flatMap((variable) => variable.vocabulary ?? []); + const [entity, collection] = firstCollectionWithEntity; return { type: 'collection-barplot', entityId: entity.id, collectionId: collection.id, selectedVariableIds: collection.memberVariableIds.slice(0, 8), - selectedValues: union(selectedValues), + selectedValues: collection.vocabulary ?? [], }; }, ConfigPanelComponent, @@ -79,14 +90,13 @@ function ConfigPanelComponent( entityId: configuration.entityId, collectionId: configuration.collectionId, }) ?? {}; - const memberVariableIdSet = new Set(variableCollection?.memberVariableIds); - const variables = - entity?.variables.filter((v): v is Variable => - memberVariableIdSet.has(v.id) - ) ?? []; - const valueCheckboxListItems = union( - variables?.flatMap((v) => v.vocabulary ?? []) - ).map( + const variablesById = new Map(entity?.variables.map((v) => [v.id, v])); + const variables: Variable[] = + variableCollection?.memberVariableIds + .map((id) => variablesById.get(id)) + .filter(Variable.is) ?? []; + + const valueCheckboxListItems = variableCollection?.vocabulary?.map( (value): Item => ({ display: value, value, @@ -116,6 +126,7 @@ function ConfigPanelComponent(
c.dataShape === 'categorical'} value={{ entityId: configuration.entityId, collectionId: configuration.collectionId, @@ -235,8 +246,55 @@ function ConfigPanelComponent( ); } -function MapLayerComponent() { - return
I am a map layer component
; +function MapLayerComponent( + props: MapTypeMapLayerProps +) { + const markerData = useMarkerData({ + studyId: props.studyId, + boundsZoomLevel: props.appState.boundsZoomLevel, + configuration: props.configuration as CollectionBarMarkerConfiguration, + geoConfigs: props.geoConfigs, + }); + + if (markerData.isError) { + return ; + } + + // TODO Order based on collection vocab + const markers = markerData.data?.markers.map((marker, index) => { + const data: BaseMarkerData[] = marker.overlayValues.map((entry, index) => ({ + color: ColorPaletteDefault[index], + label: entry.variableId, + value: Number(entry.value) || 0, + count: Number(entry.value) || 0, + })); + const bounds: BoundsDriftMarkerProps['bounds'] = { + southWest: { lat: marker.minLat, lng: marker.minLon }, + northEast: { lat: marker.maxLat, lng: marker.maxLon }, + }; + const position = { lat: marker.avgLat, lng: marker.avgLon }; + + return ( + + ); + }); + + if (markers == null) return null; + + return ( + <> + {markerData.isFetching && } + ; + + ); } function MapOverlayComponent( @@ -256,9 +314,10 @@ function MapOverlayComponent( configuration.selectedVariableIds.includes(variable.id) ) .map( - (variable): LegendItemsProps => ({ + (variable, index): LegendItemsProps => ({ label: variable.displayName, marker: 'square', + markerColor: ColorPaletteDefault[index], hasData: true, }) ); @@ -285,3 +344,87 @@ function MapOverlayComponent( function MapTypeHeaderDetails() { return
I am a map type header details component
; } + +interface MarkerDataProps { + boundsZoomLevel?: BoundsViewport; + configuration: CollectionBarMarkerConfiguration; + geoConfigs: GeoConfig[]; + studyId: string; + filters?: Filter[]; +} + +function useMarkerData({ + boundsZoomLevel, + configuration, + geoConfigs, + studyId, + filters, +}: MarkerDataProps) { + const dataClient = useDataClient(); + const studyEntities = useStudyEntities(); + + const { + outputEntity, + latitudeVariable, + longitudeVariable, + geoAggregateVariable, + viewport, + } = useCommonData( + // FIXME Using a fake overlay variable. We don't need this here. + { + variableId: configuration.selectedVariableIds[0], + entityId: configuration.entityId, + }, + geoConfigs, + studyEntities, + boundsZoomLevel + ); + + const collection = studyEntities + .filter((entity) => entity.id === configuration.entityId) + .flatMap((entity) => + entity.collections?.filter( + (collection) => collection.id === configuration.collectionId + ) + ) + .at(0); + + if (collection == null) { + throw new Error('Could not find collection'); + } + + const requestParams: StandaloneCollectionsMarkerDataRequest = { + studyId, + filters, + config: { + outputEntityId: outputEntity.id, + geoAggregateVariable, + longitudeVariable, + latitudeVariable, + viewport, + collectionOverlay: { + collection: { + entityId: configuration.entityId, + collectionId: configuration.collectionId, + }, + selectedMembers: configuration.selectedVariableIds, + }, + aggregatorConfig: { + overlayType: collection.dataShape as any, + numeratorValues: configuration.selectedValues, + // FIXME Use all values for denominator + denominatorValues: (collection as any).vocabulary, + }, + }, + }; + + return useQuery({ + queryKey: [requestParams], + queryFn: async () => { + return dataClient.getStandaloneCollectionsMarkerData( + 'standalone-map', + requestParams + ); + }, + }); +}