@genspectrum/dashboard-components 0.2.0 → 0.3.1
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.
- package/custom-elements.json +330 -179
- package/dist/dashboard-components.js +379 -187
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +160 -114
- package/dist/style.css +171 -71
- package/package.json +1 -2
- package/src/constants.ts +1 -1
- package/src/lapisApi/lapisApi.ts +46 -2
- package/src/lapisApi/lapisTypes.ts +14 -0
- package/src/preact/aggregatedData/aggregate.stories.tsx +4 -2
- package/src/preact/aggregatedData/aggregate.tsx +8 -6
- package/src/preact/components/error-boundary.stories.tsx +6 -14
- package/src/preact/components/error-boundary.tsx +2 -11
- package/src/preact/components/error-display.stories.tsx +12 -5
- package/src/preact/components/error-display.tsx +37 -3
- package/src/preact/components/loading-display.stories.tsx +1 -1
- package/src/preact/components/resize-container.tsx +5 -14
- package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +2 -0
- package/src/preact/dateRangeSelector/date-range-selector.tsx +11 -8
- package/src/preact/locationFilter/fetchAutocompletionList.ts +15 -1
- package/src/preact/locationFilter/location-filter.stories.tsx +1 -1
- package/src/preact/locationFilter/location-filter.tsx +16 -30
- package/src/preact/mutationComparison/mutation-comparison.stories.tsx +6 -3
- package/src/preact/mutationComparison/mutation-comparison.tsx +10 -13
- package/src/preact/mutationComparison/queryMutationData.ts +2 -3
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +8 -8
- package/src/preact/mutationFilter/mutation-filter.tsx +7 -6
- package/src/preact/mutations/mutations.stories.tsx +6 -3
- package/src/preact/mutations/mutations.tsx +8 -6
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +14 -7
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +10 -8
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +6 -3
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +9 -7
- package/src/preact/textInput/text-input.stories.tsx +26 -0
- package/src/preact/textInput/text-input.tsx +4 -5
- package/src/query/queryPrevalenceOverTime.ts +4 -10
- package/src/types.ts +4 -1
- package/src/web-components/ResizeContainer.mdx +13 -0
- package/src/web-components/app.ts +3 -1
- package/src/web-components/input/gs-date-range-selector.stories.ts +10 -2
- package/src/web-components/input/gs-date-range-selector.tsx +26 -16
- package/src/web-components/input/gs-location-filter.stories.ts +5 -7
- package/src/web-components/input/gs-location-filter.tsx +6 -7
- package/src/web-components/input/gs-mutation-filter.stories.ts +11 -8
- package/src/web-components/input/gs-mutation-filter.tsx +38 -26
- package/src/web-components/input/gs-text-input.stories.ts +3 -3
- package/src/web-components/input/gs-text-input.tsx +10 -10
- package/src/web-components/input/introduction.mdx +11 -0
- package/src/web-components/introduction.mdx +15 -0
- package/src/web-components/visualization/gs-aggregate.stories.ts +19 -6
- package/src/web-components/visualization/gs-aggregate.tsx +31 -15
- package/src/web-components/visualization/gs-mutation-comparison.stories.ts +13 -7
- package/src/web-components/visualization/gs-mutation-comparison.tsx +26 -17
- package/src/web-components/visualization/gs-mutations.stories.ts +14 -8
- package/src/web-components/visualization/gs-mutations.tsx +18 -8
- package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +28 -18
- package/src/web-components/visualization/gs-prevalence-over-time.tsx +45 -22
- package/src/web-components/visualization/gs-relative-growth-advantage.stories.ts +11 -5
- package/src/web-components/visualization/gs-relative-growth-advantage.tsx +21 -9
|
@@ -14,7 +14,7 @@ import Headline from '../components/headline';
|
|
|
14
14
|
import Info, { InfoHeadline1, InfoHeadline2, InfoLink, InfoParagraph } from '../components/info';
|
|
15
15
|
import { LoadingDisplay } from '../components/loading-display';
|
|
16
16
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
17
|
-
import { ResizeContainer
|
|
17
|
+
import { ResizeContainer } from '../components/resize-container';
|
|
18
18
|
import { ScalingSelector } from '../components/scaling-selector';
|
|
19
19
|
import Tabs from '../components/tabs';
|
|
20
20
|
import { type ScaleType } from '../shared/charts/getYAxisScale';
|
|
@@ -23,7 +23,8 @@ import { useQuery } from '../useQuery';
|
|
|
23
23
|
export type View = 'line';
|
|
24
24
|
|
|
25
25
|
export interface RelativeGrowthAdvantageProps extends RelativeGrowthAdvantagePropsInner {
|
|
26
|
-
|
|
26
|
+
width: string;
|
|
27
|
+
height: string;
|
|
27
28
|
headline?: string;
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -36,17 +37,18 @@ export interface RelativeGrowthAdvantagePropsInner {
|
|
|
36
37
|
|
|
37
38
|
export const RelativeGrowthAdvantage: FunctionComponent<RelativeGrowthAdvantageProps> = ({
|
|
38
39
|
views,
|
|
39
|
-
|
|
40
|
+
width,
|
|
41
|
+
height,
|
|
40
42
|
numerator,
|
|
41
43
|
denominator,
|
|
42
44
|
generationTime,
|
|
43
45
|
headline = 'Relative growth advantage',
|
|
44
46
|
}) => {
|
|
45
|
-
const
|
|
47
|
+
const size = { height, width };
|
|
46
48
|
|
|
47
49
|
return (
|
|
48
|
-
<ErrorBoundary size={size}
|
|
49
|
-
<ResizeContainer size={size}
|
|
50
|
+
<ErrorBoundary size={size} headline={headline}>
|
|
51
|
+
<ResizeContainer size={size}>
|
|
50
52
|
<Headline heading={headline}>
|
|
51
53
|
<RelativeGrowthAdvantageInner
|
|
52
54
|
views={views}
|
|
@@ -60,7 +62,7 @@ export const RelativeGrowthAdvantage: FunctionComponent<RelativeGrowthAdvantageP
|
|
|
60
62
|
);
|
|
61
63
|
};
|
|
62
64
|
|
|
63
|
-
export const RelativeGrowthAdvantageInner: FunctionComponent<
|
|
65
|
+
export const RelativeGrowthAdvantageInner: FunctionComponent<RelativeGrowthAdvantagePropsInner> = ({
|
|
64
66
|
numerator,
|
|
65
67
|
denominator,
|
|
66
68
|
generationTime,
|
|
@@ -33,6 +33,29 @@ const meta: Meta<TextInputProps> = {
|
|
|
33
33
|
},
|
|
34
34
|
},
|
|
35
35
|
decorators: [withActions],
|
|
36
|
+
argTypes: {
|
|
37
|
+
lapisField: {
|
|
38
|
+
control: {
|
|
39
|
+
type: 'select',
|
|
40
|
+
},
|
|
41
|
+
options: ['host'],
|
|
42
|
+
},
|
|
43
|
+
placeholderText: {
|
|
44
|
+
control: {
|
|
45
|
+
type: 'text',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
initialValue: {
|
|
49
|
+
control: {
|
|
50
|
+
type: 'text',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
width: {
|
|
54
|
+
control: {
|
|
55
|
+
type: 'text',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
36
59
|
};
|
|
37
60
|
|
|
38
61
|
export default meta;
|
|
@@ -44,12 +67,15 @@ export const Default: StoryObj<TextInputProps> = {
|
|
|
44
67
|
lapisField={args.lapisField}
|
|
45
68
|
placeholderText={args.placeholderText}
|
|
46
69
|
initialValue={args.initialValue}
|
|
70
|
+
width={args.width}
|
|
47
71
|
/>
|
|
48
72
|
</LapisUrlContext.Provider>
|
|
49
73
|
),
|
|
50
74
|
args: {
|
|
51
75
|
lapisField: 'host',
|
|
52
76
|
placeholderText: 'Enter a host name',
|
|
77
|
+
initialValue: '',
|
|
78
|
+
width: '100%',
|
|
53
79
|
},
|
|
54
80
|
};
|
|
55
81
|
|
|
@@ -17,16 +17,15 @@ export interface TextInputInnerProps {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export interface TextInputProps extends TextInputInnerProps {
|
|
20
|
-
width
|
|
20
|
+
width: string;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export const TextInput: FunctionComponent<TextInputProps> = ({ width, lapisField, placeholderText, initialValue }) => {
|
|
24
|
-
const
|
|
25
|
-
const size = width === undefined ? undefined : { width, height: defaultSize.height };
|
|
24
|
+
const size = { width, height: '3rem' };
|
|
26
25
|
|
|
27
26
|
return (
|
|
28
|
-
<ErrorBoundary
|
|
29
|
-
<ResizeContainer size={size}
|
|
27
|
+
<ErrorBoundary size={size}>
|
|
28
|
+
<ResizeContainer size={size}>
|
|
30
29
|
<TextInputInner lapisField={lapisField} placeholderText={placeholderText} initialValue={initialValue} />
|
|
31
30
|
</ResizeContainer>
|
|
32
31
|
</ErrorBoundary>
|
|
@@ -23,7 +23,7 @@ export type PrevalenceOverTimeVariantData = {
|
|
|
23
23
|
|
|
24
24
|
export function queryPrevalenceOverTime(
|
|
25
25
|
numeratorFilter: NamedLapisFilter | NamedLapisFilter[],
|
|
26
|
-
denominatorFilter:
|
|
26
|
+
denominatorFilter: LapisFilter,
|
|
27
27
|
granularity: TemporalGranularity,
|
|
28
28
|
smoothingWindow: number,
|
|
29
29
|
lapis: string,
|
|
@@ -31,10 +31,10 @@ export function queryPrevalenceOverTime(
|
|
|
31
31
|
): Promise<PrevalenceOverTimeData> {
|
|
32
32
|
const numeratorFilters = makeArray(numeratorFilter);
|
|
33
33
|
|
|
34
|
-
const denominatorData = fetchAndPrepare(
|
|
34
|
+
const denominatorData = fetchAndPrepare(denominatorFilter, granularity, smoothingWindow);
|
|
35
35
|
const subQueries = numeratorFilters.map(async (namedLapisFilter) => {
|
|
36
|
-
const { displayName,
|
|
37
|
-
const numeratorData = fetchAndPrepare(
|
|
36
|
+
const { displayName, lapisFilter } = namedLapisFilter;
|
|
37
|
+
const numeratorData = fetchAndPrepare(lapisFilter, granularity, smoothingWindow);
|
|
38
38
|
const divide = new DivisionOperator(
|
|
39
39
|
numeratorData,
|
|
40
40
|
denominatorData,
|
|
@@ -53,12 +53,6 @@ export function queryPrevalenceOverTime(
|
|
|
53
53
|
return Promise.all(subQueries);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
function getFilter(filter: NamedLapisFilter) {
|
|
57
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
58
|
-
const { displayName, ...filterWithoutDisplayName } = filter;
|
|
59
|
-
return filterWithoutDisplayName;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
56
|
function makeArray<T>(arrayOrSingleItem: T | T[]) {
|
|
63
57
|
if (Array.isArray(arrayOrSingleItem)) {
|
|
64
58
|
return arrayOrSingleItem;
|
package/src/types.ts
CHANGED
|
@@ -2,7 +2,10 @@ import { type Deletion, type Insertion, type Substitution } from './utils/mutati
|
|
|
2
2
|
|
|
3
3
|
export type LapisFilter = Record<string, string | number | null | boolean>;
|
|
4
4
|
|
|
5
|
-
export type NamedLapisFilter =
|
|
5
|
+
export type NamedLapisFilter = {
|
|
6
|
+
lapisFilter: LapisFilter;
|
|
7
|
+
displayName: string;
|
|
8
|
+
};
|
|
6
9
|
|
|
7
10
|
export type TemporalGranularity = 'day' | 'week' | 'month' | 'year';
|
|
8
11
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Meta } from '@storybook/blocks';
|
|
2
|
+
|
|
3
|
+
<Meta title='Components/Size of components' />
|
|
4
|
+
|
|
5
|
+
# Size of components
|
|
6
|
+
|
|
7
|
+
All visualization and input components can be provided with a width prop (or height, when applicable) to control the size of the component.
|
|
8
|
+
If not provided, the default width or height is used. In most cases, the default is a width of '100%' of its parent container and a predefined height.
|
|
9
|
+
|
|
10
|
+
Both width and height can be set using the css units (e.g. 'px', 'em', 'rem', '%', 'vh', 'vw', etc.).
|
|
11
|
+
By using '%', the size of the component can be controlled by the parent component.
|
|
12
|
+
|
|
13
|
+
Caution: When using '%' as a unit, the parent component should have a defined width or height.
|
|
@@ -27,6 +27,8 @@ import { fetchReferenceGenome } from '../lapisApi/lapisApi';
|
|
|
27
27
|
@customElement('gs-app')
|
|
28
28
|
export class App extends LitElement {
|
|
29
29
|
/**
|
|
30
|
+
* Required.
|
|
31
|
+
*
|
|
30
32
|
* The URL of the LAPIS instance that all children of this component will use.
|
|
31
33
|
*/
|
|
32
34
|
@provide({ context: lapisContext })
|
|
@@ -54,7 +56,7 @@ export class App extends LitElement {
|
|
|
54
56
|
|
|
55
57
|
override render() {
|
|
56
58
|
return this.updateReferenceGenome.render({
|
|
57
|
-
complete: () => html
|
|
59
|
+
complete: () => html``, // Children will be rendered in the light DOM anyway. We can't use slots without a shadow DOM.
|
|
58
60
|
error: () =>
|
|
59
61
|
html` <div class="m-2 w-full alert alert-error">
|
|
60
62
|
Error: Cannot fetch reference genome. Is LAPIS available?
|
|
@@ -26,9 +26,10 @@ const codeExample = String.raw`
|
|
|
26
26
|
earliestDate="1970-01-01"
|
|
27
27
|
initialValue="${PRESET_VALUE_LAST_6_MONTHS}"
|
|
28
28
|
width="100%"
|
|
29
|
+
dateColumn="myDateColumn"
|
|
29
30
|
></gs-date-range-selector>`;
|
|
30
31
|
|
|
31
|
-
const meta: Meta<DateRangeSelectorProps<'CustomDateRange'
|
|
32
|
+
const meta: Meta<Required<DateRangeSelectorProps<'CustomDateRange'>>> = {
|
|
32
33
|
title: 'Input/DateRangeSelector',
|
|
33
34
|
component: 'gs-date-range-selector',
|
|
34
35
|
parameters: withComponentDocs({
|
|
@@ -58,6 +59,7 @@ const meta: Meta<DateRangeSelectorProps<'CustomDateRange'>> = {
|
|
|
58
59
|
'CustomDateRange',
|
|
59
60
|
],
|
|
60
61
|
},
|
|
62
|
+
dateColumn: { control: { type: 'text' } },
|
|
61
63
|
customSelectOptions: {
|
|
62
64
|
control: {
|
|
63
65
|
type: 'object',
|
|
@@ -78,6 +80,7 @@ const meta: Meta<DateRangeSelectorProps<'CustomDateRange'>> = {
|
|
|
78
80
|
customSelectOptions: [{ label: 'CustomDateRange', dateFrom: '2021-01-01', dateTo: '2021-12-31' }],
|
|
79
81
|
earliestDate: '1970-01-01',
|
|
80
82
|
initialValue: PRESET_VALUE_LAST_6_MONTHS,
|
|
83
|
+
dateColumn: 'aDateColumn',
|
|
81
84
|
width: '100%',
|
|
82
85
|
},
|
|
83
86
|
decorators: [withActions],
|
|
@@ -86,7 +89,7 @@ const meta: Meta<DateRangeSelectorProps<'CustomDateRange'>> = {
|
|
|
86
89
|
|
|
87
90
|
export default meta;
|
|
88
91
|
|
|
89
|
-
export const DateRangeSelectorStory: StoryObj<DateRangeSelectorProps<'CustomDateRange'
|
|
92
|
+
export const DateRangeSelectorStory: StoryObj<Required<DateRangeSelectorProps<'CustomDateRange'>>> = {
|
|
90
93
|
render: (args) =>
|
|
91
94
|
html` <gs-app lapis="${LAPIS_URL}">
|
|
92
95
|
<div class="max-w-screen-lg">
|
|
@@ -95,6 +98,7 @@ export const DateRangeSelectorStory: StoryObj<DateRangeSelectorProps<'CustomDate
|
|
|
95
98
|
.earliestDate=${args.earliestDate}
|
|
96
99
|
.initialValue=${args.initialValue}
|
|
97
100
|
.width=${args.width}
|
|
101
|
+
.dateColumn=${args.dateColumn}
|
|
98
102
|
></gs-date-range-selector>
|
|
99
103
|
</div>
|
|
100
104
|
</gs-app>`,
|
|
@@ -108,5 +112,9 @@ export const DateRangeSelectorStory: StoryObj<DateRangeSelectorProps<'CustomDate
|
|
|
108
112
|
expect(dateTo()).toHaveValue(toYYYYMMDD(new Date()));
|
|
109
113
|
});
|
|
110
114
|
});
|
|
115
|
+
|
|
116
|
+
// Due to the limitations of storybook testing which does not fire an event,
|
|
117
|
+
// when selecting a value from the dropdown we can't test the fired event here.
|
|
118
|
+
// An e2e test (using playwright) for that can be found in tests/dateRangeSelector.spec.ts
|
|
111
119
|
},
|
|
112
120
|
};
|
|
@@ -10,7 +10,8 @@ import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* ## Context
|
|
13
|
-
* This component is a group of input fields designed to specify
|
|
13
|
+
* This component is a group of input fields designed to specify date range filters
|
|
14
|
+
* for a given date column of this Lapis instance. It consists of 3 fields:
|
|
14
15
|
*
|
|
15
16
|
* - a select field to choose a predefined date range,
|
|
16
17
|
* - an input field with an attached date picker for the start date,
|
|
@@ -20,12 +21,21 @@ import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
|
20
21
|
* Setting a value in either of the date pickers will set the select field to "custom",
|
|
21
22
|
* which represents an arbitrary date range.
|
|
22
23
|
*
|
|
23
|
-
* @fires {CustomEvent<{
|
|
24
|
+
* @fires {CustomEvent<{ `${dateColumn}From`: string; `${dateColumn}To`: string; }>} gs-date-range-changed
|
|
24
25
|
* Fired when:
|
|
25
26
|
* - The select field is changed,
|
|
26
27
|
* - A date is selected in either of the date pickers,
|
|
27
28
|
* - A date was typed into either of the date input fields, and the input field loses focus ("on blur").
|
|
28
29
|
* Contains the dates in the format `YYYY-MM-DD`.
|
|
30
|
+
*
|
|
31
|
+
* Example: For `dateColumn = yourDate`, an event with `detail` containing
|
|
32
|
+
* ```
|
|
33
|
+
* {
|
|
34
|
+
* yourDataFrom: "2021-01-01",
|
|
35
|
+
* yourDataTo: "2021-12-31"
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
* will be fired.
|
|
29
39
|
*/
|
|
30
40
|
@customElement('gs-date-range-selector')
|
|
31
41
|
export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
@@ -42,7 +52,7 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
42
52
|
* The `dateFrom` value to use in the `allTimes` preset in the format `YYYY-MM-DD`.
|
|
43
53
|
*/
|
|
44
54
|
@property({ type: String })
|
|
45
|
-
earliestDate: string
|
|
55
|
+
earliestDate: string = '1900-01-01';
|
|
46
56
|
|
|
47
57
|
// prettier-ignore
|
|
48
58
|
// The multiline union type must not start with `| 'custom'` - Storybook will list "" as the first type which is wrong
|
|
@@ -61,19 +71,21 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
61
71
|
| 'last2Months'
|
|
62
72
|
| 'last3Months'
|
|
63
73
|
| 'last6Months'
|
|
64
|
-
| string
|
|
65
|
-
| undefined = 'last6Months';
|
|
74
|
+
| string = 'last6Months';
|
|
66
75
|
|
|
67
76
|
/**
|
|
68
77
|
* The width of the component.
|
|
69
78
|
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* The width should be a string with a unit in css style, e.g. '100%', '500px' or '50vw'.
|
|
73
|
-
* If the unit is %, the size will be relative to the container of the component.
|
|
79
|
+
* Visit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.
|
|
74
80
|
*/
|
|
75
|
-
@property({ type:
|
|
76
|
-
width: string
|
|
81
|
+
@property({ type: String })
|
|
82
|
+
width: string = '100%';
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The name of the column in LAPIS that contains the date information.
|
|
86
|
+
*/
|
|
87
|
+
@property({ type: String })
|
|
88
|
+
dateColumn: string = 'date';
|
|
77
89
|
|
|
78
90
|
override render() {
|
|
79
91
|
return (
|
|
@@ -81,6 +93,7 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
81
93
|
customSelectOptions={this.customSelectOptions}
|
|
82
94
|
earliestDate={this.earliestDate}
|
|
83
95
|
initialValue={this.initialValue}
|
|
96
|
+
dateColumn={this.dateColumn}
|
|
84
97
|
width={this.width}
|
|
85
98
|
/>
|
|
86
99
|
);
|
|
@@ -93,10 +106,7 @@ declare global {
|
|
|
93
106
|
}
|
|
94
107
|
|
|
95
108
|
interface HTMLElementEventMap {
|
|
96
|
-
'gs-date-range-changed': CustomEvent<
|
|
97
|
-
dateFrom: string;
|
|
98
|
-
dateTo: string;
|
|
99
|
-
}>;
|
|
109
|
+
'gs-date-range-changed': CustomEvent<Record<string, string>>;
|
|
100
110
|
}
|
|
101
111
|
}
|
|
102
112
|
|
|
@@ -105,6 +115,6 @@ type CustomSelectOptionsMatches = Expect<
|
|
|
105
115
|
Equals<typeof DateRangeSelectorComponent.prototype.customSelectOptions, CustomSelectOption<string>[]>
|
|
106
116
|
>;
|
|
107
117
|
type InitialValueMatches = Expect<
|
|
108
|
-
Equals<typeof DateRangeSelectorComponent.prototype.initialValue, PresetOptionValues | string
|
|
118
|
+
Equals<typeof DateRangeSelectorComponent.prototype.initialValue, PresetOptionValues | string>
|
|
109
119
|
>;
|
|
110
120
|
/* eslint-enable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
@@ -14,8 +14,8 @@ import { withinShadowRoot } from '../withinShadowRoot.story';
|
|
|
14
14
|
|
|
15
15
|
const codeExample = String.raw`
|
|
16
16
|
<gs-location-filter
|
|
17
|
-
fields="
|
|
18
|
-
|
|
17
|
+
fields='["region", "country"]'
|
|
18
|
+
initialValue='Europe / Switzerland'
|
|
19
19
|
width="100%"
|
|
20
20
|
></gs-location-filter>`;
|
|
21
21
|
|
|
@@ -134,7 +134,9 @@ export const FetchingLocationsFails: StoryObj<LocationFilterProps> = {
|
|
|
134
134
|
matcher: aggregatedEndpointMatcher,
|
|
135
135
|
response: {
|
|
136
136
|
status: 400,
|
|
137
|
-
body: {
|
|
137
|
+
body: {
|
|
138
|
+
error: { status: 400, detail: 'Dummy error message from mock LAPIS', type: 'about:blank' },
|
|
139
|
+
},
|
|
138
140
|
},
|
|
139
141
|
},
|
|
140
142
|
],
|
|
@@ -167,7 +169,6 @@ export const FiresEvent: StoryObj<LocationFilterProps> = {
|
|
|
167
169
|
play: async ({ canvasElement, step }) => {
|
|
168
170
|
const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter');
|
|
169
171
|
|
|
170
|
-
const submitButton = () => canvas.getByRole('button', { name: 'Submit' });
|
|
171
172
|
const inputField = () => canvas.getByRole('combobox');
|
|
172
173
|
|
|
173
174
|
const listenerMock = fn();
|
|
@@ -183,14 +184,12 @@ export const FiresEvent: StoryObj<LocationFilterProps> = {
|
|
|
183
184
|
|
|
184
185
|
await step('Input invalid location', async () => {
|
|
185
186
|
await userEvent.type(inputField(), 'Not / A / Location');
|
|
186
|
-
await userEvent.click(submitButton());
|
|
187
187
|
await expect(listenerMock).not.toHaveBeenCalled();
|
|
188
188
|
await userEvent.type(inputField(), '{backspace>18/}');
|
|
189
189
|
});
|
|
190
190
|
|
|
191
191
|
await step('Select Asia', async () => {
|
|
192
192
|
await userEvent.type(inputField(), 'Asia');
|
|
193
|
-
await userEvent.click(submitButton());
|
|
194
193
|
await expect(listenerMock).toHaveBeenCalledWith(
|
|
195
194
|
expect.objectContaining({
|
|
196
195
|
detail: {
|
|
@@ -202,7 +201,6 @@ export const FiresEvent: StoryObj<LocationFilterProps> = {
|
|
|
202
201
|
|
|
203
202
|
await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => {
|
|
204
203
|
await userEvent.type(inputField(), ' / Bangladesh / Rajshahi / Chapainawabgonj');
|
|
205
|
-
await userEvent.click(submitButton());
|
|
206
204
|
await expect(listenerMock).toHaveBeenCalledWith(
|
|
207
205
|
expect.objectContaining({
|
|
208
206
|
detail: {
|
|
@@ -18,7 +18,7 @@ import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
|
18
18
|
* Values for the fields `i > K` are considered `undefined`.
|
|
19
19
|
*
|
|
20
20
|
* @fires {CustomEvent<Record<string, string>>} gs-location-changed
|
|
21
|
-
* Fired when the
|
|
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
23
|
* and the corresponding values as values, if they are not `undefined`.
|
|
24
24
|
* Example:
|
|
@@ -40,6 +40,8 @@ export class LocationFilterComponent extends PreactLitAdapter {
|
|
|
40
40
|
initialValue = '';
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
+
* Required.
|
|
44
|
+
*
|
|
43
45
|
* The fields to display in the location filter, in hierarchical order.
|
|
44
46
|
* The top-level field should be the first entry in the array.
|
|
45
47
|
* This component assumes that the values for each field form a strict hierarchy
|
|
@@ -51,13 +53,10 @@ export class LocationFilterComponent extends PreactLitAdapter {
|
|
|
51
53
|
/**
|
|
52
54
|
* The width of the component.
|
|
53
55
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* The width should be a string with a unit in css style, e.g. '100%', '500px' or '50vw'.
|
|
57
|
-
* If the unit is %, the size will be relative to the container of the component.
|
|
56
|
+
* Visit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.
|
|
58
57
|
*/
|
|
59
|
-
@property({ type:
|
|
60
|
-
width: string
|
|
58
|
+
@property({ type: String })
|
|
59
|
+
width: string = '100%';
|
|
61
60
|
|
|
62
61
|
override render() {
|
|
63
62
|
return <LocationFilter initialValue={this.initialValue} fields={this.fields} width={this.width} />;
|
|
@@ -13,7 +13,8 @@ import './gs-mutation-filter';
|
|
|
13
13
|
const codeExample = String.raw`
|
|
14
14
|
<gs-mutation-filter
|
|
15
15
|
initialValue='["A123T"]'
|
|
16
|
-
|
|
16
|
+
width='100%'
|
|
17
|
+
height='6.5rem'
|
|
17
18
|
></gs-mutation-filter>`;
|
|
18
19
|
|
|
19
20
|
const meta: Meta<MutationFilterProps> = {
|
|
@@ -36,11 +37,8 @@ const meta: Meta<MutationFilterProps> = {
|
|
|
36
37
|
type: 'object',
|
|
37
38
|
},
|
|
38
39
|
},
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
type: 'object',
|
|
42
|
-
},
|
|
43
|
-
},
|
|
40
|
+
width: { control: 'text' },
|
|
41
|
+
height: { control: 'text' },
|
|
44
42
|
},
|
|
45
43
|
decorators: [withActions],
|
|
46
44
|
tags: ['autodocs'],
|
|
@@ -52,13 +50,18 @@ const Template: StoryObj<MutationFilterProps> = {
|
|
|
52
50
|
render: (args) => {
|
|
53
51
|
return html` <gs-app lapis="${LAPIS_URL}">
|
|
54
52
|
<div class="max-w-screen-lg">
|
|
55
|
-
<gs-mutation-filter
|
|
53
|
+
<gs-mutation-filter
|
|
54
|
+
.initialValue=${args.initialValue}
|
|
55
|
+
.width=${args.width}
|
|
56
|
+
.height=${args.height}
|
|
57
|
+
></gs-mutation-filter>
|
|
56
58
|
</div>
|
|
57
59
|
</gs-app>`;
|
|
58
60
|
},
|
|
59
61
|
args: {
|
|
60
62
|
initialValue: [],
|
|
61
|
-
|
|
63
|
+
width: '100%',
|
|
64
|
+
height: '3rem',
|
|
62
65
|
},
|
|
63
66
|
};
|
|
64
67
|
|
|
@@ -18,22 +18,30 @@ import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
|
18
18
|
* All previously selected mutations are displayed at the input field and added to the event.
|
|
19
19
|
* Users can remove a mutation by clicking the 'x' button next to the mutation.
|
|
20
20
|
*
|
|
21
|
+
* ## Input Validation
|
|
22
|
+
*
|
|
21
23
|
* Validation of the input is performed according to the following rules:
|
|
22
24
|
*
|
|
25
|
+
* ### Mutations
|
|
26
|
+
*
|
|
23
27
|
* Mutations have to conform to the following format: `<gene/segment>:<symbol at reference><position><Substituted symbol/Deletion>`
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
28
|
+
* - Gene/segment: The gene or segment where the mutation occurs. Must be contained in the reference genome.
|
|
29
|
+
* (Optional for elements with only one gene/segment)
|
|
30
|
+
* - Symbol at reference: The symbol at the reference position. (Optional)
|
|
31
|
+
* - Position: The position of the mutation. (Required)
|
|
32
|
+
* - Substituted symbol/Deletion: The substituted symbol or '-' for a deletion. (Required)
|
|
33
|
+
*
|
|
34
|
+
* Examples: `S:614G`, `614G`, `614-`, `614G`
|
|
35
|
+
*
|
|
36
|
+
* ### Insertions
|
|
30
37
|
*
|
|
31
38
|
* Insertions have to conform to the following format: `ins_<gene/segment>:<position>:<Inserted symbols>`
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
39
|
+
* - Gene/segment: The gene or segment where the insertion occurs. Must be contained in the reference genome.
|
|
40
|
+
* (Optional for elements with only one gene/segment)
|
|
41
|
+
* - Position: The position of the insertion. (Required)
|
|
42
|
+
* - Inserted symbols: The symbols that are inserted. (Required)
|
|
43
|
+
*
|
|
44
|
+
* Examples: `ins_S:614:G`, `ins_614:G`
|
|
37
45
|
*
|
|
38
46
|
* @fires {CustomEvent<{
|
|
39
47
|
* nucleotideMutations: string[],
|
|
@@ -61,36 +69,40 @@ export class MutationFilterComponent extends PreactLitAdapter {
|
|
|
61
69
|
// The multiline union type must not start with `|` because it looks weird in the Storybook docs
|
|
62
70
|
/**
|
|
63
71
|
* The initial value to use for this mutation filter.
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* - an object with the keys `nucleotideMutations`, `aminoAcidMutations`, `nucleotideInsertions` and `aminoAcidInsertions` and corresponding string arrays.
|
|
72
|
+
* All values provided must be valid mutations or insertions.
|
|
73
|
+
* Invalid values will be ignored.
|
|
67
74
|
*/
|
|
68
75
|
@property()
|
|
69
76
|
initialValue:
|
|
70
77
|
{
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
nucleotideMutations: string[];
|
|
79
|
+
aminoAcidMutations: string[];
|
|
80
|
+
nucleotideInsertions: string[];
|
|
81
|
+
aminoAcidInsertions: string[];
|
|
82
|
+
}
|
|
76
83
|
| string[]
|
|
77
84
|
| undefined = undefined;
|
|
78
85
|
|
|
79
86
|
/**
|
|
80
|
-
* The
|
|
87
|
+
* The width of the component.
|
|
81
88
|
*
|
|
82
|
-
*
|
|
89
|
+
* Visit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.
|
|
90
|
+
*/
|
|
91
|
+
@property({ type: String })
|
|
92
|
+
width: string = '100%';
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The height of the component.
|
|
83
96
|
*
|
|
84
|
-
*
|
|
85
|
-
* If the unit is %, the size will be relative to the container of the component.
|
|
97
|
+
* Visit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.
|
|
86
98
|
*/
|
|
87
|
-
@property({ type:
|
|
88
|
-
|
|
99
|
+
@property({ type: String })
|
|
100
|
+
height: string = '6.5rem';
|
|
89
101
|
|
|
90
102
|
override render() {
|
|
91
103
|
return (
|
|
92
104
|
<ReferenceGenomesAwaiter>
|
|
93
|
-
<MutationFilter initialValue={this.initialValue}
|
|
105
|
+
<MutationFilter initialValue={this.initialValue} width={this.width} height={this.height} />
|
|
94
106
|
</ReferenceGenomesAwaiter>
|
|
95
107
|
);
|
|
96
108
|
}
|
|
@@ -19,7 +19,7 @@ const codeExample = String.raw`
|
|
|
19
19
|
width="50%">
|
|
20
20
|
</gs-text-input>`;
|
|
21
21
|
|
|
22
|
-
const meta: Meta<TextInputProps
|
|
22
|
+
const meta: Meta<Required<TextInputProps>> = {
|
|
23
23
|
title: 'Input/Text input',
|
|
24
24
|
component: 'gs-text-input',
|
|
25
25
|
parameters: withComponentDocs({
|
|
@@ -77,7 +77,7 @@ const meta: Meta<TextInputProps> = {
|
|
|
77
77
|
|
|
78
78
|
export default meta;
|
|
79
79
|
|
|
80
|
-
export const Default: StoryObj<TextInputProps
|
|
80
|
+
export const Default: StoryObj<Required<TextInputProps>> = {
|
|
81
81
|
render: (args) => {
|
|
82
82
|
return html` <gs-app lapis="${LAPIS_URL}">
|
|
83
83
|
<div class="max-w-screen-lg">
|
|
@@ -98,7 +98,7 @@ export const Default: StoryObj<TextInputProps> = {
|
|
|
98
98
|
},
|
|
99
99
|
};
|
|
100
100
|
|
|
101
|
-
export const FiresEvent: StoryObj<TextInputProps
|
|
101
|
+
export const FiresEvent: StoryObj<Required<TextInputProps>> = {
|
|
102
102
|
...Default,
|
|
103
103
|
play: async ({ canvasElement, step }) => {
|
|
104
104
|
const canvas = await withinShadowRoot(canvasElement, 'gs-text-input');
|
|
@@ -7,7 +7,7 @@ import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
|
7
7
|
*
|
|
8
8
|
* ## Context
|
|
9
9
|
*
|
|
10
|
-
* This component provides a text input field to specify filters for arbitrary fields of this
|
|
10
|
+
* This component provides a text input field to specify filters for arbitrary fields of this LAPIS instance.
|
|
11
11
|
*
|
|
12
12
|
* @fires {CustomEvent<Record<string, string>>} gs-text-input-changed
|
|
13
13
|
* Fired when the input field is changed.
|
|
@@ -25,10 +25,13 @@ export class TextInputComponent extends PreactLitAdapter {
|
|
|
25
25
|
* The initial value to use for this text input.
|
|
26
26
|
*/
|
|
27
27
|
@property()
|
|
28
|
-
initialValue: string
|
|
28
|
+
initialValue: string = '';
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* Required.
|
|
32
|
+
*
|
|
33
|
+
* The LAPIS field name to use for this text input.
|
|
34
|
+
* The field must exist on this LAPIS instance.
|
|
32
35
|
*/
|
|
33
36
|
@property()
|
|
34
37
|
lapisField = '';
|
|
@@ -37,18 +40,15 @@ export class TextInputComponent extends PreactLitAdapter {
|
|
|
37
40
|
* The placeholder text to display in the input field.
|
|
38
41
|
*/
|
|
39
42
|
@property()
|
|
40
|
-
placeholderText: string
|
|
43
|
+
placeholderText: string = '';
|
|
41
44
|
|
|
42
45
|
/**
|
|
43
46
|
* The width of the component.
|
|
44
47
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* The width should be a string with a unit in css style, e.g. '100%', '500px' or '50vw'.
|
|
48
|
-
* If the unit is %, the size will be relative to the container of the component.
|
|
48
|
+
* Visit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.
|
|
49
49
|
*/
|
|
50
|
-
@property({ type:
|
|
51
|
-
width: string
|
|
50
|
+
@property({ type: String })
|
|
51
|
+
width: string = '100%';
|
|
52
52
|
|
|
53
53
|
override render() {
|
|
54
54
|
return (
|