@truedat/qx 7.13.9 → 7.14.0

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.
Files changed (46) hide show
  1. package/package.json +3 -3
  2. package/src/components/common/ClauseViewer.js +183 -21
  3. package/src/components/common/expressions/Condition.js +13 -6
  4. package/src/components/dataViews/DataViewEditor.js +0 -2
  5. package/src/components/dataViews/DataViewSummary.js +73 -0
  6. package/src/components/dataViews/__tests__/AdvancedDataViewEditor.spec.js +4 -1
  7. package/src/components/dataViews/__tests__/DataViewEditor.spec.js +167 -132
  8. package/src/components/dataViews/__tests__/DataViewSummary.spec.js +820 -0
  9. package/src/components/dataViews/__tests__/DataViews.spec.js +57 -17
  10. package/src/components/dataViews/__tests__/SimpleDataViewEditor.spec.js +140 -141
  11. package/src/components/dataViews/__tests__/__snapshots__/AdvancedDataViewEditor.spec.js.snap +963 -759
  12. package/src/components/dataViews/__tests__/__snapshots__/DataViewSelect.spec.js.snap +17 -13
  13. package/src/components/dataViews/__tests__/__snapshots__/DataViewSummary.spec.js.snap +1786 -0
  14. package/src/components/dataViews/__tests__/__snapshots__/Queryable.spec.js.snap +18 -14
  15. package/src/components/dataViews/__tests__/__snapshots__/Queryables.spec.js.snap +18 -14
  16. package/src/components/dataViews/advancedForm/AdvancedDataViewEditor.js +59 -48
  17. package/src/components/dataViews/queryableProperties/Join.js +2 -1
  18. package/src/components/dataViews/queryableProperties/Select.js +22 -30
  19. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/Join.spec.js.snap +1 -1
  20. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/Select.spec.js.snap +37 -25
  21. package/src/components/dataViews/queryableSummaryHelpers.js +101 -0
  22. package/src/components/dataViews/simpleForm/SimpleDataViewEditor.js +9 -4
  23. package/src/components/dataViews/summary/From.js +45 -0
  24. package/src/components/dataViews/summary/GroupBy.js +82 -0
  25. package/src/components/dataViews/summary/Join.js +60 -0
  26. package/src/components/dataViews/summary/Select.js +31 -0
  27. package/src/components/dataViews/summary/Where.js +37 -0
  28. package/src/components/qualityControls/ControlPropertiesView.js +115 -63
  29. package/src/components/qualityControls/EditQualityControl.js +5 -3
  30. package/src/components/qualityControls/NewDraftQualityControl.js +8 -3
  31. package/src/components/qualityControls/NewQualityControl.js +5 -3
  32. package/src/components/qualityControls/QualityControlCrumbs.js +46 -5
  33. package/src/components/qualityControls/QualityControlRoutes.js +3 -1
  34. package/src/components/qualityControls/QualityControls.js +9 -18
  35. package/src/components/qualityControls/QualityControlsLabelResults.js +2 -2
  36. package/src/components/qualityControls/__tests__/__snapshots__/ControlPropertiesView.spec.js.snap +12 -9
  37. package/src/components/qualityControls/__tests__/__snapshots__/EditQualityControl.spec.js.snap +536 -493
  38. package/src/components/qualityControls/__tests__/__snapshots__/NewDraftQualityControl.spec.js.snap +510 -483
  39. package/src/components/qualityControls/__tests__/__snapshots__/NewQualityControl.spec.js.snap +261 -245
  40. package/src/components/qualityControls/__tests__/__snapshots__/QualityControl.spec.js.snap +11 -8
  41. package/src/components/qualityControls/__tests__/__snapshots__/QualityControlCrumbs.spec.js.snap +1 -1
  42. package/src/components/qualityControls/__tests__/__snapshots__/QualityControlHeader.spec.js.snap +1 -1
  43. package/src/components/qualityControls/__tests__/__snapshots__/QualityControls.spec.js.snap +87 -87
  44. package/src/components/qualityControls/__tests__/__snapshots__/QualityControlsLabelResults.spec.js.snap +6 -2
  45. package/src/hooks/useDataViews.js +1 -1
  46. package/src/styles/Expression.less +25 -1
@@ -6,189 +6,224 @@ import DataViewEditor, { DataViewDetailEditor } from "../DataViewEditor";
6
6
 
7
7
  const mockFormatMessage = jest.fn(({ id }) => id);
8
8
  jest.mock("react-intl", () => ({
9
- ...jest.requireActual("react-intl"),
10
- useIntl: () => ({ formatMessage: mockFormatMessage }),
9
+ ...jest.requireActual("react-intl"),
10
+ useIntl: () => ({ formatMessage: mockFormatMessage }),
11
11
  }));
12
12
 
13
13
  jest.mock("@truedat/core/routes", () => ({
14
- DATA_VIEWS: "/dataviews",
14
+ DATA_VIEWS: "/dataviews",
15
15
  }));
16
16
 
17
17
  jest.mock("react-router", () => ({
18
- ...jest.requireActual("react-router"),
19
- useParams: jest.fn(),
18
+ ...jest.requireActual("react-router"),
19
+ useParams: jest.fn(),
20
20
  }));
21
21
 
22
22
  jest.mock("@apollo/client", () => ({
23
- ...jest.requireActual("@apollo/client"),
24
- useQuery: jest.fn(() => ({
25
- data: {
26
- referenceDatasets: [
27
- { id: "10", name: "Álpha" },
28
- { id: "2", name: "Beta" },
29
- ],
30
- },
31
- loading: false,
32
- })),
23
+ ...jest.requireActual("@apollo/client"),
24
+ useQuery: jest.fn(() => ({
25
+ data: {
26
+ referenceDatasets: [
27
+ { id: "10", name: "Álpha" },
28
+ { id: "2", name: "Beta" },
29
+ ],
30
+ },
31
+ loading: false,
32
+ })),
33
33
  }));
34
34
 
35
35
  const mockUseDataViews = jest.fn();
36
36
  const mockUseDataViewFetch = jest.fn();
37
37
 
38
38
  jest.mock("@truedat/qx/hooks/useDataViews", () => ({
39
- useDataViews: () => mockUseDataViews(),
40
- useDataViewFetch: (...args) => mockUseDataViewFetch(...args),
41
- // mutation hooks are irrelevant here; don’t assert on them
42
- useDataViewCreate: () => ({ trigger: jest.fn(), isMutating: false }),
43
- useDataViewUpdate: () => ({ trigger: jest.fn(), isMutating: false }),
44
- useDataViewDelete: () => ({ trigger: jest.fn(), isMutating: false }),
39
+ useDataViews: () => mockUseDataViews(),
40
+ useDataViewFetch: (...args) => mockUseDataViewFetch(...args),
41
+ // mutation hooks are irrelevant here; don’t assert on them
42
+ useDataViewCreate: () => ({ trigger: jest.fn(), isMutating: false }),
43
+ useDataViewUpdate: () => ({ trigger: jest.fn(), isMutating: false }),
44
+ useDataViewDelete: () => ({ trigger: jest.fn(), isMutating: false }),
45
45
  }));
46
46
 
47
- const mockUseFunctions = jest.fn(() => ({ data: { data: [] }, loading: false }));
47
+ const mockUseFunctions = jest.fn(() => ({
48
+ data: { data: [] },
49
+ loading: false,
50
+ }));
48
51
 
49
52
  jest.mock("@truedat/qx/hooks/useFunctions", () => ({
50
- useFunctions: () => mockUseFunctions(),
53
+ useFunctions: () => mockUseFunctions(),
51
54
  }));
52
55
 
53
56
  jest.mock("@truedat/core/components", () => ({
54
- Loading: () => <div data-testid="loading">loading…</div>,
55
- ConfirmModal: ({ trigger, onConfirm }) => <div data-testid="confirm-trigger" onClick={onConfirm}>{trigger}</div>
57
+ Loading: () => <div data-testid="loading">loading…</div>,
58
+ ConfirmModal: ({ trigger, onConfirm }) => (
59
+ <div data-testid="confirm-trigger" onClick={onConfirm}>
60
+ {trigger}
61
+ </div>
62
+ ),
56
63
  }));
57
64
 
58
65
  jest.mock("../advancedForm/AdvancedDataViewEditor", () => () => (
59
- <div data-testid="advanced-editor" />
66
+ <div data-testid="advanced-editor" />
60
67
  ));
61
68
  jest.mock("../simpleForm/SimpleDataViewEditor", () => () => (
62
- <div data-testid="simple-editor" />
69
+ <div data-testid="simple-editor" />
63
70
  ));
64
71
  jest.mock("../BreadCrumb", () => ({ text }) => (
65
- <div data-testid="breadcrumb">{text}</div>
72
+ <div data-testid="breadcrumb">{text}</div>
66
73
  ));
67
74
 
68
75
  beforeEach(() => {
69
- jest.clearAllMocks();
70
- useParams.mockReturnValue({});
71
- require("@apollo/client").useQuery.mockImplementation(() => ({
72
- data: {
73
- referenceDatasets: [
74
- { id: "10", name: "Álpha" },
75
- { id: "2", name: "Beta" },
76
- ],
77
- },
78
- loading: false,
79
- }));
80
- mockUseDataViews.mockReturnValue({ data: { data: [] }, loading: false });
81
- mockUseDataViewFetch.mockReset();
76
+ jest.clearAllMocks();
77
+ useParams.mockReturnValue({});
78
+ require("@apollo/client").useQuery.mockImplementation(() => ({
79
+ data: {
80
+ referenceDatasets: [
81
+ { id: "10", name: "Álpha" },
82
+ { id: "2", name: "Beta" },
83
+ ],
84
+ },
85
+ loading: false,
86
+ }));
87
+ mockUseDataViews.mockReturnValue({ data: { data: [] }, loading: false });
88
+ mockUseDataViewFetch.mockReset();
82
89
  });
83
90
 
84
91
  describe("<DataViewDetailEditor /> (render behavior only)", () => {
85
- it("renders Simple editor by default (guided)", async () => {
86
- const rendered = render(<DataViewDetailEditor dataView={undefined} />);
87
- await waitForLoad(rendered);
88
-
89
- expect(rendered.getByTestId("simple-editor")).toBeInTheDocument();
90
- expect(rendered.queryByTestId("advanced-editor")).not.toBeInTheDocument();
91
- expect(rendered.container.querySelector(".ui.segment.loading")).toBeNull();
92
+ it("renders Simple editor by default (guided)", async () => {
93
+ const rendered = render(<DataViewDetailEditor dataView={undefined} />);
94
+ await waitForLoad(rendered);
92
95
 
93
- const guidedBtn = rendered.getByRole("button", { name: /actions.mode.guided/i });
94
- const advancedBtn = rendered.getByRole("button", { name: /actions.mode.advanced/i });
96
+ expect(rendered.getByTestId("simple-editor")).toBeInTheDocument();
97
+ expect(rendered.queryByTestId("advanced-editor")).not.toBeInTheDocument();
98
+ expect(rendered.container.querySelector(".ui.segment.loading")).toBeNull();
95
99
 
96
- expect(guidedBtn).toBeDisabled();
97
- expect(advancedBtn).not.toBeDisabled();
100
+ const guidedBtn = rendered.getByRole("button", {
101
+ name: /actions.mode.guided/i,
98
102
  });
99
-
100
- it("switches to Advanced editor when advanced toggle is confirmed (state managed here)", async () => {
101
- const user = userEvent.setup({ delay: null });
102
- const rendered = render(<DataViewDetailEditor dataView={undefined} />);
103
- await waitForLoad(rendered);
104
-
105
- const advancedBtn = rendered.getByRole("button", { name: /actions.mode.advanced/i });
106
- expect(advancedBtn).not.toBeDisabled();
107
- await user.click(advancedBtn);
108
-
109
- expect(rendered.getByTestId("advanced-editor")).toBeInTheDocument();
110
- expect(rendered.queryByTestId("simple-editor")).not.toBeInTheDocument();
111
-
112
- expect(rendered.getByRole("button", { name: /actions.mode.advanced/i })).toBeDisabled();
113
- expect(rendered.getByRole("button", { name: /actions.mode.guided/i })).not.toBeDisabled();
103
+ const advancedBtn = rendered.getByRole("button", {
104
+ name: /actions.mode.advanced/i,
114
105
  });
115
106
 
116
- it("when incoming dataView is advanced, shows Advanced editor and disables switching back to guided", async () => {
117
- const dataView = {
118
- id: 7,
119
- name: "Adv View",
120
- description: "",
121
- mode: "advanced",
122
- queryables: [],
123
- select: { type: "select" },
124
- };
125
- const rendered = render(<DataViewDetailEditor dataView={dataView} />);
126
- await waitForLoad(rendered);
127
-
128
- expect(rendered.getByTestId("advanced-editor")).toBeInTheDocument();
129
- expect(rendered.queryByTestId("simple-editor")).not.toBeInTheDocument();
130
-
131
- expect(rendered.getByRole("button", { name: /actions.mode.advanced/i })).toBeDisabled();
132
- expect(rendered.getByRole("button", { name: /actions.mode.guided/i })).toBeDisabled();
133
- });
107
+ expect(guidedBtn).toBeDisabled();
108
+ expect(advancedBtn).not.toBeDisabled();
109
+ });
134
110
 
135
- it("shows loading state when any dependency is loading", () => {
136
- mockUseDataViews.mockReturnValue({ data: { data: [] }, loading: true });
137
- const rendered = render(<DataViewDetailEditor dataView={undefined} />);
111
+ it("switches to Advanced editor when advanced toggle is confirmed (state managed here)", async () => {
112
+ const user = userEvent.setup({ delay: null });
113
+ const rendered = render(<DataViewDetailEditor dataView={undefined} />);
114
+ await waitForLoad(rendered);
138
115
 
139
- expect(rendered.container.querySelector(".ui.segment.loading")).not.toBeNull();
116
+ const advancedBtn = rendered.getByRole("button", {
117
+ name: /actions.mode.advanced/i,
140
118
  });
119
+ expect(advancedBtn).not.toBeDisabled();
120
+ await user.click(advancedBtn);
121
+
122
+ expect(rendered.getByTestId("advanced-editor")).toBeInTheDocument();
123
+ expect(rendered.queryByTestId("simple-editor")).not.toBeInTheDocument();
124
+
125
+ expect(
126
+ rendered.getByRole("button", { name: /actions.mode.advanced/i })
127
+ ).toBeDisabled();
128
+ expect(
129
+ rendered.getByRole("button", { name: /actions.mode.guided/i })
130
+ ).not.toBeDisabled();
131
+ });
132
+
133
+ it("when incoming dataView is advanced, shows Advanced editor and disables switching back to guided", async () => {
134
+ const dataView = {
135
+ id: 7,
136
+ name: "Adv View",
137
+ description: "",
138
+ mode: "advanced",
139
+ queryables: [],
140
+ select: { type: "select" },
141
+ };
142
+ const rendered = render(<DataViewDetailEditor dataView={dataView} />);
143
+ await waitForLoad(rendered);
144
+
145
+ expect(rendered.getByTestId("advanced-editor")).toBeInTheDocument();
146
+ expect(rendered.queryByTestId("simple-editor")).not.toBeInTheDocument();
147
+
148
+ expect(
149
+ rendered.getByRole("button", { name: /actions.mode.advanced/i })
150
+ ).toBeDisabled();
151
+ expect(
152
+ rendered.getByRole("button", { name: /actions.mode.guided/i })
153
+ ).toBeDisabled();
154
+ });
155
+
156
+ it("shows loading state when any dependency is loading", () => {
157
+ mockUseDataViews.mockReturnValue({ data: { data: [] }, loading: true });
158
+ const rendered = render(<DataViewDetailEditor dataView={undefined} />);
159
+
160
+ expect(
161
+ rendered.container.querySelector(".ui.segment.loading")
162
+ ).not.toBeNull();
163
+ });
141
164
  });
142
165
 
143
166
  describe("<DataViewEditor /> (render branches only)", () => {
144
- it("renders 'new' breadcrumb and detail editor when there is no id param", async () => {
145
- const rendered = render(<DataViewEditor />);
146
- await waitForLoad(rendered);
147
-
148
- expect(rendered.getByTestId("breadcrumb")).toHaveTextContent("dataViews.new");
149
- expect(rendered.getByTestId("simple-editor")).toBeInTheDocument();
167
+ it("renders 'new' breadcrumb and detail editor when there is no id param", async () => {
168
+ const rendered = render(<DataViewEditor />);
169
+ await waitForLoad(rendered);
170
+
171
+ expect(rendered.getByTestId("breadcrumb")).toHaveTextContent(
172
+ "dataViews.new"
173
+ );
174
+ expect(rendered.getByTestId("simple-editor")).toBeInTheDocument();
175
+ });
176
+
177
+ it("shows Loading while fetching an existing dataview", async () => {
178
+ useParams.mockReturnValue({ id: 1 });
179
+ mockUseDataViewFetch.mockReturnValue({
180
+ dataView: null,
181
+ loading: true,
182
+ error: undefined,
150
183
  });
184
+ const rendered = render(<DataViewEditor />);
151
185
 
152
- it("shows Loading while fetching an existing dataview", async () => {
153
- useParams.mockReturnValue({ id: 1 });
154
- mockUseDataViewFetch.mockReturnValue({ dataView: null, loading: true, error: undefined });
155
- const rendered = render(<DataViewEditor />);
186
+ expect(rendered.getByTestId("loading")).toBeInTheDocument();
187
+ });
156
188
 
157
- expect(rendered.getByTestId("loading")).toBeInTheDocument();
189
+ it("renders nothing when fetch returns error", async () => {
190
+ useParams.mockReturnValue({ id: 1 });
191
+ mockUseDataViewFetch.mockReturnValue({
192
+ dataView: null,
193
+ loading: false,
194
+ error: { foo: "bar" },
158
195
  });
159
196
 
160
- it("renders nothing when fetch returns error", async () => {
161
- useParams.mockReturnValue({ id: 1 });
162
- mockUseDataViewFetch.mockReturnValue({ dataView: null, loading: false, error: { foo: "bar" } });
163
-
164
- const rendered = render(<DataViewEditor />);
165
- await waitForLoad(rendered);
166
-
167
- expect(rendered.queryByTestId("breadcrumb")).toBeNull();
168
- expect(rendered.queryByTestId("simple-editor")).toBeNull();
169
- expect(rendered.queryByTestId("advanced-editor")).toBeNull();
197
+ const rendered = render(<DataViewEditor />);
198
+ await waitForLoad(rendered);
199
+
200
+ expect(rendered.queryByTestId("breadcrumb")).toBeNull();
201
+ expect(rendered.queryByTestId("simple-editor")).toBeNull();
202
+ expect(rendered.queryByTestId("advanced-editor")).toBeNull();
203
+ });
204
+
205
+ it("renders breadcrumb with existing name and advanced editor when mode is advanced", async () => {
206
+ useParams.mockReturnValue({ id: 42 });
207
+ mockUseDataViewFetch.mockReturnValue({
208
+ dataView: {
209
+ id: 42,
210
+ name: "Existing View Name",
211
+ description: "desc",
212
+ mode: "advanced",
213
+ queryables: [],
214
+ select: { type: "select" },
215
+ },
216
+ loading: false,
217
+ error: undefined,
170
218
  });
171
219
 
172
- it("renders breadcrumb with existing name and advanced editor when mode is advanced", async () => {
173
- useParams.mockReturnValue({ id: 42 });
174
- mockUseDataViewFetch.mockReturnValue({
175
- dataView: {
176
- id: 42,
177
- name: "Existing View Name",
178
- description: "desc",
179
- mode: "advanced",
180
- queryables: [],
181
- select: { type: "select" },
182
- },
183
- loading: false,
184
- error: undefined,
185
- });
186
-
187
- const rendered = render(<DataViewEditor />);
188
- await waitForLoad(rendered);
189
-
190
- expect(rendered.getByTestId("breadcrumb")).toHaveTextContent("Existing View Name");
191
- expect(rendered.getByTestId("advanced-editor")).toBeInTheDocument();
192
- expect(rendered.queryByTestId("simple-editor")).toBeNull();
193
- });
220
+ const rendered = render(<DataViewEditor />);
221
+ await waitForLoad(rendered);
222
+
223
+ expect(rendered.getByTestId("breadcrumb")).toHaveTextContent(
224
+ "Existing View Name"
225
+ );
226
+ expect(rendered.getByTestId("advanced-editor")).toBeInTheDocument();
227
+ expect(rendered.queryByTestId("simple-editor")).toBeNull();
228
+ });
194
229
  });