@plone/volto 14.0.0 → 14.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/locales/ca/LC_MESSAGES/volto.po +12 -2
  3. package/locales/ca.json +1 -1
  4. package/locales/de/LC_MESSAGES/volto.po +12 -2
  5. package/locales/de.json +1 -1
  6. package/locales/en/LC_MESSAGES/volto.po +12 -2
  7. package/locales/en.json +1 -1
  8. package/locales/es/LC_MESSAGES/volto.po +12 -2
  9. package/locales/es.json +1 -1
  10. package/locales/eu/LC_MESSAGES/volto.po +12 -2
  11. package/locales/eu.json +1 -1
  12. package/locales/fr/LC_MESSAGES/volto.po +12 -2
  13. package/locales/fr.json +1 -1
  14. package/locales/it/LC_MESSAGES/volto.po +12 -2
  15. package/locales/it.json +1 -1
  16. package/locales/ja/LC_MESSAGES/volto.po +12 -2
  17. package/locales/ja.json +1 -1
  18. package/locales/nl/LC_MESSAGES/volto.po +12 -2
  19. package/locales/nl.json +1 -1
  20. package/locales/pt/LC_MESSAGES/volto.po +12 -2
  21. package/locales/pt.json +1 -1
  22. package/locales/pt_BR/LC_MESSAGES/volto.po +12 -2
  23. package/locales/pt_BR.json +1 -1
  24. package/locales/ro/LC_MESSAGES/volto.po +12 -2
  25. package/locales/ro.json +1 -1
  26. package/locales/volto.pot +12 -2
  27. package/package.json +2 -1
  28. package/public/icon.svg +13 -0
  29. package/src/actions/vocabularies/vocabularies.js +15 -3
  30. package/src/components/index.js +1 -0
  31. package/src/components/manage/Blocks/HeroImageLeft/Edit.jsx +1 -1
  32. package/src/components/manage/Blocks/Listing/getAsyncData.js +1 -1
  33. package/src/components/manage/Blocks/Text/Edit.jsx +19 -0
  34. package/src/components/manage/Form/Form.jsx +11 -1
  35. package/src/components/manage/Form/UndoToolbar.jsx +78 -0
  36. package/src/components/manage/Widgets/AlignWidget.stories.jsx +5 -21
  37. package/src/components/manage/Widgets/ArrayWidget.jsx +83 -101
  38. package/src/components/manage/Widgets/ArrayWidget.stories.jsx +29 -64
  39. package/src/components/manage/Widgets/CheckboxWidget.stories.jsx +5 -22
  40. package/src/components/manage/Widgets/DatetimeWidget.jsx +65 -74
  41. package/src/components/manage/Widgets/DatetimeWidget.stories.jsx +7 -23
  42. package/src/components/manage/Widgets/DatetimeWidget.test.jsx +17 -15
  43. package/src/components/manage/Widgets/EmailWidget.stories.jsx +5 -22
  44. package/src/components/manage/Widgets/FileWidget.stories.jsx +5 -22
  45. package/src/components/manage/Widgets/NumberWidget.stories.jsx +5 -23
  46. package/src/components/manage/Widgets/ObjectBrowserWidget.stories.js +24 -32
  47. package/src/components/manage/Widgets/ObjectListWidget.stories.js +44 -44
  48. package/src/components/manage/Widgets/ObjectWidget.stories.jsx +13 -28
  49. package/src/components/manage/Widgets/PasswordWidget.stories.jsx +5 -22
  50. package/src/components/manage/Widgets/QueryWidget.jsx +2 -2
  51. package/src/components/manage/Widgets/QueryWidget.stories.jsx +1637 -22
  52. package/src/components/manage/Widgets/SelectAutoComplete.jsx +79 -48
  53. package/src/components/manage/Widgets/SelectAutoComplete.test.jsx +16 -0
  54. package/src/components/manage/Widgets/SelectAutocompleteWidget.stories.jsx +161 -0
  55. package/src/components/manage/Widgets/SelectUtils.js +90 -30
  56. package/src/components/manage/Widgets/SelectUtils.test.jsx +76 -1
  57. package/src/components/manage/Widgets/SelectWidget.jsx +26 -37
  58. package/src/components/manage/Widgets/SelectWidget.stories.jsx +96 -28
  59. package/src/components/manage/Widgets/TextWidget.stories.jsx +5 -22
  60. package/src/components/manage/Widgets/TextareaWidget.stories.jsx +5 -22
  61. package/src/components/manage/Widgets/TokenWidget.jsx +19 -17
  62. package/src/components/manage/Widgets/TokenWidget.stories.jsx +141 -0
  63. package/src/components/manage/Widgets/UrlWidget.stories.jsx +5 -21
  64. package/src/components/manage/Widgets/VocabularyTermsWidget.stories.js +27 -64
  65. package/src/components/manage/Widgets/WysiwygWidget.stories.jsx +5 -22
  66. package/src/components/manage/Widgets/story.jsx +38 -0
  67. package/src/components/theme/ContactForm/ContactForm.jsx +1 -0
  68. package/src/components/theme/ContactForm/ContactForm.stories.jsx +126 -0
  69. package/src/components/theme/CorsError/CorsError.jsx +2 -2
  70. package/src/config/Loadables.jsx +2 -0
  71. package/src/config/index.js +1 -0
  72. package/src/helpers/Html/Html.jsx +2 -12
  73. package/src/helpers/UndoManager/useUndoManager.js +102 -0
  74. package/src/helpers/index.js +1 -0
  75. package/src/middleware/Api.test.js +57 -6
  76. package/src/middleware/api.js +34 -13
  77. package/src/reducers/vocabularies/vocabularies.js +13 -2
  78. package/src/store.js +1 -1
  79. package/src/storybook.jsx +55 -0
  80. package/theme/themes/pastanaga/extras/time-picker-overrides.less +1 -1
  81. package/include/python3.8/Python-ast.h +0 -715
  82. package/include/python3.8/Python.h +0 -160
  83. package/include/python3.8/abstract.h +0 -844
  84. package/include/python3.8/asdl.h +0 -46
  85. package/include/python3.8/ast.h +0 -37
  86. package/include/python3.8/bitset.h +0 -23
  87. package/include/python3.8/bltinmodule.h +0 -14
  88. package/include/python3.8/boolobject.h +0 -34
  89. package/include/python3.8/bytearrayobject.h +0 -62
  90. package/include/python3.8/bytes_methods.h +0 -69
  91. package/include/python3.8/bytesobject.h +0 -224
  92. package/include/python3.8/cellobject.h +0 -29
  93. package/include/python3.8/ceval.h +0 -231
  94. package/include/python3.8/classobject.h +0 -59
  95. package/include/python3.8/code.h +0 -180
  96. package/include/python3.8/codecs.h +0 -240
  97. package/include/python3.8/compile.h +0 -106
  98. package/include/python3.8/complexobject.h +0 -69
  99. package/include/python3.8/context.h +0 -84
  100. package/include/python3.8/cpython/abstract.h +0 -319
  101. package/include/python3.8/cpython/dictobject.h +0 -94
  102. package/include/python3.8/cpython/fileobject.h +0 -24
  103. package/include/python3.8/cpython/initconfig.h +0 -434
  104. package/include/python3.8/cpython/interpreteridobject.h +0 -19
  105. package/include/python3.8/cpython/object.h +0 -470
  106. package/include/python3.8/cpython/objimpl.h +0 -113
  107. package/include/python3.8/cpython/pyerrors.h +0 -188
  108. package/include/python3.8/cpython/pylifecycle.h +0 -78
  109. package/include/python3.8/cpython/pymem.h +0 -108
  110. package/include/python3.8/cpython/pystate.h +0 -252
  111. package/include/python3.8/cpython/sysmodule.h +0 -21
  112. package/include/python3.8/cpython/traceback.h +0 -22
  113. package/include/python3.8/cpython/tupleobject.h +0 -36
  114. package/include/python3.8/cpython/unicodeobject.h +0 -1239
  115. package/include/python3.8/datetime.h +0 -259
  116. package/include/python3.8/descrobject.h +0 -108
  117. package/include/python3.8/dictobject.h +0 -94
  118. package/include/python3.8/dtoa.h +0 -19
  119. package/include/python3.8/dynamic_annotations.h +0 -499
  120. package/include/python3.8/enumobject.h +0 -17
  121. package/include/python3.8/errcode.h +0 -38
  122. package/include/python3.8/eval.h +0 -37
  123. package/include/python3.8/fileobject.h +0 -49
  124. package/include/python3.8/fileutils.h +0 -185
  125. package/include/python3.8/floatobject.h +0 -130
  126. package/include/python3.8/frameobject.h +0 -92
  127. package/include/python3.8/funcobject.h +0 -104
  128. package/include/python3.8/genobject.h +0 -109
  129. package/include/python3.8/graminit.h +0 -94
  130. package/include/python3.8/grammar.h +0 -77
  131. package/include/python3.8/import.h +0 -149
  132. package/include/python3.8/internal/pycore_accu.h +0 -39
  133. package/include/python3.8/internal/pycore_atomic.h +0 -558
  134. package/include/python3.8/internal/pycore_ceval.h +0 -37
  135. package/include/python3.8/internal/pycore_code.h +0 -27
  136. package/include/python3.8/internal/pycore_condvar.h +0 -95
  137. package/include/python3.8/internal/pycore_context.h +0 -42
  138. package/include/python3.8/internal/pycore_fileutils.h +0 -54
  139. package/include/python3.8/internal/pycore_getopt.h +0 -22
  140. package/include/python3.8/internal/pycore_gil.h +0 -50
  141. package/include/python3.8/internal/pycore_hamt.h +0 -116
  142. package/include/python3.8/internal/pycore_initconfig.h +0 -166
  143. package/include/python3.8/internal/pycore_object.h +0 -81
  144. package/include/python3.8/internal/pycore_pathconfig.h +0 -75
  145. package/include/python3.8/internal/pycore_pyerrors.h +0 -62
  146. package/include/python3.8/internal/pycore_pyhash.h +0 -10
  147. package/include/python3.8/internal/pycore_pylifecycle.h +0 -118
  148. package/include/python3.8/internal/pycore_pymem.h +0 -212
  149. package/include/python3.8/internal/pycore_pystate.h +0 -326
  150. package/include/python3.8/internal/pycore_traceback.h +0 -96
  151. package/include/python3.8/internal/pycore_tupleobject.h +0 -19
  152. package/include/python3.8/internal/pycore_warnings.h +0 -25
  153. package/include/python3.8/interpreteridobject.h +0 -17
  154. package/include/python3.8/intrcheck.h +0 -33
  155. package/include/python3.8/iterobject.h +0 -25
  156. package/include/python3.8/listobject.h +0 -81
  157. package/include/python3.8/longintrepr.h +0 -99
  158. package/include/python3.8/longobject.h +0 -242
  159. package/include/python3.8/marshal.h +0 -28
  160. package/include/python3.8/memoryobject.h +0 -72
  161. package/include/python3.8/methodobject.h +0 -131
  162. package/include/python3.8/modsupport.h +0 -248
  163. package/include/python3.8/moduleobject.h +0 -90
  164. package/include/python3.8/namespaceobject.h +0 -19
  165. package/include/python3.8/node.h +0 -48
  166. package/include/python3.8/object.h +0 -753
  167. package/include/python3.8/objimpl.h +0 -284
  168. package/include/python3.8/odictobject.h +0 -43
  169. package/include/python3.8/opcode.h +0 -148
  170. package/include/python3.8/osdefs.h +0 -51
  171. package/include/python3.8/osmodule.h +0 -17
  172. package/include/python3.8/parsetok.h +0 -110
  173. package/include/python3.8/patchlevel.h +0 -35
  174. package/include/python3.8/picklebufobject.h +0 -31
  175. package/include/python3.8/py_curses.h +0 -100
  176. package/include/python3.8/pyarena.h +0 -64
  177. package/include/python3.8/pycapsule.h +0 -59
  178. package/include/python3.8/pyconfig.h +0 -1665
  179. package/include/python3.8/pyctype.h +0 -39
  180. package/include/python3.8/pydebug.h +0 -40
  181. package/include/python3.8/pydtrace.h +0 -59
  182. package/include/python3.8/pydtrace_probes.h +0 -228
  183. package/include/python3.8/pyerrors.h +0 -335
  184. package/include/python3.8/pyexpat.h +0 -55
  185. package/include/python3.8/pyfpe.h +0 -12
  186. package/include/python3.8/pyhash.h +0 -145
  187. package/include/python3.8/pylifecycle.h +0 -75
  188. package/include/python3.8/pymacconfig.h +0 -102
  189. package/include/python3.8/pymacro.h +0 -106
  190. package/include/python3.8/pymath.h +0 -230
  191. package/include/python3.8/pymem.h +0 -150
  192. package/include/python3.8/pyport.h +0 -850
  193. package/include/python3.8/pystate.h +0 -136
  194. package/include/python3.8/pystrcmp.h +0 -23
  195. package/include/python3.8/pystrhex.h +0 -22
  196. package/include/python3.8/pystrtod.h +0 -45
  197. package/include/python3.8/pythonrun.h +0 -210
  198. package/include/python3.8/pythread.h +0 -161
  199. package/include/python3.8/pytime.h +0 -246
  200. package/include/python3.8/rangeobject.h +0 -27
  201. package/include/python3.8/setobject.h +0 -108
  202. package/include/python3.8/sliceobject.h +0 -65
  203. package/include/python3.8/structmember.h +0 -74
  204. package/include/python3.8/structseq.h +0 -49
  205. package/include/python3.8/symtable.h +0 -123
  206. package/include/python3.8/sysmodule.h +0 -41
  207. package/include/python3.8/token.h +0 -92
  208. package/include/python3.8/traceback.h +0 -28
  209. package/include/python3.8/tracemalloc.h +0 -38
  210. package/include/python3.8/tupleobject.h +0 -48
  211. package/include/python3.8/typeslots.h +0 -85
  212. package/include/python3.8/ucnhash.h +0 -36
  213. package/include/python3.8/unicodeobject.h +0 -1044
  214. package/include/python3.8/warnings.h +0 -67
  215. package/include/python3.8/weakrefobject.h +0 -86
  216. package/src/components/theme/ContactForm/ContactForm.stories.mdx +0 -39
@@ -136,6 +136,25 @@ class Edit extends Component {
136
136
  }
137
137
  }
138
138
 
139
+ componentDidUpdate(prevProps) {
140
+ if (
141
+ !isEqual(this.props.data, prevProps.data) &&
142
+ !isEqual(
143
+ convertToRaw(this.state.editorState.getCurrentContent()),
144
+ this.props.data.text,
145
+ )
146
+ ) {
147
+ const editorState =
148
+ this.props.data && this.props.data.text
149
+ ? EditorState.createWithContent(convertFromRaw(this.props.data.text))
150
+ : EditorState.createEmpty();
151
+
152
+ this.setState({
153
+ editorState: editorState,
154
+ });
155
+ }
156
+ }
157
+
139
158
  /**
140
159
  * @param {*} nextProps
141
160
  * @param {*} nextState
@@ -39,7 +39,7 @@ import {
39
39
  } from 'semantic-ui-react';
40
40
  import { v4 as uuid } from 'uuid';
41
41
  import { toast } from 'react-toastify';
42
- import { BlocksToolbar } from '@plone/volto/components';
42
+ import { BlocksToolbar, UndoToolbar } from '@plone/volto/components';
43
43
  import { setSidebarTab } from '@plone/volto/actions';
44
44
  import { compose } from 'redux';
45
45
  import config from '@plone/volto/registry';
@@ -221,6 +221,7 @@ class Form extends Component {
221
221
 
222
222
  if (this.props.onChangeFormData) {
223
223
  if (
224
+ // TODO: use fast-deep-equal
224
225
  JSON.stringify(prevState?.formData) !==
225
226
  JSON.stringify(this.state.formData)
226
227
  ) {
@@ -526,6 +527,15 @@ class Form extends Component {
526
527
  }
527
528
  onSelectBlock={this.onSelectBlock}
528
529
  />
530
+ <UndoToolbar
531
+ state={{
532
+ formData: this.state.formData,
533
+ selected: this.state.selected,
534
+ multiSelected: this.state.multiSelected,
535
+ }}
536
+ enableHotKeys
537
+ onUndoRedo={({ state }) => this.setState(state)}
538
+ />
529
539
  <BlocksForm
530
540
  onChangeFormData={(newFormData) =>
531
541
  this.setState({
@@ -0,0 +1,78 @@
1
+ import React from 'react';
2
+ import { defineMessages, useIntl } from 'react-intl';
3
+ import { Plug } from '@plone/volto/components/manage/Pluggable';
4
+ import { Icon } from '@plone/volto/components';
5
+ import { Button } from 'semantic-ui-react';
6
+ import { useUndoManager } from '@plone/volto/helpers';
7
+ import config from '@plone/volto/registry';
8
+
9
+ import undoSVG from '@plone/volto/icons/undo.svg';
10
+ import redoSVG from '@plone/volto/icons/redo.svg';
11
+
12
+ const messages = defineMessages({
13
+ undo: {
14
+ id: 'Undo',
15
+ defaultMessage: 'Undo',
16
+ },
17
+ redo: {
18
+ id: 'Redo',
19
+ defaultMessage: 'Redo',
20
+ },
21
+ });
22
+
23
+ const UndoToolbar = ({ state, onUndoRedo, maxUndoLevels, enableHotKeys }) => {
24
+ const intl = useIntl();
25
+ const undoLevels = maxUndoLevels ?? config.settings.maxUndoLevels;
26
+ const { doUndo, doRedo, canUndo, canRedo } = useUndoManager(
27
+ state,
28
+ onUndoRedo,
29
+ {
30
+ maxUndoLevels: undoLevels,
31
+ },
32
+ );
33
+
34
+ return (
35
+ <>
36
+ <Plug
37
+ pluggable="main.toolbar.bottom"
38
+ id="undo-btn"
39
+ dependencies={[canUndo, canRedo]}
40
+ >
41
+ <Button
42
+ className="undo"
43
+ onClick={() => doUndo()}
44
+ aria-label={intl.formatMessage(messages.undo)}
45
+ disabled={!canUndo}
46
+ >
47
+ <Icon
48
+ name={undoSVG}
49
+ className="circled"
50
+ size="30px"
51
+ title={intl.formatMessage(messages.undo)}
52
+ />
53
+ </Button>
54
+ </Plug>
55
+ <Plug
56
+ pluggable="main.toolbar.bottom"
57
+ id="redo-btn"
58
+ dependencies={[canUndo, canRedo]}
59
+ >
60
+ <Button
61
+ className="redo"
62
+ onClick={() => doRedo()}
63
+ aria-label={intl.formatMessage(messages.redo)}
64
+ disabled={!canRedo}
65
+ >
66
+ <Icon
67
+ name={redoSVG}
68
+ className="circled"
69
+ size="30px"
70
+ title={intl.formatMessage(messages.redo)}
71
+ />
72
+ </Button>
73
+ </Plug>
74
+ </>
75
+ );
76
+ };
77
+
78
+ export default UndoToolbar;
@@ -1,27 +1,11 @@
1
1
  import React from 'react';
2
2
  import AlignWidget from './AlignWidget';
3
- import Wrapper from '@plone/volto/storybook';
3
+ import WidgetStory from './story';
4
4
 
5
- const AlignWidgetComponent = ({ children, ...args }) => {
6
- const [value, setValue] = React.useState([]);
7
- const onChange = (block, value) => setValue(value);
8
- return (
9
- <Wrapper location={{ pathname: '/folder2/folder21/doc212' }}>
10
- <div className="ui segment form attached" style={{ width: '400px' }}>
11
- <AlignWidget
12
- {...args}
13
- id="alignWidgetItem"
14
- title="Align"
15
- block="testBlock"
16
- value={value}
17
- onChange={onChange}
18
- />
19
- </div>
20
- </Wrapper>
21
- );
22
- };
23
-
24
- export const Align = AlignWidgetComponent.bind({});
5
+ export const Align = WidgetStory.bind({
6
+ props: { id: 'align', title: 'Align' },
7
+ widget: AlignWidget,
8
+ });
25
9
 
26
10
  export default {
27
11
  title: 'Widgets/Align',
@@ -9,7 +9,7 @@ import PropTypes from 'prop-types';
9
9
  import { compose } from 'redux';
10
10
  import { connect } from 'react-redux';
11
11
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
12
- import { find, isObject, isArray } from 'lodash';
12
+ import { find, isObject } from 'lodash';
13
13
 
14
14
  import {
15
15
  getVocabFromHint,
@@ -56,10 +56,79 @@ function arrayMove(array, from, to) {
56
56
  return slicedArray;
57
57
  }
58
58
 
59
+ function normalizeArrayValue(choices, value) {
60
+ if (!value || !Array.isArray(value)) return [];
61
+ if (value.length === 0) return value;
62
+
63
+ if (typeof value[0] === 'string') {
64
+ // raw value like ['foo', 'bar']
65
+ return value.map((v) => {
66
+ return {
67
+ label: find(choices, (c) => c.value === v)?.label || v,
68
+ value: v,
69
+ };
70
+ });
71
+ }
72
+
73
+ if (
74
+ isObject(value[0]) &&
75
+ Object.keys(value[0]).includes('token') // Array of objects, w/ label+value
76
+ ) {
77
+ return value
78
+ .map((v) => {
79
+ const item = find(choices, (c) => c.value === v.token);
80
+ return item
81
+ ? {
82
+ label: item.label || item.title || item.token,
83
+ value: v.token,
84
+ }
85
+ : {
86
+ // avoid a crash if choices doesn't include this item
87
+ label: v.label,
88
+ value: v.token,
89
+ };
90
+ })
91
+ .filter((f) => !!f);
92
+ }
93
+
94
+ return [];
95
+ }
96
+
97
+ function normalizeChoices(choices) {
98
+ if (Array.isArray(choices) && choices.length && Array.isArray(choices[0])) {
99
+ return choices.map((option) => ({
100
+ value: option[0],
101
+ label:
102
+ // Fix "None" on the serializer, to remove when fixed in p.restapi
103
+ option[1] !== 'None' && option[1] ? option[1] : option[0],
104
+ }));
105
+ }
106
+
107
+ return choices;
108
+ }
109
+
59
110
  /**
60
111
  * ArrayWidget component class.
61
112
  * @class ArrayWidget
62
113
  * @extends Component
114
+ *
115
+ * A createable select array widget will be rendered if the named vocabulary is
116
+ * in the widget definition (hint) like:
117
+ *
118
+ * ```
119
+ * list_field_voc_unconstrained = schema.List(
120
+ * title=u"List field with values from vocabulary but not constrained to them.",
121
+ * description=u"zope.schema.List",
122
+ * value_type=schema.TextLine(),
123
+ * required=False,
124
+ * missing_value=[],
125
+ * )
126
+ * directives.widget(
127
+ * "list_field_voc_unconstrained",
128
+ * AjaxSelectFieldWidget,
129
+ * vocabulary="plone.app.vocabularies.PortalTypes",
130
+ * )
131
+ * ```
63
132
  */
64
133
  class ArrayWidget extends Component {
65
134
  /**
@@ -119,18 +188,6 @@ class ArrayWidget extends Component {
119
188
  super(props);
120
189
 
121
190
  this.handleChange = this.handleChange.bind(this);
122
-
123
- this.state = {
124
- selectedOption: this.props.vocabBaseUrl
125
- ? []
126
- : props.value
127
- ? props.value.map((item) =>
128
- isObject(item)
129
- ? { label: item.title || item.token, value: item.token }
130
- : { label: item, value: item },
131
- )
132
- : [],
133
- };
134
191
  }
135
192
 
136
193
  /**
@@ -150,64 +207,8 @@ class ArrayWidget extends Component {
150
207
  subrequest: this.props.intl.locale,
151
208
  });
152
209
  }
153
- this.setDefaultValues();
154
- }
155
-
156
- componentDidUpdate() {
157
- this.setDefaultValues();
158
210
  }
159
211
 
160
- normalizeArrayValue = (choices, value) => {
161
- // Array of tokens (on add, and on change tab in Tab component)
162
- if (
163
- value &&
164
- isArray(value) &&
165
- value.length > 0 &&
166
- typeof value[0] === 'string'
167
- ) {
168
- return value.map((v) => {
169
- return {
170
- label: find(choices, (c) => c.value === v)?.label || v,
171
- value: v,
172
- };
173
- });
174
- }
175
- // Array of objects, containing label,value
176
- if (
177
- value &&
178
- isArray(value) &&
179
- value.length > 0 &&
180
- isObject(value[0]) &&
181
- Object.keys(value[0]).includes('token')
182
- ) {
183
- return value.map((v) => {
184
- return {
185
- label: find(choices, (c) => c.value === v.token).label,
186
- value: v.token,
187
- };
188
- });
189
- }
190
- return null;
191
- };
192
-
193
- setDefaultValues = () => {
194
- if (
195
- (this.state.selectedOption || []).length === 0 &&
196
- this.props.value &&
197
- this.props.choices?.length > 0
198
- ) {
199
- const normalizedValue = this.normalizeArrayValue(
200
- this.props.choices,
201
- this.props.value,
202
- );
203
- if (normalizedValue !== null) {
204
- this.setState({
205
- selectedOption: normalizedValue,
206
- });
207
- }
208
- }
209
- };
210
-
211
212
  /**
212
213
  * Handle the field change, store it in the local state and back to simple
213
214
  * array of tokens for correct serialization
@@ -216,75 +217,56 @@ class ArrayWidget extends Component {
216
217
  * @returns {undefined}
217
218
  */
218
219
  handleChange(selectedOption) {
219
- this.setState({ selectedOption });
220
-
221
220
  this.props.onChange(
222
221
  this.props.id,
223
222
  selectedOption ? selectedOption.map((item) => item.value) : null,
224
223
  );
225
224
  }
226
225
 
226
+ onSortEnd = (selectedOption, { oldIndex, newIndex }) => {
227
+ const newValue = arrayMove(selectedOption, oldIndex, newIndex);
228
+
229
+ this.handleChange(newValue);
230
+ };
231
+
227
232
  /**
228
233
  * Render method.
229
234
  * @method render
230
235
  * @returns {string} Markup for the component.
231
236
  */
232
237
  render() {
233
- const { selectedOption } = this.state;
238
+ const choices = normalizeChoices(this.props?.choices || []);
239
+ const selectedOption = normalizeArrayValue(choices, this.props.value);
240
+
234
241
  const CreatableSelect = this.props.reactSelectCreateable.default;
235
242
  const { SortableContainer } = this.props.reactSortableHOC;
236
243
  const Select = this.props.reactSelect.default;
237
244
  const SortableSelect =
238
- // It will be only createable if the named vocabulary is in the widget definition
239
- // (hint) like:
240
- // list_field_voc_unconstrained = schema.List(
241
- // title=u"List field with values from vocabulary but not constrained to them.",
242
- // description=u"zope.schema.List",
243
- // value_type=schema.TextLine(),
244
- // required=False,
245
- // missing_value=[],
246
- // )
247
- // directives.widget(
248
- // "list_field_voc_unconstrained",
249
- // AjaxSelectFieldWidget,
250
- // vocabulary="plone.app.vocabularies.PortalTypes",
251
- // )
252
245
  this.props?.choices && !getVocabFromHint(this.props)
253
246
  ? SortableContainer(Select)
254
247
  : SortableContainer(CreatableSelect);
255
248
 
256
- const onSortEnd = ({ oldIndex, newIndex }) => {
257
- const newValue = arrayMove(this.state.selectedOption, oldIndex, newIndex);
258
-
259
- this.setState({ selectedOption: newValue });
260
- };
261
-
262
249
  return (
263
250
  <FormFieldWrapper {...this.props}>
264
251
  <SortableSelect
265
252
  useDragHandle
266
253
  // react-sortable-hoc props:
267
254
  axis="xy"
268
- onSortEnd={onSortEnd}
255
+ onSortEnd={this.onSortEnd}
269
256
  distance={4}
270
257
  // small fix for https://github.com/clauderic/react-sortable-hoc/pull/352:
271
258
  getHelperDimensions={({ node }) => node.getBoundingClientRect()}
272
259
  id={`field-${this.props.id}`}
273
260
  key={this.props.id}
274
- isDisabled={this.props.isDisabled}
261
+ isDisabled={this.props.disabled || this.props.isDisabled}
275
262
  className="react-select-container"
276
263
  classNamePrefix="react-select"
277
264
  options={
278
265
  this.props.vocabBaseUrl
279
- ? this.props.choices
266
+ ? choices
280
267
  : this.props.choices
281
268
  ? [
282
- ...this.props.choices.map((option) => ({
283
- value: option[0],
284
- label:
285
- // Fix "None" on the serializer, to remove when fixed in p.restapi
286
- option[1] !== 'None' && option[1] ? option[1] : option[0],
287
- })),
269
+ ...choices,
288
270
  ...(this.props.noValueOption && !this.props.default
289
271
  ? [
290
272
  {
@@ -1,70 +1,47 @@
1
1
  import React from 'react';
2
- import { ArrayWidgetComponent } from './ArrayWidget';
3
- import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
4
- import { RealStoreWrapper as Wrapper } from '@plone/volto/storybook';
2
+ import ArrayWidget, { ArrayWidgetComponent } from './ArrayWidget';
3
+ import WidgetStory from './story';
5
4
 
6
- const ArrayComponent = injectLazyLibs([
7
- 'reactSelectCreateable',
8
- 'reactSortableHOC',
9
- ])(ArrayWidgetComponent);
5
+ const choices = [
6
+ ['foo', 'Foo'],
7
+ ['bar', 'Bar'],
8
+ ['fooBar', 'FooBar'],
9
+ ];
10
10
 
11
- const Array = (args) => {
12
- const [value, setValue] = React.useState(args.value ?? '');
13
- const onChange = (block, value) => {
14
- // args.onChange({ value });
15
- setValue(value);
16
- };
17
-
18
- return (
19
- <Wrapper>
20
- <ArrayComponent {...args} onChange={onChange} value={value} />
21
- </Wrapper>
22
- );
23
- };
24
-
25
- export const Default = Array.bind({});
11
+ export const Default = WidgetStory.bind({ widget: ArrayWidget });
26
12
  Default.args = {
27
13
  id: 'field-empty',
28
14
  title: 'field 1 title',
29
15
  description: 'Optional help text',
30
16
  placeholder: 'Type something…',
31
- choices: [
32
- ['Foo', 'Foo'],
33
- ['Bar', 'Bar'],
34
- ['FooBar', 'FooBar'],
35
- ],
17
+ choices,
36
18
  };
37
19
 
38
- export const Required = Array.bind({});
20
+ export const Required = WidgetStory.bind({ widget: ArrayWidget });
39
21
  Required.args = {
40
22
  id: 'field-empty',
41
23
  title: 'field 1 title',
42
24
  description: 'Optional help text',
43
25
  placeholder: 'Type something…',
44
- choices: [
45
- ['Foo', 'Foo'],
46
- ['Bar', 'Bar'],
47
- ['FooBar', 'FooBar'],
48
- ],
26
+ choices,
49
27
  required: true,
50
28
  };
51
29
 
52
- export const Filled = Array.bind({});
30
+ export const Filled = WidgetStory.bind({
31
+ widget: ArrayWidget,
32
+ props: { value: ['foo'] },
33
+ });
53
34
  Filled.args = {
54
35
  id: 'field-filled',
55
36
  title: 'Filled field title',
56
37
  description: 'Optional help text',
57
- choices: [
58
- ['Foo', 'Foo'],
59
- ['Bar', 'Bar'],
60
- ['FooBar', 'FooBar'],
61
- ],
62
- value: ['Foo'],
38
+ choices,
39
+ value: ['foo'],
63
40
  placeholder: 'Type something…',
64
41
  required: true,
65
42
  };
66
43
 
67
- export const Errored = Array.bind({});
44
+ export const Errored = WidgetStory.bind({ widget: ArrayWidget });
68
45
  Errored.args = {
69
46
  id: 'field-errored',
70
47
  title: 'Errored field title',
@@ -81,45 +58,33 @@ Errored.args = {
81
58
  // required=False,
82
59
  // default=None,
83
60
  // )
84
- choices: [
85
- ['Foo', 'Foo'],
86
- ['Bar', 'Bar'],
87
- ['FooBar', 'FooBar'],
88
- ],
61
+ choices,
89
62
  value: ['Foo'],
90
63
  error: ['This is the error'],
91
64
  required: true,
92
65
  };
93
66
 
94
- export const NoPlaceholder = Array.bind({});
67
+ export const NoPlaceholder = WidgetStory.bind({ widget: ArrayWidget });
95
68
  NoPlaceholder.args = {
96
69
  id: 'field-without-novalue',
97
70
  title: 'Field title',
98
71
  description: 'This field has no value option',
99
- choices: [
100
- ['Foo', 'Foo'],
101
- ['Bar', 'Bar'],
102
- ['FooBar', 'FooBar'],
103
- ],
72
+ choices,
104
73
  required: true,
105
74
  };
106
75
 
107
- export const WithoutNoValueOption = Array.bind({});
76
+ export const WithoutNoValueOption = WidgetStory.bind({ widget: ArrayWidget });
108
77
  WithoutNoValueOption.args = {
109
78
  id: 'field-without-novalue',
110
79
  title: 'Field title',
111
80
  description: 'This field has no value option',
112
81
  placeholder: 'something…',
113
- choices: [
114
- ['Foo', 'Foo'],
115
- ['Bar', 'Bar'],
116
- ['FooBar', 'FooBar'],
117
- ],
82
+ choices,
118
83
  required: true,
119
84
  noValueOption: false,
120
85
  };
121
86
 
122
- export const VocabularyBased = Array.bind({});
87
+ export const VocabularyBased = WidgetStory.bind({ widget: ArrayWidget });
123
88
  VocabularyBased.args = {
124
89
  id: 'field-vocab-based',
125
90
  title: 'field title',
@@ -146,7 +111,7 @@ VocabularyBased.args = {
146
111
  vocabBaseUrl: 'https://anapivocabularyURL',
147
112
  };
148
113
 
149
- export const Disabled = Array.bind({});
114
+ export const Disabled = WidgetStory.bind({ widget: ArrayWidget });
150
115
  Disabled.args = {
151
116
  id: 'field-disabled',
152
117
  title: 'Disabled field title',
@@ -157,12 +122,12 @@ Disabled.args = {
157
122
  const getOptionsGenerator = (count) => {
158
123
  const options = [];
159
124
  for (let i = 0; i < count; i = i + 1) {
160
- options.push([i, `Option ${i}`]);
125
+ options.push([i.toString(), `Option ${i}`]);
161
126
  }
162
127
  return options;
163
128
  };
164
129
 
165
- export const ManyOptions1000 = Array.bind({});
130
+ export const ManyOptions1000 = WidgetStory.bind({ widget: ArrayWidget });
166
131
  ManyOptions1000.args = {
167
132
  id: 'field-empty',
168
133
  title: 'field 1 title',
@@ -171,7 +136,7 @@ ManyOptions1000.args = {
171
136
  choices: getOptionsGenerator(1000),
172
137
  };
173
138
 
174
- export const ManyOptions500 = Array.bind({});
139
+ export const ManyOptions500 = WidgetStory.bind({ widget: ArrayWidget });
175
140
  ManyOptions500.args = {
176
141
  id: 'field-empty',
177
142
  title: 'field 1 title',
@@ -182,7 +147,7 @@ ManyOptions500.args = {
182
147
 
183
148
  export default {
184
149
  title: 'Widgets/Array',
185
- component: ArrayComponent,
150
+ component: ArrayWidgetComponent,
186
151
  decorators: [
187
152
  (Story) => (
188
153
  <div style={{ width: '400px' }}>
@@ -1,28 +1,11 @@
1
1
  import React from 'react';
2
2
  import CheckboxWidget from './CheckboxWidget';
3
- import Wrapper from '@plone/volto/storybook';
3
+ import WidgetStory from './story';
4
4
 
5
- const CheckboxWidgetComponent = ({ children, ...args }) => {
6
- const [value, setValue] = React.useState(false);
7
- const onChange = (block, value) => setValue(value);
8
- return (
9
- <Wrapper location={{ pathname: '/folder2/folder21/doc212' }}>
10
- <div className="ui segment form attached" style={{ width: '400px' }}>
11
- <CheckboxWidget
12
- {...args}
13
- id="field"
14
- title="Checkbox"
15
- block="testBlock"
16
- value={value}
17
- onChange={onChange}
18
- />
19
- </div>
20
- <pre>Value: {JSON.stringify(value, null, 4)}</pre>
21
- </Wrapper>
22
- );
23
- };
24
-
25
- export const Checkbox = CheckboxWidgetComponent.bind({});
5
+ export const Checkbox = WidgetStory.bind({
6
+ props: { id: 'field', title: 'Checkbox', block: 'block' },
7
+ widget: CheckboxWidget,
8
+ });
26
9
 
27
10
  export default {
28
11
  title: 'Widgets/Checkbox',