@truedat/bg 4.48.11 → 4.49.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.48.12] 2022-07-22
4
+
5
+ ### Changed
6
+
7
+ - [TD-3584] Allow longer domain descriptions
8
+
3
9
  ## [4.48.10] 2022-07-19
4
10
 
5
11
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/bg",
3
- "version": "4.48.11",
3
+ "version": "4.49.0",
4
4
  "description": "Truedat Web Business Glossary",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -86,8 +86,8 @@
86
86
  ]
87
87
  },
88
88
  "dependencies": {
89
- "@truedat/core": "4.48.11",
90
- "@truedat/df": "4.48.11",
89
+ "@truedat/core": "4.49.0",
90
+ "@truedat/df": "4.49.0",
91
91
  "file-saver": "^2.0.5",
92
92
  "moment": "^2.24.0",
93
93
  "path-to-regexp": "^1.7.0",
@@ -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": "c174776672b9079d23a00a6561db635d43cbeb32"
110
+ "gitHead": "7260126df6067fe59b11c5dc2b94dbdd881fed7b"
111
111
  }
@@ -16,18 +16,12 @@ import ConceptArchive from "./ConceptArchive";
16
16
  import ConceptDetails from "./ConceptDetails";
17
17
  import Events from "./Events";
18
18
 
19
- const TemplatesLoader = React.lazy(() =>
20
- import("@truedat/df/templates/components/TemplatesLoader")
21
- );
22
-
23
19
  const ConceptRules = React.lazy(() =>
24
20
  import("@truedat/dq/components/ConceptRules")
25
21
  );
26
22
 
27
23
  const NewRule = React.lazy(() => import("@truedat/dq/components/NewRule"));
28
24
 
29
- const QualityTemplatesLoader = () => <TemplatesLoader scope="dq" />;
30
-
31
25
  export const ConceptTabPane = () => (
32
26
  <ErrorBoundary>
33
27
  <Switch>
@@ -48,15 +42,7 @@ export const ConceptTabPane = () => (
48
42
  path={CONCEPT_RULES}
49
43
  render={() => (
50
44
  <Switch>
51
- <Route
52
- path={CONCEPT_RULES_NEW}
53
- render={() => (
54
- <>
55
- <QualityTemplatesLoader />
56
- <NewRule />
57
- </>
58
- )}
59
- />
45
+ <Route path={CONCEPT_RULES_NEW} render={() => <NewRule />} />
60
46
  <Route render={() => <ConceptRules />} />
61
47
  </Switch>
62
48
  )}
@@ -1,11 +1,3 @@
1
- const API_CONCEPT_RELATION = "/api/relations/:id";
2
- const API_CONCEPT_RELATIONS = "/api/relations";
3
- const API_CONCEPT_RELATIONS_SEARCH = "/api/relations/search";
4
- const API_CONCEPT_BULK_UPDATE = "/api/business_concept_versions/bulk_update";
1
+ const API_RELATIONS = "/api/relations";
5
2
 
6
- export {
7
- API_CONCEPT_RELATION,
8
- API_CONCEPT_RELATIONS,
9
- API_CONCEPT_RELATIONS_SEARCH,
10
- API_CONCEPT_BULK_UPDATE
11
- };
3
+ export { API_RELATIONS };
@@ -2,7 +2,7 @@ import _ from "lodash/fp";
2
2
  import { call, put, takeLatest } from "redux-saga/effects";
3
3
  import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
4
4
  import { linkConcept } from "../routines";
5
- import { API_CONCEPT_RELATIONS } from "../api";
5
+ import { API_RELATIONS } from "../api";
6
6
 
7
7
  export function* linkConceptSaga({ payload }) {
8
8
  try {
@@ -22,7 +22,7 @@ export function* linkConceptSaga({ payload }) {
22
22
  yield put({ meta, ...linkConcept.request() });
23
23
  const { data } = yield call(
24
24
  apiJsonPost,
25
- API_CONCEPT_RELATIONS,
25
+ API_RELATIONS,
26
26
  requestData,
27
27
  JSON_OPTS
28
28
  );
@@ -1,261 +1,242 @@
1
1
  import _ from "lodash/fp";
2
2
  import React from "react";
3
3
  import PropTypes from "prop-types";
4
- import {
5
- Accordion,
6
- Button,
7
- Form,
8
- Icon,
9
- Label,
10
- Segment
11
- } from "semantic-ui-react";
12
- import { compose } from "redux";
4
+ import { Button, Form } from "semantic-ui-react";
13
5
  import { connect } from "react-redux";
14
- import { injectIntl } from "react-intl";
6
+ import { useIntl } from "react-intl";
7
+ import { useForm, Controller } from "react-hook-form";
15
8
  import { HistoryBackButton } from "@truedat/core/components";
9
+ import { accentInsensitiveOrder } from "@truedat/core/services/sort";
16
10
  import {
17
11
  getDomainGroups,
18
12
  getDomainTypes,
19
- domainParentOptionsSelector
13
+ domainParentOptionsSelector,
20
14
  } from "../selectors";
21
15
 
22
16
  const toOption = (t, i) => ({ key: i, text: t, value: t });
23
17
 
24
- const domainStringOptions = (collection, value) =>
18
+ const buildOptions = (collection, value) =>
25
19
  _.flow(
26
20
  _.concat(value),
27
21
  _.filter(_.isString),
28
22
  _.uniq,
29
- _.sortBy(_.lowerCase),
23
+ _.sortBy(accentInsensitiveOrder),
30
24
  _.map.convert({ cap: false })(toOption)
31
25
  )(collection);
32
26
 
33
- export class DomainForm extends React.Component {
34
- static propTypes = {
35
- domain: PropTypes.object,
36
- isSubmitting: PropTypes.bool,
37
- domainGroups: PropTypes.array,
38
- domainParentOptions: PropTypes.array,
39
- domainTypes: PropTypes.array,
40
- handleSubmit: PropTypes.func.isRequired,
41
- intl: PropTypes.object
42
- };
43
-
44
- constructor(props) {
45
- super(props);
46
- const defaults = {
47
- description: "",
48
- external_id: "",
49
- name: "",
50
- parent_id: null,
51
- type: "",
52
- domain_group: null
53
- };
54
-
55
- const domain_group = _.path("domain.domain_group.name")(props);
56
- const domain = Object.assign({}, defaults, {
57
- ...(props.domain || {}),
58
- domain_group
59
- });
60
- const touched = false;
61
- this.state = {
62
- domain,
63
- touched,
64
- default_group: _.path("domain.domain_group")(props)
65
- };
66
- this.handleChange = this.handleChange.bind(this);
67
- this.handleSubmit = this.handleSubmit.bind(this);
68
- }
69
-
70
- handleChange(event, data) {
71
- const { name, value } = data;
72
- const domain = Object.assign({}, this.state.domain, { [name]: value });
73
- this.setState({ domain, touched: true });
74
- event.preventDefault();
75
- }
76
-
77
- handleSelectionClick = () => {
78
- const { activeSelection } = this.state;
79
- this.setState({ activeSelection: !activeSelection });
80
- };
81
-
82
- handleSubmit(event) {
83
- const { domain } = this.state;
84
- this.props.handleSubmit({ domain });
85
- event.preventDefault();
86
- }
87
-
88
- validForm() {
89
- const { domain } = this.state;
90
- return _.flow(
91
- _.pick(["name", "external_id", "description"]),
92
- _.toPairs,
93
- _.every(([, v]) => _.negate(_.isEmpty)(v) && _.negate(_.isNil)(v))
94
- )(domain);
95
- }
27
+ const DEFAULTS = {
28
+ description: "",
29
+ external_id: "",
30
+ name: "",
31
+ parent_id: null,
32
+ type: "",
33
+ domain_group: null,
34
+ };
96
35
 
97
- clearableGroup() {
98
- const { default_group } = this.state;
99
- return _.isNil(default_group) || _.prop("status")(default_group) == "root";
100
- }
36
+ export const DomainForm = ({
37
+ domain = {},
38
+ isSubmitting,
39
+ domainGroups,
40
+ domainParentOptions,
41
+ domainTypes,
42
+ onSubmit,
43
+ }) => {
44
+ const { formatMessage } = useIntl();
45
+ // const domainGroup = domain?.domain_group;
46
+ const { handleSubmit, control, formState, watch } = useForm({
47
+ mode: "all",
48
+ defaultValues: {
49
+ ...DEFAULTS,
50
+ ...domain,
51
+ domain_group: domain?.domain_group?.name || null,
52
+ },
53
+ });
54
+ const [group, type] = watch(["domain_group", "type"]);
55
+ const domainGroupOptions = buildOptions(domainGroups, group);
56
+ const domainTypeOptions = buildOptions(domainTypes, type);
57
+ const groupClearable =
58
+ domain?.domain_group?.status === "root" || !domain?.domain_group;
101
59
 
102
- clearGroup() {
103
- const { domain } = this.state;
104
- this.setState({
105
- ...this.state,
106
- domain: { ...domain, domain_group: null },
107
- touched: true
108
- });
109
- }
110
- render() {
111
- const {
112
- isSubmitting,
113
- domainGroups,
114
- domainTypes,
115
- domainParentOptions,
116
- intl
117
- } = this.props;
118
- const { formatMessage } = intl;
119
- const { activeSelection, domain } = this.state;
120
- return (
121
- <Form onSubmit={this.handleSubmit}>
122
- <Form.Input
123
- name="name"
124
- label={formatMessage({ id: "domain.props.name" })}
125
- onChange={this.handleChange}
126
- value={domain.name}
127
- placeholder={formatMessage({ id: "domain.props.name.placeholder" })}
128
- autoComplete="off"
129
- required
130
- />
131
- <Form.Input
132
- name="external_id"
133
- label={formatMessage({ id: "domain.props.external_id" })}
134
- onChange={this.handleChange}
135
- value={domain.external_id || ""}
136
- placeholder={formatMessage({
137
- id: "domain.props.external_id.placeholder"
138
- })}
139
- autoComplete="off"
140
- required
60
+ const { errors, isDirty, isValid } = formState;
61
+ return (
62
+ <Form onSubmit={handleSubmit(onSubmit)}>
63
+ <Controller
64
+ control={control}
65
+ name="name"
66
+ rules={{
67
+ required: formatMessage(
68
+ { id: "form.validation.required" },
69
+ { prop: formatMessage({ id: "domain.props.name" }) }
70
+ ),
71
+ }}
72
+ render={({ field: { onBlur, onChange, value } }) => (
73
+ <Form.Input
74
+ id="name"
75
+ autoComplete="off"
76
+ error={errors?.name?.message}
77
+ label={{
78
+ children: formatMessage({ id: "domain.props.name" }),
79
+ htmlFor: "name",
80
+ }}
81
+ name="name"
82
+ onBlur={onBlur}
83
+ onChange={(_e, { value }) => onChange(value)}
84
+ placeholder={formatMessage({ id: "domain.props.name.placeholder" })}
85
+ required
86
+ value={value}
87
+ />
88
+ )}
89
+ />
90
+ <Controller
91
+ control={control}
92
+ name="external_id"
93
+ rules={{
94
+ required: formatMessage(
95
+ { id: "form.validation.required" },
96
+ { prop: formatMessage({ id: "domain.props.external_id" }) }
97
+ ),
98
+ }}
99
+ render={({ field: { onBlur, onChange, value } }) => (
100
+ <Form.Input
101
+ id="external_id"
102
+ autoComplete="off"
103
+ error={errors?.external_id?.message}
104
+ label={{
105
+ children: formatMessage({ id: "domain.props.external_id" }),
106
+ htmlFor: "external_id",
107
+ }}
108
+ name="external_id"
109
+ onBlur={onBlur}
110
+ onChange={(_e, { value }) => onChange(value)}
111
+ placeholder={formatMessage({
112
+ id: "domain.props.external_id.placeholder",
113
+ })}
114
+ required
115
+ value={value}
116
+ />
117
+ )}
118
+ />
119
+ {_.isEmpty(domainParentOptions) ? null : (
120
+ <Controller
121
+ control={control}
122
+ name="parent_id"
123
+ render={({ field: { onBlur, onChange, value } }) => (
124
+ <Form.Dropdown
125
+ name="parent_id"
126
+ label={formatMessage({ id: "domain.props.parent" })}
127
+ basic
128
+ search
129
+ selection
130
+ clearable
131
+ options={domainParentOptions}
132
+ onBlur={onBlur}
133
+ onChange={(_e, { value }) => onChange(value)}
134
+ placeholder={formatMessage({ id: "domain.selector.placeholder" })}
135
+ value={value || ""}
136
+ />
137
+ )}
141
138
  />
142
- {!_.isEmpty(domainParentOptions) && (
139
+ )}
140
+ <Controller
141
+ control={control}
142
+ name="domain_group"
143
+ render={({ field: { onBlur, onChange, value } }) => (
143
144
  <Form.Dropdown
144
- name="parent_id"
145
- label={formatMessage({ id: "domain.props.parent" })}
145
+ name="domain_group"
146
+ label={formatMessage({ id: "domain.props.domain_group" })}
146
147
  basic
147
- clearable
148
- onChange={this.handleChange}
149
- value={domain.parent_id}
150
- placeholder={formatMessage({ id: "domain.selector.placeholder" })}
151
- options={domainParentOptions}
152
148
  search
153
149
  selection
150
+ clearable={groupClearable}
151
+ options={domainGroupOptions}
152
+ onBlur={onBlur}
153
+ onChange={(_e, { value }) => onChange(value)}
154
+ placeholder={formatMessage({
155
+ id: "domain.props.domain_group.placeholder",
156
+ })}
157
+ value={value || ""}
158
+ allowAdditions
159
+ additionLabel={
160
+ <i style={{ color: "red" }}>
161
+ {formatMessage({ id: "domain.label.domain_group" })}
162
+ </i>
163
+ }
154
164
  />
155
165
  )}
156
- <Segment>
157
- <Form.Group inline>
158
- <label>{formatMessage({ id: "domain.props.domain_group" })}</label>
159
- {_.prop("domain_group")(domain) && (
160
- <Label size={"medium"}>
161
- {_.flow(
162
- _.prop("domain_group"),
163
- _.truncate({ length: 90 })
164
- )(domain)}
165
- {this.clearableGroup() && (
166
- <Icon name="delete" onClick={() => this.clearGroup()} />
167
- )}
168
- </Label>
169
- )}
170
- </Form.Group>
171
- <Accordion>
172
- <Accordion.Title
173
- active={activeSelection}
174
- onClick={this.handleSelectionClick}
175
- >
176
- <Icon name="dropdown" />
177
- {formatMessage({ id: "domain.domain_group.select" })}{" "}
178
- </Accordion.Title>
179
-
180
- <Accordion.Content active={activeSelection}>
181
- <Form.Dropdown
182
- name="domain_group"
183
- label={formatMessage({ id: "domain.props.domain_group" })}
184
- basic
185
- clearable={this.clearableGroup()}
186
- onChange={this.handleChange}
187
- value={domain.domain_group || ""}
188
- placeholder={formatMessage({
189
- id: "domain.props.domain_group.placeholder"
190
- })}
191
- options={domainStringOptions(domainGroups, domain.domain_group)}
192
- search
193
- selection
194
- allowAdditions
195
- onAddItem={this.handleAddItem}
196
- additionLabel={
197
- <i style={{ color: "red" }}>
198
- {formatMessage({
199
- id: "domain.label.domain_group"
200
- })}
201
- </i>
202
- }
203
- />
204
- </Accordion.Content>
205
- </Accordion>
206
- </Segment>
207
- <Form.Dropdown
208
- name="type"
209
- label={formatMessage({ id: "domain.props.type" })}
210
- basic
211
- clearable
212
- onChange={this.handleChange}
213
- value={domain.type}
214
- placeholder={formatMessage({ id: "domain.props.type.placeholder" })}
215
- options={domainStringOptions(domainTypes, domain.type)}
216
- search
217
- selection
218
- allowAdditions
219
- onAddItem={this.handleAddItem}
220
- additionLabel={<i style={{ color: "red" }}>New Domain Type: </i>}
221
- />
222
- <Form.TextArea
223
- name="description"
224
- label={formatMessage({ id: "domain.props.description" })}
225
- onChange={this.handleChange}
226
- value={domain.description}
227
- placeholder={formatMessage({
228
- id: "domain.props.description.placeholder"
229
- })}
230
- autoComplete="off"
231
- maxLength={255}
232
- required
233
- />
234
- <div className="actions">
235
- <HistoryBackButton
236
- content={formatMessage({ id: "actions.cancel" })}
237
- disabled={isSubmitting}
166
+ />
167
+ <Controller
168
+ control={control}
169
+ name="type"
170
+ render={({ field: { onBlur, onChange, value } }) => (
171
+ <Form.Dropdown
172
+ name="type"
173
+ label={formatMessage({ id: "domain.props.type" })}
174
+ basic
175
+ search
176
+ selection
177
+ clearable
178
+ options={domainTypeOptions}
179
+ onBlur={onBlur}
180
+ onChange={(_e, { value }) => onChange(value)}
181
+ placeholder={formatMessage({ id: "domain.props.type.placeholder" })}
182
+ value={value || ""}
183
+ allowAdditions
184
+ additionLabel={<i style={{ color: "red" }}>New Domain Type: </i>}
238
185
  />
239
- <Button
240
- type="submit"
241
- primary
242
- loading={isSubmitting}
243
- disabled={isSubmitting || !this.state.touched || !this.validForm()}
244
- content={formatMessage({ id: "actions.save" })}
186
+ )}
187
+ />
188
+ <Controller
189
+ control={control}
190
+ name="description"
191
+ render={({ field: { onBlur, onChange, value } }) => (
192
+ <Form.TextArea
193
+ autoComplete="off"
194
+ error={errors?.description?.message}
195
+ label={formatMessage({ id: "domain.props.description" })}
196
+ name="description"
197
+ onBlur={onBlur}
198
+ onChange={(_e, { value }) => onChange(value)}
199
+ placeholder={formatMessage({
200
+ id: "domain.props.description.placeholder",
201
+ })}
202
+ value={value}
245
203
  />
246
- </div>
247
- </Form>
248
- );
249
- }
250
- }
204
+ )}
205
+ />
206
+ <div className="actions">
207
+ <Button
208
+ floated="right"
209
+ type="submit"
210
+ primary
211
+ loading={isSubmitting}
212
+ disabled={isSubmitting || !isDirty || !isValid}
213
+ content={formatMessage({ id: "actions.save" })}
214
+ />
215
+ <HistoryBackButton
216
+ content={formatMessage({ id: "actions.cancel" })}
217
+ disabled={isSubmitting}
218
+ />
219
+ </div>
220
+ </Form>
221
+ );
222
+ };
223
+
224
+ DomainForm.propTypes = {
225
+ domain: PropTypes.object,
226
+ isSubmitting: PropTypes.bool,
227
+ domainGroups: PropTypes.array,
228
+ domainParentOptions: PropTypes.array,
229
+ domainTypes: PropTypes.array,
230
+ onSubmit: PropTypes.func.isRequired,
231
+ };
251
232
 
252
233
  const mapStateToProps = (state, ownProps) => ({
253
234
  domainTypes: getDomainTypes(state),
254
235
  domainGroups: getDomainGroups(state),
255
- domainParentOptions: _.path("domain.id")(ownProps)
236
+ domainParentOptions: ownProps.domain?.id
256
237
  ? domainParentOptionsSelector(state)
257
238
  : [],
258
- isSubmitting: state.domainCreating || state.domainUpdating
239
+ isSubmitting: state.domainCreating || state.domainUpdating,
259
240
  });
260
241
 
261
- export default compose(injectIntl, connect(mapStateToProps))(DomainForm);
242
+ export default connect(mapStateToProps)(DomainForm);
@@ -27,6 +27,28 @@ const RolesLoader = React.lazy(() =>
27
27
  import("@truedat/auth/roles/components/RolesLoader")
28
28
  );
29
29
 
30
+ const DomainRoutes = () => (
31
+ <>
32
+ <Route component={DomainLoader} />
33
+ <Route component={DomainMembersLoader} />
34
+ <Route exact path={DOMAIN} component={Domain} />
35
+ <Route exact path={DOMAIN_CONCEPTS} component={Domain} />
36
+ <Route exact path={DOMAIN_STRUCTURES} component={Domain} />
37
+ <Route exact path={DOMAIN_MEMBERS} component={Domain} />
38
+ <Route exact path={DOMAIN_EDIT} render={() => <EditDomain />} />
39
+ <Route exact path={DOMAIN_NEW} component={NewDomain} />
40
+ <Route
41
+ path={DOMAIN_MEMBERS_NEW}
42
+ render={() => (
43
+ <>
44
+ <Route component={RolesLoader} />
45
+ <Route path={DOMAIN_MEMBERS_NEW} component={AddDomainMember} exact />
46
+ </>
47
+ )}
48
+ />
49
+ </>
50
+ );
51
+
30
52
  const AuthorizedRoutes = () => (
31
53
  <>
32
54
  <Route component={DomainsLoader} />
@@ -34,39 +56,12 @@ const AuthorizedRoutes = () => (
34
56
  <Switch>
35
57
  <Route path={DOMAINS_SEARCH} component={Domains} exact />
36
58
  <Route path={DOMAINS_NEW} component={NewDomain} exact />
37
- <Route
38
- path={DOMAIN}
39
- render={() => (
40
- <>
41
- <Route component={DomainLoader} />
42
- <Route component={DomainMembersLoader} />
43
- <Route path={DOMAIN} component={Domain} exact />
44
- <Route path={DOMAIN_CONCEPTS} component={Domain} exact />
45
- <Route path={DOMAIN_STRUCTURES} component={Domain} exact />
46
- <Route path={DOMAIN_MEMBERS} component={Domain} exact />
47
- <Route path={DOMAIN_EDIT} component={EditDomain} exact />
48
- <Route path={DOMAIN_NEW} component={NewDomain} exact />
49
- <Route
50
- path={DOMAIN_MEMBERS_NEW}
51
- render={() => (
52
- <>
53
- <Route component={RolesLoader} />
54
- <Route
55
- path={DOMAIN_MEMBERS_NEW}
56
- component={AddDomainMember}
57
- exact
58
- />
59
- </>
60
- )}
61
- />
62
- </>
63
- )}
64
- />
59
+ <Route path={DOMAIN} render={() => <DomainRoutes />} />
65
60
  </Switch>
66
61
  </>
67
62
  );
68
63
 
69
- const DomainRoutes = () => {
64
+ const DomainsRoutes = () => {
70
65
  const authorized = useAuthorized(["taxonomy", "taxonomy_membership"]);
71
66
  return (
72
67
  <Route
@@ -76,4 +71,4 @@ const DomainRoutes = () => {
76
71
  );
77
72
  };
78
73
 
79
- export default DomainRoutes;
74
+ export default DomainsRoutes;
@@ -28,7 +28,7 @@ export const Domains = ({ domainsLoading, createDomain }) => (
28
28
  <Route
29
29
  exact
30
30
  path={DOMAINS_NEW}
31
- render={() => <DomainForm handleSubmit={createDomain} />}
31
+ render={() => <DomainForm onSubmit={createDomain} />}
32
32
  />
33
33
  </Switch>
34
34
  </Segment>
@@ -1,3 +1,4 @@
1
+ import _ from "lodash/fp";
1
2
  import React from "react";
2
3
  import PropTypes from "prop-types";
3
4
  import { connect } from "react-redux";
@@ -7,23 +8,24 @@ import { updateDomain } from "../routines";
7
8
  import DomainForm from "./DomainForm";
8
9
  import DomainCrumbs from "./DomainCrumbs";
9
10
 
10
- export const EditDomain = ({ domain, updateDomain }) => (
11
- <>
12
- <DomainCrumbs actionCrumb="domain.actions.edit.header" />
13
- <Container as={Segment} text>
14
- <Header as="h2">
15
- <Icon name="cube" />
16
- <Header.Content>
17
- <FormattedMessage id="domain.actions.edit.header" />
18
- </Header.Content>
19
- </Header>
20
- <DomainForm handleSubmit={updateDomain} domain={domain} />
21
- </Container>
22
- </>
23
- );
11
+ export const EditDomain = ({ domain, updateDomain }) =>
12
+ _.isEmpty(domain) ? null : (
13
+ <>
14
+ <DomainCrumbs actionCrumb="domain.actions.edit.header" />
15
+ <Container as={Segment} text>
16
+ <Header as="h2">
17
+ <Icon name="cube" />
18
+ <Header.Content>
19
+ <FormattedMessage id="domain.actions.edit.header" />
20
+ </Header.Content>
21
+ </Header>
22
+ <DomainForm onSubmit={updateDomain} domain={domain} />
23
+ </Container>
24
+ </>
25
+ );
24
26
 
25
27
  EditDomain.propTypes = {
26
- domain: PropTypes.object.isRequired,
28
+ domain: PropTypes.object,
27
29
  updateDomain: PropTypes.func.isRequired,
28
30
  };
29
31
 
@@ -15,18 +15,10 @@ export const NewDomain = ({ domain, createDomain }) => (
15
15
  <Header as="h2">
16
16
  <Icon name="cube" />
17
17
  <Header.Content>
18
- {!_.isEmpty(domain) && (
19
- <FormattedMessage id="domain.actions.create" />
20
- )}
21
- {_.isEmpty(domain) && (
22
- <FormattedMessage id="domains.actions.create" />
23
- )}
18
+ <FormattedMessage id="domain.actions.create" />
24
19
  </Header.Content>
25
20
  </Header>
26
- <DomainForm
27
- handleSubmit={createDomain}
28
- domain={{ parent_id: domain.id }}
29
- />
21
+ <DomainForm onSubmit={createDomain} domain={{ parent_id: domain.id }} />
30
22
  </Container>
31
23
  </>
32
24
  );
@@ -1,23 +1,26 @@
1
1
  import React from "react";
2
- import { shallowWithIntl } from "@truedat/test/intl-stub";
2
+ import userEvent from "@testing-library/user-event";
3
+ import { render } from "@truedat/test/render";
3
4
  import { DomainForm } from "../DomainForm";
4
5
 
5
6
  describe("<DomainForm />", () => {
6
- const handleSubmit = jest.fn();
7
+ const onSubmit = jest.fn();
7
8
  const domain = { id: 1, name: "nn", external_id: "foo", description: "dd" };
8
9
  const isSubmitting = false;
9
- const props = { domain, handleSubmit, isSubmitting };
10
+ const props = { domain, onSubmit, isSubmitting };
10
11
 
11
- it("matches the latest snapshot", () => {
12
- const wrapper = shallowWithIntl(<DomainForm {...props} />);
13
- expect(wrapper).toMatchSnapshot();
12
+ it("matches the latest snapshot", async () => {
13
+ const { container, findByRole } = render(<DomainForm {...props} />);
14
+ await findByRole("button", { name: "Save" });
15
+ expect(container).toMatchSnapshot();
14
16
  });
15
17
 
16
- it("disables submit button when there are uniformed mandatory fields", () => {
17
- const wrapper = shallowWithIntl(<DomainForm {...props} />);
18
- wrapper.setState({ touched: true });
19
- expect(wrapper.find({ type: "submit" }).prop("disabled")).toBeFalsy();
20
- wrapper.setState({ domain: { ...props, external_id: null } });
21
- expect(wrapper.find({ type: "submit" }).prop("disabled")).toBeTruthy();
18
+ it("enables submit when required fields have values", async () => {
19
+ const props = { domain: {}, onSubmit: jest.fn() };
20
+ const { findByRole } = render(<DomainForm {...props} />);
21
+ expect(await findByRole("button", { name: "Save" })).toBeDisabled();
22
+ userEvent.type(await findByRole("textbox", { name: "Name" }), "name");
23
+ userEvent.type(await findByRole("textbox", { name: "External Id" }), "id");
24
+ expect(await findByRole("button", { name: "Save" })).toBeEnabled();
22
25
  });
23
26
  });
@@ -1,142 +1,182 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<DomainForm /> matches the latest snapshot 1`] = `
4
- <Form
5
- as="form"
6
- onSubmit={[Function]}
7
- >
8
- <FormInput
9
- as={[Function]}
10
- autoComplete="off"
11
- control={[Function]}
12
- label="domain.props.name"
13
- name="name"
14
- onChange={[Function]}
15
- placeholder="domain.props.name.placeholder"
16
- required={true}
17
- value="nn"
18
- />
19
- <FormInput
20
- as={[Function]}
21
- autoComplete="off"
22
- control={[Function]}
23
- label="domain.props.external_id"
24
- name="external_id"
25
- onChange={[Function]}
26
- placeholder="domain.props.external_id.placeholder"
27
- required={true}
28
- value="foo"
29
- />
30
- <Segment>
31
- <FormGroup
32
- inline={true}
4
+ <div>
5
+ <form
6
+ class="ui form"
7
+ >
8
+ <div
9
+ class="required field"
10
+ >
11
+ <label
12
+ for="name"
13
+ >
14
+ Name
15
+ </label>
16
+ <div
17
+ class="ui input"
18
+ >
19
+ <input
20
+ autocomplete="off"
21
+ id="name"
22
+ name="name"
23
+ placeholder="Domain Name"
24
+ required=""
25
+ type="text"
26
+ value="nn"
27
+ />
28
+ </div>
29
+ </div>
30
+ <div
31
+ class="required field"
32
+ >
33
+ <label
34
+ for="external_id"
35
+ >
36
+ External Id
37
+ </label>
38
+ <div
39
+ class="ui input"
40
+ >
41
+ <input
42
+ autocomplete="off"
43
+ id="external_id"
44
+ name="external_id"
45
+ placeholder="Domain's external reference"
46
+ required=""
47
+ type="text"
48
+ value="foo"
49
+ />
50
+ </div>
51
+ </div>
52
+ <div
53
+ class="field"
33
54
  >
34
55
  <label>
35
- domain.props.domain_group
56
+ Group
36
57
  </label>
37
- </FormGroup>
38
- <Accordion>
39
- <AccordionTitle
40
- onClick={[Function]}
58
+ <div
59
+ aria-expanded="false"
60
+ class="ui basic search selection dropdown"
61
+ name="domain_group"
62
+ role="combobox"
41
63
  >
42
- <Icon
43
- as="i"
44
- name="dropdown"
64
+ <input
65
+ aria-autocomplete="list"
66
+ autocomplete="off"
67
+ class="search"
68
+ tabindex="0"
69
+ type="text"
70
+ value=""
71
+ />
72
+ <div
73
+ aria-atomic="true"
74
+ aria-live="polite"
75
+ class="divider default text"
76
+ role="alert"
77
+ >
78
+ Domain Group
79
+ </div>
80
+ <i
81
+ aria-hidden="true"
82
+ class="dropdown icon"
45
83
  />
46
- domain.domain_group.select
47
-
48
- </AccordionTitle>
49
- <AccordionContent>
50
- <FormDropdown
51
- additionLabel={
52
- <i
53
- style={
54
- Object {
55
- "color": "red",
56
- }
57
- }
58
- >
59
- domain.label.domain_group
60
- </i>
61
- }
62
- allowAdditions={true}
63
- as={[Function]}
64
- basic={true}
65
- clearable={true}
66
- control={[Function]}
67
- label="domain.props.domain_group"
68
- name="domain_group"
69
- onChange={[Function]}
70
- options={Array []}
71
- placeholder="domain.props.domain_group.placeholder"
72
- search={true}
73
- selection={true}
84
+ <div
85
+ class="menu transition"
86
+ role="listbox"
87
+ >
88
+ <div
89
+ class="message"
90
+ >
91
+ No results found.
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ <div
97
+ class="field"
98
+ >
99
+ <label>
100
+ Type
101
+ </label>
102
+ <div
103
+ aria-expanded="false"
104
+ class="ui basic search selection dropdown"
105
+ name="type"
106
+ role="combobox"
107
+ >
108
+ <input
109
+ aria-autocomplete="list"
110
+ autocomplete="off"
111
+ class="search"
112
+ tabindex="0"
113
+ type="text"
74
114
  value=""
75
115
  />
76
- </AccordionContent>
77
- </Accordion>
78
- </Segment>
79
- <FormDropdown
80
- additionLabel={
81
- <i
82
- style={
83
- Object {
84
- "color": "red",
85
- }
86
- }
116
+ <div
117
+ aria-atomic="true"
118
+ aria-live="polite"
119
+ class="divider default text"
120
+ role="alert"
121
+ >
122
+ Domain Type
123
+ </div>
124
+ <i
125
+ aria-hidden="true"
126
+ class="dropdown icon"
127
+ />
128
+ <div
129
+ class="menu transition"
130
+ role="listbox"
131
+ >
132
+ <div
133
+ aria-checked="true"
134
+ aria-selected="true"
135
+ class="active selected item"
136
+ role="option"
137
+ style="pointer-events: all;"
138
+ >
139
+ <span
140
+ class="text"
141
+ />
142
+ </div>
143
+ </div>
144
+ </div>
145
+ </div>
146
+ <div
147
+ class="field"
148
+ >
149
+ <label>
150
+ Description
151
+ </label>
152
+ <textarea
153
+ autocomplete="off"
154
+ name="description"
155
+ placeholder="A description for this domain"
156
+ rows="3"
87
157
  >
88
- New Domain Type:
89
- </i>
90
- }
91
- allowAdditions={true}
92
- as={[Function]}
93
- basic={true}
94
- clearable={true}
95
- control={[Function]}
96
- label="domain.props.type"
97
- name="type"
98
- onChange={[Function]}
99
- options={
100
- Array [
101
- Object {
102
- "key": 0,
103
- "text": "",
104
- "value": "",
105
- },
106
- ]
107
- }
108
- placeholder="domain.props.type.placeholder"
109
- search={true}
110
- selection={true}
111
- value=""
112
- />
113
- <FormTextArea
114
- as={[Function]}
115
- autoComplete="off"
116
- control={[Function]}
117
- label="domain.props.description"
118
- maxLength={255}
119
- name="description"
120
- onChange={[Function]}
121
- placeholder="domain.props.description.placeholder"
122
- required={true}
123
- value="dd"
124
- />
125
- <div
126
- className="actions"
127
- >
128
- <HistoryBackButton
129
- content="actions.cancel"
130
- disabled={false}
131
- />
132
- <Button
133
- as="button"
134
- content="actions.save"
135
- disabled={true}
136
- loading={false}
137
- primary={true}
138
- type="submit"
139
- />
140
- </div>
141
- </Form>
158
+ dd
159
+ </textarea>
160
+ </div>
161
+ <div
162
+ class="actions"
163
+ >
164
+ <button
165
+ class="ui primary disabled right floated button"
166
+ disabled=""
167
+ tabindex="-1"
168
+ type="submit"
169
+ >
170
+ Save
171
+ </button>
172
+ <a
173
+ class="ui secondary button"
174
+ href="/"
175
+ role="button"
176
+ >
177
+ Cancel
178
+ </a>
179
+ </div>
180
+ </form>
181
+ </div>
142
182
  `;
@@ -22,7 +22,7 @@ exports[`<EditDomain /> matches the latest snapshot 1`] = `
22
22
  />
23
23
  </HeaderContent>
24
24
  </Header>
25
- <injectIntl(Connect(DomainForm))
25
+ <Connect(DomainForm)
26
26
  domain={
27
27
  Object {
28
28
  "description": "dd",
@@ -30,7 +30,7 @@ exports[`<EditDomain /> matches the latest snapshot 1`] = `
30
30
  "name": "nn",
31
31
  }
32
32
  }
33
- handleSubmit={[MockFunction]}
33
+ onSubmit={[MockFunction]}
34
34
  />
35
35
  </Container>
36
36
  </Fragment>
@@ -22,13 +22,13 @@ exports[`<NewDomain /> matches the latest snapshot 1`] = `
22
22
  />
23
23
  </HeaderContent>
24
24
  </Header>
25
- <injectIntl(Connect(DomainForm))
25
+ <Connect(DomainForm)
26
26
  domain={
27
27
  Object {
28
28
  "parent_id": 1,
29
29
  }
30
30
  }
31
- handleSubmit={[MockFunction]}
31
+ onSubmit={[MockFunction]}
32
32
  />
33
33
  </Container>
34
34
  </Fragment>
@@ -11,7 +11,7 @@ const pickFields = _.pick([
11
11
  "description",
12
12
  "type",
13
13
  "parentable_ids",
14
- "domain_group"
14
+ "domain_group",
15
15
  ]);
16
16
 
17
17
  const domain = (state = initialState, { type, payload }) => {
@@ -5,11 +5,10 @@ import { API_DOMAINS } from "../api";
5
5
 
6
6
  export function* createDomainSaga({ payload }) {
7
7
  try {
8
- const { domain } = payload;
9
8
  const url = API_DOMAINS;
10
- const requestData = { domain };
11
- yield put(createDomain.request());
12
- const { data } = yield call(apiJsonPost, url, requestData, JSON_OPTS);
9
+ const body = { domain: payload };
10
+ yield put(createDomain.request(payload));
11
+ const { data } = yield call(apiJsonPost, url, body, JSON_OPTS);
13
12
  yield put(createDomain.success(data));
14
13
  } catch (error) {
15
14
  if (error.response) {
@@ -6,13 +6,12 @@ import { API_DOMAIN } from "../api";
6
6
 
7
7
  const toApiPath = compile(API_DOMAIN);
8
8
 
9
- export function* updateDomainSaga({ payload }) {
9
+ export function* updateDomainSaga({ payload: domain }) {
10
10
  try {
11
- const { domain } = payload;
12
11
  const url = toApiPath(domain);
13
- const requestData = { domain };
12
+ const body = { domain };
14
13
  yield put(updateDomain.request());
15
- const { data } = yield call(apiJsonPut, url, requestData, JSON_OPTS);
14
+ const { data } = yield call(apiJsonPut, url, body, JSON_OPTS);
16
15
  yield put(updateDomain.success(data));
17
16
  } catch (error) {
18
17
  if (error.response) {