@truedat/lm 8.2.0 → 8.2.2
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/api.js +4 -0
- package/src/components/ConceptLinkForm.js +111 -0
- package/src/components/LinksPagination.js +15 -0
- package/src/components/LinksSearch.js +139 -0
- package/src/components/QualityControlConcepts.js +156 -0
- package/src/components/QualityControlStructures.js +183 -0
- package/src/components/RelationTagForm.js +4 -0
- package/src/components/StructureLinkForm.js +105 -0
- package/src/components/__tests__/ConceptLinkForm.spec.js +252 -0
- package/src/components/__tests__/LinksSearch.spec.js +299 -0
- package/src/components/__tests__/QualityControlConcepts.spec.js +262 -0
- package/src/components/__tests__/QualityControlStructures.spec.js +213 -0
- package/src/components/__tests__/StructureLinkForm.spec.js +284 -0
- package/src/components/__tests__/__snapshots__/ConceptLinkForm.spec.js.snap +48 -0
- package/src/components/__tests__/__snapshots__/LinksSearch.spec.js.snap +29 -0
- package/src/components/__tests__/__snapshots__/NewRelationTag.spec.js.snap +13 -0
- package/src/components/__tests__/__snapshots__/QualityControlConcepts.spec.js.snap +49 -0
- package/src/components/__tests__/__snapshots__/QualityControlStructures.spec.js.snap +49 -0
- package/src/components/__tests__/__snapshots__/RelationTagForm.spec.js.snap +13 -0
- package/src/components/__tests__/__snapshots__/StructureLinkForm.spec.js.snap +48 -0
- package/src/hooks/useRelations.js +53 -0
- package/src/messages/en.js +1 -0
- package/src/messages/es.js +1 -0
- package/src/selectors/index.js +1 -0
- package/src/selectors/messages.js +48 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import userEvent from "@testing-library/user-event";
|
|
2
|
+
import { waitFor } from "@testing-library/react";
|
|
3
|
+
import { render, waitForLoad } from "@truedat/test/render";
|
|
4
|
+
import { StructureLinkForm } from "../StructureLinkForm";
|
|
5
|
+
|
|
6
|
+
const mockNavigate = jest.fn();
|
|
7
|
+
const mockCreateRelation = jest.fn();
|
|
8
|
+
const mockDispatch = jest.fn();
|
|
9
|
+
const mockSetAlertMessage = jest.fn();
|
|
10
|
+
|
|
11
|
+
jest.mock("react-router", () => ({
|
|
12
|
+
...jest.requireActual("react-router"),
|
|
13
|
+
useNavigate: () => mockNavigate,
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock("../../hooks/useRelations", () => ({
|
|
17
|
+
useCreateRelation: () => ({
|
|
18
|
+
trigger: mockCreateRelation,
|
|
19
|
+
isMutating: false,
|
|
20
|
+
}),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
jest.mock("react-redux", () => ({
|
|
24
|
+
...jest.requireActual("react-redux"),
|
|
25
|
+
useDispatch: jest.fn(() => mockDispatch),
|
|
26
|
+
useSelector: jest.fn((selector) => {
|
|
27
|
+
const state = {
|
|
28
|
+
selectedRelationTags: [1],
|
|
29
|
+
relationTags: [{ value: { target_type: "data_structure" } }],
|
|
30
|
+
};
|
|
31
|
+
return selector(state);
|
|
32
|
+
}),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
jest.mock("react-intl", () => ({
|
|
36
|
+
...jest.requireActual("react-intl"),
|
|
37
|
+
useIntl: () => ({
|
|
38
|
+
formatMessage: ({ id }) => id,
|
|
39
|
+
}),
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
jest.mock("@truedat/core/webContext", () => ({
|
|
43
|
+
useWebContext: () => ({
|
|
44
|
+
setAlertMessage: mockSetAlertMessage,
|
|
45
|
+
alertMessage: {},
|
|
46
|
+
}),
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
jest.mock("../../routines", () => ({
|
|
50
|
+
clearSelectedRelationTags: {
|
|
51
|
+
trigger: jest.fn(() => ({ type: "CLEAR_SELECTED_RELATION_TAGS" })),
|
|
52
|
+
},
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
jest.mock("../RelationTagsLoader", () =>
|
|
56
|
+
jest.fn(() => <div>RelationTagsLoader</div>)
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
jest.mock("@truedat/lm/components/TagTypeDropdownSelector", () =>
|
|
60
|
+
jest.fn(({ options }) => (
|
|
61
|
+
<div>
|
|
62
|
+
<div>TagTypeDropdownSelector</div>
|
|
63
|
+
{options?.map((option, i) => (
|
|
64
|
+
<button
|
|
65
|
+
key={i}
|
|
66
|
+
onClick={() => option.onClick && option.onClick(option.value)}
|
|
67
|
+
>
|
|
68
|
+
{option.text}
|
|
69
|
+
</button>
|
|
70
|
+
))}
|
|
71
|
+
</div>
|
|
72
|
+
))
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
jest.mock("@truedat/dd/components/StructureSelector", () =>
|
|
76
|
+
jest.fn(({ selectedStructure, onSelect }) => (
|
|
77
|
+
<div>
|
|
78
|
+
<div>StructureSelector</div>
|
|
79
|
+
<button
|
|
80
|
+
onClick={() =>
|
|
81
|
+
onSelect({
|
|
82
|
+
id: 456,
|
|
83
|
+
name: "Test Structure",
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
>
|
|
87
|
+
Select Structure
|
|
88
|
+
</button>
|
|
89
|
+
{selectedStructure && (
|
|
90
|
+
<div data-testid="selected-structure">{selectedStructure.name}</div>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
93
|
+
))
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
jest.mock("@truedat/core/components", () => ({
|
|
97
|
+
HistoryBackButton: ({ content }) => <button>{content}</button>,
|
|
98
|
+
}));
|
|
99
|
+
|
|
100
|
+
const props = {
|
|
101
|
+
sourceId: 10,
|
|
102
|
+
sourceType: "quality_control",
|
|
103
|
+
redirectUrl: "/qualityControls/10/version/1/links/structures",
|
|
104
|
+
selectTagOptions: jest.fn(() => [{ id: 1, text: "relates_to", value: 1 }]),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
describe("<StructureLinkForm />", () => {
|
|
108
|
+
beforeEach(() => {
|
|
109
|
+
jest.clearAllMocks();
|
|
110
|
+
mockCreateRelation.mockResolvedValue({
|
|
111
|
+
data: {
|
|
112
|
+
data: {
|
|
113
|
+
id: 789,
|
|
114
|
+
source_id: 10,
|
|
115
|
+
target_id: 456,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("matches the latest snapshot", async () => {
|
|
122
|
+
const rendered = render(<StructureLinkForm {...props} />);
|
|
123
|
+
await waitForLoad(rendered);
|
|
124
|
+
expect(rendered.container).toMatchSnapshot();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("renders all form components", async () => {
|
|
128
|
+
const rendered = render(<StructureLinkForm {...props} />);
|
|
129
|
+
await waitForLoad(rendered);
|
|
130
|
+
|
|
131
|
+
expect(rendered.getByText("RelationTagsLoader")).toBeInTheDocument();
|
|
132
|
+
expect(rendered.getByText("TagTypeDropdownSelector")).toBeInTheDocument();
|
|
133
|
+
expect(rendered.getByText("StructureSelector")).toBeInTheDocument();
|
|
134
|
+
expect(rendered.getByText("actions.create")).toBeInTheDocument();
|
|
135
|
+
expect(rendered.getByText("actions.cancel")).toBeInTheDocument();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("disables create button when structure or tags are not selected", async () => {
|
|
139
|
+
const rendered = render(<StructureLinkForm {...props} />);
|
|
140
|
+
await waitForLoad(rendered);
|
|
141
|
+
|
|
142
|
+
const createButton = rendered.getByText("actions.create");
|
|
143
|
+
expect(createButton).toBeDisabled();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("enables create button when both structure and tags are selected", async () => {
|
|
147
|
+
const user = userEvent.setup({ delay: null });
|
|
148
|
+
const rendered = render(<StructureLinkForm {...props} />);
|
|
149
|
+
await waitForLoad(rendered);
|
|
150
|
+
|
|
151
|
+
const createButton = rendered.getByText("actions.create");
|
|
152
|
+
expect(createButton).toBeDisabled();
|
|
153
|
+
|
|
154
|
+
await user.click(rendered.getByText("Select Structure"));
|
|
155
|
+
|
|
156
|
+
await waitFor(() => {
|
|
157
|
+
expect(createButton).toBeEnabled();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("calls createRelation and navigates on submit", async () => {
|
|
162
|
+
const user = userEvent.setup({ delay: null });
|
|
163
|
+
const rendered = render(<StructureLinkForm {...props} />);
|
|
164
|
+
await waitForLoad(rendered);
|
|
165
|
+
|
|
166
|
+
await user.click(rendered.getByText("Select Structure"));
|
|
167
|
+
|
|
168
|
+
await waitFor(() => {
|
|
169
|
+
expect(rendered.getByText("actions.create")).toBeEnabled();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
await user.click(rendered.getByText("actions.create"));
|
|
173
|
+
|
|
174
|
+
await waitFor(() => {
|
|
175
|
+
expect(mockCreateRelation).toHaveBeenCalledWith({
|
|
176
|
+
relation: {
|
|
177
|
+
source_id: 10,
|
|
178
|
+
source_type: "quality_control",
|
|
179
|
+
target_id: 456,
|
|
180
|
+
target_type: "data_structure",
|
|
181
|
+
tag_ids: [1],
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
await waitFor(() => {
|
|
187
|
+
expect(mockNavigate).toHaveBeenCalledWith(props.redirectUrl, {
|
|
188
|
+
state: {
|
|
189
|
+
createdRelation: {
|
|
190
|
+
id: 789,
|
|
191
|
+
source_id: 10,
|
|
192
|
+
target_id: 456,
|
|
193
|
+
target_name: "Test Structure",
|
|
194
|
+
target_data: {
|
|
195
|
+
id: 456,
|
|
196
|
+
name: "Test Structure",
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("handles error with _originalErrors and sets alert message", async () => {
|
|
205
|
+
const user = userEvent.setup({ delay: null });
|
|
206
|
+
const error = {
|
|
207
|
+
_originalErrors: {
|
|
208
|
+
source_id: ["has already been taken"],
|
|
209
|
+
},
|
|
210
|
+
message: "An error occurred",
|
|
211
|
+
};
|
|
212
|
+
mockCreateRelation.mockRejectedValueOnce(error);
|
|
213
|
+
|
|
214
|
+
const rendered = render(<StructureLinkForm {...props} />);
|
|
215
|
+
await waitForLoad(rendered);
|
|
216
|
+
|
|
217
|
+
await user.click(rendered.getByText("Select Structure"));
|
|
218
|
+
|
|
219
|
+
await waitFor(() => {
|
|
220
|
+
expect(rendered.getByText("actions.create")).toBeEnabled();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
await user.click(rendered.getByText("actions.create"));
|
|
224
|
+
|
|
225
|
+
await waitFor(() => {
|
|
226
|
+
expect(mockSetAlertMessage).toHaveBeenCalledWith(
|
|
227
|
+
expect.objectContaining({
|
|
228
|
+
error: true,
|
|
229
|
+
header: "relations.create.failed.header",
|
|
230
|
+
icon: "attention",
|
|
231
|
+
content: expect.any(String),
|
|
232
|
+
})
|
|
233
|
+
);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("handles error without _originalErrors and sets alert message", async () => {
|
|
238
|
+
const user = userEvent.setup({ delay: null });
|
|
239
|
+
const error = {
|
|
240
|
+
message: "Network error",
|
|
241
|
+
};
|
|
242
|
+
mockCreateRelation.mockRejectedValueOnce(error);
|
|
243
|
+
|
|
244
|
+
const rendered = render(<StructureLinkForm {...props} />);
|
|
245
|
+
await waitForLoad(rendered);
|
|
246
|
+
|
|
247
|
+
await user.click(rendered.getByText("Select Structure"));
|
|
248
|
+
|
|
249
|
+
await waitFor(() => {
|
|
250
|
+
expect(rendered.getByText("actions.create")).toBeEnabled();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
await user.click(rendered.getByText("actions.create"));
|
|
254
|
+
|
|
255
|
+
await waitFor(() => {
|
|
256
|
+
expect(mockSetAlertMessage).toHaveBeenCalledWith({
|
|
257
|
+
error: true,
|
|
258
|
+
content: "Network error",
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("dispatches clearSelectedRelationTags on unmount", () => {
|
|
264
|
+
const { unmount } = render(<StructureLinkForm {...props} />);
|
|
265
|
+
unmount();
|
|
266
|
+
|
|
267
|
+
expect(mockDispatch).toHaveBeenCalledWith({
|
|
268
|
+
type: "CLEAR_SELECTED_RELATION_TAGS",
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it("does not render TagTypeDropdownSelector when tagOptions is empty", async () => {
|
|
273
|
+
const rendered = render(
|
|
274
|
+
<StructureLinkForm
|
|
275
|
+
{...{ ...props, selectTagOptions: jest.fn(() => []) }}
|
|
276
|
+
/>
|
|
277
|
+
);
|
|
278
|
+
await waitForLoad(rendered);
|
|
279
|
+
|
|
280
|
+
expect(
|
|
281
|
+
rendered.queryByText("TagTypeDropdownSelector")
|
|
282
|
+
).not.toBeInTheDocument();
|
|
283
|
+
});
|
|
284
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<ConceptLinkForm /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="ui hidden divider"
|
|
7
|
+
/>
|
|
8
|
+
<div>
|
|
9
|
+
RelationTagsLoader
|
|
10
|
+
</div>
|
|
11
|
+
<div>
|
|
12
|
+
<div>
|
|
13
|
+
TagTypeDropdownSelector
|
|
14
|
+
</div>
|
|
15
|
+
<button>
|
|
16
|
+
relates_to
|
|
17
|
+
</button>
|
|
18
|
+
</div>
|
|
19
|
+
<div
|
|
20
|
+
class="ui hidden divider"
|
|
21
|
+
/>
|
|
22
|
+
<div>
|
|
23
|
+
<div>
|
|
24
|
+
ConceptSelector
|
|
25
|
+
</div>
|
|
26
|
+
<button>
|
|
27
|
+
Select Concept
|
|
28
|
+
</button>
|
|
29
|
+
</div>
|
|
30
|
+
<div
|
|
31
|
+
class="ui hidden divider"
|
|
32
|
+
/>
|
|
33
|
+
<div
|
|
34
|
+
class="ui buttons"
|
|
35
|
+
>
|
|
36
|
+
<button
|
|
37
|
+
class="ui primary disabled button"
|
|
38
|
+
disabled=""
|
|
39
|
+
tabindex="-1"
|
|
40
|
+
>
|
|
41
|
+
actions.create
|
|
42
|
+
</button>
|
|
43
|
+
<button>
|
|
44
|
+
actions.cancel
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
`;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<LinksSearch /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="ui segment"
|
|
7
|
+
>
|
|
8
|
+
<div>
|
|
9
|
+
SearchWidget
|
|
10
|
+
</div>
|
|
11
|
+
<h4
|
|
12
|
+
class="ui header"
|
|
13
|
+
>
|
|
14
|
+
<i
|
|
15
|
+
aria-hidden="true"
|
|
16
|
+
class="search icon"
|
|
17
|
+
/>
|
|
18
|
+
<div
|
|
19
|
+
class="content"
|
|
20
|
+
>
|
|
21
|
+
linked_concepts.empty
|
|
22
|
+
</div>
|
|
23
|
+
</h4>
|
|
24
|
+
<div>
|
|
25
|
+
LinksPagination
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
`;
|
|
@@ -126,6 +126,19 @@ exports[`<NewRelationTag /> matches the latest snapshot 1`] = `
|
|
|
126
126
|
target_type.data_field
|
|
127
127
|
</span>
|
|
128
128
|
</div>
|
|
129
|
+
<div
|
|
130
|
+
aria-checked="false"
|
|
131
|
+
aria-selected="false"
|
|
132
|
+
class="item"
|
|
133
|
+
role="option"
|
|
134
|
+
style="pointer-events: all;"
|
|
135
|
+
>
|
|
136
|
+
<span
|
|
137
|
+
class="text"
|
|
138
|
+
>
|
|
139
|
+
target_type.quality_control
|
|
140
|
+
</span>
|
|
141
|
+
</div>
|
|
129
142
|
</div>
|
|
130
143
|
</div>
|
|
131
144
|
</div>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<QualityControlConcepts /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="ui basic segment"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
class="ui grid"
|
|
10
|
+
>
|
|
11
|
+
<div
|
|
12
|
+
class="sixteen wide column"
|
|
13
|
+
>
|
|
14
|
+
<a
|
|
15
|
+
class="ui primary right floated button"
|
|
16
|
+
href="/qualityControls/10/version/1/links/concepts/new"
|
|
17
|
+
role="button"
|
|
18
|
+
>
|
|
19
|
+
links.actions.create
|
|
20
|
+
</a>
|
|
21
|
+
</div>
|
|
22
|
+
<div
|
|
23
|
+
class="sixteen wide column"
|
|
24
|
+
>
|
|
25
|
+
<div>
|
|
26
|
+
<div>
|
|
27
|
+
LinksSearch
|
|
28
|
+
</div>
|
|
29
|
+
<div
|
|
30
|
+
data-testid="columns-count"
|
|
31
|
+
>
|
|
32
|
+
5
|
|
33
|
+
</div>
|
|
34
|
+
<div
|
|
35
|
+
data-testid="set-refetch-search"
|
|
36
|
+
>
|
|
37
|
+
function
|
|
38
|
+
</div>
|
|
39
|
+
<div
|
|
40
|
+
data-testid="default-filters"
|
|
41
|
+
>
|
|
42
|
+
{"target_id":10,"target_type":"quality_control","source_type":"business_concept"}
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
`;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<QualityControlStructures /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="ui basic segment"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
class="ui grid"
|
|
10
|
+
>
|
|
11
|
+
<div
|
|
12
|
+
class="sixteen wide column"
|
|
13
|
+
>
|
|
14
|
+
<a
|
|
15
|
+
class="ui primary right floated button"
|
|
16
|
+
href="/qualityControls/10/version/1/links/structures/new"
|
|
17
|
+
role="button"
|
|
18
|
+
>
|
|
19
|
+
links.actions.create
|
|
20
|
+
</a>
|
|
21
|
+
</div>
|
|
22
|
+
<div
|
|
23
|
+
class="sixteen wide column"
|
|
24
|
+
>
|
|
25
|
+
<div>
|
|
26
|
+
<div>
|
|
27
|
+
LinksSearch
|
|
28
|
+
</div>
|
|
29
|
+
<div
|
|
30
|
+
data-testid="columns-count"
|
|
31
|
+
>
|
|
32
|
+
8
|
|
33
|
+
</div>
|
|
34
|
+
<div
|
|
35
|
+
data-testid="set-refetch-search"
|
|
36
|
+
>
|
|
37
|
+
function
|
|
38
|
+
</div>
|
|
39
|
+
<div
|
|
40
|
+
data-testid="default-filters"
|
|
41
|
+
>
|
|
42
|
+
{"source_id":10,"target_type":"data_structure","source_type":"quality_control"}
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
`;
|
|
@@ -110,6 +110,19 @@ exports[`<RelationTagForm /> matches the latest snapshot 1`] = `
|
|
|
110
110
|
target_type.data_field
|
|
111
111
|
</span>
|
|
112
112
|
</div>
|
|
113
|
+
<div
|
|
114
|
+
aria-checked="false"
|
|
115
|
+
aria-selected="false"
|
|
116
|
+
class="item"
|
|
117
|
+
role="option"
|
|
118
|
+
style="pointer-events: all;"
|
|
119
|
+
>
|
|
120
|
+
<span
|
|
121
|
+
class="text"
|
|
122
|
+
>
|
|
123
|
+
target_type.quality_control
|
|
124
|
+
</span>
|
|
125
|
+
</div>
|
|
113
126
|
</div>
|
|
114
127
|
</div>
|
|
115
128
|
</div>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<StructureLinkForm /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="ui hidden divider"
|
|
7
|
+
/>
|
|
8
|
+
<div>
|
|
9
|
+
RelationTagsLoader
|
|
10
|
+
</div>
|
|
11
|
+
<div>
|
|
12
|
+
<div>
|
|
13
|
+
TagTypeDropdownSelector
|
|
14
|
+
</div>
|
|
15
|
+
<button>
|
|
16
|
+
relates_to
|
|
17
|
+
</button>
|
|
18
|
+
</div>
|
|
19
|
+
<div
|
|
20
|
+
class="ui hidden divider"
|
|
21
|
+
/>
|
|
22
|
+
<div>
|
|
23
|
+
<div>
|
|
24
|
+
StructureSelector
|
|
25
|
+
</div>
|
|
26
|
+
<button>
|
|
27
|
+
Select Structure
|
|
28
|
+
</button>
|
|
29
|
+
</div>
|
|
30
|
+
<div
|
|
31
|
+
class="ui hidden divider"
|
|
32
|
+
/>
|
|
33
|
+
<div
|
|
34
|
+
class="ui buttons"
|
|
35
|
+
>
|
|
36
|
+
<button
|
|
37
|
+
class="ui primary disabled button"
|
|
38
|
+
disabled=""
|
|
39
|
+
tabindex="-1"
|
|
40
|
+
>
|
|
41
|
+
actions.create
|
|
42
|
+
</button>
|
|
43
|
+
<button>
|
|
44
|
+
actions.cancel
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
`;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import useSWRMutations from "swr/mutation";
|
|
2
|
+
import { apiJsonPost, apiJsonDelete } from "@truedat/core/services/api";
|
|
3
|
+
import {
|
|
4
|
+
API_RELATION,
|
|
5
|
+
API_RELATIONS,
|
|
6
|
+
API_RELATIONS_INDEX_SEARCH,
|
|
7
|
+
API_RELATIONS_FILTERS,
|
|
8
|
+
} from "../api";
|
|
9
|
+
import { compile } from "path-to-regexp";
|
|
10
|
+
|
|
11
|
+
export const useRelations = (params) => {
|
|
12
|
+
const query = queryString(params);
|
|
13
|
+
return useSWRMutations(API_RELATIONS_INDEX_SEARCH, (url, { arg }) =>
|
|
14
|
+
apiJsonPost(`${url}${query}`, arg)
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const useRelationFilters = (params) => {
|
|
19
|
+
const query = queryString(params);
|
|
20
|
+
return useSWRMutations(API_RELATIONS_FILTERS, (url, { arg }) =>
|
|
21
|
+
apiJsonPost(`${url}${query}`, arg)
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const useCreateRelation = () => {
|
|
26
|
+
return useSWRMutations(
|
|
27
|
+
API_RELATIONS,
|
|
28
|
+
(url, { arg }) => apiJsonPost(url, arg),
|
|
29
|
+
{
|
|
30
|
+
onError: (error) => {
|
|
31
|
+
// Review! This is a workaround to prevent the middleware from dispatching logError in redux store
|
|
32
|
+
// Remove errors from response to prevent middleware from dispatching logError
|
|
33
|
+
// The error will still be available in the catch block
|
|
34
|
+
if (error?.response?.data?.errors) {
|
|
35
|
+
// Store original errors for our custom handling
|
|
36
|
+
error._originalErrors = error.response.data.errors;
|
|
37
|
+
// Remove errors so middleware doesn't dispatch logError
|
|
38
|
+
delete error.response.data.errors;
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const useDeleteRelation = () => {
|
|
46
|
+
return useSWRMutations(API_RELATION, (_url, { arg }) =>
|
|
47
|
+
apiJsonDelete(compile(API_RELATION)({ id: `${arg}` }))
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const queryString = (params) => {
|
|
52
|
+
return params ? `?${new URLSearchParams(params).toString()}` : "";
|
|
53
|
+
};
|
package/src/messages/en.js
CHANGED
package/src/messages/es.js
CHANGED
package/src/selectors/index.js
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
|
|
3
|
+
export const getRelationErrorAlertMessage = (error, formatMessage) => {
|
|
4
|
+
if (!error._originalErrors) {
|
|
5
|
+
return {
|
|
6
|
+
error: true,
|
|
7
|
+
content: error.message || "An error occurred",
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const errors = error?._originalErrors;
|
|
12
|
+
|
|
13
|
+
if (errors) {
|
|
14
|
+
const errorMessages = _.flow(
|
|
15
|
+
_.toPairs,
|
|
16
|
+
_.map(([field, messages]) => ({
|
|
17
|
+
field,
|
|
18
|
+
message: Array.isArray(messages) ? messages[0] : messages,
|
|
19
|
+
}))
|
|
20
|
+
)(errors);
|
|
21
|
+
|
|
22
|
+
const content = errorMessages
|
|
23
|
+
.map(({ field, message }) =>
|
|
24
|
+
formatMessage({
|
|
25
|
+
id: `relations.create.failed.${message}`,
|
|
26
|
+
defaultMessage: `${field}: ${message}`,
|
|
27
|
+
})
|
|
28
|
+
)
|
|
29
|
+
.join(", ");
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
error: true,
|
|
33
|
+
header: "relations.create.failed.header",
|
|
34
|
+
icon: "attention",
|
|
35
|
+
content,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const errorMessage =
|
|
40
|
+
data?.error?.message || data?.error || "An error occurred";
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
error: true,
|
|
44
|
+
header: "relations.create.failed.header",
|
|
45
|
+
icon: "attention",
|
|
46
|
+
content: errorMessage,
|
|
47
|
+
};
|
|
48
|
+
};
|