@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.
Files changed (35) hide show
  1. package/custom-elements.json +45 -26
  2. package/dist/dashboard-components.js +518 -434
  3. package/dist/dashboard-components.js.map +1 -1
  4. package/dist/genspectrum-components.d.ts +21 -12
  5. package/dist/style.css +150 -229
  6. package/package.json +3 -1
  7. package/src/preact/aggregatedData/aggregate.tsx +1 -1
  8. package/src/preact/components/SegmentSelector.tsx +0 -1
  9. package/src/preact/components/checkbox-selector.tsx +7 -9
  10. package/src/preact/components/dropdown.tsx +40 -0
  11. package/src/preact/components/info.stories.tsx +8 -8
  12. package/src/preact/components/info.tsx +38 -19
  13. package/src/preact/components/mutation-type-selector.tsx +0 -1
  14. package/src/preact/components/proportion-selector-dropdown.tsx +9 -18
  15. package/src/preact/components/tabs.tsx +12 -3
  16. package/src/preact/dateRangeSelector/computeInitialValues.spec.ts +99 -0
  17. package/src/preact/dateRangeSelector/computeInitialValues.ts +73 -0
  18. package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +93 -4
  19. package/src/preact/dateRangeSelector/date-range-selector.tsx +49 -106
  20. package/src/preact/dateRangeSelector/selectableOptions.ts +79 -0
  21. package/src/preact/locationFilter/location-filter.tsx +1 -1
  22. package/src/preact/mutationComparison/mutation-comparison.tsx +3 -3
  23. package/src/preact/mutationFilter/mutation-filter.stories.tsx +3 -6
  24. package/src/preact/mutationFilter/mutation-filter.tsx +48 -54
  25. package/src/preact/mutations/mutations.tsx +3 -4
  26. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +3 -5
  27. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +3 -3
  28. package/src/preact/shared/floating-ui/hooks.ts +83 -0
  29. package/src/web-components/input/gs-date-range-selector.stories.ts +11 -5
  30. package/src/web-components/input/gs-date-range-selector.tsx +22 -5
  31. package/src/web-components/input/gs-location-filter.stories.ts +6 -7
  32. package/src/web-components/input/gs-location-filter.tsx +3 -2
  33. package/src/web-components/input/gs-mutation-filter.stories.ts +1 -8
  34. package/src/web-components/input/gs-mutation-filter.tsx +1 -9
  35. 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/date-range-selector';
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
- type CustomSelectOption,
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).toHaveBeenCalledWith(
194
- expect.objectContaining({
195
- detail: {
196
- region: 'Asia',
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 not `undefined`.
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} height={this.height} />
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, { type PrevalenceOverTimeProps } from '../../preact/prevalenceOverTime/prevalence-over-time';
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