@eeacms/volto-editing-progress 0.2.3 → 0.4.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/.husky/pre-commit +2 -0
- package/CHANGELOG.md +51 -3
- package/DEVELOP.md +6 -7
- package/README.md +21 -105
- package/RELEASE.md +74 -0
- package/cypress.config.js +2 -2
- package/locales/de/LC_MESSAGES/volto.po +10 -0
- package/locales/en/LC_MESSAGES/volto.po +10 -0
- package/locales/it/LC_MESSAGES/volto.po +10 -0
- package/locales/ro/LC_MESSAGES/volto.po +10 -0
- package/locales/volto.pot +11 -1
- package/package.json +26 -2
- package/src/EditingProgress.jsx +1 -2
- package/src/TextareaJSONWidget.jsx +25 -19
- package/src/VisualJSONWidget.jsx +294 -0
- package/src/VisualWidget.test.js +51 -0
- package/src/WidgetDataComponent.jsx +262 -0
- package/src/WidgetSidebar.jsx +88 -0
- package/src/actionTypes/index.js +1 -0
- package/src/actions/index.js +12 -2
- package/src/index.js +5 -4
- package/src/less/editor.less +34 -0
- package/src/reducers/index.js +44 -1
- package/src/schema.js +27 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { ModalForm } from '@plone/volto/components';
|
|
3
|
+
import { JSONSchema } from './schema';
|
|
4
|
+
import { Dropdown } from 'semantic-ui-react';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
Button,
|
|
8
|
+
Container,
|
|
9
|
+
Segment,
|
|
10
|
+
Divider,
|
|
11
|
+
Sidebar,
|
|
12
|
+
} from 'semantic-ui-react';
|
|
13
|
+
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
14
|
+
import './less/editor.less';
|
|
15
|
+
import _ from 'lodash';
|
|
16
|
+
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
|
17
|
+
import PropTypes from 'prop-types';
|
|
18
|
+
import { useSelector, useDispatch } from 'react-redux';
|
|
19
|
+
import { getRawContent } from './actions';
|
|
20
|
+
import SidebarComponent from './WidgetSidebar';
|
|
21
|
+
import EditDataComponent from './WidgetDataComponent';
|
|
22
|
+
import './less/editor.less';
|
|
23
|
+
const messages = defineMessages({
|
|
24
|
+
jsonTitle: {
|
|
25
|
+
id: 'Edit JSON',
|
|
26
|
+
defaultMessage: 'Edit JSON',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const SIDEBAR_WIDTH = '250px';
|
|
31
|
+
export const COMPONENT_HEIGHT = '750px';
|
|
32
|
+
|
|
33
|
+
function isValidJson(json) {
|
|
34
|
+
try {
|
|
35
|
+
JSON.parse(json);
|
|
36
|
+
return true;
|
|
37
|
+
} catch (e) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function addNewStateToAlreadyExistingField(
|
|
43
|
+
currentContentTypeData,
|
|
44
|
+
currentField,
|
|
45
|
+
statesToAdd,
|
|
46
|
+
message,
|
|
47
|
+
condition,
|
|
48
|
+
link,
|
|
49
|
+
) {
|
|
50
|
+
for (
|
|
51
|
+
let localRuleIndex = 0;
|
|
52
|
+
localRuleIndex < currentContentTypeData.length;
|
|
53
|
+
localRuleIndex++
|
|
54
|
+
) {
|
|
55
|
+
if (currentContentTypeData[localRuleIndex].prefix !== currentField)
|
|
56
|
+
continue;
|
|
57
|
+
|
|
58
|
+
// TODO rewrite message as an object with multiple message as a key and add it
|
|
59
|
+
// to currentContentTypeData dinamically in order to create the posibillity
|
|
60
|
+
// for multiple fields
|
|
61
|
+
|
|
62
|
+
if (message) currentContentTypeData[localRuleIndex].linkLabel = message;
|
|
63
|
+
if (condition) currentContentTypeData[localRuleIndex].condition = condition;
|
|
64
|
+
if (link) currentContentTypeData[localRuleIndex].link = link;
|
|
65
|
+
if (statesToAdd !== undefined) {
|
|
66
|
+
currentContentTypeData[localRuleIndex].states = statesToAdd;
|
|
67
|
+
} else if (!message) currentContentTypeData.splice(localRuleIndex, 1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function doesPrefixExistInCurrentContentTypeData(
|
|
72
|
+
currentContentTypeData,
|
|
73
|
+
currentField,
|
|
74
|
+
) {
|
|
75
|
+
return currentContentTypeData.every((rule) => rule.prefix !== currentField);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function createFieldRule(currentField, statesToAdd) {
|
|
79
|
+
return {
|
|
80
|
+
prefix: currentField,
|
|
81
|
+
states: statesToAdd,
|
|
82
|
+
condition: 'python:value',
|
|
83
|
+
link: 'edit#fieldset-metadata-field-label-' + currentField,
|
|
84
|
+
linkLabel: 'Add {label}',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const VisualJSONWidget = (props) => {
|
|
89
|
+
const { id, value = {}, onChange } = props;
|
|
90
|
+
const [isJSONEditorOpen, setIsJSONEditorOpen] = useState(false);
|
|
91
|
+
const [currentContentType, setCurrentContentType] = useState();
|
|
92
|
+
|
|
93
|
+
const path = flattenToAppURL(
|
|
94
|
+
currentContentType?.['@id'] ? `${currentContentType['@id']}` : null,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const dispatch = useDispatch();
|
|
98
|
+
const request = useSelector((state) => state.rawdata?.[path]);
|
|
99
|
+
const content = request?.data;
|
|
100
|
+
const types = useSelector((state) => state.types);
|
|
101
|
+
const fields =
|
|
102
|
+
request?.data?.fieldsets.reduce((acc, cur) => {
|
|
103
|
+
return [...acc, ...(cur.fields || [])];
|
|
104
|
+
}, []) || [];
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
if (path && !request?.loading && !request?.loaded && !content)
|
|
107
|
+
dispatch(getRawContent(path));
|
|
108
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
109
|
+
}, [dispatch, path, content, request?.loaded, request?.loading]);
|
|
110
|
+
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
if (types.loaded && !types.loading && Array.isArray(types.types)) {
|
|
113
|
+
setCurrentContentType(types.types[0]);
|
|
114
|
+
}
|
|
115
|
+
}, [types]);
|
|
116
|
+
|
|
117
|
+
const handleOnCancel = (e) => {
|
|
118
|
+
setIsJSONEditorOpen(false);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const handleEditJSON = (e) => {
|
|
122
|
+
e.preventDefault();
|
|
123
|
+
setIsJSONEditorOpen(true);
|
|
124
|
+
};
|
|
125
|
+
const makeFirstLetterCapital = (string) => {
|
|
126
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
127
|
+
};
|
|
128
|
+
const onJSONSubmit = (e) => {
|
|
129
|
+
setIsJSONEditorOpen(false);
|
|
130
|
+
if (typeof e.json === 'string' && isValidJson(e.json)) {
|
|
131
|
+
onChange(id, JSON.parse(e.json));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
onChange(id, e.json);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const handleChangeSelectedContentType = (e, type) => {
|
|
138
|
+
setCurrentContentType(type);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const handleOnDropdownChange = (
|
|
142
|
+
e,
|
|
143
|
+
data,
|
|
144
|
+
currentField,
|
|
145
|
+
message,
|
|
146
|
+
condition,
|
|
147
|
+
link,
|
|
148
|
+
) => {
|
|
149
|
+
const states = data.value;
|
|
150
|
+
const statesToAdd = states?.map((state) => state.toLowerCase());
|
|
151
|
+
const localCopyOfValue = _.cloneDeep(value);
|
|
152
|
+
const currentContentTypeData = localCopyOfValue[currentContentType.id];
|
|
153
|
+
|
|
154
|
+
if (!currentContentTypeData && data) {
|
|
155
|
+
//Reference doesn't work with currentContentTypeData
|
|
156
|
+
localCopyOfValue[currentContentType.id] = [
|
|
157
|
+
createFieldRule(currentField, statesToAdd),
|
|
158
|
+
];
|
|
159
|
+
} else if (
|
|
160
|
+
doesPrefixExistInCurrentContentTypeData(
|
|
161
|
+
currentContentTypeData,
|
|
162
|
+
currentField,
|
|
163
|
+
) &&
|
|
164
|
+
data
|
|
165
|
+
) {
|
|
166
|
+
currentContentTypeData.push(createFieldRule(currentField, statesToAdd));
|
|
167
|
+
} else {
|
|
168
|
+
addNewStateToAlreadyExistingField(
|
|
169
|
+
currentContentTypeData,
|
|
170
|
+
currentField,
|
|
171
|
+
statesToAdd,
|
|
172
|
+
message,
|
|
173
|
+
condition,
|
|
174
|
+
link,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
//The variable currentContentTypeData cannot be used here because of eslint and delete keyword
|
|
178
|
+
if (localCopyOfValue[currentContentType.id]?.length === 0) {
|
|
179
|
+
delete localCopyOfValue[currentContentType.id];
|
|
180
|
+
}
|
|
181
|
+
onChange(id, localCopyOfValue);
|
|
182
|
+
};
|
|
183
|
+
const getDropdownValues = (currentField) => {
|
|
184
|
+
if (
|
|
185
|
+
!request.loading &&
|
|
186
|
+
request.loaded &&
|
|
187
|
+
currentContentType &&
|
|
188
|
+
value[currentContentType.id]
|
|
189
|
+
)
|
|
190
|
+
return value[currentContentType.id]
|
|
191
|
+
.find((rule) => rule?.prefix === currentField)
|
|
192
|
+
?.states.map((state) => makeFirstLetterCapital(state));
|
|
193
|
+
|
|
194
|
+
return undefined;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<>
|
|
199
|
+
<div>
|
|
200
|
+
{isJSONEditorOpen && (
|
|
201
|
+
<ModalForm
|
|
202
|
+
schema={JSONSchema(props)}
|
|
203
|
+
onSubmit={onJSONSubmit}
|
|
204
|
+
title={props.intl.formatMessage(messages.jsonTitle)}
|
|
205
|
+
open={isJSONEditorOpen}
|
|
206
|
+
formData={{ json: JSON.stringify(value, undefined, 2) }}
|
|
207
|
+
onCancel={handleOnCancel}
|
|
208
|
+
key="JSON"
|
|
209
|
+
/>
|
|
210
|
+
)}
|
|
211
|
+
<Container>
|
|
212
|
+
<Button onClick={handleEditJSON} color="grey" id="json_button">
|
|
213
|
+
<FormattedMessage id="Edit JSON" defaultMessage="Edit JSON" />
|
|
214
|
+
</Button>
|
|
215
|
+
|
|
216
|
+
{fields && (
|
|
217
|
+
<Dropdown
|
|
218
|
+
className="ui grey button dropdown-button"
|
|
219
|
+
text="Add Property"
|
|
220
|
+
options={fields
|
|
221
|
+
.filter((field) => {
|
|
222
|
+
return (
|
|
223
|
+
getDropdownValues(field) === undefined &&
|
|
224
|
+
!request.data.required.includes(field)
|
|
225
|
+
);
|
|
226
|
+
})
|
|
227
|
+
.map((field) => {
|
|
228
|
+
return { key: field, text: field, value: field };
|
|
229
|
+
})}
|
|
230
|
+
onChange={(e, t) => {
|
|
231
|
+
handleOnDropdownChange(e, { value: ['all'] }, t.value);
|
|
232
|
+
}}
|
|
233
|
+
/>
|
|
234
|
+
)}
|
|
235
|
+
</Container>
|
|
236
|
+
<Divider />
|
|
237
|
+
</div>
|
|
238
|
+
<Sidebar.Pushable as={Segment} style={{ height: COMPONENT_HEIGHT }}>
|
|
239
|
+
<SidebarComponent
|
|
240
|
+
handleChangeSelectedContentType={handleChangeSelectedContentType}
|
|
241
|
+
currentContentType={currentContentType}
|
|
242
|
+
types={types}
|
|
243
|
+
value={value}
|
|
244
|
+
/>
|
|
245
|
+
<Sidebar.Pusher style={{ width: `calc(100% - ${SIDEBAR_WIDTH})` }}>
|
|
246
|
+
<EditDataComponent
|
|
247
|
+
request={request}
|
|
248
|
+
handleOnDropdownChange={handleOnDropdownChange}
|
|
249
|
+
currentContentType={currentContentType}
|
|
250
|
+
value={value}
|
|
251
|
+
fields={fields}
|
|
252
|
+
getDropdownValues={getDropdownValues}
|
|
253
|
+
/>
|
|
254
|
+
</Sidebar.Pusher>
|
|
255
|
+
</Sidebar.Pushable>
|
|
256
|
+
</>
|
|
257
|
+
);
|
|
258
|
+
};
|
|
259
|
+
/**
|
|
260
|
+
* Property types.
|
|
261
|
+
* @property {Object} propTypes Property types.
|
|
262
|
+
* @static
|
|
263
|
+
*/
|
|
264
|
+
VisualJSONWidget.propTypes = {
|
|
265
|
+
id: PropTypes.string.isRequired,
|
|
266
|
+
title: PropTypes.string.isRequired,
|
|
267
|
+
description: PropTypes.string,
|
|
268
|
+
required: PropTypes.bool,
|
|
269
|
+
error: PropTypes.arrayOf(PropTypes.string),
|
|
270
|
+
value: PropTypes.object,
|
|
271
|
+
onChange: PropTypes.func,
|
|
272
|
+
onEdit: PropTypes.func,
|
|
273
|
+
onDelete: PropTypes.func,
|
|
274
|
+
wrapped: PropTypes.bool,
|
|
275
|
+
placeholder: PropTypes.string,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Default properties.
|
|
280
|
+
* @property {Object} defaultProps Default properties.
|
|
281
|
+
* @static
|
|
282
|
+
*/
|
|
283
|
+
VisualJSONWidget.defaultProps = {
|
|
284
|
+
description: null,
|
|
285
|
+
required: false,
|
|
286
|
+
error: [],
|
|
287
|
+
value: null,
|
|
288
|
+
onChange: null,
|
|
289
|
+
onEdit: null,
|
|
290
|
+
onDelete: null,
|
|
291
|
+
title: '',
|
|
292
|
+
id: '',
|
|
293
|
+
};
|
|
294
|
+
export default injectIntl(VisualJSONWidget);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { makeFirstLetterCapital } from './WidgetDataComponent';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Provider } from 'react-intl-redux';
|
|
4
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
5
|
+
import renderer from 'react-test-renderer';
|
|
6
|
+
import configureStore from 'redux-mock-store';
|
|
7
|
+
import VisualJSONWidget from './VisualJSONWidget';
|
|
8
|
+
import { backgroundColor } from './WidgetSidebar';
|
|
9
|
+
const mockStore = configureStore();
|
|
10
|
+
const propsEmpty = {};
|
|
11
|
+
describe('Widget Data Component', () => {
|
|
12
|
+
it('should make first letter capital', () => {
|
|
13
|
+
const testString = 'this is a test string';
|
|
14
|
+
expect(makeFirstLetterCapital(testString)[0]).toEqual(
|
|
15
|
+
testString[0].toUpperCase(),
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe('Widget Sidebar', () => {
|
|
20
|
+
it('should return a background lightblue', () => {
|
|
21
|
+
expect(backgroundColor(true, false)).toEqual('lightblue');
|
|
22
|
+
expect(backgroundColor(true, true)).toEqual('lightblue');
|
|
23
|
+
});
|
|
24
|
+
it('should return a background lightpink', () => {
|
|
25
|
+
expect(backgroundColor(false, true)).toEqual('lightpink');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('Visual widget', () => {
|
|
29
|
+
it('renders the VisualJSONWidget component without breaking if props and progressEditing are empty', () => {
|
|
30
|
+
const store = mockStore({
|
|
31
|
+
intl: {
|
|
32
|
+
locale: 'en',
|
|
33
|
+
messages: {},
|
|
34
|
+
},
|
|
35
|
+
progressEditing: {},
|
|
36
|
+
});
|
|
37
|
+
const component = renderer.create(
|
|
38
|
+
<Provider store={store}>
|
|
39
|
+
<MemoryRouter>
|
|
40
|
+
<VisualJSONWidget
|
|
41
|
+
pathname="/test"
|
|
42
|
+
{...propsEmpty}
|
|
43
|
+
hasToolbar={true}
|
|
44
|
+
/>
|
|
45
|
+
</MemoryRouter>
|
|
46
|
+
</Provider>,
|
|
47
|
+
);
|
|
48
|
+
const json = component.toJSON();
|
|
49
|
+
expect(json).toMatchSnapshot();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Segment, Dropdown, Accordion, Icon } from 'semantic-ui-react';
|
|
3
|
+
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
4
|
+
import { useSelector, useDispatch } from 'react-redux';
|
|
5
|
+
import { getRawContent } from './actions';
|
|
6
|
+
import { COMPONENT_HEIGHT } from './VisualJSONWidget';
|
|
7
|
+
|
|
8
|
+
import './less/editor.less';
|
|
9
|
+
|
|
10
|
+
export function makeFirstLetterCapital(string) {
|
|
11
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const EditDataComponent = ({
|
|
15
|
+
request,
|
|
16
|
+
handleOnDropdownChange,
|
|
17
|
+
currentContentType,
|
|
18
|
+
value,
|
|
19
|
+
fields,
|
|
20
|
+
getDropdownValues,
|
|
21
|
+
}) => {
|
|
22
|
+
const path = flattenToAppURL(
|
|
23
|
+
'/@vocabularies/plone.app.vocabularies.WorkflowStates',
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const dispatch = useDispatch();
|
|
27
|
+
const requestStateOptions = useSelector((state) => state.rawdata?.[path]);
|
|
28
|
+
const content = requestStateOptions?.data;
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (
|
|
32
|
+
path &&
|
|
33
|
+
!requestStateOptions?.loading &&
|
|
34
|
+
!requestStateOptions?.loaded &&
|
|
35
|
+
!content
|
|
36
|
+
)
|
|
37
|
+
dispatch(getRawContent(path));
|
|
38
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
39
|
+
}, [
|
|
40
|
+
dispatch,
|
|
41
|
+
path,
|
|
42
|
+
content,
|
|
43
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
44
|
+
requestStateOptions?.loaded,
|
|
45
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
46
|
+
requestStateOptions?.loading,
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
//Returns the saved values for dropdown with the first letter in uppercase
|
|
50
|
+
|
|
51
|
+
const getValues = (currentField) => {
|
|
52
|
+
if (
|
|
53
|
+
!request.loading &&
|
|
54
|
+
request.loaded &&
|
|
55
|
+
currentContentType &&
|
|
56
|
+
value[currentContentType.id]
|
|
57
|
+
)
|
|
58
|
+
return value[currentContentType.id].find(
|
|
59
|
+
(rule) => rule?.prefix === currentField,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return undefined;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const renderLabel = (label) => ({
|
|
66
|
+
color: 'blue',
|
|
67
|
+
content: `${label.text}`,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const createStateOption = (stateOptions) => {
|
|
71
|
+
return ['all', ...(stateOptions || [])].map((state) => ({
|
|
72
|
+
key: makeFirstLetterCapital(state),
|
|
73
|
+
text: makeFirstLetterCapital(state),
|
|
74
|
+
value: makeFirstLetterCapital(state),
|
|
75
|
+
}));
|
|
76
|
+
};
|
|
77
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
78
|
+
// const inputRef = useRef();
|
|
79
|
+
const [inputValue, setInputValue] = useState('');
|
|
80
|
+
const [conditionValue, setConditionValue] = useState('');
|
|
81
|
+
const [linkValue, setLinkValue] = useState();
|
|
82
|
+
const handleClick = (e, titleProps, currentField) => {
|
|
83
|
+
const { index } = titleProps;
|
|
84
|
+
const newIndex = activeIndex === index ? -1 : index;
|
|
85
|
+
|
|
86
|
+
setActiveIndex(newIndex);
|
|
87
|
+
setInputValue(getValues(currentField)?.linkLabel || '');
|
|
88
|
+
setConditionValue(getValues(currentField)?.condition || '');
|
|
89
|
+
setLinkValue(getValues(currentField)?.link || '');
|
|
90
|
+
};
|
|
91
|
+
const handleInputChange = (e, currentField) => {
|
|
92
|
+
setInputValue(e.target.value);
|
|
93
|
+
handleOnDropdownChange(
|
|
94
|
+
null,
|
|
95
|
+
{ value: getValues(currentField).states || [] },
|
|
96
|
+
currentField,
|
|
97
|
+
e.target.value,
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
const handleInputLinkChange = (e, currentField) => {
|
|
101
|
+
setLinkValue(e.target.value);
|
|
102
|
+
handleOnDropdownChange(
|
|
103
|
+
null,
|
|
104
|
+
{ value: getValues(currentField).states || [] },
|
|
105
|
+
currentField,
|
|
106
|
+
undefined,
|
|
107
|
+
undefined,
|
|
108
|
+
e.target.value,
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
const handleInputConditionChange = (e, currentField) => {
|
|
112
|
+
setConditionValue(e.target.value);
|
|
113
|
+
handleOnDropdownChange(
|
|
114
|
+
null,
|
|
115
|
+
{ value: getValues(currentField).states || [] },
|
|
116
|
+
currentField,
|
|
117
|
+
undefined,
|
|
118
|
+
e.target.value,
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
return (
|
|
122
|
+
<Segment
|
|
123
|
+
style={{
|
|
124
|
+
width: '100%',
|
|
125
|
+
paddingBottom:
|
|
126
|
+
request?.data?.fieldsets[0]?.fields.length > 9 ? '120px' : '',
|
|
127
|
+
height: COMPONENT_HEIGHT,
|
|
128
|
+
overflow: 'auto',
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
<Accordion styled fluid>
|
|
132
|
+
{request?.loaded &&
|
|
133
|
+
!request?.loading &&
|
|
134
|
+
requestStateOptions?.loaded &&
|
|
135
|
+
!requestStateOptions?.loading &&
|
|
136
|
+
requestStateOptions?.data &&
|
|
137
|
+
fields.map((currentField, index) => {
|
|
138
|
+
if (
|
|
139
|
+
request.data.required.includes(currentField) ||
|
|
140
|
+
getDropdownValues(currentField) === undefined
|
|
141
|
+
)
|
|
142
|
+
return null;
|
|
143
|
+
return (
|
|
144
|
+
<React.Fragment key={`${currentField}${index}`}>
|
|
145
|
+
<Accordion.Title
|
|
146
|
+
active={activeIndex === index}
|
|
147
|
+
index={index}
|
|
148
|
+
onClick={(e, titleProps) =>
|
|
149
|
+
handleClick(e, titleProps, currentField)
|
|
150
|
+
}
|
|
151
|
+
id={`property_${currentField}`}
|
|
152
|
+
>
|
|
153
|
+
<div className="title-editing-progress">
|
|
154
|
+
<Icon name="dropdown" size="tiny" />
|
|
155
|
+
|
|
156
|
+
{currentField}
|
|
157
|
+
</div>
|
|
158
|
+
<div className="title-editing-progress">
|
|
159
|
+
<Icon
|
|
160
|
+
name="cancel"
|
|
161
|
+
size="mini"
|
|
162
|
+
onClick={(e) => {
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
handleOnDropdownChange(
|
|
165
|
+
e,
|
|
166
|
+
{ value: undefined },
|
|
167
|
+
currentField,
|
|
168
|
+
);
|
|
169
|
+
}}
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
172
|
+
</Accordion.Title>
|
|
173
|
+
<Accordion.Content
|
|
174
|
+
active={activeIndex === index}
|
|
175
|
+
id={`property_content_${currentField}`}
|
|
176
|
+
>
|
|
177
|
+
<label
|
|
178
|
+
htmlFor="message"
|
|
179
|
+
style={{ display: 'block', padding: '10px' }}
|
|
180
|
+
>
|
|
181
|
+
Message
|
|
182
|
+
</label>
|
|
183
|
+
<input
|
|
184
|
+
className="message-input"
|
|
185
|
+
value={inputValue}
|
|
186
|
+
onChange={(e) => handleInputChange(e, currentField)}
|
|
187
|
+
// ref={inputRef}
|
|
188
|
+
name="message"
|
|
189
|
+
style={{ padding: '10px' }}
|
|
190
|
+
disabled={getDropdownValues(currentField) == null}
|
|
191
|
+
placeholder="Write a dfferent message after you set at lest one state"
|
|
192
|
+
/>
|
|
193
|
+
<label
|
|
194
|
+
htmlFor="message"
|
|
195
|
+
style={{ display: 'block', padding: '10px' }}
|
|
196
|
+
>
|
|
197
|
+
Link
|
|
198
|
+
</label>
|
|
199
|
+
<input
|
|
200
|
+
className="link-input"
|
|
201
|
+
value={linkValue}
|
|
202
|
+
onChange={(e) => handleInputLinkChange(e, currentField)}
|
|
203
|
+
// ref={inputRef}
|
|
204
|
+
name="link"
|
|
205
|
+
style={{ padding: '10px' }}
|
|
206
|
+
disabled={getDropdownValues(currentField) == null}
|
|
207
|
+
placeholder="Write a dfferent href link"
|
|
208
|
+
/>
|
|
209
|
+
<label
|
|
210
|
+
htmlFor="condition"
|
|
211
|
+
style={{ display: 'block', padding: '10px' }}
|
|
212
|
+
>
|
|
213
|
+
Condition
|
|
214
|
+
</label>
|
|
215
|
+
<input
|
|
216
|
+
className="condition-input"
|
|
217
|
+
value={conditionValue}
|
|
218
|
+
onChange={(e) =>
|
|
219
|
+
handleInputConditionChange(e, currentField)
|
|
220
|
+
}
|
|
221
|
+
// ref={inputRef}
|
|
222
|
+
name="condition"
|
|
223
|
+
style={{ padding: '10px' }}
|
|
224
|
+
disabled={getDropdownValues(currentField) == null}
|
|
225
|
+
placeholder="Write a dfferent condition"
|
|
226
|
+
/>
|
|
227
|
+
<label
|
|
228
|
+
htmlFor="dropdown"
|
|
229
|
+
style={{ display: 'block', padding: '10px' }}
|
|
230
|
+
>
|
|
231
|
+
States
|
|
232
|
+
</label>
|
|
233
|
+
<Dropdown
|
|
234
|
+
placeholder="Fields"
|
|
235
|
+
multiple
|
|
236
|
+
floating
|
|
237
|
+
selection
|
|
238
|
+
search
|
|
239
|
+
name="dropdown"
|
|
240
|
+
defaultValue={getDropdownValues(currentField)}
|
|
241
|
+
options={[
|
|
242
|
+
...createStateOption(
|
|
243
|
+
requestStateOptions.data.items.map(
|
|
244
|
+
(option) => option.token,
|
|
245
|
+
),
|
|
246
|
+
),
|
|
247
|
+
]}
|
|
248
|
+
onChange={(e, data) =>
|
|
249
|
+
handleOnDropdownChange(e, data, currentField)
|
|
250
|
+
}
|
|
251
|
+
renderLabel={renderLabel}
|
|
252
|
+
/>
|
|
253
|
+
</Accordion.Content>
|
|
254
|
+
</React.Fragment>
|
|
255
|
+
);
|
|
256
|
+
})}
|
|
257
|
+
</Accordion>
|
|
258
|
+
</Segment>
|
|
259
|
+
);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
export default EditDataComponent;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Sidebar, List } from 'semantic-ui-react';
|
|
3
|
+
import { SIDEBAR_WIDTH } from './VisualJSONWidget';
|
|
4
|
+
|
|
5
|
+
export const backgroundColor = (currentContentType, modified) => {
|
|
6
|
+
let color = undefined;
|
|
7
|
+
if (modified) {
|
|
8
|
+
color = 'lightpink';
|
|
9
|
+
}
|
|
10
|
+
if (currentContentType) {
|
|
11
|
+
color = 'lightblue';
|
|
12
|
+
}
|
|
13
|
+
return color;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const SidebarComponent = (props) => {
|
|
17
|
+
const { types, currentContentType, handleChangeSelectedContentType } = props;
|
|
18
|
+
// console.log(types);
|
|
19
|
+
const [filtredTypes, setFiltredTypes] = useState({ ...types });
|
|
20
|
+
const [inputValue, setInputValue] = useState('');
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
setFiltredTypes({ ...types });
|
|
24
|
+
}, [types]);
|
|
25
|
+
|
|
26
|
+
const handleInputChange = (e) => {
|
|
27
|
+
if (e.target.value == null) return;
|
|
28
|
+
setInputValue(e.target.value);
|
|
29
|
+
if (
|
|
30
|
+
types.loaded &&
|
|
31
|
+
!types.loading &&
|
|
32
|
+
Array.isArray(types.types) &&
|
|
33
|
+
types.types.length > 0
|
|
34
|
+
) {
|
|
35
|
+
setFiltredTypes({
|
|
36
|
+
...types,
|
|
37
|
+
types: types.types.filter((type) =>
|
|
38
|
+
type.title.toLowerCase().includes(e.target.value.toLowerCase()),
|
|
39
|
+
),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Sidebar
|
|
46
|
+
as={List}
|
|
47
|
+
animation="push"
|
|
48
|
+
icon="labeled"
|
|
49
|
+
visible
|
|
50
|
+
relaxed
|
|
51
|
+
size="big"
|
|
52
|
+
divided
|
|
53
|
+
selection
|
|
54
|
+
style={{ width: SIDEBAR_WIDTH }}
|
|
55
|
+
>
|
|
56
|
+
<input
|
|
57
|
+
value={inputValue}
|
|
58
|
+
onChange={handleInputChange}
|
|
59
|
+
placeholder="Search... "
|
|
60
|
+
style={{ paddingLeft: ' 10px' }}
|
|
61
|
+
/>
|
|
62
|
+
{filtredTypes.loaded &&
|
|
63
|
+
!filtredTypes.loading &&
|
|
64
|
+
Array.isArray(filtredTypes.types) &&
|
|
65
|
+
filtredTypes.types.map((type) => (
|
|
66
|
+
<List.Item
|
|
67
|
+
style={{
|
|
68
|
+
padding: '25px 5px',
|
|
69
|
+
textAlign: 'center',
|
|
70
|
+
backgroundColor: backgroundColor(
|
|
71
|
+
currentContentType?.id === type.id,
|
|
72
|
+
Object.keys(props.value).includes(type.id),
|
|
73
|
+
),
|
|
74
|
+
}}
|
|
75
|
+
key={type.id}
|
|
76
|
+
id={`sidebar_${type.id}`}
|
|
77
|
+
onClick={(e) => handleChangeSelectedContentType(e, type)}
|
|
78
|
+
>
|
|
79
|
+
<List.Content>
|
|
80
|
+
<List.Header>{type.title}</List.Header>
|
|
81
|
+
</List.Content>
|
|
82
|
+
</List.Item>
|
|
83
|
+
))}
|
|
84
|
+
</Sidebar>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export default SidebarComponent;
|
package/src/actionTypes/index.js
CHANGED