@genspectrum/dashboard-components 0.3.2 → 0.4.1
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 +45 -26
- package/dist/dashboard-components.js +518 -434
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +21 -12
- package/dist/style.css +150 -229
- package/package.json +3 -1
- package/src/preact/aggregatedData/aggregate.tsx +1 -1
- package/src/preact/components/SegmentSelector.tsx +0 -1
- package/src/preact/components/checkbox-selector.tsx +7 -9
- package/src/preact/components/dropdown.tsx +40 -0
- package/src/preact/components/info.stories.tsx +8 -8
- package/src/preact/components/info.tsx +38 -19
- package/src/preact/components/mutation-type-selector.tsx +0 -1
- package/src/preact/components/proportion-selector-dropdown.tsx +9 -18
- package/src/preact/components/tabs.tsx +12 -3
- package/src/preact/dateRangeSelector/computeInitialValues.spec.ts +99 -0
- package/src/preact/dateRangeSelector/computeInitialValues.ts +73 -0
- package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +93 -4
- package/src/preact/dateRangeSelector/date-range-selector.tsx +49 -106
- package/src/preact/dateRangeSelector/selectableOptions.ts +79 -0
- package/src/preact/locationFilter/location-filter.tsx +1 -1
- package/src/preact/mutationComparison/mutation-comparison.tsx +3 -3
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +3 -6
- package/src/preact/mutationFilter/mutation-filter.tsx +48 -54
- package/src/preact/mutations/mutations.tsx +3 -4
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +3 -5
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +3 -3
- package/src/preact/shared/floating-ui/hooks.ts +83 -0
- package/src/web-components/input/gs-date-range-selector.stories.ts +11 -5
- package/src/web-components/input/gs-date-range-selector.tsx +22 -5
- package/src/web-components/input/gs-location-filter.stories.ts +6 -7
- package/src/web-components/input/gs-location-filter.tsx +3 -2
- package/src/web-components/input/gs-mutation-filter.stories.ts +1 -8
- package/src/web-components/input/gs-mutation-filter.tsx +1 -9
- package/src/web-components/visualization/gs-prevalence-over-time.tsx +1 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { autoUpdate, computePosition, type Middleware } from '@floating-ui/dom';
|
|
2
|
+
import type { Placement } from '@floating-ui/utils';
|
|
3
|
+
import { useEffect, useRef } from 'preact/hooks';
|
|
4
|
+
import type { MutableRefObject } from 'react';
|
|
5
|
+
|
|
6
|
+
export function useFloatingUi(
|
|
7
|
+
referenceRef: MutableRefObject<HTMLElement | null>,
|
|
8
|
+
floatingRef: MutableRefObject<HTMLElement | null>,
|
|
9
|
+
middleware?: Array<Middleware | null | undefined | false>,
|
|
10
|
+
placement?: Placement,
|
|
11
|
+
) {
|
|
12
|
+
const cleanupRef = useRef<Function | null>(null);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!referenceRef.current || !floatingRef.current) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { current: reference } = referenceRef;
|
|
20
|
+
const { current: floating } = floatingRef;
|
|
21
|
+
|
|
22
|
+
const update = () => {
|
|
23
|
+
computePosition(reference, floating, {
|
|
24
|
+
placement,
|
|
25
|
+
middleware,
|
|
26
|
+
}).then(({ x, y }) => {
|
|
27
|
+
floating.style.left = `${x}px`;
|
|
28
|
+
floating.style.top = `${y}px`;
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
update();
|
|
33
|
+
cleanupRef.current = autoUpdate(reference, floating, update);
|
|
34
|
+
|
|
35
|
+
return () => {
|
|
36
|
+
if (cleanupRef.current) {
|
|
37
|
+
cleanupRef.current();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}, [placement, middleware, referenceRef, floatingRef]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function useCloseOnClickOutside(
|
|
44
|
+
floatingRef: MutableRefObject<HTMLElement | null>,
|
|
45
|
+
referenceRef: MutableRefObject<HTMLElement | null>,
|
|
46
|
+
setShowContent: (value: ((prevState: boolean) => boolean) | boolean) => void,
|
|
47
|
+
) {
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
50
|
+
const path = event.composedPath();
|
|
51
|
+
if (
|
|
52
|
+
floatingRef.current &&
|
|
53
|
+
!path.includes(floatingRef.current) &&
|
|
54
|
+
referenceRef.current &&
|
|
55
|
+
!path.includes(referenceRef.current)
|
|
56
|
+
) {
|
|
57
|
+
setShowContent(false);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
62
|
+
|
|
63
|
+
return () => {
|
|
64
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
65
|
+
};
|
|
66
|
+
}, [floatingRef, referenceRef, setShowContent]);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function useCloseOnEsc(setShowHelp: (value: ((prevState: boolean) => boolean) | boolean) => void) {
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
72
|
+
if (event.key === 'Escape') {
|
|
73
|
+
setShowHelp(false);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
78
|
+
|
|
79
|
+
return () => {
|
|
80
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
81
|
+
};
|
|
82
|
+
}, [setShowHelp]);
|
|
83
|
+
}
|
|
@@ -5,8 +5,11 @@ import { html } from 'lit';
|
|
|
5
5
|
|
|
6
6
|
import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
|
|
7
7
|
import { LAPIS_URL } from '../../constants';
|
|
8
|
+
import { type DateRangeSelectorProps } from '../../preact/dateRangeSelector/date-range-selector';
|
|
9
|
+
import './gs-date-range-selector';
|
|
10
|
+
import '../app';
|
|
11
|
+
import { toYYYYMMDD } from '../../preact/dateRangeSelector/dateConversion';
|
|
8
12
|
import {
|
|
9
|
-
type DateRangeSelectorProps,
|
|
10
13
|
PRESET_VALUE_ALL_TIMES,
|
|
11
14
|
PRESET_VALUE_CUSTOM,
|
|
12
15
|
PRESET_VALUE_LAST_2_MONTHS,
|
|
@@ -14,10 +17,7 @@ import {
|
|
|
14
17
|
PRESET_VALUE_LAST_3_MONTHS,
|
|
15
18
|
PRESET_VALUE_LAST_6_MONTHS,
|
|
16
19
|
PRESET_VALUE_LAST_MONTH,
|
|
17
|
-
} from '../../preact/dateRangeSelector/
|
|
18
|
-
import './gs-date-range-selector';
|
|
19
|
-
import '../app';
|
|
20
|
-
import { toYYYYMMDD } from '../../preact/dateRangeSelector/dateConversion';
|
|
20
|
+
} from '../../preact/dateRangeSelector/selectableOptions';
|
|
21
21
|
import { withinShadowRoot } from '../withinShadowRoot.story';
|
|
22
22
|
|
|
23
23
|
const codeExample = String.raw`
|
|
@@ -25,6 +25,8 @@ const codeExample = String.raw`
|
|
|
25
25
|
customSelectOptions='[{ "label": "Year 2021", "dateFrom": "2021-01-01", "dateTo": "2021-12-31" }]'
|
|
26
26
|
earliestDate="1970-01-01"
|
|
27
27
|
initialValue="${PRESET_VALUE_LAST_6_MONTHS}"
|
|
28
|
+
initialDateFrom="2020-01-01"
|
|
29
|
+
initialDateTo="2021-01-01"
|
|
28
30
|
width="100%"
|
|
29
31
|
dateColumn="myDateColumn"
|
|
30
32
|
></gs-date-range-selector>`;
|
|
@@ -82,6 +84,8 @@ const meta: Meta<Required<DateRangeSelectorProps<'CustomDateRange'>>> = {
|
|
|
82
84
|
initialValue: PRESET_VALUE_LAST_6_MONTHS,
|
|
83
85
|
dateColumn: 'aDateColumn',
|
|
84
86
|
width: '100%',
|
|
87
|
+
initialDateFrom: '',
|
|
88
|
+
initialDateTo: '',
|
|
85
89
|
},
|
|
86
90
|
decorators: [withActions],
|
|
87
91
|
tags: ['autodocs'],
|
|
@@ -97,6 +101,8 @@ export const DateRangeSelectorStory: StoryObj<Required<DateRangeSelectorProps<'C
|
|
|
97
101
|
.customSelectOptions=${args.customSelectOptions}
|
|
98
102
|
.earliestDate=${args.earliestDate}
|
|
99
103
|
.initialValue=${args.initialValue}
|
|
104
|
+
.initialDateFrom=${args.initialDateFrom}
|
|
105
|
+
.initialDateTo=${args.initialDateTo}
|
|
100
106
|
.width=${args.width}
|
|
101
107
|
.dateColumn=${args.dateColumn}
|
|
102
108
|
></gs-date-range-selector>
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { customElement, property } from 'lit/decorators.js';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
DateRangeSelector,
|
|
6
|
-
type PresetOptionValues,
|
|
7
|
-
} from '../../preact/dateRangeSelector/date-range-selector';
|
|
3
|
+
import { DateRangeSelector } from '../../preact/dateRangeSelector/date-range-selector';
|
|
4
|
+
import { type CustomSelectOption, type PresetOptionValues } from '../../preact/dateRangeSelector/selectableOptions';
|
|
8
5
|
import { type Equals, type Expect } from '../../utils/typeAssertions';
|
|
9
6
|
import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
10
7
|
|
|
@@ -61,6 +58,8 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
61
58
|
* Must be a valid label from the preset labels or a `label` given in the `customSelectOptions`.
|
|
62
59
|
*
|
|
63
60
|
* If the value is invalid, the component will default to `'last6Months'`.
|
|
61
|
+
*
|
|
62
|
+
* It will be overwritten if `initialDateFrom` or `initialDateTo` is set.
|
|
64
63
|
*/
|
|
65
64
|
@property()
|
|
66
65
|
initialValue:
|
|
@@ -73,6 +72,22 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
73
72
|
| 'last6Months'
|
|
74
73
|
| string = 'last6Months';
|
|
75
74
|
|
|
75
|
+
/**
|
|
76
|
+
* A date string in the format `YYYY-MM-DD`.
|
|
77
|
+
* If set, the date range selector will be initialized with the given date (overwriting `initialValue` to `custom`).
|
|
78
|
+
* If `initialDateTo` is set, but this is unset, it will default to `earliestDate`.
|
|
79
|
+
*/
|
|
80
|
+
@property()
|
|
81
|
+
initialDateFrom: string = '';
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* A date string in the format `YYYY-MM-DD`.
|
|
85
|
+
* If set, the date range selector will be initialized with the given date (overwriting `initialValue` to `custom`).
|
|
86
|
+
* If `initialDateFrom` is set, but this is unset, it will default to the current date.
|
|
87
|
+
*/
|
|
88
|
+
@property()
|
|
89
|
+
initialDateTo: string = '';
|
|
90
|
+
|
|
76
91
|
/**
|
|
77
92
|
* The width of the component.
|
|
78
93
|
*
|
|
@@ -93,6 +108,8 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
93
108
|
customSelectOptions={this.customSelectOptions}
|
|
94
109
|
earliestDate={this.earliestDate}
|
|
95
110
|
initialValue={this.initialValue}
|
|
111
|
+
initialDateFrom={this.initialDateFrom}
|
|
112
|
+
initialDateTo={this.initialDateTo}
|
|
96
113
|
dateColumn={this.dateColumn}
|
|
97
114
|
width={this.width}
|
|
98
115
|
/>
|
|
@@ -190,13 +190,12 @@ export const FiresEvent: StoryObj<LocationFilterProps> = {
|
|
|
190
190
|
|
|
191
191
|
await step('Select Asia', async () => {
|
|
192
192
|
await userEvent.type(inputField(), 'Asia');
|
|
193
|
-
await expect(listenerMock).
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
);
|
|
193
|
+
await expect(listenerMock.mock.calls.at(-1)[0].detail).toStrictEqual({
|
|
194
|
+
region: 'Asia',
|
|
195
|
+
country: undefined,
|
|
196
|
+
division: undefined,
|
|
197
|
+
location: undefined,
|
|
198
|
+
});
|
|
200
199
|
});
|
|
201
200
|
|
|
202
201
|
await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => {
|
|
@@ -20,13 +20,14 @@ import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
|
20
20
|
* @fires {CustomEvent<Record<string, string>>} gs-location-changed
|
|
21
21
|
* Fired when a value from the datalist is selected or when a valid value is typed into the field.
|
|
22
22
|
* The `details` of this event contain an object with all `fields` as keys
|
|
23
|
-
* and the corresponding values as values, if they are
|
|
23
|
+
* and the corresponding values as values, even if they are `undefined`.
|
|
24
24
|
* Example:
|
|
25
25
|
* ```
|
|
26
26
|
* {
|
|
27
27
|
* continent: "Asia",
|
|
28
28
|
* country: "China",
|
|
29
|
-
* city: "Beijing"
|
|
29
|
+
* city: "Beijing",
|
|
30
|
+
* district: undefined,
|
|
30
31
|
* }
|
|
31
32
|
* ```
|
|
32
33
|
*/
|
|
@@ -14,7 +14,6 @@ const codeExample = String.raw`
|
|
|
14
14
|
<gs-mutation-filter
|
|
15
15
|
initialValue='["A123T"]'
|
|
16
16
|
width='100%'
|
|
17
|
-
height='6.5rem'
|
|
18
17
|
></gs-mutation-filter>`;
|
|
19
18
|
|
|
20
19
|
const meta: Meta<MutationFilterProps> = {
|
|
@@ -38,7 +37,6 @@ const meta: Meta<MutationFilterProps> = {
|
|
|
38
37
|
},
|
|
39
38
|
},
|
|
40
39
|
width: { control: 'text' },
|
|
41
|
-
height: { control: 'text' },
|
|
42
40
|
},
|
|
43
41
|
decorators: [withActions],
|
|
44
42
|
tags: ['autodocs'],
|
|
@@ -50,18 +48,13 @@ const Template: StoryObj<MutationFilterProps> = {
|
|
|
50
48
|
render: (args) => {
|
|
51
49
|
return html` <gs-app lapis="${LAPIS_URL}">
|
|
52
50
|
<div class="max-w-screen-lg">
|
|
53
|
-
<gs-mutation-filter
|
|
54
|
-
.initialValue=${args.initialValue}
|
|
55
|
-
.width=${args.width}
|
|
56
|
-
.height=${args.height}
|
|
57
|
-
></gs-mutation-filter>
|
|
51
|
+
<gs-mutation-filter .initialValue=${args.initialValue} .width=${args.width}></gs-mutation-filter>
|
|
58
52
|
</div>
|
|
59
53
|
</gs-app>`;
|
|
60
54
|
},
|
|
61
55
|
args: {
|
|
62
56
|
initialValue: [],
|
|
63
57
|
width: '100%',
|
|
64
|
-
height: '3rem',
|
|
65
58
|
},
|
|
66
59
|
};
|
|
67
60
|
|
|
@@ -91,18 +91,10 @@ export class MutationFilterComponent extends PreactLitAdapter {
|
|
|
91
91
|
@property({ type: String })
|
|
92
92
|
width: string = '100%';
|
|
93
93
|
|
|
94
|
-
/**
|
|
95
|
-
* The height of the component.
|
|
96
|
-
*
|
|
97
|
-
* Visit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.
|
|
98
|
-
*/
|
|
99
|
-
@property({ type: String })
|
|
100
|
-
height: string = '6.5rem';
|
|
101
|
-
|
|
102
94
|
override render() {
|
|
103
95
|
return (
|
|
104
96
|
<ReferenceGenomesAwaiter>
|
|
105
|
-
<MutationFilter initialValue={this.initialValue} width={this.width}
|
|
97
|
+
<MutationFilter initialValue={this.initialValue} width={this.width} />
|
|
106
98
|
</ReferenceGenomesAwaiter>
|
|
107
99
|
);
|
|
108
100
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { customElement, property } from 'lit/decorators.js';
|
|
2
2
|
|
|
3
|
-
import PrevalenceOverTime,
|
|
3
|
+
import { PrevalenceOverTime, type PrevalenceOverTimeProps } from '../../preact/prevalenceOverTime/prevalence-over-time';
|
|
4
4
|
import { type Equals, type Expect } from '../../utils/typeAssertions';
|
|
5
5
|
import { PreactLitAdapterWithGridJsStyles } from '../PreactLitAdapterWithGridJsStyles';
|
|
6
6
|
|