@genspectrum/dashboard-components 1.3.1 → 1.5.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 +97 -5
- package/dist/assets/{mutationOverTimeWorker-CQxrFo53.js.map → mutationOverTimeWorker-BJ_P2T8Y.js.map} +1 -1
- package/dist/components.d.ts +45 -25
- package/dist/components.js +118 -21
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +25 -25
- package/package.json +1 -1
- package/src/preact/lineageFilter/lineage-filter.stories.tsx +24 -0
- package/src/preact/lineageFilter/lineage-filter.tsx +13 -2
- package/src/preact/locationFilter/location-filter.stories.tsx +24 -0
- package/src/preact/locationFilter/location-filter.tsx +19 -3
- package/src/preact/mutationsOverTime/__mockData__/withGaps.ts +352 -0
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +38 -0
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +11 -1
- package/src/preact/mutationsOverTime/mutationOverTimeWorker.mock.ts +2 -0
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +35 -0
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +28 -4
- package/src/preact/textFilter/text-filter.stories.tsx +29 -4
- package/src/preact/textFilter/text-filter.tsx +13 -2
- package/src/query/queryMutationsOverTime.ts +37 -4
- package/src/query/queryMutationsOverTimeNewEndpoint.spec.ts +122 -0
- package/src/utils/map2d.spec.ts +30 -0
- package/src/utils/map2d.ts +14 -1
- package/src/web-components/input/gs-lineage-filter.stories.ts +7 -0
- package/src/web-components/input/gs-lineage-filter.tsx +8 -0
- package/src/web-components/input/gs-location-filter.stories.ts +7 -0
- package/src/web-components/input/gs-location-filter.tsx +9 -1
- package/src/web-components/input/gs-text-filter.stories.ts +7 -0
- package/src/web-components/input/gs-text-filter.tsx +8 -0
- package/src/web-components/visualization/gs-mutations-over-time.stories.ts +14 -1
- package/src/web-components/visualization/gs-mutations-over-time.tsx +8 -0
- package/standalone-bundle/assets/{mutationOverTimeWorker-CDACUs6w.js.map → mutationOverTimeWorker-CkeGpKWp.js.map} +1 -1
- package/standalone-bundle/dashboard-components.js +1413 -1330
- package/standalone-bundle/dashboard-components.js.map +1 -1
|
@@ -46,6 +46,11 @@ const meta: Meta<TextFilterProps> = {
|
|
|
46
46
|
type: 'text',
|
|
47
47
|
},
|
|
48
48
|
},
|
|
49
|
+
hideCounts: {
|
|
50
|
+
control: {
|
|
51
|
+
type: 'boolean',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
49
54
|
value: {
|
|
50
55
|
control: {
|
|
51
56
|
type: 'text',
|
|
@@ -75,6 +80,7 @@ export const Default: StoryObj<TextFilterProps> = {
|
|
|
75
80
|
args: {
|
|
76
81
|
lapisField: 'host',
|
|
77
82
|
placeholderText: 'Enter a host name',
|
|
83
|
+
hideCounts: false,
|
|
78
84
|
value: '',
|
|
79
85
|
width: '100%',
|
|
80
86
|
lapisFilter: {
|
|
@@ -128,10 +134,6 @@ export const KeepsPartialInputInInputField: StoryObj<TextFilterProps> = {
|
|
|
128
134
|
</LapisUrlContextProvider>
|
|
129
135
|
</>
|
|
130
136
|
),
|
|
131
|
-
args: {
|
|
132
|
-
...Default.args,
|
|
133
|
-
value: '',
|
|
134
|
-
},
|
|
135
137
|
play: async ({ canvasElement, step }) => {
|
|
136
138
|
const canvas = within(canvasElement);
|
|
137
139
|
|
|
@@ -176,6 +178,29 @@ export const KeepsPartialInputInInputField: StoryObj<TextFilterProps> = {
|
|
|
176
178
|
},
|
|
177
179
|
};
|
|
178
180
|
|
|
181
|
+
export const WithHideCountsTrue: StoryObj<TextFilterProps> = {
|
|
182
|
+
...Default,
|
|
183
|
+
args: {
|
|
184
|
+
...Default.args,
|
|
185
|
+
hideCounts: true,
|
|
186
|
+
},
|
|
187
|
+
play: async ({ canvasElement, step }) => {
|
|
188
|
+
const canvas = within(canvasElement);
|
|
189
|
+
const inputField = () => canvas.getByPlaceholderText('Enter a host name', { exact: false });
|
|
190
|
+
|
|
191
|
+
await waitFor(async () => {
|
|
192
|
+
await expect(inputField()).toHaveValue('');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
await step('visible without counts', async () => {
|
|
196
|
+
const input = inputField();
|
|
197
|
+
await userEvent.clear(input);
|
|
198
|
+
await userEvent.type(input, 'Homo');
|
|
199
|
+
await expect(canvas.getByRole('option', { name: 'Homo' })).toBeVisible();
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
|
|
179
204
|
export const WithNoLapisField: StoryObj<TextFilterProps> = {
|
|
180
205
|
...Default,
|
|
181
206
|
args: {
|
|
@@ -15,6 +15,7 @@ const textSelectorPropsSchema = z.object({
|
|
|
15
15
|
lapisField: z.string().min(1),
|
|
16
16
|
placeholderText: z.string().optional(),
|
|
17
17
|
value: z.string().optional(),
|
|
18
|
+
hideCounts: z.boolean().optional(),
|
|
18
19
|
});
|
|
19
20
|
const textFilterInnerPropsSchema = textSelectorPropsSchema.extend({ lapisFilter: lapisFilterSchema });
|
|
20
21
|
const textFilterPropsSchema = textFilterInnerPropsSchema.extend({
|
|
@@ -42,6 +43,7 @@ const TextFilterInner: FunctionComponent<TextFilterInnerProps> = ({
|
|
|
42
43
|
value,
|
|
43
44
|
lapisField,
|
|
44
45
|
placeholderText,
|
|
46
|
+
hideCounts,
|
|
45
47
|
lapisFilter,
|
|
46
48
|
}) => {
|
|
47
49
|
const lapis = useLapisUrl();
|
|
@@ -59,7 +61,15 @@ const TextFilterInner: FunctionComponent<TextFilterInnerProps> = ({
|
|
|
59
61
|
throw error;
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
return
|
|
64
|
+
return (
|
|
65
|
+
<TextSelector
|
|
66
|
+
lapisField={lapisField}
|
|
67
|
+
value={value}
|
|
68
|
+
placeholderText={placeholderText}
|
|
69
|
+
hideCounts={hideCounts}
|
|
70
|
+
data={data}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
63
73
|
};
|
|
64
74
|
|
|
65
75
|
type SelectItem = {
|
|
@@ -72,6 +82,7 @@ const TextSelector = ({
|
|
|
72
82
|
value,
|
|
73
83
|
placeholderText,
|
|
74
84
|
data,
|
|
85
|
+
hideCounts = false,
|
|
75
86
|
}: TextSelectorProps & {
|
|
76
87
|
data: SelectItem[];
|
|
77
88
|
}) => {
|
|
@@ -89,7 +100,7 @@ const TextSelector = ({
|
|
|
89
100
|
return (
|
|
90
101
|
<p>
|
|
91
102
|
<span>{item.value}</span>
|
|
92
|
-
<span className='ml-2 text-gray-500'>({item.count})</span>
|
|
103
|
+
{!hideCounts && <span className='ml-2 text-gray-500'>({item.count})</span>}
|
|
93
104
|
</p>
|
|
94
105
|
);
|
|
95
106
|
}}
|
|
@@ -267,12 +267,33 @@ async function queryMutationsOverTimeDataDirectEndpoint(
|
|
|
267
267
|
signal,
|
|
268
268
|
);
|
|
269
269
|
|
|
270
|
+
const responseMutations = apiResult.data.mutations.map(parseMutationCode);
|
|
271
|
+
const mutationEntries: SubstitutionOrDeletionEntry[] = responseMutations.map((mutation, i) => {
|
|
272
|
+
const numbers = {
|
|
273
|
+
count: overallMutationData[i].count,
|
|
274
|
+
proportion: overallMutationData[i].proportion,
|
|
275
|
+
};
|
|
276
|
+
if (mutation.type === 'deletion') {
|
|
277
|
+
return {
|
|
278
|
+
type: 'deletion',
|
|
279
|
+
mutation,
|
|
280
|
+
...numbers,
|
|
281
|
+
};
|
|
282
|
+
} else {
|
|
283
|
+
return {
|
|
284
|
+
type: 'substitution',
|
|
285
|
+
mutation,
|
|
286
|
+
...numbers,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
270
291
|
const mutationOverTimeData: Map2DContents<Substitution | Deletion, Temporal, MutationOverTimeMutationValue> = {
|
|
271
|
-
keysFirstAxis: new Map(
|
|
292
|
+
keysFirstAxis: new Map(responseMutations.map((mutation) => [mutation.code, mutation])),
|
|
272
293
|
keysSecondAxis: new Map(allDates.map((date) => [date.dateString, date])),
|
|
273
294
|
data: new Map(
|
|
274
|
-
|
|
275
|
-
mutation.
|
|
295
|
+
responseMutations.map((mutation, i) => [
|
|
296
|
+
mutation.code,
|
|
276
297
|
new Map(
|
|
277
298
|
allDates.map((date, j): [string, MutationOverTimeMutationValue] => [
|
|
278
299
|
date.dateString,
|
|
@@ -292,10 +313,22 @@ async function queryMutationsOverTimeDataDirectEndpoint(
|
|
|
292
313
|
|
|
293
314
|
return {
|
|
294
315
|
mutationOverTimeData: new BaseMutationOverTimeDataMap(mutationOverTimeData),
|
|
295
|
-
overallMutationData,
|
|
316
|
+
overallMutationData: mutationEntries,
|
|
296
317
|
};
|
|
297
318
|
}
|
|
298
319
|
|
|
320
|
+
function parseMutationCode(code: string): SubstitutionClass | DeletionClass {
|
|
321
|
+
const maybeDeletion = DeletionClass.parse(code);
|
|
322
|
+
if (maybeDeletion) {
|
|
323
|
+
return maybeDeletion;
|
|
324
|
+
}
|
|
325
|
+
const maybeSubstitution = SubstitutionClass.parse(code);
|
|
326
|
+
if (maybeSubstitution) {
|
|
327
|
+
return maybeSubstitution;
|
|
328
|
+
}
|
|
329
|
+
throw Error('Given code is not valid');
|
|
330
|
+
}
|
|
331
|
+
|
|
299
332
|
/**
|
|
300
333
|
* Returns a list of date ranges as TemporalClass.
|
|
301
334
|
* Respects date range filters given in the lapisFilter as <lapisDateField>From and <lapisDateField>To.
|
|
@@ -1031,6 +1031,128 @@ describe('queryMutationsOverTimeNewEndpoint', () => {
|
|
|
1031
1031
|
expect(dates[1].dateString).toBe('2023-02');
|
|
1032
1032
|
});
|
|
1033
1033
|
|
|
1034
|
+
it('should return full mutation codes even if partial includeMutations are given', async () => {
|
|
1035
|
+
const lapisFilter = { field1: 'value1', field2: 'value2' };
|
|
1036
|
+
const dateField = 'dateField';
|
|
1037
|
+
|
|
1038
|
+
lapisRequestMocks.multipleAggregated([
|
|
1039
|
+
{
|
|
1040
|
+
body: { ...lapisFilter, fields: [dateField] },
|
|
1041
|
+
response: {
|
|
1042
|
+
data: [
|
|
1043
|
+
{ count: 1, [dateField]: '2023-01-05' },
|
|
1044
|
+
{ count: 2, [dateField]: '2023-02-15' },
|
|
1045
|
+
],
|
|
1046
|
+
},
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
body: {
|
|
1050
|
+
...lapisFilter,
|
|
1051
|
+
dateFieldFrom: '2023-01-01',
|
|
1052
|
+
dateFieldTo: '2023-01-31',
|
|
1053
|
+
fields: [],
|
|
1054
|
+
},
|
|
1055
|
+
response: { data: [{ count: 11 }] },
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
body: {
|
|
1059
|
+
...lapisFilter,
|
|
1060
|
+
dateFieldFrom: '2023-02-01',
|
|
1061
|
+
dateFieldTo: '2023-02-28',
|
|
1062
|
+
fields: [],
|
|
1063
|
+
},
|
|
1064
|
+
response: { data: [{ count: 12 }] },
|
|
1065
|
+
},
|
|
1066
|
+
]);
|
|
1067
|
+
|
|
1068
|
+
lapisRequestMocks.multipleMutations(
|
|
1069
|
+
[
|
|
1070
|
+
{
|
|
1071
|
+
body: {
|
|
1072
|
+
...lapisFilter,
|
|
1073
|
+
dateFieldFrom: '2023-01-01',
|
|
1074
|
+
dateFieldTo: '2023-02-28',
|
|
1075
|
+
minProportion: 0.001,
|
|
1076
|
+
},
|
|
1077
|
+
response: {
|
|
1078
|
+
data: [getSomeTestMutation(0.21, 6), getSomeOtherTestMutation(0.22, 4)],
|
|
1079
|
+
},
|
|
1080
|
+
},
|
|
1081
|
+
],
|
|
1082
|
+
'nucleotide',
|
|
1083
|
+
);
|
|
1084
|
+
|
|
1085
|
+
const dateRanges = [
|
|
1086
|
+
{
|
|
1087
|
+
dateFrom: '2023-01-01',
|
|
1088
|
+
dateTo: '2023-01-31',
|
|
1089
|
+
},
|
|
1090
|
+
{
|
|
1091
|
+
dateFrom: '2023-02-01',
|
|
1092
|
+
dateTo: '2023-02-28',
|
|
1093
|
+
},
|
|
1094
|
+
];
|
|
1095
|
+
|
|
1096
|
+
lapisRequestMocks.mutationsOverTime(
|
|
1097
|
+
[
|
|
1098
|
+
{
|
|
1099
|
+
body: {
|
|
1100
|
+
filters: lapisFilter,
|
|
1101
|
+
dateRanges,
|
|
1102
|
+
includeMutations: ['122', 'otherSequenceName:G234C'],
|
|
1103
|
+
dateField,
|
|
1104
|
+
},
|
|
1105
|
+
response: {
|
|
1106
|
+
data: {
|
|
1107
|
+
data: [
|
|
1108
|
+
[
|
|
1109
|
+
{ count: 0, coverage: 0 },
|
|
1110
|
+
{ count: 0, coverage: 0 },
|
|
1111
|
+
],
|
|
1112
|
+
[
|
|
1113
|
+
{ count: 2, coverage: 10 },
|
|
1114
|
+
{ count: 3, coverage: 10 },
|
|
1115
|
+
],
|
|
1116
|
+
],
|
|
1117
|
+
dateRanges,
|
|
1118
|
+
mutations: ['A122T', 'otherSequenceName:G234C'],
|
|
1119
|
+
},
|
|
1120
|
+
},
|
|
1121
|
+
},
|
|
1122
|
+
],
|
|
1123
|
+
'nucleotide',
|
|
1124
|
+
);
|
|
1125
|
+
|
|
1126
|
+
const { mutationOverTimeData } = await queryMutationsOverTimeData({
|
|
1127
|
+
lapisFilter,
|
|
1128
|
+
sequenceType: 'nucleotide',
|
|
1129
|
+
lapis: DUMMY_LAPIS_URL,
|
|
1130
|
+
lapisDateField: dateField,
|
|
1131
|
+
granularity: 'month',
|
|
1132
|
+
useNewEndpoint: true,
|
|
1133
|
+
displayMutations: ['otherSequenceName:G234C', '122'],
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
1137
|
+
[
|
|
1138
|
+
{ type: 'value', proportion: NaN, count: 0, totalCount: 11 },
|
|
1139
|
+
{ type: 'value', proportion: NaN, count: 0, totalCount: 12 },
|
|
1140
|
+
],
|
|
1141
|
+
[
|
|
1142
|
+
{ type: 'value', proportion: 0.2, count: 2, totalCount: 11 },
|
|
1143
|
+
{ type: 'value', proportion: 0.3, count: 3, totalCount: 12 },
|
|
1144
|
+
],
|
|
1145
|
+
]);
|
|
1146
|
+
|
|
1147
|
+
const sequences = mutationOverTimeData.getFirstAxisKeys();
|
|
1148
|
+
expect(sequences[0].code).toBe('A122T');
|
|
1149
|
+
expect(sequences[1].code).toBe('otherSequenceName:G234C');
|
|
1150
|
+
|
|
1151
|
+
const dates = mutationOverTimeData.getSecondAxisKeys();
|
|
1152
|
+
expect(dates[0].dateString).toBe('2023-01');
|
|
1153
|
+
expect(dates[1].dateString).toBe('2023-02');
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1034
1156
|
function getSomeTestMutation(proportion: number, count: number) {
|
|
1035
1157
|
return {
|
|
1036
1158
|
mutation: 'sequenceName:A123T',
|
package/src/utils/map2d.spec.ts
CHANGED
|
@@ -186,6 +186,11 @@ describe('Map2dView', () => {
|
|
|
186
186
|
view.deleteRow('c');
|
|
187
187
|
|
|
188
188
|
expect(view.getAsArray()).toEqual([[1, undefined]]);
|
|
189
|
+
|
|
190
|
+
const view2 = new Map2dView<string, string, number>(container);
|
|
191
|
+
view2.deleteColumn('b');
|
|
192
|
+
|
|
193
|
+
expect(view2.getAsArray()).toEqual([[undefined], [4]]);
|
|
189
194
|
});
|
|
190
195
|
|
|
191
196
|
it('should throw an error when trying to set a value', () => {
|
|
@@ -201,12 +206,31 @@ describe('Map2dView', () => {
|
|
|
201
206
|
expect(view.getRow('a')).toEqual([1, undefined]);
|
|
202
207
|
});
|
|
203
208
|
|
|
209
|
+
it('should return a column by key', () => {
|
|
210
|
+
const container = createBaseContainer();
|
|
211
|
+
const view = new Map2dView<string, string, number>(container);
|
|
212
|
+
|
|
213
|
+
expect(view.getColumn('b')).toEqual([1, 3]);
|
|
214
|
+
expect(view.getColumn('d')).toEqual([undefined, 4]);
|
|
215
|
+
});
|
|
216
|
+
|
|
204
217
|
it('should return an empty array when the row does not exist', () => {
|
|
205
218
|
const container = createBaseContainer();
|
|
206
219
|
const view = new Map2dView<string, string, number>(container);
|
|
207
220
|
view.deleteRow('c');
|
|
208
221
|
|
|
209
222
|
expect(view.getRow('c')).toEqual([]);
|
|
223
|
+
expect(view.getColumn('b')).toEqual([1]);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should return an empty array when the column does not exist', () => {
|
|
227
|
+
const container = createBaseContainer();
|
|
228
|
+
const view = new Map2dView<string, string, number>(container);
|
|
229
|
+
view.deleteColumn('b');
|
|
230
|
+
|
|
231
|
+
expect(view.getColumn('b')).toEqual([]);
|
|
232
|
+
expect(view.getRow('a')).toEqual([undefined]);
|
|
233
|
+
expect(view.getRow('c')).toEqual([4]);
|
|
210
234
|
});
|
|
211
235
|
|
|
212
236
|
function createBaseContainer() {
|
|
@@ -217,6 +241,12 @@ describe('Map2dView', () => {
|
|
|
217
241
|
container.set('a', 'b', 1);
|
|
218
242
|
container.set('c', 'b', 3);
|
|
219
243
|
container.set('c', 'd', 4);
|
|
244
|
+
|
|
245
|
+
// | | b | d |
|
|
246
|
+
// |---|---|---|
|
|
247
|
+
// | a | 1 | |
|
|
248
|
+
// | c | 3 | 4 |
|
|
249
|
+
|
|
220
250
|
return container;
|
|
221
251
|
}
|
|
222
252
|
});
|
package/src/utils/map2d.ts
CHANGED
|
@@ -168,6 +168,10 @@ export class Map2dView<Key1 extends object | string, Key2 extends object | strin
|
|
|
168
168
|
this.keysFirstAxis.delete(this.serializeFirstAxis(key));
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
deleteColumn(key: Key2) {
|
|
172
|
+
this.keysSecondAxis.delete(this.serializeSecondAxis(key));
|
|
173
|
+
}
|
|
174
|
+
|
|
171
175
|
get(keyFirstAxis: Key1, keySecondAxis: Key2) {
|
|
172
176
|
const firstAxisKey = this.serializeFirstAxis(keyFirstAxis);
|
|
173
177
|
const secondAxisKey = this.serializeSecondAxis(keySecondAxis);
|
|
@@ -205,7 +209,16 @@ export class Map2dView<Key1 extends object | string, Key2 extends object | strin
|
|
|
205
209
|
return [];
|
|
206
210
|
}
|
|
207
211
|
|
|
208
|
-
return this.baseMap.
|
|
212
|
+
return this.getSecondAxisKeys().map((k2) => this.baseMap.get(key, k2));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
getColumn(key: Key2) {
|
|
216
|
+
const serializedKeySecondAxis = this.serializeSecondAxis(key);
|
|
217
|
+
if (!this.keysSecondAxis.has(serializedKeySecondAxis)) {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return this.getFirstAxisKeys().map((k1) => this.baseMap.get(k1, key));
|
|
209
222
|
}
|
|
210
223
|
|
|
211
224
|
getContents() {
|
|
@@ -92,6 +92,11 @@ const meta: Meta<Required<LineageFilterProps>> = {
|
|
|
92
92
|
type: 'object',
|
|
93
93
|
},
|
|
94
94
|
},
|
|
95
|
+
hideCounts: {
|
|
96
|
+
control: {
|
|
97
|
+
type: 'boolean',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
95
100
|
},
|
|
96
101
|
};
|
|
97
102
|
|
|
@@ -105,6 +110,7 @@ const Template: StoryObj<Required<LineageFilterProps>> = {
|
|
|
105
110
|
.lapisField=${args.lapisField}
|
|
106
111
|
.lapisFilter=${args.lapisFilter}
|
|
107
112
|
.placeholderText=${args.placeholderText}
|
|
113
|
+
.hideCounts=${args.hideCounts}
|
|
108
114
|
.value=${args.value}
|
|
109
115
|
.width=${args.width}
|
|
110
116
|
></gs-lineage-filter>
|
|
@@ -119,6 +125,7 @@ const Template: StoryObj<Required<LineageFilterProps>> = {
|
|
|
119
125
|
placeholderText: 'Enter a lineage',
|
|
120
126
|
value: 'B.1.1.7',
|
|
121
127
|
width: '100%',
|
|
128
|
+
hideCounts: false,
|
|
122
129
|
},
|
|
123
130
|
};
|
|
124
131
|
|
|
@@ -73,6 +73,13 @@ export class LineageFilterComponent extends PreactLitAdapter {
|
|
|
73
73
|
@property({ type: String })
|
|
74
74
|
width: string = '100%';
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Whether to hide counts behind lineage options in the drop down selection.
|
|
78
|
+
* Defaults to false.
|
|
79
|
+
*/
|
|
80
|
+
@property({ type: Boolean })
|
|
81
|
+
hideCounts: boolean | undefined = false;
|
|
82
|
+
|
|
76
83
|
override render() {
|
|
77
84
|
return (
|
|
78
85
|
<LineageFilter
|
|
@@ -81,6 +88,7 @@ export class LineageFilterComponent extends PreactLitAdapter {
|
|
|
81
88
|
placeholderText={this.placeholderText}
|
|
82
89
|
value={this.value}
|
|
83
90
|
width={this.width}
|
|
91
|
+
hideCounts={this.hideCounts}
|
|
84
92
|
/>
|
|
85
93
|
);
|
|
86
94
|
}
|
|
@@ -56,6 +56,11 @@ const meta: Meta = {
|
|
|
56
56
|
type: 'text',
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
|
+
hideCounts: {
|
|
60
|
+
control: {
|
|
61
|
+
type: 'boolean',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
59
64
|
lapisFilter: {
|
|
60
65
|
age: 18,
|
|
61
66
|
},
|
|
@@ -75,6 +80,7 @@ const Template: StoryObj<LocationFilterProps> = {
|
|
|
75
80
|
.value=${args.value}
|
|
76
81
|
.width=${args.width}
|
|
77
82
|
placeholderText=${ifDefined(args.placeholderText)}
|
|
83
|
+
.hideCounts=${args.hideCounts}
|
|
78
84
|
></gs-location-filter>
|
|
79
85
|
</div>
|
|
80
86
|
</gs-app>`;
|
|
@@ -87,6 +93,7 @@ const Template: StoryObj<LocationFilterProps> = {
|
|
|
87
93
|
value: undefined,
|
|
88
94
|
width: '100%',
|
|
89
95
|
placeholderText: 'Enter a location',
|
|
96
|
+
hideCounts: false,
|
|
90
97
|
},
|
|
91
98
|
};
|
|
92
99
|
|
|
@@ -74,9 +74,16 @@ export class LocationFilterComponent extends PreactLitAdapter {
|
|
|
74
74
|
/**
|
|
75
75
|
* The placeholder text to display in the input field, if it is empty.
|
|
76
76
|
*/
|
|
77
|
-
@property()
|
|
77
|
+
@property({ type: String })
|
|
78
78
|
placeholderText: string | undefined = undefined;
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Whether to hide counts behind location options in the drop down selection.
|
|
82
|
+
* Defaults to false.
|
|
83
|
+
*/
|
|
84
|
+
@property({ type: Boolean })
|
|
85
|
+
hideCounts: boolean | undefined = false;
|
|
86
|
+
|
|
80
87
|
override render() {
|
|
81
88
|
return (
|
|
82
89
|
<LocationFilter
|
|
@@ -85,6 +92,7 @@ export class LocationFilterComponent extends PreactLitAdapter {
|
|
|
85
92
|
lapisFilter={this.lapisFilter}
|
|
86
93
|
width={this.width}
|
|
87
94
|
placeholderText={this.placeholderText}
|
|
95
|
+
hideCounts={this.hideCounts}
|
|
88
96
|
/>
|
|
89
97
|
);
|
|
90
98
|
}
|
|
@@ -63,6 +63,11 @@ const meta: Meta<Required<TextFilterProps>> = {
|
|
|
63
63
|
type: 'text',
|
|
64
64
|
},
|
|
65
65
|
},
|
|
66
|
+
hideCounts: {
|
|
67
|
+
control: {
|
|
68
|
+
type: 'boolean',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
66
71
|
value: {
|
|
67
72
|
control: {
|
|
68
73
|
type: 'text',
|
|
@@ -92,6 +97,7 @@ export const Default: StoryObj<Required<TextFilterProps>> = {
|
|
|
92
97
|
.lapisField=${args.lapisField}
|
|
93
98
|
.lapisFilter=${args.lapisFilter}
|
|
94
99
|
.placeholderText=${args.placeholderText}
|
|
100
|
+
.hideCounts=${args.hideCounts}
|
|
95
101
|
.value=${args.value}
|
|
96
102
|
.width=${args.width}
|
|
97
103
|
></gs-text-filter>
|
|
@@ -102,6 +108,7 @@ export const Default: StoryObj<Required<TextFilterProps>> = {
|
|
|
102
108
|
lapisField: 'host',
|
|
103
109
|
lapisFilter: { country: 'Germany' },
|
|
104
110
|
placeholderText: 'Enter host name',
|
|
111
|
+
hideCounts: false,
|
|
105
112
|
value: 'Homo sapiens',
|
|
106
113
|
width: '100%',
|
|
107
114
|
},
|
|
@@ -59,6 +59,13 @@ export class TextFilterComponent extends PreactLitAdapter {
|
|
|
59
59
|
@property()
|
|
60
60
|
placeholderText: string | undefined = undefined;
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Whether to hide counts behind options in the drop down selection.
|
|
64
|
+
* Defaults to false.
|
|
65
|
+
*/
|
|
66
|
+
@property({ type: Boolean })
|
|
67
|
+
hideCounts: boolean | undefined = false;
|
|
68
|
+
|
|
62
69
|
/**
|
|
63
70
|
* The width of the component.
|
|
64
71
|
*
|
|
@@ -73,6 +80,7 @@ export class TextFilterComponent extends PreactLitAdapter {
|
|
|
73
80
|
lapisField={this.lapisField}
|
|
74
81
|
lapisFilter={this.lapisFilter}
|
|
75
82
|
placeholderText={this.placeholderText}
|
|
83
|
+
hideCounts={this.hideCounts}
|
|
76
84
|
value={this.value}
|
|
77
85
|
width={this.width}
|
|
78
86
|
/>
|
|
@@ -42,6 +42,7 @@ const meta: Meta<Required<MutationsOverTimeProps>> = {
|
|
|
42
42
|
lapisDateField: { control: 'text' },
|
|
43
43
|
displayMutations: { control: 'object' },
|
|
44
44
|
initialMeanProportionInterval: { control: 'object' },
|
|
45
|
+
hideGaps: { control: 'boolean' },
|
|
45
46
|
useNewEndpoint: { control: 'boolean' },
|
|
46
47
|
pageSizes: { control: 'object' },
|
|
47
48
|
},
|
|
@@ -53,6 +54,7 @@ const meta: Meta<Required<MutationsOverTimeProps>> = {
|
|
|
53
54
|
granularity: 'month',
|
|
54
55
|
lapisDateField: 'date',
|
|
55
56
|
initialMeanProportionInterval: { min: 0.05, max: 0.9 },
|
|
57
|
+
hideGaps: false,
|
|
56
58
|
useNewEndpoint: false,
|
|
57
59
|
pageSizes: [10, 20, 30, 40, 50],
|
|
58
60
|
},
|
|
@@ -100,6 +102,7 @@ const Template: StoryObj<Required<MutationsOverTimeProps>> = {
|
|
|
100
102
|
.lapisDateField=${args.lapisDateField}
|
|
101
103
|
.displayMutations=${args.displayMutations}
|
|
102
104
|
.initialMeanProportionInterval=${args.initialMeanProportionInterval}
|
|
105
|
+
.hideGaps=${args.hideGaps}
|
|
103
106
|
.pageSizes=${args.pageSizes}
|
|
104
107
|
.useNewEndpoint=${args.useNewEndpoint}
|
|
105
108
|
></gs-mutations-over-time>
|
|
@@ -112,7 +115,7 @@ export const ByMonth: StoryObj<Required<MutationsOverTimeProps>> = {
|
|
|
112
115
|
...Template,
|
|
113
116
|
};
|
|
114
117
|
|
|
115
|
-
// This test uses mock data:
|
|
118
|
+
// This test uses mock data: withDisplayMutations.ts (through mutationOverTimeWorker.mock.ts)
|
|
116
119
|
export const ByMonthWithFilterOnDisplayedMutations: StoryObj<Required<MutationsOverTimeProps>> = {
|
|
117
120
|
...Template,
|
|
118
121
|
args: {
|
|
@@ -121,6 +124,16 @@ export const ByMonthWithFilterOnDisplayedMutations: StoryObj<Required<MutationsO
|
|
|
121
124
|
},
|
|
122
125
|
};
|
|
123
126
|
|
|
127
|
+
// This test uses mock data: withGaps.ts (through mutationOverTimeWorker.mock.ts)
|
|
128
|
+
export const ByMonthWithFilterOnDisplayedMutationsAndGaps: StoryObj<Required<MutationsOverTimeProps>> = {
|
|
129
|
+
...Template,
|
|
130
|
+
args: {
|
|
131
|
+
...Template.args,
|
|
132
|
+
displayMutations: ['A19722G', 'G21641T', 'T21652-'],
|
|
133
|
+
hideGaps: true,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
124
137
|
// This test uses mock data: byWeek.ts (through mutationOverTimeWorker.mock.ts)
|
|
125
138
|
export const ByWeek: StoryObj<Required<MutationsOverTimeProps>> = {
|
|
126
139
|
...Template,
|
|
@@ -113,6 +113,13 @@ export class MutationsOverTimeComponent extends PreactLitAdapterWithGridJsStyles
|
|
|
113
113
|
@property({ type: Object })
|
|
114
114
|
initialMeanProportionInterval: { min: number; max: number } = { min: 0.05, max: 0.9 };
|
|
115
115
|
|
|
116
|
+
/**
|
|
117
|
+
* If true, date ranges with no data will be hidden initially; if false, not.
|
|
118
|
+
* Can be switched with a button in the toolbar.
|
|
119
|
+
*/
|
|
120
|
+
@property({ type: Boolean })
|
|
121
|
+
hideGaps: boolean = false;
|
|
122
|
+
|
|
116
123
|
/**
|
|
117
124
|
* Whether to use the mutationsOverTime endpoint from LAPIS.
|
|
118
125
|
* If true, use the endpoint, if false, compute component data as before.
|
|
@@ -146,6 +153,7 @@ export class MutationsOverTimeComponent extends PreactLitAdapterWithGridJsStyles
|
|
|
146
153
|
lapisDateField={this.lapisDateField}
|
|
147
154
|
displayMutations={this.displayMutations}
|
|
148
155
|
initialMeanProportionInterval={this.initialMeanProportionInterval}
|
|
156
|
+
hideGaps={this.hideGaps}
|
|
149
157
|
useNewEndpoint={this.useNewEndpoint}
|
|
150
158
|
pageSizes={this.pageSizes}
|
|
151
159
|
/>
|