@truedat/dd 7.14.1 → 7.14.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dd",
3
- "version": "7.14.1",
3
+ "version": "7.14.3",
4
4
  "description": "Truedat Web Data Dictionary",
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.14.1",
51
+ "@truedat/test": "7.14.3",
52
52
  "identity-obj-proxy": "^3.0.0",
53
53
  "jest": "^29.7.0",
54
54
  "redux-saga-test-plan": "^4.0.6"
@@ -83,5 +83,5 @@
83
83
  "svg-pan-zoom": "^3.6.2",
84
84
  "swr": "^2.3.3"
85
85
  },
86
- "gitHead": "afc55f553bb99ed37bfcc85fc83ba815d48b4769"
86
+ "gitHead": "f8b2f517aa25cf1eab47a89b17e0b08dfe6c8a26"
87
87
  }
@@ -72,6 +72,7 @@ export const StructureSelector = (props) => {
72
72
  const pageSize = _.prop("pageSize")(props);
73
73
  const withGrantRequests = _.prop("withGrantRequests")(props);
74
74
  const withDataFields = _.prop("withDataFields")(props);
75
+ const withLinkStructures = _.prop("withLinkStructures")(props);
75
76
  const defaultOverwriteColumns = useSelector(
76
77
  defaultColumnsForStructureSelector
77
78
  );
@@ -83,6 +84,7 @@ export const StructureSelector = (props) => {
83
84
  const enrichSearchPayload = {
84
85
  my_grant_requests: !!withGrantRequests,
85
86
  with_data_fields: !!withDataFields,
87
+ link_structures: !!withLinkStructures,
86
88
  };
87
89
 
88
90
  const searchProps = {
@@ -65,7 +65,6 @@ export const StructureTabPane = ({ path }) => {
65
65
  structureTabsOrder,
66
66
  customTabs,
67
67
  });
68
-
69
68
  return (
70
69
  <ErrorBoundary>
71
70
  {activeTab === "fields" && <StructureFields />}
@@ -184,7 +184,7 @@ export const StructureTabs = ({ path }) => {
184
184
  ])
185
185
  : null;
186
186
 
187
- structureTabsOrder
187
+ const sortedItems = items && structureTabsOrder
188
188
  ? items.sort((a, b) => {
189
189
  const aIndex = _.indexOf(a.key)(structureTabsOrder);
190
190
  const bIndex = _.indexOf(b.key)(structureTabsOrder);
@@ -208,7 +208,7 @@ export const StructureTabs = ({ path }) => {
208
208
  secondary
209
209
  pointing
210
210
  tabular
211
- items={items}
211
+ items={sortedItems}
212
212
  className="tab-overflow"
213
213
  />
214
214
  <Divider hidden />
@@ -2,14 +2,7 @@ import _ from "lodash/fp";
2
2
  import React from "react";
3
3
  import { useIntl } from "react-intl";
4
4
  import { Routes, Route, useParams } from "react-router";
5
- import {
6
- STRUCTURE,
7
- STRUCTURES,
8
- STRUCTURES_BULK_UPDATE,
9
- STRUCTURE_EVENTS,
10
- STRUCTURE_VERSION,
11
- STRUCTURE_MEMBERS_NEW,
12
- } from "@truedat/core/routes";
5
+
13
6
  import { useSelector } from "react-redux";
14
7
  import AddStructureMember from "./AddStructureMember";
15
8
  import StructureCrumbs from "./StructureCrumbs";
@@ -35,4 +35,58 @@ describe("<StructureTabs />", () => {
35
35
  await waitForLoad(rendered);
36
36
  expect(rendered.container).toMatchSnapshot();
37
37
  });
38
+
39
+ it("should render correctly with structureTabsOrder", async () => {
40
+ jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
41
+
42
+ const structureTabsOrder = ["links", "notes", "fields", "profile"];
43
+ const props = { path: "/structures/:id" };
44
+ const rendered = render(<StructureTabs {...props} />, {
45
+ state: { structure, structureVersion: version, structureTabsOrder },
46
+ });
47
+ await waitForLoad(rendered);
48
+
49
+ const menuItems = rendered.container.querySelectorAll(".item");
50
+ expect(menuItems.length).toBeGreaterThan(0);
51
+ expect(rendered.container).toMatchSnapshot();
52
+ });
53
+
54
+ it("should use default order when structureTabsOrder is empty", async () => {
55
+ jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
56
+
57
+ const props = { path: "/structures/:id" };
58
+ const rendered = render(<StructureTabs {...props} />, {
59
+ state: { structure, structureVersion: version, structureTabsOrder: [] },
60
+ });
61
+ await waitForLoad(rendered);
62
+
63
+ const menuItems = rendered.container.querySelectorAll(".item");
64
+ expect(menuItems.length).toBeGreaterThan(0);
65
+ });
66
+
67
+ it("should handle structureTabsOrder with tabs not in items", async () => {
68
+ jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
69
+
70
+ const structureTabsOrder = ["nonexistent", "fields", "notes"];
71
+ const props = { path: "/structures/:id" };
72
+ const rendered = render(<StructureTabs {...props} />, {
73
+ state: { structure, structureVersion: version, structureTabsOrder },
74
+ });
75
+ await waitForLoad(rendered);
76
+
77
+ const menuItems = rendered.container.querySelectorAll(".item");
78
+ expect(menuItems.length).toBeGreaterThan(0);
79
+ });
80
+
81
+ it("should not crash when structure is empty", async () => {
82
+ jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
83
+
84
+ const props = { path: "/structures/:id" };
85
+ const rendered = render(<StructureTabs {...props} />, {
86
+ state: { structure: null, structureVersion: version, structureTabsOrder: [] },
87
+ });
88
+ await waitForLoad(rendered);
89
+
90
+ expect(rendered.container).toBeTruthy();
91
+ });
38
92
  });
@@ -81,3 +81,85 @@ exports[`<StructureTabs /> matches the latest snapshot: metadata 1`] = `
81
81
  />
82
82
  </div>
83
83
  `;
84
+
85
+ exports[`<StructureTabs /> should render correctly with structureTabsOrder 1`] = `
86
+ <div>
87
+ <div
88
+ class="ui pointing secondary top attached tabular tab-overflow menu"
89
+ >
90
+ <a
91
+ class="active item"
92
+ data-discover="true"
93
+ href="/structures/123/links"
94
+ >
95
+ tabs.dd.links
96
+ </a>
97
+ <a
98
+ class="item"
99
+ data-discover="true"
100
+ href="/structures/123/notes"
101
+ >
102
+ tabs.dd.notes
103
+ </a>
104
+ <a
105
+ class="item"
106
+ data-discover="true"
107
+ href="/structures/123/versions/1/fields"
108
+ >
109
+ tabs.dd.fields
110
+ </a>
111
+ <a
112
+ class="item"
113
+ data-discover="true"
114
+ href="/structures/123/profile"
115
+ >
116
+ tabs.dd.profile
117
+ </a>
118
+ <a
119
+ class="item"
120
+ data-discover="true"
121
+ href="/structures/123/grants"
122
+ >
123
+ tabs.dd.grants
124
+ </a>
125
+ <a
126
+ class="item"
127
+ data-discover="true"
128
+ href="/structures/123/metadata"
129
+ >
130
+ tabs.dd.metadata
131
+ </a>
132
+ <a
133
+ class="item"
134
+ data-discover="true"
135
+ href="/structures/123/rules"
136
+ >
137
+ tabs.dd.rules
138
+ </a>
139
+ <a
140
+ class="item"
141
+ data-discover="true"
142
+ href="/structures/123/versions/1/versions"
143
+ >
144
+ tabs.dd.versions
145
+ </a>
146
+ <a
147
+ class="item"
148
+ data-discover="true"
149
+ href="/structures/123/events"
150
+ >
151
+ tabs.dd.audit
152
+ </a>
153
+ <a
154
+ class="item"
155
+ data-discover="true"
156
+ href="/structures/123/members"
157
+ >
158
+ tabs.dd.roles
159
+ </a>
160
+ </div>
161
+ <div
162
+ class="ui hidden divider"
163
+ />
164
+ </div>
165
+ `;
@@ -75,6 +75,68 @@ describe("selectors: defaultTab", () => {
75
75
  expect(defaultTab({ fields }, tabsOrder)).toBe("fields");
76
76
  expect(defaultTab({}, tabsOrder)).toBe(undefined);
77
77
  });
78
+
79
+ it("should return the first visible tab according to custom order when all tabs are visible", () => {
80
+ const tabsOrder = ["links", "fields", "notes", "metadata"];
81
+ const tabVisibility = {
82
+ links: true,
83
+ fields: true,
84
+ notes: true,
85
+ metadata: true,
86
+ children: true,
87
+ grants: true,
88
+ };
89
+ expect(defaultTab(tabVisibility, tabsOrder)).toBe("links");
90
+ });
91
+
92
+ it("should return the first visible tab in custom order, skipping invisible ones", () => {
93
+ const tabsOrder = ["links", "fields", "notes", "metadata"];
94
+ const tabVisibility = {
95
+ links: false,
96
+ fields: true,
97
+ notes: true,
98
+ metadata: true,
99
+ };
100
+ expect(defaultTab(tabVisibility, tabsOrder)).toBe("fields");
101
+ });
102
+
103
+ it("should include custom tabs in the order when provided", () => {
104
+ const tabsOrder = ["links", "fields"];
105
+ const customTabs = [{ name: "custom1" }, { name: "custom2" }];
106
+ const tabVisibility = {
107
+ links: false,
108
+ fields: false,
109
+ };
110
+ expect(defaultTab(tabVisibility, tabsOrder, customTabs)).toBe("custom1");
111
+ });
112
+
113
+ it("should place custom tabs after tabsOrder when tabsOrder is provided", () => {
114
+ const tabsOrder = ["links", "fields"];
115
+ const customTabs = [{ name: "custom1" }, { name: "custom2" }];
116
+ const tabVisibility = {
117
+ links: true,
118
+ fields: true,
119
+ };
120
+ expect(defaultTab(tabVisibility, tabsOrder, customTabs)).toBe("links");
121
+ });
122
+
123
+ it("should place custom tabs at the end when no tabsOrder is provided", () => {
124
+ const customTabs = [{ name: "custom1" }, { name: "custom2" }];
125
+ const tabVisibility = {
126
+ fields: true,
127
+ };
128
+ expect(defaultTab(tabVisibility, [], customTabs)).toBe("fields");
129
+ });
130
+
131
+ it("should return custom tab as first when it is first in tabsOrder", () => {
132
+ const tabsOrder = ["custom", "links", "fields"];
133
+ const customTabs = [{ name: "custom", route: "/structures/:id/custom" }];
134
+ const tabVisibility = {
135
+ links: true,
136
+ fields: true,
137
+ };
138
+ expect(defaultTab(tabVisibility, tabsOrder, customTabs)).toBe("custom");
139
+ });
78
140
  });
79
141
 
80
142
  describe("selectors: getActiveTab", () => {
@@ -103,4 +165,86 @@ describe("selectors: getActiveTab", () => {
103
165
  expect(getActiveTab({ tabVisibility, path: "foo" })).toBe(undefined);
104
166
  expect(getActiveTab({ tabVisibility, path: customPath, customTabs: [{ name: "custom", route: customPath }] })).toBe("custom");
105
167
  });
168
+
169
+ it("should return the first visible tab according to structureTabsOrder when path is STRUCTURE", () => {
170
+ const structureTabsOrder = ["links", "fields", "notes"];
171
+ const tabVisibility = {
172
+ links: true,
173
+ fields: true,
174
+ notes: true,
175
+ };
176
+ expect(getActiveTab({ tabVisibility, path: STRUCTURE, structureTabsOrder })).toBe("links");
177
+ });
178
+
179
+ it("should return the first visible tab according to structureTabsOrder when path is STRUCTURE_VERSION", () => {
180
+ const structureTabsOrder = ["notes", "fields", "links"];
181
+ const tabVisibility = {
182
+ notes: true,
183
+ fields: true,
184
+ links: true,
185
+ };
186
+ expect(getActiveTab({ tabVisibility, path: STRUCTURE_VERSION, structureTabsOrder })).toBe("notes");
187
+ });
188
+
189
+ it("should skip invisible tabs in structureTabsOrder and return the first visible one", () => {
190
+ const structureTabsOrder = ["links", "fields", "notes"];
191
+ const tabVisibility = {
192
+ links: false,
193
+ fields: true,
194
+ notes: true,
195
+ };
196
+ expect(getActiveTab({ tabVisibility, path: STRUCTURE, structureTabsOrder })).toBe("fields");
197
+ });
198
+
199
+ it("should return first visible tab from default order when all tabs in structureTabsOrder are invisible", () => {
200
+ const structureTabsOrder = ["links", "versions"];
201
+ const tabVisibility = {
202
+ links: false,
203
+ versions: false,
204
+ fields: true,
205
+ notes: true,
206
+ };
207
+ expect(getActiveTab({ tabVisibility, path: STRUCTURE, structureTabsOrder })).toBe("fields");
208
+ });
209
+
210
+ it("should handle tabs in structureTabsOrder that are not in tabVisibility", () => {
211
+ const structureTabsOrder = ["nonexistent", "fields", "notes"];
212
+ const tabVisibility = {
213
+ fields: true,
214
+ notes: true,
215
+ };
216
+ expect(getActiveTab({ tabVisibility, path: STRUCTURE, structureTabsOrder })).toBe("fields");
217
+ });
218
+
219
+ it("should handle structureLinks, lineage, and impact tabs in custom order", () => {
220
+ const structureTabsOrder = ["lineage", "structureLinks", "impact", "fields"];
221
+ const tabVisibility = {
222
+ lineage: true,
223
+ structureLinks: true,
224
+ impact: true,
225
+ fields: true,
226
+ };
227
+ expect(getActiveTab({ tabVisibility, path: STRUCTURE, structureTabsOrder })).toBe("lineage");
228
+ });
229
+
230
+ it("should return custom tab as first when it is first in structureTabsOrder", () => {
231
+ const structureTabsOrder = ["custom", "links", "fields", "notes"];
232
+ const customTabs = [{ name: "custom", route: "/structures/:id/custom" }];
233
+ const tabVisibility = {
234
+ links: true,
235
+ fields: true,
236
+ notes: true,
237
+ };
238
+ expect(getActiveTab({ tabVisibility, path: STRUCTURE, structureTabsOrder, customTabs })).toBe("custom");
239
+ });
240
+
241
+ it("should skip custom tab if it is not in customTabs array even if in structureTabsOrder", () => {
242
+ const structureTabsOrder = ["custom", "links", "fields"];
243
+ const customTabs = [];
244
+ const tabVisibility = {
245
+ links: true,
246
+ fields: true,
247
+ };
248
+ expect(getActiveTab({ tabVisibility, path: STRUCTURE, structureTabsOrder, customTabs })).toBe("links");
249
+ });
106
250
  });
@@ -36,19 +36,41 @@ export const defaultTab = (tabVisibility, tabsOrder = [], customTabs = []) => {
36
36
  "rules",
37
37
  "roles",
38
38
  "links",
39
+ "structureLinks",
40
+ "lineage",
41
+ "impact",
39
42
  "versions",
40
- ..._.map((tab) => tab.name)(customTabs),
43
+ "events",
41
44
  ];
42
45
 
43
- return _.find((tab) => tabVisibility[tab])([...tabsOrder, ...defaultOrder]);
46
+ const customTabsNames = _.map((tab) => tab.name)(customTabs);
47
+ const customTabsNamesSet = new Set(customTabsNames);
48
+
49
+ const isTabVisible = (tab) => {
50
+ if (customTabsNamesSet.has(tab)) {
51
+ return true;
52
+ }
53
+ return tabVisibility[tab] === true;
54
+ };
55
+
56
+ if (_.isArray(tabsOrder) && _.size(tabsOrder) > 0) {
57
+ for (const tab of tabsOrder) {
58
+ if (isTabVisible(tab)) {
59
+ return tab;
60
+ }
61
+ }
62
+ }
63
+
64
+ const order = [...defaultOrder, ...customTabsNames];
65
+ return _.find((tab) => isTabVisible(tab))(order);
44
66
  };
45
67
 
46
- const getCustomTabs = (customTabs) => _.map((tab) => [_.eq(tab.route), _.constant(tab.name)])(customTabs)
68
+ const getCustomTabs = (customTabs) => _.map((tab) => [_.eq(tab.route), _.constant(tab.name)])(customTabs);
47
69
 
48
- export const getActiveTab = ({ tabVisibility, path, tabsOrder, customTabs }) => {
70
+ export const getActiveTab = ({ tabVisibility, path, structureTabsOrder, customTabs }) => {
49
71
  return _.cond([
50
- [_.eq(STRUCTURE), _.constant(defaultTab(tabVisibility, tabsOrder, customTabs))],
51
- [_.eq(STRUCTURE_VERSION), _.constant(defaultTab(tabVisibility, tabsOrder, customTabs))],
72
+ [_.eq(STRUCTURE), _.constant(defaultTab(tabVisibility, structureTabsOrder, customTabs))],
73
+ [_.eq(STRUCTURE_VERSION), _.constant(defaultTab(tabVisibility, structureTabsOrder, customTabs))],
52
74
  [_.eq(STRUCTURE_VERSION_FIELDS), _.constant("fields")],
53
75
  [_.eq(STRUCTURE_FIELDS), _.constant("fields")],
54
76
  [_.eq(STRUCTURE_CHILDREN), _.constant("children")],