Skip to content

Commit

Permalink
feat(components): gs-sequences-by-location: add table view (#602)
Browse files Browse the repository at this point in the history
resolves #597
  • Loading branch information
fengelniederhammer authored Dec 11, 2024
1 parent d0e0e56 commit c04b7ba
Show file tree
Hide file tree
Showing 18 changed files with 152 additions and 36 deletions.
22 changes: 20 additions & 2 deletions components/src/preact/map/sequences-by-location-map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import Leaflet, { type Layer, type LayerGroup } from 'leaflet';
import type { FunctionComponent } from 'preact';
import { useEffect, useRef } from 'preact/hooks';

import { type GeoJsonFeatureProperties } from './useGeoJsonMap';
import { type GeoJsonFeatureProperties, type MapSource, useGeoJsonMap } from './useGeoJsonMap';
import { type AggregateData } from '../../query/queryAggregateData';
import { LoadingDisplay } from '../components/loading-display';
import { formatProportion } from '../shared/table/formatProportion';

type FeatureData = { proportion: number; count: number };
Expand All @@ -14,7 +15,7 @@ type EnhancedGeoJsonFeatureProperties = GeoJsonFeatureProperties & {
};

type SequencesByLocationMapProps = {
geojsonData: FeatureCollection<GeometryObject, GeoJsonFeatureProperties>;
mapSource: MapSource;
locationData: AggregateData;
enableMapNavigation: boolean;
lapisLocationField: string;
Expand All @@ -24,6 +25,23 @@ type SequencesByLocationMapProps = {
};

export const SequencesByLocationMap: FunctionComponent<SequencesByLocationMapProps> = ({
mapSource,
...otherProps
}) => {
const { isLoading: isLoadingMap, geojsonData } = useGeoJsonMap(mapSource);

if (isLoadingMap) {
return <LoadingDisplay />;
}

return <SequencesByLocationMapInner geojsonData={geojsonData} {...otherProps} />;
};

type SequencesByLocationMapInnerProps = Omit<SequencesByLocationMapProps, 'mapSource'> & {
geojsonData: FeatureCollection<GeometryObject, GeoJsonFeatureProperties>;
};

export const SequencesByLocationMapInner: FunctionComponent<SequencesByLocationMapInnerProps> = ({
geojsonData,
locationData,
enableMapNavigation,
Expand Down
18 changes: 18 additions & 0 deletions components/src/preact/map/sequences-by-location-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { type FunctionComponent } from 'preact';

import type { AggregateData } from '../../query/queryAggregateData';
import { AggregateTable } from '../aggregatedData/aggregate-table';

type SequencesByLocationTableProps = {
locationData: AggregateData;
lapisLocationField: string;
pageSize: boolean | number;
};

export const SequencesByLocationTable: FunctionComponent<SequencesByLocationTableProps> = ({
locationData,
lapisLocationField,
pageSize,
}) => {
return <AggregateTable data={locationData} fields={[lapisLocationField]} pageSize={pageSize} />;
};
3 changes: 2 additions & 1 deletion components/src/preact/map/sequences-by-location.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ export const Default: StoryObj<SequencesByLocationProps> = {
enableMapNavigation: false,
width: '1100px',
height: '800px',
views: ['map'],
views: ['map', 'table'],
zoom: 2,
offsetX: 0,
offsetY: 10,
pageSize: 10,
},
parameters: {
fetchMock: {
Expand Down
35 changes: 23 additions & 12 deletions components/src/preact/map/sequences-by-location.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type * as GeoJSON from 'geojson';
import type { GeometryObject } from 'geojson';
import type { FunctionComponent } from 'preact';
import { useContext } from 'preact/hooks';
import z from 'zod';

import { SequencesByLocationMap } from './sequences-by-location-map';
import { SequencesByLocationTable } from './sequences-by-location-table';
import { type AggregateData, queryAggregateData } from '../../query/queryAggregateData';
import { LapisUrlContext } from '../LapisUrlContext';
import { ErrorBoundary } from '../components/error-boundary';
Expand All @@ -13,24 +12,25 @@ import Info, { InfoComponentCode, InfoHeadline1, InfoParagraph } from '../compon
import { LoadingDisplay } from '../components/loading-display';
import { ResizeContainer } from '../components/resize-container';
import { useQuery } from '../useQuery';
import { type GeoJsonFeatureProperties, mapSourceSchema, useGeoJsonMap } from './useGeoJsonMap';
import { mapSourceSchema } from './useGeoJsonMap';
import { lapisFilterSchema, views } from '../../types';
import Tabs from '../components/tabs';

export const sequencesByLocationViewSchema = z.literal(views.map);
export const sequencesByLocationViewSchema = z.union([z.literal(views.map), z.literal(views.table)]);
export type SequencesByLocationMapView = z.infer<typeof sequencesByLocationViewSchema>;

const sequencesByLocationPropsSchema = z.object({
lapisFilter: lapisFilterSchema,
lapisLocationField: z.string().min(1),
mapSource: mapSourceSchema,
mapSource: mapSourceSchema.optional(),
enableMapNavigation: z.boolean(),
width: z.string(),
height: z.string(),
views: z.array(sequencesByLocationViewSchema),
zoom: z.number(),
offsetX: z.number(),
offsetY: z.number(),
pageSize: z.union([z.boolean(), z.number()]),
});

export type SequencesByLocationProps = z.infer<typeof sequencesByLocationPropsSchema>;
Expand All @@ -49,10 +49,9 @@ export const SequencesByLocation: FunctionComponent<SequencesByLocationProps> =
};

const SequencesByLocationMapInner: FunctionComponent<SequencesByLocationProps> = (props) => {
const { lapisFilter, lapisLocationField, mapSource } = props;
const { lapisFilter, lapisLocationField } = props;

const lapis = useContext(LapisUrlContext);
const { isLoading: isLoadingMap, geojsonData } = useGeoJsonMap(mapSource);
const {
data,
error,
Expand All @@ -62,37 +61,38 @@ const SequencesByLocationMapInner: FunctionComponent<SequencesByLocationProps> =
[lapisFilter, lapisLocationField, lapis],
);

if (isLoadingMap || isLoadingLapisData) {
if (isLoadingLapisData) {
return <LoadingDisplay />;
}

if (error) {
throw error;
}

return <SequencesByLocationMapTabs geojsonData={geojsonData} data={data} originalComponentProps={props} />;
return <SequencesByLocationMapTabs data={data} originalComponentProps={props} />;
};

type SequencesByLocationMapTabsProps = {
originalComponentProps: SequencesByLocationProps;
geojsonData: GeoJSON.FeatureCollection<GeometryObject, GeoJsonFeatureProperties>;
data: AggregateData;
};

const SequencesByLocationMapTabs: FunctionComponent<SequencesByLocationMapTabsProps> = ({
originalComponentProps,
geojsonData,
data,
}) => {
const getTab = (view: SequencesByLocationMapView) => {
switch (view) {
case views.map:
if (originalComponentProps.mapSource === undefined) {
throw new Error('mapSource is required when using the map view');
}
return {
title: 'Map',
content: (
<SequencesByLocationMap
locationData={data}
geojsonData={geojsonData}
mapSource={originalComponentProps.mapSource}
enableMapNavigation={originalComponentProps.enableMapNavigation}
lapisLocationField={originalComponentProps.lapisLocationField}
zoom={originalComponentProps.zoom}
Expand All @@ -101,6 +101,17 @@ const SequencesByLocationMapTabs: FunctionComponent<SequencesByLocationMapTabsPr
/>
),
};
case views.table:
return {
title: 'Table',
content: (
<SequencesByLocationTable
locationData={data}
lapisLocationField={originalComponentProps.lapisLocationField}
pageSize={originalComponentProps.pageSize}
/>
),
};
}
};

Expand Down
2 changes: 1 addition & 1 deletion components/src/preact/map/useGeoJsonMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function useGeoJsonMap(mapSource: MapSource) {
case 'topojson':
return await loadTopojsonMap(mapSource);
}
}, [mapSource.url]);
}, [mapSource]);

if (isLoading) {
return { isLoading };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const codeExample = `<gs-sequences-by-location
zoom='2'
offsetX='0'
offsetY='10'
pageSize='5'
/>`;

const meta: Meta<Required<SequencesByLocationProps>> = {
Expand Down Expand Up @@ -76,25 +77,30 @@ const Template: StoryObj<SequencesByLocationProps> = {
.zoom=${args.zoom}
.offsetX=${args.offsetX}
.offsetY=${args.offsetY}
.pageSize=${args.pageSize}
></gs-sequences-by-location>
</gs-app>
`,
args: {
enableMapNavigation: false,
width: '1100px',
height: '800px',
views: ['map', 'table'],
pageSize: 10,
},
};

export const WorldMap: StoryObj<SequencesByLocationProps> = {
...Template,
args: {
...Template.args,
lapisFilter: { dateFrom: '2022-01-01', dateTo: '2022-04-01' },
lapisLocationField: 'country',
mapSource: {
type: 'topojson',
url: mockMapUrl,
topologyObjectsKey: 'countries',
},
enableMapNavigation: false,
width: '1100px',
height: '800px',
views: ['map'],
zoom: 2,
offsetX: 0,
offsetY: 10,
Expand Down Expand Up @@ -135,17 +141,15 @@ export const WorldMap: StoryObj<SequencesByLocationProps> = {
export const Germany: StoryObj<SequencesByLocationProps> = {
...Template,
args: {
...Template.args,
lapisFilter: { dateFrom: '2022-01-01', dateTo: '2022-04-01', country: 'Germany' },
lapisLocationField: 'division',
mapSource: {
type: 'topojson',
url: mockMapUrl,
topologyObjectsKey: 'deu',
},
enableMapNavigation: false,
width: '1100px',
height: '800px',
views: ['map'],
views: ['map', 'table'],
zoom: 6.3,
offsetX: 10,
offsetY: 51.4,
Expand Down Expand Up @@ -183,3 +187,48 @@ export const Germany: StoryObj<SequencesByLocationProps> = {
},
},
};

export const GermanyTableOnly: StoryObj<SequencesByLocationProps> = {
render: (args) => html`
<gs-app lapis="${LAPIS_URL}">
<gs-sequences-by-location
.lapisFilter=${args.lapisFilter}
.lapisLocationField=${args.lapisLocationField}
.width=${args.width}
.height=${args.height}
.views=${args.views}
.pageSize=${args.pageSize}
></gs-sequences-by-location>
</gs-app>
`,
args: {
lapisFilter: { dateFrom: '2022-01-01', dateTo: '2022-04-01', country: 'Germany' },
lapisLocationField: 'division',
width: '100%',
height: '700px',
views: ['table'],
pageSize: 10,
},
parameters: {
fetchMock: {
mocks: [
{
matcher: {
name: 'aggregatedData',
url: AGGREGATED_ENDPOINT,
body: {
fields: ['division'],
dateFrom: '2022-01-01',
dateTo: '2022-04-01',
country: 'Germany',
},
},
response: {
status: 200,
body: aggregatedGermany,
},
},
],
},
},
};
Loading

0 comments on commit c04b7ba

Please sign in to comment.