@genspectrum/dashboard-components 1.9.1 → 1.9.2

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/dist/util.d.ts CHANGED
@@ -939,22 +939,6 @@ declare global {
939
939
  }
940
940
 
941
941
 
942
- declare global {
943
- interface HTMLElementTagNameMap {
944
- 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
945
- }
946
- }
947
-
948
-
949
- declare global {
950
- namespace JSX {
951
- interface IntrinsicElements {
952
- 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
953
- }
954
- }
955
- }
956
-
957
-
958
942
  declare global {
959
943
  interface HTMLElementTagNameMap {
960
944
  'gs-genome-data-viewer': GenomeDataViewerComponent;
@@ -1117,10 +1101,7 @@ declare global {
1117
1101
 
1118
1102
  declare global {
1119
1103
  interface HTMLElementTagNameMap {
1120
- 'gs-location-filter': LocationFilterComponent;
1121
- }
1122
- interface HTMLElementEventMap {
1123
- [gsEventNames.locationChanged]: LocationChangedEvent;
1104
+ 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
1124
1105
  }
1125
1106
  }
1126
1107
 
@@ -1128,7 +1109,7 @@ declare global {
1128
1109
  declare global {
1129
1110
  namespace JSX {
1130
1111
  interface IntrinsicElements {
1131
- 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1112
+ 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1132
1113
  }
1133
1114
  }
1134
1115
  }
@@ -1154,6 +1135,25 @@ declare global {
1154
1135
  }
1155
1136
 
1156
1137
 
1138
+ declare global {
1139
+ interface HTMLElementTagNameMap {
1140
+ 'gs-location-filter': LocationFilterComponent;
1141
+ }
1142
+ interface HTMLElementEventMap {
1143
+ [gsEventNames.locationChanged]: LocationChangedEvent;
1144
+ }
1145
+ }
1146
+
1147
+
1148
+ declare global {
1149
+ namespace JSX {
1150
+ interface IntrinsicElements {
1151
+ 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1152
+ }
1153
+ }
1154
+ }
1155
+
1156
+
1157
1157
  declare global {
1158
1158
  interface HTMLElementTagNameMap {
1159
1159
  'gs-text-filter': TextFilterComponent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "1.9.1",
3
+ "version": "1.9.2",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -7,6 +7,7 @@ import {
7
7
  MutationsOverTimeMutationsFilter,
8
8
  type MutationsOverTimeMutationsFilterProps,
9
9
  } from './mutations-over-time-mutations-filter';
10
+ import { type MutationAnnotations } from '../../web-components/mutation-annotations-context';
10
11
  import { MutationAnnotationsContextProvider } from '../MutationAnnotationsContext';
11
12
  import { type MutationFilter } from '../mutationsOverTime/getFilteredMutationsOverTimeData';
12
13
 
@@ -18,30 +19,38 @@ const meta: Meta = {
18
19
 
19
20
  export default meta;
20
21
 
22
+ const manyMutationAnnotations = Array.from({ length: 300 }, (_, i) => ({
23
+ name: `Annotation ${i + 1}`,
24
+ description: `This is test annotation number ${i + 1} for testing many annotations.`,
25
+ symbol: String.fromCharCode(33 + (i % 94)), // Cycle through printable ASCII characters
26
+ nucleotideMutations: ['A23G'],
27
+ aminoAcidMutations: [],
28
+ })) satisfies MutationAnnotations;
29
+
21
30
  const WrapperWithState = ({
22
31
  setFilterValue,
23
32
  value,
33
+ annotations = [
34
+ {
35
+ name: 'Test Annotation 1',
36
+ description: 'Test Annotation 1',
37
+ symbol: '#',
38
+ },
39
+ {
40
+ name: 'Test Annotation 2',
41
+ description: 'Test Annotation 2',
42
+ symbol: '+',
43
+ },
44
+ ],
24
45
  }: {
25
46
  setFilterValue: Dispatch<StateUpdater<MutationFilter>>;
26
47
  value: MutationFilter;
48
+ annotations?: MutationAnnotations;
27
49
  }) => {
28
50
  const [state, setState] = useState(value);
29
51
 
30
52
  return (
31
- <MutationAnnotationsContextProvider
32
- value={[
33
- {
34
- name: 'Test Annotation 1',
35
- description: 'Test Annotation 1',
36
- symbol: '#',
37
- },
38
- {
39
- name: 'Test Annotation 2',
40
- description: 'Test Annotation 2',
41
- symbol: '+',
42
- },
43
- ]}
44
- >
53
+ <MutationAnnotationsContextProvider value={annotations}>
45
54
  <MutationsOverTimeMutationsFilter
46
55
  setFilterValue={(value) => {
47
56
  setFilterValue(value);
@@ -107,3 +116,53 @@ export const FilterByAnnotation: StoryObj<MutationsOverTimeMutationsFilterProps>
107
116
  });
108
117
  },
109
118
  };
119
+
120
+ export const WithManyMutationAnnotations: StoryObj<MutationsOverTimeMutationsFilterProps> = {
121
+ render: (args) => {
122
+ return (
123
+ <WrapperWithState
124
+ setFilterValue={args.setFilterValue}
125
+ value={args.value}
126
+ annotations={manyMutationAnnotations}
127
+ />
128
+ );
129
+ },
130
+ args: {
131
+ setFilterValue: fn(),
132
+ value: { textFilter: '', annotationNameFilter: new Set() },
133
+ },
134
+ play: async ({ canvasElement, step }) => {
135
+ const canvas = within(canvasElement);
136
+
137
+ await step('Open filter dropdown', async () => {
138
+ const filterButton = canvas.getByRole('button', { name: 'Filter mutations' });
139
+ await userEvent.click(filterButton);
140
+ });
141
+
142
+ await step('Verify scroll container is scrollable', () => {
143
+ const scrollContainer = canvas
144
+ .getByText('Filter by annotations')
145
+ .parentElement!.querySelector('.overflow-scroll')!;
146
+ void expect(scrollContainer).toBeInTheDocument();
147
+
148
+ // Verify the container has scrollable content
149
+ void expect(scrollContainer.scrollHeight).toBeGreaterThan(scrollContainer.clientHeight);
150
+ });
151
+
152
+ await step('Scroll to bottom and verify we can scroll', async () => {
153
+ const scrollContainer = canvas
154
+ .getByText('Filter by annotations')
155
+ .parentElement!.querySelector('.overflow-scroll')!;
156
+
157
+ const initialScrollTop = scrollContainer.scrollTop;
158
+
159
+ // Scroll to the bottom
160
+ scrollContainer.scrollTop = scrollContainer.scrollHeight;
161
+
162
+ await waitFor(async () => {
163
+ // Verify that scrollTop actually changed
164
+ await expect(scrollContainer.scrollTop).toBeGreaterThan(initialScrollTop);
165
+ });
166
+ });
167
+ },
168
+ };
@@ -105,34 +105,36 @@ const AnnotationCheckboxes: FunctionComponent<MutationsOverTimeMutationsFilterPr
105
105
  <div className='divider mt-0.5 mb-0' />
106
106
  <div className='text-sm'>
107
107
  <div className='font-bold mb-1'>Filter by annotations</div>
108
- {mutationAnnotations.map((annotation, index) => (
109
- <li className='flex flex-row items-center' key={annotation.name}>
110
- <label>
111
- <input
112
- className={'mr-2'}
113
- type='checkbox'
114
- id={`item-${index}`}
115
- checked={value.annotationNameFilter.has(annotation.name)}
116
- onChange={() => {
117
- setFilterValue((previousFilter) => {
118
- const newAnnotationFilter = previousFilter.annotationNameFilter.has(
119
- annotation.name,
120
- )
121
- ? [...previousFilter.annotationNameFilter].filter(
122
- (name) => name !== annotation.name,
123
- )
124
- : [...previousFilter.annotationNameFilter, annotation.name];
125
- return {
126
- ...previousFilter,
127
- annotationNameFilter: new Set(newAnnotationFilter),
128
- };
129
- });
130
- }}
131
- />
132
- {annotation.name} (<span className='text-red-600'>{annotation.symbol}</span>)
133
- </label>
134
- </li>
135
- ))}
108
+ <div className='max-h-72 overflow-scroll'>
109
+ {mutationAnnotations.map((annotation, index) => (
110
+ <li className='flex flex-row items-center' key={annotation.name}>
111
+ <label>
112
+ <input
113
+ className={'mr-2'}
114
+ type='checkbox'
115
+ id={`item-${index}`}
116
+ checked={value.annotationNameFilter.has(annotation.name)}
117
+ onChange={() => {
118
+ setFilterValue((previousFilter) => {
119
+ const newAnnotationFilter = previousFilter.annotationNameFilter.has(
120
+ annotation.name,
121
+ )
122
+ ? [...previousFilter.annotationNameFilter].filter(
123
+ (name) => name !== annotation.name,
124
+ )
125
+ : [...previousFilter.annotationNameFilter, annotation.name];
126
+ return {
127
+ ...previousFilter,
128
+ annotationNameFilter: new Set(newAnnotationFilter),
129
+ };
130
+ });
131
+ }}
132
+ />
133
+ {annotation.name} (<span className='text-red-600'>{annotation.symbol}</span>)
134
+ </label>
135
+ </li>
136
+ ))}
137
+ </div>
136
138
  </div>
137
139
  </>
138
140
  );