@genspectrum/dashboard-components 0.14.1 → 0.15.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.
- package/custom-elements.json +90 -58
- package/dist/{LineageFilterChangedEvent-C9dXOxt6.js → LineageFilterChangedEvent-COWV-Y0k.js} +6 -6
- package/dist/LineageFilterChangedEvent-COWV-Y0k.js.map +1 -0
- package/dist/assets/mutationOverTimeWorker-BL50C-yi.js.map +1 -0
- package/dist/components.d.ts +52 -56
- package/dist/components.js +79 -58
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +49 -49
- package/dist/util.js +2 -2
- package/package.json +2 -2
- package/src/lapisApi/lapisApi.ts +1 -1
- package/src/operator/FillMissingOperator.spec.ts +1 -1
- package/src/operator/GroupByAndSumOperator.spec.ts +1 -1
- package/src/operator/GroupByOperator.spec.ts +2 -2
- package/src/operator/MapOperator.spec.ts +1 -1
- package/src/operator/MockOperator.spec.ts +1 -1
- package/src/operator/MockOperator.ts +6 -4
- package/src/operator/SortOperator.spec.ts +1 -1
- package/src/preact/aggregatedData/aggregate.stories.tsx +1 -1
- package/src/preact/components/csv-download-button.stories.tsx +2 -2
- package/src/preact/components/csv-download-button.tsx +1 -1
- package/src/preact/components/error-boundary.stories.tsx +5 -5
- package/src/preact/components/error-boundary.tsx +14 -3
- package/src/preact/components/error-display.stories.tsx +9 -9
- package/src/preact/components/fullscreen.tsx +3 -3
- package/src/preact/components/info.tsx +1 -1
- package/src/preact/components/mutation-type-selector.stories.tsx +1 -1
- package/src/preact/components/table.stories.tsx +3 -3
- package/src/preact/components/table.tsx +1 -1
- package/src/preact/{dateRangeSelector/date-range-selector.stories.tsx → dateRangeFilter/date-range-filter.stories.tsx} +18 -21
- package/src/preact/{dateRangeSelector/date-range-selector.tsx → dateRangeFilter/date-range-filter.tsx} +11 -11
- package/src/preact/{dateRangeSelector → dateRangeFilter}/dateRangeOption.ts +2 -2
- package/src/preact/lineageFilter/lineage-filter.stories.tsx +6 -6
- package/src/preact/locationFilter/fetchAutocompletionList.ts +1 -1
- package/src/preact/locationFilter/location-filter.stories.tsx +6 -6
- package/src/preact/map/sequences-by-location.stories.tsx +1 -1
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +2 -2
- package/src/preact/mutations/getMutationsGridData.ts +1 -1
- package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +3 -3
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +1 -1
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +1 -0
- package/src/preact/numberSequencesOverTime/number-sequences-over-time.stories.tsx +1 -1
- package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +4 -4
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +1 -1
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +1 -1
- package/src/preact/shared/floating-ui/hooks.ts +1 -1
- package/src/preact/{textInput/TextInputChangedEvent.ts → textFilter/TextFilterChangedEvent.ts} +2 -2
- package/src/preact/{textInput/text-input.stories.tsx → textFilter/text-filter.stories.tsx} +14 -14
- package/src/preact/{textInput/text-input.tsx → textFilter/text-filter.tsx} +10 -10
- package/src/utilEntrypoint.ts +2 -2
- package/src/utils/map2d.ts +1 -0
- package/src/web-components/gs-app.stories.ts +7 -7
- package/src/web-components/input/{gs-date-range-selector.stories.ts → gs-date-range-filter.stories.ts} +65 -20
- package/src/web-components/input/{gs-date-range-selector.tsx → gs-date-range-filter.tsx} +28 -13
- package/src/web-components/input/gs-lineage-filter.stories.ts +1 -1
- package/src/web-components/input/gs-location-filter.stories.ts +1 -1
- package/src/web-components/input/gs-mutation-filter.stories.ts +7 -7
- package/src/web-components/input/{gs-text-input.stories.ts → gs-text-filter.stories.ts} +18 -18
- package/src/web-components/input/{gs-text-input.tsx → gs-text-filter.tsx} +16 -16
- package/src/web-components/input/index.ts +2 -2
- package/src/web-components/visualization/gs-aggregate.tsx +2 -2
- package/standalone-bundle/assets/mutationOverTimeWorker-CFB5-Mdk.js.map +1 -0
- package/standalone-bundle/dashboard-components.js +2233 -2220
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/dist/LineageFilterChangedEvent-C9dXOxt6.js.map +0 -1
- package/dist/assets/mutationOverTimeWorker-Dxnxrfe0.js.map +0 -1
- package/standalone-bundle/assets/mutationOverTimeWorker-CmSrq4SZ.js.map +0 -1
- /package/src/preact/{dateRangeSelector → dateRangeFilter}/computeInitialValues.spec.ts +0 -0
- /package/src/preact/{dateRangeSelector → dateRangeFilter}/computeInitialValues.ts +0 -0
- /package/src/preact/{dateRangeSelector → dateRangeFilter}/dateConversion.ts +0 -0
- /package/src/preact/{dateRangeSelector → dateRangeFilter}/selectableOptions.ts +0 -0
- /package/src/preact/{textInput → textFilter}/__mockData__/aggregated_hosts.json +0 -0
- /package/src/preact/{textInput → textFilter}/fetchStringAutocompleteList.spec.ts +0 -0
- /package/src/preact/{textInput → textFilter}/fetchStringAutocompleteList.ts +0 -0
|
@@ -2,7 +2,7 @@ import { type FunctionComponent } from 'preact';
|
|
|
2
2
|
import z from 'zod';
|
|
3
3
|
|
|
4
4
|
import { useLapisUrl } from '../LapisUrlContext';
|
|
5
|
-
import {
|
|
5
|
+
import { TextFilterChangedEvent } from './TextFilterChangedEvent';
|
|
6
6
|
import { fetchStringAutocompleteList } from './fetchStringAutocompleteList';
|
|
7
7
|
import { lapisFilterSchema } from '../../types';
|
|
8
8
|
import { DownshiftCombobox } from '../components/downshift-combobox';
|
|
@@ -17,29 +17,29 @@ const textSelectorPropsSchema = z.object({
|
|
|
17
17
|
placeholderText: z.string().optional(),
|
|
18
18
|
value: z.string().optional(),
|
|
19
19
|
});
|
|
20
|
-
const
|
|
21
|
-
const
|
|
20
|
+
const textFilterInnerPropsSchema = textSelectorPropsSchema.extend({ lapisFilter: lapisFilterSchema });
|
|
21
|
+
const textFilterPropsSchema = textFilterInnerPropsSchema.extend({
|
|
22
22
|
width: z.string(),
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
export type
|
|
26
|
-
export type
|
|
25
|
+
export type TextFilterInnerProps = z.infer<typeof textFilterInnerPropsSchema>;
|
|
26
|
+
export type TextFilterProps = z.infer<typeof textFilterPropsSchema>;
|
|
27
27
|
type TextSelectorProps = z.infer<typeof textSelectorPropsSchema>;
|
|
28
28
|
|
|
29
|
-
export const
|
|
29
|
+
export const TextFilter: FunctionComponent<TextFilterProps> = (props) => {
|
|
30
30
|
const { width, ...innerProps } = props;
|
|
31
31
|
const size = { width, height: '3rem' };
|
|
32
32
|
|
|
33
33
|
return (
|
|
34
|
-
<ErrorBoundary size={size} layout='horizontal' componentProps={props} schema={
|
|
34
|
+
<ErrorBoundary size={size} layout='horizontal' componentProps={props} schema={textFilterPropsSchema}>
|
|
35
35
|
<ResizeContainer size={size}>
|
|
36
|
-
<
|
|
36
|
+
<TextFilterInner {...innerProps} />
|
|
37
37
|
</ResizeContainer>
|
|
38
38
|
</ErrorBoundary>
|
|
39
39
|
);
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
const
|
|
42
|
+
const TextFilterInner: FunctionComponent<TextFilterInnerProps> = ({
|
|
43
43
|
value,
|
|
44
44
|
lapisField,
|
|
45
45
|
placeholderText,
|
|
@@ -88,7 +88,7 @@ const TextSelector = ({
|
|
|
88
88
|
value={initialSelectedItem}
|
|
89
89
|
filterItemsByInputValue={filterByInputValue}
|
|
90
90
|
createEvent={(item: SelectItem | null) =>
|
|
91
|
-
new
|
|
91
|
+
new TextFilterChangedEvent({ [lapisField]: item?.value ?? undefined })
|
|
92
92
|
}
|
|
93
93
|
itemToString={(item: SelectItem | undefined | null) => item?.value ?? ''}
|
|
94
94
|
placeholderText={placeholderText}
|
package/src/utilEntrypoint.ts
CHANGED
|
@@ -3,7 +3,7 @@ export {
|
|
|
3
3
|
type DateRangeSelectOption,
|
|
4
4
|
dateRangeOptionPresets,
|
|
5
5
|
DateRangeOptionChangedEvent,
|
|
6
|
-
} from './preact/
|
|
6
|
+
} from './preact/dateRangeFilter/dateRangeOption';
|
|
7
7
|
|
|
8
8
|
export {
|
|
9
9
|
type NamedLapisFilter,
|
|
@@ -35,4 +35,4 @@ export type { AxisMax, YAxisMaxConfig } from './preact/shared/charts/getYAxisMax
|
|
|
35
35
|
|
|
36
36
|
export { LocationChangedEvent } from './preact/locationFilter/LocationChangedEvent';
|
|
37
37
|
export { LineageFilterChangedEvent } from './preact/lineageFilter/LineageFilterChangedEvent';
|
|
38
|
-
export {
|
|
38
|
+
export { TextFilterChangedEvent } from './preact/textFilter/TextFilterChangedEvent';
|
package/src/utils/map2d.ts
CHANGED
|
@@ -150,6 +150,7 @@ export class Map2dView<Key1 extends object | string, Key2 extends object | strin
|
|
|
150
150
|
this.keysSecondAxis = new Map(map.keysSecondAxis);
|
|
151
151
|
|
|
152
152
|
if (map instanceof Map2dView) {
|
|
153
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- the generics match, we know that.
|
|
153
154
|
this.baseMap = map.baseMap;
|
|
154
155
|
}
|
|
155
156
|
this.baseMap = map;
|
|
@@ -50,9 +50,9 @@ export const Default: StoryObj<{ lapis: string }> = {
|
|
|
50
50
|
play: async ({ canvasElement }) => {
|
|
51
51
|
const canvas = within(canvasElement);
|
|
52
52
|
|
|
53
|
-
await waitFor(() => {
|
|
54
|
-
expect(canvas.getByText(LAPIS_URL)).toBeVisible();
|
|
55
|
-
expect(canvas.getByText('"name": "ORF1a",', { exact: false })).toBeVisible();
|
|
53
|
+
await waitFor(async () => {
|
|
54
|
+
await expect(canvas.getByText(LAPIS_URL)).toBeVisible();
|
|
55
|
+
await expect(canvas.getByText('"name": "ORF1a",', { exact: false })).toBeVisible();
|
|
56
56
|
});
|
|
57
57
|
},
|
|
58
58
|
};
|
|
@@ -66,8 +66,8 @@ export const WithNoLapisUrl: StoryObj<{ lapis: string }> = {
|
|
|
66
66
|
play: async ({ canvasElement }) => {
|
|
67
67
|
const canvas = within(canvasElement);
|
|
68
68
|
|
|
69
|
-
await waitFor(() => {
|
|
70
|
-
expect(canvas.getByText("Error: Invalid LAPIS URL: 'notAValidUrl'", { exact: false })).toBeVisible();
|
|
69
|
+
await waitFor(async () => {
|
|
70
|
+
await expect(canvas.getByText("Error: Invalid LAPIS URL: 'notAValidUrl'", { exact: false })).toBeVisible();
|
|
71
71
|
});
|
|
72
72
|
},
|
|
73
73
|
};
|
|
@@ -103,8 +103,8 @@ export const FailsToFetchReferenceGenome: StoryObj<{ lapis: string }> = {
|
|
|
103
103
|
play: async ({ canvasElement }) => {
|
|
104
104
|
const canvas = within(canvasElement);
|
|
105
105
|
|
|
106
|
-
await waitFor(() => {
|
|
107
|
-
expect(canvas.getByText('Error: Cannot fetch reference genome.', { exact: false })).toBeVisible();
|
|
106
|
+
await waitFor(async () => {
|
|
107
|
+
await expect(canvas.getByText('Error: Cannot fetch reference genome.', { exact: false })).toBeVisible();
|
|
108
108
|
});
|
|
109
109
|
},
|
|
110
110
|
};
|
|
@@ -5,27 +5,27 @@ import { html } from 'lit';
|
|
|
5
5
|
import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
|
|
6
6
|
import { previewHandles } from '../../../.storybook/preview';
|
|
7
7
|
import { LAPIS_URL } from '../../constants';
|
|
8
|
-
import { type
|
|
9
|
-
import './gs-date-range-
|
|
8
|
+
import { type DateRangeFilterProps } from '../../preact/dateRangeFilter/date-range-filter';
|
|
9
|
+
import './gs-date-range-filter';
|
|
10
10
|
import '../gs-app';
|
|
11
|
-
import { toYYYYMMDD } from '../../preact/
|
|
12
|
-
import { dateRangeOptionPresets } from '../../preact/
|
|
11
|
+
import { toYYYYMMDD } from '../../preact/dateRangeFilter/dateConversion';
|
|
12
|
+
import { dateRangeOptionPresets } from '../../preact/dateRangeFilter/dateRangeOption';
|
|
13
13
|
import { withinShadowRoot } from '../withinShadowRoot.story';
|
|
14
14
|
|
|
15
15
|
const codeExample = String.raw`
|
|
16
|
-
<gs-date-range-
|
|
16
|
+
<gs-date-range-filter
|
|
17
17
|
dateRangeOptions='[{ "label": "Year 2021", "dateFrom": "2021-01-01", "dateTo": "2021-12-31" }]'
|
|
18
18
|
earliestDate="1970-01-01"
|
|
19
|
-
|
|
19
|
+
value="Year 2021"
|
|
20
20
|
width="100%"
|
|
21
21
|
lapisDateField="myDateColumn"
|
|
22
|
-
></gs-date-range-
|
|
22
|
+
></gs-date-range-filter>`;
|
|
23
23
|
|
|
24
24
|
const customDateRange = { label: 'CustomDateRange', dateFrom: '2021-01-01', dateTo: '2021-12-31' };
|
|
25
25
|
|
|
26
|
-
const meta: Meta<Required<
|
|
27
|
-
title: 'Input/
|
|
28
|
-
component: 'gs-date-range-
|
|
26
|
+
const meta: Meta<Required<DateRangeFilterProps>> = {
|
|
27
|
+
title: 'Input/DateRangeFilter',
|
|
28
|
+
component: 'gs-date-range-filter',
|
|
29
29
|
parameters: withComponentDocs({
|
|
30
30
|
actions: {
|
|
31
31
|
handles: ['gs-date-range-filter-changed', 'gs-date-range-option-changed', ...previewHandles],
|
|
@@ -65,6 +65,7 @@ const meta: Meta<Required<DateRangeSelectorProps>> = {
|
|
|
65
65
|
dateRangeOptionPresets.lastMonth,
|
|
66
66
|
dateRangeOptionPresets.last3Months,
|
|
67
67
|
dateRangeOptionPresets.allTimes,
|
|
68
|
+
{ label: '2021', dateFrom: '2021-01-01', dateTo: '2021-12-31' },
|
|
68
69
|
customDateRange,
|
|
69
70
|
],
|
|
70
71
|
earliestDate: '1970-01-01',
|
|
@@ -77,37 +78,81 @@ const meta: Meta<Required<DateRangeSelectorProps>> = {
|
|
|
77
78
|
|
|
78
79
|
export default meta;
|
|
79
80
|
|
|
80
|
-
export const Default: StoryObj<Required<
|
|
81
|
+
export const Default: StoryObj<Required<DateRangeFilterProps>> = {
|
|
81
82
|
render: (args) =>
|
|
82
83
|
html` <gs-app lapis="${LAPIS_URL}">
|
|
83
84
|
<div class="max-w-screen-lg">
|
|
84
|
-
<gs-date-range-
|
|
85
|
+
<gs-date-range-filter
|
|
85
86
|
.dateRangeOptions=${args.dateRangeOptions}
|
|
86
87
|
.earliestDate=${args.earliestDate}
|
|
87
88
|
.value=${args.value}
|
|
88
89
|
.width=${args.width}
|
|
89
90
|
.lapisDateField=${args.lapisDateField}
|
|
90
|
-
></gs-date-range-
|
|
91
|
+
></gs-date-range-filter>
|
|
91
92
|
</div>
|
|
92
93
|
</gs-app>`,
|
|
93
94
|
};
|
|
94
95
|
|
|
95
|
-
export const
|
|
96
|
+
export const TestRenderAttributesInHtmlInsteadOfUsingPropertyExpression: StoryObj<Required<DateRangeFilterProps>> = {
|
|
97
|
+
render: (args) =>
|
|
98
|
+
html` <gs-app lapis="${LAPIS_URL}">
|
|
99
|
+
<div class="max-w-screen-lg">
|
|
100
|
+
<gs-date-range-filter
|
|
101
|
+
.dateRangeOptions=${args.dateRangeOptions}
|
|
102
|
+
earliestDate="${args.earliestDate}"
|
|
103
|
+
value="${args.value}"
|
|
104
|
+
width="${args.width}"
|
|
105
|
+
lapisDateField="${args.lapisDateField}"
|
|
106
|
+
></gs-date-range-filter>
|
|
107
|
+
</div>
|
|
108
|
+
</gs-app>`,
|
|
109
|
+
play: async ({ canvasElement }) => {
|
|
110
|
+
const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-filter');
|
|
111
|
+
|
|
112
|
+
await waitFor(async () => {
|
|
113
|
+
await expect(selectField(canvas)).toHaveValue('Last month');
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
argTypes: {
|
|
117
|
+
value: {
|
|
118
|
+
control: {
|
|
119
|
+
type: 'text',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const TestSettingANumericValueIsTreatedAsString: StoryObj<Required<DateRangeFilterProps>> = {
|
|
126
|
+
...TestRenderAttributesInHtmlInsteadOfUsingPropertyExpression,
|
|
127
|
+
args: {
|
|
128
|
+
...TestRenderAttributesInHtmlInsteadOfUsingPropertyExpression.args,
|
|
129
|
+
value: '2021',
|
|
130
|
+
},
|
|
131
|
+
play: async ({ canvasElement }) => {
|
|
132
|
+
const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-filter');
|
|
133
|
+
|
|
134
|
+
await waitFor(async () => {
|
|
135
|
+
await expect(selectField(canvas)).toHaveValue('2021');
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export const FiresEvents: StoryObj<Required<DateRangeFilterProps>> = {
|
|
96
141
|
...Default,
|
|
97
142
|
play: async ({ canvasElement, step }) => {
|
|
98
|
-
const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-
|
|
143
|
+
const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-filter');
|
|
99
144
|
|
|
100
145
|
const filterChangedListenerMock = fn();
|
|
101
146
|
const optionChangedListenerMock = fn();
|
|
102
|
-
await step('Setup event listener mock',
|
|
147
|
+
await step('Setup event listener mock', () => {
|
|
103
148
|
canvasElement.addEventListener('gs-date-range-filter-changed', filterChangedListenerMock);
|
|
104
149
|
canvasElement.addEventListener('gs-date-range-option-changed', optionChangedListenerMock);
|
|
105
150
|
});
|
|
106
151
|
|
|
107
152
|
await step('Expect last 6 months to be selected', async () => {
|
|
108
153
|
await expect(selectField(canvas)).toHaveValue('Last month');
|
|
109
|
-
await waitFor(() => {
|
|
110
|
-
expect(dateToPicker(canvas)).toHaveValue(toYYYYMMDD(new Date()));
|
|
154
|
+
await waitFor(async () => {
|
|
155
|
+
await expect(dateToPicker(canvas)).toHaveValue(toYYYYMMDD(new Date()));
|
|
111
156
|
});
|
|
112
157
|
});
|
|
113
158
|
|
|
@@ -133,5 +178,5 @@ export const FiresEvents: StoryObj<Required<DateRangeSelectorProps>> = {
|
|
|
133
178
|
},
|
|
134
179
|
};
|
|
135
180
|
|
|
136
|
-
const dateToPicker = (canvas: ReturnType<typeof within>) => canvas.getByPlaceholderText('Date to');
|
|
137
|
-
const selectField = (canvas: ReturnType<typeof within>) => canvas.getByRole('combobox');
|
|
181
|
+
const dateToPicker = (canvas: ReturnType<typeof within>): HTMLElement => canvas.getByPlaceholderText('Date to');
|
|
182
|
+
const selectField = (canvas: ReturnType<typeof within>): HTMLElement => canvas.getByRole('combobox');
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { customElement, property } from 'lit/decorators.js';
|
|
2
2
|
import type { DetailedHTMLProps, HTMLAttributes } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { type DateRangeOptionChangedEvent } from '../../preact/
|
|
4
|
+
import { DateRangeFilter, type DateRangeFilterProps } from '../../preact/dateRangeFilter/date-range-filter';
|
|
5
|
+
import { type DateRangeOptionChangedEvent } from '../../preact/dateRangeFilter/dateRangeOption';
|
|
6
6
|
import { type Equals, type Expect } from '../../utils/typeAssertions';
|
|
7
7
|
import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
8
8
|
|
|
@@ -48,8 +48,8 @@ import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
|
48
48
|
* Use this event, when you want to control this component in your JS application.
|
|
49
49
|
* You can supply the `detail` of this event to the `value` attribute of this component.
|
|
50
50
|
*/
|
|
51
|
-
@customElement('gs-date-range-
|
|
52
|
-
export class
|
|
51
|
+
@customElement('gs-date-range-filter')
|
|
52
|
+
export class DateRangeFilterComponent extends PreactLitAdapter {
|
|
53
53
|
/**
|
|
54
54
|
* An array of date range options that the select field should provide.
|
|
55
55
|
* The `label` will be shown to the user, and it will be available as `value`.
|
|
@@ -78,7 +78,22 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
78
78
|
* The `detail` of the `gs-date-range-option-changed` event can be used for this attribute,
|
|
79
79
|
* if you want to control this component in your JS application.
|
|
80
80
|
*/
|
|
81
|
-
@property({
|
|
81
|
+
@property({
|
|
82
|
+
converter: (value) => {
|
|
83
|
+
if (value === null) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const result = JSON.parse(value) as unknown;
|
|
88
|
+
if (typeof result !== 'object' && typeof result !== 'string') {
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
} catch (_) {
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
})
|
|
82
97
|
value: string | { dateFrom?: string; dateTo?: string } | undefined = undefined;
|
|
83
98
|
|
|
84
99
|
/**
|
|
@@ -97,7 +112,7 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
97
112
|
|
|
98
113
|
override render() {
|
|
99
114
|
return (
|
|
100
|
-
<
|
|
115
|
+
<DateRangeFilter
|
|
101
116
|
dateRangeOptions={this.dateRangeOptions}
|
|
102
117
|
earliestDate={this.earliestDate}
|
|
103
118
|
value={this.value}
|
|
@@ -110,7 +125,7 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
110
125
|
|
|
111
126
|
declare global {
|
|
112
127
|
interface HTMLElementTagNameMap {
|
|
113
|
-
'gs-date-range-
|
|
128
|
+
'gs-date-range-filter': DateRangeFilterComponent;
|
|
114
129
|
}
|
|
115
130
|
|
|
116
131
|
interface HTMLElementEventMap {
|
|
@@ -123,21 +138,21 @@ declare global {
|
|
|
123
138
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
124
139
|
namespace JSX {
|
|
125
140
|
interface IntrinsicElements {
|
|
126
|
-
'gs-date-range-
|
|
141
|
+
'gs-date-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
127
142
|
}
|
|
128
143
|
}
|
|
129
144
|
}
|
|
130
145
|
|
|
131
146
|
/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
132
147
|
type CustomSelectOptionsMatches = Expect<
|
|
133
|
-
Equals<typeof
|
|
148
|
+
Equals<typeof DateRangeFilterComponent.prototype.dateRangeOptions, DateRangeFilterProps['dateRangeOptions']>
|
|
134
149
|
>;
|
|
135
150
|
type EarliestDateMatches = Expect<
|
|
136
|
-
Equals<typeof
|
|
151
|
+
Equals<typeof DateRangeFilterComponent.prototype.earliestDate, DateRangeFilterProps['earliestDate']>
|
|
137
152
|
>;
|
|
138
|
-
type ValueMatches = Expect<Equals<typeof
|
|
139
|
-
type WidthMatches = Expect<Equals<typeof
|
|
153
|
+
type ValueMatches = Expect<Equals<typeof DateRangeFilterComponent.prototype.value, DateRangeFilterProps['value']>>;
|
|
154
|
+
type WidthMatches = Expect<Equals<typeof DateRangeFilterComponent.prototype.width, DateRangeFilterProps['width']>>;
|
|
140
155
|
type DateColumnMatches = Expect<
|
|
141
|
-
Equals<typeof
|
|
156
|
+
Equals<typeof DateRangeFilterComponent.prototype.lapisDateField, DateRangeFilterProps['lapisDateField']>
|
|
142
157
|
>;
|
|
143
158
|
/* eslint-enable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
@@ -181,7 +181,7 @@ export const FiresEvent: StoryObj<Required<LineageFilterProps>> = {
|
|
|
181
181
|
|
|
182
182
|
const inputField = () => canvas.getByPlaceholderText('Enter a lineage');
|
|
183
183
|
const listenerMock = fn();
|
|
184
|
-
await step('Setup event listener mock',
|
|
184
|
+
await step('Setup event listener mock', () => {
|
|
185
185
|
canvasElement.addEventListener('gs-lineage-filter-changed', listenerMock);
|
|
186
186
|
});
|
|
187
187
|
|
|
@@ -188,7 +188,7 @@ export const FiresEvent: StoryObj<LocationFilterProps> = {
|
|
|
188
188
|
const inputField = () => canvas.getByRole('combobox');
|
|
189
189
|
|
|
190
190
|
const listenerMock = fn();
|
|
191
|
-
await step('Setup event listener mock',
|
|
191
|
+
await step('Setup event listener mock', () => {
|
|
192
192
|
canvasElement.addEventListener('gs-location-changed', listenerMock);
|
|
193
193
|
});
|
|
194
194
|
|
|
@@ -72,7 +72,7 @@ export const FiresFilterChangedEvent: StoryObj<MutationFilterProps> = {
|
|
|
72
72
|
|
|
73
73
|
const inputField = () => canvas.getByPlaceholderText('Enter a mutation', { exact: false });
|
|
74
74
|
const listenerMock = fn();
|
|
75
|
-
await step('Setup event listener mock',
|
|
75
|
+
await step('Setup event listener mock', () => {
|
|
76
76
|
canvasElement.addEventListener('gs-mutation-filter-changed', listenerMock);
|
|
77
77
|
});
|
|
78
78
|
|
|
@@ -154,18 +154,18 @@ export const MultiSegmentedReferenceGenomes: StoryObj<MutationFilterProps> = {
|
|
|
154
154
|
|
|
155
155
|
const inputField = () => canvas.getByPlaceholderText('Enter a mutation', { exact: false });
|
|
156
156
|
|
|
157
|
-
await waitFor(() => {
|
|
157
|
+
await waitFor(async () => {
|
|
158
158
|
const placeholderText = inputField().getAttribute('placeholder');
|
|
159
159
|
|
|
160
|
-
expect(placeholderText).toEqual(
|
|
160
|
+
await expect(placeholderText).toEqual(
|
|
161
161
|
'Enter a mutation (e.g. seg1:23T, ins_seg1:10462:A, gene1:57Q, ins_gene1:31:N)',
|
|
162
162
|
);
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
-
await waitFor(() => {
|
|
166
|
-
expect(canvas.getByText('seg1:123T')).toBeVisible();
|
|
167
|
-
expect(canvas.getByText('gene2:56')).toBeVisible();
|
|
168
|
-
|
|
165
|
+
await waitFor(async () => {
|
|
166
|
+
await expect(canvas.getByText('seg1:123T')).toBeVisible();
|
|
167
|
+
await expect(canvas.getByText('gene2:56')).toBeVisible();
|
|
168
|
+
await expect(canvas.getByText('ins_seg2:78:AAA')).toBeVisible();
|
|
169
169
|
});
|
|
170
170
|
},
|
|
171
171
|
};
|
|
@@ -6,26 +6,26 @@ import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
|
|
|
6
6
|
import { previewHandles } from '../../../.storybook/preview';
|
|
7
7
|
import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
|
|
8
8
|
import '../gs-app';
|
|
9
|
-
import './gs-text-
|
|
10
|
-
import data from '../../preact/
|
|
11
|
-
import type {
|
|
9
|
+
import './gs-text-filter';
|
|
10
|
+
import data from '../../preact/textFilter/__mockData__/aggregated_hosts.json';
|
|
11
|
+
import type { TextFilterProps } from '../../preact/textFilter/text-filter';
|
|
12
12
|
import { withinShadowRoot } from '../withinShadowRoot.story';
|
|
13
13
|
|
|
14
14
|
const codeExample = String.raw`
|
|
15
|
-
<gs-text-
|
|
15
|
+
<gs-text-filter
|
|
16
16
|
lapisField="host"
|
|
17
17
|
lapisFilter='{"country": "Germany"}'
|
|
18
18
|
placeholderText="Enter host name"
|
|
19
19
|
value="Homo sapiens"
|
|
20
20
|
width="50%">
|
|
21
|
-
</gs-text-
|
|
21
|
+
</gs-text-filter>`;
|
|
22
22
|
|
|
23
|
-
const meta: Meta<Required<
|
|
24
|
-
title: 'Input/Text
|
|
25
|
-
component: 'gs-text-
|
|
23
|
+
const meta: Meta<Required<TextFilterProps>> = {
|
|
24
|
+
title: 'Input/Text filter',
|
|
25
|
+
component: 'gs-text-filter',
|
|
26
26
|
parameters: withComponentDocs({
|
|
27
27
|
actions: {
|
|
28
|
-
handles: ['gs-text-
|
|
28
|
+
handles: ['gs-text-filter-changed', ...previewHandles],
|
|
29
29
|
},
|
|
30
30
|
fetchMock: {
|
|
31
31
|
mocks: [
|
|
@@ -83,17 +83,17 @@ const meta: Meta<Required<TextInputProps>> = {
|
|
|
83
83
|
|
|
84
84
|
export default meta;
|
|
85
85
|
|
|
86
|
-
export const Default: StoryObj<Required<
|
|
86
|
+
export const Default: StoryObj<Required<TextFilterProps>> = {
|
|
87
87
|
render: (args) => {
|
|
88
88
|
return html` <gs-app lapis="${LAPIS_URL}">
|
|
89
89
|
<div class="max-w-screen-lg">
|
|
90
|
-
<gs-text-
|
|
90
|
+
<gs-text-filter
|
|
91
91
|
.lapisField=${args.lapisField}
|
|
92
92
|
.lapisFilter=${args.lapisFilter}
|
|
93
93
|
.placeholderText=${args.placeholderText}
|
|
94
94
|
.value=${args.value}
|
|
95
95
|
.width=${args.width}
|
|
96
|
-
></gs-text-
|
|
96
|
+
></gs-text-filter>
|
|
97
97
|
</div>
|
|
98
98
|
</gs-app>`;
|
|
99
99
|
},
|
|
@@ -106,15 +106,15 @@ export const Default: StoryObj<Required<TextInputProps>> = {
|
|
|
106
106
|
},
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
export const FiresEvents: StoryObj<Required<
|
|
109
|
+
export const FiresEvents: StoryObj<Required<TextFilterProps>> = {
|
|
110
110
|
...Default,
|
|
111
111
|
play: async ({ canvasElement, step }) => {
|
|
112
|
-
const canvas = await withinShadowRoot(canvasElement, 'gs-text-
|
|
112
|
+
const canvas = await withinShadowRoot(canvasElement, 'gs-text-filter');
|
|
113
113
|
|
|
114
114
|
const inputField = () => canvas.getByPlaceholderText('Enter host name');
|
|
115
115
|
const listenerMock = fn();
|
|
116
|
-
await step('Setup event listener mock',
|
|
117
|
-
canvasElement.addEventListener('gs-text-
|
|
116
|
+
await step('Setup event listener mock', () => {
|
|
117
|
+
canvasElement.addEventListener('gs-text-filter-changed', listenerMock);
|
|
118
118
|
});
|
|
119
119
|
|
|
120
120
|
await step('wait until data is loaded', async () => {
|
|
@@ -140,8 +140,8 @@ export const FiresEvents: StoryObj<Required<TextInputProps>> = {
|
|
|
140
140
|
});
|
|
141
141
|
|
|
142
142
|
await step('Verify event is fired with correct detail', async () => {
|
|
143
|
-
await waitFor(() => {
|
|
144
|
-
expect(listenerMock).toHaveBeenCalledWith(
|
|
143
|
+
await waitFor(async () => {
|
|
144
|
+
await expect(listenerMock).toHaveBeenCalledWith(
|
|
145
145
|
expect.objectContaining({
|
|
146
146
|
detail: {
|
|
147
147
|
host: 'Homo sapiens',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { customElement, property } from 'lit/decorators.js';
|
|
2
2
|
import type { DetailedHTMLProps, HTMLAttributes } from 'react';
|
|
3
3
|
|
|
4
|
-
import { type
|
|
5
|
-
import {
|
|
4
|
+
import { type TextFilterChangedEvent } from '../../preact/textFilter/TextFilterChangedEvent';
|
|
5
|
+
import { TextFilter, type TextFilterProps } from '../../preact/textFilter/text-filter';
|
|
6
6
|
import type { Equals, Expect } from '../../utils/typeAssertions';
|
|
7
7
|
import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
|
12
12
|
*
|
|
13
13
|
* This component provides a text input field to specify filters for arbitrary fields of this LAPIS instance.
|
|
14
14
|
*
|
|
15
|
-
* @fires {CustomEvent<Record<string, string | undefined>>} gs-text-
|
|
15
|
+
* @fires {CustomEvent<Record<string, string | undefined>>} gs-text-filter-changed
|
|
16
16
|
* Fired when the input field is changed.
|
|
17
17
|
* The `details` of this event contain an object with the `lapisField` as key and the input value as value.
|
|
18
18
|
* Example:
|
|
@@ -22,10 +22,10 @@ import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
|
22
22
|
* }
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
|
-
@customElement('gs-text-
|
|
26
|
-
export class
|
|
25
|
+
@customElement('gs-text-filter')
|
|
26
|
+
export class TextFilterComponent extends PreactLitAdapter {
|
|
27
27
|
/**
|
|
28
|
-
* The initial value to use for this text
|
|
28
|
+
* The initial value to use for this text filter.
|
|
29
29
|
*/
|
|
30
30
|
@property()
|
|
31
31
|
value: string | undefined = undefined;
|
|
@@ -33,7 +33,7 @@ export class TextInputComponent extends PreactLitAdapter {
|
|
|
33
33
|
/**
|
|
34
34
|
* Required.
|
|
35
35
|
*
|
|
36
|
-
* The LAPIS field name to use for this text
|
|
36
|
+
* The LAPIS field name to use for this text filter.
|
|
37
37
|
* The field must exist on this LAPIS instance.
|
|
38
38
|
*/
|
|
39
39
|
@property()
|
|
@@ -68,7 +68,7 @@ export class TextInputComponent extends PreactLitAdapter {
|
|
|
68
68
|
|
|
69
69
|
override render() {
|
|
70
70
|
return (
|
|
71
|
-
<
|
|
71
|
+
<TextFilter
|
|
72
72
|
lapisField={this.lapisField}
|
|
73
73
|
lapisFilter={this.lapisFilter}
|
|
74
74
|
placeholderText={this.placeholderText}
|
|
@@ -81,11 +81,11 @@ export class TextInputComponent extends PreactLitAdapter {
|
|
|
81
81
|
|
|
82
82
|
declare global {
|
|
83
83
|
interface HTMLElementTagNameMap {
|
|
84
|
-
'gs-text-
|
|
84
|
+
'gs-text-filter': TextFilterComponent;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
interface HTMLElementEventMap {
|
|
88
|
-
'gs-text-
|
|
88
|
+
'gs-text-filter-changed': TextFilterChangedEvent;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -93,19 +93,19 @@ declare global {
|
|
|
93
93
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
94
94
|
namespace JSX {
|
|
95
95
|
interface IntrinsicElements {
|
|
96
|
-
'gs-text-
|
|
96
|
+
'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
102
|
-
type InitialValueMatches = Expect<Equals<typeof
|
|
103
|
-
type LapisFieldMatches = Expect<Equals<typeof
|
|
102
|
+
type InitialValueMatches = Expect<Equals<typeof TextFilterComponent.prototype.value, TextFilterProps['value']>>;
|
|
103
|
+
type LapisFieldMatches = Expect<Equals<typeof TextFilterComponent.prototype.lapisField, TextFilterProps['lapisField']>>;
|
|
104
104
|
type LapisFilterMatches = Expect<
|
|
105
|
-
Equals<typeof
|
|
105
|
+
Equals<typeof TextFilterComponent.prototype.lapisFilter, TextFilterProps['lapisFilter']>
|
|
106
106
|
>;
|
|
107
107
|
type PlaceholderTextMatches = Expect<
|
|
108
|
-
Equals<typeof
|
|
108
|
+
Equals<typeof TextFilterComponent.prototype.placeholderText, TextFilterProps['placeholderText']>
|
|
109
109
|
>;
|
|
110
|
-
type WidthMatches = Expect<Equals<typeof
|
|
110
|
+
type WidthMatches = Expect<Equals<typeof TextFilterComponent.prototype.width, TextFilterProps['width']>>;
|
|
111
111
|
/* eslint-enable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { DateRangeFilterComponent } from './gs-date-range-filter';
|
|
2
2
|
export { LocationFilterComponent } from './gs-location-filter';
|
|
3
|
-
export {
|
|
3
|
+
export { TextFilterComponent } from './gs-text-filter';
|
|
4
4
|
export { MutationFilterComponent } from './gs-mutation-filter';
|
|
5
5
|
export { LineageFilterComponent } from './gs-lineage-filter';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { customElement, property } from 'lit/decorators.js';
|
|
2
2
|
import type { DetailedHTMLProps, HTMLAttributes } from 'react';
|
|
3
3
|
|
|
4
|
-
import { Aggregate, type AggregateProps
|
|
4
|
+
import { Aggregate, type AggregateProps } from '../../preact/aggregatedData/aggregate';
|
|
5
5
|
import { type Equals, type Expect } from '../../utils/typeAssertions';
|
|
6
6
|
import { PreactLitAdapterWithGridJsStyles } from '../PreactLitAdapterWithGridJsStyles';
|
|
7
7
|
|
|
@@ -47,7 +47,7 @@ export class AggregateComponent extends PreactLitAdapterWithGridJsStyles {
|
|
|
47
47
|
* A list of tabs with views that this component should provide.
|
|
48
48
|
*/
|
|
49
49
|
@property({ type: Array })
|
|
50
|
-
views:
|
|
50
|
+
views: ('table' | 'bar')[] = ['table'];
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* The filter to apply to the data.
|