@truedat/dd 5.18.3 → 5.19.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/package.json +5 -5
- package/src/components/StructureNoteActions.js +1 -0
- package/src/components/StructureNoteSuggestions.js +179 -0
- package/src/components/StructureNotesEdit.js +19 -2
- package/src/components/__tests__/StructureNoteSuggestions.spec.js +151 -0
- package/src/components/__tests__/StructureNotesEdit.spec.js +34 -0
- package/src/components/__tests__/__snapshots__/StructureNoteSuggestions.spec.js.snap +316 -0
- package/src/components/__tests__/__snapshots__/StructureNotesEdit.spec.js.snap +49 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dd",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.19.0",
|
|
4
4
|
"description": "Truedat Web Data Dictionary",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -88,9 +88,9 @@
|
|
|
88
88
|
},
|
|
89
89
|
"dependencies": {
|
|
90
90
|
"@apollo/client": "^3.7.1",
|
|
91
|
-
"@truedat/auth": "5.
|
|
92
|
-
"@truedat/core": "5.
|
|
93
|
-
"@truedat/df": "5.
|
|
91
|
+
"@truedat/auth": "5.19.0",
|
|
92
|
+
"@truedat/core": "5.19.0",
|
|
93
|
+
"@truedat/df": "5.19.0",
|
|
94
94
|
"lodash": "^4.17.21",
|
|
95
95
|
"moment": "^2.29.4",
|
|
96
96
|
"path-to-regexp": "^1.7.0",
|
|
@@ -115,5 +115,5 @@
|
|
|
115
115
|
"react-dom": ">= 16.8.6 < 17",
|
|
116
116
|
"semantic-ui-react": ">= 2.0.3 < 2.2"
|
|
117
117
|
},
|
|
118
|
-
"gitHead": "
|
|
118
|
+
"gitHead": "df3fcd6655456c3468259d79762800c3c417147d"
|
|
119
119
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
Checkbox,
|
|
7
|
+
Container,
|
|
8
|
+
Divider,
|
|
9
|
+
Icon,
|
|
10
|
+
List,
|
|
11
|
+
Message,
|
|
12
|
+
Segment,
|
|
13
|
+
} from "semantic-ui-react";
|
|
14
|
+
import { FormattedMessage, useIntl } from "react-intl";
|
|
15
|
+
import { apiJson } from "@truedat/core/services/api";
|
|
16
|
+
|
|
17
|
+
export default function StructureNoteSuggestions({
|
|
18
|
+
aiSuggestionsAction,
|
|
19
|
+
applySuggestions,
|
|
20
|
+
template,
|
|
21
|
+
isModification,
|
|
22
|
+
}) {
|
|
23
|
+
const [loadingSuggestion, setLoadingSuggestion] = useState();
|
|
24
|
+
const [suggestions, setSuggestions] = useState();
|
|
25
|
+
const [suggestionsError, setSuggestionsError] = useState();
|
|
26
|
+
const [selectedSuggestions, setSelectedSuggestions] = useState([]);
|
|
27
|
+
|
|
28
|
+
const { locale } = useIntl();
|
|
29
|
+
|
|
30
|
+
const suggestionKeys = _.keys(suggestions);
|
|
31
|
+
const fieldInSuggestions = ({ name }) => _.includes(name)(suggestionKeys);
|
|
32
|
+
const templateFields = _.flow(
|
|
33
|
+
_.prop("content"),
|
|
34
|
+
_.filter(({ fields }) => _.any(fieldInSuggestions)(fields)),
|
|
35
|
+
_.map(({ name, fields }) => ({
|
|
36
|
+
name,
|
|
37
|
+
fields: _.filter(fieldInSuggestions)(fields),
|
|
38
|
+
}))
|
|
39
|
+
)(template);
|
|
40
|
+
const validFields = _.flow(
|
|
41
|
+
_.prop("content"),
|
|
42
|
+
_.flatMap(({ fields }) =>
|
|
43
|
+
_.flow(
|
|
44
|
+
_.filter(({ editable }) => editable || !isModification),
|
|
45
|
+
_.map(({ name }) => name)
|
|
46
|
+
)(fields)
|
|
47
|
+
)
|
|
48
|
+
)(template);
|
|
49
|
+
|
|
50
|
+
const handleRequestSuggestions = () => {
|
|
51
|
+
setLoadingSuggestion(true);
|
|
52
|
+
setSuggestionsError(null);
|
|
53
|
+
setSelectedSuggestions([]);
|
|
54
|
+
setSuggestions(null);
|
|
55
|
+
const url = `${aiSuggestionsAction.href}?language=${locale}`;
|
|
56
|
+
apiJson(url).then(({ data: { data: suggestions } }) => {
|
|
57
|
+
if (_.prop("[0]")(suggestions) === "error") {
|
|
58
|
+
setSuggestionsError(
|
|
59
|
+
_.prop("[1].error.message")(suggestions) || _.prop("[1]")(suggestions)
|
|
60
|
+
);
|
|
61
|
+
} else {
|
|
62
|
+
setSuggestions(suggestions);
|
|
63
|
+
const selectedFields = _.flow(
|
|
64
|
+
_.keys,
|
|
65
|
+
_.filter(
|
|
66
|
+
(key) =>
|
|
67
|
+
_.includes(key)(validFields) && !_.isEmpty(suggestions[key])
|
|
68
|
+
)
|
|
69
|
+
)(suggestions);
|
|
70
|
+
setSelectedSuggestions(selectedFields);
|
|
71
|
+
}
|
|
72
|
+
setLoadingSuggestion(false);
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
const handleApplySuggestions = () => {
|
|
76
|
+
applySuggestions(_.pick(selectedSuggestions)(suggestions));
|
|
77
|
+
setSelectedSuggestions([]);
|
|
78
|
+
setSuggestions(null);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const toggleSelectedSuggestion = (name) => {
|
|
82
|
+
setSelectedSuggestions(
|
|
83
|
+
_.includes(name)(selectedSuggestions)
|
|
84
|
+
? _.reject((v) => v == name)(selectedSuggestions)
|
|
85
|
+
: [...selectedSuggestions, name]
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Segment loading={loadingSuggestion}>
|
|
91
|
+
<Container style={{ display: "flex", justifyContent: "space-between" }}>
|
|
92
|
+
<h4>
|
|
93
|
+
{suggestions ? (
|
|
94
|
+
<Icon name="lightbulb outline" />
|
|
95
|
+
) : (
|
|
96
|
+
<Icon name="lightbulb" />
|
|
97
|
+
)}
|
|
98
|
+
<FormattedMessage id="structure_note.ai_suggestion.header" />
|
|
99
|
+
</h4>
|
|
100
|
+
<Divider hidden />
|
|
101
|
+
{suggestions ? null : (
|
|
102
|
+
<Button onClick={handleRequestSuggestions}>
|
|
103
|
+
<FormattedMessage id="actions.ai_suggestion" />
|
|
104
|
+
</Button>
|
|
105
|
+
)}
|
|
106
|
+
</Container>
|
|
107
|
+
{suggestionsError ? (
|
|
108
|
+
<Message negative>
|
|
109
|
+
<Message.Header>
|
|
110
|
+
<FormattedMessage id="structure_note.ai_suggestion.error" />
|
|
111
|
+
</Message.Header>
|
|
112
|
+
<p>{suggestionsError}</p>
|
|
113
|
+
</Message>
|
|
114
|
+
) : null}
|
|
115
|
+
{suggestions ? (
|
|
116
|
+
<>
|
|
117
|
+
{_.map(({ name: groupName, fields }) => (
|
|
118
|
+
<Container key={`group-${groupName}`}>
|
|
119
|
+
<>
|
|
120
|
+
<h5>{groupName ? groupName : ""}</h5>
|
|
121
|
+
<List>
|
|
122
|
+
{_.map(({ name, label, editable }) => (
|
|
123
|
+
<List.Item
|
|
124
|
+
key={name}
|
|
125
|
+
style={{
|
|
126
|
+
display: "flex",
|
|
127
|
+
alignItems: "center",
|
|
128
|
+
gap: "12px",
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
<Checkbox
|
|
132
|
+
id={name}
|
|
133
|
+
disabled={
|
|
134
|
+
(isModification && !editable) ||
|
|
135
|
+
_.isEmpty(suggestions[name])
|
|
136
|
+
}
|
|
137
|
+
checked={_.includes(name)(selectedSuggestions)}
|
|
138
|
+
onChange={() => toggleSelectedSuggestion(name)}
|
|
139
|
+
/>
|
|
140
|
+
<label
|
|
141
|
+
htmlFor={name}
|
|
142
|
+
style={{ cursor: "pointer" }}
|
|
143
|
+
disabled
|
|
144
|
+
>
|
|
145
|
+
<List.Header>{label}</List.Header>
|
|
146
|
+
<List.Description>
|
|
147
|
+
{_.isEmpty(suggestions[name])
|
|
148
|
+
? "-"
|
|
149
|
+
: suggestions[name]}
|
|
150
|
+
</List.Description>
|
|
151
|
+
</label>
|
|
152
|
+
</List.Item>
|
|
153
|
+
))(fields)}
|
|
154
|
+
</List>
|
|
155
|
+
<Divider hidden />
|
|
156
|
+
</>
|
|
157
|
+
</Container>
|
|
158
|
+
))(templateFields)}
|
|
159
|
+
<Container textAlign="right">
|
|
160
|
+
<Button
|
|
161
|
+
primary
|
|
162
|
+
onClick={handleApplySuggestions}
|
|
163
|
+
disabled={_.isEmpty(selectedSuggestions)}
|
|
164
|
+
>
|
|
165
|
+
<FormattedMessage id="actions.apply_ai_suggestion" />
|
|
166
|
+
</Button>
|
|
167
|
+
</Container>
|
|
168
|
+
</>
|
|
169
|
+
) : null}
|
|
170
|
+
</Segment>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
StructureNoteSuggestions.propTypes = {
|
|
175
|
+
aiSuggestionsAction: PropTypes.object,
|
|
176
|
+
applySuggestions: PropTypes.func,
|
|
177
|
+
template: PropTypes.object,
|
|
178
|
+
isModification: PropTypes.bool,
|
|
179
|
+
};
|
|
@@ -9,6 +9,7 @@ import { selectTemplate, selectDomains } from "@truedat/df/routines";
|
|
|
9
9
|
import { applyTemplate, validateContent } from "@truedat/df/utils";
|
|
10
10
|
import { doStructureNoteAction } from "../routines";
|
|
11
11
|
import { getLatestDfContent } from "../selectors/getSortedStructureNotes";
|
|
12
|
+
import StructureNoteSuggestions from "./StructureNoteSuggestions";
|
|
12
13
|
|
|
13
14
|
const DynamicForm = React.lazy(() =>
|
|
14
15
|
import("@truedat/df/components/DynamicForm")
|
|
@@ -29,6 +30,9 @@ export const StructureNotesEdit = ({
|
|
|
29
30
|
latestDfContent,
|
|
30
31
|
}) => {
|
|
31
32
|
const [content, setContent] = useState(latestDfContent);
|
|
33
|
+
const aiSuggestionsAction = _.prop("_actions.ai_suggestions")(structureNotes);
|
|
34
|
+
|
|
35
|
+
const isModification = !_.isEmpty(latestDfContent);
|
|
32
36
|
|
|
33
37
|
useEffect(() => {
|
|
34
38
|
selectDomains(structure.domain_ids);
|
|
@@ -41,7 +45,7 @@ export const StructureNotesEdit = ({
|
|
|
41
45
|
}
|
|
42
46
|
}, [applyTemplate, template, latestDfContent, structure.domain_id]);
|
|
43
47
|
|
|
44
|
-
const { formatMessage } = useIntl();
|
|
48
|
+
const { formatMessage, locale } = useIntl();
|
|
45
49
|
|
|
46
50
|
const submitDisabled =
|
|
47
51
|
!template || _.size(validateContent(template)(content)) > 0;
|
|
@@ -67,14 +71,27 @@ export const StructureNotesEdit = ({
|
|
|
67
71
|
doStructureNoteAction(payload);
|
|
68
72
|
};
|
|
69
73
|
|
|
74
|
+
const applySuggestions = (suggestions) => {
|
|
75
|
+
setContent({ ...content, ...suggestions });
|
|
76
|
+
};
|
|
77
|
+
|
|
70
78
|
return (
|
|
71
79
|
<Segment attached="bottom">
|
|
72
80
|
<TemplateLoader />
|
|
81
|
+
|
|
82
|
+
{aiSuggestionsAction ? (
|
|
83
|
+
<StructureNoteSuggestions
|
|
84
|
+
aiSuggestionsAction={aiSuggestionsAction}
|
|
85
|
+
template={template}
|
|
86
|
+
applySuggestions={applySuggestions}
|
|
87
|
+
isModification={isModification}
|
|
88
|
+
/>
|
|
89
|
+
) : null}
|
|
73
90
|
<Form>
|
|
74
91
|
<DynamicForm
|
|
75
92
|
onChange={setContent}
|
|
76
93
|
content={content}
|
|
77
|
-
isModification={
|
|
94
|
+
isModification={isModification}
|
|
78
95
|
/>
|
|
79
96
|
<Button
|
|
80
97
|
primary
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import React, { Suspense } from "react";
|
|
2
|
+
import { waitFor } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { render } from "@truedat/test/render";
|
|
5
|
+
import StructureNoteSuggestions from "../StructureNoteSuggestions";
|
|
6
|
+
|
|
7
|
+
const mockSuggestions = {
|
|
8
|
+
editable_field: "editable_field_value",
|
|
9
|
+
non_editable_field: "non_editable_field_value",
|
|
10
|
+
unselect_field: "unselect_field_value",
|
|
11
|
+
};
|
|
12
|
+
jest.mock("@truedat/core/services/api", () => ({
|
|
13
|
+
apiJson: (url) => ({
|
|
14
|
+
then: (callback) =>
|
|
15
|
+
callback({
|
|
16
|
+
data: {
|
|
17
|
+
data: url.startsWith("suggestions")
|
|
18
|
+
? mockSuggestions
|
|
19
|
+
: url.startsWith("error_message")
|
|
20
|
+
? ["error", { error: { message: "ERROR MESSAGE" } }]
|
|
21
|
+
: ["error", "ERROR TEXT"],
|
|
22
|
+
},
|
|
23
|
+
}),
|
|
24
|
+
}),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
const messages = {
|
|
28
|
+
en: {
|
|
29
|
+
"actions.cancel": "cancel",
|
|
30
|
+
"actions.save": "save",
|
|
31
|
+
"structure_note.ai_suggestion.header": "header",
|
|
32
|
+
"actions.apply_ai_suggestion": "actions.apply_ai_suggestion",
|
|
33
|
+
"actions.ai_suggestion": "actions.ai_suggestion",
|
|
34
|
+
"structure_note.ai_suggestion.error": "error",
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
describe("<StructureNoteSuggestions />", () => {
|
|
39
|
+
it("matches the latest snapshot", () => {
|
|
40
|
+
const props = {
|
|
41
|
+
template: {},
|
|
42
|
+
};
|
|
43
|
+
const { container } = render(
|
|
44
|
+
<Suspense fallback={null}>
|
|
45
|
+
<StructureNoteSuggestions {...props} />
|
|
46
|
+
</Suspense>,
|
|
47
|
+
{ messages }
|
|
48
|
+
);
|
|
49
|
+
expect(container).toMatchSnapshot();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("component lifecycle", async () => {
|
|
53
|
+
const applySuggestions = jest.fn();
|
|
54
|
+
const props = {
|
|
55
|
+
aiSuggestionsAction: { href: "suggestions" },
|
|
56
|
+
applySuggestions,
|
|
57
|
+
template: {
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
fields: [
|
|
61
|
+
{
|
|
62
|
+
name: "editable_field",
|
|
63
|
+
label: "EditableField",
|
|
64
|
+
editable: true,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "Group2",
|
|
70
|
+
fields: [
|
|
71
|
+
{
|
|
72
|
+
name: "non_editable_field",
|
|
73
|
+
label: "NonEditableField",
|
|
74
|
+
editable: false,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "unselect_field",
|
|
78
|
+
label: "UnselectedField",
|
|
79
|
+
editable: true,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
isModification: true,
|
|
86
|
+
};
|
|
87
|
+
const { container, findByText, queryByText } = render(
|
|
88
|
+
<Suspense fallback={null}>
|
|
89
|
+
<StructureNoteSuggestions {...props} />
|
|
90
|
+
</Suspense>,
|
|
91
|
+
{ messages }
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
userEvent.click(await findByText(/actions.ai_suggestion/i));
|
|
95
|
+
await waitFor(() =>
|
|
96
|
+
expect(queryByText(/actions.ai_suggestion/i)).not.toBeInTheDocument()
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(container).toMatchSnapshot();
|
|
100
|
+
|
|
101
|
+
userEvent.click(await findByText(/UnselectedField/i));
|
|
102
|
+
userEvent.click(await findByText(/UnselectedField/i));
|
|
103
|
+
userEvent.click(await findByText(/UnselectedField/i));
|
|
104
|
+
|
|
105
|
+
userEvent.click(await findByText(/actions.apply_ai_suggestion/i));
|
|
106
|
+
await waitFor(() =>
|
|
107
|
+
expect(
|
|
108
|
+
queryByText(/actions.apply_ai_suggestion/i)
|
|
109
|
+
).not.toBeInTheDocument()
|
|
110
|
+
);
|
|
111
|
+
expect(applySuggestions).toHaveBeenCalledWith(
|
|
112
|
+
expect.objectContaining({ editable_field: "editable_field_value" })
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
expect(container).toMatchSnapshot();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("renders error with message", async () => {
|
|
119
|
+
const props = {
|
|
120
|
+
aiSuggestionsAction: { href: "error_message" },
|
|
121
|
+
};
|
|
122
|
+
const { container, findByText, queryByText } = render(
|
|
123
|
+
<Suspense fallback={null}>
|
|
124
|
+
<StructureNoteSuggestions {...props} />
|
|
125
|
+
</Suspense>,
|
|
126
|
+
{ messages }
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
userEvent.click(await findByText(/actions.ai_suggestion/i));
|
|
130
|
+
await waitFor(() =>
|
|
131
|
+
expect(queryByText(/error message/i)).toBeInTheDocument()
|
|
132
|
+
);
|
|
133
|
+
expect(container).toMatchSnapshot();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("renders error with message", async () => {
|
|
137
|
+
const props = {
|
|
138
|
+
aiSuggestionsAction: { href: "error_text" },
|
|
139
|
+
};
|
|
140
|
+
const { container, findByText, queryByText } = render(
|
|
141
|
+
<Suspense fallback={null}>
|
|
142
|
+
<StructureNoteSuggestions {...props} />
|
|
143
|
+
</Suspense>,
|
|
144
|
+
{ messages }
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
userEvent.click(await findByText(/actions.ai_suggestion/i));
|
|
148
|
+
await waitFor(() => expect(queryByText(/error text/i)).toBeInTheDocument());
|
|
149
|
+
expect(container).toMatchSnapshot();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -28,6 +28,40 @@ describe("<StructureNotesEdit />", () => {
|
|
|
28
28
|
expect(container).toMatchSnapshot();
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
+
it("matches the latest snapshot with ai suggestion action", () => {
|
|
32
|
+
const props = {
|
|
33
|
+
structure: { id: 1 },
|
|
34
|
+
template: { id: 2 },
|
|
35
|
+
doStructureNoteAction: () => {},
|
|
36
|
+
selectTemplate: () => {},
|
|
37
|
+
selectDomain: () => {},
|
|
38
|
+
structureNotes: {
|
|
39
|
+
_actions: {
|
|
40
|
+
ai_suggestions: { href: "suggestion" },
|
|
41
|
+
},
|
|
42
|
+
structureNotes: [],
|
|
43
|
+
},
|
|
44
|
+
latestDfContent: { foo: "bar" },
|
|
45
|
+
applyTemplate: applyTemplate({ id: 2 }),
|
|
46
|
+
selectDomains: jest.fn(),
|
|
47
|
+
};
|
|
48
|
+
const { container } = render(
|
|
49
|
+
<Suspense fallback={null}>
|
|
50
|
+
<StructureNotesEdit {...props} />
|
|
51
|
+
</Suspense>,
|
|
52
|
+
{
|
|
53
|
+
messages: {
|
|
54
|
+
en: {
|
|
55
|
+
"actions.cancel": "cancel",
|
|
56
|
+
"actions.save": "save",
|
|
57
|
+
"structure_note.ai_suggestion.header": "header",
|
|
58
|
+
"actions.ai_suggestion": "ai_suggestion",
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
expect(container).toMatchSnapshot();
|
|
64
|
+
});
|
|
31
65
|
it("calls doStructureNoteAction when submit is clicked", async () => {
|
|
32
66
|
const doStructureNoteAction = jest.fn();
|
|
33
67
|
const template = {
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<StructureNoteSuggestions /> component lifecycle 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="ui segment"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
class="ui container"
|
|
10
|
+
style="display: flex; justify-content: space-between;"
|
|
11
|
+
>
|
|
12
|
+
<h4>
|
|
13
|
+
<i
|
|
14
|
+
aria-hidden="true"
|
|
15
|
+
class="lightbulb outline icon"
|
|
16
|
+
/>
|
|
17
|
+
header
|
|
18
|
+
</h4>
|
|
19
|
+
<div
|
|
20
|
+
class="ui hidden divider"
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
23
|
+
<div
|
|
24
|
+
class="ui container"
|
|
25
|
+
>
|
|
26
|
+
<h5 />
|
|
27
|
+
<div
|
|
28
|
+
class="ui list"
|
|
29
|
+
role="list"
|
|
30
|
+
>
|
|
31
|
+
<div
|
|
32
|
+
class="item"
|
|
33
|
+
role="listitem"
|
|
34
|
+
style="display: flex; align-items: center; gap: 12px;"
|
|
35
|
+
>
|
|
36
|
+
<div
|
|
37
|
+
class="ui checked fitted checkbox"
|
|
38
|
+
>
|
|
39
|
+
<input
|
|
40
|
+
checked=""
|
|
41
|
+
class="hidden"
|
|
42
|
+
id="editable_field"
|
|
43
|
+
readonly=""
|
|
44
|
+
tabindex="0"
|
|
45
|
+
type="checkbox"
|
|
46
|
+
value=""
|
|
47
|
+
/>
|
|
48
|
+
<label
|
|
49
|
+
for="editable_field"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
<label
|
|
53
|
+
disabled=""
|
|
54
|
+
for="editable_field"
|
|
55
|
+
style="cursor: pointer;"
|
|
56
|
+
>
|
|
57
|
+
<div
|
|
58
|
+
class="header"
|
|
59
|
+
>
|
|
60
|
+
EditableField
|
|
61
|
+
</div>
|
|
62
|
+
<div
|
|
63
|
+
class="description"
|
|
64
|
+
>
|
|
65
|
+
editable_field_value
|
|
66
|
+
</div>
|
|
67
|
+
</label>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<div
|
|
71
|
+
class="ui hidden divider"
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
<div
|
|
75
|
+
class="ui container"
|
|
76
|
+
>
|
|
77
|
+
<h5>
|
|
78
|
+
Group2
|
|
79
|
+
</h5>
|
|
80
|
+
<div
|
|
81
|
+
class="ui list"
|
|
82
|
+
role="list"
|
|
83
|
+
>
|
|
84
|
+
<div
|
|
85
|
+
class="item"
|
|
86
|
+
role="listitem"
|
|
87
|
+
style="display: flex; align-items: center; gap: 12px;"
|
|
88
|
+
>
|
|
89
|
+
<div
|
|
90
|
+
class="ui disabled fitted checkbox"
|
|
91
|
+
>
|
|
92
|
+
<input
|
|
93
|
+
class="hidden"
|
|
94
|
+
disabled=""
|
|
95
|
+
id="non_editable_field"
|
|
96
|
+
readonly=""
|
|
97
|
+
tabindex="-1"
|
|
98
|
+
type="checkbox"
|
|
99
|
+
value=""
|
|
100
|
+
/>
|
|
101
|
+
<label
|
|
102
|
+
for="non_editable_field"
|
|
103
|
+
/>
|
|
104
|
+
</div>
|
|
105
|
+
<label
|
|
106
|
+
disabled=""
|
|
107
|
+
for="non_editable_field"
|
|
108
|
+
style="cursor: pointer;"
|
|
109
|
+
>
|
|
110
|
+
<div
|
|
111
|
+
class="header"
|
|
112
|
+
>
|
|
113
|
+
NonEditableField
|
|
114
|
+
</div>
|
|
115
|
+
<div
|
|
116
|
+
class="description"
|
|
117
|
+
>
|
|
118
|
+
non_editable_field_value
|
|
119
|
+
</div>
|
|
120
|
+
</label>
|
|
121
|
+
</div>
|
|
122
|
+
<div
|
|
123
|
+
class="item"
|
|
124
|
+
role="listitem"
|
|
125
|
+
style="display: flex; align-items: center; gap: 12px;"
|
|
126
|
+
>
|
|
127
|
+
<div
|
|
128
|
+
class="ui checked fitted checkbox"
|
|
129
|
+
>
|
|
130
|
+
<input
|
|
131
|
+
checked=""
|
|
132
|
+
class="hidden"
|
|
133
|
+
id="unselect_field"
|
|
134
|
+
readonly=""
|
|
135
|
+
tabindex="0"
|
|
136
|
+
type="checkbox"
|
|
137
|
+
value=""
|
|
138
|
+
/>
|
|
139
|
+
<label
|
|
140
|
+
for="unselect_field"
|
|
141
|
+
/>
|
|
142
|
+
</div>
|
|
143
|
+
<label
|
|
144
|
+
disabled=""
|
|
145
|
+
for="unselect_field"
|
|
146
|
+
style="cursor: pointer;"
|
|
147
|
+
>
|
|
148
|
+
<div
|
|
149
|
+
class="header"
|
|
150
|
+
>
|
|
151
|
+
UnselectedField
|
|
152
|
+
</div>
|
|
153
|
+
<div
|
|
154
|
+
class="description"
|
|
155
|
+
>
|
|
156
|
+
unselect_field_value
|
|
157
|
+
</div>
|
|
158
|
+
</label>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
<div
|
|
162
|
+
class="ui hidden divider"
|
|
163
|
+
/>
|
|
164
|
+
</div>
|
|
165
|
+
<div
|
|
166
|
+
class="ui right aligned container"
|
|
167
|
+
>
|
|
168
|
+
<button
|
|
169
|
+
class="ui primary button"
|
|
170
|
+
>
|
|
171
|
+
actions.apply_ai_suggestion
|
|
172
|
+
</button>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
`;
|
|
177
|
+
|
|
178
|
+
exports[`<StructureNoteSuggestions /> component lifecycle 2`] = `
|
|
179
|
+
<div>
|
|
180
|
+
<div
|
|
181
|
+
class="ui segment"
|
|
182
|
+
>
|
|
183
|
+
<div
|
|
184
|
+
class="ui container"
|
|
185
|
+
style="display: flex; justify-content: space-between;"
|
|
186
|
+
>
|
|
187
|
+
<h4>
|
|
188
|
+
<i
|
|
189
|
+
aria-hidden="true"
|
|
190
|
+
class="lightbulb icon"
|
|
191
|
+
/>
|
|
192
|
+
header
|
|
193
|
+
</h4>
|
|
194
|
+
<div
|
|
195
|
+
class="ui hidden divider"
|
|
196
|
+
/>
|
|
197
|
+
<button
|
|
198
|
+
class="ui button"
|
|
199
|
+
>
|
|
200
|
+
actions.ai_suggestion
|
|
201
|
+
</button>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
`;
|
|
206
|
+
|
|
207
|
+
exports[`<StructureNoteSuggestions /> matches the latest snapshot 1`] = `
|
|
208
|
+
<div>
|
|
209
|
+
<div
|
|
210
|
+
class="ui segment"
|
|
211
|
+
>
|
|
212
|
+
<div
|
|
213
|
+
class="ui container"
|
|
214
|
+
style="display: flex; justify-content: space-between;"
|
|
215
|
+
>
|
|
216
|
+
<h4>
|
|
217
|
+
<i
|
|
218
|
+
aria-hidden="true"
|
|
219
|
+
class="lightbulb icon"
|
|
220
|
+
/>
|
|
221
|
+
header
|
|
222
|
+
</h4>
|
|
223
|
+
<div
|
|
224
|
+
class="ui hidden divider"
|
|
225
|
+
/>
|
|
226
|
+
<button
|
|
227
|
+
class="ui button"
|
|
228
|
+
>
|
|
229
|
+
actions.ai_suggestion
|
|
230
|
+
</button>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
`;
|
|
235
|
+
|
|
236
|
+
exports[`<StructureNoteSuggestions /> renders error with message 1`] = `
|
|
237
|
+
<div>
|
|
238
|
+
<div
|
|
239
|
+
class="ui segment"
|
|
240
|
+
>
|
|
241
|
+
<div
|
|
242
|
+
class="ui container"
|
|
243
|
+
style="display: flex; justify-content: space-between;"
|
|
244
|
+
>
|
|
245
|
+
<h4>
|
|
246
|
+
<i
|
|
247
|
+
aria-hidden="true"
|
|
248
|
+
class="lightbulb icon"
|
|
249
|
+
/>
|
|
250
|
+
header
|
|
251
|
+
</h4>
|
|
252
|
+
<div
|
|
253
|
+
class="ui hidden divider"
|
|
254
|
+
/>
|
|
255
|
+
<button
|
|
256
|
+
class="ui button"
|
|
257
|
+
>
|
|
258
|
+
actions.ai_suggestion
|
|
259
|
+
</button>
|
|
260
|
+
</div>
|
|
261
|
+
<div
|
|
262
|
+
class="ui negative message"
|
|
263
|
+
>
|
|
264
|
+
<div
|
|
265
|
+
class="header"
|
|
266
|
+
>
|
|
267
|
+
error
|
|
268
|
+
</div>
|
|
269
|
+
<p>
|
|
270
|
+
ERROR MESSAGE
|
|
271
|
+
</p>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
`;
|
|
276
|
+
|
|
277
|
+
exports[`<StructureNoteSuggestions /> renders error with message 2`] = `
|
|
278
|
+
<div>
|
|
279
|
+
<div
|
|
280
|
+
class="ui segment"
|
|
281
|
+
>
|
|
282
|
+
<div
|
|
283
|
+
class="ui container"
|
|
284
|
+
style="display: flex; justify-content: space-between;"
|
|
285
|
+
>
|
|
286
|
+
<h4>
|
|
287
|
+
<i
|
|
288
|
+
aria-hidden="true"
|
|
289
|
+
class="lightbulb icon"
|
|
290
|
+
/>
|
|
291
|
+
header
|
|
292
|
+
</h4>
|
|
293
|
+
<div
|
|
294
|
+
class="ui hidden divider"
|
|
295
|
+
/>
|
|
296
|
+
<button
|
|
297
|
+
class="ui button"
|
|
298
|
+
>
|
|
299
|
+
actions.ai_suggestion
|
|
300
|
+
</button>
|
|
301
|
+
</div>
|
|
302
|
+
<div
|
|
303
|
+
class="ui negative message"
|
|
304
|
+
>
|
|
305
|
+
<div
|
|
306
|
+
class="header"
|
|
307
|
+
>
|
|
308
|
+
error
|
|
309
|
+
</div>
|
|
310
|
+
<p>
|
|
311
|
+
ERROR TEXT
|
|
312
|
+
</p>
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
`;
|
|
@@ -25,3 +25,52 @@ exports[`<StructureNotesEdit /> matches the latest snapshot 1`] = `
|
|
|
25
25
|
</div>
|
|
26
26
|
</div>
|
|
27
27
|
`;
|
|
28
|
+
|
|
29
|
+
exports[`<StructureNotesEdit /> matches the latest snapshot with ai suggestion action 1`] = `
|
|
30
|
+
<div>
|
|
31
|
+
<div
|
|
32
|
+
class="ui bottom attached segment"
|
|
33
|
+
>
|
|
34
|
+
<div
|
|
35
|
+
class="ui segment"
|
|
36
|
+
>
|
|
37
|
+
<div
|
|
38
|
+
class="ui container"
|
|
39
|
+
style="display: flex; justify-content: space-between;"
|
|
40
|
+
>
|
|
41
|
+
<h4>
|
|
42
|
+
<i
|
|
43
|
+
aria-hidden="true"
|
|
44
|
+
class="lightbulb icon"
|
|
45
|
+
/>
|
|
46
|
+
header
|
|
47
|
+
</h4>
|
|
48
|
+
<div
|
|
49
|
+
class="ui hidden divider"
|
|
50
|
+
/>
|
|
51
|
+
<button
|
|
52
|
+
class="ui button"
|
|
53
|
+
>
|
|
54
|
+
ai_suggestion
|
|
55
|
+
</button>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
<form
|
|
59
|
+
class="ui form"
|
|
60
|
+
>
|
|
61
|
+
<button
|
|
62
|
+
class="ui primary button"
|
|
63
|
+
>
|
|
64
|
+
save
|
|
65
|
+
</button>
|
|
66
|
+
<a
|
|
67
|
+
class="ui secondary button"
|
|
68
|
+
href="/"
|
|
69
|
+
role="button"
|
|
70
|
+
>
|
|
71
|
+
cancel
|
|
72
|
+
</a>
|
|
73
|
+
</form>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
`;
|