@truedat/bg 8.11.0 → 8.11.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/package.json +16 -16
- package/src/concepts/components/ConceptRoutes.js +0 -2
- package/src/concepts/components/__tests__/ConceptRow.spec.js +36 -1
- package/src/concepts/components/__tests__/__snapshots__/ConceptLinksManagement.spec.js.snap +3 -1
- package/src/concepts/components/__tests__/__snapshots__/ConceptsPanel.spec.js.snap +3 -1
- package/src/concepts/relations/components/ConceptSelector.js +6 -2
- package/src/concepts/relations/components/__tests__/ConceptSelector.spec.js +24 -0
- package/src/concepts/relations/components/__tests__/__snapshots__/ConceptRelationForm.spec.js.snap +6 -2
- package/src/concepts/relations/components/__tests__/__snapshots__/ConceptSelector.spec.js.snap +20 -2
- package/src/concepts/selectors/getConceptColumns.js +8 -1
- package/src/taxonomy/components/DomainPath.js +86 -0
- package/src/taxonomy/components/__tests__/DomainPath.spec.js +179 -0
- package/src/taxonomy/components/__tests__/__snapshots__/DomainPath.spec.js.snap +26 -0
- package/src/taxonomy/styles/DomainPath.less +25 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/bg",
|
|
3
|
-
"version": "8.11.
|
|
3
|
+
"version": "8.11.3",
|
|
4
4
|
"description": "Truedat Web Business Glossary",
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css",
|
|
@@ -53,42 +53,42 @@
|
|
|
53
53
|
]
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@testing-library/dom": "^10.4.
|
|
57
|
-
"@testing-library/jest-dom": "^6.6.
|
|
58
|
-
"@testing-library/react": "^16.3.
|
|
59
|
-
"@testing-library/user-event": "^14.6.
|
|
60
|
-
"@truedat/test": "8.11.
|
|
56
|
+
"@testing-library/dom": "^10.4.1",
|
|
57
|
+
"@testing-library/jest-dom": "^6.6.4",
|
|
58
|
+
"@testing-library/react": "^16.3.3",
|
|
59
|
+
"@testing-library/user-event": "^14.6.7",
|
|
60
|
+
"@truedat/test": "8.11.3",
|
|
61
61
|
"identity-obj-proxy": "^3.0.0",
|
|
62
62
|
"jest": "^29.7.0",
|
|
63
63
|
"redux-saga-test-plan": "^4.0.6"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@apollo/client": "^3.13.
|
|
67
|
-
"axios": "^1.15.
|
|
66
|
+
"@apollo/client": "^3.13.9",
|
|
67
|
+
"axios": "^1.15.2",
|
|
68
68
|
"file-saver": "^2.0.5",
|
|
69
69
|
"graphql": "^16.11.0",
|
|
70
70
|
"is-hotkey": "^0.2.0",
|
|
71
71
|
"is-url": "^1.2.4",
|
|
72
|
-
"lodash": "^4.17.
|
|
72
|
+
"lodash": "^4.17.23",
|
|
73
73
|
"moment": "^2.30.1",
|
|
74
74
|
"path-to-regexp": "^8.2.0",
|
|
75
75
|
"prop-types": "^15.8.1",
|
|
76
76
|
"query-string": "^7.1.3",
|
|
77
|
-
"react": "^19.2.
|
|
77
|
+
"react": "^19.2.8",
|
|
78
78
|
"react-csv": "^2.2.2",
|
|
79
|
-
"react-dom": "^19.2.
|
|
79
|
+
"react-dom": "^19.2.8",
|
|
80
80
|
"react-dropzone": "^14.3.8",
|
|
81
81
|
"react-hook-form": "^7.56.4",
|
|
82
|
-
"react-intl": "^7.1.
|
|
83
|
-
"react-moment": "^1.1.
|
|
82
|
+
"react-intl": "^7.1.14",
|
|
83
|
+
"react-moment": "^1.1.4",
|
|
84
84
|
"react-redux": "^9.2.0",
|
|
85
|
-
"react-router": "^8.3.
|
|
85
|
+
"react-router": "^8.3.1",
|
|
86
86
|
"redux": "^5.0.1",
|
|
87
87
|
"redux-saga": "^1.3.0",
|
|
88
88
|
"redux-saga-routines": "^3.2.3",
|
|
89
89
|
"reselect": "^5.1.1",
|
|
90
90
|
"semantic-ui-react": "^3.0.0-beta.2",
|
|
91
|
-
"swr": "^2.3.
|
|
91
|
+
"swr": "^2.3.8"
|
|
92
92
|
},
|
|
93
|
-
"gitHead": "
|
|
93
|
+
"gitHead": "c0d3c1ed5c9480e6207a6e696d8941043c34de2f"
|
|
94
94
|
}
|
|
@@ -15,8 +15,6 @@ import {
|
|
|
15
15
|
CONCEPTS_PENDING,
|
|
16
16
|
CONCEPTS_DEPRECATED,
|
|
17
17
|
CONCEPTS_SUBSCOPE,
|
|
18
|
-
CONCEPTS_SIDEMENU_MANAGEMENT_LINKS,
|
|
19
|
-
CONCEPTS_SIDEMENU_MANAGEMENT_UPLOADS,
|
|
20
18
|
CONCEPTS_SIDEMENU_SUBSCOPE,
|
|
21
19
|
CONCEPTS_SIDEMENU_SUBSCOPE_PENDING,
|
|
22
20
|
CONCEPTS_SIDEMENU_SUBSCOPE_DEPRECATED,
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import { render } from "@truedat/test/render";
|
|
1
|
+
import { render, waitForLoad } from "@truedat/test/render";
|
|
2
2
|
import { ConceptRow } from "../ConceptRow";
|
|
3
3
|
import { getConceptColumns } from "../../selectors";
|
|
4
4
|
|
|
5
|
+
jest.mock("../../../hooks/useDomains", () => ({
|
|
6
|
+
useDomains: () => ({ data: [] }),
|
|
7
|
+
}));
|
|
8
|
+
|
|
5
9
|
describe("<ConceptRow />", () => {
|
|
6
10
|
it("matches the latest snapshot", () => {
|
|
7
11
|
const columns = getConceptColumns({ conceptColumns: null }).concepts;
|
|
@@ -44,4 +48,35 @@ describe("<ConceptRow />", () => {
|
|
|
44
48
|
const link = wrapper.getByText("foo");
|
|
45
49
|
expect(link).toHaveAttribute("href", "/concepts/12/versions/42");
|
|
46
50
|
});
|
|
51
|
+
|
|
52
|
+
it("renders the full domain path in the domain title", async () => {
|
|
53
|
+
const columns = getConceptColumns({ conceptColumns: null }).concepts;
|
|
54
|
+
const concept = {
|
|
55
|
+
business_concept_id: "12",
|
|
56
|
+
id: "42",
|
|
57
|
+
name: "foo",
|
|
58
|
+
status: "draft",
|
|
59
|
+
domain: { id: 122, name: "Lollipop City" },
|
|
60
|
+
domain_parents: [
|
|
61
|
+
{ id: 122, name: "Lollipop City" },
|
|
62
|
+
{ id: 120, name: "Happyland" },
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
const rendered = render(
|
|
66
|
+
<table>
|
|
67
|
+
<tbody>
|
|
68
|
+
<ConceptRow concept={concept} columns={columns} />
|
|
69
|
+
</tbody>
|
|
70
|
+
</table>,
|
|
71
|
+
);
|
|
72
|
+
await waitForLoad(rendered);
|
|
73
|
+
const domain = rendered.container.querySelector(
|
|
74
|
+
'span[data-domain-path="Happyland › Lollipop City"]',
|
|
75
|
+
);
|
|
76
|
+
expect(domain).toHaveTextContent("Lollipop City");
|
|
77
|
+
expect(
|
|
78
|
+
domain.querySelector("i.domain-path-info-icon.info.circle.icon"),
|
|
79
|
+
).not.toBeNull();
|
|
80
|
+
expect(rendered.container.querySelector("td[title]")).toBeNull();
|
|
81
|
+
});
|
|
47
82
|
});
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
SearchContextProvider,
|
|
16
16
|
useSearchContext,
|
|
17
17
|
} from "@truedat/core/search/SearchContext";
|
|
18
|
+
import { DomainTooltip } from "../../../taxonomy/components/DomainPath";
|
|
18
19
|
import ConceptsPagination from "../../components/ConceptsPagination";
|
|
19
20
|
import { mapStatusColor } from "../../constants/mappings";
|
|
20
21
|
import { translations } from "../../utils/filterOptions";
|
|
@@ -46,7 +47,10 @@ const ConceptSelectorRow = ({
|
|
|
46
47
|
positive,
|
|
47
48
|
extraColumns = [],
|
|
48
49
|
}) => {
|
|
49
|
-
const { name, status, domain } = concept;
|
|
50
|
+
const { name, status, domain, domain_parents } = concept;
|
|
51
|
+
const domainWithParents = domain
|
|
52
|
+
? { ...domain, parents: [...(domain_parents || [])].reverse() }
|
|
53
|
+
: domain;
|
|
50
54
|
return (
|
|
51
55
|
<Table.Row
|
|
52
56
|
disabled={disabled}
|
|
@@ -55,7 +59,7 @@ const ConceptSelectorRow = ({
|
|
|
55
59
|
positive={positive}
|
|
56
60
|
>
|
|
57
61
|
<Table.Cell content={name} />
|
|
58
|
-
<Table.Cell content={domain
|
|
62
|
+
<Table.Cell content={<DomainTooltip domain={domainWithParents} />} />
|
|
59
63
|
<Table.Cell
|
|
60
64
|
content={
|
|
61
65
|
<Label color={mapStatusColor[status]}>
|
|
@@ -8,6 +8,10 @@ const concept = {
|
|
|
8
8
|
id: 1,
|
|
9
9
|
name: "foo",
|
|
10
10
|
domain: { id: 1, name: "bar" },
|
|
11
|
+
domain_parents: [
|
|
12
|
+
{ id: 1, name: "bar" },
|
|
13
|
+
{ id: 0, name: "root" },
|
|
14
|
+
],
|
|
11
15
|
status: "draft",
|
|
12
16
|
business_concept_id: 1,
|
|
13
17
|
last_change_at: "2020-01-01T00:00:00.000Z",
|
|
@@ -19,6 +23,10 @@ const data = {
|
|
|
19
23
|
id: 1,
|
|
20
24
|
name: "foo",
|
|
21
25
|
domain: { id: 1, name: "bar" },
|
|
26
|
+
domain_parents: [
|
|
27
|
+
{ id: 1, name: "bar" },
|
|
28
|
+
{ id: 0, name: "root" },
|
|
29
|
+
],
|
|
22
30
|
status: "draft",
|
|
23
31
|
business_concept_id: 1,
|
|
24
32
|
last_change_at: "2020-01-01T00:00:00.000Z",
|
|
@@ -65,6 +73,10 @@ jest.mock("@truedat/bg/concepts/hooks/useConcepts", () => {
|
|
|
65
73
|
|
|
66
74
|
jest.mock("@truedat/core/hooks/useUserFilters");
|
|
67
75
|
|
|
76
|
+
jest.mock("@truedat/bg/hooks/useDomains", () => ({
|
|
77
|
+
useDomains: () => ({ data: [] }),
|
|
78
|
+
}));
|
|
79
|
+
|
|
68
80
|
describe("<ConceptSelector />", () => {
|
|
69
81
|
const handleConceptSelected = jest.fn();
|
|
70
82
|
|
|
@@ -127,6 +139,10 @@ describe("<ConceptSelectorTable />", () => {
|
|
|
127
139
|
id: 1,
|
|
128
140
|
name: "foo",
|
|
129
141
|
domain: { id: 1, name: "bar" },
|
|
142
|
+
domain_parents: [
|
|
143
|
+
{ id: 1, name: "bar" },
|
|
144
|
+
{ id: 0, name: "root" },
|
|
145
|
+
],
|
|
130
146
|
status: "draft",
|
|
131
147
|
business_concept_id: 1,
|
|
132
148
|
last_change_at: "2020-01-01T00:00:00.000Z",
|
|
@@ -171,6 +187,14 @@ describe("<ConceptSelectorTable />", () => {
|
|
|
171
187
|
expect(rendered.container).toMatchSnapshot();
|
|
172
188
|
});
|
|
173
189
|
|
|
190
|
+
it("renders the domain path tooltip", async () => {
|
|
191
|
+
const rendered = render(<ConceptSelectorTable {...props} />);
|
|
192
|
+
await waitForLoad(rendered);
|
|
193
|
+
expect(
|
|
194
|
+
rendered.container.querySelector('span[data-domain-path="root › bar"]'),
|
|
195
|
+
).toHaveTextContent("bar");
|
|
196
|
+
});
|
|
197
|
+
|
|
174
198
|
test("renders Message when there are no concepts and no selectedConcept", () => {
|
|
175
199
|
const rendered = render(
|
|
176
200
|
<ConceptSelectorTable
|
package/src/concepts/relations/components/__tests__/__snapshots__/ConceptRelationForm.spec.js.snap
CHANGED
|
@@ -222,7 +222,9 @@ exports[`<ConceptRelationForm /> matches the latest snapshot 1`] = `
|
|
|
222
222
|
<td
|
|
223
223
|
class=""
|
|
224
224
|
>
|
|
225
|
-
|
|
225
|
+
<span>
|
|
226
|
+
foo domain
|
|
227
|
+
</span>
|
|
226
228
|
</td>
|
|
227
229
|
<td
|
|
228
230
|
class=""
|
|
@@ -245,7 +247,9 @@ exports[`<ConceptRelationForm /> matches the latest snapshot 1`] = `
|
|
|
245
247
|
<td
|
|
246
248
|
class=""
|
|
247
249
|
>
|
|
248
|
-
|
|
250
|
+
<span>
|
|
251
|
+
foo domain
|
|
252
|
+
</span>
|
|
249
253
|
</td>
|
|
250
254
|
<td
|
|
251
255
|
class=""
|
package/src/concepts/relations/components/__tests__/__snapshots__/ConceptSelector.spec.js.snap
CHANGED
|
@@ -184,7 +184,16 @@ exports[`<ConceptSelector /> matches the latest snapshot 1`] = `
|
|
|
184
184
|
<td
|
|
185
185
|
class=""
|
|
186
186
|
>
|
|
187
|
-
|
|
187
|
+
<span
|
|
188
|
+
class="domain-path-trigger"
|
|
189
|
+
data-domain-path="root › bar"
|
|
190
|
+
>
|
|
191
|
+
bar
|
|
192
|
+
<i
|
|
193
|
+
aria-label="Subdomain"
|
|
194
|
+
class="blue info circle icon domain-path-info-icon"
|
|
195
|
+
/>
|
|
196
|
+
</span>
|
|
188
197
|
</td>
|
|
189
198
|
<td
|
|
190
199
|
class=""
|
|
@@ -249,7 +258,16 @@ exports[`<ConceptSelectorTable /> matches the latest snapshot 1`] = `
|
|
|
249
258
|
<td
|
|
250
259
|
class=""
|
|
251
260
|
>
|
|
252
|
-
|
|
261
|
+
<span
|
|
262
|
+
class="domain-path-trigger"
|
|
263
|
+
data-domain-path="root › bar"
|
|
264
|
+
>
|
|
265
|
+
bar
|
|
266
|
+
<i
|
|
267
|
+
aria-label="Subdomain"
|
|
268
|
+
class="blue info circle icon domain-path-info-icon"
|
|
269
|
+
/>
|
|
270
|
+
</span>
|
|
253
271
|
</td>
|
|
254
272
|
<td
|
|
255
273
|
class=""
|
|
@@ -5,6 +5,7 @@ import { createSelector } from "reselect";
|
|
|
5
5
|
import Moment from "react-moment";
|
|
6
6
|
import { Link } from "react-router";
|
|
7
7
|
import { linkTo } from "@truedat/core/routes";
|
|
8
|
+
import { DomainTooltip } from "../../taxonomy/components/DomainPath";
|
|
8
9
|
|
|
9
10
|
const ConceptLink = (concept) =>
|
|
10
11
|
concept?.id ? (
|
|
@@ -20,6 +21,11 @@ const dateDecorator = ({ date }) => (
|
|
|
20
21
|
<Moment locale="es" date={date} format="YYYY-MM-DD HH:mm" />
|
|
21
22
|
);
|
|
22
23
|
|
|
24
|
+
const domainSelector = ({ domain, domain_parents }) =>
|
|
25
|
+
domain
|
|
26
|
+
? { ...domain, parents: [...(domain_parents || [])].reverse() }
|
|
27
|
+
: domain;
|
|
28
|
+
|
|
23
29
|
export const defaultConceptColumns = [
|
|
24
30
|
{
|
|
25
31
|
name: "name",
|
|
@@ -31,7 +37,8 @@ export const defaultConceptColumns = [
|
|
|
31
37
|
{
|
|
32
38
|
name: "domain",
|
|
33
39
|
sort: { name: "domain.name.sort" },
|
|
34
|
-
fieldSelector:
|
|
40
|
+
fieldSelector: domainSelector,
|
|
41
|
+
fieldDecorator: (domain) => <DomainTooltip domain={domain} />,
|
|
35
42
|
width: 3,
|
|
36
43
|
},
|
|
37
44
|
{
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { Icon, Popup } from "semantic-ui-react";
|
|
4
|
+
import { ascendants } from "@truedat/core/services/tree";
|
|
5
|
+
import { useDomains } from "../../hooks/useDomains";
|
|
6
|
+
|
|
7
|
+
export { useDomains };
|
|
8
|
+
|
|
9
|
+
const domainKey = ({ id, name } = {}) =>
|
|
10
|
+
_.isNil(id) ? `name:${name}` : `id:${id}`;
|
|
11
|
+
|
|
12
|
+
const uniqueDomains = (domains) => {
|
|
13
|
+
const keys = new Set();
|
|
14
|
+
return domains.filter((domain) => {
|
|
15
|
+
if (_.isEmpty(domain)) return false;
|
|
16
|
+
const key = domainKey(domain);
|
|
17
|
+
if (keys.has(key)) return false;
|
|
18
|
+
keys.add(key);
|
|
19
|
+
return true;
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const sameDomain = (left, right) => {
|
|
24
|
+
if (!_.isNil(left?.id) && !_.isNil(right?.id)) {
|
|
25
|
+
return `${left.id}` === `${right.id}`;
|
|
26
|
+
}
|
|
27
|
+
return left?.name === right?.name;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const pathFromParents = (domain) => {
|
|
31
|
+
const parents = domain?.parents;
|
|
32
|
+
return _.isEmpty(parents)
|
|
33
|
+
? []
|
|
34
|
+
: uniqueDomains([
|
|
35
|
+
...parents.filter((parent) => !sameDomain(parent, domain)),
|
|
36
|
+
domain,
|
|
37
|
+
]);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const useDomainPath = (domain) => {
|
|
41
|
+
const { data: domains } = useDomains();
|
|
42
|
+
if (_.isEmpty(domain)) return [];
|
|
43
|
+
|
|
44
|
+
if (!_.isNil(domain.id) && !_.isEmpty(domains)) {
|
|
45
|
+
const self = domains.find(({ id }) => `${id}` === `${domain.id}`);
|
|
46
|
+
if (self) {
|
|
47
|
+
return ascendants(self.id, domains)
|
|
48
|
+
.map((id) => domains.find((item) => `${item.id}` === `${id}`))
|
|
49
|
+
.filter(Boolean);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return pathFromParents(domain);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const DomainTooltip = ({ domain }) => {
|
|
57
|
+
const path = useDomainPath(domain);
|
|
58
|
+
if (_.isEmpty(domain)) return null;
|
|
59
|
+
if (path.length <= 1) return <span>{domain.name}</span>;
|
|
60
|
+
|
|
61
|
+
const content = _.flow(_.map("name"), _.join(" › "))(path);
|
|
62
|
+
return (
|
|
63
|
+
<Popup
|
|
64
|
+
className="domain-path-popup"
|
|
65
|
+
content={content}
|
|
66
|
+
trigger={
|
|
67
|
+
<span className="domain-path-trigger" data-domain-path={content}>
|
|
68
|
+
{domain.name}
|
|
69
|
+
<Icon
|
|
70
|
+
aria-label="Subdomain"
|
|
71
|
+
className="domain-path-info-icon"
|
|
72
|
+
color="blue"
|
|
73
|
+
name="info circle"
|
|
74
|
+
/>
|
|
75
|
+
</span>
|
|
76
|
+
}
|
|
77
|
+
on="hover"
|
|
78
|
+
position="top center"
|
|
79
|
+
basic
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
DomainTooltip.propTypes = {
|
|
85
|
+
domain: PropTypes.object,
|
|
86
|
+
};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { render, waitForLoad } from "@truedat/test/render";
|
|
2
|
+
import { renderHook } from "@testing-library/react";
|
|
3
|
+
import { useDomainPath, DomainTooltip } from "../DomainPath";
|
|
4
|
+
|
|
5
|
+
const mockDomains = [
|
|
6
|
+
{ id: 1, name: "Root" },
|
|
7
|
+
{ id: 2, name: "Level1", parent_id: 1 },
|
|
8
|
+
{ id: 3, name: "Level2", parent_id: 2 },
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
jest.mock("../../../hooks/useDomains", () => ({
|
|
12
|
+
useDomains: () => ({ data: mockDomains }),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
describe("useDomainPath", () => {
|
|
16
|
+
it("returns empty array for null domain", () => {
|
|
17
|
+
const { result } = renderHook(() => useDomainPath(null));
|
|
18
|
+
expect(result.current).toEqual([]);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("returns empty array for undefined domain", () => {
|
|
22
|
+
const { result } = renderHook(() => useDomainPath(undefined));
|
|
23
|
+
expect(result.current).toEqual([]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("returns empty array for empty object", () => {
|
|
27
|
+
const { result } = renderHook(() => useDomainPath({}));
|
|
28
|
+
expect(result.current).toEqual([]);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("returns path from canonical tree when domain id exists", () => {
|
|
32
|
+
const domain = { id: 3, name: "Level2" };
|
|
33
|
+
const { result } = renderHook(() => useDomainPath(domain));
|
|
34
|
+
expect(result.current).toHaveLength(3);
|
|
35
|
+
expect(result.current.map((d) => d.name)).toEqual([
|
|
36
|
+
"Root",
|
|
37
|
+
"Level1",
|
|
38
|
+
"Level2",
|
|
39
|
+
]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("returns path from parents when domain id not in tree", () => {
|
|
43
|
+
const domain = {
|
|
44
|
+
id: 999,
|
|
45
|
+
name: "Unknown",
|
|
46
|
+
parents: [
|
|
47
|
+
{ id: 1, name: "Root" },
|
|
48
|
+
{ id: 2, name: "Level1" },
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
const { result } = renderHook(() => useDomainPath(domain));
|
|
52
|
+
expect(result.current).toHaveLength(3);
|
|
53
|
+
expect(result.current.map((d) => d.name)).toEqual([
|
|
54
|
+
"Root",
|
|
55
|
+
"Level1",
|
|
56
|
+
"Unknown",
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("returns path from parents when domain has no id", () => {
|
|
61
|
+
const domain = {
|
|
62
|
+
name: "NoId",
|
|
63
|
+
parents: [
|
|
64
|
+
{ id: 1, name: "Root" },
|
|
65
|
+
{ id: 2, name: "Level1" },
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
const { result } = renderHook(() => useDomainPath(domain));
|
|
69
|
+
expect(result.current).toHaveLength(3);
|
|
70
|
+
expect(result.current.map((d) => d.name)).toEqual([
|
|
71
|
+
"Root",
|
|
72
|
+
"Level1",
|
|
73
|
+
"NoId",
|
|
74
|
+
]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("returns empty array when parents are empty", () => {
|
|
78
|
+
const domain = { id: 999, name: "Orphan", parents: [] };
|
|
79
|
+
const { result } = renderHook(() => useDomainPath(domain));
|
|
80
|
+
expect(result.current).toEqual([]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("deduplicates domains in path", () => {
|
|
84
|
+
const domain = {
|
|
85
|
+
id: 2,
|
|
86
|
+
name: "Level1",
|
|
87
|
+
parents: [
|
|
88
|
+
{ id: 1, name: "Root" },
|
|
89
|
+
{ id: 2, name: "Level1" },
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
const { result } = renderHook(() => useDomainPath(domain));
|
|
93
|
+
expect(result.current).toHaveLength(2);
|
|
94
|
+
expect(result.current.map((d) => d.name)).toEqual(["Root", "Level1"]);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe("DomainTooltip", () => {
|
|
99
|
+
it("returns null for null domain", async () => {
|
|
100
|
+
const rendered = render(<DomainTooltip domain={null} />);
|
|
101
|
+
await waitForLoad(rendered);
|
|
102
|
+
expect(rendered.container.firstChild).toBeNull();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("returns null for undefined domain", async () => {
|
|
106
|
+
const rendered = render(<DomainTooltip domain={undefined} />);
|
|
107
|
+
await waitForLoad(rendered);
|
|
108
|
+
expect(rendered.container.firstChild).toBeNull();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("returns null for empty object", async () => {
|
|
112
|
+
const rendered = render(<DomainTooltip domain={{}} />);
|
|
113
|
+
await waitForLoad(rendered);
|
|
114
|
+
expect(rendered.container.firstChild).toBeNull();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("renders simple span when path has only one element", async () => {
|
|
118
|
+
const domain = { id: 1, name: "Root" };
|
|
119
|
+
const rendered = render(<DomainTooltip domain={domain} />);
|
|
120
|
+
await waitForLoad(rendered);
|
|
121
|
+
const span = rendered.container.querySelector("span");
|
|
122
|
+
expect(span).toHaveTextContent("Root");
|
|
123
|
+
expect(span.getAttribute("data-domain-path")).toBeNull();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("renders Popup with full path when path has multiple elements", async () => {
|
|
127
|
+
const domain = { id: 3, name: "Level2" };
|
|
128
|
+
const rendered = render(<DomainTooltip domain={domain} />);
|
|
129
|
+
await waitForLoad(rendered);
|
|
130
|
+
const trigger = rendered.container.querySelector(
|
|
131
|
+
'span[data-domain-path="Root › Level1 › Level2"]',
|
|
132
|
+
);
|
|
133
|
+
expect(trigger).not.toBeNull();
|
|
134
|
+
expect(trigger).toHaveTextContent("Level2");
|
|
135
|
+
expect(
|
|
136
|
+
trigger.querySelector("i.domain-path-info-icon.info.circle.icon"),
|
|
137
|
+
).not.toBeNull();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("renders Popup with path from parents when domain not in tree", async () => {
|
|
141
|
+
const domain = {
|
|
142
|
+
id: 999,
|
|
143
|
+
name: "Unknown",
|
|
144
|
+
parents: [
|
|
145
|
+
{ id: 1, name: "Root" },
|
|
146
|
+
{ id: 2, name: "Level1" },
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
const rendered = render(<DomainTooltip domain={domain} />);
|
|
150
|
+
await waitForLoad(rendered);
|
|
151
|
+
const trigger = rendered.container.querySelector(
|
|
152
|
+
'span[data-domain-path="Root › Level1 › Unknown"]',
|
|
153
|
+
);
|
|
154
|
+
expect(trigger).not.toBeNull();
|
|
155
|
+
expect(trigger).toHaveTextContent("Unknown");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe("snapshots", () => {
|
|
159
|
+
it("matches snapshot for null domain", async () => {
|
|
160
|
+
const rendered = render(<DomainTooltip domain={null} />);
|
|
161
|
+
await waitForLoad(rendered);
|
|
162
|
+
expect(rendered.container).toMatchSnapshot();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("matches snapshot for single element path", async () => {
|
|
166
|
+
const domain = { id: 1, name: "Root" };
|
|
167
|
+
const rendered = render(<DomainTooltip domain={domain} />);
|
|
168
|
+
await waitForLoad(rendered);
|
|
169
|
+
expect(rendered.container).toMatchSnapshot();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("matches snapshot for multi-element path", async () => {
|
|
173
|
+
const domain = { id: 3, name: "Level2" };
|
|
174
|
+
const rendered = render(<DomainTooltip domain={domain} />);
|
|
175
|
+
await waitForLoad(rendered);
|
|
176
|
+
expect(rendered.container).toMatchSnapshot();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`DomainTooltip snapshots matches snapshot for multi-element path 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<span
|
|
6
|
+
class="domain-path-trigger"
|
|
7
|
+
data-domain-path="Root › Level1 › Level2"
|
|
8
|
+
>
|
|
9
|
+
Level2
|
|
10
|
+
<i
|
|
11
|
+
aria-label="Subdomain"
|
|
12
|
+
class="blue info circle icon domain-path-info-icon"
|
|
13
|
+
/>
|
|
14
|
+
</span>
|
|
15
|
+
</div>
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
exports[`DomainTooltip snapshots matches snapshot for null domain 1`] = `<div />`;
|
|
19
|
+
|
|
20
|
+
exports[`DomainTooltip snapshots matches snapshot for single element path 1`] = `
|
|
21
|
+
<div>
|
|
22
|
+
<span>
|
|
23
|
+
Root
|
|
24
|
+
</span>
|
|
25
|
+
</div>
|
|
26
|
+
`;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.ui.popup.domain-path-popup {
|
|
2
|
+
--domain-path-bg: #1f1f1f;
|
|
3
|
+
--domain-path-glow: rgba(255, 255, 255, 0.35);
|
|
4
|
+
|
|
5
|
+
max-width: none !important;
|
|
6
|
+
width: max-content;
|
|
7
|
+
white-space: nowrap !important;
|
|
8
|
+
padding: 0.6em 1em !important;
|
|
9
|
+
background: var(--domain-path-bg) !important;
|
|
10
|
+
color: #fff !important;
|
|
11
|
+
border-radius: 10px !important;
|
|
12
|
+
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35) !important;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.ui.popup.domain-path-popup::before {
|
|
16
|
+
border-color: var(--domain-path-bg) transparent transparent !important;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.domain-path-trigger:hover {
|
|
20
|
+
text-shadow: 0 0 8px var(--domain-path-glow);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.domain-path-trigger .domain-path-info-icon {
|
|
24
|
+
margin-left: 0.4em;
|
|
25
|
+
}
|