@truedat/df 8.5.6 → 8.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/df",
3
- "version": "8.5.6",
3
+ "version": "8.5.8",
4
4
  "description": "Truedat Web Data Quality Module",
5
5
  "sideEffects": false,
6
6
  "module": "src/index.js",
@@ -51,14 +51,14 @@
51
51
  "@testing-library/jest-dom": "^6.6.3",
52
52
  "@testing-library/react": "^16.3.0",
53
53
  "@testing-library/user-event": "^14.6.1",
54
- "@truedat/test": "8.5.6",
54
+ "@truedat/test": "8.5.8",
55
55
  "identity-obj-proxy": "^3.0.0",
56
56
  "jest": "^29.7.0",
57
57
  "redux-saga-test-plan": "^4.0.6"
58
58
  },
59
59
  "dependencies": {
60
60
  "@apollo/client": "^3.13.8",
61
- "@truedat/core": "8.5.6",
61
+ "@truedat/core": "8.5.8",
62
62
  "axios": "^1.15.0",
63
63
  "graphql": "^16.11.0",
64
64
  "is-hotkey": "^0.2.0",
@@ -87,5 +87,5 @@
87
87
  "semantic-ui-react": "^3.0.0-beta.2",
88
88
  "swr": "^2.3.3"
89
89
  },
90
- "gitHead": "41e5e6138f5622558bae4151e720c040c4581162"
90
+ "gitHead": "61ad9443b3d822d30dcfa977f5fad3494b3ed5b4"
91
91
  }
@@ -7,7 +7,6 @@ import DomainPreview from "./widgets/DomainPreview";
7
7
  import HierarchyPreview from "./widgets/HierarchyPreview";
8
8
  import SystemPreview from "./widgets/SystemPreview";
9
9
  import FieldViewerValue from "./FieldViewerValue";
10
- import "../styles/fieldGroupDetail.less";
11
10
 
12
11
  export const DynamicFieldValue = ({
13
12
  label,
@@ -8,7 +8,6 @@ import DomainPreview from "./widgets/DomainPreview";
8
8
  import SystemPreview from "./widgets/SystemPreview";
9
9
  import DynamicField from "./widgets/DynamicField";
10
10
  import FieldViewerValue from "./FieldViewerValue";
11
- import "../styles/fieldGroupDetail.less";
12
11
 
13
12
  export const EditableDynamicFieldValue = (props) => {
14
13
  const [updatedFailed, setUpdateFailed] = useState(false);
@@ -3,7 +3,6 @@ import PropTypes from "prop-types";
3
3
  import { Divider, List, Segment } from "semantic-ui-react";
4
4
  import DynamicFieldValue from "./DynamicFieldValue";
5
5
  import EditableDynamicFieldValue from "./EditableDynamicFieldValue";
6
- import "../styles/fieldGroupDetail.less";
7
6
 
8
7
  const parseField = (field) => {
9
8
  const { value, values } = field;
@@ -25,6 +25,7 @@ export const FieldGroupWithTranslations = ({
25
25
  setAltLang,
26
26
  isMultilingual,
27
27
  } = useLanguage();
28
+ const selectedAltLang = altLang || altLangs[0];
28
29
  const hasTranslations = hasTranslatableFields(
29
30
  fieldsByLang[defaultLang] || []
30
31
  );
@@ -58,7 +59,7 @@ export const FieldGroupWithTranslations = ({
58
59
  )}
59
60
  {hasTranslations && isMultilingual ? (
60
61
  <>
61
- <Grid>
62
+ <Grid className="translation-tabs-header-grid">
62
63
  <Grid.Row columns={2}>
63
64
  <Grid.Column>
64
65
  <Tab
@@ -79,7 +80,7 @@ export const FieldGroupWithTranslations = ({
79
80
  <LanguagesTabs
80
81
  altLangs={altLangs}
81
82
  requiredLangs={requiredLangs}
82
- selectedLang={altLang}
83
+ selectedLang={selectedAltLang}
83
84
  onLangChange={setAltLang}
84
85
  />
85
86
  </Grid.Column>
@@ -115,17 +116,17 @@ export const FieldGroupWithTranslations = ({
115
116
  }
116
117
  </Grid.Column>
117
118
  <Grid.Column>
118
- {isTranslatableField(field) && fieldsByLang[altLang] ? (
119
- <I18nProvider lang={altLang}>
119
+ {isTranslatableField(field) && selectedAltLang ? (
120
+ <I18nProvider lang={selectedAltLang}>
120
121
  <DynamicField
121
122
  scope={scope}
122
- field={getFieldForLang(field, altLang)}
123
- fields={fieldsByLang[altLang]}
123
+ field={getFieldForLang(field, selectedAltLang)}
124
+ fields={fieldsByLang[selectedAltLang] || []}
124
125
  isModification={isModification}
125
- key={`${field.name}-${altLang}`}
126
+ key={`${field.name}-${selectedAltLang}`}
126
127
  onChange={(e, { name, value }) =>
127
128
  onFieldChange(e, {
128
- lang: altLang,
129
+ lang: selectedAltLang,
129
130
  name,
130
131
  value,
131
132
  })
@@ -5,7 +5,6 @@ import { Icon, Label, Table } from "semantic-ui-react";
5
5
  import { MarkdownReader, SafeLink } from "@truedat/core/components";
6
6
  import ImagePreview from "./widgets/ImagePreview";
7
7
  import SystemPreview from "./widgets/SystemPreview";
8
- import "../styles/fieldGroupDetail.less";
9
8
 
10
9
  export const LocalizedFieldValue = ({ label, value }) => {
11
10
  const { formatMessage } = useIntl();
@@ -1,13 +1,15 @@
1
1
  import _ from "lodash/fp";
2
- import { useState } from "react";
2
+ import { useEffect, useState } from "react";
3
3
  import PropTypes from "prop-types";
4
- import { Grid } from "semantic-ui-react";
4
+ import { Grid, Header } from "semantic-ui-react";
5
5
  import { TemplateSelector } from "@truedat/core/components";
6
6
  import { applyTemplate, validateContent } from "@truedat/df/utils";
7
7
  import { useLanguage } from "@truedat/core/i18n";
8
+ import { useIntl } from "react-intl";
8
9
  import DynamicFormWithTranslations from "./DynamicFormWithTranslations";
9
10
 
10
11
  export default function SelectDynamicFormWithTranslations({
12
+ beforeSelector,
11
13
  i18nContent,
12
14
  onChangeContent,
13
15
  domainIds,
@@ -24,12 +26,22 @@ export default function SelectDynamicFormWithTranslations({
24
26
  disabled,
25
27
  disableSelector,
26
28
  selectedTemplate,
29
+ showTemplateSectionTitle,
27
30
  }) {
28
31
  const [thisTemplate, setThisTemplate] = useState();
29
- const [showSelector, setShowSelector] = useState(false);
30
- const template = selectedTemplate || thisTemplate;
31
- const { defaultLang, enabledLangs, isMultilingual } = useLanguage();
32
- const domain = _.size(domainIds) > 0 ? { id: _.head(domainIds) } : null;
32
+ const template = _.isEmpty(selectedTemplate) ? thisTemplate : selectedTemplate;
33
+ const { enabledLangs } = useLanguage();
34
+ const { formatMessage } = useIntl();
35
+ const domainIdsArray = _.isArray(domainIds) ? domainIds : [];
36
+ const domain = _.size(domainIdsArray) > 0 ? { id: _.head(domainIdsArray) } : null;
37
+ const hasDomainSelection = domain !== null;
38
+ const domainKey = domainIdsArray.join(",");
39
+ const templateTitle =
40
+ template &&
41
+ formatMessage({
42
+ id: `templates.${template.label || template.name}`,
43
+ defaultMessage: template.label || template.name,
44
+ });
33
45
 
34
46
  const handleChange = (content, currentTemplate = thisTemplate, lang) => {
35
47
  onChangeContent(lang, {
@@ -38,6 +50,21 @@ export default function SelectDynamicFormWithTranslations({
38
50
  });
39
51
  };
40
52
 
53
+ useEffect(() => {
54
+ if (actionKey === "create") {
55
+ setThisTemplate(undefined);
56
+ onChangeTemplate(undefined);
57
+ }
58
+ // eslint-disable-next-line react-hooks/exhaustive-deps
59
+ }, [domainKey]);
60
+
61
+ const applyTemplateToLangs = (tpl) => {
62
+ enabledLangs.forEach((lang) => {
63
+ const content = tpl ? applyTemplate(tpl)(i18nContent[lang] || {}, domain?.id) : {};
64
+ handleChange(content, tpl, lang);
65
+ });
66
+ };
67
+
41
68
  const handleLoad = ({ templates }) => {
42
69
  const templateFound = name
43
70
  ? _.find(_.propEq("name", name))(templates)
@@ -45,19 +72,10 @@ export default function SelectDynamicFormWithTranslations({
45
72
  ? _.head(templates)
46
73
  : null;
47
74
 
48
- setShowSelector(templates.length > 1);
49
-
50
- if (templateFound && (!name || templateFound.name === name)) {
75
+ if (templateFound) {
51
76
  onChangeTemplate(templateFound);
52
77
  setThisTemplate(templateFound);
53
-
54
- enabledLangs.forEach((lang) => {
55
- const contentForLang = applyTemplate(templateFound)(
56
- i18nContent[lang]?.content || {},
57
- domain?.id
58
- );
59
- handleChange(contentForLang, templateFound, defaultLang);
60
- });
78
+ applyTemplateToLangs(templateFound);
61
79
  }
62
80
  };
63
81
 
@@ -65,42 +83,44 @@ export default function SelectDynamicFormWithTranslations({
65
83
  if (selectedTpl !== thisTemplate) {
66
84
  onChangeTemplate(selectedTpl);
67
85
  setThisTemplate(selectedTpl);
68
-
69
- enabledLangs.forEach((lang) => {
70
- const languageContent = i18nContent[lang]?.content || {};
71
- const processedContent = selectedTpl
72
- ? applyTemplate(selectedTpl)(languageContent, domain?.id)
73
- : {};
74
- handleChange(processedContent, selectedTpl, defaultLang);
75
- });
86
+ applyTemplateToLangs(selectedTpl);
76
87
  }
77
88
  };
78
89
 
79
90
  return (
80
91
  <>
81
- {(!actionKey || actionKey === "create") && (
82
- <Grid style={{ display: showSelector ? "block" : "none" }}>
83
- <Grid.Row columns={isMultilingual ? 2 : 1}>
84
- <Grid.Column>
85
- <TemplateSelector
86
- scope={scope}
87
- domainIds={domainIds}
88
- hideLabel={hideLabel}
89
- onChange={handleSelected}
90
- onLoad={handleLoad}
91
- label={label}
92
- placeholder={placeholder}
93
- required={required}
94
- selectedValue={template?.id}
95
- disabled={disabled || disableSelector}
96
- />
97
- </Grid.Column>
98
- {isMultilingual && <Grid.Column />}
92
+ {(beforeSelector || (hasDomainSelection && (!actionKey || actionKey === "create"))) && (
93
+ <Grid stackable>
94
+ <Grid.Row columns={beforeSelector ? 2 : 1} verticalAlign="bottom">
95
+ {beforeSelector ? <Grid.Column>{beforeSelector}</Grid.Column> : null}
96
+ {hasDomainSelection && (!actionKey || actionKey === "create") ? (
97
+ <Grid.Column>
98
+ <TemplateSelector
99
+ scope={scope}
100
+ domainIds={domainIds}
101
+ hideLabel={hideLabel}
102
+ onChange={handleSelected}
103
+ onLoad={handleLoad}
104
+ label={label}
105
+ placeholder={placeholder}
106
+ required={required}
107
+ liftRequiredLabel
108
+ selectedValue={template?.id}
109
+ disabled={disabled || disableSelector}
110
+ showSingleOption
111
+ />
112
+ </Grid.Column>
113
+ ) : null}
99
114
  </Grid.Row>
100
115
  </Grid>
101
116
  )}
102
117
  {!disabled && template && (
103
118
  <>
119
+ {showTemplateSectionTitle && templateTitle ? (
120
+ <Header as="h3" dividing>
121
+ {templateTitle}
122
+ </Header>
123
+ ) : null}
104
124
  {header}
105
125
  <DynamicFormWithTranslations
106
126
  template={template}
@@ -116,6 +136,7 @@ export default function SelectDynamicFormWithTranslations({
116
136
  }
117
137
 
118
138
  SelectDynamicFormWithTranslations.propTypes = {
139
+ beforeSelector: PropTypes.node,
119
140
  i18nContent: PropTypes.object,
120
141
  onChangeContent: PropTypes.func,
121
142
  domainIds: PropTypes.array,
@@ -132,4 +153,5 @@ SelectDynamicFormWithTranslations.propTypes = {
132
153
  disabled: PropTypes.bool,
133
154
  disableSelector: PropTypes.bool,
134
155
  selectedTemplate: PropTypes.object,
156
+ showTemplateSectionTitle: PropTypes.bool,
135
157
  };
@@ -118,5 +118,38 @@ describe("<DynamicFormWithTranslations /> markdown", () => {
118
118
  },
119
119
  });
120
120
  });
121
- });
122
121
 
122
+ it("renders the alternate language field when its content is missing", () => {
123
+ const template = {
124
+ scope: "bg",
125
+ name: "tmpl",
126
+ content: [
127
+ {
128
+ name: "g1",
129
+ fields: [
130
+ {
131
+ name: "md-field",
132
+ label: "md-label",
133
+ type: "markdown",
134
+ widget: "markdown",
135
+ cardinality: "1",
136
+ },
137
+ ],
138
+ },
139
+ ],
140
+ };
141
+
142
+ const rendered = render(
143
+ <DynamicFormWithTranslations
144
+ applyTemplate={(content) => content}
145
+ i18nContent={{
146
+ es: { "md-field": { value: "## ES", origin: "user" } },
147
+ }}
148
+ onChange={jest.fn()}
149
+ template={template}
150
+ />,
151
+ );
152
+
153
+ expect(rendered.getAllByText(/edit markdown/i)).toHaveLength(2);
154
+ });
155
+ });
@@ -0,0 +1,86 @@
1
+ import { render, waitFor } from "@testing-library/react";
2
+ import SelectDynamicFormWithTranslations from "../SelectDynamicFormWithTranslations";
3
+
4
+ const mockTemplate = {
5
+ id: "1",
6
+ name: "business_term",
7
+ content: [
8
+ {
9
+ name: "main",
10
+ fields: [
11
+ {
12
+ name: "description",
13
+ label: "Description",
14
+ type: "string",
15
+ widget: "textarea",
16
+ },
17
+ ],
18
+ },
19
+ ],
20
+ };
21
+
22
+ jest.mock("@truedat/core/i18n", () => ({
23
+ useLanguage: () => ({
24
+ defaultLang: "en",
25
+ enabledLangs: ["en", "es"],
26
+ isMultilingual: true,
27
+ }),
28
+ }));
29
+
30
+ jest.mock("react-intl", () => ({
31
+ useIntl: () => ({
32
+ formatMessage: ({ defaultMessage, id }) => defaultMessage || id,
33
+ }),
34
+ }));
35
+
36
+ jest.mock("@truedat/core/components", () => {
37
+ const React = require("react");
38
+
39
+ return {
40
+ TemplateSelector: ({ onLoad }) => {
41
+ React.useEffect(() => {
42
+ onLoad({ templates: [mockTemplate] });
43
+ // eslint-disable-next-line react-hooks/exhaustive-deps
44
+ }, []);
45
+
46
+ return null;
47
+ },
48
+ };
49
+ });
50
+
51
+ jest.mock("../DynamicFormWithTranslations", () => () => null);
52
+
53
+ describe("<SelectDynamicFormWithTranslations />", () => {
54
+ it("loads template content for each enabled language", async () => {
55
+ const onChangeContent = jest.fn();
56
+
57
+ render(
58
+ <SelectDynamicFormWithTranslations
59
+ actionKey="create"
60
+ domainIds={[1]}
61
+ i18nContent={{
62
+ en: { description: { value: "English text", origin: "user" } },
63
+ es: { description: { value: "Texto español", origin: "user" } },
64
+ }}
65
+ onChangeContent={onChangeContent}
66
+ onChangeTemplate={jest.fn()}
67
+ scope="bg"
68
+ />
69
+ );
70
+
71
+ await waitFor(() => {
72
+ expect(onChangeContent).toHaveBeenCalledWith("en", {
73
+ content: {
74
+ description: { value: "English text", origin: "user" },
75
+ },
76
+ valid: [],
77
+ });
78
+ expect(onChangeContent).toHaveBeenCalledWith("es", {
79
+ content: {
80
+ description: { value: "Texto español", origin: "user" },
81
+ },
82
+ valid: [],
83
+ });
84
+ });
85
+ });
86
+ });
@@ -76,13 +76,8 @@ exports[`<DropdownField /> matches the latest snapshot when type domain 1`] = `
76
76
  tabindex="0"
77
77
  >
78
78
  <div
79
- class="ui label"
80
- >
81
- <i
82
- aria-hidden="true"
83
- class="delete icon"
84
- />
85
- </div>
79
+ class="text"
80
+ />
86
81
  <i
87
82
  aria-hidden="true"
88
83
  class="dropdown icon"
@@ -1,26 +1,26 @@
1
1
  .list > .item > .header.dynamic-field-header {
2
2
  margin-bottom: 10px;
3
3
  }
4
- .list > .item > .header.dynamic-field-header i{
4
+ .list > .item > .header.dynamic-field-header i {
5
5
  visibility: hidden;
6
6
  }
7
7
 
8
- .list > .item > .header.editable-dynamic-field-header{
8
+ .list > .item > .header.editable-dynamic-field-header {
9
9
  cursor: pointer;
10
10
  }
11
11
 
12
- .list > .item > .header.editable-dynamic-field-header i{
12
+ .list > .item > .header.editable-dynamic-field-header i {
13
13
  color: rgba(0, 0, 0, 0.8);
14
14
  font-size: 14px;
15
15
  margin-left: 5px;
16
16
  visibility: hidden;
17
17
  }
18
18
 
19
- .list > .item > .header.editable-dynamic-field-header:hover i{
19
+ .list > .item > .header.editable-dynamic-field-header:hover i {
20
20
  visibility: visible;
21
21
  }
22
22
 
23
- .editable-dynamic-field-buttons{
23
+ .editable-dynamic-field-buttons {
24
24
  padding: 8px;
25
25
  }
26
26
 
@@ -29,41 +29,83 @@
29
29
  }
30
30
 
31
31
  @keyframes shake {
32
- 10%, 90% {
32
+ 10%,
33
+ 90% {
33
34
  transform: translate3d(-1px, 0, 0);
34
35
  }
35
-
36
- 20%, 80% {
36
+
37
+ 20%,
38
+ 80% {
37
39
  transform: translate3d(2px, 0, 0);
38
40
  }
39
41
 
40
- 30%, 50%, 70% {
42
+ 30%,
43
+ 50%,
44
+ 70% {
41
45
  transform: translate3d(-4px, 0, 0);
42
46
  }
43
47
 
44
- 40%, 60% {
48
+ 40%,
49
+ 60% {
45
50
  transform: translate3d(4px, 0, 0);
46
51
  }
47
52
  }
48
53
 
49
54
  .hidden-secret-value {
50
- font-family: 'Courier New', Courier, monospace;
55
+ font-family: "Courier New", Courier, monospace;
51
56
  color: #888;
52
57
  }
53
58
 
54
-
55
59
  .identifier-value {
56
- font-family: 'Courier New', Courier, monospace;
60
+ font-family: "Courier New", Courier, monospace;
57
61
  font-size: medium;
58
62
  }
59
63
  .default-value {
60
64
  white-space: pre-wrap;
61
65
  }
62
66
 
63
- .attached-tab.ui.menu.attached.top {
64
- border: 1px solid #ddd;
65
- border-radius: 4px 4px 0 0;
66
- margin: 0;
67
+ .translation-tabs-header-grid.ui.grid {
68
+ margin-left: 0;
69
+ margin-right: 0;
70
+ }
71
+
72
+ .translation-tabs-header-grid.ui.grid > .row {
73
+ padding-left: 0;
74
+ padding-right: 0;
75
+ }
76
+
77
+ .translation-tabs-header-grid.ui.grid > .row > .column:first-child {
78
+ padding-left: 0;
79
+ }
80
+
81
+ .translation-tabs-header-grid.ui.grid > .row > .column:last-child {
82
+ padding-right: 0;
83
+ }
84
+
85
+ .translation-tabs-header-grid.ui.grid
86
+ > .row
87
+ > .column:first-child
88
+ .attached-tab[attached="top"]
89
+ > .ui.attached.tabular.menu {
90
+ border-left: none;
91
+ border-top-left-radius: 0;
92
+ }
93
+
94
+ .translation-tabs-header-grid.ui.grid
95
+ > .row
96
+ .ui.attached.tabular.menu
97
+ > .item:first-child {
98
+ margin-left: -1px;
99
+ }
100
+
101
+ .translation-tabs-header-grid.ui.grid
102
+ > .row
103
+ > .column:first-child
104
+ .attached-tab[attached="top"]
105
+ > .ui.attached.tabular.menu
106
+ > .item:first-child {
107
+ border-left: 1px solid #ddd;
108
+ border-top-left-radius: 4px;
67
109
  }
68
110
 
69
111
  .attached-segment.ui.attached.segment.bottom {
@@ -71,4 +113,4 @@
71
113
  border-top: none;
72
114
  border-radius: 0 0 4px 4px;
73
115
  padding: 1em;
74
- }
116
+ }
@@ -3,8 +3,6 @@ import {
3
3
  Header,
4
4
  Icon,
5
5
  Segment,
6
- Grid,
7
- Container,
8
6
  Button,
9
7
  } from "semantic-ui-react";
10
8
  import { Link } from "react-router";
@@ -31,21 +29,16 @@ export default function Templates() {
31
29
  </Header.Subheader>
32
30
  </Header.Content>
33
31
  </Header>
34
- <Grid>
35
- <Grid.Column width={8}>
36
- <TemplateFilters />
37
- </Grid.Column>
38
- <Grid.Column width={8}>
39
- <Container textAlign="right">
40
- <Button
41
- primary
42
- as={Link}
43
- to={TEMPLATES_NEW}
44
- content={<FormattedMessage id="templates.actions.create" />}
45
- />
46
- </Container>
47
- </Grid.Column>
48
- </Grid>
32
+ <Segment basic clearing>
33
+ <TemplateFilters />
34
+ <Button
35
+ primary
36
+ floated="right"
37
+ as={Link}
38
+ to={TEMPLATES_NEW}
39
+ content={<FormattedMessage id="templates.actions.create" />}
40
+ />
41
+ </Segment>
49
42
  <TemplatesTable />
50
43
  </Segment>
51
44
  );