@genspectrum/dashboard-components 0.1.4 → 0.1.5

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 (38) hide show
  1. package/custom-elements.json +213 -78
  2. package/dist/dashboard-components.js +303 -53
  3. package/dist/dashboard-components.js.map +1 -1
  4. package/dist/genspectrum-components.d.ts +288 -69
  5. package/dist/style.css +142 -15
  6. package/package.json +3 -3
  7. package/src/preact/aggregatedData/aggregate.stories.tsx +2 -0
  8. package/src/preact/aggregatedData/aggregate.tsx +9 -4
  9. package/src/preact/components/headline.stories.tsx +19 -1
  10. package/src/preact/components/headline.tsx +9 -1
  11. package/src/preact/components/info.stories.tsx +24 -3
  12. package/src/preact/components/info.tsx +49 -5
  13. package/src/preact/dateRangeSelector/date-range-selector.tsx +10 -10
  14. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +3 -0
  15. package/src/preact/mutationComparison/mutation-comparison.tsx +3 -3
  16. package/src/preact/mutationFilter/mutation-filter.tsx +1 -1
  17. package/src/preact/mutations/mutations.stories.tsx +3 -0
  18. package/src/preact/mutations/mutations.tsx +9 -3
  19. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +4 -0
  20. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +14 -4
  21. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +3 -0
  22. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +49 -4
  23. package/src/web-components/display/aggregate-component.stories.ts +3 -0
  24. package/src/web-components/display/aggregate-component.tsx +15 -1
  25. package/src/web-components/display/mutation-comparison-component.stories.ts +3 -0
  26. package/src/web-components/display/mutation-comparison-component.tsx +7 -0
  27. package/src/web-components/display/mutations-component.stories.ts +27 -7
  28. package/src/web-components/display/mutations-component.tsx +58 -4
  29. package/src/web-components/display/prevalence-over-time-component.stories.ts +24 -0
  30. package/src/web-components/display/prevalence-over-time-component.tsx +93 -5
  31. package/src/web-components/display/relative-growth-advantage-component.stories.ts +21 -0
  32. package/src/web-components/display/relative-growth-advantage-component.tsx +54 -3
  33. package/src/web-components/input/date-range-selector-component.stories.ts +17 -2
  34. package/src/web-components/input/date-range-selector-component.tsx +57 -5
  35. package/src/web-components/input/mutation-filter-component.stories.ts +13 -3
  36. package/src/web-components/input/mutation-filter-component.tsx +50 -2
  37. package/src/web-components/input/text-input-component.stories.ts +14 -3
  38. package/src/web-components/input/text-input-component.tsx +23 -1
@@ -3,27 +3,102 @@ import { customElement, property } from 'lit/decorators.js';
3
3
  import PrevalenceOverTime, { type View } from '../../preact/prevalenceOverTime/prevalence-over-time';
4
4
  import { type ConfidenceIntervalMethod } from '../../preact/shared/charts/confideceInterval';
5
5
  import { type NamedLapisFilter, type TemporalGranularity } from '../../types';
6
+ import { type Equals, type Expect } from '../../utils/typeAssertions';
6
7
  import { PreactLitAdapterWithGridJsStyles } from '../PreactLitAdapterWithGridJsStyles';
7
8
 
9
+ /**
10
+ * This component displays the prevalence over time of one or more variants.
11
+ * The prevalence is calculated as the ratio of the number of cases of each variant given as `numerator`
12
+ * to the number of cases of the variant given as `denominator`.
13
+ *
14
+ * In the chart views,
15
+ * - the user can select whether to display a confidence interval (not available in the bubble chart).
16
+ * The confidence interval is calculated using [Wilson score interval](https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval),
17
+ * with a confidence level of 95%.
18
+ * - the x-axis shows time steps in the selected `granularity`.
19
+ * - the user can select the y-axis scale (linear, logistic, logit).
20
+ *
21
+ * ## Views
22
+ *
23
+ * ### Bar View
24
+ *
25
+ * Displays the prevalence over time as a bar chart.
26
+ * Shows a bar for each variant in the `numerator` on every time step.
27
+ *
28
+ * ### Line View
29
+ *
30
+ * Displays the prevalence over time as a line chart.
31
+ * Each data point is connected for better visibility.
32
+ * Shows a line for each variant in the `numerator`.
33
+ *
34
+ * ### Bubble View
35
+ *
36
+ * Displays the prevalence over time as a bubble chart.
37
+ * The size of the bubbles represents the number of cases of the `denominator` variant.
38
+ * The height of the bubbles represents the prevalence of the `numerator` variants.
39
+ *
40
+ * ### Table View
41
+ *
42
+ * Displays the prevalence over time as a table with one row per time point.
43
+ */
8
44
  @customElement('gs-prevalence-over-time')
9
45
  export class PrevalenceOverTimeComponent extends PreactLitAdapterWithGridJsStyles {
46
+ /**
47
+ * Either a single variant or an array of variants to compare.
48
+ * This must be a valid LAPIS filter object with an additional `displayName` property
49
+ * which will be used as the label for the variant in the views,
50
+ * or an array of such objects.
51
+ */
10
52
  @property({ type: Object })
11
- numerator: NamedLapisFilter | NamedLapisFilter[] = { displayName: '' };
53
+ numerator:
54
+ | (Record<string, string | number | null | boolean> & { displayName: string })
55
+ | (Record<string, string | number | null | boolean> & {
56
+ displayName: string;
57
+ })[] = { displayName: '' };
12
58
 
59
+ /**
60
+ * The variant that the variants in `numerator` are compared to.
61
+ */
13
62
  @property({ type: Object })
14
- denominator: NamedLapisFilter = { displayName: '' };
63
+ denominator: Record<string, string | number | null | boolean> & { displayName: string } = { displayName: '' };
15
64
 
65
+ /**
66
+ * The granularity of the time axis.
67
+ */
16
68
  @property({ type: String })
17
- granularity: TemporalGranularity = 'day';
69
+ granularity: 'day' | 'week' | 'month' | 'year' = 'day';
18
70
 
71
+ /**
72
+ * The number of time steps to use for smoothing the data.
73
+ * `0` means no smoothing.
74
+ * Must be a non-negative integer.
75
+ *
76
+ * For a given time, the shown value is the mean of the neighbouring measured values.
77
+ * The `smoothingWindow` value provides the number of neighbouring values to take into account.
78
+ * The resulting time is computed via `Math.floor(smoothingWindow / 2)`.
79
+ */
19
80
  @property({ type: Number })
20
81
  smoothingWindow: number = 0;
21
82
 
83
+ /**
84
+ * A list of tabs with views that this component should provide.
85
+ */
22
86
  @property({ type: Array })
23
- views: View[] = ['bar', 'line', 'bubble', 'table'];
87
+ views: ('bar' | 'line' | 'bubble' | 'table')[] = ['bar', 'line', 'bubble', 'table'];
24
88
 
89
+ /**
90
+ * A list of methods to calculate the confidence interval.
91
+ * The option `none` is always available and disables confidence intervals.
92
+ * Pass an empty array to disable the confidence interval selector.
93
+ */
25
94
  @property({ type: Array })
26
- confidenceIntervalMethods: ConfidenceIntervalMethod[] = ['wilson'];
95
+ confidenceIntervalMethods: ('wilson' | 'none')[] = ['wilson'];
96
+
97
+ /**
98
+ * The headline of the component. Set to an empty string to hide the headline.
99
+ */
100
+ @property({ type: String })
101
+ headline: string | undefined = 'Prevalence over time';
27
102
 
28
103
  /**
29
104
  * The size of the component.
@@ -46,6 +121,7 @@ export class PrevalenceOverTimeComponent extends PreactLitAdapterWithGridJsStyle
46
121
  views={this.views}
47
122
  confidenceIntervalMethods={this.confidenceIntervalMethods}
48
123
  size={this.size}
124
+ headline={this.headline}
49
125
  />
50
126
  );
51
127
  }
@@ -56,3 +132,15 @@ declare global {
56
132
  'gs-prevalence-over-time': PrevalenceOverTimeComponent;
57
133
  }
58
134
  }
135
+
136
+ /* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
137
+ type NumeratorMatches = Expect<
138
+ Equals<typeof PrevalenceOverTimeComponent.prototype.numerator, NamedLapisFilter | NamedLapisFilter[]>
139
+ >;
140
+ type DenominatorMatches = Expect<Equals<typeof PrevalenceOverTimeComponent.prototype.denominator, NamedLapisFilter>>;
141
+ type GranularityMatches = Expect<Equals<typeof PrevalenceOverTimeComponent.prototype.granularity, TemporalGranularity>>;
142
+ type ViewsMatches = Expect<Equals<typeof PrevalenceOverTimeComponent.prototype.views, View[]>>;
143
+ type ConfidenceIntervalMethodsMatches = Expect<
144
+ Equals<typeof PrevalenceOverTimeComponent.prototype.confidenceIntervalMethods, ConfidenceIntervalMethod[]>
145
+ >;
146
+ /* eslint-enable @typescript-eslint/no-unused-vars, no-unused-vars */
@@ -3,11 +3,20 @@ import { html } from 'lit';
3
3
 
4
4
  import './relative-growth-advantage-component';
5
5
  import '../app';
6
+ import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
6
7
  import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
7
8
  import denominator from '../../preact/relativeGrowthAdvantage/__mockData__/denominator.json';
8
9
  import numerator from '../../preact/relativeGrowthAdvantage/__mockData__/numerator.json';
9
10
  import { type RelativeGrowthAdvantageProps } from '../../preact/relativeGrowthAdvantage/relative-growth-advantage';
10
11
 
12
+ const codeExample = String.raw`
13
+ <gs-relative-growth-advantage
14
+ numerator='{ "country": "Switzerland", "pangoLineage": "B.1.1.7", "dateFrom": "2020-12-01" }'
15
+ denominator='{ "country": "Switzerland", "dateFrom": "2020-12-01" }'
16
+ generationTime="7"
17
+ views='["line"]'
18
+ ></gs-relative-growth-advantage>`;
19
+
11
20
  const meta: Meta<RelativeGrowthAdvantageProps> = {
12
21
  title: 'Visualization/Relative growth advantage',
13
22
  component: 'gs-relative-growth-advantage',
@@ -20,7 +29,17 @@ const meta: Meta<RelativeGrowthAdvantageProps> = {
20
29
  control: { type: 'check' },
21
30
  },
22
31
  size: [{ control: 'object' }],
32
+ headline: { control: 'text' },
23
33
  },
34
+ parameters: withComponentDocs({
35
+ componentDocs: {
36
+ tag: 'gs-relative-growth-advantage',
37
+ opensShadowDom: true,
38
+ expectsChildren: false,
39
+ codeExample,
40
+ },
41
+ }),
42
+ tags: ['autodocs'],
24
43
  };
25
44
 
26
45
  export default meta;
@@ -34,6 +53,7 @@ const Template: StoryObj<RelativeGrowthAdvantageProps> = {
34
53
  .generationTime=${args.generationTime}
35
54
  .views=${args.views}
36
55
  .size=${args.size}
56
+ .headline=${args.headline}
37
57
  ></gs-relative-growth-advantage>
38
58
  </gs-app>
39
59
  `,
@@ -47,6 +67,7 @@ export const Default: StoryObj<RelativeGrowthAdvantageProps> = {
47
67
  generationTime: 7,
48
68
  views: ['line'],
49
69
  size: { width: '100%', height: '700px' },
70
+ headline: 'Relative growth advantage',
50
71
  },
51
72
  parameters: {
52
73
  fetchMock: {
@@ -2,21 +2,65 @@ import { customElement, property } from 'lit/decorators.js';
2
2
 
3
3
  import { RelativeGrowthAdvantage, type View } from '../../preact/relativeGrowthAdvantage/relative-growth-advantage';
4
4
  import type { LapisFilter } from '../../types';
5
+ import { type Equals, type Expect } from '../../utils/typeAssertions';
5
6
  import { PreactLitAdapter } from '../PreactLitAdapter';
6
7
 
8
+ /**
9
+ * We assume a discrete time model, where new infections happen exactly every `generationTime` days.
10
+ * This is what we call a "generation".
11
+ *
12
+ * This component estimates the relative growth advantage of a variant by performing a logistic regression.
13
+ * Based on the inferred logistic growth rate, we derive the relative growth advantage (per generation).
14
+ *
15
+ * For details on the scientific method, see:
16
+ * Chen, Chaoran, et al. "Quantification of the spread of SARS-CoV-2 variant B.1.1.7 in Switzerland." Epidemics (2021);
17
+ * doi: [10.1016/j.epidem.2021.100480](https://doi.org/10.1016/j.epidem.2021.100480)
18
+ *
19
+ * This component fetches aggregated data from LAPIS.
20
+ * Then the data is sent to `https://cov-spectrum.org/api/v2/computed/model/chen2021Fitness`
21
+ * which performs the logistic regression and calculates the relative growth advantage.
22
+ *
23
+ * ## Views
24
+ *
25
+ * ### Line View
26
+ *
27
+ * The line view shows the relative growth advantage over time in a line chart.
28
+ * The dots in the plot show the proportions of the focal variant (`numerator`) to the `denominator` variant
29
+ * for every day as observed in the data.
30
+ * The line shows a logistic curve fitted to the data points, including a 95% confidence interval.
31
+ */
7
32
  @customElement('gs-relative-growth-advantage')
8
33
  export class RelativeGrowthAdvantageComponent extends PreactLitAdapter {
34
+ /**
35
+ * The LAPIS filter for the focal variant.
36
+ */
9
37
  @property({ type: Object })
10
- numerator: LapisFilter = {};
38
+ numerator: Record<string, string | number | null | boolean> = {};
11
39
 
40
+ /**
41
+ * The LAPIS filter for the variant that the focal variant (`numerator`) should be compared to.
42
+ */
12
43
  @property({ type: Object })
13
- denominator: LapisFilter = {};
44
+ denominator: Record<string, string | number | null | boolean> = {};
14
45
 
46
+ /**
47
+ * The generation time represents the number of days over which the variant's relative growth advantage is measured.
48
+ * For example, if we set a generation time of 7 days, then we estimate the growth advantage per week.
49
+ */
15
50
  @property({ type: Number })
16
51
  generationTime: number = 7;
17
52
 
53
+ /**
54
+ * A list of tabs with views that this component should provide.
55
+ */
18
56
  @property({ type: Array })
19
- views: View[] = ['line'];
57
+ views: 'line'[] = ['line'];
58
+
59
+ /**
60
+ * The headline of the component. Set to an empty string to hide the headline.
61
+ */
62
+ @property({ type: String })
63
+ headline: string | undefined = 'Relative growth advantage';
20
64
 
21
65
  /**
22
66
  * The size of the component.
@@ -37,6 +81,7 @@ export class RelativeGrowthAdvantageComponent extends PreactLitAdapter {
37
81
  generationTime={this.generationTime}
38
82
  views={this.views}
39
83
  size={this.size}
84
+ headline={this.headline}
40
85
  />
41
86
  );
42
87
  }
@@ -47,3 +92,9 @@ declare global {
47
92
  'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
48
93
  }
49
94
  }
95
+
96
+ /* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
97
+ type NumeratorMatches = Expect<Equals<typeof RelativeGrowthAdvantageComponent.prototype.numerator, LapisFilter>>;
98
+ type DenominatorMatches = Expect<Equals<typeof RelativeGrowthAdvantageComponent.prototype.denominator, LapisFilter>>;
99
+ type ViewsMatches = Expect<Equals<typeof RelativeGrowthAdvantageComponent.prototype.views, View[]>>;
100
+ /* eslint-enable @typescript-eslint/no-unused-vars, no-unused-vars */
@@ -3,6 +3,7 @@ import { expect, waitFor } from '@storybook/test';
3
3
  import type { Meta, StoryObj } from '@storybook/web-components';
4
4
  import { html } from 'lit';
5
5
 
6
+ import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
6
7
  import { LAPIS_URL } from '../../constants';
7
8
  import {
8
9
  type DateRangeSelectorProps,
@@ -19,15 +20,28 @@ import '../app';
19
20
  import { toYYYYMMDD } from '../../preact/dateRangeSelector/dateConversion';
20
21
  import { withinShadowRoot } from '../withinShadowRoot.story';
21
22
 
23
+ const codeExample = String.raw`
24
+ <gs-date-range-selector
25
+ customSelectOptions='[{ "label": "Year 2021", "dateFrom": "2021-01-01", "dateTo": "2021-12-31" }]'
26
+ earliestDate="1970-01-01"
27
+ initialValue="${PRESET_VALUE_LAST_6_MONTHS}"
28
+ ></gs-date-range-selector>`;
29
+
22
30
  const meta: Meta<DateRangeSelectorProps<'CustomDateRange'>> = {
23
31
  title: 'Input/DateRangeSelector',
24
32
  component: 'gs-date-range-selector',
25
- parameters: {
33
+ parameters: withComponentDocs({
26
34
  actions: {
27
35
  handles: ['gs-date-range-changed'],
28
36
  },
29
37
  fetchMock: {},
30
- },
38
+ componentDocs: {
39
+ tag: 'gs-date-range-selector',
40
+ opensShadowDom: true,
41
+ expectsChildren: false,
42
+ codeExample,
43
+ },
44
+ }),
31
45
  argTypes: {
32
46
  initialValue: {
33
47
  control: {
@@ -51,6 +65,7 @@ const meta: Meta<DateRangeSelectorProps<'CustomDateRange'>> = {
51
65
  initialValue: PRESET_VALUE_LAST_6_MONTHS,
52
66
  },
53
67
  decorators: [withActions],
68
+ tags: ['autodocs'],
54
69
  };
55
70
 
56
71
  export default meta;
@@ -5,21 +5,64 @@ import {
5
5
  DateRangeSelector,
6
6
  type PresetOptionValues,
7
7
  } from '../../preact/dateRangeSelector/date-range-selector';
8
+ import { type Equals, type Expect } from '../../utils/typeAssertions';
8
9
  import { PreactLitAdapter } from '../PreactLitAdapter';
9
10
 
10
11
  /**
11
- * @fires {CustomEvent<{ dateFrom: string; dateTo: string; }>} gs-date-range-changed - When the date range has changed
12
+ * ## Context
13
+ * This component is a group of input fields designed to specify a date range. It consists of 3 fields:
14
+ *
15
+ * - a select field to choose a predefined date range,
16
+ * - an input field with an attached date picker for the start date,
17
+ * - an input field with an attached date picker for the end date.
18
+ *
19
+ * Setting a value in the select field will overwrite the previous values of the start and end date.
20
+ * Setting a value in either of the date pickers will set the select field to "custom",
21
+ * which represents an arbitrary date range.
22
+ *
23
+ * @fires {CustomEvent<{ dateFrom: string; dateTo: string; }>} gs-date-range-changed
24
+ * Fired when:
25
+ * - The select field is changed,
26
+ * - A date is selected in either of the date pickers,
27
+ * - A date was typed into either of the date input fields, and the input field loses focus ("on blur").
28
+ * Contains the dates in the format `YYYY-MM-DD`.
12
29
  */
13
30
  @customElement('gs-date-range-selector')
14
- export class DateRangeSelectorComponent<CustomLabel extends string> extends PreactLitAdapter {
31
+ export class DateRangeSelectorComponent extends PreactLitAdapter {
32
+ /**
33
+ * An array of custom options that the select field should provide,
34
+ * in addition to the predefined options.
35
+ * The `label` will be shown to the user, and it will be available as `initialValue`.
36
+ * The dates must be in the format `YYYY-MM-DD`.
37
+ */
15
38
  @property({ type: Array })
16
- customSelectOptions: CustomSelectOption<CustomLabel>[] = [];
39
+ customSelectOptions: { label: string; dateFrom: string; dateTo: string }[] = [];
17
40
 
41
+ /**
42
+ * The `dateFrom` value to use in the `allTimes` preset in the format `YYYY-MM-DD`.
43
+ */
18
44
  @property({ type: String })
19
45
  earliestDate: string | undefined = '1900-01-01';
20
46
 
47
+ // prettier-ignore
48
+ // The multiline union type must not start with `| 'custom'` - Storybook will list "" as the first type which is wrong
49
+ /**
50
+ * The initial value to use for this date range selector.
51
+ * Must be a valid label from the preset labels or a `label` given in the `customSelectOptions`.
52
+ *
53
+ * If the value is invalid, the component will default to `'last6Months'`.
54
+ */
21
55
  @property()
22
- initialValue: PresetOptionValues | CustomLabel | string | undefined = '';
56
+ initialValue:
57
+ 'custom'
58
+ | 'allTimes'
59
+ | 'last2Weeks'
60
+ | 'lastMonth'
61
+ | 'last2Months'
62
+ | 'last3Months'
63
+ | 'last6Months'
64
+ | string
65
+ | undefined = 'last6Months';
23
66
 
24
67
  override render() {
25
68
  return (
@@ -34,7 +77,7 @@ export class DateRangeSelectorComponent<CustomLabel extends string> extends Prea
34
77
 
35
78
  declare global {
36
79
  interface HTMLElementTagNameMap {
37
- 'gs-date-range-selector': DateRangeSelectorComponent<string>;
80
+ 'gs-date-range-selector': DateRangeSelectorComponent;
38
81
  }
39
82
 
40
83
  interface HTMLElementEventMap {
@@ -44,3 +87,12 @@ declare global {
44
87
  }>;
45
88
  }
46
89
  }
90
+
91
+ /* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
92
+ type CustomSelectOptionsMatches = Expect<
93
+ Equals<typeof DateRangeSelectorComponent.prototype.customSelectOptions, CustomSelectOption<string>[]>
94
+ >;
95
+ type InitialValueMatches = Expect<
96
+ Equals<typeof DateRangeSelectorComponent.prototype.initialValue, PresetOptionValues | string | undefined>
97
+ >;
98
+ /* eslint-enable @typescript-eslint/no-unused-vars, no-unused-vars */
@@ -3,22 +3,32 @@ import { expect, fn, userEvent, waitFor } from '@storybook/test';
3
3
  import type { Meta, StoryObj } from '@storybook/web-components';
4
4
  import { html } from 'lit';
5
5
 
6
+ import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
6
7
  import { LAPIS_URL } from '../../constants';
7
8
  import '../app';
8
9
  import { type MutationFilterProps } from '../../preact/mutationFilter/mutation-filter';
9
10
  import { withinShadowRoot } from '../withinShadowRoot.story';
10
11
  import './mutation-filter-component';
11
12
 
12
- const meta: Meta = {
13
+ const codeExample = String.raw`<gs-mutation-filter initialValue='["A123T"]'></gs-mutation-filter>`;
14
+
15
+ const meta: Meta<MutationFilterProps> = {
13
16
  title: 'Input/Mutation filter',
14
17
  component: 'gs-mutation-filter',
15
- parameters: {
18
+ parameters: withComponentDocs({
16
19
  actions: {
17
20
  handles: ['gs-mutation-filter-changed', 'gs-mutation-filter-on-blur'],
18
21
  },
19
22
  fetchMock: {},
20
- },
23
+ componentDocs: {
24
+ tag: 'gs-mutation-filter',
25
+ opensShadowDom: true,
26
+ expectsChildren: false,
27
+ codeExample,
28
+ },
29
+ }),
21
30
  decorators: [withActions],
31
+ tags: ['autodocs'],
22
32
  };
23
33
 
24
34
  export default meta;
@@ -6,11 +6,59 @@ import { MutationFilter, type SelectedMutationFilterStrings } from '../../preact
6
6
  import { PreactLitAdapter } from '../PreactLitAdapter';
7
7
 
8
8
  /**
9
- * @fires {CustomEvent<SelectedMutationFilterStrings>} gs-mutation-filter-changed - When the mutation filter values have changed
10
- * @fires {CustomEvent<SelectedMutationFilterStrings>} gs-mutation-filter-on-blur - When the mutation filter has lost focus
9
+ * ## Context
10
+ * This component provides an input field to specify filters for nucleotide and amino acid mutations and insertions.
11
+ *
12
+ * Input values have to be provided one at a time and submitted by pressing the Enter key or by clicking the '+' button.
13
+ * After submission, the component validates the input and fires an event with the selected mutations.
14
+ * All previously selected mutations are displayed at the input field and added to the event.
15
+ * Users can remove a mutation by clicking the 'x' button next to the mutation.
16
+ *
17
+ * Validation of the input is performed according to the following rules:
18
+ *
19
+ * Mutations have to conform to the following format: `<gene/segment>:<symbol at reference><position><Substituted symbol/Deletion>`
20
+ * - Gene/segment: The gene or segment where the mutation occurs. Must be contained in the reference genome
21
+ * (Optional for elements with only one gene/segment)
22
+ * - Symbol at reference: The symbol at the reference position. (Optional)
23
+ * - Position: The position of the mutation. (Required)
24
+ * - Substituted symbol/Deletion: The substituted symbol or '-' for a deletion. (Required)
25
+ * Example: S:614G, 614G, 614- or 614G
26
+ *
27
+ * Insertions have to conform to the following format: `ins_<gene/segment>:<position>:<Inserted symbols>`
28
+ * - Gene/segment: The gene or segment where the insertion occurs. Must be contained in the reference genome
29
+ * (Optional for elements with only one gene/segment)
30
+ * - Position: The position of the insertion. (Required)
31
+ * - Inserted symbols: The symbols that are inserted. (Required)
32
+ * Example: ins_S:614:G, ins_614:G
33
+ *
34
+ * @fires {CustomEvent<{
35
+ * nucleotideMutations: string[],
36
+ * aminoAcidMutations: string[],
37
+ * nucleotideInsertions: string[],
38
+ * aminoAcidInsertions: string[]
39
+ * }>} gs-mutation-filter-changed
40
+ * Fired when:
41
+ * - The user has submitted a valid mutation or insertion
42
+ * - The user has removed a mutation or insertion
43
+ *
44
+ * @fires {CustomEvent<{
45
+ * nucleotideMutations: string[],
46
+ * aminoAcidMutations: string[],
47
+ * nucleotideInsertions: string[],
48
+ * aminoAcidInsertions: string[]
49
+ * }>} gs-mutation-filter-on-blur
50
+ * Fired when:
51
+ * - the mutation filter has lost focus
52
+ * Contains the selected mutations in the format
11
53
  */
12
54
  @customElement('gs-mutation-filter')
13
55
  export class MutationFilterComponent extends PreactLitAdapter {
56
+ /**
57
+ * The initial value to use for this mutation filter.
58
+ * Must be either
59
+ * - an array of strings of valid mutations.
60
+ * - an object with the keys `nucleotideMutations`, `aminoAcidMutations`, `nucleotideInsertions` and `aminoAcidInsertions` and corresponding string arrays.
61
+ */
14
62
  @property()
15
63
  initialValue: SelectedMutationFilterStrings | string[] | undefined = undefined;
16
64
 
@@ -3,6 +3,7 @@ import { expect, fn, userEvent, waitFor } from '@storybook/test';
3
3
  import type { Meta, StoryObj } from '@storybook/web-components';
4
4
  import { html } from 'lit';
5
5
 
6
+ import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
6
7
  import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
7
8
  import '../app';
8
9
  import './text-input-component';
@@ -10,10 +11,13 @@ import data from '../../preact/textInput/__mockData__/aggregated_hosts.json';
10
11
  import type { TextInputProps } from '../../preact/textInput/text-input';
11
12
  import { withinShadowRoot } from '../withinShadowRoot.story';
12
13
 
13
- const meta: Meta = {
14
+ const codeExample = String.raw`
15
+ <gs-text-input lapisField="host" placeholderText="Enter host name" initialValue="Homo sapiens"></gs-text-input>`;
16
+
17
+ const meta: Meta<TextInputProps> = {
14
18
  title: 'Input/Text input',
15
19
  component: 'gs-text-input',
16
- parameters: {
20
+ parameters: withComponentDocs({
17
21
  actions: {
18
22
  handles: ['gs-text-input-changed'],
19
23
  },
@@ -34,8 +38,15 @@ const meta: Meta = {
34
38
  },
35
39
  ],
36
40
  },
37
- },
41
+ componentDocs: {
42
+ tag: 'gs-text-input',
43
+ opensShadowDom: true,
44
+ expectsChildren: false,
45
+ codeExample,
46
+ },
47
+ }),
38
48
  decorators: [withActions],
49
+ tags: ['autodocs'],
39
50
  };
40
51
 
41
52
  export default meta;
@@ -4,16 +4,38 @@ import { TextInput } from '../../preact/textInput/text-input';
4
4
  import { PreactLitAdapter } from '../PreactLitAdapter';
5
5
 
6
6
  /**
7
- * @fires {CustomEvent<Record<string, string>>} gs-text-input-changed - When the text input has changed
7
+ *
8
+ * ## Context
9
+ *
10
+ * This component provides a text input field to specify filters for arbitrary fields of this Lapis instance.
11
+ *
12
+ * @fires {CustomEvent<Record<string, string>>} gs-text-input-changed
13
+ * Fired when the input field is changed.
14
+ * The `details` of this event contain an object with the `lapisField` as key and the input value as value.
15
+ * Example:
16
+ * ```
17
+ * {
18
+ * "host": "Homo sapiens"
19
+ * }
20
+ * ```
8
21
  */
9
22
  @customElement('gs-text-input')
10
23
  export class TextInputComponent extends PreactLitAdapter {
24
+ /**
25
+ * The initial value to use for this text input.
26
+ */
11
27
  @property()
12
28
  initialValue: string | undefined = '';
13
29
 
30
+ /**
31
+ * The Lapis field name to use for this text input.
32
+ */
14
33
  @property()
15
34
  lapisField = '';
16
35
 
36
+ /**
37
+ * The placeholder text to display in the input field.
38
+ */
17
39
  @property()
18
40
  placeholderText: string | undefined = '';
19
41