@truedat/bg 7.12.6 → 7.13.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/bg",
3
- "version": "7.12.6",
3
+ "version": "7.13.1",
4
4
  "description": "Truedat Web Business Glossary",
5
5
  "sideEffects": false,
6
6
  "module": "src/index.js",
@@ -48,7 +48,7 @@
48
48
  "@testing-library/jest-dom": "^6.6.3",
49
49
  "@testing-library/react": "^16.3.0",
50
50
  "@testing-library/user-event": "^14.6.1",
51
- "@truedat/test": "7.12.6",
51
+ "@truedat/test": "7.13.1",
52
52
  "identity-obj-proxy": "^3.0.0",
53
53
  "jest": "^29.7.0",
54
54
  "redux-saga-test-plan": "^4.0.6"
@@ -81,5 +81,5 @@
81
81
  "semantic-ui-react": "^3.0.0-beta.2",
82
82
  "swr": "^2.3.3"
83
83
  },
84
- "gitHead": "2bff250dd5ea89920a3ec641209716ad53a51062"
84
+ "gitHead": "17c040e6539b00eab7870d0d82ac91910e85b7ac"
85
85
  }
@@ -7,6 +7,7 @@ import Moment from "react-moment";
7
7
  export const EventRow = ({ user, user_name, event, ts, payload }) => {
8
8
  const { formatMessage, locale } = useIntl();
9
9
  const userName = _.propOr(user_name, "user_name")(user);
10
+ const eventVia = payload.length > 0 ? payload[0].msg_via : null;
10
11
 
11
12
  return (
12
13
  <Feed.Event>
@@ -14,20 +15,23 @@ export const EventRow = ({ user, user_name, event, ts, payload }) => {
14
15
  <Feed.Summary>
15
16
  <Feed.User>{userName}</Feed.User>{" "}
16
17
  {formatMessage({ id: `concept.events.${event}` })}{" "}
18
+ {eventVia ? <span className="audit-event-via">{formatMessage({ id: `audit.events.via.${eventVia}` })}</span> : null}{" "}
17
19
  <Feed.Date>
18
20
  <Moment locale={locale} date={ts} format="YYYY-MM-DD HH:mm" />
19
21
  </Feed.Date>
20
22
  </Feed.Summary>
21
- {payload.map(({ msg_key, msg_params }, i) => (
22
- <Feed.Extra
23
- key={i}
24
- text
25
- content={formatMessage(
26
- { id: `concept.events.extra.${msg_key}` },
27
- msg_params
28
- )}
29
- />
30
- ))}
23
+ {payload.map(({ msg_key, msg_params }, i) =>
24
+ msg_key ? (
25
+ <Feed.Extra
26
+ key={i}
27
+ text
28
+ content={formatMessage(
29
+ { id: `concept.events.extra.${msg_key}` },
30
+ msg_params
31
+ )}
32
+ />
33
+ ) : null
34
+ )}
31
35
  </Feed.Content>
32
36
  </Feed.Event>
33
37
  );
@@ -28,4 +28,30 @@ describe("<Events />", () => {
28
28
  await waitForLoad(rendered);
29
29
  expect(rendered.container).toMatchSnapshot();
30
30
  });
31
+ it("matches the latest snapshot using payload with msg_key, msg_params and msg_via", async () => {
32
+ const events = [
33
+ {
34
+ id: 1,
35
+ event: "new_concept_draft",
36
+ service: "My invented service 1",
37
+ resource_id: 2,
38
+ payload: [
39
+ { msg_key: "created_version", msg_params: ["1"], msg_via: "single_update" },
40
+ ],
41
+ },
42
+ {
43
+ id: 2,
44
+ event: "concept_published",
45
+ service: "My invented service 2",
46
+ resource_id: 2,
47
+ payload: [
48
+ { msg_via: "file" },
49
+ ],
50
+ },
51
+ ];
52
+ const props = { events };
53
+ const rendered = render(<Events {...props} />);
54
+ await waitForLoad(rendered);
55
+ expect(rendered.container).toMatchSnapshot();
56
+ });
31
57
  });
@@ -23,6 +23,7 @@ exports[`<Events /> matches the latest snapshot 1`] = `
23
23
 
24
24
  concept.events.undefined
25
25
 
26
+
26
27
  <div
27
28
  class="date"
28
29
  >
@@ -33,18 +34,49 @@ exports[`<Events /> matches the latest snapshot 1`] = `
33
34
  </time>
34
35
  </div>
35
36
  </div>
37
+ </div>
38
+ </div>
39
+ <div
40
+ class="event"
41
+ >
42
+ <div
43
+ class="content"
44
+ >
36
45
  <div
37
- class="text extra"
38
- >
39
- concept.events.extra.undefined
40
- </div>
41
- <div
42
- class="text extra"
46
+ class="summary"
43
47
  >
44
- concept.events.extra.undefined
48
+ <a
49
+ class="user"
50
+ />
51
+
52
+ concept.events.undefined
53
+
54
+
55
+ <div
56
+ class="date"
57
+ >
58
+ <time
59
+ datetime="1735689600000"
60
+ >
61
+ 2025-01-01 00:00
62
+ </time>
63
+ </div>
45
64
  </div>
46
65
  </div>
47
66
  </div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ `;
71
+
72
+ exports[`<Events /> matches the latest snapshot using payload with msg_key, msg_params and msg_via 1`] = `
73
+ <div>
74
+ <div
75
+ class="ui bottom attached segment"
76
+ >
77
+ <div
78
+ class="ui small feed"
79
+ >
48
80
  <div
49
81
  class="event"
50
82
  >
@@ -58,7 +90,13 @@ exports[`<Events /> matches the latest snapshot 1`] = `
58
90
  class="user"
59
91
  />
60
92
 
61
- concept.events.undefined
93
+ concept.events.new_concept_draft
94
+
95
+ <span
96
+ class="audit-event-via"
97
+ >
98
+ audit.events.via.single_update
99
+ </span>
62
100
 
63
101
  <div
64
102
  class="date"
@@ -73,12 +111,40 @@ exports[`<Events /> matches the latest snapshot 1`] = `
73
111
  <div
74
112
  class="text extra"
75
113
  >
76
- concept.events.extra.undefined
114
+ concept.events.extra.created_version
77
115
  </div>
116
+ </div>
117
+ </div>
118
+ <div
119
+ class="event"
120
+ >
121
+ <div
122
+ class="content"
123
+ >
78
124
  <div
79
- class="text extra"
125
+ class="summary"
80
126
  >
81
- concept.events.extra.undefined
127
+ <a
128
+ class="user"
129
+ />
130
+
131
+ concept.events.concept_published
132
+
133
+ <span
134
+ class="audit-event-via"
135
+ >
136
+ audit.events.via.file
137
+ </span>
138
+
139
+ <div
140
+ class="date"
141
+ >
142
+ <time
143
+ datetime="1735689600000"
144
+ >
145
+ 2025-01-01 00:00
146
+ </time>
147
+ </div>
82
148
  </div>
83
149
  </div>
84
150
  </div>
@@ -5,11 +5,18 @@ import { connect } from "react-redux";
5
5
  import { Button, Icon } from "semantic-ui-react";
6
6
  import { FormattedMessage } from "react-intl";
7
7
  import { ConfirmModal } from "@truedat/core/components";
8
- import { conceptLinkAction } from "../routines";
8
+ import { deleteRelation } from "@truedat/lm/routines";
9
9
 
10
- export const DeleteLink = ({ conceptLinkAction, action, loading }) => {
10
+ export const DeleteLink = ({ deleteRelation, action: { href }, loading }) => {
11
11
  const [openModal, setOpenModal] = useState(false);
12
12
 
13
+ const handleDelete = () => {
14
+ const linkId = href ? parseInt(href.match(/\/links\/(\d+)$/)?.[1]) : null;
15
+ if (linkId) {
16
+ deleteRelation({ id: linkId });
17
+ }
18
+ };
19
+
13
20
  return loading ? (
14
21
  <Icon loading name="circle notch" color="red" />
15
22
  ) : (
@@ -33,7 +40,7 @@ export const DeleteLink = ({ conceptLinkAction, action, loading }) => {
33
40
  content={
34
41
  <FormattedMessage id="relations.actions.data_field.delete.confirmation.content" />
35
42
  }
36
- onConfirm={() => conceptLinkAction(action)}
43
+ onConfirm={handleDelete}
37
44
  onOpen={() => {
38
45
  setOpenModal(true);
39
46
  }}
@@ -46,18 +53,16 @@ export const DeleteLink = ({ conceptLinkAction, action, loading }) => {
46
53
  };
47
54
 
48
55
  DeleteLink.propTypes = {
49
- conceptLinkAction: PropTypes.func,
56
+ deleteRelation: PropTypes.func,
50
57
  action: PropTypes.object,
51
58
  loading: PropTypes.bool,
52
59
  };
53
60
 
54
- const mapStateToProps = ({ conceptLinkActionLoading }, ownProps) => ({
55
- loading: _.pathEq("action.href")(conceptLinkActionLoading)(ownProps),
61
+ const mapStateToProps = ({ deleteRelationLoading }, { action }) => ({
62
+ loading: _.pathEq("id")(deleteRelationLoading)(action),
56
63
  });
57
64
 
58
- export const DeleteLinkConnected = connect(mapStateToProps, {
59
- conceptLinkAction,
60
- })(DeleteLink);
65
+ export const DeleteLinkConnected = connect(mapStateToProps, { deleteRelation, })(DeleteLink);
61
66
 
62
67
  export const LinkActions = ({ delete: deleteAction }) =>
63
68
  deleteAction ? <DeleteLinkConnected action={deleteAction} /> : null;
@@ -0,0 +1,116 @@
1
+ import React from "react";
2
+ import userEvent from "@testing-library/user-event";
3
+ import { render, waitForLoad } from "@truedat/test/render";
4
+ import { DeleteLink } from "../ConceptLinkActions";
5
+
6
+ describe("ConceptLinkActions", () => {
7
+ describe("DeleteLink", () => {
8
+ const deleteRelation = jest.fn();
9
+ const user = userEvent.setup({ delay: null });
10
+
11
+ afterEach(() => {
12
+ jest.clearAllMocks();
13
+ });
14
+
15
+ it("should render loading icon when loading is true", () => {
16
+ const action = { href: "/api/links/123" };
17
+ const rendered = render(
18
+ <DeleteLink deleteRelation={deleteRelation} action={action} loading={true} />
19
+ );
20
+
21
+ expect(rendered.container.querySelector(".icon.loading")).toBeInTheDocument();
22
+ expect(rendered.container).toMatchSnapshot();
23
+ });
24
+
25
+ it("should render delete button when loading is false", async () => {
26
+ const action = { href: "/api/links/123" };
27
+ const rendered = render(
28
+ <DeleteLink deleteRelation={deleteRelation} action={action} loading={false} />
29
+ );
30
+ await waitForLoad(rendered);
31
+
32
+ expect(rendered.getByRole("button")).toBeInTheDocument();
33
+ expect(rendered.container).toMatchSnapshot();
34
+ });
35
+
36
+ it("should extract link id from href and call deleteRelation on confirm", async () => {
37
+ const action = { href: "/api/links/456" };
38
+ const rendered = render(
39
+ <DeleteLink deleteRelation={deleteRelation} action={action} loading={false} />
40
+ );
41
+ await waitForLoad(rendered);
42
+
43
+ await user.click(rendered.getByRole("button"));
44
+
45
+ const confirmButton = rendered.getByLabelText("modal-affirmative-action");
46
+ await user.click(confirmButton);
47
+
48
+ expect(deleteRelation).toHaveBeenCalledWith({ id: 456 });
49
+ });
50
+
51
+ it("should handle href without link id", async () => {
52
+ const action = { href: "/api/other/endpoint" };
53
+ const rendered = render(
54
+ <DeleteLink deleteRelation={deleteRelation} action={action} loading={false} />
55
+ );
56
+ await waitForLoad(rendered);
57
+
58
+ await user.click(rendered.getByRole("button"));
59
+
60
+ const confirmButton = rendered.getByLabelText("modal-affirmative-action");
61
+ await user.click(confirmButton);
62
+
63
+ expect(deleteRelation).not.toHaveBeenCalled();
64
+ });
65
+
66
+ it("should handle null href", async () => {
67
+ const action = { href: null };
68
+ const rendered = render(
69
+ <DeleteLink deleteRelation={deleteRelation} action={action} loading={false} />
70
+ );
71
+ await waitForLoad(rendered);
72
+
73
+ await user.click(rendered.getByRole("button"));
74
+
75
+ const confirmButton = rendered.getByLabelText("modal-affirmative-action");
76
+ await user.click(confirmButton);
77
+
78
+ expect(deleteRelation).not.toHaveBeenCalled();
79
+ });
80
+
81
+ it("should handle undefined href", async () => {
82
+ const action = {};
83
+ const rendered = render(
84
+ <DeleteLink deleteRelation={deleteRelation} action={action} loading={false} />
85
+ );
86
+ await waitForLoad(rendered);
87
+
88
+ await user.click(rendered.getByRole("button"));
89
+
90
+ const confirmButton = rendered.getByLabelText("modal-affirmative-action");
91
+ await user.click(confirmButton);
92
+
93
+ expect(deleteRelation).not.toHaveBeenCalled();
94
+ });
95
+
96
+ it("should open and close modal correctly", async () => {
97
+ const action = { href: "/api/links/789" };
98
+ const rendered = render(
99
+ <DeleteLink deleteRelation={deleteRelation} action={action} loading={false} />
100
+ );
101
+ await waitForLoad(rendered);
102
+
103
+ await user.click(rendered.getByRole("button"));
104
+
105
+ const confirmButton = rendered.getByLabelText("modal-affirmative-action");
106
+ expect(confirmButton).toBeInTheDocument();
107
+
108
+ const cancelButton = rendered.getByLabelText("modal-negative-action");
109
+ await user.click(cancelButton);
110
+
111
+ expect(rendered.queryByLabelText("modal-affirmative-action")).not.toBeInTheDocument();
112
+ expect(deleteRelation).not.toHaveBeenCalled();
113
+ });
114
+ });
115
+ });
116
+
@@ -0,0 +1,23 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ConceptLinkActions DeleteLink should render delete button when loading is false 1`] = `
4
+ <div>
5
+ <button
6
+ class="ui red mini basic icon button"
7
+ >
8
+ <i
9
+ aria-hidden="true"
10
+ class="red trash alternate outline icon"
11
+ />
12
+ </button>
13
+ </div>
14
+ `;
15
+
16
+ exports[`ConceptLinkActions DeleteLink should render loading icon when loading is true 1`] = `
17
+ <div>
18
+ <i
19
+ aria-hidden="true"
20
+ class="red circle notch loading icon"
21
+ />
22
+ </div>
23
+ `;