Skip to content

Commit

Permalink
fix(components): gs-prevalence-over-time: validate attributes
Browse files Browse the repository at this point in the history
Resolves: #576
  • Loading branch information
JonasKellerer committed Dec 9, 2024
1 parent 84fc7ff commit 90cc442
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import numeratorFilterNoData from './__mockData__/numeratorFilterNoData.json';
import numeratorOneDataset from './__mockData__/numeratorFilterOneDataset.json';
import { PrevalenceOverTime, type PrevalenceOverTimeProps } from './prevalence-over-time';
import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectInvalidAttributesErrorMessage';

export default {
title: 'Visualization/PrevalenceOverTime',
Expand Down Expand Up @@ -256,3 +257,16 @@ export const ShowsNoDataBanner: StoryObj<PrevalenceOverTimeProps> = {
});
},
};

export const WithNoLapisDateField: StoryObj<PrevalenceOverTimeProps> = {
...OneVariant,
args: {
...OneVariant.args,
lapisDateField: '',
},
play: async ({ canvasElement, step }) => {
step('expect error message', async () => {
await expectInvalidAttributesErrorMessage(canvasElement, 'String must contain at least 1 character(s)');
});
},
};
47 changes: 28 additions & 19 deletions components/src/preact/prevalenceOverTime/prevalence-over-time.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { type FunctionComponent } from 'preact';
import { useContext, useEffect, useState } from 'preact/hooks';
import z from 'zod';

import { getPrevalenceOverTimeTableData } from './getPrevalenceOverTimeTableData';
import PrevalenceOverTimeBarChart from './prevalence-over-time-bar-chart';
import PrevalenceOverTimeBubbleChart from './prevalence-over-time-bubble-chart';
import PrevalenceOverTimeLineChart from './prevalence-over-time-line-chart';
import PrevalenceOverTimeTable from './prevalence-over-time-table';
import { type PrevalenceOverTimeData, queryPrevalenceOverTime } from '../../query/queryPrevalenceOverTime';
import { type LapisFilter, type NamedLapisFilter, type TemporalGranularity } from '../../types';
import { lapisFilterSchema, namedLapisFilterSchema, temporalGranularitySchema, views } from '../../types';
import { LapisUrlContext } from '../LapisUrlContext';
import { ConfidenceIntervalSelector } from '../components/confidence-interval-selector';
import { CsvDownloadButton } from '../components/csv-download-button';
Expand All @@ -19,34 +20,42 @@ import { NoDataDisplay } from '../components/no-data-display';
import { ResizeContainer } from '../components/resize-container';
import { ScalingSelector } from '../components/scaling-selector';
import Tabs from '../components/tabs';
import { type ConfidenceIntervalMethod } from '../shared/charts/confideceInterval';
import { type AxisMax } from '../shared/charts/getYAxisMax';
import { type ConfidenceIntervalMethod, confidenceIntervalMethodSchema } from '../shared/charts/confideceInterval';
import { axisMaxSchema } from '../shared/charts/getYAxisMax';
import { type ScaleType } from '../shared/charts/getYAxisScale';
import { useQuery } from '../useQuery';

export type View = 'bar' | 'line' | 'bubble' | 'table';
const viewSchema = z.union([
z.literal(views.table),
z.literal(views.bar),
z.literal(views.line),
z.literal(views.bubble),
]);
export type View = z.infer<typeof viewSchema>;

export interface PrevalenceOverTimeProps {
width: string;
height: string;
numeratorFilter: NamedLapisFilter | NamedLapisFilter[];
denominatorFilter: LapisFilter;
granularity: TemporalGranularity;
smoothingWindow: number;
views: View[];
confidenceIntervalMethods: ConfidenceIntervalMethod[];
lapisDateField: string;
pageSize: boolean | number;
yAxisMaxLinear: AxisMax;
yAxisMaxLogarithmic: AxisMax;
}
const prevalenceOverTimePropsSchema = z.object({
width: z.string(),
height: z.string(),
numeratorFilter: z.union([namedLapisFilterSchema, z.array(namedLapisFilterSchema)]),
denominatorFilter: lapisFilterSchema,
granularity: temporalGranularitySchema,
smoothingWindow: z.number(),
views: z.array(viewSchema),
confidenceIntervalMethods: z.array(confidenceIntervalMethodSchema),
lapisDateField: z.string().min(1),
pageSize: z.union([z.boolean(), z.number()]),
yAxisMaxLinear: axisMaxSchema,
yAxisMaxLogarithmic: axisMaxSchema,
});

export type PrevalenceOverTimeProps = z.infer<typeof prevalenceOverTimePropsSchema>;

export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
const { width, height } = componentProps;
const size = { height, width };

return (
<ErrorBoundary size={size}>
<ErrorBoundary size={size} schema={prevalenceOverTimePropsSchema} componentProps={componentProps}>
<ResizeContainer size={size}>
<PrevalenceOverTimeInner {...componentProps} />
</ResizeContainer>
Expand Down
5 changes: 4 additions & 1 deletion components/src/preact/shared/charts/confideceInterval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
//
// observed - number of observed positive outcomes
// sample - number of experiments or size of the sample
import z from 'zod';

export function wilson95PercentConfidenceInterval(observed: number, sample: number) {
const p = observed / sample;
const n = sample;
Expand All @@ -31,4 +33,5 @@ export const confidenceIntervalDataLabel = (
return `${label}${value.toFixed(3)} (${lowerLimit?.toFixed(3)} - ${upperLimit?.toFixed(3)})`;
};

export type ConfidenceIntervalMethod = 'wilson' | 'none';
export const confidenceIntervalMethodSchema = z.union([z.literal('wilson'), z.literal('none')]);
export type ConfidenceIntervalMethod = z.infer<typeof confidenceIntervalMethodSchema>;
5 changes: 4 additions & 1 deletion components/src/preact/shared/charts/getYAxisMax.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import z from 'zod';

export interface YAxisMaxConfig {
linear?: AxisMax;
logarithmic?: AxisMax;
}

export type AxisMax = 'maxInData' | 'limitTo1' | number;
export const axisMaxSchema = z.union([z.literal('maxInData'), z.literal('limitTo1'), z.number()]);
export type AxisMax = z.infer<typeof axisMaxSchema>;

export const getYAxisMax = (maxInData: number, axisMax?: AxisMax) => {
if (!axisMax) {
Expand Down
11 changes: 10 additions & 1 deletion components/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ export const namedLapisFilterSchema = z.object({
});
export type NamedLapisFilter = z.infer<typeof namedLapisFilterSchema>;

export type TemporalGranularity = 'day' | 'week' | 'month' | 'year';
export const temporalGranularitySchema = z.union([
z.literal('day'),
z.literal('week'),
z.literal('month'),
z.literal('year'),
]);
export type TemporalGranularity = z.infer<typeof temporalGranularitySchema>;

export const sequenceTypeSchema = z.union([z.literal('nucleotide'), z.literal('amino acid')]);
export type SequenceType = z.infer<typeof sequenceTypeSchema>;
Expand Down Expand Up @@ -53,6 +59,9 @@ export type MutationEntry = SubstitutionEntry | DeletionEntry | InsertionEntry;
export const views = {
table: 'table',
venn: 'venn',
bar: 'bar',
line: 'line',
bubble: 'bubble',
} as const;

export const mutationComparisonViewSchema = z.union([z.literal(views.table), z.literal(views.venn)]);
Expand Down
4 changes: 4 additions & 0 deletions components/src/utilEntrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ export {
} from './types';

export { type SelectedMutationFilterStrings } from './preact/mutationFilter/mutation-filter';

export { type ConfidenceIntervalMethod } from './preact/shared/charts/confideceInterval';

export { type AxisMax, type YAxisMaxConfig } from './preact/shared/charts/getYAxisMax';
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export class PrevalenceOverTimeComponent extends PreactLitAdapterWithGridJsStyle
// prettier-ignore
// The multiline union type must not start with `|` because it looks weird in the Storybook docs
/**
* Required.
* Either a LAPIS filter or an array of LAPIS filters to calculate the prevalence for.
*
* The `lapisFilter` will be sent as is to LAPIS to select the data.
Expand All @@ -69,8 +68,6 @@ export class PrevalenceOverTimeComponent extends PreactLitAdapterWithGridJsStyle
}[] = {displayName: '', lapisFilter: {}};

/**
* Required.
*
* The LAPIS filter, to select the data of the reference.
* It must be a valid LAPIS filter object.
*/
Expand Down

0 comments on commit 90cc442

Please sign in to comment.