@truedat/lm 8.7.3 → 8.7.5
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 +3 -3
- package/src/components/ConfirmDeleteRelation.js +14 -2
- package/src/components/LinksPane.js +116 -88
- package/src/components/LinksPaneDataCell.js +72 -0
- package/src/components/LinksPaneRow.js +18 -0
- package/src/components/LinksPaneTypeFilter.js +82 -0
- package/src/components/__tests__/ConfirmDeleteRelation.spec.js +9 -0
- package/src/components/__tests__/LinksPane.spec.js +60 -95
- package/src/components/__tests__/LinksPaneDataCell.spec.js +192 -0
- package/src/components/__tests__/LinksPaneRow.spec.js +94 -0
- package/src/components/__tests__/LinksPaneTypeFilter.spec.js +164 -0
- package/src/components/__tests__/__snapshots__/ConfirmDeleteRelation.spec.js.snap +2 -2
- package/src/components/__tests__/__snapshots__/ImplementationLinks.spec.js.snap +4 -4
- package/src/components/__tests__/__snapshots__/LinksPane.spec.js.snap +98 -40
- package/src/hooks/__tests__/useFilteredLinks.spec.js +116 -0
- package/src/hooks/__tests__/useSortedLinks.spec.js +129 -0
- package/src/hooks/__tests__/useStructureNotes.spec.js +138 -0
- package/src/hooks/__tests__/useTruncatedPopup.spec.js +109 -0
- package/src/hooks/useFilteredLinks.js +98 -0
- package/src/hooks/useSortedLinks.js +8 -0
- package/src/hooks/useStructureNotes.js +52 -0
- package/src/hooks/useTruncatedPopup.js +30 -0
- package/src/styles/LinksPane.less +117 -0
- package/src/styles/RelationActions.less +7 -0
- package/src/utils/__tests__/linksPaneHelpers.spec.js +316 -0
- package/src/utils/linksPaneHelpers.js +164 -0
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import { waitFor } from "@testing-library/react";
|
|
2
|
+
import { fireEvent, waitFor } from "@testing-library/react";
|
|
3
3
|
import { render, waitForLoad } from "@truedat/test/render";
|
|
4
4
|
import { useDataStructureSearch } from "@truedat/dd/hooks/useStructures";
|
|
5
5
|
import { LinksPane } from "../LinksPane";
|
|
6
6
|
|
|
7
|
-
jest.mock(
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
jest.mock(
|
|
8
|
+
"@truedat/dd/hooks/useStructures",
|
|
9
|
+
() => ({
|
|
10
|
+
useDataStructureSearch: jest.fn(),
|
|
11
|
+
}),
|
|
12
|
+
{ virtual: true },
|
|
13
|
+
);
|
|
10
14
|
|
|
11
15
|
describe("<LinksPane />", () => {
|
|
12
16
|
const renderOpts = {
|
|
@@ -17,6 +21,12 @@ describe("<LinksPane />", () => {
|
|
|
17
21
|
"implementationRelation.table.title": "Relation Title",
|
|
18
22
|
"implementationRelation.master.table.title": "Master title",
|
|
19
23
|
"concept.column": "column",
|
|
24
|
+
"concept.name": "Name",
|
|
25
|
+
"concept.relation.relation_type_name": "Relation type",
|
|
26
|
+
"concept.type": "Type",
|
|
27
|
+
"conceptRelations.relationType.main": "Main",
|
|
28
|
+
"conceptRelations.relationType.secondary": "Secondary",
|
|
29
|
+
"filter.empty": "Empty",
|
|
20
30
|
},
|
|
21
31
|
},
|
|
22
32
|
};
|
|
@@ -44,76 +54,32 @@ describe("<LinksPane />", () => {
|
|
|
44
54
|
targetType: "concept",
|
|
45
55
|
linksActions: <button>action</button>,
|
|
46
56
|
};
|
|
57
|
+
|
|
47
58
|
it("matches the latest snapshot", async () => {
|
|
48
59
|
const rendered = render(<LinksPane {...props} />, renderOpts);
|
|
49
60
|
await waitForLoad(rendered);
|
|
50
61
|
expect(rendered.container).toMatchSnapshot();
|
|
51
62
|
});
|
|
52
63
|
|
|
53
|
-
it("
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
{ id: 123, note: { status: "published", content: "Test note" } },
|
|
58
|
-
{ id: 456, note: { status: "draft", content: "Another note" } },
|
|
59
|
-
],
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
useDataStructureSearch.mockReturnValue({
|
|
64
|
-
trigger: mockTrigger,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const structureLinks = [
|
|
68
|
-
{
|
|
69
|
-
id: 1,
|
|
70
|
-
resource_type: "data_structure",
|
|
71
|
-
resource_id: 123,
|
|
72
|
-
name: "Structure 1",
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
id: 2,
|
|
76
|
-
resource_type: "data_structure",
|
|
77
|
-
resource_id: 456,
|
|
78
|
-
name: "Structure 2",
|
|
79
|
-
},
|
|
80
|
-
];
|
|
81
|
-
|
|
82
|
-
const propsWithStructures = {
|
|
83
|
-
...props,
|
|
84
|
-
groups: [[columns, ["tag", structureLinks]]],
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const rendered = render(<LinksPane {...propsWithStructures} />, renderOpts);
|
|
88
|
-
await waitForLoad(rendered);
|
|
89
|
-
|
|
90
|
-
expect(mockTrigger).toHaveBeenCalledWith({
|
|
91
|
-
must: { ids: [123, 456] },
|
|
92
|
-
page: 0,
|
|
93
|
-
size: 2,
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("handles empty structure links", async () => {
|
|
98
|
-
const mockTrigger = jest.fn();
|
|
99
|
-
|
|
100
|
-
useDataStructureSearch.mockReturnValue({
|
|
101
|
-
trigger: mockTrigger,
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
const nonStructureLinks = [
|
|
105
|
-
{ id: 1, resource_type: "other", resource_id: 123, name: "Other 1" },
|
|
106
|
-
];
|
|
107
|
-
|
|
108
|
-
const propsWithOther = {
|
|
109
|
-
...props,
|
|
110
|
-
groups: [[columns, ["tag", nonStructureLinks]]],
|
|
111
|
-
};
|
|
64
|
+
it("renders multiple groups as separate tables", async () => {
|
|
65
|
+
const firstColumns = [{ name: "name" }, { name: "type" }];
|
|
66
|
+
const secondColumns = [{ name: "name" }, { name: "deleted_at" }];
|
|
67
|
+
const groupLinks = [{ id: 1, name: "Group 1", type: "table" }];
|
|
112
68
|
|
|
113
|
-
const rendered = render(
|
|
69
|
+
const rendered = render(
|
|
70
|
+
<LinksPane
|
|
71
|
+
{...props}
|
|
72
|
+
groups={[
|
|
73
|
+
[firstColumns, ["tag", groupLinks]],
|
|
74
|
+
[secondColumns, ["deleted", groupLinks]],
|
|
75
|
+
]}
|
|
76
|
+
/>,
|
|
77
|
+
renderOpts,
|
|
78
|
+
);
|
|
114
79
|
await waitForLoad(rendered);
|
|
115
80
|
|
|
116
|
-
|
|
81
|
+
const tables = rendered.container.querySelectorAll("table");
|
|
82
|
+
expect(tables).toHaveLength(2);
|
|
117
83
|
});
|
|
118
84
|
|
|
119
85
|
it("should display note columns only when note values are present for that column", async () => {
|
|
@@ -126,11 +92,20 @@ describe("<LinksPane />", () => {
|
|
|
126
92
|
},
|
|
127
93
|
});
|
|
128
94
|
|
|
129
|
-
useDataStructureSearch.mockReturnValue({
|
|
130
|
-
trigger: mockTrigger,
|
|
131
|
-
});
|
|
95
|
+
useDataStructureSearch.mockReturnValue({ trigger: mockTrigger });
|
|
132
96
|
|
|
133
|
-
const
|
|
97
|
+
const noteColumns = [
|
|
98
|
+
{ name: "name" },
|
|
99
|
+
{
|
|
100
|
+
name: "note.foo",
|
|
101
|
+
fieldSelector: _.path(["note", "foo"]),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: "note.bar",
|
|
105
|
+
fieldSelector: _.path(["note", "bar"]),
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
const noteLinks = [
|
|
134
109
|
{
|
|
135
110
|
id: 1,
|
|
136
111
|
resource_type: "data_structure",
|
|
@@ -145,20 +120,6 @@ describe("<LinksPane />", () => {
|
|
|
145
120
|
},
|
|
146
121
|
];
|
|
147
122
|
|
|
148
|
-
const columnsWithNotes = [
|
|
149
|
-
...columns,
|
|
150
|
-
{
|
|
151
|
-
name: "note.foo",
|
|
152
|
-
header: "note.foo",
|
|
153
|
-
fieldSelector: _.path(["note", "foo"]),
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
name: "note.bar",
|
|
157
|
-
header: "note.bar",
|
|
158
|
-
fieldSelector: _.path(["note", "bar"]),
|
|
159
|
-
},
|
|
160
|
-
];
|
|
161
|
-
|
|
162
123
|
const renderOptsWithNotes = {
|
|
163
124
|
messages: {
|
|
164
125
|
en: {
|
|
@@ -169,13 +130,8 @@ describe("<LinksPane />", () => {
|
|
|
169
130
|
},
|
|
170
131
|
};
|
|
171
132
|
|
|
172
|
-
const propsWithMixedNotes = {
|
|
173
|
-
...props,
|
|
174
|
-
groups: [[columnsWithNotes, ["tag", structureLinksMixedNotes]]],
|
|
175
|
-
};
|
|
176
|
-
|
|
177
133
|
const rendered = render(
|
|
178
|
-
<LinksPane {...
|
|
134
|
+
<LinksPane {...props} groups={[[noteColumns, ["tag", noteLinks]]]} />,
|
|
179
135
|
renderOptsWithNotes,
|
|
180
136
|
);
|
|
181
137
|
await waitForLoad(rendered);
|
|
@@ -184,12 +140,6 @@ describe("<LinksPane />", () => {
|
|
|
184
140
|
expect(rendered.queryByText(/foo/i)).toBeInTheDocument();
|
|
185
141
|
});
|
|
186
142
|
|
|
187
|
-
expect(mockTrigger).toHaveBeenCalledWith({
|
|
188
|
-
must: { ids: [123, 456] },
|
|
189
|
-
page: 0,
|
|
190
|
-
size: 2,
|
|
191
|
-
});
|
|
192
|
-
|
|
193
143
|
const fooCells = rendered.queryAllByText(/baz/i);
|
|
194
144
|
expect(fooCells.length).toBeGreaterThan(0);
|
|
195
145
|
|
|
@@ -198,4 +148,19 @@ describe("<LinksPane />", () => {
|
|
|
198
148
|
|
|
199
149
|
expect(rendered.container).toMatchSnapshot();
|
|
200
150
|
});
|
|
151
|
+
|
|
152
|
+
it("passes linksActions to the UI", async () => {
|
|
153
|
+
const rendered = render(
|
|
154
|
+
<LinksPane
|
|
155
|
+
groups={[[columns, ["tag", links]]]}
|
|
156
|
+
sourceType="implementation"
|
|
157
|
+
targetType="concept"
|
|
158
|
+
linksActions={<button data-testid="action-btn">Action</button>}
|
|
159
|
+
/>,
|
|
160
|
+
renderOpts,
|
|
161
|
+
);
|
|
162
|
+
await waitForLoad(rendered);
|
|
163
|
+
|
|
164
|
+
expect(rendered.getByTestId("action-btn")).toBeInTheDocument();
|
|
165
|
+
});
|
|
201
166
|
});
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import _ from "lodash/fp";
|
|
6
|
+
import { fireEvent, waitFor } from "@testing-library/react";
|
|
7
|
+
import { render, waitForLoad } from "@truedat/test/render";
|
|
8
|
+
import { LinksPaneDataCell } from "../LinksPaneDataCell";
|
|
9
|
+
|
|
10
|
+
jest.mock(
|
|
11
|
+
"@truedat/core/services",
|
|
12
|
+
() => {
|
|
13
|
+
const lodashFp = require("lodash/fp");
|
|
14
|
+
return {
|
|
15
|
+
columnDecorator: (column) => (obj) => {
|
|
16
|
+
const { fieldSelector, fieldDecorator, name } = column || {};
|
|
17
|
+
const field = lodashFp.isFunction(fieldSelector)
|
|
18
|
+
? fieldSelector(obj)
|
|
19
|
+
: lodashFp.path(lodashFp.defaultTo(name)(fieldSelector))(obj);
|
|
20
|
+
return lodashFp.isFunction(fieldDecorator)
|
|
21
|
+
? fieldDecorator(field)
|
|
22
|
+
: field;
|
|
23
|
+
},
|
|
24
|
+
columnPredicate: ({ name, fieldSelector }) =>
|
|
25
|
+
lodashFp.flow(
|
|
26
|
+
lodashFp.isFunction(fieldSelector)
|
|
27
|
+
? fieldSelector
|
|
28
|
+
: lodashFp.path(lodashFp.defaultTo(name)(fieldSelector)),
|
|
29
|
+
Boolean,
|
|
30
|
+
),
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
{ virtual: true },
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
describe("<LinksPaneDataCell />", () => {
|
|
37
|
+
const setHorizontalOverflow = (element, scrollWidth, clientWidth) => {
|
|
38
|
+
Object.defineProperties(element, {
|
|
39
|
+
scrollWidth: { configurable: true, value: scrollWidth },
|
|
40
|
+
clientWidth: { configurable: true, value: clientWidth },
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const renderOpts = {
|
|
45
|
+
messages: {
|
|
46
|
+
en: {},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
it("renders cell content from column", () => {
|
|
51
|
+
const column = { name: "name" };
|
|
52
|
+
const link = { id: 1, name: "Alpha" };
|
|
53
|
+
|
|
54
|
+
const { getByText } = render(
|
|
55
|
+
<LinksPaneDataCell column={column} link={link} />,
|
|
56
|
+
renderOpts,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
expect(getByText("Alpha")).toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("renders fieldDecorator output", () => {
|
|
63
|
+
const column = {
|
|
64
|
+
name: "type",
|
|
65
|
+
fieldSelector: _.pick(["type"]),
|
|
66
|
+
fieldDecorator: ({ type }) => type,
|
|
67
|
+
};
|
|
68
|
+
const link = { id: 1, type: "column" };
|
|
69
|
+
|
|
70
|
+
const { getByText } = render(
|
|
71
|
+
<LinksPaneDataCell column={column} link={link} />,
|
|
72
|
+
renderOpts,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
expect(getByText("column")).toBeInTheDocument();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("shows the complete value when a data cell is truncated", async () => {
|
|
79
|
+
const value = "A complete value that does not fit in the table cell";
|
|
80
|
+
const column = { name: "name" };
|
|
81
|
+
const link = { id: 1, name: value };
|
|
82
|
+
|
|
83
|
+
const { container } = render(
|
|
84
|
+
<LinksPaneDataCell column={column} link={link} />,
|
|
85
|
+
renderOpts,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const cell = container.querySelector("td");
|
|
89
|
+
setHorizontalOverflow(cell, 240, 120);
|
|
90
|
+
expect(cell).not.toHaveAttribute("title");
|
|
91
|
+
|
|
92
|
+
fireEvent.mouseEnter(cell);
|
|
93
|
+
|
|
94
|
+
await waitFor(() => {
|
|
95
|
+
expect(
|
|
96
|
+
document.body.querySelector(".links-pane__truncated-popup"),
|
|
97
|
+
).toHaveTextContent(value);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("does not show a popup when the data cell content fits", async () => {
|
|
102
|
+
const value = "Visible value";
|
|
103
|
+
const column = { name: "name" };
|
|
104
|
+
const link = { id: 1, name: value };
|
|
105
|
+
|
|
106
|
+
const { container } = render(
|
|
107
|
+
<LinksPaneDataCell column={column} link={link} />,
|
|
108
|
+
renderOpts,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const cell = container.querySelector("td");
|
|
112
|
+
setHorizontalOverflow(cell, 120, 120);
|
|
113
|
+
|
|
114
|
+
fireEvent.mouseEnter(cell);
|
|
115
|
+
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
expect(
|
|
118
|
+
document.body.querySelector(".links-pane__truncated-popup"),
|
|
119
|
+
).not.toBeInTheDocument();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("does not add popup to action cells", async () => {
|
|
124
|
+
const column = {
|
|
125
|
+
name: "_actions",
|
|
126
|
+
fieldSelector: _.prop("_actions"),
|
|
127
|
+
fieldDecorator: () => <button type="button">delete</button>,
|
|
128
|
+
};
|
|
129
|
+
const link = { id: 1, _actions: { delete: true } };
|
|
130
|
+
|
|
131
|
+
const { container } = render(
|
|
132
|
+
<LinksPaneDataCell column={column} link={link} />,
|
|
133
|
+
renderOpts,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const cell = container.querySelector("td");
|
|
137
|
+
setHorizontalOverflow(cell, 240, 60);
|
|
138
|
+
fireEvent.mouseEnter(cell);
|
|
139
|
+
|
|
140
|
+
await waitFor(() => {
|
|
141
|
+
expect(
|
|
142
|
+
document.body.querySelector(".links-pane__truncated-popup"),
|
|
143
|
+
).not.toBeInTheDocument();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("does not add popup to group cells", async () => {
|
|
148
|
+
const column = {
|
|
149
|
+
name: "group",
|
|
150
|
+
fieldDecorator: (group) => group,
|
|
151
|
+
};
|
|
152
|
+
const link = { id: 1, group: "Analytics" };
|
|
153
|
+
|
|
154
|
+
const { container } = render(
|
|
155
|
+
<LinksPaneDataCell column={column} link={link} />,
|
|
156
|
+
renderOpts,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const cell = container.querySelector("td");
|
|
160
|
+
setHorizontalOverflow(cell, 240, 60);
|
|
161
|
+
fireEvent.mouseEnter(cell);
|
|
162
|
+
|
|
163
|
+
await waitFor(() => {
|
|
164
|
+
expect(
|
|
165
|
+
document.body.querySelector(".links-pane__truncated-popup"),
|
|
166
|
+
).not.toBeInTheDocument();
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("does not add popup to note text cells", async () => {
|
|
171
|
+
const column = {
|
|
172
|
+
name: "note.text_input",
|
|
173
|
+
fieldSelector: _.path(["note", "text_input"]),
|
|
174
|
+
};
|
|
175
|
+
const link = { id: 1, note: { text_input: "Complete note value" } };
|
|
176
|
+
|
|
177
|
+
const { container } = render(
|
|
178
|
+
<LinksPaneDataCell column={column} link={link} />,
|
|
179
|
+
renderOpts,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const cell = container.querySelector("td");
|
|
183
|
+
setHorizontalOverflow(cell, 240, 60);
|
|
184
|
+
fireEvent.mouseEnter(cell);
|
|
185
|
+
|
|
186
|
+
await waitFor(() => {
|
|
187
|
+
expect(
|
|
188
|
+
document.body.querySelector(".links-pane__truncated-popup"),
|
|
189
|
+
).not.toBeInTheDocument();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import _ from "lodash/fp";
|
|
6
|
+
import { render } from "@truedat/test/render";
|
|
7
|
+
import { LinksPaneRow } from "../LinksPaneRow";
|
|
8
|
+
|
|
9
|
+
jest.mock(
|
|
10
|
+
"@truedat/core/services",
|
|
11
|
+
() => {
|
|
12
|
+
const lodashFp = require("lodash/fp");
|
|
13
|
+
return {
|
|
14
|
+
columnDecorator: (column) => (obj) => {
|
|
15
|
+
const { fieldSelector, fieldDecorator, name } = column || {};
|
|
16
|
+
const field = lodashFp.isFunction(fieldSelector)
|
|
17
|
+
? fieldSelector(obj)
|
|
18
|
+
: lodashFp.path(lodashFp.defaultTo(name)(fieldSelector))(obj);
|
|
19
|
+
return lodashFp.isFunction(fieldDecorator)
|
|
20
|
+
? fieldDecorator(field)
|
|
21
|
+
: field;
|
|
22
|
+
},
|
|
23
|
+
columnPredicate: ({ name, fieldSelector }) =>
|
|
24
|
+
lodashFp.flow(
|
|
25
|
+
lodashFp.isFunction(fieldSelector)
|
|
26
|
+
? fieldSelector
|
|
27
|
+
: lodashFp.path(lodashFp.defaultTo(name)(fieldSelector)),
|
|
28
|
+
Boolean,
|
|
29
|
+
),
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
{ virtual: true },
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
describe("<LinksPaneRow />", () => {
|
|
36
|
+
const renderOpts = {
|
|
37
|
+
messages: { en: {} },
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
it("renders one cell per column", () => {
|
|
41
|
+
const columns = [{ name: "name" }, { name: "type" }];
|
|
42
|
+
const link = { name: "Alpha", type: "field" };
|
|
43
|
+
|
|
44
|
+
const { container } = render(
|
|
45
|
+
<table>
|
|
46
|
+
<tbody>
|
|
47
|
+
<LinksPaneRow columns={columns} link={link} />
|
|
48
|
+
</tbody>
|
|
49
|
+
</table>,
|
|
50
|
+
renderOpts,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const cells = container.querySelectorAll("td");
|
|
54
|
+
expect(cells).toHaveLength(2);
|
|
55
|
+
expect(cells[0]).toHaveTextContent("Alpha");
|
|
56
|
+
expect(cells[1]).toHaveTextContent("field");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("renders empty row when no columns", () => {
|
|
60
|
+
const { container } = render(
|
|
61
|
+
<table>
|
|
62
|
+
<tbody>
|
|
63
|
+
<LinksPaneRow columns={[]} link={{ name: "Alpha" }} />
|
|
64
|
+
</tbody>
|
|
65
|
+
</table>,
|
|
66
|
+
renderOpts,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const cells = container.querySelectorAll("td");
|
|
70
|
+
expect(cells).toHaveLength(0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("renders fieldDecorator output", () => {
|
|
74
|
+
const columns = [
|
|
75
|
+
{
|
|
76
|
+
name: "type",
|
|
77
|
+
fieldSelector: _.pick(["type"]),
|
|
78
|
+
fieldDecorator: ({ type }) => `Type: ${type}`,
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
const link = { type: "table" };
|
|
82
|
+
|
|
83
|
+
const { container } = render(
|
|
84
|
+
<table>
|
|
85
|
+
<tbody>
|
|
86
|
+
<LinksPaneRow columns={columns} link={link} />
|
|
87
|
+
</tbody>
|
|
88
|
+
</table>,
|
|
89
|
+
renderOpts,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
expect(container.querySelector("td")).toHaveTextContent("Type: table");
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import _ from "lodash/fp";
|
|
6
|
+
import { fireEvent, waitFor } from "@testing-library/react";
|
|
7
|
+
import { render } from "@truedat/test/render";
|
|
8
|
+
import { LinksPaneTypeFilter } from "../LinksPaneTypeFilter";
|
|
9
|
+
|
|
10
|
+
describe("<LinksPaneTypeFilter />", () => {
|
|
11
|
+
const renderOpts = {
|
|
12
|
+
messages: {
|
|
13
|
+
en: {
|
|
14
|
+
"structure.type.column.text": "Column",
|
|
15
|
+
"structure.type.table.text": "Table",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
it("renders nothing when options are empty", () => {
|
|
21
|
+
const { container } = render(
|
|
22
|
+
<LinksPaneTypeFilter
|
|
23
|
+
onChange={jest.fn()}
|
|
24
|
+
options={[]}
|
|
25
|
+
selectedTypes={[]}
|
|
26
|
+
/>,
|
|
27
|
+
renderOpts,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
expect(container.querySelector(".filter.icon")).not.toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("renders filter icon with options", () => {
|
|
34
|
+
const { container } = render(
|
|
35
|
+
<LinksPaneTypeFilter
|
|
36
|
+
onChange={jest.fn()}
|
|
37
|
+
options={["column", "table"]}
|
|
38
|
+
selectedTypes={[]}
|
|
39
|
+
/>,
|
|
40
|
+
renderOpts,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
expect(container.querySelector(".filter.icon")).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("opens popup on filter icon click", async () => {
|
|
47
|
+
const rendered = render(
|
|
48
|
+
<LinksPaneTypeFilter
|
|
49
|
+
onChange={jest.fn()}
|
|
50
|
+
options={["column", "table"]}
|
|
51
|
+
selectedTypes={[]}
|
|
52
|
+
/>,
|
|
53
|
+
renderOpts,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
fireEvent.click(rendered.container.querySelector(".filter.icon"));
|
|
57
|
+
|
|
58
|
+
await waitFor(() => {
|
|
59
|
+
expect(document.body.querySelector(".ui.popup")).toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("renders all options as checkboxes in the popup", async () => {
|
|
64
|
+
const rendered = render(
|
|
65
|
+
<LinksPaneTypeFilter
|
|
66
|
+
onChange={jest.fn()}
|
|
67
|
+
options={["column", "table"]}
|
|
68
|
+
selectedTypes={[]}
|
|
69
|
+
/>,
|
|
70
|
+
renderOpts,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
fireEvent.click(rendered.container.querySelector(".filter.icon"));
|
|
74
|
+
|
|
75
|
+
await waitFor(() => {
|
|
76
|
+
const menuItems = document.body.querySelectorAll(
|
|
77
|
+
".links-pane__filter-menu-item",
|
|
78
|
+
);
|
|
79
|
+
expect(menuItems).toHaveLength(2);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("calls onChange with [] when selecting an option makes all options selected", async () => {
|
|
84
|
+
const onChange = jest.fn();
|
|
85
|
+
|
|
86
|
+
const rendered = render(
|
|
87
|
+
<LinksPaneTypeFilter
|
|
88
|
+
onChange={onChange}
|
|
89
|
+
options={["column", "table"]}
|
|
90
|
+
selectedTypes={["column"]}
|
|
91
|
+
/>,
|
|
92
|
+
renderOpts,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
fireEvent.click(rendered.container.querySelector(".filter.icon"));
|
|
96
|
+
|
|
97
|
+
await waitFor(() => {
|
|
98
|
+
expect(document.body.querySelector(".ui.popup")).toBeInTheDocument();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const tableItem = Array.from(
|
|
102
|
+
document.body.querySelectorAll(".links-pane__filter-menu-item"),
|
|
103
|
+
).find((item) => item.textContent.includes("Table"));
|
|
104
|
+
|
|
105
|
+
fireEvent.click(tableItem);
|
|
106
|
+
|
|
107
|
+
expect(onChange).toHaveBeenCalledWith([]);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("calls onChange with remaining option when one is deselected", async () => {
|
|
111
|
+
const onChange = jest.fn();
|
|
112
|
+
|
|
113
|
+
const rendered = render(
|
|
114
|
+
<LinksPaneTypeFilter
|
|
115
|
+
onChange={onChange}
|
|
116
|
+
options={["column", "table"]}
|
|
117
|
+
selectedTypes={["column", "table"]}
|
|
118
|
+
/>,
|
|
119
|
+
renderOpts,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
fireEvent.click(rendered.container.querySelector(".filter.icon"));
|
|
123
|
+
|
|
124
|
+
await waitFor(() => {
|
|
125
|
+
expect(document.body.querySelector(".ui.popup")).toBeInTheDocument();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const columnItem = Array.from(
|
|
129
|
+
document.body.querySelectorAll(".links-pane__filter-menu-item"),
|
|
130
|
+
).find((item) => item.textContent.includes("Column"));
|
|
131
|
+
|
|
132
|
+
fireEvent.click(columnItem);
|
|
133
|
+
|
|
134
|
+
expect(onChange).toHaveBeenCalledWith(["table"]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("shows icon in blue when some filters are active", () => {
|
|
138
|
+
const { container } = render(
|
|
139
|
+
<LinksPaneTypeFilter
|
|
140
|
+
onChange={jest.fn()}
|
|
141
|
+
options={["column", "table"]}
|
|
142
|
+
selectedTypes={["column"]}
|
|
143
|
+
/>,
|
|
144
|
+
renderOpts,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const icon = container.querySelector(".filter.icon");
|
|
148
|
+
expect(icon).toHaveClass("blue");
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("shows icon in default color when no filters are active", () => {
|
|
152
|
+
const { container } = render(
|
|
153
|
+
<LinksPaneTypeFilter
|
|
154
|
+
onChange={jest.fn()}
|
|
155
|
+
options={["column", "table"]}
|
|
156
|
+
selectedTypes={[]}
|
|
157
|
+
/>,
|
|
158
|
+
renderOpts,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const icon = container.querySelector(".filter.icon");
|
|
162
|
+
expect(icon).not.toHaveClass("blue");
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
exports[`<ConfirmDeleteRelation /> matches the latest snapshot 1`] = `
|
|
4
4
|
<div>
|
|
5
5
|
<button
|
|
6
|
-
class="ui red mini basic icon button"
|
|
6
|
+
class="ui red mini basic compact icon button relation-actions__delete-button"
|
|
7
7
|
>
|
|
8
8
|
<i
|
|
9
9
|
aria-hidden="true"
|
|
10
|
-
class="red trash alternate outline icon"
|
|
10
|
+
class="red trash alternate outline small icon relation-actions__delete-icon"
|
|
11
11
|
/>
|
|
12
12
|
</button>
|
|
13
13
|
</div>
|