@genspectrum/dashboard-components 0.3.0 → 0.3.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.
@@ -171,7 +171,7 @@ declare type LapisFilter = Record<string, string | number | null | boolean>;
171
171
  * Values for the fields `i > K` are considered `undefined`.
172
172
  *
173
173
  * @fires {CustomEvent<Record<string, string>>} gs-location-changed
174
- * Fired when the field is submitted with a valid location value.
174
+ * Fired when a value from the datalist is selected or when a valid value is typed into the field.
175
175
  * The `details` of this event contain an object with all `fields` as keys
176
176
  * and the corresponding values as values, if they are not `undefined`.
177
177
  * Example:
@@ -732,20 +732,20 @@ declare global {
732
732
 
733
733
  declare global {
734
734
  interface HTMLElementTagNameMap {
735
- 'gs-date-range-selector': DateRangeSelectorComponent;
735
+ 'gs-location-filter': LocationFilterComponent;
736
736
  }
737
737
  interface HTMLElementEventMap {
738
- 'gs-date-range-changed': CustomEvent<Record<string, string>>;
738
+ 'gs-location-changed': CustomEvent<Record<string, string>>;
739
739
  }
740
740
  }
741
741
 
742
742
 
743
743
  declare global {
744
744
  interface HTMLElementTagNameMap {
745
- 'gs-location-filter': LocationFilterComponent;
745
+ 'gs-date-range-selector': DateRangeSelectorComponent;
746
746
  }
747
747
  interface HTMLElementEventMap {
748
- 'gs-location-changed': CustomEvent<Record<string, string>>;
748
+ 'gs-date-range-changed': CustomEvent<Record<string, string>>;
749
749
  }
750
750
  }
751
751
 
package/dist/style.css CHANGED
@@ -1246,19 +1246,6 @@ html {
1246
1246
  }
1247
1247
  }
1248
1248
 
1249
- .btn-outline.btn-primary:hover {
1250
- --tw-text-opacity: 1;
1251
- color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));
1252
- }
1253
-
1254
- @supports (color: color-mix(in oklab, black, black)) {
1255
-
1256
- .btn-outline.btn-primary:hover {
1257
- background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black);
1258
- border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black);
1259
- }
1260
- }
1261
-
1262
1249
  .btn-disabled:hover,
1263
1250
  .btn[disabled]:hover,
1264
1251
  .btn:disabled:hover {
@@ -1728,34 +1715,12 @@ input.tab:checked + .tab-content,
1728
1715
  background-color: var(--btn-color, var(--fallback-b2));
1729
1716
  border-color: var(--btn-color, var(--fallback-b2));
1730
1717
  }
1731
-
1732
- .btn-primary {
1733
- --btn-color: var(--fallback-p);
1734
- }
1735
- }
1736
- @supports (color: color-mix(in oklab, black, black)) {
1737
-
1738
- .btn-outline.btn-primary.btn-active {
1739
- background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black);
1740
- border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black);
1741
- }
1742
1718
  }
1743
1719
  .btn:focus-visible {
1744
1720
  outline-style: solid;
1745
1721
  outline-width: 2px;
1746
1722
  outline-offset: 2px;
1747
1723
  }
1748
- .btn-primary {
1749
- --tw-text-opacity: 1;
1750
- color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));
1751
- outline-color: var(--fallback-p,oklch(var(--p)/1));
1752
- }
1753
- @supports (color: oklch(0% 0 0)) {
1754
-
1755
- .btn-primary {
1756
- --btn-color: var(--p);
1757
- }
1758
- }
1759
1724
  .btn.glass {
1760
1725
  --tw-shadow: 0 0 #0000;
1761
1726
  --tw-shadow-colored: 0 0 #0000;
@@ -1780,14 +1745,6 @@ input.tab:checked + .tab-content,
1780
1745
  border-color: transparent;
1781
1746
  background-color: var(--fallback-bc,oklch(var(--bc)/0.2));
1782
1747
  }
1783
- .btn-outline.btn-primary {
1784
- --tw-text-opacity: 1;
1785
- color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)));
1786
- }
1787
- .btn-outline.btn-primary.btn-active {
1788
- --tw-text-opacity: 1;
1789
- color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));
1790
- }
1791
1748
  .btn.btn-disabled,
1792
1749
  .btn[disabled],
1793
1750
  .btn:disabled {
@@ -2938,9 +2895,6 @@ input.tab:checked + .tab-content,
2938
2895
  .me-1 {
2939
2896
  margin-inline-end: 0.25rem;
2940
2897
  }
2941
- .ml-1 {
2942
- margin-left: 0.25rem;
2943
- }
2944
2898
  .ml-2 {
2945
2899
  margin-left: 0.5rem;
2946
2900
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -34,7 +34,7 @@ const meta: Meta<LocationFilterProps> = {
34
34
  args: {
35
35
  width: '100%',
36
36
  fields: ['region', 'country', 'division', 'location'],
37
- initialValue: 'United States',
37
+ initialValue: 'Europe',
38
38
  },
39
39
  argTypes: {
40
40
  fields: {
@@ -1,5 +1,6 @@
1
1
  import { type FunctionComponent } from 'preact';
2
2
  import { useContext, useRef, useState } from 'preact/hooks';
3
+ import { type JSXInternal } from 'preact/src/jsx';
3
4
 
4
5
  import { fetchAutocompletionList } from './fetchAutocompletionList';
5
6
  import { LapisUrlContext } from '../LapisUrlContext';
@@ -36,7 +37,7 @@ export const LocationFilterInner = ({ initialValue, fields }: LocationFilterInne
36
37
  const [value, setValue] = useState(initialValue ?? '');
37
38
  const [unknownLocation, setUnknownLocation] = useState(false);
38
39
 
39
- const formRef = useRef<HTMLFormElement>(null);
40
+ const divRef = useRef<HTMLDivElement>(null);
40
41
 
41
42
  const { data, error, isLoading } = useQuery(() => fetchAutocompletionList(fields, lapis), [fields, lapis]);
42
43
 
@@ -47,40 +48,29 @@ export const LocationFilterInner = ({ initialValue, fields }: LocationFilterInne
47
48
  return <ErrorDisplay error={error} />;
48
49
  }
49
50
 
50
- const onInput = (event: InputEvent) => {
51
- if (event.target instanceof HTMLInputElement) {
52
- const inputValue = event.target.value;
53
- setValue(inputValue);
54
- if (unknownLocation) {
55
- const eventDetail = parseLocation(inputValue, fields);
56
- if (hasMatchingEntry(data, eventDetail)) {
57
- setUnknownLocation(false);
58
- }
59
- }
51
+ const onInput = (event: JSXInternal.TargetedInputEvent<HTMLInputElement>) => {
52
+ const inputValue = event.currentTarget.value;
53
+ setValue(inputValue);
54
+ if (inputValue.trim() === value.trim()) {
55
+ return;
60
56
  }
61
- };
62
-
63
- const submit = (event: SubmitEvent) => {
64
- event.preventDefault();
65
- const eventDetail = parseLocation(value, fields);
66
-
57
+ const eventDetail = parseLocation(inputValue, fields);
67
58
  if (hasMatchingEntry(data, eventDetail)) {
68
- setUnknownLocation(false);
69
-
70
- formRef.current?.dispatchEvent(
59
+ divRef.current?.dispatchEvent(
71
60
  new CustomEvent('gs-location-changed', {
72
61
  detail: eventDetail,
73
62
  bubbles: true,
74
63
  composed: true,
75
64
  }),
76
65
  );
66
+ setUnknownLocation(false);
77
67
  } else {
78
68
  setUnknownLocation(true);
79
69
  }
80
70
  };
81
71
 
82
72
  return (
83
- <form class='flex w-full' onSubmit={submit} ref={formRef}>
73
+ <div class='flex w-full' ref={divRef}>
84
74
  <input
85
75
  type='text'
86
76
  class={`input input-bordered grow ${unknownLocation ? 'border-2 border-error' : ''}`}
@@ -97,10 +87,7 @@ export const LocationFilterInner = ({ initialValue, fields }: LocationFilterInne
97
87
  return <option key={value} value={value} />;
98
88
  })}
99
89
  </datalist>
100
- <button class='btn btn-primary ml-1' type='submit'>
101
- Submit
102
- </button>
103
- </form>
90
+ </div>
104
91
  );
105
92
  };
106
93
 
@@ -169,7 +169,6 @@ export const FiresEvent: StoryObj<LocationFilterProps> = {
169
169
  play: async ({ canvasElement, step }) => {
170
170
  const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter');
171
171
 
172
- const submitButton = () => canvas.getByRole('button', { name: 'Submit' });
173
172
  const inputField = () => canvas.getByRole('combobox');
174
173
 
175
174
  const listenerMock = fn();
@@ -185,14 +184,12 @@ export const FiresEvent: StoryObj<LocationFilterProps> = {
185
184
 
186
185
  await step('Input invalid location', async () => {
187
186
  await userEvent.type(inputField(), 'Not / A / Location');
188
- await userEvent.click(submitButton());
189
187
  await expect(listenerMock).not.toHaveBeenCalled();
190
188
  await userEvent.type(inputField(), '{backspace>18/}');
191
189
  });
192
190
 
193
191
  await step('Select Asia', async () => {
194
192
  await userEvent.type(inputField(), 'Asia');
195
- await userEvent.click(submitButton());
196
193
  await expect(listenerMock).toHaveBeenCalledWith(
197
194
  expect.objectContaining({
198
195
  detail: {
@@ -204,7 +201,6 @@ export const FiresEvent: StoryObj<LocationFilterProps> = {
204
201
 
205
202
  await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => {
206
203
  await userEvent.type(inputField(), ' / Bangladesh / Rajshahi / Chapainawabgonj');
207
- await userEvent.click(submitButton());
208
204
  await expect(listenerMock).toHaveBeenCalledWith(
209
205
  expect.objectContaining({
210
206
  detail: {
@@ -18,7 +18,7 @@ import { PreactLitAdapter } from '../PreactLitAdapter';
18
18
  * Values for the fields `i > K` are considered `undefined`.
19
19
  *
20
20
  * @fires {CustomEvent<Record<string, string>>} gs-location-changed
21
- * Fired when the field is submitted with a valid location value.
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
23
  * and the corresponding values as values, if they are not `undefined`.
24
24
  * Example: