@genspectrum/dashboard-components 0.18.5 → 0.18.6

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 (33) hide show
  1. package/README.md +12 -0
  2. package/custom-elements.json +1 -1
  3. package/dist/components.d.ts +16 -16
  4. package/dist/components.js +765 -315
  5. package/dist/components.js.map +1 -1
  6. package/dist/style.css +2 -2
  7. package/dist/util.d.ts +16 -16
  8. package/package.json +2 -2
  9. package/src/preact/MutationAnnotationsContext.tsx +34 -27
  10. package/src/preact/components/dropdown.tsx +1 -1
  11. package/src/preact/components/info.tsx +1 -1
  12. package/src/preact/components/mutations-over-time-text-filter.stories.tsx +57 -0
  13. package/src/preact/components/mutations-over-time-text-filter.tsx +63 -0
  14. package/src/preact/components/segment-selector.tsx +1 -1
  15. package/src/preact/mutationFilter/mutation-filter.stories.tsx +169 -50
  16. package/src/preact/mutationFilter/mutation-filter.tsx +239 -234
  17. package/src/preact/mutationFilter/parseAndValidateMutation.ts +62 -10
  18. package/src/preact/mutationFilter/parseMutation.spec.ts +62 -47
  19. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +128 -0
  20. package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +39 -2
  21. package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +8 -11
  22. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +27 -0
  23. package/src/preact/mutationsOverTime/mutations-over-time.tsx +26 -5
  24. package/src/preact/shared/tanstackTable/pagination-context.tsx +30 -0
  25. package/src/preact/shared/tanstackTable/pagination.tsx +19 -6
  26. package/src/preact/shared/tanstackTable/tanstackTable.tsx +17 -3
  27. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.stories.tsx +19 -1
  28. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +6 -1
  29. package/src/web-components/input/gs-mutation-filter.stories.ts +4 -4
  30. package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +1 -1
  31. package/standalone-bundle/dashboard-components.js +12896 -13366
  32. package/standalone-bundle/dashboard-components.js.map +1 -1
  33. package/standalone-bundle/style.css +1 -1
@@ -0,0 +1,30 @@
1
+ import { createContext, type FunctionComponent } from 'preact';
2
+ import { type Dispatch, type StateUpdater, useContext, useState } from 'preact/hooks';
3
+
4
+ import type { PageSizes } from './pagination';
5
+
6
+ type PageSizeContext = {
7
+ pageSize: number;
8
+ setPageSize: Dispatch<StateUpdater<number>>;
9
+ };
10
+
11
+ const pageSizeContext = createContext<PageSizeContext>({
12
+ pageSize: -1,
13
+ setPageSize: () => {
14
+ throw new Error('pageSizeContext not initialized');
15
+ },
16
+ });
17
+
18
+ export function usePageSizeContext() {
19
+ return useContext(pageSizeContext);
20
+ }
21
+
22
+ export type PageSizeContextProviderProps = {
23
+ pageSizes: PageSizes;
24
+ };
25
+
26
+ export const PageSizeContextProvider: FunctionComponent<PageSizeContextProviderProps> = ({ children, pageSizes }) => {
27
+ const [pageSize, setPageSize] = useState(typeof pageSizes === 'number' ? pageSizes : (pageSizes.at(0) ?? 10));
28
+
29
+ return <pageSizeContext.Provider value={{ pageSize, setPageSize }}>{children}</pageSizeContext.Provider>;
30
+ };
@@ -1,8 +1,12 @@
1
1
  import type { Table } from '@tanstack/table-core';
2
- import z from 'zod'; // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
+ import z from 'zod';
3
3
 
4
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
- type PaginationProps = { table: Table<any> };
4
+ import { usePageSizeContext } from './pagination-context';
5
+
6
+ type PaginationProps = {
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ table: Table<any>;
9
+ };
6
10
  export const pageSizesSchema = z.union([z.array(z.number()), z.number()]);
7
11
  export type PageSizes = z.infer<typeof pageSizesSchema>;
8
12
 
@@ -50,6 +54,8 @@ function PageSizeSelector({
50
54
  }: PaginationProps & {
51
55
  pageSizes: PageSizes;
52
56
  }) {
57
+ const { pageSize, setPageSize } = usePageSizeContext();
58
+
53
59
  if (typeof pageSizes === 'number' || pageSizes.length <= 1) {
54
60
  return null;
55
61
  }
@@ -59,9 +65,16 @@ function PageSizeSelector({
59
65
  <div className={'text-nowrap text-sm'}>Rows per page:</div>
60
66
  <select
61
67
  className={`select select-ghost select-sm ${heightForSmallerLines}`}
62
- value={table.getState().pagination.pageSize}
68
+ value={pageSize}
63
69
  onChange={(e) => {
64
- table.setPageSize(Number(e.currentTarget?.value));
70
+ const pageSize = Number(e.currentTarget?.value);
71
+ if (Number.isNaN(pageSize)) {
72
+ throw new Error(
73
+ `Invalid page size selected: The value ${e.currentTarget?.value} could not be parsed as a number.`,
74
+ );
75
+ }
76
+ setPageSize(pageSize);
77
+ table.setPageSize(pageSize);
65
78
  }}
66
79
  aria-label='Select number of rows per page'
67
80
  >
@@ -90,7 +103,7 @@ function GotoPageSelector({ table }: PaginationProps) {
90
103
  defaultValue={table.getState().pagination.pageIndex + 1}
91
104
  onChange={(e) => {
92
105
  const page = e.currentTarget.value ? Number(e.currentTarget.value) - 1 : 0;
93
- table.setPageIndex(page);
106
+ table.setPageIndex(Math.min(page, table.getPageCount() - 1));
94
107
  }}
95
108
  className={`input input-ghost input-sm ${heightForSmallerLines}`}
96
109
  aria-label='Enter page number to go to'
@@ -1,17 +1,23 @@
1
1
  import { createTable, type RowData, type TableOptions, type TableOptionsResolved } from '@tanstack/table-core';
2
2
  import { type ComponentType, h, type VNode } from 'preact';
3
- import { useState } from 'preact/hooks';
3
+ import { useEffect, useState } from 'preact/hooks';
4
4
 
5
- export * from '@tanstack/table-core';
5
+ import { usePageSizeContext } from './pagination-context';
6
6
 
7
- // Adapted from https://github.com/TanStack/table/blob/55ea94863b6b6e6d17bd51ecda61c6a6a1262c88/packages/preact-table/src/FlexRender.tsx
7
+ export * from '@tanstack/table-core';
8
8
 
9
9
  export type Renderable<TProps> = VNode<TProps> | ComponentType<TProps> | undefined | null | string | number | boolean;
10
10
 
11
+ /*
12
+ * Adapted from https://github.com/TanStack/table/blob/55ea94863b6b6e6d17bd51ecda61c6a6a1262c88/packages/preact-table/src/FlexRender.tsx
13
+ */
11
14
  export function flexRender<TProps extends object>(Comp: Renderable<TProps>, props: TProps) {
12
15
  return !Comp ? null : typeof Comp === 'function' ? <Comp {...props} /> : Comp;
13
16
  }
14
17
 
18
+ /*
19
+ * Taken from https://github.com/TanStack/table/blob/f7bf6f1adfa4f8b28b9968b29745f2452d4be9d8/packages/react-table/src/index.tsx
20
+ */
15
21
  export function usePreactTable<TData extends RowData>(options: TableOptions<TData>) {
16
22
  const resolvedOptions: TableOptionsResolved<TData> = {
17
23
  state: {},
@@ -39,5 +45,13 @@ export function usePreactTable<TData extends RowData>(options: TableOptions<TDat
39
45
  },
40
46
  }));
41
47
 
48
+ const { pageSize } = usePageSizeContext();
49
+ useEffect(
50
+ () => {
51
+ tableRef.current.setPageSize(pageSize);
52
+ },
53
+ [pageSize], // eslint-disable-line react-hooks/exhaustive-deps -- only run this when the pageSize changes
54
+ );
55
+
42
56
  return tableRef.current;
43
57
  }
@@ -1,5 +1,5 @@
1
1
  import { type Meta, type StoryObj } from '@storybook/preact';
2
- import { expect } from '@storybook/test';
2
+ import { expect, userEvent } from '@storybook/test';
3
3
 
4
4
  import { WastewaterMutationsOverTime, type WastewaterMutationsOverTimeProps } from './wastewater-mutations-over-time';
5
5
  import { WISE_DETAILS_ENDPOINT, WISE_LAPIS_URL } from '../../../constants';
@@ -67,6 +67,24 @@ export const Default: StoryObj<WastewaterMutationsOverTimeProps> = {
67
67
  },
68
68
  };
69
69
 
70
+ export const ChangingRowsPerPageChangesItForEveryTag: StoryObj<WastewaterMutationsOverTimeProps> = {
71
+ ...Default,
72
+ play: async ({ canvas, step }) => {
73
+ await step('Wait for component to render', async () => {
74
+ await canvas.findByText('Lugano');
75
+ });
76
+
77
+ const getRowsPerPageSelectors = async () => await canvas.findAllByLabelText('Rows per page', { exact: false });
78
+
79
+ await step('change rows per page', async () => {
80
+ await expect((await getRowsPerPageSelectors())[0]).toHaveValue('10');
81
+ await expect((await getRowsPerPageSelectors())[1]).toHaveValue('10');
82
+ await userEvent.selectOptions((await getRowsPerPageSelectors())[0], '20');
83
+ await expect((await getRowsPerPageSelectors())[1]).toHaveValue('20');
84
+ });
85
+ },
86
+ };
87
+
70
88
  export const AminoAcids: StoryObj<WastewaterMutationsOverTimeProps> = {
71
89
  ...Default,
72
90
  args: {
@@ -19,6 +19,7 @@ import Tabs from '../../components/tabs';
19
19
  import { type MutationOverTimeDataMap } from '../../mutationsOverTime/MutationOverTimeData';
20
20
  import MutationsOverTimeGrid from '../../mutationsOverTime/mutations-over-time-grid';
21
21
  import { pageSizesSchema } from '../../shared/tanstackTable/pagination';
22
+ import { PageSizeContextProvider } from '../../shared/tanstackTable/pagination-context';
22
23
  import { useQuery } from '../../useQuery';
23
24
 
24
25
  const wastewaterMutationOverTimeSchema = z.object({
@@ -161,7 +162,11 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
161
162
  />
162
163
  );
163
164
 
164
- return <Tabs tabs={tabs} toolbar={toolbar} />;
165
+ return (
166
+ <PageSizeContextProvider pageSizes={originalComponentProps.pageSizes}>
167
+ <Tabs tabs={tabs} toolbar={toolbar} />
168
+ </PageSizeContextProvider>
169
+ );
165
170
  };
166
171
 
167
172
  type ToolbarProps = {
@@ -107,7 +107,7 @@ export const MultiSegmentedReferenceGenomes: StoryObj<MutationFilterProps> = {
107
107
  ...Template,
108
108
  args: {
109
109
  ...Template.args,
110
- initialValue: ['seg1:123T', 'gene2:56', 'ins_seg2:78:AAA'],
110
+ initialValue: ['seg1:3T', 'gene2:4', 'ins_seg2:4:AAA'],
111
111
  },
112
112
  parameters: {
113
113
  fetchMock: {
@@ -163,9 +163,9 @@ export const MultiSegmentedReferenceGenomes: StoryObj<MutationFilterProps> = {
163
163
  });
164
164
 
165
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();
166
+ await expect(canvas.getByText('seg1:3T')).toBeVisible();
167
+ await expect(canvas.getByText('gene2:4')).toBeVisible();
168
+ await expect(canvas.getByText('ins_seg2:4:AAA')).toBeVisible();
169
169
  });
170
170
  },
171
171
  };
@@ -16,7 +16,7 @@ import { withinShadowRoot } from '../withinShadowRoot.story';
16
16
 
17
17
  const codeExample = String.raw`
18
18
  <gs-prevalence-over-time
19
- numeratorFilter='[{ "displayName": "EG", "lapisFilter": { "country": "USA", "pangoLineage": "EG*" }}, { "displayName": "JN.1", "lapisFilter": { "country": "USA", "pangoLineage": "JN.1*" }}]'
19
+ numeratorFilters='[{ "displayName": "EG", "lapisFilter": { "country": "USA", "pangoLineage": "EG*" }}, { "displayName": "JN.1", "lapisFilter": { "country": "USA", "pangoLineage": "JN.1*" }}]'
20
20
  denominatorFilter='{ "country": "USA"}'
21
21
  granularity="month"
22
22
  smoothingWindow="0"