@genspectrum/dashboard-components 1.8.1 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/custom-elements.json +2 -2
  2. package/dist/assets/{mutationOverTimeWorker-BRPqAM5Z.js.map → mutationOverTimeWorker-dhufsWQ2.js.map} +1 -1
  3. package/dist/components.d.ts +20 -20
  4. package/dist/components.js +118 -31
  5. package/dist/components.js.map +1 -1
  6. package/dist/util.d.ts +20 -20
  7. package/package.json +1 -1
  8. package/src/preact/components/downshift-combobox.tsx +2 -1
  9. package/src/preact/components/portal-tooltip.tsx +129 -0
  10. package/src/preact/components/tooltip.tsx +32 -16
  11. package/src/preact/lineageFilter/lineage-filter.stories.tsx +57 -1
  12. package/src/preact/lineageFilter/lineage-filter.tsx +1 -1
  13. package/src/preact/locationFilter/location-filter.tsx +3 -1
  14. package/src/preact/mutationsOverTime/__mockData__/withGaps.ts +0 -54
  15. package/src/preact/mutationsOverTime/mutations-over-time-grid-tooltip.tsx +1 -1
  16. package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +19 -9
  17. package/src/preact/mutationsOverTime/mutations-over-time.tsx +13 -4
  18. package/src/preact/textFilter/text-filter.tsx +3 -1
  19. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +8 -4
  20. package/src/query/queryMutationsOverTime.ts +28 -11
  21. package/src/query/queryMutationsOverTimeNewEndpoint.spec.ts +6 -6
  22. package/src/web-components/input/gs-lineage-filter.stories.ts +1 -1
  23. package/src/web-components/input/gs-text-filter.stories.ts +1 -1
  24. package/standalone-bundle/assets/{mutationOverTimeWorker-DtFX4Ihx.js.map → mutationOverTimeWorker-CGqPKySO.js.map} +1 -1
  25. package/standalone-bundle/dashboard-components.js +4663 -4594
  26. package/standalone-bundle/dashboard-components.js.map +1 -1
@@ -1,5 +1,5 @@
1
1
  import { type FunctionComponent } from 'preact';
2
- import { type Dispatch, type StateUpdater, useMemo, useState, useEffect } from 'preact/hooks';
2
+ import { type Dispatch, type StateUpdater, useMemo, useState, useEffect, useLayoutEffect, useRef } from 'preact/hooks';
3
3
  import z from 'zod';
4
4
 
5
5
  // @ts-expect-error -- uses subpath imports and vite worker import
@@ -142,6 +142,12 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
142
142
  overallMutationData,
143
143
  }) => {
144
144
  const tabsRef = useDispatchFinishedLoadingEvent();
145
+ const tooltipPortalTargetRef = useRef<HTMLDivElement>(null);
146
+ const [tooltipPortalTarget, setTooltipPortalTarget] = useState<HTMLDivElement | null>(null);
147
+
148
+ useLayoutEffect(() => {
149
+ setTooltipPortalTarget(tooltipPortalTargetRef.current);
150
+ }, []);
145
151
 
146
152
  const [mutationFilterValue, setMutationFilterValue] = useState<MutationFilter>({
147
153
  textFilter: '',
@@ -198,6 +204,7 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
198
204
  colorScale={colorScale}
199
205
  sequenceType={originalComponentProps.sequenceType}
200
206
  pageSizes={originalComponentProps.pageSizes}
207
+ tooltipPortalTarget={tooltipPortalTarget}
201
208
  />
202
209
  ),
203
210
  };
@@ -227,9 +234,11 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
227
234
  );
228
235
 
229
236
  return (
230
- <PageSizeContextProvider pageSizes={originalComponentProps.pageSizes}>
231
- <Tabs ref={tabsRef} tabs={tabs} toolbar={toolbar} />
232
- </PageSizeContextProvider>
237
+ <div ref={tooltipPortalTargetRef}>
238
+ <PageSizeContextProvider pageSizes={originalComponentProps.pageSizes}>
239
+ <Tabs ref={tabsRef} tabs={tabs} toolbar={toolbar} />
240
+ </PageSizeContextProvider>
241
+ </div>
233
242
  );
234
243
  };
235
244
 
@@ -100,7 +100,9 @@ const TextSelector = ({
100
100
  return (
101
101
  <p>
102
102
  <span>{item.value}</span>
103
- {!hideCounts && <span className='ml-2 text-gray-500'>({item.count})</span>}
103
+ {!hideCounts && (
104
+ <span className='ml-2 text-gray-500'>({item.count.toLocaleString('en-US')})</span>
105
+ )}
104
106
  </p>
105
107
  );
106
108
  }}
@@ -1,5 +1,5 @@
1
1
  import { type FunctionComponent } from 'preact';
2
- import { type Dispatch, type StateUpdater, useMemo, useState } from 'preact/hooks';
2
+ import { type Dispatch, type StateUpdater, useMemo, useState, useRef } from 'preact/hooks';
3
3
  import z from 'zod';
4
4
 
5
5
  import { computeWastewaterMutationsOverTimeDataPerLocation } from './computeWastewaterMutationsOverTimeDataPerLocation';
@@ -150,6 +150,7 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
150
150
  originalComponentProps,
151
151
  }) => {
152
152
  const tabsRef = useDispatchFinishedLoadingEvent();
153
+ const tooltipPortalTargetRef = useRef<HTMLDivElement>(null);
153
154
 
154
155
  const [mutationFilterValue, setMutationFilterValue] = useState<MutationFilter>({
155
156
  textFilter: '',
@@ -176,6 +177,7 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
176
177
  colorScale={colorScale}
177
178
  pageSizes={originalComponentProps.pageSizes}
178
179
  sequenceType={originalComponentProps.sequenceType}
180
+ tooltipPortalTarget={tooltipPortalTargetRef.current}
179
181
  />
180
182
  ),
181
183
  })),
@@ -204,9 +206,11 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
204
206
  );
205
207
 
206
208
  return (
207
- <PageSizeContextProvider pageSizes={originalComponentProps.pageSizes}>
208
- <Tabs ref={tabsRef} tabs={tabs} toolbar={toolbar} />
209
- </PageSizeContextProvider>
209
+ <div ref={tooltipPortalTargetRef}>
210
+ <PageSizeContextProvider pageSizes={originalComponentProps.pageSizes}>
211
+ <Tabs ref={tabsRef} tabs={tabs} toolbar={toolbar} />
212
+ </PageSizeContextProvider>
213
+ </div>
210
214
  );
211
215
  };
212
216
 
@@ -295,17 +295,34 @@ async function queryMutationsOverTimeDataDirectEndpoint(
295
295
  responseMutations.map((mutation, i) => [
296
296
  mutation.code,
297
297
  new Map(
298
- allDates.map((date, j): [string, MutationOverTimeMutationValue] => [
299
- date.dateString,
300
- {
301
- type: 'value',
302
- // 'coverage' in the API resp. is the number of seqs. that have a non-ambiguous symbol at position
303
- // 'count' in the API resp. is the number of seqs with the mutation
304
- proportion: apiResult.data.data[i][j].count / apiResult.data.data[i][j].coverage,
305
- count: apiResult.data.data[i][j].count,
306
- totalCount: totalCounts[j],
307
- },
308
- ]),
298
+ allDates.map((date, j): [string, MutationOverTimeMutationValue] => {
299
+ if (totalCounts[j] === 0) {
300
+ return [date.dateString, null];
301
+ }
302
+ // 'count' in the API resp. is the number of seqs with the mutation
303
+ const count = apiResult.data.data[i][j].count;
304
+ // 'coverage' in the API resp. is the number of seqs. that have a non-ambiguous symbol at position
305
+ const coverage = apiResult.data.data[i][j].coverage;
306
+ const totalCount = totalCounts[j];
307
+ if (coverage === 0) {
308
+ return [
309
+ date.dateString,
310
+ {
311
+ type: 'belowThreshold',
312
+ totalCount,
313
+ },
314
+ ];
315
+ }
316
+ return [
317
+ date.dateString,
318
+ {
319
+ type: 'value',
320
+ proportion: count / coverage,
321
+ count,
322
+ totalCount,
323
+ },
324
+ ];
325
+ }),
309
326
  ),
310
327
  ]),
311
328
  ),
@@ -290,12 +290,12 @@ describe('queryMutationsOverTimeNewEndpoint', () => {
290
290
  expect(mutationOverTimeData.getAsArray()).to.deep.equal([
291
291
  [
292
292
  { type: 'value', proportion: 0.4, count: 4, totalCount: 11 },
293
- { type: 'value', proportion: 0, count: 0, totalCount: 0 },
293
+ null,
294
294
  { type: 'value', proportion: 0, count: 0, totalCount: 13 },
295
295
  ],
296
296
  [
297
297
  { type: 'value', proportion: 0.1, count: 1, totalCount: 11 },
298
- { type: 'value', proportion: 0, count: 0, totalCount: 0 },
298
+ null,
299
299
  { type: 'value', proportion: 0.3, count: 3, totalCount: 13 },
300
300
  ],
301
301
  ]);
@@ -1013,8 +1013,8 @@ describe('queryMutationsOverTimeNewEndpoint', () => {
1013
1013
 
1014
1014
  expect(mutationOverTimeData.getAsArray()).to.deep.equal([
1015
1015
  [
1016
- { type: 'value', proportion: NaN, count: 0, totalCount: 11 },
1017
- { type: 'value', proportion: NaN, count: 0, totalCount: 12 },
1016
+ { type: 'belowThreshold', totalCount: 11 },
1017
+ { type: 'belowThreshold', totalCount: 12 },
1018
1018
  ],
1019
1019
  [
1020
1020
  { type: 'value', proportion: 0.2, count: 2, totalCount: 11 },
@@ -1135,8 +1135,8 @@ describe('queryMutationsOverTimeNewEndpoint', () => {
1135
1135
 
1136
1136
  expect(mutationOverTimeData.getAsArray()).to.deep.equal([
1137
1137
  [
1138
- { type: 'value', proportion: NaN, count: 0, totalCount: 11 },
1139
- { type: 'value', proportion: NaN, count: 0, totalCount: 12 },
1138
+ { type: 'belowThreshold', totalCount: 11 },
1139
+ { type: 'belowThreshold', totalCount: 12 },
1140
1140
  ],
1141
1141
  [
1142
1142
  { type: 'value', proportion: 0.2, count: 2, totalCount: 11 },
@@ -229,7 +229,7 @@ export const FiresEvent: StoryObj<Required<LineageFilterProps>> = {
229
229
 
230
230
  await step('Enter a valid lineage value', async () => {
231
231
  await userEvent.type(inputField(), 'B.1.1.7*');
232
- await userEvent.click(canvas.getByRole('option', { name: 'B.1.1.7*(677146)' }));
232
+ await userEvent.click(canvas.getByRole('option', { name: 'B.1.1.7*(677,146)' }));
233
233
 
234
234
  await waitFor(() => {
235
235
  return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({
@@ -162,7 +162,7 @@ export const FiresEvents: StoryObj<Required<TextFilterProps>> = {
162
162
  await step('Remove initial value', async () => {
163
163
  await userEvent.click(canvas.getByRole('button', { name: 'clear selection' }));
164
164
 
165
- await expect(listenerMock).toHaveBeenLastCalledWith(
165
+ await expect(listenerMock).toHaveBeenCalledWith(
166
166
  expect.objectContaining({
167
167
  detail: {
168
168
  host: undefined,