@genspectrum/dashboard-components 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/custom-elements.json +86 -29
  2. package/dist/dashboard-components.js +391 -207
  3. package/dist/dashboard-components.js.map +1 -1
  4. package/dist/genspectrum-components.d.ts +37 -12
  5. package/dist/style.css +58 -50
  6. package/package.json +2 -1
  7. package/src/operator/RenameFieldOperator.spec.ts +28 -0
  8. package/src/operator/RenameFieldOperator.ts +19 -0
  9. package/src/preact/aggregatedData/aggregate.tsx +1 -1
  10. package/src/preact/components/info.stories.tsx +8 -8
  11. package/src/preact/components/info.tsx +113 -20
  12. package/src/preact/dateRangeSelector/computeInitialValues.spec.ts +99 -0
  13. package/src/preact/dateRangeSelector/computeInitialValues.ts +73 -0
  14. package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +93 -4
  15. package/src/preact/dateRangeSelector/date-range-selector.tsx +27 -88
  16. package/src/preact/dateRangeSelector/selectableOptions.ts +79 -0
  17. package/src/preact/locationFilter/location-filter.tsx +1 -1
  18. package/src/preact/mutationComparison/mutation-comparison.tsx +1 -1
  19. package/src/preact/mutationFilter/mutation-filter.stories.tsx +3 -6
  20. package/src/preact/mutationFilter/mutation-filter.tsx +48 -54
  21. package/src/preact/mutations/mutations.tsx +1 -1
  22. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +3 -0
  23. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +7 -3
  24. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +2 -0
  25. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +6 -2
  26. package/src/query/queryPrevalenceOverTime.ts +13 -7
  27. package/src/query/queryRelativeGrowthAdvantage.ts +11 -7
  28. package/src/web-components/input/gs-date-range-selector.stories.ts +11 -5
  29. package/src/web-components/input/gs-date-range-selector.tsx +22 -5
  30. package/src/web-components/input/gs-location-filter.stories.ts +6 -7
  31. package/src/web-components/input/gs-location-filter.tsx +3 -2
  32. package/src/web-components/input/gs-mutation-filter.stories.ts +1 -8
  33. package/src/web-components/input/gs-mutation-filter.tsx +1 -9
  34. package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +6 -2
  35. package/src/web-components/visualization/gs-prevalence-over-time.tsx +11 -0
  36. package/src/web-components/visualization/gs-relative-growth-advantage.stories.ts +3 -0
  37. package/src/web-components/visualization/gs-relative-growth-advantage.tsx +11 -0
@@ -7,7 +7,6 @@ import { type Deletion, type Insertion, type Mutation, type Substitution } from
7
7
  import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
8
8
  import { ErrorBoundary } from '../components/error-boundary';
9
9
  import Info from '../components/info';
10
- import { ResizeContainer } from '../components/resize-container';
11
10
  import { singleGraphColorRGBByName } from '../shared/charts/colors';
12
11
  import { DeleteIcon } from '../shared/icons/DeleteIcon';
13
12
 
@@ -17,7 +16,6 @@ export interface MutationFilterInnerProps {
17
16
 
18
17
  export interface MutationFilterProps extends MutationFilterInnerProps {
19
18
  width: string;
20
- height: string;
21
19
  }
22
20
 
23
21
  export type SelectedFilters = {
@@ -31,14 +29,12 @@ export type SelectedMutationFilterStrings = {
31
29
  [Key in keyof SelectedFilters]: string[];
32
30
  };
33
31
 
34
- export const MutationFilter: FunctionComponent<MutationFilterProps> = ({ initialValue, width, height }) => {
35
- const size = { height, width };
36
-
32
+ export const MutationFilter: FunctionComponent<MutationFilterProps> = ({ initialValue, width }) => {
37
33
  return (
38
- <ErrorBoundary size={size}>
39
- <ResizeContainer size={size}>
34
+ <ErrorBoundary size={{ height: '3.375rem', width }}>
35
+ <div style={width}>
40
36
  <MutationFilterInner initialValue={initialValue} />
41
- </ResizeContainer>
37
+ </div>
42
38
  </ErrorBoundary>
43
39
  );
44
40
  };
@@ -54,6 +50,9 @@ export const MutationFilterInner: FunctionComponent<MutationFilterInnerProps> =
54
50
 
55
51
  const handleSubmit = (event: Event) => {
56
52
  event.preventDefault();
53
+ if (inputValue === '') {
54
+ return;
55
+ }
57
56
 
58
57
  const parsedMutation = parseAndValidateMutation(inputValue, referenceGenome);
59
58
 
@@ -102,30 +101,33 @@ export const MutationFilterInner: FunctionComponent<MutationFilterInnerProps> =
102
101
  };
103
102
 
104
103
  return (
105
- <div class={`h-full w-full rounded-lg border border-gray-300 bg-white p-2 overflow-scroll`}>
106
- <div class='flex justify-between'>
104
+ <form className='w-full border boder-gray-300 rounded-md relative' onSubmit={handleSubmit} ref={formRef}>
105
+ <div className='absolute -top-3 -right-3'>
106
+ <Info height={'100px'}>Info for mutation filter</Info>
107
+ </div>
108
+ <div className='w-full flex p-2 flex-wrap items-center'>
107
109
  <SelectedMutationDisplay
108
110
  selectedFilters={selectedFilters}
109
111
  setSelectedFilters={setSelectedFilters}
110
112
  fireChangeEvent={fireChangeEvent}
111
113
  />
112
- <Info>Info for mutation filter</Info>
113
- </div>
114
-
115
- <form className='mt-2 w-full' onSubmit={handleSubmit} ref={formRef}>
116
- <label className={`input flex items-center gap-2 ${isError ? 'input-error' : 'input-bordered'}`}>
114
+ <div
115
+ className={`w-full flex border ${isError ? 'border-red-500' : 'border-gray-300'} border-solid m-2 text-sm focus-within:border-gray-400 `}
116
+ >
117
117
  <input
118
- className='grow min-w-0'
118
+ className='grow flex-1 p-1 border-none focus:outline-none focus:ring-0'
119
119
  type='text'
120
120
  value={inputValue}
121
121
  onInput={handleInputChange}
122
122
  placeholder={getPlaceholder(referenceGenome)}
123
123
  onBlur={handleOnBlur}
124
124
  />
125
- <button className='btn btn-sm'>+</button>
126
- </label>
127
- </form>
128
- </div>
125
+ <button type='submit' className='btn btn-xs m-1'>
126
+ +
127
+ </button>
128
+ </div>
129
+ </div>
130
+ </form>
129
131
  );
130
132
  };
131
133
 
@@ -193,44 +195,36 @@ const SelectedMutationDisplay: FunctionComponent<{
193
195
  };
194
196
 
195
197
  return (
196
- <ul class='flex flex-wrap'>
198
+ <>
197
199
  {selectedFilters.nucleotideMutations.map((mutation) => (
198
- <li key={mutation.toString()}>
199
- <SelectedNucleotideMutation
200
- mutation={mutation}
201
- onDelete={(mutation: Substitution | Deletion) =>
202
- onSelectedRemoved(mutation, 'nucleotideMutations')
203
- }
204
- />
205
- </li>
200
+ <SelectedNucleotideMutation
201
+ key={mutation.toString()}
202
+ mutation={mutation}
203
+ onDelete={(mutation: Substitution | Deletion) => onSelectedRemoved(mutation, 'nucleotideMutations')}
204
+ />
206
205
  ))}
207
206
  {selectedFilters.aminoAcidMutations.map((mutation) => (
208
- <li key={mutation.toString()}>
209
- <SelectedAminoAcidMutation
210
- mutation={mutation}
211
- onDelete={(mutation: Substitution | Deletion) =>
212
- onSelectedRemoved(mutation, 'aminoAcidMutations')
213
- }
214
- />
215
- </li>
207
+ <SelectedAminoAcidMutation
208
+ key={mutation.toString()}
209
+ mutation={mutation}
210
+ onDelete={(mutation: Substitution | Deletion) => onSelectedRemoved(mutation, 'aminoAcidMutations')}
211
+ />
216
212
  ))}
217
213
  {selectedFilters.nucleotideInsertions.map((insertion) => (
218
- <li key={insertion.toString()}>
219
- <SelectedNucleotideInsertion
220
- insertion={insertion}
221
- onDelete={(insertion) => onSelectedRemoved(insertion, 'nucleotideInsertions')}
222
- />
223
- </li>
214
+ <SelectedNucleotideInsertion
215
+ key={insertion.toString()}
216
+ insertion={insertion}
217
+ onDelete={(insertion) => onSelectedRemoved(insertion, 'nucleotideInsertions')}
218
+ />
224
219
  ))}
225
220
  {selectedFilters.aminoAcidInsertions.map((insertion) => (
226
- <li key={insertion.toString()}>
227
- <SelectedAminoAcidInsertion
228
- insertion={insertion}
229
- onDelete={(insertion: Insertion) => onSelectedRemoved(insertion, 'aminoAcidInsertions')}
230
- />
231
- </li>
221
+ <SelectedAminoAcidInsertion
222
+ key={insertion.toString()}
223
+ insertion={insertion}
224
+ onDelete={(insertion: Insertion) => onSelectedRemoved(insertion, 'aminoAcidInsertions')}
225
+ />
232
226
  ))}
233
- </ul>
227
+ </>
234
228
  );
235
229
  };
236
230
 
@@ -313,15 +307,15 @@ const SelectedFilter = <MutationType extends Mutation>({
313
307
  textColor,
314
308
  }: SelectedFilterProps<MutationType>) => {
315
309
  return (
316
- <div
317
- class='flex items-center flex-nowrap gap-1 rounded me-1 px-2.5 py-0.5 font-medium text-xs mb-1 min-w-max'
310
+ <span
311
+ class='inline-block mx-1 px-2 py-1 font-medium text-xs rounded-full'
318
312
  style={{ backgroundColor, color: textColor }}
319
313
  >
320
- <div className='whitespace-nowrap min-w-max'>{mutation.toString()}</div>
314
+ {mutation.toString()}
321
315
  <button type='button' onClick={() => onDelete(mutation)}>
322
316
  <DeleteIcon />
323
317
  </button>
324
- </div>
318
+ </span>
325
319
  );
326
320
  };
327
321
 
@@ -205,7 +205,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
205
205
  filename='insertions.csv'
206
206
  />
207
207
  )}
208
- <Info>Info for mutations</Info>
208
+ <Info height={'100px'}>Info for mutations</Info>
209
209
  </div>
210
210
  );
211
211
  };
@@ -48,6 +48,7 @@ const Template = {
48
48
  width={args.width}
49
49
  height={args.height}
50
50
  headline={args.headline}
51
+ lapisDateField={args.lapisDateField}
51
52
  />
52
53
  </LapisUrlContext.Provider>
53
54
  ),
@@ -68,6 +69,7 @@ export const TwoVariants = {
68
69
  width: '100%',
69
70
  height: '700px',
70
71
  headline: 'Prevalence over time',
72
+ lapisDateField: 'date',
71
73
  },
72
74
  parameters: {
73
75
  fetchMock: {
@@ -139,6 +141,7 @@ export const OneVariant = {
139
141
  width: '100%',
140
142
  height: '700px',
141
143
  headline: 'Prevalence over time',
144
+ lapisDateField: 'date',
142
145
  },
143
146
  parameters: {
144
147
  fetchMock: {
@@ -39,6 +39,7 @@ export interface PrevalenceOverTimeInnerProps {
39
39
  smoothingWindow: number;
40
40
  views: View[];
41
41
  confidenceIntervalMethods: ConfidenceIntervalMethod[];
42
+ lapisDateField: string;
42
43
  }
43
44
 
44
45
  export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = ({
@@ -51,6 +52,7 @@ export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = ({
51
52
  width,
52
53
  height,
53
54
  headline = 'Prevalence over time',
55
+ lapisDateField,
54
56
  }) => {
55
57
  const size = { height, width };
56
58
 
@@ -65,6 +67,7 @@ export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = ({
65
67
  smoothingWindow={smoothingWindow}
66
68
  views={views}
67
69
  confidenceIntervalMethods={confidenceIntervalMethods}
70
+ lapisDateField={lapisDateField}
68
71
  />
69
72
  </Headline>
70
73
  </ResizeContainer>
@@ -79,11 +82,12 @@ export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeInnerP
79
82
  smoothingWindow,
80
83
  views,
81
84
  confidenceIntervalMethods,
85
+ lapisDateField,
82
86
  }) => {
83
87
  const lapis = useContext(LapisUrlContext);
84
88
 
85
89
  const { data, error, isLoading } = useQuery(
86
- () => queryPrevalenceOverTime(numerator, denominator, granularity, smoothingWindow, lapis),
90
+ () => queryPrevalenceOverTime(numerator, denominator, granularity, smoothingWindow, lapis, lapisDateField),
87
91
  [lapis, numerator, denominator, granularity, smoothingWindow],
88
92
  );
89
93
 
@@ -226,9 +230,9 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
226
230
  );
227
231
  };
228
232
 
229
- const PrevalenceOverTimeInfo: FunctionComponent = () => {
233
+ const PrevalenceOverTimeInfo: FunctionComponent = ({}) => {
230
234
  return (
231
- <Info size={{ width: '600px', height: '30vh' }}>
235
+ <Info height={'100px'}>
232
236
  <InfoHeadline1>Prevalence over time</InfoHeadline1>
233
237
  <InfoParagraph>Prevalence over time info.</InfoParagraph>
234
238
  </Info>
@@ -35,6 +35,7 @@ export const Primary = {
35
35
  width={args.width}
36
36
  height={args.height}
37
37
  headline={args.headline}
38
+ lapisDateField={args.lapisDateField}
38
39
  />
39
40
  </LapisUrlContext.Provider>
40
41
  ),
@@ -46,6 +47,7 @@ export const Primary = {
46
47
  width: '100%',
47
48
  height: '700px',
48
49
  headline: 'Relative growth advantage',
50
+ lapisDateField: 'date',
49
51
  },
50
52
  parameters: {
51
53
  fetchMock: {
@@ -33,6 +33,7 @@ export interface RelativeGrowthAdvantagePropsInner {
33
33
  denominator: LapisFilter;
34
34
  generationTime: number;
35
35
  views: View[];
36
+ lapisDateField: string;
36
37
  }
37
38
 
38
39
  export const RelativeGrowthAdvantage: FunctionComponent<RelativeGrowthAdvantageProps> = ({
@@ -43,6 +44,7 @@ export const RelativeGrowthAdvantage: FunctionComponent<RelativeGrowthAdvantageP
43
44
  denominator,
44
45
  generationTime,
45
46
  headline = 'Relative growth advantage',
47
+ lapisDateField,
46
48
  }) => {
47
49
  const size = { height, width };
48
50
 
@@ -55,6 +57,7 @@ export const RelativeGrowthAdvantage: FunctionComponent<RelativeGrowthAdvantageP
55
57
  numerator={numerator}
56
58
  denominator={denominator}
57
59
  generationTime={generationTime}
60
+ lapisDateField={lapisDateField}
58
61
  />
59
62
  </Headline>
60
63
  </ResizeContainer>
@@ -67,12 +70,13 @@ export const RelativeGrowthAdvantageInner: FunctionComponent<RelativeGrowthAdvan
67
70
  denominator,
68
71
  generationTime,
69
72
  views,
73
+ lapisDateField,
70
74
  }) => {
71
75
  const lapis = useContext(LapisUrlContext);
72
76
  const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
73
77
 
74
78
  const { data, error, isLoading } = useQuery(
75
- () => queryRelativeGrowthAdvantage(numerator, denominator, generationTime, lapis),
79
+ () => queryRelativeGrowthAdvantage(numerator, denominator, generationTime, lapis, lapisDateField),
76
80
  [lapis, numerator, denominator, generationTime, views],
77
81
  );
78
82
 
@@ -166,7 +170,7 @@ const RelativeGrowthAdvantageToolbar: FunctionComponent<RelativeGrowthAdvantageT
166
170
 
167
171
  const RelativeGrowthAdvantageInfo: FunctionComponent<{ generationTime: number }> = ({ generationTime }) => {
168
172
  return (
169
- <Info size={{ width: '600px', height: '30vh' }}>
173
+ <Info>
170
174
  <InfoHeadline1>Relative growth advantage</InfoHeadline1>
171
175
  <InfoParagraph>
172
176
  If variants spread pre-dominantly by local transmission across demographic groups, this estimate
@@ -3,6 +3,7 @@ import { FetchAggregatedOperator } from '../operator/FetchAggregatedOperator';
3
3
  import { FillMissingOperator } from '../operator/FillMissingOperator';
4
4
  import { GroupByAndSumOperator } from '../operator/GroupByAndSumOperator';
5
5
  import { MapOperator } from '../operator/MapOperator';
6
+ import { RenameFieldOperator } from '../operator/RenameFieldOperator';
6
7
  import { SlidingOperator } from '../operator/SlidingOperator';
7
8
  import { SortOperator } from '../operator/SortOperator';
8
9
  import { type LapisFilter, type NamedLapisFilter, type TemporalGranularity } from '../types';
@@ -27,14 +28,15 @@ export function queryPrevalenceOverTime(
27
28
  granularity: TemporalGranularity,
28
29
  smoothingWindow: number,
29
30
  lapis: string,
31
+ lapisDateField: string,
30
32
  signal?: AbortSignal,
31
33
  ): Promise<PrevalenceOverTimeData> {
32
34
  const numeratorFilters = makeArray(numeratorFilter);
33
35
 
34
- const denominatorData = fetchAndPrepare(denominatorFilter, granularity, smoothingWindow);
36
+ const denominatorData = fetchAndPrepare(denominatorFilter, granularity, smoothingWindow, lapisDateField);
35
37
  const subQueries = numeratorFilters.map(async (namedLapisFilter) => {
36
38
  const { displayName, lapisFilter } = namedLapisFilter;
37
- const numeratorData = fetchAndPrepare(lapisFilter, granularity, smoothingWindow);
39
+ const numeratorData = fetchAndPrepare(lapisFilter, granularity, smoothingWindow, lapisDateField);
38
40
  const divide = new DivisionOperator(
39
41
  numeratorData,
40
42
  denominatorData,
@@ -60,11 +62,15 @@ function makeArray<T>(arrayOrSingleItem: T | T[]) {
60
62
  return [arrayOrSingleItem];
61
63
  }
62
64
 
63
- function fetchAndPrepare(filter: LapisFilter, granularity: TemporalGranularity, smoothingWindow: number) {
64
- const fetchData = new FetchAggregatedOperator<{
65
- date: string | null;
66
- }>(filter, ['date']);
67
- const mapData = new MapOperator(fetchData, (d) => mapDateToGranularityRange(d, granularity));
65
+ function fetchAndPrepare<LapisDateField extends string>(
66
+ filter: LapisFilter,
67
+ granularity: TemporalGranularity,
68
+ smoothingWindow: number,
69
+ lapisDateField: LapisDateField,
70
+ ) {
71
+ const fetchData = new FetchAggregatedOperator<{ [key in LapisDateField]: string | null }>(filter, [lapisDateField]);
72
+ const dataWithFixedDateKey = new RenameFieldOperator(fetchData, lapisDateField, 'date');
73
+ const mapData = new MapOperator(dataWithFixedDateKey, (d) => mapDateToGranularityRange(d, granularity));
68
74
  const groupByData = new GroupByAndSumOperator(mapData, 'dateRange', 'count');
69
75
  const fillData = new FillMissingOperator(
70
76
  groupByData,
@@ -1,25 +1,29 @@
1
1
  import { FetchAggregatedOperator } from '../operator/FetchAggregatedOperator';
2
2
  import { MapOperator } from '../operator/MapOperator';
3
+ import { RenameFieldOperator } from '../operator/RenameFieldOperator';
3
4
  import { type LapisFilter } from '../types';
4
5
  import { getMinMaxTemporal, TemporalCache, type YearMonthDay } from '../utils/temporal';
5
6
 
6
7
  export type RelativeGrowthAdvantageData = Awaited<ReturnType<typeof queryRelativeGrowthAdvantage>>;
7
8
 
8
- export async function queryRelativeGrowthAdvantage(
9
+ export async function queryRelativeGrowthAdvantage<LapisDateField extends string>(
9
10
  numerator: LapisFilter,
10
11
  denominator: LapisFilter,
11
12
  generationTime: number,
12
13
  lapis: string,
14
+ lapisDateField: LapisDateField,
13
15
  signal?: AbortSignal,
14
16
  ) {
15
17
  const fetchNumerator = new FetchAggregatedOperator<{
16
- date: string | null;
17
- }>(numerator, ['date']);
18
+ [key in LapisDateField]: string | null;
19
+ }>(numerator, [lapisDateField]);
18
20
  const fetchDenominator = new FetchAggregatedOperator<{
19
- date: string | null;
20
- }>(denominator, ['date']);
21
- const mapNumerator = new MapOperator(fetchNumerator, toYearMonthDay);
22
- const mapDenominator = new MapOperator(fetchDenominator, toYearMonthDay);
21
+ [key in LapisDateField]: string | null;
22
+ }>(denominator, [lapisDateField]);
23
+ const mapToFixedDateKeyNumerator = new RenameFieldOperator(fetchNumerator, lapisDateField, 'date');
24
+ const mapToFixedDateKeyDenominator = new RenameFieldOperator(fetchDenominator, lapisDateField, 'date');
25
+ const mapNumerator = new MapOperator(mapToFixedDateKeyNumerator, toYearMonthDay);
26
+ const mapDenominator = new MapOperator(mapToFixedDateKeyDenominator, toYearMonthDay);
23
27
  const [numeratorData, denominatorData] = await Promise.all([
24
28
  mapNumerator.evaluate(lapis, signal),
25
29
  mapDenominator.evaluate(lapis, signal),
@@ -5,8 +5,11 @@ import { html } from 'lit';
5
5
 
6
6
  import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
7
7
  import { LAPIS_URL } from '../../constants';
8
+ import { type DateRangeSelectorProps } from '../../preact/dateRangeSelector/date-range-selector';
9
+ import './gs-date-range-selector';
10
+ import '../app';
11
+ import { toYYYYMMDD } from '../../preact/dateRangeSelector/dateConversion';
8
12
  import {
9
- type DateRangeSelectorProps,
10
13
  PRESET_VALUE_ALL_TIMES,
11
14
  PRESET_VALUE_CUSTOM,
12
15
  PRESET_VALUE_LAST_2_MONTHS,
@@ -14,10 +17,7 @@ import {
14
17
  PRESET_VALUE_LAST_3_MONTHS,
15
18
  PRESET_VALUE_LAST_6_MONTHS,
16
19
  PRESET_VALUE_LAST_MONTH,
17
- } from '../../preact/dateRangeSelector/date-range-selector';
18
- import './gs-date-range-selector';
19
- import '../app';
20
- import { toYYYYMMDD } from '../../preact/dateRangeSelector/dateConversion';
20
+ } from '../../preact/dateRangeSelector/selectableOptions';
21
21
  import { withinShadowRoot } from '../withinShadowRoot.story';
22
22
 
23
23
  const codeExample = String.raw`
@@ -25,6 +25,8 @@ const codeExample = String.raw`
25
25
  customSelectOptions='[{ "label": "Year 2021", "dateFrom": "2021-01-01", "dateTo": "2021-12-31" }]'
26
26
  earliestDate="1970-01-01"
27
27
  initialValue="${PRESET_VALUE_LAST_6_MONTHS}"
28
+ initialDateFrom="2020-01-01"
29
+ initialDateTo="2021-01-01"
28
30
  width="100%"
29
31
  dateColumn="myDateColumn"
30
32
  ></gs-date-range-selector>`;
@@ -82,6 +84,8 @@ const meta: Meta<Required<DateRangeSelectorProps<'CustomDateRange'>>> = {
82
84
  initialValue: PRESET_VALUE_LAST_6_MONTHS,
83
85
  dateColumn: 'aDateColumn',
84
86
  width: '100%',
87
+ initialDateFrom: '',
88
+ initialDateTo: '',
85
89
  },
86
90
  decorators: [withActions],
87
91
  tags: ['autodocs'],
@@ -97,6 +101,8 @@ export const DateRangeSelectorStory: StoryObj<Required<DateRangeSelectorProps<'C
97
101
  .customSelectOptions=${args.customSelectOptions}
98
102
  .earliestDate=${args.earliestDate}
99
103
  .initialValue=${args.initialValue}
104
+ .initialDateFrom=${args.initialDateFrom}
105
+ .initialDateTo=${args.initialDateTo}
100
106
  .width=${args.width}
101
107
  .dateColumn=${args.dateColumn}
102
108
  ></gs-date-range-selector>
@@ -1,10 +1,7 @@
1
1
  import { customElement, property } from 'lit/decorators.js';
2
2
 
3
- import {
4
- type CustomSelectOption,
5
- DateRangeSelector,
6
- type PresetOptionValues,
7
- } from '../../preact/dateRangeSelector/date-range-selector';
3
+ import { DateRangeSelector } from '../../preact/dateRangeSelector/date-range-selector';
4
+ import { type CustomSelectOption, type PresetOptionValues } from '../../preact/dateRangeSelector/selectableOptions';
8
5
  import { type Equals, type Expect } from '../../utils/typeAssertions';
9
6
  import { PreactLitAdapter } from '../PreactLitAdapter';
10
7
 
@@ -61,6 +58,8 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
61
58
  * Must be a valid label from the preset labels or a `label` given in the `customSelectOptions`.
62
59
  *
63
60
  * If the value is invalid, the component will default to `'last6Months'`.
61
+ *
62
+ * It will be overwritten if `initialDateFrom` or `initialDateTo` is set.
64
63
  */
65
64
  @property()
66
65
  initialValue:
@@ -73,6 +72,22 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
73
72
  | 'last6Months'
74
73
  | string = 'last6Months';
75
74
 
75
+ /**
76
+ * A date string in the format `YYYY-MM-DD`.
77
+ * If set, the date range selector will be initialized with the given date (overwriting `initialValue` to `custom`).
78
+ * If `initialDateTo` is set, but this is unset, it will default to `earliestDate`.
79
+ */
80
+ @property()
81
+ initialDateFrom: string = '';
82
+
83
+ /**
84
+ * A date string in the format `YYYY-MM-DD`.
85
+ * If set, the date range selector will be initialized with the given date (overwriting `initialValue` to `custom`).
86
+ * If `initialDateFrom` is set, but this is unset, it will default to the current date.
87
+ */
88
+ @property()
89
+ initialDateTo: string = '';
90
+
76
91
  /**
77
92
  * The width of the component.
78
93
  *
@@ -93,6 +108,8 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
93
108
  customSelectOptions={this.customSelectOptions}
94
109
  earliestDate={this.earliestDate}
95
110
  initialValue={this.initialValue}
111
+ initialDateFrom={this.initialDateFrom}
112
+ initialDateTo={this.initialDateTo}
96
113
  dateColumn={this.dateColumn}
97
114
  width={this.width}
98
115
  />
@@ -190,13 +190,12 @@ export const FiresEvent: StoryObj<LocationFilterProps> = {
190
190
 
191
191
  await step('Select Asia', async () => {
192
192
  await userEvent.type(inputField(), 'Asia');
193
- await expect(listenerMock).toHaveBeenCalledWith(
194
- expect.objectContaining({
195
- detail: {
196
- region: 'Asia',
197
- },
198
- }),
199
- );
193
+ await expect(listenerMock.mock.calls.at(-1)[0].detail).toStrictEqual({
194
+ region: 'Asia',
195
+ country: undefined,
196
+ division: undefined,
197
+ location: undefined,
198
+ });
200
199
  });
201
200
 
202
201
  await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => {
@@ -20,13 +20,14 @@ import { PreactLitAdapter } from '../PreactLitAdapter';
20
20
  * @fires {CustomEvent<Record<string, string>>} gs-location-changed
21
21
  * Fired when a value from the datalist is selected or when a valid value is typed into the field.
22
22
  * The `details` of this event contain an object with all `fields` as keys
23
- * and the corresponding values as values, if they are not `undefined`.
23
+ * and the corresponding values as values, even if they are `undefined`.
24
24
  * Example:
25
25
  * ```
26
26
  * {
27
27
  * continent: "Asia",
28
28
  * country: "China",
29
- * city: "Beijing"
29
+ * city: "Beijing",
30
+ * district: undefined,
30
31
  * }
31
32
  * ```
32
33
  */
@@ -14,7 +14,6 @@ const codeExample = String.raw`
14
14
  <gs-mutation-filter
15
15
  initialValue='["A123T"]'
16
16
  width='100%'
17
- height='6.5rem'
18
17
  ></gs-mutation-filter>`;
19
18
 
20
19
  const meta: Meta<MutationFilterProps> = {
@@ -38,7 +37,6 @@ const meta: Meta<MutationFilterProps> = {
38
37
  },
39
38
  },
40
39
  width: { control: 'text' },
41
- height: { control: 'text' },
42
40
  },
43
41
  decorators: [withActions],
44
42
  tags: ['autodocs'],
@@ -50,18 +48,13 @@ const Template: StoryObj<MutationFilterProps> = {
50
48
  render: (args) => {
51
49
  return html` <gs-app lapis="${LAPIS_URL}">
52
50
  <div class="max-w-screen-lg">
53
- <gs-mutation-filter
54
- .initialValue=${args.initialValue}
55
- .width=${args.width}
56
- .height=${args.height}
57
- ></gs-mutation-filter>
51
+ <gs-mutation-filter .initialValue=${args.initialValue} .width=${args.width}></gs-mutation-filter>
58
52
  </div>
59
53
  </gs-app>`;
60
54
  },
61
55
  args: {
62
56
  initialValue: [],
63
57
  width: '100%',
64
- height: '3rem',
65
58
  },
66
59
  };
67
60
 
@@ -91,18 +91,10 @@ export class MutationFilterComponent extends PreactLitAdapter {
91
91
  @property({ type: String })
92
92
  width: string = '100%';
93
93
 
94
- /**
95
- * The height of the component.
96
- *
97
- * Visit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.
98
- */
99
- @property({ type: String })
100
- height: string = '6.5rem';
101
-
102
94
  override render() {
103
95
  return (
104
96
  <ReferenceGenomesAwaiter>
105
- <MutationFilter initialValue={this.initialValue} width={this.width} height={this.height} />
97
+ <MutationFilter initialValue={this.initialValue} width={this.width} />
106
98
  </ReferenceGenomesAwaiter>
107
99
  );
108
100
  }
@@ -23,8 +23,9 @@ const codeExample = String.raw`
23
23
  views='["bar", "line", "bubble", "table"]'
24
24
  confidenceIntervalMethods='["wilson"]'
25
25
  headline="Prevalence over time"
26
- width='100%'
27
- height='700px'
26
+ width="100%"
27
+ height="700px"
28
+ lapisDateField="date"
28
29
  ></gs-prevalence-over-time>`;
29
30
 
30
31
  const meta: Meta<Required<PrevalenceOverTimeProps>> = {
@@ -75,6 +76,7 @@ const Template: StoryObj<Required<PrevalenceOverTimeProps>> = {
75
76
  .width=${args.width}
76
77
  .height=${args.height}
77
78
  .headline=${args.headline}
79
+ .lapisDateField=${args.lapisDateField}
78
80
  ></gs-prevalence-over-time>
79
81
  </gs-app>
80
82
  `,
@@ -95,6 +97,7 @@ export const TwoVariants: StoryObj<Required<PrevalenceOverTimeProps>> = {
95
97
  width: '100%',
96
98
  height: '700px',
97
99
  headline: 'Prevalence over time',
100
+ lapisDateField: 'date',
98
101
  },
99
102
  parameters: {
100
103
  fetchMock: {
@@ -166,6 +169,7 @@ export const OneVariant: StoryObj<Required<PrevalenceOverTimeProps>> = {
166
169
  width: '100%',
167
170
  height: '700px',
168
171
  headline: 'Prevalence over time',
172
+ lapisDateField: 'date',
169
173
  },
170
174
  parameters: {
171
175
  fetchMock: {