@databiosphere/findable-ui 50.5.0 → 50.6.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.
- package/lib/components/Export/components/ExportMethod/constants.d.ts +2 -0
- package/lib/components/Export/components/ExportMethod/constants.js +6 -0
- package/lib/components/Export/components/ExportMethod/exportMethod.d.ts +2 -1
- package/lib/components/Export/components/ExportMethod/exportMethod.js +6 -4
- package/lib/components/Export/components/ExportMethod/exportMethod.styles.d.ts +8 -2
- package/lib/components/Export/components/ExportMethod/exportMethod.styles.js +4 -6
- package/package.json +6 -1
- package/src/components/Export/components/ExportMethod/constants.ts +8 -0
- package/src/components/Export/components/ExportMethod/exportMethod.styles.ts +10 -8
- package/src/components/Export/components/ExportMethod/exportMethod.tsx +13 -4
- package/.eslintignore +0 -5
- package/.eslintrc.json +0 -79
- package/.github/copilot-instructions.md +0 -176
- package/.github/workflows/release-please.yml +0 -49
- package/.github/workflows/run-checks.yml +0 -57
- package/.husky/commit-msg +0 -18
- package/.husky/pre-commit +0 -43
- package/.prettierignore +0 -19
- package/.prettierrc.json +0 -1
- package/.release-please-manifest.json +0 -3
- package/.storybook/main.ts +0 -10
- package/.storybook/preview-head.html +0 -4
- package/.storybook/preview.js +0 -6
- package/CHANGELOG.md +0 -1110
- package/CLAUDE.md +0 -214
- package/backend/README.md +0 -64
- package/backend/__init__.py +0 -0
- package/backend/controllers/__init__.py +0 -0
- package/backend/controllers/facets_controller.py +0 -16
- package/backend/controllers/models.py +0 -11
- package/backend/main.py +0 -8
- package/backend/requirements.txt +0 -4
- package/backend/services/__init__.py +0 -0
- package/backend/services/facets_service.py +0 -68
- package/backend/services/models.py +0 -43
- package/commitlint.config.js +0 -1
- package/docs/TRUSTED_PUBLISHING.md +0 -132
- package/jest.config.js +0 -6
- package/release-please-config.json +0 -23
- package/tests/azulFileDownload.test.tsx +0 -96
- package/tests/buildCategoryViews.test.ts +0 -282
- package/tests/buildRequestFilters.test.ts +0 -60
- package/tests/buildRequestManifest.test.ts +0 -103
- package/tests/chart.test.tsx +0 -274
- package/tests/chartSortUtils.test.ts +0 -119
- package/tests/chartView.test.tsx +0 -48
- package/tests/dataDictionaryColumnFilters.test.tsx +0 -101
- package/tests/dataDictionary_utils.test.ts +0 -153
- package/tests/fetchApi.test.ts +0 -93
- package/tests/filter.test.tsx +0 -100
- package/tests/filterMenu.test.ts +0 -100
- package/tests/filterRange.test.tsx +0 -372
- package/tests/filterSortUtils.test.ts +0 -180
- package/tests/filters.test.tsx +0 -61
- package/tests/getFacetedMinMaxValues.test.ts +0 -166
- package/tests/getFilterSortType.test.ts +0 -45
- package/tests/getProfileStatus.test.ts +0 -290
- package/tests/linkCell.test.tsx +0 -89
- package/tests/markdownCell.test.tsx +0 -52
- package/tests/provider.test.tsx +0 -189
- package/tests/research.chatState.test.ts +0 -463
- package/tests/research.fetchResponse.test.ts +0 -164
- package/tests/research.queryProvider.test.ts +0 -321
- package/tests/research.useKeyShortCuts.test.ts +0 -256
- package/tests/rowSelectionValidation.test.ts +0 -282
- package/tests/setup.ts +0 -19
- package/tests/stepIcon.test.tsx +0 -42
- package/tests/tableFilter.test.tsx +0 -90
- package/tests/terraProfileProvider.test.tsx +0 -117
- package/tests/theme.test.ts +0 -465
- package/tests/toggleButtonGroupProvider.test.tsx +0 -125
- package/tests/transformRoute.test.ts +0 -21
- package/tests/tsconfig.json +0 -8
- package/tests/useFileLocation.test.ts +0 -36
- package/tests/useRequestManifest.test.ts +0 -201
- package/tests/useRouteHistory.test.ts +0 -97
- package/tests/useSessionActive.test.ts +0 -106
- package/tests/useWindowResize.test.ts +0 -130
- package/tests/viewModelBuilders_utils.test.ts +0 -58
- package/tests/viewToggle.test.tsx +0 -54
- package/tsconfig.json +0 -25
package/tests/chart.test.tsx
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import { composeStories } from "@storybook/react";
|
|
2
|
-
import { render, screen } from "@testing-library/react";
|
|
3
|
-
import React from "react";
|
|
4
|
-
import { SelectCategoryValueView } from "../src/common/entities";
|
|
5
|
-
import {
|
|
6
|
-
getCategoryTotalCount,
|
|
7
|
-
getCountText,
|
|
8
|
-
parseTranslate,
|
|
9
|
-
} from "../src/components/Index/components/EntityView/components/views/ChartView/components/Chart/barX/utils";
|
|
10
|
-
import { CHART_TEST_ID } from "../src/components/Index/components/EntityView/components/views/ChartView/components/Chart/constants";
|
|
11
|
-
import * as stories from "../src/components/Index/components/EntityView/components/views/ChartView/components/Chart/stories/chart.stories";
|
|
12
|
-
import { PALETTE } from "../src/styles/common/constants/palette";
|
|
13
|
-
|
|
14
|
-
const CLASSNAMES = {
|
|
15
|
-
TEXT_CATEGORY_LABEL: "text-category-label",
|
|
16
|
-
TEXT_COUNT: "text-count",
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const { Default, Selected } = composeStories(stories);
|
|
20
|
-
|
|
21
|
-
const DATA: SelectCategoryValueView[] =
|
|
22
|
-
Default.args.selectCategoryValueViews || [];
|
|
23
|
-
const SELECTED_DATA: SelectCategoryValueView[] =
|
|
24
|
-
Selected.args.selectCategoryValueViews || [];
|
|
25
|
-
const TOTAL_COUNT = getCategoryTotalCount(DATA);
|
|
26
|
-
|
|
27
|
-
describe("Chart", () => {
|
|
28
|
-
describe("basic rendering", () => {
|
|
29
|
-
let chartEl: HTMLElement;
|
|
30
|
-
let svgEl: Element | null;
|
|
31
|
-
|
|
32
|
-
beforeEach(() => {
|
|
33
|
-
render(<Default testId={CHART_TEST_ID} />);
|
|
34
|
-
chartEl = screen.getByTestId(CHART_TEST_ID);
|
|
35
|
-
svgEl = chartEl.querySelector("svg");
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("renders correctly with default data", () => {
|
|
39
|
-
expect(chartEl).toBeDefined();
|
|
40
|
-
expect(svgEl).toBeDefined();
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe("category labels and counts", () => {
|
|
45
|
-
const counts = DATA.map((d) => mapCount(d, TOTAL_COUNT));
|
|
46
|
-
const labels = DATA.map(mapLabel);
|
|
47
|
-
let countTextEls: NodeListOf<SVGElement>;
|
|
48
|
-
let labelTextEls: NodeListOf<SVGElement>;
|
|
49
|
-
let titleTextEls: NodeListOf<SVGElement>;
|
|
50
|
-
|
|
51
|
-
beforeEach(() => {
|
|
52
|
-
render(<Default testId={CHART_TEST_ID} />);
|
|
53
|
-
countTextEls = getEls(CLASSNAMES.TEXT_COUNT, "text");
|
|
54
|
-
labelTextEls = getEls(CLASSNAMES.TEXT_CATEGORY_LABEL, "text");
|
|
55
|
-
titleTextEls = getEls(CLASSNAMES.TEXT_CATEGORY_LABEL, "title");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("renders category labels and matching titles", () => {
|
|
59
|
-
expect(labelTextEls.length).toEqual(labels.length);
|
|
60
|
-
expect(titleTextEls.length).toEqual(labels.length);
|
|
61
|
-
labelTextEls.forEach((el, i) => {
|
|
62
|
-
const labelText = getLabelText(el);
|
|
63
|
-
const titleText = titleTextEls[i].textContent || "";
|
|
64
|
-
expect(labels.some((l) => l.includes(labelText))).toBeTruthy();
|
|
65
|
-
expect(labels.includes(titleText)).toBeTruthy();
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("renders category counts", () => {
|
|
70
|
-
expect(countTextEls.length).toEqual(counts.length);
|
|
71
|
-
countTextEls.forEach(({ textContent }) => {
|
|
72
|
-
expect(textContent).toBeDefined();
|
|
73
|
-
expect(counts.includes(textContent || "")).toBeTruthy();
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe("category labels with selected values", () => {
|
|
79
|
-
it("renders selected category labels with '(selected)'", () => {
|
|
80
|
-
render(<Selected testId={CHART_TEST_ID} />);
|
|
81
|
-
const textEls = getEls(CLASSNAMES.TEXT_CATEGORY_LABEL, "text");
|
|
82
|
-
const titleEls = getEls(CLASSNAMES.TEXT_CATEGORY_LABEL, "title");
|
|
83
|
-
SELECTED_DATA.forEach((selectedData, i) => {
|
|
84
|
-
const labelText = getLabelText(textEls[i]);
|
|
85
|
-
const titleText = titleEls[i].textContent || "";
|
|
86
|
-
if (selectedData.selected) {
|
|
87
|
-
const expected = `${selectedData.label} (selected)`;
|
|
88
|
-
expect(titleText).toEqual(expected);
|
|
89
|
-
expect(expected.includes(labelText)).toBeTruthy();
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
const expected = selectedData.label;
|
|
93
|
-
expect(titleText).toEqual(expected);
|
|
94
|
-
expect(expected.includes(labelText)).toBeTruthy();
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe("bars with unselected values", () => {
|
|
100
|
-
let barEls: NodeListOf<SVGElement>;
|
|
101
|
-
|
|
102
|
-
beforeEach(() => {
|
|
103
|
-
render(<Default testId={CHART_TEST_ID} />);
|
|
104
|
-
barEls = getEls("x-bar", "path");
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("renders correct number of bars", () => {
|
|
108
|
-
expect(barEls.length).toEqual(DATA.length);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("renders all bars in #C5E3FC", () => {
|
|
112
|
-
expect([...barEls].every(isFillDefault)).toBeTruthy();
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe("bars with selected values", () => {
|
|
117
|
-
let barEls: NodeListOf<SVGElement>;
|
|
118
|
-
|
|
119
|
-
beforeEach(() => {
|
|
120
|
-
render(<Selected testId={CHART_TEST_ID} />);
|
|
121
|
-
barEls = getEls("x-bar", "path");
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("renders no bars in #C5E3FC", () => {
|
|
125
|
-
expect([...barEls].some(isFillDefault)).toBeFalsy();
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("renders at least one bar in PRIMARY_MAIN", () => {
|
|
129
|
-
expect([...barEls].some(isFillPrimaryMain)).toBeTruthy();
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
describe("bars sorted by count", () => {
|
|
134
|
-
it("renders bars in descending order of count", () => {
|
|
135
|
-
render(<Default testId={CHART_TEST_ID} />);
|
|
136
|
-
// Order data by count in descending order.
|
|
137
|
-
const counts = [...DATA]
|
|
138
|
-
.sort(sortByCount)
|
|
139
|
-
.map((d) => mapCount(d, TOTAL_COUNT));
|
|
140
|
-
// Sort count <text> elements by their transform’s translate‑Y (vertical) value, since they’re rendered in data order.
|
|
141
|
-
const countTextEls = getEls(CLASSNAMES.TEXT_COUNT, "text");
|
|
142
|
-
const textContents = [...countTextEls]
|
|
143
|
-
.sort(sortByTransform)
|
|
144
|
-
.map(mapTextContent);
|
|
145
|
-
expect(textContents).toEqual(counts);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Finds the first text node within the given SVGElement.
|
|
152
|
-
* @param el - The SVG element to search for a text node.
|
|
153
|
-
* @returns The first text node found within the element.
|
|
154
|
-
*/
|
|
155
|
-
function findTextNode(el: SVGElement): Node | undefined {
|
|
156
|
-
return [...el.childNodes].find(({ nodeType }) => nodeType === Node.TEXT_NODE);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Get the SVG group element for a given class name.
|
|
161
|
-
* @param className - Class name.
|
|
162
|
-
* @returns SVG group element.
|
|
163
|
-
*/
|
|
164
|
-
function getGroupEls(className: string): HTMLCollectionOf<Element> {
|
|
165
|
-
const chartEl = screen.getByTestId(CHART_TEST_ID);
|
|
166
|
-
const gEls = chartEl.getElementsByClassName(className);
|
|
167
|
-
expect(gEls.length).toEqual(1);
|
|
168
|
-
return gEls;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Get the SVG elements for a given class name and selectors.
|
|
173
|
-
* @param className - Class name.
|
|
174
|
-
* @param selectors - Selectors.
|
|
175
|
-
* @returns SVG elements.
|
|
176
|
-
*/
|
|
177
|
-
function getEls(className: string, selectors: string): NodeListOf<SVGElement> {
|
|
178
|
-
const gEls = getGroupEls(className);
|
|
179
|
-
return gEls[0].querySelectorAll(selectors);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Retrieves the label text from a given SVGElement by extracting and processing its text content.
|
|
184
|
-
* @param el - The SVGElement from which the label text is to be retrieved.
|
|
185
|
-
* @returns The processed label text derived from the SVGElement.
|
|
186
|
-
*/
|
|
187
|
-
function getLabelText(el: SVGElement): string {
|
|
188
|
-
const labelText = stripTrailingEllipsis(findTextNode(el)?.textContent);
|
|
189
|
-
expect(labelText).toBeDefined();
|
|
190
|
-
return labelText || "";
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Check if the fill attribute of the element is #C5E3FC.
|
|
195
|
-
* @param element - Element to check.
|
|
196
|
-
* @returns True if the fill attribute is #C5E3FC, false otherwise.
|
|
197
|
-
*/
|
|
198
|
-
function isFillDefault(element: Element): boolean {
|
|
199
|
-
return element.getAttribute("fill") === "#C5E3FC";
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Check if the fill attribute of the element is PRIMARY_MAIN.
|
|
204
|
-
* @param element - Element.
|
|
205
|
-
* @returns True if the fill attribute is PRIMARY_MAIN, false otherwise.
|
|
206
|
-
*/
|
|
207
|
-
function isFillPrimaryMain(element: Element): boolean {
|
|
208
|
-
return element.getAttribute("fill") === PALETTE.PRIMARY_MAIN;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Maps the count of a category value to a string representation.
|
|
213
|
-
* @param categoryValueView - The category value view to be processed.
|
|
214
|
-
* @param total - The total count associated with the category value.
|
|
215
|
-
* @returns The formatted count text for the category value.
|
|
216
|
-
*/
|
|
217
|
-
function mapCount(
|
|
218
|
-
categoryValueView: SelectCategoryValueView,
|
|
219
|
-
total: number,
|
|
220
|
-
): string {
|
|
221
|
-
return getCountText(categoryValueView, total);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Maps the label of a category value view to a string.
|
|
226
|
-
* @param categoryValueView - Category value view.
|
|
227
|
-
* @returns Label as a string.
|
|
228
|
-
*/
|
|
229
|
-
function mapLabel(categoryValueView: SelectCategoryValueView): string {
|
|
230
|
-
return categoryValueView.label;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Maps the text content of an SVG element to a string.
|
|
235
|
-
* @param el - SVG element.
|
|
236
|
-
* @returns Text content as a string.
|
|
237
|
-
*/
|
|
238
|
-
function mapTextContent(el: SVGElement): string | null {
|
|
239
|
-
return el.textContent;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Sorts category value views by count in descending order.
|
|
244
|
-
* @param a - First category value view.
|
|
245
|
-
* @param b - Second category value view.
|
|
246
|
-
* @returns Sorted category value views.
|
|
247
|
-
*/
|
|
248
|
-
function sortByCount(
|
|
249
|
-
a: SelectCategoryValueView,
|
|
250
|
-
b: SelectCategoryValueView,
|
|
251
|
-
): number {
|
|
252
|
-
return b.count - a.count;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Sorts SVG elements by their "y" transform attribute.
|
|
257
|
-
* @param a - First SVG element.
|
|
258
|
-
* @param b - Second SVG element.
|
|
259
|
-
* @returns Sorted SVG elements.
|
|
260
|
-
*/
|
|
261
|
-
function sortByTransform(a: SVGElement, b: SVGElement): number {
|
|
262
|
-
const aTransform = parseTranslate(a.getAttribute("transform"));
|
|
263
|
-
const bTransform = parseTranslate(b.getAttribute("transform"));
|
|
264
|
-
return aTransform[1] - bTransform[1];
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Strips trailing ellipsis from a string.
|
|
269
|
-
* @param text - The input string.
|
|
270
|
-
* @returns The string without trailing ellipsis.
|
|
271
|
-
*/
|
|
272
|
-
function stripTrailingEllipsis(text?: string | null): string {
|
|
273
|
-
return (text || "").replace(/…{1,3}$/, "");
|
|
274
|
-
}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { CHART_SORT_FN } from "../src/common/chart/sort/constants";
|
|
2
|
-
import { CHART_SORT, ChartSortFn } from "../src/common/chart/sort/types";
|
|
3
|
-
import { getChartSortFn } from "../src/common/chart/sort/utils";
|
|
4
|
-
import { SelectCategoryValueView } from "../src/common/entities";
|
|
5
|
-
import {
|
|
6
|
-
sortCategoryValueViewsAlpha,
|
|
7
|
-
sortCategoryValueViewsCount,
|
|
8
|
-
} from "../src/common/filters/sort/models/utils";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Creates a mock category value view for testing.
|
|
12
|
-
* @param key - The key for the category value.
|
|
13
|
-
* @param count - The count for the category value.
|
|
14
|
-
* @returns A mock SelectCategoryValueView.
|
|
15
|
-
*/
|
|
16
|
-
const createMockCategoryValueView = (
|
|
17
|
-
key: unknown,
|
|
18
|
-
count: number,
|
|
19
|
-
): SelectCategoryValueView => ({
|
|
20
|
-
count,
|
|
21
|
-
key,
|
|
22
|
-
label: key ? String(key) : "",
|
|
23
|
-
selected: false,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe("Chart Sort Utils", () => {
|
|
27
|
-
describe("CHART_SORT_FN", () => {
|
|
28
|
-
it("maps CHART_SORT.ALPHA to sortCategoryValueViewsAlpha", () => {
|
|
29
|
-
expect(CHART_SORT_FN[CHART_SORT.ALPHA]).toBe(sortCategoryValueViewsAlpha);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("maps CHART_SORT.COUNT to sortCategoryValueViewsCount", () => {
|
|
33
|
-
expect(CHART_SORT_FN[CHART_SORT.COUNT]).toBe(sortCategoryValueViewsCount);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
describe("getChartSortFn", () => {
|
|
38
|
-
it("returns COUNT sort function when undefined", () => {
|
|
39
|
-
const sortFn = getChartSortFn(undefined);
|
|
40
|
-
expect(sortFn).toBe(sortCategoryValueViewsCount);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("returns ALPHA sort function for CHART_SORT.ALPHA", () => {
|
|
44
|
-
const sortFn = getChartSortFn(CHART_SORT.ALPHA);
|
|
45
|
-
expect(sortFn).toBe(sortCategoryValueViewsAlpha);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("returns COUNT sort function for CHART_SORT.COUNT", () => {
|
|
49
|
-
const sortFn = getChartSortFn(CHART_SORT.COUNT);
|
|
50
|
-
expect(sortFn).toBe(sortCategoryValueViewsCount);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("returns custom function when provided", () => {
|
|
54
|
-
const customSortFn: ChartSortFn = (a, b) =>
|
|
55
|
-
a.label.localeCompare(b.label);
|
|
56
|
-
const sortFn = getChartSortFn(customSortFn);
|
|
57
|
-
expect(sortFn).toBe(customSortFn);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("custom sort function works correctly", () => {
|
|
61
|
-
// Custom sort: by label length descending
|
|
62
|
-
const customSortFn: ChartSortFn = (a, b) =>
|
|
63
|
-
b.label.length - a.label.length;
|
|
64
|
-
const sortFn = getChartSortFn(customSortFn);
|
|
65
|
-
|
|
66
|
-
const data = [
|
|
67
|
-
createMockCategoryValueView("a", 10),
|
|
68
|
-
createMockCategoryValueView("abc", 5),
|
|
69
|
-
createMockCategoryValueView("ab", 3),
|
|
70
|
-
];
|
|
71
|
-
|
|
72
|
-
const sorted = [...data].sort(sortFn);
|
|
73
|
-
|
|
74
|
-
expect(sorted.map((cv) => cv.label)).toEqual(["abc", "ab", "a"]);
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe("sorting integration", () => {
|
|
79
|
-
const testData = [
|
|
80
|
-
createMockCategoryValueView("Zebra", 5),
|
|
81
|
-
createMockCategoryValueView("Apple", 10),
|
|
82
|
-
createMockCategoryValueView("Banana", 3),
|
|
83
|
-
];
|
|
84
|
-
|
|
85
|
-
it("sorts alphabetically with CHART_SORT.ALPHA", () => {
|
|
86
|
-
const sortFn = getChartSortFn(CHART_SORT.ALPHA);
|
|
87
|
-
const sorted = [...testData].sort(sortFn);
|
|
88
|
-
|
|
89
|
-
expect(sorted.map((cv) => cv.label)).toEqual([
|
|
90
|
-
"Apple",
|
|
91
|
-
"Banana",
|
|
92
|
-
"Zebra",
|
|
93
|
-
]);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("sorts by count descending with CHART_SORT.COUNT", () => {
|
|
97
|
-
const sortFn = getChartSortFn(CHART_SORT.COUNT);
|
|
98
|
-
const sorted = [...testData].sort(sortFn);
|
|
99
|
-
|
|
100
|
-
expect(sorted.map((cv) => cv.label)).toEqual([
|
|
101
|
-
"Apple",
|
|
102
|
-
"Zebra",
|
|
103
|
-
"Banana",
|
|
104
|
-
]);
|
|
105
|
-
expect(sorted.map((cv) => cv.count)).toEqual([10, 5, 3]);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("defaults to count sort when no option provided", () => {
|
|
109
|
-
const sortFn = getChartSortFn();
|
|
110
|
-
const sorted = [...testData].sort(sortFn);
|
|
111
|
-
|
|
112
|
-
expect(sorted.map((cv) => cv.label)).toEqual([
|
|
113
|
-
"Apple",
|
|
114
|
-
"Zebra",
|
|
115
|
-
"Banana",
|
|
116
|
-
]);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
});
|
package/tests/chartView.test.tsx
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { composeStories } from "@storybook/react";
|
|
2
|
-
import { render, screen } from "@testing-library/react";
|
|
3
|
-
import React from "react";
|
|
4
|
-
import { CHART_VIEW_TEST_ID } from "../src/components/Index/components/EntityView/components/views/ChartView/constants";
|
|
5
|
-
import * as stories from "../src/components/Index/components/EntityView/components/views/ChartView/stories/chartView.stories";
|
|
6
|
-
|
|
7
|
-
const { Default } = composeStories(stories);
|
|
8
|
-
|
|
9
|
-
describe("ChartView", () => {
|
|
10
|
-
it("renders correctly", () => {
|
|
11
|
-
render(<Default testId={CHART_VIEW_TEST_ID} />);
|
|
12
|
-
const chartEl = screen.getByTestId(CHART_VIEW_TEST_ID);
|
|
13
|
-
expect(chartEl).toBeDefined();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("renders nothing when no categories are available", () => {
|
|
17
|
-
render(<Default categoryFilters={[]} testId={CHART_VIEW_TEST_ID} />);
|
|
18
|
-
expect(screen.queryByTestId(CHART_VIEW_TEST_ID)).toBeNull();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("renders correct number of chart sections", () => {
|
|
22
|
-
render(<Default />);
|
|
23
|
-
// Mocks include the facets `Biological Sex`, `Genus Species` and `Paired End`.
|
|
24
|
-
const categoryLabels = screen.getAllByText(
|
|
25
|
-
/Biological Sex|Genus Species|Paired End}/,
|
|
26
|
-
);
|
|
27
|
-
// `Paired End` is not included in the chart view, `enableChartView` is false.
|
|
28
|
-
expect(categoryLabels.length).toBe(2);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("renders chart titles correctly", () => {
|
|
32
|
-
render(<Default />);
|
|
33
|
-
const {
|
|
34
|
-
args: { entityName },
|
|
35
|
-
} = Default;
|
|
36
|
-
["Biological Sex", "Genus Species"].forEach((category) => {
|
|
37
|
-
expect(
|
|
38
|
-
screen.getByText(new RegExp(`${entityName} by ${category}`)),
|
|
39
|
-
).toBeDefined();
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("renders charts for each category", () => {
|
|
44
|
-
render(<Default />);
|
|
45
|
-
const svgEls = document.querySelectorAll("svg");
|
|
46
|
-
expect(svgEls.length).toBe(2);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { matchers } from "@emotion/jest";
|
|
2
|
-
import { jest } from "@jest/globals";
|
|
3
|
-
import { composeStories } from "@storybook/react";
|
|
4
|
-
import "@testing-library/jest-dom";
|
|
5
|
-
import { fireEvent, render, screen, within } from "@testing-library/react";
|
|
6
|
-
import React from "react";
|
|
7
|
-
import { waitFor } from "storybook/test";
|
|
8
|
-
import * as stories from "../src/components/DataDictionary/components/Filters/stories/filters.stories";
|
|
9
|
-
import { MUI_CLASSES } from "../src/tests/mui/constants";
|
|
10
|
-
import {
|
|
11
|
-
getButton,
|
|
12
|
-
getRole,
|
|
13
|
-
getStartsWithRegex,
|
|
14
|
-
getText,
|
|
15
|
-
} from "../src/tests/utils";
|
|
16
|
-
|
|
17
|
-
expect.extend(matchers);
|
|
18
|
-
|
|
19
|
-
const { Default } = composeStories(stories);
|
|
20
|
-
|
|
21
|
-
describe("DataDictionaryColumnFilters", () => {
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
jest.clearAllMocks();
|
|
24
|
-
render(<Default />);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("renders correctly", async () => {
|
|
28
|
-
expect(await screen.findByText("Required")).toBeInTheDocument();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("renders all filterable column headers", async () => {
|
|
32
|
-
expect(await screen.findByText("Required")).toBeInTheDocument();
|
|
33
|
-
expect(await screen.findByText("BioNetwork")).toBeInTheDocument();
|
|
34
|
-
expect(screen.queryByText("Example")).not.toBeInTheDocument();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("renders BioNetwork filter options", async () => {
|
|
38
|
-
const buttonEl = getButton("BioNetwork");
|
|
39
|
-
fireEvent.click(buttonEl);
|
|
40
|
-
// Wait for menu to open.
|
|
41
|
-
await waitFor(() => expect(getRole("menu")).toBeInTheDocument());
|
|
42
|
-
expect(getText("Brain")).toBeInTheDocument();
|
|
43
|
-
expect(getText("Lung")).toBeInTheDocument();
|
|
44
|
-
expect(getText("Nervous System")).toBeInTheDocument();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("renders BioNetwork filter options in alphabetical order", async () => {
|
|
48
|
-
const buttonEl = getButton("BioNetwork");
|
|
49
|
-
fireEvent.click(buttonEl);
|
|
50
|
-
// Wait for menu to open.
|
|
51
|
-
await waitFor(() => expect(getRole("menu")).toBeInTheDocument());
|
|
52
|
-
const optionEls = within(getRole("menu")).getAllByRole("button");
|
|
53
|
-
["Brain", "Lung", "Nervous System", "Clear All"].forEach((expected, i) => {
|
|
54
|
-
expect(optionEls[i].textContent).toMatch(getStartsWithRegex(expected));
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("can select and deselect a filter option", async () => {
|
|
59
|
-
const buttonEl = getButton("BioNetwork");
|
|
60
|
-
fireEvent.click(buttonEl);
|
|
61
|
-
// Wait for menu to open.
|
|
62
|
-
await waitFor(() => expect(getRole("menu")).toBeInTheDocument());
|
|
63
|
-
// Select first option.
|
|
64
|
-
const optionEls = within(getRole("menu")).getAllByRole("button");
|
|
65
|
-
const optionEl = optionEls[0];
|
|
66
|
-
fireEvent.click(optionEl);
|
|
67
|
-
await waitFor(() => expect(optionEl).toHaveClass(MUI_CLASSES.SELECTED));
|
|
68
|
-
// Deselect first option.
|
|
69
|
-
fireEvent.click(optionEl);
|
|
70
|
-
await waitFor(() => expect(optionEl).not.toHaveClass(MUI_CLASSES.SELECTED));
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("clears all filters", async () => {
|
|
74
|
-
const buttonEl = getButton("BioNetwork");
|
|
75
|
-
fireEvent.click(buttonEl);
|
|
76
|
-
// Wait for menu to open.
|
|
77
|
-
await waitFor(() => expect(getRole("menu")).toBeInTheDocument());
|
|
78
|
-
// Select all options.
|
|
79
|
-
const optionEls = within(getRole("menu")).getAllByRole("button");
|
|
80
|
-
optionEls.forEach(async (el) => {
|
|
81
|
-
fireEvent.click(el);
|
|
82
|
-
await waitFor(() => expect(el).toHaveClass(MUI_CLASSES.SELECTED));
|
|
83
|
-
});
|
|
84
|
-
// Select "Clear All" button.
|
|
85
|
-
const clearEl = getButton("Clear All");
|
|
86
|
-
fireEvent.click(clearEl);
|
|
87
|
-
// Wait for options to be deselected.
|
|
88
|
-
await waitFor(() =>
|
|
89
|
-
optionEls.forEach((el) =>
|
|
90
|
-
expect(el).not.toHaveClass(MUI_CLASSES.SELECTED),
|
|
91
|
-
),
|
|
92
|
-
);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("disables Clear All when nothing selected", async () => {
|
|
96
|
-
const buttonEl = getButton("BioNetwork");
|
|
97
|
-
fireEvent.click(buttonEl);
|
|
98
|
-
await waitFor(() => expect(getRole("menu")).toBeInTheDocument());
|
|
99
|
-
expect(getButton("Clear All")).toHaveClass(MUI_CLASSES.DISABLED);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
annotateColumnConfig,
|
|
3
|
-
annotateDefaultCategoryConfig,
|
|
4
|
-
annotateEntityCategoryConfig,
|
|
5
|
-
annotateEntityConfig,
|
|
6
|
-
} from "../src/components/DataDictionary/common/utils";
|
|
7
|
-
import { SiteConfig } from "../src/config/entities";
|
|
8
|
-
|
|
9
|
-
describe("Data Dictionary", () => {
|
|
10
|
-
it("annotates entity", () => {
|
|
11
|
-
const key = "entity";
|
|
12
|
-
|
|
13
|
-
// Create annotation for column and add to dummy annotation map.
|
|
14
|
-
const annotation = {
|
|
15
|
-
description: "description for entity",
|
|
16
|
-
label: "entity",
|
|
17
|
-
};
|
|
18
|
-
const annotationsByKey = {
|
|
19
|
-
[key]: annotation,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// Create dummy site config.
|
|
23
|
-
const siteConfig = {
|
|
24
|
-
entities: [
|
|
25
|
-
{
|
|
26
|
-
key,
|
|
27
|
-
list: {
|
|
28
|
-
columns: [{ id: "col150" }, { id: "col1" }],
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
} as unknown as SiteConfig;
|
|
33
|
-
|
|
34
|
-
// Annotate
|
|
35
|
-
annotateEntityConfig(siteConfig, annotationsByKey);
|
|
36
|
-
|
|
37
|
-
// Confirm entity is annotated.
|
|
38
|
-
const entity = siteConfig.entities[0];
|
|
39
|
-
expect(entity.annotation).toEqual(annotation);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("annotates entity category config", () => {
|
|
43
|
-
const key = "filter0";
|
|
44
|
-
|
|
45
|
-
// Create annotation for column and add to dummy annotation map.
|
|
46
|
-
const annotation = {
|
|
47
|
-
description: "description for filter 0",
|
|
48
|
-
label: "filter 0",
|
|
49
|
-
};
|
|
50
|
-
const annotationsByKey = {
|
|
51
|
-
[key]: annotation,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
// Create dummy site config.
|
|
55
|
-
const siteConfig = {
|
|
56
|
-
entities: [
|
|
57
|
-
{
|
|
58
|
-
categoryGroupConfig: {
|
|
59
|
-
categoryGroups: [
|
|
60
|
-
{
|
|
61
|
-
categoryConfigs: [
|
|
62
|
-
{
|
|
63
|
-
key,
|
|
64
|
-
label: "filter 0",
|
|
65
|
-
},
|
|
66
|
-
],
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
} as unknown as SiteConfig;
|
|
73
|
-
|
|
74
|
-
// Annotate
|
|
75
|
-
annotateEntityCategoryConfig(siteConfig, annotationsByKey);
|
|
76
|
-
|
|
77
|
-
// Confirm filter is annotated.
|
|
78
|
-
const categoryConfig =
|
|
79
|
-
siteConfig.entities[0].categoryGroupConfig?.categoryGroups[0]
|
|
80
|
-
.categoryConfigs[0];
|
|
81
|
-
expect(categoryConfig?.annotation).toEqual(annotation);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("annotates default category config", () => {
|
|
85
|
-
const key = "filter0";
|
|
86
|
-
|
|
87
|
-
// Create annotation for column and add to dummy annotation map.
|
|
88
|
-
const annotation = {
|
|
89
|
-
description: "description for filter 0",
|
|
90
|
-
label: "filter 0",
|
|
91
|
-
};
|
|
92
|
-
const annotationsByKey = {
|
|
93
|
-
[key]: annotation,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// Create dummy site config.
|
|
97
|
-
const siteConfig = {
|
|
98
|
-
categoryGroupConfig: {
|
|
99
|
-
categoryGroups: [
|
|
100
|
-
{
|
|
101
|
-
categoryConfigs: [
|
|
102
|
-
{
|
|
103
|
-
key,
|
|
104
|
-
label: "filter 0",
|
|
105
|
-
},
|
|
106
|
-
],
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
|
-
},
|
|
110
|
-
} as unknown as SiteConfig;
|
|
111
|
-
|
|
112
|
-
// Annotate
|
|
113
|
-
annotateDefaultCategoryConfig(siteConfig, annotationsByKey);
|
|
114
|
-
|
|
115
|
-
// Confirm filter is annotated.
|
|
116
|
-
const categoryConfig =
|
|
117
|
-
siteConfig.categoryGroupConfig?.categoryGroups[0].categoryConfigs[0];
|
|
118
|
-
expect(categoryConfig?.annotation).toEqual(annotation);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("annotates column", () => {
|
|
122
|
-
const key = "col0";
|
|
123
|
-
|
|
124
|
-
// Create annotation for column and add to dummy annotation map.
|
|
125
|
-
const annotation = {
|
|
126
|
-
description: "description for column 0",
|
|
127
|
-
label: "column 0",
|
|
128
|
-
};
|
|
129
|
-
const annotationsByKey = {
|
|
130
|
-
[key]: annotation,
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
// Create dummy site config.
|
|
134
|
-
const siteConfig = {
|
|
135
|
-
entities: [
|
|
136
|
-
{
|
|
137
|
-
list: {
|
|
138
|
-
columns: [{ id: key }, { id: "col1" }],
|
|
139
|
-
},
|
|
140
|
-
name: "entity",
|
|
141
|
-
},
|
|
142
|
-
],
|
|
143
|
-
} as unknown as SiteConfig;
|
|
144
|
-
|
|
145
|
-
// Annotate
|
|
146
|
-
annotateColumnConfig(siteConfig, annotationsByKey);
|
|
147
|
-
|
|
148
|
-
// Confirm column 0 is annotated and column 1 is not.
|
|
149
|
-
const columns = siteConfig.entities[0].list.columns ?? [];
|
|
150
|
-
expect((columns[0]?.meta as any)?.annotation).toEqual(annotation);
|
|
151
|
-
expect((columns[1]?.meta as any)?.annotation).toBeUndefined();
|
|
152
|
-
});
|
|
153
|
-
});
|