@genspectrum/dashboard-components 0.19.2 → 0.19.3
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 +160 -10
- package/dist/{LineageFilterChangedEvent-ixHQkq8y.js → LineageFilterChangedEvent-b0iuroUL.js} +15 -5
- package/dist/LineageFilterChangedEvent-b0iuroUL.js.map +1 -0
- package/dist/assets/mutationOverTimeWorker-ChQTFL68.js.map +1 -1
- package/dist/components.d.ts +71 -25
- package/dist/components.js +9047 -8699
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +51 -25
- package/dist/util.js +2 -1
- package/package.json +1 -1
- package/src/componentsEntrypoint.ts +3 -1
- package/src/preact/components/error-display.stories.tsx +2 -1
- package/src/preact/components/error-display.tsx +2 -3
- package/src/preact/components/resize-container.tsx +7 -10
- package/src/preact/components/tooltip.tsx +7 -4
- package/src/preact/dateRangeFilter/date-range-filter.stories.tsx +5 -4
- package/src/preact/dateRangeFilter/date-range-filter.tsx +2 -1
- package/src/preact/dateRangeFilter/dateRangeOption.ts +2 -1
- package/src/preact/genomeViewer/CDSPlot.tsx +219 -0
- package/src/preact/genomeViewer/genome-data-viewer.stories.tsx +113 -0
- package/src/preact/genomeViewer/genome-data-viewer.tsx +69 -0
- package/src/preact/genomeViewer/loadGff3.spec.ts +61 -0
- package/src/preact/genomeViewer/loadGff3.ts +174 -0
- package/src/preact/lineageFilter/LineageFilterChangedEvent.ts +3 -1
- package/src/preact/lineageFilter/lineage-filter.stories.tsx +3 -2
- package/src/preact/locationFilter/LocationChangedEvent.ts +2 -1
- package/src/preact/locationFilter/location-filter.stories.tsx +3 -2
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +3 -2
- package/src/preact/mutationFilter/mutation-filter.tsx +2 -1
- package/src/preact/shared/charts/colors.ts +1 -1
- package/src/preact/textFilter/TextFilterChangedEvent.ts +3 -1
- package/src/preact/textFilter/text-filter.stories.tsx +4 -3
- package/src/utilEntrypoint.ts +2 -0
- package/src/utils/gsEventNames.ts +9 -0
- package/src/web-components/input/gs-date-range-filter.stories.ts +4 -3
- package/src/web-components/input/gs-date-range-filter.tsx +3 -2
- package/src/web-components/input/gs-lineage-filter.stories.ts +3 -2
- package/src/web-components/input/gs-lineage-filter.tsx +2 -1
- package/src/web-components/input/gs-location-filter.stories.ts +3 -2
- package/src/web-components/input/gs-location-filter.tsx +2 -1
- package/src/web-components/input/gs-mutation-filter.stories.ts +3 -2
- package/src/web-components/input/gs-mutation-filter.tsx +2 -1
- package/src/web-components/input/gs-text-filter.stories.ts +3 -2
- package/src/web-components/input/gs-text-filter.tsx +2 -1
- package/src/web-components/visualization/gs-genome-data-viewer.spec-d.ts +18 -0
- package/src/web-components/visualization/gs-genome-data-viewer.stories.ts +108 -0
- package/src/web-components/visualization/gs-genome-data-viewer.tsx +59 -0
- package/src/web-components/visualization/index.ts +1 -0
- package/standalone-bundle/assets/mutationOverTimeWorker-jChgWnwp.js.map +1 -1
- package/standalone-bundle/dashboard-components.js +8275 -8002
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/dist/LineageFilterChangedEvent-ixHQkq8y.js.map +0 -1
package/custom-elements.json
CHANGED
|
@@ -337,7 +337,7 @@
|
|
|
337
337
|
"type": {
|
|
338
338
|
"text": "Meta<Required<DateRangeFilterProps>>"
|
|
339
339
|
},
|
|
340
|
-
"default": "{ title: 'Input/DateRangeFilter', component: 'gs-date-range-filter', parameters: withComponentDocs({ actions: { handles: [
|
|
340
|
+
"default": "{ title: 'Input/DateRangeFilter', component: 'gs-date-range-filter', parameters: withComponentDocs({ actions: { handles: [gsEventNames.dateRangeFilterChanged, gsEventNames.dateRangeOptionChanged, ...previewHandles], }, fetchMock: {}, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { value: { control: { type: 'object' }, }, lapisDateField: { control: { type: 'text' } }, dateRangeOptions: { control: { type: 'object' }, }, earliestDate: { control: { type: 'text' }, }, width: { control: { type: 'text' }, }, placeholder: { control: { type: 'text' }, }, }, args: { dateRangeOptions: [ dateRangeOptionPresets.lastMonth, dateRangeOptionPresets.last3Months, dateRangeOptionPresets.allTimes, { label: '2021', dateFrom: '2021-01-01', dateTo: '2021-12-31' }, customDateRange, ], earliestDate: '1970-01-01', value: dateRangeOptionPresets.lastMonth.label, lapisDateField: 'aDateColumn', width: '100%', placeholder: 'Date range', }, tags: ['autodocs'], }"
|
|
341
341
|
},
|
|
342
342
|
{
|
|
343
343
|
"kind": "variable",
|
|
@@ -369,7 +369,7 @@
|
|
|
369
369
|
"type": {
|
|
370
370
|
"text": "StoryObj<Required<DateRangeFilterProps>>"
|
|
371
371
|
},
|
|
372
|
-
"default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-filter'); const filterChangedListenerMock = fn(); const optionChangedListenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(
|
|
372
|
+
"default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-filter'); const filterChangedListenerMock = fn(); const optionChangedListenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(gsEventNames.dateRangeFilterChanged, filterChangedListenerMock); canvasElement.addEventListener(gsEventNames.dateRangeOptionChanged, optionChangedListenerMock); }); await step('Expect last 6 months to be selected', async () => { await waitFor(async () => { const placeholderOption = canvas.getByRole('combobox').querySelector('option:checked'); await expect(placeholderOption).toHaveTextContent('Last month'); }); await waitFor(async () => { await 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 userEvent.click(canvas.getByText('CustomDateRange')); await waitFor(async () => { 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, }), ); }); }); }, }"
|
|
373
373
|
}
|
|
374
374
|
],
|
|
375
375
|
"exports": [
|
|
@@ -594,7 +594,7 @@
|
|
|
594
594
|
"type": {
|
|
595
595
|
"text": "Meta<Required<LineageFilterProps>>"
|
|
596
596
|
},
|
|
597
|
-
"default": "{ title: 'Input/Lineage filter', component: 'gs-lineage-filter', parameters: withComponentDocs({ actions: { handles: [
|
|
597
|
+
"default": "{ title: 'Input/Lineage filter', component: 'gs-lineage-filter', parameters: withComponentDocs({ actions: { handles: [gsEventNames.lineageFilterChanged, ...previewHandles], }, fetchMock: { mocks: [ { matcher: { name: 'pangoLineage', url: AGGREGATED_ENDPOINT, body: { fields: ['pangoLineage'], country: 'Germany', }, }, response: { status: 200, body: aggregatedData, }, }, ], }, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], argTypes: { lapisField: { control: { type: 'select', }, options: ['host'], }, placeholderText: { control: { type: 'text', }, }, value: { control: { type: 'text', }, }, width: { control: { type: 'text', }, }, lapisFilter: { control: { type: 'object', }, }, }, }"
|
|
598
598
|
},
|
|
599
599
|
{
|
|
600
600
|
"kind": "variable",
|
|
@@ -626,7 +626,7 @@
|
|
|
626
626
|
"type": {
|
|
627
627
|
"text": "StoryObj<Required<LineageFilterProps>>"
|
|
628
628
|
},
|
|
629
|
-
"default": "{ ...LineageFilter, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-lineage-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a lineage'); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(
|
|
629
|
+
"default": "{ ...LineageFilter, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-lineage-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a lineage'); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(gsEventNames.lineageFilterChanged, listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enters an invalid lineage value', async () => { await userEvent.type(inputField(), 'notInList'); await expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>9/}'); await userEvent.click(canvas.getByLabelText('toggle menu')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ pangoLineage: undefined, }); }); }); await step('Enter a valid lineage value', async () => { await userEvent.type(inputField(), 'B.1.1.7*'); await userEvent.click(canvas.getByRole('option', { name: 'B.1.1.7*' })); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ pangoLineage: 'B.1.1.7*', }); }); }); }, args: { ...LineageFilter.args, value: '', }, }"
|
|
630
630
|
}
|
|
631
631
|
],
|
|
632
632
|
"exports": [
|
|
@@ -825,7 +825,7 @@
|
|
|
825
825
|
"type": {
|
|
826
826
|
"text": "Meta"
|
|
827
827
|
},
|
|
828
|
-
"default": "{ title: 'Input/Location filter', component: 'gs-location-filter', parameters: withComponentDocs({ actions: { handles: [
|
|
828
|
+
"default": "{ title: 'Input/Location filter', component: 'gs-location-filter', parameters: withComponentDocs({ actions: { handles: [gsEventNames.locationChanged, ...previewHandles], }, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { fields: { control: { type: 'object', }, }, value: { control: { type: 'object', }, }, width: { control: { type: 'text', }, }, placeholderText: { control: { type: 'text', }, }, lapisFilter: { age: 18, }, }, tags: ['autodocs'], }"
|
|
829
829
|
},
|
|
830
830
|
{
|
|
831
831
|
"kind": "variable",
|
|
@@ -857,7 +857,7 @@
|
|
|
857
857
|
"type": {
|
|
858
858
|
"text": "StoryObj<LocationFilterProps>"
|
|
859
859
|
},
|
|
860
|
-
"default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 200, body: data, }, }, ], }, }, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter'); const inputField = () => canvas.getByRole('combobox'); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(
|
|
860
|
+
"default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 200, body: data, }, }, ], }, }, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter'); const inputField = () => canvas.getByRole('combobox'); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(gsEventNames.locationChanged, listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Input invalid location', async () => { await userEvent.type(inputField(), 'Not / A / Location'); await expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>18/}'); await userEvent.click(canvas.getByLabelText('toggle menu')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: undefined, country: undefined, division: undefined, location: undefined, }); }); }); await step('Select Asia', async () => { await userEvent.type(inputField(), 'Asia'); await userEvent.click(canvas.getByRole('option', { name: /^Asia.*Asia$/ })); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: 'Asia', country: undefined, division: undefined, location: undefined, }); }); }); await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => { await userEvent.type(inputField(), ' / Bangladesh / Rajshahi / Chapainawabgonj'); await userEvent.click(canvas.getByText('Asia / Bangladesh / Rajshahi / Chapainawabgonj')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: 'Asia', country: 'Bangladesh', division: 'Rajshahi', location: 'Chapainawabgonj', }); }); }); }, }"
|
|
861
861
|
}
|
|
862
862
|
],
|
|
863
863
|
"exports": [
|
|
@@ -1056,7 +1056,7 @@
|
|
|
1056
1056
|
"type": {
|
|
1057
1057
|
"text": "Meta<MutationFilterProps>"
|
|
1058
1058
|
},
|
|
1059
|
-
"default": "{ title: 'Input/Mutation filter', component: 'gs-mutation-filter', parameters: withComponentDocs({ actions: { handles: [
|
|
1059
|
+
"default": "{ title: 'Input/Mutation filter', component: 'gs-mutation-filter', parameters: withComponentDocs({ actions: { handles: [gsEventNames.mutationFilterChanged, ...previewHandles], }, fetchMock: {}, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { initialValue: { control: { type: 'object', }, }, width: { control: 'text' }, }, tags: ['autodocs'], }"
|
|
1060
1060
|
},
|
|
1061
1061
|
{
|
|
1062
1062
|
"kind": "variable",
|
|
@@ -1072,7 +1072,7 @@
|
|
|
1072
1072
|
"type": {
|
|
1073
1073
|
"text": "StoryObj<MutationFilterProps>"
|
|
1074
1074
|
},
|
|
1075
|
-
"default": "{ ...Template, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a mutation', { exact: false }); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(
|
|
1075
|
+
"default": "{ ...Template, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a mutation', { exact: false }); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(gsEventNames.mutationFilterChanged, listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enter a valid mutation', async () => { await userEvent.type(inputField(), 'A123T'); const option = await canvas.findByRole('option'); await userEvent.click(option); await waitFor(() => expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { nucleotideMutations: ['A123T'], aminoAcidMutations: [], nucleotideInsertions: [], aminoAcidInsertions: [], }, }), ), ); }); }, }"
|
|
1076
1076
|
},
|
|
1077
1077
|
{
|
|
1078
1078
|
"kind": "variable",
|
|
@@ -1214,7 +1214,7 @@
|
|
|
1214
1214
|
"type": {
|
|
1215
1215
|
"text": "Meta<Required<TextFilterProps>>"
|
|
1216
1216
|
},
|
|
1217
|
-
"default": "{ title: 'Input/Text filter', component: 'gs-text-filter', parameters: withComponentDocs({ actions: { handles: [
|
|
1217
|
+
"default": "{ title: 'Input/Text filter', component: 'gs-text-filter', parameters: withComponentDocs({ actions: { handles: [gsEventNames.textFilterChanged, ...previewHandles], }, fetchMock: { mocks: [ { matcher: { name: 'hosts', url: AGGREGATED_ENDPOINT, body: { fields: ['host'], country: 'Germany', }, }, response: { status: 200, body: data, }, }, ], }, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { lapisField: { control: { type: 'text', }, }, placeholderText: { control: { type: 'text', }, }, value: { control: { type: 'text', }, }, width: { control: { type: 'text', }, }, lapisFilter: { control: { type: 'object', }, }, }, tags: ['autodocs'], }"
|
|
1218
1218
|
},
|
|
1219
1219
|
{
|
|
1220
1220
|
"kind": "variable",
|
|
@@ -1230,7 +1230,7 @@
|
|
|
1230
1230
|
"type": {
|
|
1231
1231
|
"text": "StoryObj<Required<TextFilterProps>>"
|
|
1232
1232
|
},
|
|
1233
|
-
"default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-text-filter'); const inputField = () => canvas.getByPlaceholderText('Enter host name'); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(
|
|
1233
|
+
"default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-text-filter'); const inputField = () => canvas.getByPlaceholderText('Enter host name'); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(gsEventNames.textFilterChanged, listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enters an invalid host name', async () => { await userEvent.type(inputField(), 'notInList'); await expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>9/}'); }); await step('Enter a valid host name', async () => { await userEvent.type(inputField(), 'Homo s'); const dropdownOption = await canvas.findByText('Homo sapiens'); await userEvent.click(dropdownOption); }); await step('Verify event is fired with correct detail', async () => { await waitFor(async () => { await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { host: 'Homo sapiens', }, }), ); }); }); await step('Remove initial value', async () => { await userEvent.click(canvas.getByRole('button', { name: 'clear selection' })); await expect(listenerMock).toHaveBeenLastCalledWith( expect.objectContaining({ detail: { host: undefined, }, }), ); }); }, args: { ...Default.args, value: '', }, }"
|
|
1234
1234
|
}
|
|
1235
1235
|
],
|
|
1236
1236
|
"exports": [
|
|
@@ -1838,6 +1838,148 @@
|
|
|
1838
1838
|
}
|
|
1839
1839
|
]
|
|
1840
1840
|
},
|
|
1841
|
+
{
|
|
1842
|
+
"kind": "javascript-module",
|
|
1843
|
+
"path": "src/web-components/visualization/gs-genome-data-viewer.spec-d.ts",
|
|
1844
|
+
"declarations": [],
|
|
1845
|
+
"exports": []
|
|
1846
|
+
},
|
|
1847
|
+
{
|
|
1848
|
+
"kind": "javascript-module",
|
|
1849
|
+
"path": "src/web-components/visualization/gs-genome-data-viewer.stories.ts",
|
|
1850
|
+
"declarations": [
|
|
1851
|
+
{
|
|
1852
|
+
"kind": "variable",
|
|
1853
|
+
"name": "meta",
|
|
1854
|
+
"type": {
|
|
1855
|
+
"text": "Meta<Required<GenomeDataViewerProps>>"
|
|
1856
|
+
},
|
|
1857
|
+
"default": "{ title: 'Visualization/Genome Data Viewer', component: 'gs-genome-data-viewer', argTypes: { gff3Source: { control: 'text' }, genomeLength: { control: 'number' }, width: { control: 'text' }, }, parameters: withComponentDocs({ componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], }"
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
"kind": "variable",
|
|
1861
|
+
"name": "Default",
|
|
1862
|
+
"type": {
|
|
1863
|
+
"text": "StoryObj<Required<GenomeDataViewerProps>>"
|
|
1864
|
+
},
|
|
1865
|
+
"default": "{ ...Template, args: { genomeLength: 11029, gff3Source: 'https://gff3Url', width: '100%', }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'gff3Data', url: 'https://gff3Url', }, response: { status: 200, body: SimpleData, headers: { 'Content-Type': 'text/plain', }, }, }, ], }, }, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-genome-data-viewer'); await step('CDS visible', async () => { const ns1NameMatches = await canvas.findAllByText('NS1'); await waitFor(async () => expect(ns1NameMatches[0]).toBeVisible()); const xAxisTick = await canvas.findAllByText('1000'); await waitFor(async () => expect(xAxisTick[0]).toBeVisible()); await waitFor(async () => expect(xAxisTick.length).toBe(2)); }); }, }"
|
|
1866
|
+
}
|
|
1867
|
+
],
|
|
1868
|
+
"exports": [
|
|
1869
|
+
{
|
|
1870
|
+
"kind": "js",
|
|
1871
|
+
"name": "default",
|
|
1872
|
+
"declaration": {
|
|
1873
|
+
"name": "meta",
|
|
1874
|
+
"module": "src/web-components/visualization/gs-genome-data-viewer.stories.ts"
|
|
1875
|
+
}
|
|
1876
|
+
},
|
|
1877
|
+
{
|
|
1878
|
+
"kind": "js",
|
|
1879
|
+
"name": "Default",
|
|
1880
|
+
"declaration": {
|
|
1881
|
+
"name": "Default",
|
|
1882
|
+
"module": "src/web-components/visualization/gs-genome-data-viewer.stories.ts"
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
]
|
|
1886
|
+
},
|
|
1887
|
+
{
|
|
1888
|
+
"kind": "javascript-module",
|
|
1889
|
+
"path": "src/web-components/visualization/gs-genome-data-viewer.tsx",
|
|
1890
|
+
"declarations": [
|
|
1891
|
+
{
|
|
1892
|
+
"kind": "class",
|
|
1893
|
+
"description": "## Context\n\nThis component shows the Coding Sequence (CDS) of a genome using a gff3 file as input.\nThe CDS shows which parts of the genome are translated into proteins.",
|
|
1894
|
+
"name": "GenomeDataViewerComponent",
|
|
1895
|
+
"members": [
|
|
1896
|
+
{
|
|
1897
|
+
"kind": "field",
|
|
1898
|
+
"name": "gff3Source",
|
|
1899
|
+
"type": {
|
|
1900
|
+
"text": "string"
|
|
1901
|
+
},
|
|
1902
|
+
"default": "''",
|
|
1903
|
+
"description": "Required\n\nThe source of the gff3 file. Any spec-compliant gff3 should be accepted, however we use the same format as Nextclade.\nSee https://docs.nextstrain.org/projects/nextclade/en/stable/user/input-files/03-genome-annotation.html for more information.\nWe only use the CDS and gene features from the gff3 file, if you have other features in the gff3 file they will be ignored.\nAlso note that if a CDS has a gene feature as a parent, the gene feature will be ignored.",
|
|
1904
|
+
"attribute": "gff3Source"
|
|
1905
|
+
},
|
|
1906
|
+
{
|
|
1907
|
+
"kind": "field",
|
|
1908
|
+
"name": "genomeLength",
|
|
1909
|
+
"type": {
|
|
1910
|
+
"text": "number | undefined"
|
|
1911
|
+
},
|
|
1912
|
+
"default": "0",
|
|
1913
|
+
"description": "The length of the genome, if this is not given it will be computed from the `sequence-region` line of the start of the gff3 file.",
|
|
1914
|
+
"attribute": "genomeLength"
|
|
1915
|
+
},
|
|
1916
|
+
{
|
|
1917
|
+
"kind": "field",
|
|
1918
|
+
"name": "width",
|
|
1919
|
+
"type": {
|
|
1920
|
+
"text": "string"
|
|
1921
|
+
},
|
|
1922
|
+
"default": "'100%'",
|
|
1923
|
+
"description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboard-components/?path=/docs/concepts-size-of-components--docs for more information.",
|
|
1924
|
+
"attribute": "width"
|
|
1925
|
+
}
|
|
1926
|
+
],
|
|
1927
|
+
"attributes": [
|
|
1928
|
+
{
|
|
1929
|
+
"name": "gff3Source",
|
|
1930
|
+
"type": {
|
|
1931
|
+
"text": "string"
|
|
1932
|
+
},
|
|
1933
|
+
"default": "''",
|
|
1934
|
+
"description": "Required\n\nThe source of the gff3 file. Any spec-compliant gff3 should be accepted, however we use the same format as Nextclade.\nSee https://docs.nextstrain.org/projects/nextclade/en/stable/user/input-files/03-genome-annotation.html for more information.\nWe only use the CDS and gene features from the gff3 file, if you have other features in the gff3 file they will be ignored.\nAlso note that if a CDS has a gene feature as a parent, the gene feature will be ignored.",
|
|
1935
|
+
"fieldName": "gff3Source"
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
"name": "genomeLength",
|
|
1939
|
+
"type": {
|
|
1940
|
+
"text": "number | undefined"
|
|
1941
|
+
},
|
|
1942
|
+
"default": "0",
|
|
1943
|
+
"description": "The length of the genome, if this is not given it will be computed from the `sequence-region` line of the start of the gff3 file.",
|
|
1944
|
+
"fieldName": "genomeLength"
|
|
1945
|
+
},
|
|
1946
|
+
{
|
|
1947
|
+
"name": "width",
|
|
1948
|
+
"type": {
|
|
1949
|
+
"text": "string"
|
|
1950
|
+
},
|
|
1951
|
+
"default": "'100%'",
|
|
1952
|
+
"description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboard-components/?path=/docs/concepts-size-of-components--docs for more information.",
|
|
1953
|
+
"fieldName": "width"
|
|
1954
|
+
}
|
|
1955
|
+
],
|
|
1956
|
+
"superclass": {
|
|
1957
|
+
"name": "PreactLitAdapter",
|
|
1958
|
+
"module": "/src/web-components/PreactLitAdapter"
|
|
1959
|
+
},
|
|
1960
|
+
"tagName": "gs-genome-data-viewer",
|
|
1961
|
+
"customElement": true
|
|
1962
|
+
}
|
|
1963
|
+
],
|
|
1964
|
+
"exports": [
|
|
1965
|
+
{
|
|
1966
|
+
"kind": "js",
|
|
1967
|
+
"name": "GenomeDataViewerComponent",
|
|
1968
|
+
"declaration": {
|
|
1969
|
+
"name": "GenomeDataViewerComponent",
|
|
1970
|
+
"module": "src/web-components/visualization/gs-genome-data-viewer.tsx"
|
|
1971
|
+
}
|
|
1972
|
+
},
|
|
1973
|
+
{
|
|
1974
|
+
"kind": "custom-element-definition",
|
|
1975
|
+
"name": "gs-genome-data-viewer",
|
|
1976
|
+
"declaration": {
|
|
1977
|
+
"name": "GenomeDataViewerComponent",
|
|
1978
|
+
"module": "src/web-components/visualization/gs-genome-data-viewer.tsx"
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
]
|
|
1982
|
+
},
|
|
1841
1983
|
{
|
|
1842
1984
|
"kind": "javascript-module",
|
|
1843
1985
|
"path": "src/web-components/visualization/gs-mutation-comparison.stories.ts",
|
|
@@ -4170,6 +4312,14 @@
|
|
|
4170
4312
|
"path": "src/web-components/visualization/index.ts",
|
|
4171
4313
|
"declarations": [],
|
|
4172
4314
|
"exports": [
|
|
4315
|
+
{
|
|
4316
|
+
"kind": "js",
|
|
4317
|
+
"name": "GenomeDataViewerComponent",
|
|
4318
|
+
"declaration": {
|
|
4319
|
+
"name": "GenomeDataViewerComponent",
|
|
4320
|
+
"module": "./gs-genome-data-viewer"
|
|
4321
|
+
}
|
|
4322
|
+
},
|
|
4173
4323
|
{
|
|
4174
4324
|
"kind": "js",
|
|
4175
4325
|
"name": "MutationComparisonComponent",
|
package/dist/{LineageFilterChangedEvent-ixHQkq8y.js → LineageFilterChangedEvent-b0iuroUL.js}
RENAMED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import z from "zod";
|
|
2
|
+
const gsEventNames = {
|
|
3
|
+
error: "gs-error",
|
|
4
|
+
dateRangeFilterChanged: "gs-date-range-filter-changed",
|
|
5
|
+
dateRangeOptionChanged: "gs-date-range-option-changed",
|
|
6
|
+
mutationFilterChanged: "gs-mutation-filter-changed",
|
|
7
|
+
lineageFilterChanged: "gs-lineage-filter-changed",
|
|
8
|
+
locationChanged: "gs-location-changed",
|
|
9
|
+
textFilterChanged: "gs-text-filter-changed"
|
|
10
|
+
};
|
|
2
11
|
const mutationsFilterSchema = z.object({
|
|
3
12
|
nucleotideMutations: z.array(z.string()),
|
|
4
13
|
aminoAcidMutations: z.array(z.string()),
|
|
@@ -55,7 +64,7 @@ const dateRangeValueSchema = z.union([
|
|
|
55
64
|
]).nullable();
|
|
56
65
|
class DateRangeOptionChangedEvent extends CustomEvent {
|
|
57
66
|
constructor(detail) {
|
|
58
|
-
super(
|
|
67
|
+
super(gsEventNames.dateRangeOptionChanged, {
|
|
59
68
|
detail,
|
|
60
69
|
bubbles: true,
|
|
61
70
|
composed: true
|
|
@@ -106,7 +115,7 @@ const dateRangeOptionPresets = {
|
|
|
106
115
|
};
|
|
107
116
|
class LocationChangedEvent extends CustomEvent {
|
|
108
117
|
constructor(detail) {
|
|
109
|
-
super(
|
|
118
|
+
super(gsEventNames.locationChanged, {
|
|
110
119
|
detail,
|
|
111
120
|
bubbles: true,
|
|
112
121
|
composed: true
|
|
@@ -115,7 +124,7 @@ class LocationChangedEvent extends CustomEvent {
|
|
|
115
124
|
}
|
|
116
125
|
class TextFilterChangedEvent extends CustomEvent {
|
|
117
126
|
constructor(detail) {
|
|
118
|
-
super(
|
|
127
|
+
super(gsEventNames.textFilterChanged, {
|
|
119
128
|
detail,
|
|
120
129
|
bubbles: true,
|
|
121
130
|
composed: true
|
|
@@ -124,7 +133,7 @@ class TextFilterChangedEvent extends CustomEvent {
|
|
|
124
133
|
}
|
|
125
134
|
class LineageFilterChangedEvent extends CustomEvent {
|
|
126
135
|
constructor(detail) {
|
|
127
|
-
super(
|
|
136
|
+
super(gsEventNames.lineageFilterChanged, {
|
|
128
137
|
detail,
|
|
129
138
|
bubbles: true,
|
|
130
139
|
composed: true
|
|
@@ -141,6 +150,7 @@ export {
|
|
|
141
150
|
dateRangeOptionPresets as d,
|
|
142
151
|
toYYYYMMDD as e,
|
|
143
152
|
lapisLocationFilterSchema as f,
|
|
153
|
+
gsEventNames as g,
|
|
144
154
|
lapisFilterSchema as l,
|
|
145
155
|
mutationsFilterSchema as m,
|
|
146
156
|
namedLapisFilterSchema as n,
|
|
@@ -148,4 +158,4 @@ export {
|
|
|
148
158
|
temporalGranularitySchema as t,
|
|
149
159
|
views as v
|
|
150
160
|
};
|
|
151
|
-
//# sourceMappingURL=LineageFilterChangedEvent-
|
|
161
|
+
//# sourceMappingURL=LineageFilterChangedEvent-b0iuroUL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LineageFilterChangedEvent-b0iuroUL.js","sources":["../src/utils/gsEventNames.ts","../src/types.ts","../src/preact/dateRangeFilter/dateConversion.ts","../src/preact/dateRangeFilter/dateRangeOption.ts","../src/preact/locationFilter/LocationChangedEvent.ts","../src/preact/textFilter/TextFilterChangedEvent.ts","../src/preact/lineageFilter/LineageFilterChangedEvent.ts"],"sourcesContent":["export const gsEventNames = {\n error: 'gs-error',\n dateRangeFilterChanged: 'gs-date-range-filter-changed',\n dateRangeOptionChanged: 'gs-date-range-option-changed',\n mutationFilterChanged: 'gs-mutation-filter-changed',\n lineageFilterChanged: 'gs-lineage-filter-changed',\n locationChanged: 'gs-location-changed',\n textFilterChanged: 'gs-text-filter-changed',\n} as const;\n","import z from 'zod';\n\nimport {\n type Deletion,\n type DeletionClass,\n type Insertion,\n type InsertionClass,\n type Substitution,\n type SubstitutionClass,\n} from './utils/mutations';\n\nexport const mutationsFilterSchema = z.object({\n nucleotideMutations: z.array(z.string()),\n aminoAcidMutations: z.array(z.string()),\n nucleotideInsertions: z.array(z.string()),\n aminoAcidInsertions: z.array(z.string()),\n});\nexport type MutationsFilter = z.infer<typeof mutationsFilterSchema>;\n\nexport const lapisFilterSchema = z\n .record(z.union([z.string(), z.array(z.string()), z.number(), z.null(), z.boolean(), z.undefined()]))\n .and(mutationsFilterSchema.partial());\nexport type LapisFilter = z.infer<typeof lapisFilterSchema>;\n\nexport const namedLapisFilterSchema = z.object({\n lapisFilter: lapisFilterSchema,\n displayName: z.string(),\n});\nexport type NamedLapisFilter = z.infer<typeof namedLapisFilterSchema>;\n\nexport const lapisLocationFilterSchema = z.record(z.union([z.string(), z.undefined()]));\nexport type LapisLocationFilter = z.infer<typeof lapisLocationFilterSchema>;\n\nexport const temporalGranularitySchema = z.union([\n z.literal('day'),\n z.literal('week'),\n z.literal('month'),\n z.literal('year'),\n]);\nexport type TemporalGranularity = z.infer<typeof temporalGranularitySchema>;\n\nexport const sequenceTypeSchema = z.union([z.literal('nucleotide'), z.literal('amino acid')]);\nexport type SequenceType = z.infer<typeof sequenceTypeSchema>;\n\nexport type SubstitutionOrDeletion = 'substitution' | 'deletion';\n\nexport type MutationType = SubstitutionOrDeletion | 'insertion';\n\nexport type SubstitutionEntry<T extends Substitution = SubstitutionClass> = {\n type: 'substitution';\n mutation: T;\n count: number;\n proportion: number;\n};\n\nexport type DeletionEntry<T extends Deletion = DeletionClass> = {\n type: 'deletion';\n mutation: T;\n count: number;\n proportion: number;\n};\n\nexport type InsertionEntry<T extends Insertion = InsertionClass> = { type: 'insertion'; mutation: T; count: number };\n\nexport type SubstitutionOrDeletionEntry<\n S extends Substitution = SubstitutionClass,\n D extends Deletion = DeletionClass,\n> = SubstitutionEntry<S> | DeletionEntry<D>;\n\nexport type MutationEntry = SubstitutionEntry | DeletionEntry | InsertionEntry;\n\nexport const views = {\n table: 'table',\n venn: 'venn',\n grid: 'grid',\n insertions: 'insertions',\n bar: 'bar',\n line: 'line',\n bubble: 'bubble',\n map: 'map',\n} as const;\n","export const toYYYYMMDD = (date: Date) => {\n const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: '2-digit', day: '2-digit' };\n return date.toLocaleDateString('en-CA', options);\n};\n","import z from 'zod';\n\nimport { toYYYYMMDD } from './dateConversion';\nimport { gsEventNames } from '../../utils/gsEventNames';\n\n/**\n * A date range option that can be used in the `gs-date-range-filter` component.\n */\nexport const dateRangeOptionSchema = z.object({\n /** The label of the date range option that will be shown to the user */\n label: z.string(),\n /**\n * The start date of the date range in the format `YYYY-MM-DD`.\n * If not set, the date range selector will default to the `earliestDate` property.\n */\n dateFrom: z.string().date().optional(),\n /**\n * The end date of the date range in the format `YYYY-MM-DD`.\n * If not set, the date range selector will default to the current date.\n */\n dateTo: z.string().date().optional(),\n});\n\nexport type DateRangeOption = z.infer<typeof dateRangeOptionSchema>;\n\nexport const dateRangeValueSchema = z\n .union([\n z.string(),\n z.object({\n dateFrom: z.string().date().optional(),\n dateTo: z.string().date().optional(),\n }),\n ])\n .nullable();\n\nexport type DateRangeValue = z.infer<typeof dateRangeValueSchema>;\n\nexport class DateRangeOptionChangedEvent extends CustomEvent<DateRangeValue> {\n constructor(detail: DateRangeValue) {\n super(gsEventNames.dateRangeOptionChanged, {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n\nconst today = new Date();\n\nconst twoWeeksAgo = new Date();\ntwoWeeksAgo.setDate(today.getDate() - 14);\n\nconst lastMonth = new Date(today);\nlastMonth.setMonth(today.getMonth() - 1);\n\nconst last2Months = new Date(today);\nlast2Months.setMonth(today.getMonth() - 2);\n\nconst last3Months = new Date(today);\nlast3Months.setMonth(today.getMonth() - 3);\n\nconst last6Months = new Date(today);\nlast6Months.setMonth(today.getMonth() - 6);\n\nconst lastYear = new Date(today);\nlastYear.setFullYear(today.getFullYear() - 1);\n\n/**\n * Presets for the `gs-date-range-filter` component that can be used as `dateRangeOptions`.\n */\nexport const dateRangeOptionPresets = {\n last2Weeks: {\n label: 'Last 2 weeks',\n dateFrom: toYYYYMMDD(twoWeeksAgo),\n },\n lastMonth: {\n label: 'Last month',\n dateFrom: toYYYYMMDD(lastMonth),\n },\n last2Months: {\n label: 'Last 2 months',\n dateFrom: toYYYYMMDD(last2Months),\n },\n last3Months: {\n label: 'Last 3 months',\n dateFrom: toYYYYMMDD(last3Months),\n },\n last6Months: {\n label: 'Last 6 months',\n dateFrom: toYYYYMMDD(last6Months),\n },\n lastYear: {\n label: 'Last year',\n dateFrom: toYYYYMMDD(lastYear),\n },\n allTimes: {\n label: 'All times',\n },\n} satisfies Record<string, DateRangeOption>;\n","import { type LapisLocationFilter } from '../../types';\nimport { gsEventNames } from '../../utils/gsEventNames';\n\nexport class LocationChangedEvent extends CustomEvent<LapisLocationFilter> {\n constructor(detail: LapisLocationFilter) {\n super(gsEventNames.locationChanged, {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n","import { gsEventNames } from '../../utils/gsEventNames';\n\ntype LapisTextFilter = Record<string, string | undefined>;\n\nexport class TextFilterChangedEvent extends CustomEvent<LapisTextFilter> {\n constructor(detail: LapisTextFilter) {\n super(gsEventNames.textFilterChanged, {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n","import { gsEventNames } from '../../utils/gsEventNames';\n\ntype LapisLineageFilter = Record<string, string | undefined>;\n\nexport class LineageFilterChangedEvent extends CustomEvent<LapisLineageFilter> {\n constructor(detail: LapisLineageFilter) {\n super(gsEventNames.lineageFilterChanged, {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n"],"names":[],"mappings":"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"}
|