@truedat/dd 4.28.6 → 4.29.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.29.1] 2021-09-21
4
+
5
+ ### Added
6
+
7
+ - [TD-4079] Select default template in grant request cart
8
+
9
+ ## [4.28.8] 2021-09-20
10
+
11
+ ### Added
12
+
13
+ - [TD-3970] `StructureFieldsDropdown` support for cast type and modifier
14
+
3
15
  ## [4.28.6] 2021-09-20
4
16
 
5
17
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dd",
3
- "version": "4.28.6",
3
+ "version": "4.29.1",
4
4
  "description": "Truedat Web Data Dictionary",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -16,8 +16,8 @@
16
16
  "scripts": {
17
17
  "clean": "rimraf yarn-error.log",
18
18
  "debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
19
- "test": "jest --coverage",
20
- "test:watch": "jest --watch",
19
+ "test": "TZ=UTC jest --coverage",
20
+ "test:watch": "TZ=UTC jest --watch",
21
21
  "eslint": "eslint src/**",
22
22
  "eslint:fix": "eslint --fix src/**"
23
23
  },
@@ -34,7 +34,7 @@
34
34
  "@testing-library/jest-dom": "^5.14.1",
35
35
  "@testing-library/react": "^12.0.0",
36
36
  "@testing-library/user-event": "^13.2.1",
37
- "@truedat/test": "4.28.6",
37
+ "@truedat/test": "4.29.1",
38
38
  "babel-jest": "^27.0.6",
39
39
  "babel-plugin-dynamic-import-node": "^2.3.3",
40
40
  "babel-plugin-lodash": "^3.3.4",
@@ -84,9 +84,9 @@
84
84
  ]
85
85
  },
86
86
  "dependencies": {
87
- "@truedat/auth": "4.28.6",
88
- "@truedat/core": "4.28.6",
89
- "@truedat/df": "4.28.6",
87
+ "@truedat/auth": "4.29.1",
88
+ "@truedat/core": "4.29.1",
89
+ "@truedat/df": "4.29.1",
90
90
  "lodash": "^4.17.21",
91
91
  "path-to-regexp": "^1.7.0",
92
92
  "prop-types": "^15.7.2",
@@ -107,5 +107,5 @@
107
107
  "react-dom": ">= 16.8.6 < 17",
108
108
  "semantic-ui-react": ">= 0.88.2 < 2.1"
109
109
  },
110
- "gitHead": "c1ed65b6c8440e73e478f07db743c0fd3a473c24"
110
+ "gitHead": "bd33b4258962a80b10ffedf9d222272cd3babaf0"
111
111
  }
@@ -6,52 +6,6 @@ import { OptionGroup } from "@truedat/core/components";
6
6
  import { useIntl } from "react-intl";
7
7
  import { getStructuresFields } from "../selectors/getStructuresFields";
8
8
 
9
- const withFilters = (structureFields, filters) =>
10
- filters
11
- ? _.filter((f) =>
12
- _.every.convert({ cap: false })((v, k) => v.includes(_.prop(k)(f)))(
13
- filters
14
- )
15
- )(structureFields)
16
- : structureFields;
17
-
18
- const getOptions = (structureFields, formatMessage) =>
19
- _.flow(
20
- _.sortBy(_.flow(_.prop("name"), _.toLower)),
21
- _.groupBy("label"),
22
- _.reduce.convert({ cap: false })(
23
- (acc, v, k) =>
24
- _.isNil(k) || k === "undefined"
25
- ? [...acc, ...v]
26
- : [...acc, { label: k, options: v }],
27
- []
28
- ),
29
- _.map.convert({ cap: false })((field) =>
30
- toFieldOption(field, formatMessage)
31
- )
32
- )(structureFields);
33
-
34
- const toFieldOption = (field, formatMessage) =>
35
- _.has("options")(field)
36
- ? {
37
- icon: "table",
38
- label: field.label,
39
- options: _.map((field) => formatOption(field, formatMessage))(
40
- field.options
41
- ),
42
- }
43
- : formatOption(field, formatMessage);
44
-
45
- const formatOption = (field, formatMessage) => ({
46
- key: field.data_structure_id,
47
- text: field.name,
48
- value: field.data_structure_id,
49
- icon: formatMessage({
50
- id: `field_type.icon.${field.field_type}`,
51
- defaultMessage: "hashtag",
52
- }),
53
- });
54
-
55
9
  export const StructureFieldsDropdown = ({
56
10
  label,
57
11
  inline,
@@ -59,12 +13,78 @@ export const StructureFieldsDropdown = ({
59
13
  structureFields,
60
14
  filters,
61
15
  value,
16
+ typeCastModifiers,
62
17
  }) => {
63
18
  const { formatMessage } = useIntl();
64
- const onClick = ({ value }) => {
19
+ const onClick = ({ value, modifier }) => {
65
20
  const field = _.find(_.propEq("data_structure_id", value))(structureFields);
66
- onSelectField(field);
21
+ onSelectField(field, modifier);
22
+ };
23
+
24
+ const formatOption = (field) => {
25
+ const modifier = _.find({ sourceType: field.field_type })(
26
+ typeCastModifiers
27
+ );
28
+ if (modifier) {
29
+ return {
30
+ key: field.data_structure_id,
31
+ text: `${field.name} (${formatMessage({
32
+ id: `filtersGrid.field.modifier.${modifier.name}`,
33
+ })})`,
34
+ modifier,
35
+ value: field.data_structure_id,
36
+ icon: formatMessage({
37
+ id: `field_type.icon.${field.field_type}`,
38
+ defaultMessage: "hashtag",
39
+ }),
40
+ };
41
+ }
42
+
43
+ return {
44
+ key: field.data_structure_id,
45
+ text: field.name,
46
+ value: field.data_structure_id,
47
+ icon: formatMessage({
48
+ id: `field_type.icon.${field.field_type}`,
49
+ defaultMessage: "hashtag",
50
+ }),
51
+ };
67
52
  };
53
+ const toFieldOption = (field) =>
54
+ _.has("options")(field)
55
+ ? {
56
+ icon: "table",
57
+ label: field.label,
58
+ options: _.map(formatOption)(field.options),
59
+ }
60
+ : formatOption(field);
61
+
62
+ const getOptions = _.flow(
63
+ _.sortBy(_.flow(_.prop("name"), _.toLower)),
64
+ _.groupBy("label"),
65
+ _.reduce.convert({ cap: false })(
66
+ (acc, v, k) =>
67
+ _.isNil(k) || k === "undefined"
68
+ ? [...acc, ...v]
69
+ : [...acc, { label: k, options: v }],
70
+ []
71
+ ),
72
+ _.map(toFieldOption)
73
+ );
74
+
75
+ const castSourceTypes = _.flow(
76
+ _.map(({ sourceType }) => sourceType),
77
+ _.uniq
78
+ )(typeCastModifiers);
79
+ const options = _.flow(
80
+ _.filter(
81
+ (f) =>
82
+ _.every.convert({ cap: false })((v, k) => v.includes(_.prop(k)(f)))(
83
+ filters
84
+ ) || castSourceTypes.includes(f.field_type)
85
+ ),
86
+ getOptions
87
+ )(structureFields);
68
88
 
69
89
  return (
70
90
  <OptionGroup
@@ -75,7 +95,7 @@ export const StructureFieldsDropdown = ({
75
95
  placeholder={formatMessage({
76
96
  id: "structureFields.dropdown.placeholder",
77
97
  })}
78
- options={getOptions(withFilters(structureFields, filters), formatMessage)}
98
+ options={options}
79
99
  />
80
100
  );
81
101
  };
@@ -87,6 +107,7 @@ StructureFieldsDropdown.propTypes = {
87
107
  structureFields: PropTypes.array,
88
108
  onSelectField: PropTypes.func,
89
109
  filters: PropTypes.object,
110
+ typeCastModifiers: PropTypes.array,
90
111
  };
91
112
 
92
113
  const mapStateToProps = (state, props) => ({
@@ -74,13 +74,15 @@ export const StructureGrantCartCheckout = ({
74
74
  );
75
75
  };
76
76
 
77
- StructureGrantCart.propTypes = {
77
+ StructureGrantCartCheckout.propTypes = {
78
78
  grantRequestsCart: PropTypes.object,
79
79
  checkoutGrantRequest: PropTypes.func,
80
80
  };
81
+
81
82
  const mapStateToProps = ({ grantRequestsCart }) => ({
82
83
  grantRequestsCart,
83
84
  });
85
+
84
86
  const mapDispatchToProps = (dispatch) =>
85
87
  bindActionCreators({ checkoutGrantRequest }, dispatch);
86
88
 
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash/fp";
2
- import React, { useState, useEffect } from "react";
2
+ import React, { useEffect } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { Segment, Dropdown, Divider, Form, Header } from "semantic-ui-react";
5
5
  import { connect } from "react-redux";
@@ -29,6 +29,18 @@ export const StructureGrantCartTemplate = ({
29
29
  false,
30
30
  "templatesLoading"
31
31
  )(grantRequestsCart);
32
+ const singleTemplate = _.size(templates) === 1;
33
+
34
+ useEffect(() => {
35
+ fetchGrantRequestTemplates();
36
+ }, [fetchGrantRequestTemplates]);
37
+
38
+ useEffect(() => {
39
+ if (singleTemplate) {
40
+ const defaultTemplate = _.head(templates);
41
+ selectGrantRequestTemplate(defaultTemplate?.id);
42
+ }
43
+ }, [singleTemplate, selectGrantRequestTemplate, templates]);
32
44
 
33
45
  const getOptions = _.map((template) => {
34
46
  const { id, name } = _.pick(["id", "name"])(template);
@@ -43,28 +55,33 @@ export const StructureGrantCartTemplate = ({
43
55
  {formatMessage({ id: `structure.grant.cart.template` })}
44
56
  </Header>
45
57
  <Divider />
46
- <Dropdown
47
- label={formatMessage({ id: "structureType.template" })}
48
- name="template_id"
49
- fluid
50
- onChange={(_e, { value }) => selectGrantRequestTemplate(value)}
51
- onOpen={fetchGrantRequestTemplates}
52
- options={getOptions(templates) || []}
53
- selection
54
- clearable
55
- loading={templatesLoading}
56
- value={template?.id || ""}
57
- />
58
+ {!singleTemplate && (
59
+ <Dropdown
60
+ placeholder={formatMessage({
61
+ id: `structure.grant.cart.template.placeholder`,
62
+ })}
63
+ label={formatMessage({ id: "structureType.template" })}
64
+ name="template_id"
65
+ fluid
66
+ onChange={(_e, { value }) => selectGrantRequestTemplate(value)}
67
+ options={getOptions(templates) || []}
68
+ selection
69
+ clearable
70
+ loading={templatesLoading}
71
+ value={template?.id || ""}
72
+ />
73
+ )}
58
74
  {template ? (
59
- <Form>
60
- <Divider />
61
- <DynamicForm
62
- onChange={updateGrantRequestTemplateContent}
63
- template={template}
64
- content={templateContent}
65
- />
75
+ <>
66
76
  <Divider />
67
- </Form>
77
+ <Form>
78
+ <DynamicForm
79
+ onChange={updateGrantRequestTemplateContent}
80
+ template={template}
81
+ content={templateContent}
82
+ />
83
+ </Form>
84
+ </>
68
85
  ) : null}
69
86
  </Segment>
70
87
  );
@@ -40,6 +40,7 @@ export const StructureGrantRequestButton = ({
40
40
  };
41
41
 
42
42
  StructureGrantRequestButton.propTypes = {
43
+ authorized: PropTypes.bool,
43
44
  structure: PropTypes.object,
44
45
  requested: PropTypes.bool,
45
46
  addGrantRequestToCart: PropTypes.func,
@@ -0,0 +1,134 @@
1
+ import React, { Suspense } from "react";
2
+ import { waitFor } from "@testing-library/react";
3
+ import { render } from "@truedat/test/render";
4
+ import { StructureGrantCartTemplate } from "../StructureGrantCartTemplate";
5
+
6
+ describe("<StructureGrantCartTemplate />", () => {
7
+ const fetchGrantRequestTemplates = jest.fn();
8
+ const selectGrantRequestTemplate = jest.fn();
9
+ const updateGrantRequestTemplateContent = jest.fn();
10
+
11
+ const template = {
12
+ id: 1,
13
+ content: [
14
+ {
15
+ name: "foo",
16
+ fields: [
17
+ {
18
+ type: "string",
19
+ name: "foo",
20
+ label: "Label",
21
+ cardinality: "1",
22
+ },
23
+ ],
24
+ },
25
+ ],
26
+ };
27
+ const templates = [template];
28
+ const templatesLoading = false;
29
+ const templateContent = { foo: "bar" };
30
+ const grantRequestsCart = {
31
+ templatesLoading,
32
+ template,
33
+ templates,
34
+ };
35
+ const props = {
36
+ grantRequestsCart,
37
+ templateContent,
38
+ fetchGrantRequestTemplates,
39
+ selectGrantRequestTemplate,
40
+ updateGrantRequestTemplateContent,
41
+ };
42
+
43
+ const messages = {
44
+ en: {
45
+ "structure.grant.cart.template": "template",
46
+ "structure.grant.cart.template.placeholder": "Select request type",
47
+ "structureType.template": "Type",
48
+ },
49
+ };
50
+
51
+ it("matches the latest snapshot", () => {
52
+ const { container } = render(
53
+ <Suspense fallback={null}>
54
+ <StructureGrantCartTemplate {...props} />
55
+ </Suspense>,
56
+ {
57
+ messages,
58
+ }
59
+ );
60
+ expect(container).toMatchSnapshot();
61
+ });
62
+
63
+ it("it calls fetchGrantRequestTemplates on render and selects template by default", async () => {
64
+ fetchGrantRequestTemplates.mockClear();
65
+ const { findByText } = render(
66
+ <Suspense fallback={null}>
67
+ <StructureGrantCartTemplate {...props} />
68
+ </Suspense>,
69
+ {
70
+ messages,
71
+ }
72
+ );
73
+ await waitFor(() => {
74
+ expect(fetchGrantRequestTemplates).toHaveBeenCalledTimes(1);
75
+ });
76
+ await waitFor(() => {
77
+ expect(selectGrantRequestTemplate).toHaveBeenCalledWith(template.id);
78
+ });
79
+
80
+ await waitFor(() => {
81
+ expect(findByText(/template/)).toBeTruthy();
82
+ });
83
+ });
84
+
85
+ it("it renders templates dropdown when there are many", async () => {
86
+ fetchGrantRequestTemplates.mockClear();
87
+ selectGrantRequestTemplate.mockClear();
88
+ const templates = [
89
+ template,
90
+ {
91
+ id: 2,
92
+ name: "bar",
93
+ content: [
94
+ {
95
+ fields: [
96
+ {
97
+ type: "string",
98
+ name: "bar",
99
+ label: "Label",
100
+ cardinality: "1",
101
+ },
102
+ ],
103
+ },
104
+ ],
105
+ },
106
+ ];
107
+ const { findByRole } = render(
108
+ <Suspense fallback={null}>
109
+ <StructureGrantCartTemplate
110
+ {...{
111
+ ...props,
112
+ grantRequestsCart: {
113
+ ...grantRequestsCart,
114
+ template: null,
115
+ templates,
116
+ },
117
+ }}
118
+ />
119
+ </Suspense>,
120
+ {
121
+ messages,
122
+ }
123
+ );
124
+ await waitFor(() => {
125
+ expect(fetchGrantRequestTemplates).toHaveBeenCalledTimes(1);
126
+ });
127
+ await waitFor(() => {
128
+ expect(selectGrantRequestTemplate).toHaveBeenCalledTimes(0);
129
+ });
130
+
131
+ expect(findByRole("option", { name: "foo" })).toBeTruthy();
132
+ expect(findByRole("option", { name: "bar" })).toBeTruthy();
133
+ });
134
+ });
@@ -46,13 +46,16 @@ describe("<ValueConditionStructure />", () => {
46
46
  const option = getByRole("option", { name: /baz/i });
47
47
  userEvent.click(option);
48
48
  await waitFor(() => {
49
- expect(onChange).toHaveBeenCalledWith({
50
- id: 2,
51
- name: "baz",
52
- data_structure_id: 2,
53
- field_type: "string",
54
- label: "bar",
55
- });
49
+ expect(onChange).toHaveBeenCalledWith(
50
+ {
51
+ id: 2,
52
+ name: "baz",
53
+ data_structure_id: 2,
54
+ field_type: "string",
55
+ label: "bar",
56
+ },
57
+ undefined
58
+ );
56
59
  });
57
60
  });
58
61
  });
@@ -0,0 +1,26 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<StructureGrantCartTemplate /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui segment"
7
+ style="display: none;"
8
+ >
9
+ <div
10
+ class="ui header"
11
+ title="template"
12
+ >
13
+ template
14
+ </div>
15
+ <div
16
+ class="ui divider"
17
+ />
18
+ <div
19
+ class="ui divider"
20
+ />
21
+ <form
22
+ class="ui form"
23
+ />
24
+ </div>
25
+ </div>
26
+ `;
@@ -135,8 +135,9 @@ export default {
135
135
  "structure.grant.has_grant": "You have granted access on this structure",
136
136
  "structure.grant.request_grant": "Request grant",
137
137
  "structure.grant.requested_grant": "Pending request",
138
- "structure.grant.cart": "Grants to request",
139
- "structure.grant.cart.template": "Request type",
138
+ "structure.grant.cart": "Requested grants",
139
+ "structure.grant.cart.template": "Request information",
140
+ "structure.grant.cart.template.placeholder": "Select request type",
140
141
  "structure.grant.cart.filters": "Filters for structure",
141
142
  "structure.group": "Group",
142
143
  "structure.inserted_at": "Created at",
@@ -138,7 +138,8 @@ export default {
138
138
  "structure.grant.request_grant": "Solicitar acceso",
139
139
  "structure.grant.requested_grant": "Solicitud pendiente",
140
140
  "structure.grant.cart": "Accesos a solicitar",
141
- "structure.grant.cart.template": "Tipo de solicitud",
141
+ "structure.grant.cart.template": "Información de solicitud",
142
+ "structure.grant.cart.template.placeholder": "Seleccione tipo de solicitud",
142
143
  "structure.grant.cart.filters": "Filtros para la estructura",
143
144
  "structure.group": "Grupo",
144
145
  "structure.inserted_at": "Importada",