@genspectrum/dashboard-components 0.11.4 → 0.11.6
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 +54 -3
- package/dist/assets/{mutationOverTimeWorker-Cr-NmYEs.js.map → mutationOverTimeWorker-CWneD7i5.js.map} +1 -1
- package/dist/components.d.ts +29 -15
- package/dist/components.js +184 -55
- package/dist/components.js.map +1 -1
- package/dist/style.css +5 -5
- package/dist/util.d.ts +21 -18
- package/package.json +1 -1
- package/src/preact/aggregatedData/__mockData__/aggregatedWith1Field.json +399 -0
- package/src/preact/aggregatedData/__mockData__/aggregatedWith2Fields.json +1771 -0
- package/src/preact/aggregatedData/aggregate-bar-chart.tsx +177 -0
- package/src/preact/aggregatedData/aggregate-table.tsx +24 -2
- package/src/preact/aggregatedData/aggregate.stories.tsx +61 -2
- package/src/preact/aggregatedData/aggregate.tsx +18 -6
- package/src/preact/components/tabs.tsx +19 -39
- package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +1 -1
- package/src/preact/shared/charts/colors.ts +1 -1
- package/src/query/queryAggregateData.spec.ts +16 -109
- package/src/query/queryAggregateData.ts +2 -12
- package/src/query/queryGeneralStatistics.ts +2 -2
- package/src/utils/temporal.spec.ts +62 -8
- package/src/utils/temporalClass.ts +1 -8
- package/src/web-components/visualization/gs-aggregate.stories.ts +90 -20
- package/src/web-components/visualization/gs-aggregate.tsx +20 -0
- package/standalone-bundle/assets/{mutationOverTimeWorker-DIQRmxvC.js.map → mutationOverTimeWorker-x1ipPFL0.js.map} +1 -1
- package/standalone-bundle/dashboard-components.js +3771 -3651
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/standalone-bundle/style.css +1 -1
|
@@ -68,15 +68,69 @@ describe('YearMonthDay', () => {
|
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
describe('YearWeek', () => {
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
const examples = [
|
|
72
|
+
{
|
|
73
|
+
string: '2020-W01',
|
|
74
|
+
expectedYear: 2020,
|
|
75
|
+
expectedWeek: 1,
|
|
76
|
+
expectedFirstDay: '2019-12-30',
|
|
77
|
+
expectedLastDay: '2020-01-05',
|
|
78
|
+
expectedText: '2020-W01',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
string: '2020-W53',
|
|
82
|
+
expectedYear: 2020,
|
|
83
|
+
expectedWeek: 53,
|
|
84
|
+
expectedFirstDay: '2020-12-28',
|
|
85
|
+
expectedLastDay: '2021-01-03',
|
|
86
|
+
expectedText: '2020-W53',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
string: '2021-W01',
|
|
90
|
+
expectedYear: 2021,
|
|
91
|
+
expectedWeek: 1,
|
|
92
|
+
expectedFirstDay: '2021-01-04',
|
|
93
|
+
expectedLastDay: '2021-01-10',
|
|
94
|
+
expectedText: '2021-W01',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
string: '2021-W53',
|
|
98
|
+
expectedYear: 2021,
|
|
99
|
+
expectedWeek: 53,
|
|
100
|
+
expectedFirstDay: '2022-01-03',
|
|
101
|
+
expectedLastDay: '2022-01-09',
|
|
102
|
+
expectedText: '2022-W01',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
string: '2022-W01',
|
|
106
|
+
expectedYear: 2022,
|
|
107
|
+
expectedWeek: 1,
|
|
108
|
+
expectedFirstDay: '2022-01-03',
|
|
109
|
+
expectedLastDay: '2022-01-09',
|
|
110
|
+
expectedText: '2022-W01',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
string: '2024-W01',
|
|
114
|
+
expectedYear: 2024,
|
|
115
|
+
expectedWeek: 1,
|
|
116
|
+
expectedFirstDay: '2024-01-01',
|
|
117
|
+
expectedLastDay: '2024-01-07',
|
|
118
|
+
expectedText: '2024-W01',
|
|
119
|
+
},
|
|
120
|
+
];
|
|
73
121
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
122
|
+
for (const example of examples) {
|
|
123
|
+
const { string, expectedYear, expectedWeek, expectedFirstDay, expectedLastDay, expectedText } = example;
|
|
124
|
+
it(`should parse ${string} from string`, () => {
|
|
125
|
+
const underTest = YearWeekClass.parse(string, cache);
|
|
126
|
+
|
|
127
|
+
expect(underTest.isoYearNumber).equal(expectedYear);
|
|
128
|
+
expect(underTest.isoWeekNumber).equal(expectedWeek);
|
|
129
|
+
expect(underTest.firstDay.text).equal(expectedFirstDay);
|
|
130
|
+
expect(underTest.text).equal(expectedText);
|
|
131
|
+
expect(underTest.lastDay.text).equal(expectedLastDay);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
80
134
|
});
|
|
81
135
|
|
|
82
136
|
describe('YearMonth', () => {
|
|
@@ -171,14 +171,7 @@ export class YearWeekClass implements YearWeek {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
get lastDay(): YearMonthDayClass {
|
|
174
|
-
const
|
|
175
|
-
.year(this.isoYearNumber)
|
|
176
|
-
.startOf('year')
|
|
177
|
-
.add((this.isoWeekNumber - 1) * 7, 'day')
|
|
178
|
-
.startOf('week')
|
|
179
|
-
.add(1, 'day');
|
|
180
|
-
const lastDay = firstDay.add(6, 'day');
|
|
181
|
-
|
|
174
|
+
const lastDay = this.firstDay.dayjs.add(6, 'days');
|
|
182
175
|
return this.cache.getYearMonthDay(lastDay.format('YYYY-MM-DD'));
|
|
183
176
|
}
|
|
184
177
|
|
|
@@ -4,6 +4,8 @@ import { html } from 'lit';
|
|
|
4
4
|
import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
|
|
5
5
|
import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
|
|
6
6
|
import aggregatedData from '../../preact/aggregatedData/__mockData__/aggregated.json';
|
|
7
|
+
import aggregatedDataWith1Field from '../../preact/aggregatedData/__mockData__/aggregatedWith1Field.json';
|
|
8
|
+
import aggregatedDataWith2Fields from '../../preact/aggregatedData/__mockData__/aggregatedWith2Fields.json';
|
|
7
9
|
import type { AggregateProps } from '../../preact/aggregatedData/aggregate';
|
|
8
10
|
|
|
9
11
|
import './gs-aggregate';
|
|
@@ -19,6 +21,7 @@ const codeExample = `
|
|
|
19
21
|
initialSortField="count"
|
|
20
22
|
initialSortDirection="descending"
|
|
21
23
|
pageSize="10"
|
|
24
|
+
maxNumberOfBars="50"
|
|
22
25
|
></gs-aggregate>`;
|
|
23
26
|
|
|
24
27
|
const meta: Meta<Required<AggregateProps>> = {
|
|
@@ -27,7 +30,7 @@ const meta: Meta<Required<AggregateProps>> = {
|
|
|
27
30
|
argTypes: {
|
|
28
31
|
fields: [{ control: 'object' }],
|
|
29
32
|
views: {
|
|
30
|
-
options: ['table'],
|
|
33
|
+
options: ['table', 'bar'],
|
|
31
34
|
control: { type: 'check' },
|
|
32
35
|
},
|
|
33
36
|
width: { control: 'text' },
|
|
@@ -40,24 +43,6 @@ const meta: Meta<Required<AggregateProps>> = {
|
|
|
40
43
|
},
|
|
41
44
|
},
|
|
42
45
|
parameters: withComponentDocs({
|
|
43
|
-
fetchMock: {
|
|
44
|
-
mocks: [
|
|
45
|
-
{
|
|
46
|
-
matcher: {
|
|
47
|
-
name: 'aggregatedData',
|
|
48
|
-
url: AGGREGATED_ENDPOINT,
|
|
49
|
-
body: {
|
|
50
|
-
fields: ['division', 'host'],
|
|
51
|
-
country: 'USA',
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
response: {
|
|
55
|
-
status: 200,
|
|
56
|
-
body: aggregatedData,
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
},
|
|
61
46
|
componentDocs: {
|
|
62
47
|
opensShadowDom: true,
|
|
63
48
|
expectsChildren: false,
|
|
@@ -81,12 +66,33 @@ export const Table: StoryObj<Required<AggregateProps>> = {
|
|
|
81
66
|
.initialSortField=${args.initialSortField}
|
|
82
67
|
.initialSortDirection=${args.initialSortDirection}
|
|
83
68
|
.pageSize=${args.pageSize}
|
|
69
|
+
.maxNumberOfBars=${args.maxNumberOfBars}
|
|
84
70
|
></gs-aggregate>
|
|
85
71
|
</gs-app>
|
|
86
72
|
`,
|
|
73
|
+
parameters: {
|
|
74
|
+
fetchMock: {
|
|
75
|
+
mocks: [
|
|
76
|
+
{
|
|
77
|
+
matcher: {
|
|
78
|
+
name: 'aggregatedData',
|
|
79
|
+
url: AGGREGATED_ENDPOINT,
|
|
80
|
+
body: {
|
|
81
|
+
fields: ['division', 'host'],
|
|
82
|
+
country: 'USA',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
response: {
|
|
86
|
+
status: 200,
|
|
87
|
+
body: aggregatedData,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
87
93
|
args: {
|
|
88
94
|
fields: ['division', 'host'],
|
|
89
|
-
views: ['table'],
|
|
95
|
+
views: ['table', 'bar'],
|
|
90
96
|
lapisFilter: {
|
|
91
97
|
country: 'USA',
|
|
92
98
|
},
|
|
@@ -95,5 +101,69 @@ export const Table: StoryObj<Required<AggregateProps>> = {
|
|
|
95
101
|
initialSortField: 'count',
|
|
96
102
|
initialSortDirection: 'descending',
|
|
97
103
|
pageSize: 10,
|
|
104
|
+
maxNumberOfBars: 10,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const BarChartWithOneField: StoryObj<Required<AggregateProps>> = {
|
|
109
|
+
...Table,
|
|
110
|
+
args: {
|
|
111
|
+
...Table.args,
|
|
112
|
+
fields: ['division'],
|
|
113
|
+
views: ['bar', 'table'],
|
|
114
|
+
},
|
|
115
|
+
parameters: {
|
|
116
|
+
fetchMock: {
|
|
117
|
+
mocks: [
|
|
118
|
+
{
|
|
119
|
+
matcher: {
|
|
120
|
+
name: 'aggregatedData',
|
|
121
|
+
url: AGGREGATED_ENDPOINT,
|
|
122
|
+
body: {
|
|
123
|
+
fields: ['division'],
|
|
124
|
+
country: 'USA',
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
response: {
|
|
128
|
+
status: 200,
|
|
129
|
+
body: aggregatedDataWith1Field,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const BarChartWithTwoFields: StoryObj<Required<AggregateProps>> = {
|
|
138
|
+
...Table,
|
|
139
|
+
args: {
|
|
140
|
+
...Table.args,
|
|
141
|
+
fields: ['division', 'nextstrainClade'],
|
|
142
|
+
lapisFilter: {
|
|
143
|
+
country: 'Germany',
|
|
144
|
+
dateTo: '2022-02-01',
|
|
145
|
+
},
|
|
146
|
+
views: ['bar', 'table'],
|
|
147
|
+
},
|
|
148
|
+
parameters: {
|
|
149
|
+
fetchMock: {
|
|
150
|
+
mocks: [
|
|
151
|
+
{
|
|
152
|
+
matcher: {
|
|
153
|
+
name: 'aggregatedData',
|
|
154
|
+
url: AGGREGATED_ENDPOINT,
|
|
155
|
+
body: {
|
|
156
|
+
fields: ['division', 'nextstrainClade'],
|
|
157
|
+
country: 'Germany',
|
|
158
|
+
dateTo: '2022-02-01',
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
response: {
|
|
162
|
+
status: 200,
|
|
163
|
+
body: aggregatedDataWith2Fields,
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
},
|
|
98
168
|
},
|
|
99
169
|
};
|
|
@@ -20,6 +20,16 @@ import { PreactLitAdapterWithGridJsStyles } from '../PreactLitAdapterWithGridJsS
|
|
|
20
20
|
* along with the aggregated value and its proportion.
|
|
21
21
|
* The proportion represents the ratio of the aggregated value to the total count of the data
|
|
22
22
|
* (considering the applied filter).
|
|
23
|
+
*
|
|
24
|
+
* ### Bar Chart View
|
|
25
|
+
*
|
|
26
|
+
* In the bar chart view, the data is presented in vertical bars.
|
|
27
|
+
* The bar chart is supported when `fields` contains one or two entries.
|
|
28
|
+
* The first field will be used as the y-axis.
|
|
29
|
+
* If a second field is provided, it's values will be stacked along the x-axis for each key on the y-axis.
|
|
30
|
+
*
|
|
31
|
+
* The chart shows the bars with the highest aggregated `count`.
|
|
32
|
+
* The number of bars can be adjusted with the `maxNumberOfBars` property.
|
|
23
33
|
*/
|
|
24
34
|
@customElement('gs-aggregate')
|
|
25
35
|
export class AggregateComponent extends PreactLitAdapterWithGridJsStyles {
|
|
@@ -87,6 +97,12 @@ export class AggregateComponent extends PreactLitAdapterWithGridJsStyles {
|
|
|
87
97
|
@property({ type: Object })
|
|
88
98
|
pageSize: boolean | number = false;
|
|
89
99
|
|
|
100
|
+
/**
|
|
101
|
+
* The maximum number of bars to display in the bar chart view.
|
|
102
|
+
*/
|
|
103
|
+
@property({ type: Object })
|
|
104
|
+
maxNumberOfBars: number = 20;
|
|
105
|
+
|
|
90
106
|
override render() {
|
|
91
107
|
return (
|
|
92
108
|
<Aggregate
|
|
@@ -98,6 +114,7 @@ export class AggregateComponent extends PreactLitAdapterWithGridJsStyles {
|
|
|
98
114
|
initialSortField={this.initialSortField}
|
|
99
115
|
initialSortDirection={this.initialSortDirection}
|
|
100
116
|
pageSize={this.pageSize}
|
|
117
|
+
maxNumberOfBars={this.maxNumberOfBars}
|
|
101
118
|
/>
|
|
102
119
|
);
|
|
103
120
|
}
|
|
@@ -131,4 +148,7 @@ type InitialSortDirectionMatches = Expect<
|
|
|
131
148
|
Equals<typeof AggregateComponent.prototype.initialSortDirection, AggregateProps['initialSortDirection']>
|
|
132
149
|
>;
|
|
133
150
|
type PageSizeMatches = Expect<Equals<typeof AggregateComponent.prototype.pageSize, AggregateProps['pageSize']>>;
|
|
151
|
+
type MaxNumberOfBarsMatches = Expect<
|
|
152
|
+
Equals<typeof AggregateComponent.prototype.maxNumberOfBars, AggregateProps['maxNumberOfBars']>
|
|
153
|
+
>;
|
|
134
154
|
/* eslint-enable @typescript-eslint/no-unused-vars, no-unused-vars */
|