@genspectrum/dashboard-components 0.8.5 → 0.9.0

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/README.md CHANGED
@@ -13,7 +13,7 @@ Usage with a bundler in HTML:
13
13
  ```html
14
14
  <body>
15
15
  <script>
16
- import '@genspectrum/dashboard-components';
16
+ import '@genspectrum/dashboard-components/components';
17
17
  import '@genspectrum/dashboard-components/style.css';
18
18
  </script>
19
19
  <gs-app lapis="https://your.lapis.url"></gs-app>
@@ -42,6 +42,13 @@ We also provide a standalone version of the components that can be used without
42
42
  Internally, the components use [Preact](https://preactjs.com/).
43
43
  We use [Lit](https://lit.dev/) to create web components.
44
44
 
45
+ We have split the package into two parts:
46
+
47
+ - The components, which are web components that can be used in the browser.
48
+ - They can be imported with `import '@genspectrum/dashboard-components/components';`
49
+ - utility functions, which can also be used in a node environment.
50
+ - They can be imported with `import '@genspectrum/dashboard-components/util';`
51
+
45
52
  We primarily provide two kinds of components:
46
53
 
47
54
  - **Visualization components** (charts, tables, etc.)
@@ -130,4 +137,9 @@ We follow this testing concept:
130
137
 
131
138
  #### Mocking
132
139
 
133
- All our tests use mock data. In general, we use `storybook-addon-fetch-mock` for all outgoing requests. This strategy cannot be used for components that use web workers, like gs-mutations-over-time. Therefore, we created custom mock workers that return mocked data. The mock workers are enabled in the package.json using Node.js [subpath imports](https://nodejs.org/api/packages.html#subpath-imports), following the guide from [storybook](https://storybook.js.org/docs/writing-stories/mocking-data-and-modules/mocking-modules). This ensures that when importing the worker in the component, the mock worker is used inside Storybook instead of the real worker.
140
+ All our tests use mock data. In general, we use `storybook-addon-fetch-mock` for all outgoing requests. This strategy
141
+ cannot be used for components that use web workers, like gs-mutations-over-time. Therefore, we created custom mock
142
+ workers that return mocked data. The mock workers are enabled in the package.json using
143
+ Node.js [subpath imports](https://nodejs.org/api/packages.html#subpath-imports), following the guide
144
+ from [storybook](https://storybook.js.org/docs/writing-stories/mocking-data-and-modules/mocking-modules). This ensures
145
+ that when importing the worker in the component, the mock worker is used inside Storybook instead of the real worker.
@@ -274,15 +274,23 @@
274
274
  "type": {
275
275
  "text": "Meta<Required<DateRangeSelectorProps>>"
276
276
  },
277
- "default": "{ title: 'Input/DateRangeSelector', component: 'gs-date-range-selector', parameters: withComponentDocs({ actions: { handles: ['gs-date-range-changed', ...previewHandles], }, fetchMock: {}, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { initialValue: { control: { type: 'select', }, options: [dateRangeOptionPresets.lastMonth.label, dateRangeOptionPresets.allTimes.label, 'CustomDateRange'], }, dateColumn: { control: { type: 'text' } }, dateRangeOptions: { control: { type: 'object', }, }, earliestDate: { control: { type: 'text', }, }, width: { control: { type: 'text', }, }, }, args: { dateRangeOptions: [ dateRangeOptionPresets.lastMonth, dateRangeOptionPresets.last3Months, dateRangeOptionPresets.allTimes, { label: 'CustomDateRange', dateFrom: '2021-01-01', dateTo: '2021-12-31' }, ], earliestDate: '1970-01-01', initialValue: dateRangeOptionPresets.lastMonth.label, dateColumn: 'aDateColumn', width: '100%', initialDateFrom: '', initialDateTo: '', }, tags: ['autodocs'], }"
277
+ "default": "{ title: 'Input/DateRangeSelector', component: 'gs-date-range-selector', parameters: withComponentDocs({ actions: { handles: ['gs-date-range-filter-changed', 'gs-date-range-option-changed', ...previewHandles], }, fetchMock: {}, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { initialValue: { control: { type: 'select', }, options: [dateRangeOptionPresets.lastMonth.label, dateRangeOptionPresets.allTimes.label, 'CustomDateRange'], }, dateColumn: { control: { type: 'text' } }, dateRangeOptions: { control: { type: 'object', }, }, earliestDate: { control: { type: 'text', }, }, width: { control: { type: 'text', }, }, }, args: { dateRangeOptions: [ dateRangeOptionPresets.lastMonth, dateRangeOptionPresets.last3Months, dateRangeOptionPresets.allTimes, customDateRange, ], earliestDate: '1970-01-01', initialValue: dateRangeOptionPresets.lastMonth.label, dateColumn: 'aDateColumn', width: '100%', initialDateFrom: '', initialDateTo: '', }, tags: ['autodocs'], }"
278
278
  },
279
279
  {
280
280
  "kind": "variable",
281
- "name": "DateRangeSelectorStory",
281
+ "name": "Default",
282
+ "type": {
283
+ "text": "StoryObj<Required<DateRangeSelectorProps>>"
284
+ },
285
+ "default": "{ render: (args) => html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-screen-lg\"> <gs-date-range-selector .dateRangeOptions=${args.dateRangeOptions} .earliestDate=${args.earliestDate} .initialValue=${args.initialValue} .initialDateFrom=${args.initialDateFrom} .initialDateTo=${args.initialDateTo} .width=${args.width} .dateColumn=${args.dateColumn} ></gs-date-range-selector> </div> </gs-app>`, }"
286
+ },
287
+ {
288
+ "kind": "variable",
289
+ "name": "FiresEvents",
282
290
  "type": {
283
291
  "text": "StoryObj<Required<DateRangeSelectorProps>>"
284
292
  },
285
- "default": "{ render: (args) => html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-screen-lg\"> <gs-date-range-selector .dateRangeOptions=${args.dateRangeOptions} .earliestDate=${args.earliestDate} .initialValue=${args.initialValue} .initialDateFrom=${args.initialDateFrom} .initialDateTo=${args.initialDateTo} .width=${args.width} .dateColumn=${args.dateColumn} ></gs-date-range-selector> </div> </gs-app>`, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-selector'); const dateTo = () => canvas.getByPlaceholderText('Date to'); await step('Expect last 6 months to be selected', async () => { await expect(canvas.getByRole('combobox')).toHaveValue('Last month'); await waitFor(() => { expect(dateTo()).toHaveValue(toYYYYMMDD(new Date())); }); }); // Due to the limitations of storybook testing which does not fire an event, // when selecting a value from the dropdown we can't test the fired event here. // An e2e test (using playwright) for that can be found in tests/dateRangeSelector.spec.ts }, }"
293
+ "default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-selector'); const filterChangedListenerMock = fn(); const optionChangedListenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-date-range-filter-changed', filterChangedListenerMock); canvasElement.addEventListener('gs-date-range-option-changed', optionChangedListenerMock); }); await step('Expect last 6 months to be selected', async () => { await expect(selectField(canvas)).toHaveValue('Last month'); await waitFor(() => { expect(dateToPicker(canvas)).toHaveValue(toYYYYMMDD(new Date())); }); }); await step('Expect event to be fired when selecting a different value', async () => { await userEvent.selectOptions(selectField(canvas), 'CustomDateRange'); await expect(dateToPicker(canvas)).toHaveValue(customDateRange.dateTo); await expect(filterChangedListenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { aDateColumnFrom: customDateRange.dateFrom, aDateColumnTo: customDateRange.dateTo, }, }), ); await expect(optionChangedListenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: customDateRange.label, }), ); }); }, }"
286
294
  }
287
295
  ],
288
296
  "exports": [
@@ -296,9 +304,17 @@
296
304
  },
297
305
  {
298
306
  "kind": "js",
299
- "name": "DateRangeSelectorStory",
307
+ "name": "Default",
300
308
  "declaration": {
301
- "name": "DateRangeSelectorStory",
309
+ "name": "Default",
310
+ "module": "src/web-components/input/gs-date-range-selector.stories.ts"
311
+ }
312
+ },
313
+ {
314
+ "kind": "js",
315
+ "name": "FiresEvents",
316
+ "declaration": {
317
+ "name": "FiresEvents",
302
318
  "module": "src/web-components/input/gs-date-range-selector.stories.ts"
303
319
  }
304
320
  }
@@ -389,8 +405,15 @@
389
405
  "type": {
390
406
  "text": "CustomEvent<{ `${dateColumn}From`: string; `${dateColumn}To`: string; }>"
391
407
  },
392
- "description": "Fired when: - The select field is changed, - A date is selected in either of the date pickers, - A date was typed into either of the date input fields, and the input field loses focus (\"on blur\"). Contains the dates in the format `YYYY-MM-DD`. Example: For `dateColumn = yourDate`, an event with `detail` containing ``` { yourDataFrom: \"2021-01-01\", yourDataTo: \"2021-12-31\" } ``` will be fired.",
393
- "name": "gs-date-range-changed"
408
+ "description": "Fired when: - The select field is changed, - A date is selected in either of the date pickers, - A date was typed into either of the date input fields, and the input field loses focus (\"on blur\"). Contains the dates in the format `YYYY-MM-DD`. Example: For `dateColumn = yourDate`, an event with `detail` containing ``` { yourDataFrom: \"2021-01-01\", yourDataTo: \"2021-12-31\" } ``` will be fired. Use this event, when you want to use the filter directly as a LAPIS filter.",
409
+ "name": "gs-date-range-filter-changed"
410
+ },
411
+ {
412
+ "type": {
413
+ "text": "CustomEvent<{ string | {dateFrom: string, dateTo: string}}>"
414
+ },
415
+ "description": "Fired when: - The select field is changed, - A date is selected in either of the date pickers, - A date was typed into either of the date input fields, and the input field loses focus (\"on blur\"). Contains the selected dateRangeOption or when users select custom values it contains the selected dates. Use this event, when you want to control this component in your JS application.",
416
+ "name": "gs-date-range-option-changed"
394
417
  }
395
418
  ],
396
419
  "attributes": [
@@ -99,53 +99,6 @@ export declare class App extends LitElement {
99
99
  createRenderRoot(): this;
100
100
  }
101
101
 
102
- /**
103
- * A date range option that can be used in the `gs-date-range-selector` component.
104
- */
105
- export declare type DateRangeOption = {
106
- /** The label of the date range option that will be shown to the user */
107
- label: string;
108
- /**
109
- * The start date of the date range in the format `YYYY-MM-DD`.
110
- * If not set, the date range selector will default to the `earliestDate` property.
111
- */
112
- dateFrom?: string;
113
- /**
114
- * The end date of the date range in the format `YYYY-MM-DD`.
115
- * If not set, the date range selector will default to the current date.
116
- */
117
- dateTo?: string;
118
- };
119
-
120
- /**
121
- * Presets for the `gs-date-range-selector` component that can be used as `dateRangeOptions`.
122
- */
123
- export declare const dateRangeOptionPresets: {
124
- last2Weeks: {
125
- label: string;
126
- dateFrom: string | undefined;
127
- };
128
- lastMonth: {
129
- label: string;
130
- dateFrom: string | undefined;
131
- };
132
- last2Months: {
133
- label: string;
134
- dateFrom: string | undefined;
135
- };
136
- last3Months: {
137
- label: string;
138
- dateFrom: string | undefined;
139
- };
140
- last6Months: {
141
- label: string;
142
- dateFrom: string | undefined;
143
- };
144
- allTimes: {
145
- label: string;
146
- };
147
- };
148
-
149
102
  /**
150
103
  * ## Context
151
104
  * This component is a group of input fields designed to specify date range filters
@@ -159,7 +112,7 @@ export declare const dateRangeOptionPresets: {
159
112
  * Setting a value in either of the date pickers will set the select field to "custom",
160
113
  * which represents an arbitrary date range.
161
114
  *
162
- * @fires {CustomEvent<{ `${dateColumn}From`: string; `${dateColumn}To`: string; }>} gs-date-range-changed
115
+ * @fires {CustomEvent<{ `${dateColumn}From`: string; `${dateColumn}To`: string; }>} gs-date-range-filter-changed
163
116
  * Fired when:
164
117
  * - The select field is changed,
165
118
  * - A date is selected in either of the date pickers,
@@ -174,6 +127,18 @@ export declare const dateRangeOptionPresets: {
174
127
  * }
175
128
  * ```
176
129
  * will be fired.
130
+ *
131
+ * Use this event, when you want to use the filter directly as a LAPIS filter.
132
+ *
133
+ *
134
+ * @fires {CustomEvent<{ string | {dateFrom: string, dateTo: string}}>} gs-date-range-option-changed
135
+ * Fired when:
136
+ * - The select field is changed,
137
+ * - A date is selected in either of the date pickers,
138
+ * - A date was typed into either of the date input fields, and the input field loses focus ("on blur").
139
+ * Contains the selected dateRangeOption or when users select custom values it contains the selected dates.
140
+ *
141
+ * Use this event, when you want to control this component in your JS application.
177
142
  */
178
143
  export declare class DateRangeSelectorComponent extends PreactLitAdapter {
179
144
  /**
@@ -1145,7 +1110,8 @@ declare global {
1145
1110
  'gs-date-range-selector': DateRangeSelectorComponent;
1146
1111
  }
1147
1112
  interface HTMLElementEventMap {
1148
- 'gs-date-range-changed': CustomEvent<Record<string, string>>;
1113
+ 'gs-date-range-filter-changed': CustomEvent<Record<string, string>>;
1114
+ 'gs-date-range-option-changed': DateRangeOptionChangedEvent;
1149
1115
  }
1150
1116
  }
1151
1117
 
@@ -10,6 +10,7 @@ import { autoUpdate, computePosition, offset, shift, flip } from "@floating-ui/d
10
10
  import { ReactiveElement } from "@lit/reactive-element";
11
11
  import { BarWithErrorBarsController, BarWithErrorBar } from "chartjs-chart-error-bars";
12
12
  import flatpickr from "flatpickr";
13
+ import { t as toYYYYMMDD, D as DateRangeOptionChangedEvent } from "./utilEntrypoint-g4DsyhU7.js";
13
14
  /**
14
15
  * @license
15
16
  * Copyright 2017 Google LLC
@@ -9189,8 +9190,8 @@ const getSelectableOptions = (dateRangeOptions) => {
9189
9190
  });
9190
9191
  };
9191
9192
  const getDatesForSelectorValue = (initialSelectedDateRange, dateRangeOptions, earliestDate) => {
9192
- const today2 = /* @__PURE__ */ new Date();
9193
- const defaultDates = { dateFrom: new Date(earliestDate), dateTo: today2 };
9193
+ const today = /* @__PURE__ */ new Date();
9194
+ const defaultDates = { dateFrom: new Date(earliestDate), dateTo: today };
9194
9195
  if (initialSelectedDateRange === void 0) {
9195
9196
  return defaultDates;
9196
9197
  }
@@ -9198,7 +9199,7 @@ const getDatesForSelectorValue = (initialSelectedDateRange, dateRangeOptions, ea
9198
9199
  if (dateRangeOption) {
9199
9200
  return {
9200
9201
  dateFrom: new Date(dateRangeOption.dateFrom ?? earliestDate),
9201
- dateTo: new Date(dateRangeOption.dateTo ?? today2)
9202
+ dateTo: new Date(dateRangeOption.dateTo ?? today)
9202
9203
  };
9203
9204
  }
9204
9205
  return defaultDates;
@@ -9247,13 +9248,6 @@ function computeInitialValues(initialValue, initialDateFrom, initialDateTo, earl
9247
9248
  function isUndefinedOrEmpty(value) {
9248
9249
  return value === void 0 || value === "";
9249
9250
  }
9250
- const toYYYYMMDD = (date) => {
9251
- if (!date) {
9252
- return void 0;
9253
- }
9254
- const options2 = { year: "numeric", month: "2-digit", day: "2-digit" };
9255
- return date.toLocaleDateString("en-CA", options2);
9256
- };
9257
9251
  const customOption = "Custom";
9258
9252
  const DateRangeSelector = ({ width, ...innerProps }) => {
9259
9253
  const size = { width, height: "3rem" };
@@ -9323,36 +9317,53 @@ const DateRangeSelectorInner = ({
9323
9317
  dateFrom: dateRange.dateFrom,
9324
9318
  dateTo: dateRange.dateTo
9325
9319
  });
9326
- submit();
9320
+ fireFilterChangedEvent();
9321
+ fireOptionChangedEvent(value);
9327
9322
  };
9328
9323
  const onChangeDateFrom = () => {
9329
9324
  if (selectedDates.dateFrom.toDateString() === (dateFromPicker == null ? void 0 : dateFromPicker.selectedDates[0].toDateString())) {
9330
9325
  return;
9331
9326
  }
9332
- selectedDates.dateFrom = (dateFromPicker == null ? void 0 : dateFromPicker.selectedDates[0]) || /* @__PURE__ */ new Date();
9333
- dateToPicker == null ? void 0 : dateToPicker.set("minDate", dateFromPicker == null ? void 0 : dateFromPicker.selectedDates[0]);
9327
+ const dateTo = dateToPicker == null ? void 0 : dateToPicker.selectedDates[0];
9328
+ const dateFrom = dateFromPicker == null ? void 0 : dateFromPicker.selectedDates[0];
9329
+ selectedDates.dateFrom = dateFrom || /* @__PURE__ */ new Date();
9330
+ dateToPicker == null ? void 0 : dateToPicker.set("minDate", dateFrom);
9334
9331
  setSelectedDateRange(customOption);
9335
- submit();
9332
+ fireFilterChangedEvent();
9333
+ fireOptionChangedEvent({
9334
+ dateFrom: dateFrom !== void 0 ? toYYYYMMDD(dateFrom) : earliestDate,
9335
+ dateTo: toYYYYMMDD(dateTo || /* @__PURE__ */ new Date())
9336
+ });
9336
9337
  };
9337
9338
  const onChangeDateTo = () => {
9338
9339
  if (selectedDates.dateTo.toDateString() === (dateToPicker == null ? void 0 : dateToPicker.selectedDates[0].toDateString())) {
9339
9340
  return;
9340
9341
  }
9341
- selectedDates.dateTo = (dateToPicker == null ? void 0 : dateToPicker.selectedDates[0]) || /* @__PURE__ */ new Date();
9342
- dateFromPicker == null ? void 0 : dateFromPicker.set("maxDate", dateToPicker == null ? void 0 : dateToPicker.selectedDates[0]);
9342
+ const dateTo = dateToPicker == null ? void 0 : dateToPicker.selectedDates[0];
9343
+ const dateFrom = dateFromPicker == null ? void 0 : dateFromPicker.selectedDates[0];
9344
+ selectedDates.dateTo = dateTo || /* @__PURE__ */ new Date();
9345
+ dateFromPicker == null ? void 0 : dateFromPicker.set("maxDate", dateTo);
9343
9346
  setSelectedDateRange(customOption);
9344
- submit();
9347
+ fireFilterChangedEvent();
9348
+ fireOptionChangedEvent({
9349
+ dateFrom: dateFrom !== void 0 ? toYYYYMMDD(dateFrom) : earliestDate,
9350
+ dateTo: toYYYYMMDD(dateTo || /* @__PURE__ */ new Date())
9351
+ });
9345
9352
  };
9346
- const submit = () => {
9353
+ const fireOptionChangedEvent = (option) => {
9347
9354
  var _a;
9348
- const dateFrom = toYYYYMMDD(dateFromPicker == null ? void 0 : dateFromPicker.selectedDates[0]);
9349
- const dateTo = toYYYYMMDD(dateToPicker == null ? void 0 : dateToPicker.selectedDates[0]);
9355
+ (_a = divRef.current) == null ? void 0 : _a.dispatchEvent(new DateRangeOptionChangedEvent(option));
9356
+ };
9357
+ const fireFilterChangedEvent = () => {
9358
+ var _a;
9359
+ const dateFrom = dateFromPicker == null ? void 0 : dateFromPicker.selectedDates[0];
9360
+ const dateTo = dateToPicker == null ? void 0 : dateToPicker.selectedDates[0];
9350
9361
  const detail = {
9351
- ...dateFrom !== void 0 && { [`${dateColumn}From`]: dateFrom },
9352
- ...dateTo !== void 0 && { [`${dateColumn}To`]: dateTo }
9362
+ ...dateFrom !== void 0 && { [`${dateColumn}From`]: toYYYYMMDD(dateFrom) },
9363
+ ...dateTo !== void 0 && { [`${dateColumn}To`]: toYYYYMMDD(dateTo) }
9353
9364
  };
9354
9365
  (_a = divRef.current) == null ? void 0 : _a.dispatchEvent(
9355
- new CustomEvent("gs-date-range-changed", {
9366
+ new CustomEvent("gs-date-range-filter-changed", {
9356
9367
  detail,
9357
9368
  bubbles: true,
9358
9369
  composed: true
@@ -10372,42 +10383,6 @@ __decorateClass([
10372
10383
  LineageFilterComponent = __decorateClass([
10373
10384
  t$2("gs-lineage-filter")
10374
10385
  ], LineageFilterComponent);
10375
- const today = /* @__PURE__ */ new Date();
10376
- const twoWeeksAgo = /* @__PURE__ */ new Date();
10377
- twoWeeksAgo.setDate(today.getDate() - 14);
10378
- const lastMonth = new Date(today);
10379
- lastMonth.setMonth(today.getMonth() - 1);
10380
- const last2Months = new Date(today);
10381
- last2Months.setMonth(today.getMonth() - 2);
10382
- const last3Months = new Date(today);
10383
- last3Months.setMonth(today.getMonth() - 3);
10384
- const last6Months = new Date(today);
10385
- last6Months.setMonth(today.getMonth() - 6);
10386
- const dateRangeOptionPresets = {
10387
- last2Weeks: {
10388
- label: "Last 2 weeks",
10389
- dateFrom: toYYYYMMDD(twoWeeksAgo)
10390
- },
10391
- lastMonth: {
10392
- label: "Last month",
10393
- dateFrom: toYYYYMMDD(lastMonth)
10394
- },
10395
- last2Months: {
10396
- label: "Last 2 months",
10397
- dateFrom: toYYYYMMDD(last2Months)
10398
- },
10399
- last3Months: {
10400
- label: "Last 3 months",
10401
- dateFrom: toYYYYMMDD(last3Months)
10402
- },
10403
- last6Months: {
10404
- label: "Last 6 months",
10405
- dateFrom: toYYYYMMDD(last6Months)
10406
- },
10407
- allTimes: {
10408
- label: "All times"
10409
- }
10410
- };
10411
10386
  export {
10412
10387
  AggregateComponent,
10413
10388
  App,
@@ -10422,7 +10397,6 @@ export {
10422
10397
  PrevalenceOverTimeComponent,
10423
10398
  RelativeGrowthAdvantageComponent,
10424
10399
  TextInputComponent,
10425
- UserFacingError,
10426
- dateRangeOptionPresets
10400
+ UserFacingError
10427
10401
  };
10428
- //# sourceMappingURL=dashboard-components.js.map
10402
+ //# sourceMappingURL=components.js.map