@truedat/qx 5.14.1 → 5.15.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 (120) hide show
  1. package/package.json +3 -3
  2. package/src/api.js +3 -2
  3. package/src/components/QxRoutes.js +4 -4
  4. package/src/components/common/ResourceSelector.js +80 -0
  5. package/src/components/common/TestFormWrapper.js +9 -6
  6. package/src/components/common/TypeSelector.js +3 -1
  7. package/src/components/common/__tests__/DescriptionInput.spec.js +48 -0
  8. package/src/components/common/__tests__/ResourceSelector.spec.js +124 -0
  9. package/src/components/common/__tests__/TypeSelector.spec.js +29 -0
  10. package/src/components/common/__tests__/__snapshots__/DescriptionInput.spec.js.snap +19 -0
  11. package/src/components/common/__tests__/__snapshots__/ResourceSelector.spec.js.snap +484 -0
  12. package/src/components/common/__tests__/__snapshots__/TypeSelector.spec.js.snap +230 -0
  13. package/src/components/common/expressions/Clauses.js +100 -0
  14. package/src/components/common/expressions/Condition.js +178 -0
  15. package/src/components/common/expressions/Expression.js +54 -0
  16. package/src/components/common/expressions/FieldSelector.js +66 -0
  17. package/src/components/{functions → common}/expressions/FunctionArgs.js +1 -0
  18. package/src/components/{functions → common}/expressions/FunctionSelector.js +24 -4
  19. package/src/components/{functions → common}/expressions/ParamSelector.js +3 -3
  20. package/src/components/{functions → common}/expressions/ShapeSelector.js +22 -6
  21. package/src/components/common/expressions/__tests__/Clauses.spec.js +53 -0
  22. package/src/components/common/expressions/__tests__/Condition.spec.js +33 -0
  23. package/src/components/{functions → common}/expressions/__tests__/ConstantSelector.spec.js +6 -1
  24. package/src/components/{functions → common}/expressions/__tests__/Expression.spec.js +7 -5
  25. package/src/components/common/expressions/__tests__/FieldSelector.spec.js +24 -0
  26. package/src/components/{functions → common}/expressions/__tests__/FunctionArgs.spec.js +20 -6
  27. package/src/components/{functions → common}/expressions/__tests__/FunctionSelector.spec.js +1 -1
  28. package/src/components/{functions → common}/expressions/__tests__/ParamSelector.spec.js +22 -10
  29. package/src/components/{functions → common}/expressions/__tests__/ShapeSelector.spec.js +3 -3
  30. package/src/components/common/expressions/__tests__/__snapshots__/Clauses.spec.js.snap +64 -0
  31. package/src/components/common/expressions/__tests__/__snapshots__/Condition.spec.js.snap +316 -0
  32. package/src/components/{functions → common}/expressions/__tests__/__snapshots__/ConstantSelector.spec.js.snap +1 -1
  33. package/src/components/common/expressions/__tests__/__snapshots__/Expression.spec.js.snap +939 -0
  34. package/src/components/common/expressions/__tests__/__snapshots__/FieldSelector.spec.js.snap +5 -0
  35. package/src/components/common/expressions/__tests__/__snapshots__/FunctionArgs.spec.js.snap +344 -0
  36. package/src/components/{functions → common}/expressions/__tests__/__snapshots__/ParamSelector.spec.js.snap +4 -4
  37. package/src/components/{functions → common}/expressions/__tests__/__snapshots__/ShapeSelector.spec.js.snap +17 -24
  38. package/src/components/common/expressions/constantInputs/AnySelector.js +44 -0
  39. package/src/components/{functions → common}/expressions/constantInputs/__tests__/AnySelector.spec.js +21 -14
  40. package/src/components/{functions → common}/expressions/constantInputs/__tests__/DefaultSelector.spec.js +5 -2
  41. package/src/components/{functions → common}/expressions/constantInputs/__tests__/__snapshots__/AnySelector.spec.js.snap +2 -2
  42. package/src/components/common/expressions/index.js +4 -0
  43. package/src/components/common/resourceSelectors/DataStructureSelector.js +63 -0
  44. package/src/components/common/resourceSelectors/DataViewSelector.js +65 -0
  45. package/src/components/common/resourceSelectors/ReferenceDatasetSelector.js +61 -0
  46. package/src/components/common/resourceSelectors/__tests__/DataStructureSelector.spec.js +31 -0
  47. package/src/components/common/resourceSelectors/__tests__/DataViewSelector.spec.js +133 -0
  48. package/src/components/common/resourceSelectors/__tests__/ReferenceDatasetSelector.spec.js +97 -0
  49. package/src/components/common/resourceSelectors/__tests__/__snapshots__/DataStructureSelector.spec.js.snap +105 -0
  50. package/src/components/common/resourceSelectors/__tests__/__snapshots__/DataViewSelector.spec.js.snap +50 -0
  51. package/src/components/common/resourceSelectors/__tests__/__snapshots__/ReferenceDatasetSelector.spec.js.snap +50 -0
  52. package/src/components/common/resourceSelectors/index.js +5 -0
  53. package/src/components/dataViews/DataViewEditor.js +170 -0
  54. package/src/components/dataViews/DataViewSelect.js +47 -0
  55. package/src/components/dataViews/DataViews.js +169 -0
  56. package/src/components/dataViews/Queryable.js +138 -0
  57. package/src/components/dataViews/Queryables.js +53 -0
  58. package/src/components/dataViews/__tests__/DataViewEditor.spec.js +293 -0
  59. package/src/components/dataViews/__tests__/DataViewSelect.spec.js +28 -0
  60. package/src/components/dataViews/__tests__/DataViews.spec.js +57 -0
  61. package/src/components/dataViews/__tests__/Queryable.spec.js +201 -0
  62. package/src/components/dataViews/__tests__/Queryables.spec.js +183 -0
  63. package/src/components/dataViews/__tests__/__snapshots__/DataViewEditor.spec.js.snap +1000 -0
  64. package/src/components/dataViews/__tests__/__snapshots__/DataViewSelect.spec.js.snap +55 -0
  65. package/src/components/dataViews/__tests__/__snapshots__/DataViews.spec.js.snap +77 -0
  66. package/src/components/dataViews/__tests__/__snapshots__/Queryable.spec.js.snap +874 -0
  67. package/src/components/dataViews/__tests__/__snapshots__/Queryables.spec.js.snap +911 -0
  68. package/src/components/dataViews/__tests__/queryableFunctions.spec.js +193 -0
  69. package/src/components/dataViews/queryableFunctions.js +80 -0
  70. package/src/components/dataViews/queryableProperties/From.js +15 -0
  71. package/src/components/dataViews/queryableProperties/GroupBy.js +124 -0
  72. package/src/components/dataViews/queryableProperties/Join.js +62 -0
  73. package/src/components/dataViews/queryableProperties/JoinTypeIcon.js +49 -0
  74. package/src/components/dataViews/queryableProperties/Select.js +76 -0
  75. package/src/components/dataViews/queryableProperties/SelectField.js +116 -0
  76. package/src/components/dataViews/queryableProperties/Where.js +15 -0
  77. package/src/components/dataViews/queryableProperties/__tests__/From.spec.js +30 -0
  78. package/src/components/dataViews/queryableProperties/__tests__/GroupBy.spec.js +75 -0
  79. package/src/components/dataViews/queryableProperties/__tests__/Join.spec.js +38 -0
  80. package/src/components/dataViews/queryableProperties/__tests__/JoinTypeIcon.spec.js +36 -0
  81. package/src/components/dataViews/queryableProperties/__tests__/Select.spec.js +144 -0
  82. package/src/components/dataViews/queryableProperties/__tests__/SelectField.spec.js +107 -0
  83. package/src/components/dataViews/queryableProperties/__tests__/Where.spec.js +27 -0
  84. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/From.spec.js.snap +75 -0
  85. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/GroupBy.spec.js.snap +276 -0
  86. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/Join.spec.js.snap +252 -0
  87. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/JoinTypeIcon.spec.js.snap +77 -0
  88. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/Select.spec.js.snap +411 -0
  89. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/SelectField.spec.js.snap +343 -0
  90. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/Where.spec.js.snap +22 -0
  91. package/src/components/dataViews/queryableProperties/index.js +7 -0
  92. package/src/components/functions/FunctionEditor.js +30 -29
  93. package/src/components/functions/Functions.js +3 -11
  94. package/src/components/functions/__tests__/FunctionEditor.spec.js +6 -0
  95. package/src/components/functions/__tests__/FunctionParams.spec.js +6 -0
  96. package/src/components/functions/__tests__/__snapshots__/FunctionEditor.spec.js.snap +490 -466
  97. package/src/components/functions/__tests__/__snapshots__/FunctionParams.spec.js.snap +1 -1
  98. package/src/hooks/__tests__/{useDataSets.spec.js → useDataViews.spec.js} +5 -5
  99. package/src/hooks/useDataViews.js +33 -0
  100. package/src/styles/Expression.less +150 -0
  101. package/src/types.js +11 -3
  102. package/src/components/DataSets.js +0 -64
  103. package/src/components/__tests__/DataSets.spec.js +0 -46
  104. package/src/components/__tests__/__snapshots__/DataSets.spec.js.snap +0 -82
  105. package/src/components/functions/expressions/Expression.js +0 -40
  106. package/src/components/functions/expressions/FieldSelector.js +0 -56
  107. package/src/components/functions/expressions/__tests__/__snapshots__/Expression.spec.js.snap +0 -904
  108. package/src/components/functions/expressions/__tests__/__snapshots__/FunctionArgs.spec.js.snap +0 -392
  109. package/src/components/functions/expressions/constantInputs/AnySelector.js +0 -29
  110. package/src/hooks/useDataSets.js +0 -8
  111. /package/src/components/{functions → common}/expressions/ConstantSelector.js +0 -0
  112. /package/src/components/{functions → common}/expressions/__tests__/__snapshots__/FunctionSelector.spec.js.snap +0 -0
  113. /package/src/components/{functions → common/expressions}/__tests__/useWatchParams.spec.js +0 -0
  114. /package/src/components/{functions → common}/expressions/constantInputs/BooleanSelector.js +0 -0
  115. /package/src/components/{functions → common}/expressions/constantInputs/DefaultSelector.js +0 -0
  116. /package/src/components/{functions → common}/expressions/constantInputs/__tests__/BooleanSelector.spec.js +0 -0
  117. /package/src/components/{functions → common}/expressions/constantInputs/__tests__/__snapshots__/BooleanSelector.spec.js.snap +0 -0
  118. /package/src/components/{functions → common}/expressions/constantInputs/__tests__/__snapshots__/DefaultSelector.spec.js.snap +0 -0
  119. /package/src/components/{functions → common}/expressions/constantInputs/index.js +0 -0
  120. /package/src/components/{functions → common/expressions}/useWatchParams.js +0 -0
@@ -0,0 +1,193 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+
4
+ import {
5
+ calculateGroupByFieldId,
6
+ reduceQueryableFields,
7
+ } from "../queryableFunctions";
8
+
9
+ describe("calculateGroupByFieldId", () => {
10
+ it("calculate pair ids for group and odd for aggregate", () => {
11
+ expect(calculateGroupByFieldId(0, "group")).toBe(0);
12
+ expect(calculateGroupByFieldId(1, "group")).toBe(2);
13
+ expect(calculateGroupByFieldId(0, "aggregate")).toBe(1);
14
+ expect(calculateGroupByFieldId(1, "aggregate")).toBe(3);
15
+ });
16
+ it("different group returns the same id", () => {
17
+ expect(calculateGroupByFieldId(8, "invalid")).toBe(8);
18
+ });
19
+ });
20
+
21
+ describe("reduceQueryableFields", () => {
22
+ const formatMessage = ({ id }) => id;
23
+
24
+ it("returns the fields for From queryable", () => {
25
+ const parentId = 1;
26
+ const alias = "parentAlias";
27
+ const queryables = [
28
+ {
29
+ type: "from",
30
+ properties: {
31
+ resource: {
32
+ embedded: {
33
+ fields: [
34
+ { id: 2, name: "field2" },
35
+ { id: 3, name: "field3" },
36
+ ],
37
+ },
38
+ },
39
+ },
40
+ alias,
41
+ id: parentId,
42
+ },
43
+ ];
44
+ const expected = [
45
+ {
46
+ alias,
47
+ color: "orange",
48
+ id: 2,
49
+ name: "field2",
50
+ parent_id: parentId,
51
+ },
52
+ {
53
+ alias,
54
+ color: "orange",
55
+ id: 3,
56
+ name: "field3",
57
+ parent_id: parentId,
58
+ },
59
+ ];
60
+ expect(reduceQueryableFields(formatMessage)(queryables)).toStrictEqual(
61
+ expected
62
+ );
63
+ });
64
+
65
+ it("returns the fields for From and Join queryable", () => {
66
+ const queryables = [
67
+ {
68
+ type: "from",
69
+ properties: {
70
+ resource: {
71
+ embedded: {
72
+ fields: [{ id: 2, name: "field1" }],
73
+ },
74
+ },
75
+ },
76
+ alias: "qryFrom",
77
+ id: 5,
78
+ },
79
+ {
80
+ type: "join",
81
+ properties: {
82
+ resource: {
83
+ embedded: {
84
+ fields: [{ id: 3, name: "field2" }],
85
+ },
86
+ },
87
+ },
88
+ alias: "qryJoin",
89
+ id: 6,
90
+ },
91
+ ];
92
+ const expected = [
93
+ {
94
+ alias: "qryFrom",
95
+ color: "yellow",
96
+ id: 2,
97
+ name: "field1",
98
+ parent_id: 5,
99
+ },
100
+ {
101
+ alias: "qryJoin",
102
+ color: "violet",
103
+ id: 3,
104
+ name: "field2",
105
+ parent_id: 6,
106
+ },
107
+ ];
108
+ expect(reduceQueryableFields(formatMessage)(queryables)).toStrictEqual(
109
+ expected
110
+ );
111
+ });
112
+
113
+ it("select queryable prevents previous queryables to return fields", () => {
114
+ const queryables = [
115
+ {
116
+ type: "from",
117
+ properties: {
118
+ resource: {
119
+ embedded: {
120
+ fields: [{ id: 2, name: "field1" }],
121
+ },
122
+ },
123
+ },
124
+ alias: "qryFrom",
125
+ id: 5,
126
+ },
127
+ {
128
+ type: "select",
129
+ properties: {
130
+ fields: [{ id: 3, alias: "field2" }],
131
+ },
132
+ alias: "qrySelect",
133
+ id: 6,
134
+ },
135
+ ];
136
+ const expected = [
137
+ {
138
+ alias: "qrySelect",
139
+ color: "violet",
140
+ id: 3,
141
+ name: "field2",
142
+ parent_id: 6,
143
+ },
144
+ ];
145
+ expect(reduceQueryableFields(formatMessage)(queryables)).toStrictEqual(
146
+ expected
147
+ );
148
+ });
149
+
150
+ it("groupBy queryable prevents previous queryables to return fields", () => {
151
+ const queryables = [
152
+ {
153
+ type: "from",
154
+ properties: {
155
+ resource: {
156
+ embedded: {
157
+ fields: [{ id: 2, name: "field1" }],
158
+ },
159
+ },
160
+ },
161
+ alias: "qryFrom",
162
+ id: 5,
163
+ },
164
+ {
165
+ type: "group_by",
166
+ properties: {
167
+ group_fields: [{ id: 3, alias: "field2" }],
168
+ aggregate_fields: [{ id: 4, alias: "field4" }],
169
+ },
170
+ id: 6,
171
+ },
172
+ ];
173
+ const expected = [
174
+ {
175
+ alias: "dataViews.form.queryable.group_by",
176
+ color: "violet",
177
+ id: 3,
178
+ name: "field2",
179
+ parent_id: 6,
180
+ },
181
+ {
182
+ alias: "dataViews.form.queryable.group_by",
183
+ color: "violet",
184
+ id: 4,
185
+ name: "field4",
186
+ parent_id: 6,
187
+ },
188
+ ];
189
+ expect(reduceQueryableFields(formatMessage)(queryables)).toStrictEqual(
190
+ expected
191
+ );
192
+ });
193
+ });
@@ -0,0 +1,80 @@
1
+ import _ from "lodash/fp";
2
+
3
+ export const calculateGroupByFieldId = (id, type) => {
4
+ switch (type) {
5
+ case "group":
6
+ return id * 2;
7
+ case "aggregate":
8
+ return id * 2 + 1;
9
+ default:
10
+ return id;
11
+ }
12
+ };
13
+
14
+ const queryableToFields =
15
+ (formatMessage) =>
16
+ ({ type, properties, alias, id: parent_id }) => {
17
+ const selectFieldsMap = _.map.convert({ cap: false })((field) => ({
18
+ ...field,
19
+ alias: alias || formatMessage({ id: `dataViews.form.queryable.${type}` }),
20
+ name: field.alias,
21
+ parent_id,
22
+ color: getColorById(parent_id),
23
+ }));
24
+
25
+ switch (type) {
26
+ case "from":
27
+ case "join":
28
+ return _.flow(
29
+ _.propOr([], "resource.embedded.fields"),
30
+ _.map((field) => ({
31
+ ...field,
32
+ alias,
33
+ parent_id,
34
+ color: getColorById(parent_id),
35
+ }))
36
+ )(properties);
37
+ case "select":
38
+ return _.flow(_.propOr([], "fields"), selectFieldsMap)(properties);
39
+ case "group_by":
40
+ const groupFields = _.flow(
41
+ _.propOr([], "group_fields"),
42
+ selectFieldsMap
43
+ )(properties);
44
+ const aggregateFields = _.flow(
45
+ _.propOr([], "aggregate_fields"),
46
+ selectFieldsMap
47
+ )(properties);
48
+ return _.concat(groupFields, aggregateFields);
49
+ }
50
+ };
51
+
52
+ export const reduceQueryableFields = (formatMessage) => (queryables) => {
53
+ const [_halt, fields] = _.flow(
54
+ _.reverse,
55
+ _.reduce(
56
+ ([halt, accFields], queryable) =>
57
+ halt
58
+ ? [halt, accFields]
59
+ : [
60
+ _.includes(queryable.type)(["select", "group_by"]),
61
+ _.concat(queryableToFields(formatMessage)(queryable), accFields),
62
+ ],
63
+ [false, []]
64
+ )
65
+ )(queryables);
66
+ return fields;
67
+ };
68
+
69
+ export const getColorById = (id) =>
70
+ [
71
+ "blue",
72
+ "orange",
73
+ "green",
74
+ "purple",
75
+ "teal",
76
+ "yellow",
77
+ "violet",
78
+ "olive",
79
+ "pink",
80
+ ][id];
@@ -0,0 +1,15 @@
1
+ import React, { useContext } from "react";
2
+
3
+ import ResourceSelector from "@truedat/qx/components/common/ResourceSelector";
4
+ import QxContext from "@truedat/qx/components/QxContext";
5
+
6
+ export default function From() {
7
+ const context = useContext(QxContext);
8
+ const { field } = context;
9
+
10
+ return (
11
+ <QxContext.Provider value={{ ...context, field: `${field}.resource` }}>
12
+ <ResourceSelector required />
13
+ </QxContext.Provider>
14
+ );
15
+ }
@@ -0,0 +1,124 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useContext } from "react";
3
+ import { useIntl } from "react-intl";
4
+ import { useFieldArray, useFormState } from "react-hook-form";
5
+ import { Button, Form, List, Popup } from "semantic-ui-react";
6
+ import QxContext from "@truedat/qx/components/QxContext";
7
+ import { calculateGroupByFieldId } from "../queryableFunctions";
8
+ import SelectField, { newSelectField } from "./SelectField";
9
+
10
+ export default function GroupBy() {
11
+ const { formatMessage } = useIntl();
12
+ const context = useContext(QxContext);
13
+ const { field } = context;
14
+ const {
15
+ fields: groupFields,
16
+ append: appendGroupField,
17
+ remove: removeGroupField,
18
+ } = useFieldArray({
19
+ name: `${field}.group_fields`,
20
+ keyName: "key",
21
+ rules: {
22
+ required: formatMessage({ id: "dataViews.form.error.empty_select" }),
23
+ },
24
+ });
25
+
26
+ const {
27
+ fields: aggregateFields,
28
+ append: appendAggregateField,
29
+ remove: removeAggregateField,
30
+ } = useFieldArray({
31
+ name: `${field}.aggregate_fields`,
32
+ keyName: "key",
33
+ });
34
+ const { errors } = useFormState();
35
+ const requiredError = _.prop(`${field}.group_fields.root.message`)(errors);
36
+
37
+ const maxGroupId = (_.flow(_.map("id"), _.max)(groupFields) || 0) / 2;
38
+ const maxAggregateId =
39
+ ((_.flow(_.map("id"), _.max)(aggregateFields) || 1) - 1) / 2;
40
+
41
+ const nextGroupId = calculateGroupByFieldId(maxGroupId + 1, "group");
42
+ const nextAggregateId = calculateGroupByFieldId(
43
+ maxAggregateId + 1,
44
+ "aggregate"
45
+ );
46
+
47
+ const addGroupFieldButton = (
48
+ <Button onClick={() => appendGroupField(newSelectField(nextGroupId))}>
49
+ {formatMessage({ id: "queryables.group_by.form.add_group_field" })}
50
+ </Button>
51
+ );
52
+ return (
53
+ <>
54
+ <Form.Field required>
55
+ <label>
56
+ {formatMessage({ id: "queryables.group_by.form.group_fields" })}
57
+ </label>
58
+ <List>
59
+ {groupFields.map((selectField, index) => (
60
+ <List.Item key={selectField.id}>
61
+ <QxContext.Provider
62
+ value={{
63
+ ...context,
64
+ field: `${field}.group_fields[${index}]`,
65
+ index,
66
+ }}
67
+ >
68
+ <SelectField onDelete={() => removeGroupField(index)} />
69
+ </QxContext.Provider>
70
+ </List.Item>
71
+ ))}
72
+ <List.Item>
73
+ {requiredError ? (
74
+ <Popup
75
+ content={requiredError}
76
+ open
77
+ pinned
78
+ position="bottom center"
79
+ trigger={addGroupFieldButton}
80
+ />
81
+ ) : (
82
+ addGroupFieldButton
83
+ )}
84
+ </List.Item>
85
+ </List>
86
+ </Form.Field>
87
+ <Form.Field>
88
+ <label>
89
+ {formatMessage({ id: "queryables.group_by.form.aggregate_fields" })}
90
+ </label>
91
+ <List>
92
+ {aggregateFields.map((selectField, index) => (
93
+ <List.Item key={selectField.id}>
94
+ <QxContext.Provider
95
+ value={{
96
+ ...context,
97
+ field: `${field}.aggregate_fields[${index}]`,
98
+ index,
99
+ aggregate: true,
100
+ }}
101
+ >
102
+ <SelectField onDelete={() => removeAggregateField(index)} />
103
+ </QxContext.Provider>
104
+ </List.Item>
105
+ ))}
106
+ <List.Item>
107
+ <Button
108
+ onClick={() =>
109
+ appendAggregateField({
110
+ ...newSelectField(nextAggregateId),
111
+ expression: { shape: "function" },
112
+ })
113
+ }
114
+ >
115
+ {formatMessage({
116
+ id: "queryables.group_by.form.add_aggregate_field",
117
+ })}
118
+ </Button>
119
+ </List.Item>
120
+ </List>
121
+ </Form.Field>
122
+ </>
123
+ );
124
+ }
@@ -0,0 +1,62 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useContext } from "react";
3
+ import { useIntl } from "react-intl";
4
+ import { Clauses } from "@truedat/qx/components/common/expressions";
5
+ import { Controller, useFormContext } from "react-hook-form";
6
+ import { Dropdown, Form } from "semantic-ui-react";
7
+ import ResourceSelector from "@truedat/qx/components/common/ResourceSelector";
8
+ import QxContext from "@truedat/qx/components/QxContext";
9
+ import JoinTypeIcon from "./JoinTypeIcon";
10
+
11
+ export default function Join() {
12
+ const { formatMessage } = useIntl();
13
+ const context = useContext(QxContext);
14
+ const { control } = useFormContext();
15
+ const { field } = context;
16
+
17
+ const joinTypes = ["inner", "left", "right", "full_outer"];
18
+
19
+ const joinTypeOptions = _.map((joinType) => ({
20
+ key: joinType,
21
+ value: joinType,
22
+ text: formatMessage({ id: `joinType.${joinType}` }),
23
+ image: <JoinTypeIcon type={joinType} />,
24
+ }))(joinTypes);
25
+
26
+ return (
27
+ <>
28
+ <QxContext.Provider value={{ ...context, field: `${field}.resource` }}>
29
+ <ResourceSelector required />
30
+ </QxContext.Provider>
31
+ <div className="vertical-space">
32
+ <Controller
33
+ control={control}
34
+ name={`${field}.type`}
35
+ render={({ field: { onBlur, onChange, value = "inner" } }) => (
36
+ <Form.Field>
37
+ <label>
38
+ {formatMessage({ id: "queryables.form.join.type" })}
39
+ </label>
40
+ <Dropdown
41
+ className="select-field-dropdown"
42
+ selection
43
+ value={value}
44
+ options={joinTypeOptions}
45
+ onChange={(_e, { value }) => onChange(value)}
46
+ onBlur={onBlur}
47
+ label={formatMessage({
48
+ id: "dataViews.form.queryable.alias",
49
+ })}
50
+ />
51
+ </Form.Field>
52
+ )}
53
+ />
54
+ </div>
55
+ <div className="vertical-space">
56
+ <QxContext.Provider value={{ ...context, field: `${field}.clauses` }}>
57
+ <Clauses />
58
+ </QxContext.Provider>
59
+ </div>
60
+ </>
61
+ );
62
+ }
@@ -0,0 +1,49 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+
4
+ export const JoinTypeIcon = ({ type }) => {
5
+ switch (type) {
6
+ case "inner":
7
+ return (
8
+ <div className="join_type">
9
+ <div className="circle circle-left">
10
+ <div className="circle circle-center fill"></div>
11
+ </div>
12
+ <div className="circle circle-right"></div>
13
+ </div>
14
+ );
15
+ case "left":
16
+ return (
17
+ <div className="join_type">
18
+ <div className="circle circle-left fill">
19
+ <div className="circle circle-center fill"></div>
20
+ </div>
21
+ <div className="circle circle-right"></div>
22
+ </div>
23
+ );
24
+ case "right":
25
+ return (
26
+ <div className="join_type">
27
+ <div className="circle circle-left">
28
+ <div className="circle circle-center fill"></div>
29
+ </div>
30
+ <div className="circle circle-right fill"></div>
31
+ </div>
32
+ );
33
+ case "full_outer":
34
+ return (
35
+ <div className="join_type">
36
+ <div className="circle circle-left fill">
37
+ <div className="circle circle-center fill"></div>
38
+ </div>
39
+ <div className="circle circle-right fill"></div>
40
+ </div>
41
+ );
42
+ }
43
+ };
44
+
45
+ JoinTypeIcon.propTypes = {
46
+ type: PropTypes.string.isRequired,
47
+ };
48
+
49
+ export default JoinTypeIcon;
@@ -0,0 +1,76 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useContext } from "react";
3
+ import { useIntl } from "react-intl";
4
+ import { useFieldArray, useFormState } from "react-hook-form";
5
+ import { Button, List, Popup } from "semantic-ui-react";
6
+ import QxContext from "@truedat/qx/components/QxContext";
7
+ import SelectField, { newSelectField } from "./SelectField";
8
+
9
+ export default function Select() {
10
+ const { formatMessage } = useIntl();
11
+ const context = useContext(QxContext);
12
+ const { field, fields: ctxFields } = context;
13
+ const { fields, append, remove } = useFieldArray({
14
+ name: `${field}.fields`,
15
+ keyName: "key",
16
+ rules: {
17
+ required: formatMessage({ id: "dataViews.form.error.empty_select" }),
18
+ },
19
+ });
20
+ const { errors } = useFormState();
21
+ const requiredError = _.prop("select.properties.fields.root.message")(errors);
22
+
23
+ const allFields = _.map.convert({ cap: false })(
24
+ ({ id, parent_id, name, type }, idx) => ({
25
+ id: idx,
26
+ alias: name,
27
+ expression: { shape: "field", value: { id, parent_id, name, type } },
28
+ })
29
+ )(ctxFields);
30
+ const maxId = _.flow(_.map("id"), _.max)(fields) || 0;
31
+
32
+ const addFieldButton = (
33
+ <Button onClick={() => append(newSelectField(maxId + 1))}>
34
+ {formatMessage({ id: "queryables.select.form.add_select_field" })}
35
+ </Button>
36
+ );
37
+
38
+ return (
39
+ <List>
40
+ {fields.map((selectField, index) => (
41
+ <List.Item key={selectField.key}>
42
+ <QxContext.Provider
43
+ value={{ ...context, field: `${field}.fields[${index}]`, index }}
44
+ >
45
+ <SelectField onDelete={() => remove(index)} />
46
+ </QxContext.Provider>
47
+ </List.Item>
48
+ ))}
49
+ <List.Item>
50
+ {requiredError ? (
51
+ <Popup
52
+ className="error-popup"
53
+ content={requiredError}
54
+ open
55
+ pinned
56
+ position="bottom center"
57
+ trigger={addFieldButton}
58
+ />
59
+ ) : (
60
+ addFieldButton
61
+ )}
62
+
63
+ {_.size(fields) == 0 ? (
64
+ <Button
65
+ onClick={() => append(allFields)}
66
+ disabled={_.isEmpty(allFields)}
67
+ >
68
+ {formatMessage({
69
+ id: "queryables.select.form.add_all_select_fields",
70
+ })}
71
+ </Button>
72
+ ) : null}
73
+ </List.Item>
74
+ </List>
75
+ );
76
+ }