@plone/volto 14.0.2 → 14.1.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.
Files changed (212) hide show
  1. package/CHANGELOG.md +22 -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/src/actions/vocabularies/vocabularies.js +14 -3
  29. package/src/components/index.js +1 -0
  30. package/src/components/manage/Blocks/HeroImageLeft/Edit.jsx +1 -1
  31. package/src/components/manage/Blocks/Listing/getAsyncData.js +1 -1
  32. package/src/components/manage/Blocks/Text/Edit.jsx +19 -0
  33. package/src/components/manage/Form/Form.jsx +11 -1
  34. package/src/components/manage/Form/UndoToolbar.jsx +78 -0
  35. package/src/components/manage/Widgets/AlignWidget.stories.jsx +5 -21
  36. package/src/components/manage/Widgets/ArrayWidget.jsx +83 -101
  37. package/src/components/manage/Widgets/ArrayWidget.stories.jsx +29 -64
  38. package/src/components/manage/Widgets/CheckboxWidget.stories.jsx +5 -22
  39. package/src/components/manage/Widgets/DatetimeWidget.jsx +65 -74
  40. package/src/components/manage/Widgets/DatetimeWidget.stories.jsx +7 -23
  41. package/src/components/manage/Widgets/DatetimeWidget.test.jsx +17 -15
  42. package/src/components/manage/Widgets/EmailWidget.stories.jsx +5 -22
  43. package/src/components/manage/Widgets/FileWidget.stories.jsx +5 -22
  44. package/src/components/manage/Widgets/NumberWidget.stories.jsx +5 -23
  45. package/src/components/manage/Widgets/ObjectBrowserWidget.stories.js +24 -32
  46. package/src/components/manage/Widgets/ObjectListWidget.stories.js +44 -44
  47. package/src/components/manage/Widgets/ObjectWidget.stories.jsx +13 -28
  48. package/src/components/manage/Widgets/PasswordWidget.stories.jsx +5 -22
  49. package/src/components/manage/Widgets/QueryWidget.jsx +2 -2
  50. package/src/components/manage/Widgets/QueryWidget.stories.jsx +1637 -22
  51. package/src/components/manage/Widgets/SelectAutoComplete.jsx +79 -48
  52. package/src/components/manage/Widgets/SelectAutoComplete.test.jsx +16 -0
  53. package/src/components/manage/Widgets/SelectAutocompleteWidget.stories.jsx +161 -0
  54. package/src/components/manage/Widgets/SelectUtils.js +90 -30
  55. package/src/components/manage/Widgets/SelectUtils.test.jsx +76 -1
  56. package/src/components/manage/Widgets/SelectWidget.jsx +26 -37
  57. package/src/components/manage/Widgets/SelectWidget.stories.jsx +96 -28
  58. package/src/components/manage/Widgets/TextWidget.stories.jsx +5 -22
  59. package/src/components/manage/Widgets/TextareaWidget.stories.jsx +5 -22
  60. package/src/components/manage/Widgets/TokenWidget.jsx +19 -17
  61. package/src/components/manage/Widgets/TokenWidget.stories.jsx +141 -0
  62. package/src/components/manage/Widgets/UrlWidget.stories.jsx +5 -21
  63. package/src/components/manage/Widgets/VocabularyTermsWidget.stories.js +27 -64
  64. package/src/components/manage/Widgets/WysiwygWidget.stories.jsx +5 -22
  65. package/src/components/manage/Widgets/story.jsx +38 -0
  66. package/src/components/theme/ContactForm/ContactForm.jsx +1 -0
  67. package/src/components/theme/ContactForm/ContactForm.stories.jsx +126 -0
  68. package/src/components/theme/CorsError/CorsError.jsx +2 -2
  69. package/src/config/Loadables.jsx +2 -0
  70. package/src/config/index.js +1 -0
  71. package/src/helpers/UndoManager/useUndoManager.js +102 -0
  72. package/src/helpers/index.js +1 -0
  73. package/src/reducers/vocabularies/vocabularies.js +13 -2
  74. package/src/store.js +1 -1
  75. package/src/storybook.jsx +55 -0
  76. package/theme/themes/pastanaga/extras/time-picker-overrides.less +1 -1
  77. package/include/python3.8/Python-ast.h +0 -715
  78. package/include/python3.8/Python.h +0 -160
  79. package/include/python3.8/abstract.h +0 -844
  80. package/include/python3.8/asdl.h +0 -46
  81. package/include/python3.8/ast.h +0 -37
  82. package/include/python3.8/bitset.h +0 -23
  83. package/include/python3.8/bltinmodule.h +0 -14
  84. package/include/python3.8/boolobject.h +0 -34
  85. package/include/python3.8/bytearrayobject.h +0 -62
  86. package/include/python3.8/bytes_methods.h +0 -69
  87. package/include/python3.8/bytesobject.h +0 -224
  88. package/include/python3.8/cellobject.h +0 -29
  89. package/include/python3.8/ceval.h +0 -231
  90. package/include/python3.8/classobject.h +0 -59
  91. package/include/python3.8/code.h +0 -180
  92. package/include/python3.8/codecs.h +0 -240
  93. package/include/python3.8/compile.h +0 -106
  94. package/include/python3.8/complexobject.h +0 -69
  95. package/include/python3.8/context.h +0 -84
  96. package/include/python3.8/cpython/abstract.h +0 -319
  97. package/include/python3.8/cpython/dictobject.h +0 -94
  98. package/include/python3.8/cpython/fileobject.h +0 -24
  99. package/include/python3.8/cpython/initconfig.h +0 -434
  100. package/include/python3.8/cpython/interpreteridobject.h +0 -19
  101. package/include/python3.8/cpython/object.h +0 -470
  102. package/include/python3.8/cpython/objimpl.h +0 -113
  103. package/include/python3.8/cpython/pyerrors.h +0 -188
  104. package/include/python3.8/cpython/pylifecycle.h +0 -78
  105. package/include/python3.8/cpython/pymem.h +0 -108
  106. package/include/python3.8/cpython/pystate.h +0 -252
  107. package/include/python3.8/cpython/sysmodule.h +0 -21
  108. package/include/python3.8/cpython/traceback.h +0 -22
  109. package/include/python3.8/cpython/tupleobject.h +0 -36
  110. package/include/python3.8/cpython/unicodeobject.h +0 -1239
  111. package/include/python3.8/datetime.h +0 -259
  112. package/include/python3.8/descrobject.h +0 -108
  113. package/include/python3.8/dictobject.h +0 -94
  114. package/include/python3.8/dtoa.h +0 -19
  115. package/include/python3.8/dynamic_annotations.h +0 -499
  116. package/include/python3.8/enumobject.h +0 -17
  117. package/include/python3.8/errcode.h +0 -38
  118. package/include/python3.8/eval.h +0 -37
  119. package/include/python3.8/fileobject.h +0 -49
  120. package/include/python3.8/fileutils.h +0 -185
  121. package/include/python3.8/floatobject.h +0 -130
  122. package/include/python3.8/frameobject.h +0 -92
  123. package/include/python3.8/funcobject.h +0 -104
  124. package/include/python3.8/genobject.h +0 -109
  125. package/include/python3.8/graminit.h +0 -94
  126. package/include/python3.8/grammar.h +0 -77
  127. package/include/python3.8/import.h +0 -149
  128. package/include/python3.8/internal/pycore_accu.h +0 -39
  129. package/include/python3.8/internal/pycore_atomic.h +0 -558
  130. package/include/python3.8/internal/pycore_ceval.h +0 -37
  131. package/include/python3.8/internal/pycore_code.h +0 -27
  132. package/include/python3.8/internal/pycore_condvar.h +0 -95
  133. package/include/python3.8/internal/pycore_context.h +0 -42
  134. package/include/python3.8/internal/pycore_fileutils.h +0 -54
  135. package/include/python3.8/internal/pycore_getopt.h +0 -22
  136. package/include/python3.8/internal/pycore_gil.h +0 -50
  137. package/include/python3.8/internal/pycore_hamt.h +0 -116
  138. package/include/python3.8/internal/pycore_initconfig.h +0 -166
  139. package/include/python3.8/internal/pycore_object.h +0 -81
  140. package/include/python3.8/internal/pycore_pathconfig.h +0 -75
  141. package/include/python3.8/internal/pycore_pyerrors.h +0 -62
  142. package/include/python3.8/internal/pycore_pyhash.h +0 -10
  143. package/include/python3.8/internal/pycore_pylifecycle.h +0 -118
  144. package/include/python3.8/internal/pycore_pymem.h +0 -212
  145. package/include/python3.8/internal/pycore_pystate.h +0 -326
  146. package/include/python3.8/internal/pycore_traceback.h +0 -96
  147. package/include/python3.8/internal/pycore_tupleobject.h +0 -19
  148. package/include/python3.8/internal/pycore_warnings.h +0 -25
  149. package/include/python3.8/interpreteridobject.h +0 -17
  150. package/include/python3.8/intrcheck.h +0 -33
  151. package/include/python3.8/iterobject.h +0 -25
  152. package/include/python3.8/listobject.h +0 -81
  153. package/include/python3.8/longintrepr.h +0 -99
  154. package/include/python3.8/longobject.h +0 -242
  155. package/include/python3.8/marshal.h +0 -28
  156. package/include/python3.8/memoryobject.h +0 -72
  157. package/include/python3.8/methodobject.h +0 -131
  158. package/include/python3.8/modsupport.h +0 -248
  159. package/include/python3.8/moduleobject.h +0 -90
  160. package/include/python3.8/namespaceobject.h +0 -19
  161. package/include/python3.8/node.h +0 -48
  162. package/include/python3.8/object.h +0 -753
  163. package/include/python3.8/objimpl.h +0 -284
  164. package/include/python3.8/odictobject.h +0 -43
  165. package/include/python3.8/opcode.h +0 -148
  166. package/include/python3.8/osdefs.h +0 -51
  167. package/include/python3.8/osmodule.h +0 -17
  168. package/include/python3.8/parsetok.h +0 -110
  169. package/include/python3.8/patchlevel.h +0 -35
  170. package/include/python3.8/picklebufobject.h +0 -31
  171. package/include/python3.8/py_curses.h +0 -100
  172. package/include/python3.8/pyarena.h +0 -64
  173. package/include/python3.8/pycapsule.h +0 -59
  174. package/include/python3.8/pyconfig.h +0 -1665
  175. package/include/python3.8/pyctype.h +0 -39
  176. package/include/python3.8/pydebug.h +0 -40
  177. package/include/python3.8/pydtrace.h +0 -59
  178. package/include/python3.8/pydtrace_probes.h +0 -228
  179. package/include/python3.8/pyerrors.h +0 -335
  180. package/include/python3.8/pyexpat.h +0 -55
  181. package/include/python3.8/pyfpe.h +0 -12
  182. package/include/python3.8/pyhash.h +0 -145
  183. package/include/python3.8/pylifecycle.h +0 -75
  184. package/include/python3.8/pymacconfig.h +0 -102
  185. package/include/python3.8/pymacro.h +0 -106
  186. package/include/python3.8/pymath.h +0 -230
  187. package/include/python3.8/pymem.h +0 -150
  188. package/include/python3.8/pyport.h +0 -850
  189. package/include/python3.8/pystate.h +0 -136
  190. package/include/python3.8/pystrcmp.h +0 -23
  191. package/include/python3.8/pystrhex.h +0 -22
  192. package/include/python3.8/pystrtod.h +0 -45
  193. package/include/python3.8/pythonrun.h +0 -210
  194. package/include/python3.8/pythread.h +0 -161
  195. package/include/python3.8/pytime.h +0 -246
  196. package/include/python3.8/rangeobject.h +0 -27
  197. package/include/python3.8/setobject.h +0 -108
  198. package/include/python3.8/sliceobject.h +0 -65
  199. package/include/python3.8/structmember.h +0 -74
  200. package/include/python3.8/structseq.h +0 -49
  201. package/include/python3.8/symtable.h +0 -123
  202. package/include/python3.8/sysmodule.h +0 -41
  203. package/include/python3.8/token.h +0 -92
  204. package/include/python3.8/traceback.h +0 -28
  205. package/include/python3.8/tracemalloc.h +0 -38
  206. package/include/python3.8/tupleobject.h +0 -48
  207. package/include/python3.8/typeslots.h +0 -85
  208. package/include/python3.8/ucnhash.h +0 -36
  209. package/include/python3.8/unicodeobject.h +0 -1044
  210. package/include/python3.8/warnings.h +0 -67
  211. package/include/python3.8/weakrefobject.h +0 -86
  212. package/src/components/theme/ContactForm/ContactForm.stories.mdx +0 -39
@@ -16,7 +16,7 @@ import {
16
16
  } from '@plone/volto/helpers';
17
17
  import { FormFieldWrapper } from '@plone/volto/components';
18
18
  import { getVocabulary, getVocabularyTokenTitle } from '@plone/volto/actions';
19
- import { normalizeValue } from './SelectUtils';
19
+ import { normalizeValue } from '@plone/volto/components/manage/Widgets/SelectUtils';
20
20
 
21
21
  import {
22
22
  customSelectStyles,
@@ -107,6 +107,8 @@ class SelectWidget extends Component {
107
107
  PropTypes.object,
108
108
  PropTypes.string,
109
109
  PropTypes.bool,
110
+ PropTypes.func,
111
+ PropTypes.array,
110
112
  ]),
111
113
  onChange: PropTypes.func.isRequired,
112
114
  onBlur: PropTypes.func,
@@ -115,6 +117,8 @@ class SelectWidget extends Component {
115
117
  onDelete: PropTypes.func,
116
118
  wrapped: PropTypes.bool,
117
119
  noValueOption: PropTypes.bool,
120
+ customOptionStyling: PropTypes.any,
121
+ isMulti: PropTypes.bool,
118
122
  };
119
123
 
120
124
  /**
@@ -140,11 +144,7 @@ class SelectWidget extends Component {
140
144
  onEdit: null,
141
145
  onDelete: null,
142
146
  noValueOption: true,
143
- };
144
-
145
- state = {
146
- // TODO: also take into account this.props.defaultValue?
147
- selectedOption: normalizeValue(this.props.choices, this.props.value),
147
+ customOptionStyling: null,
148
148
  };
149
149
 
150
150
  /**
@@ -165,32 +165,14 @@ class SelectWidget extends Component {
165
165
  }
166
166
  }
167
167
 
168
- componentDidUpdate() {
169
- if (
170
- !this.state.selectedOption &&
171
- this.props.value &&
172
- this.props.choices?.length > 0
173
- ) {
174
- const normalizedValue = normalizeValue(
175
- this.props.choices,
176
- this.props.value,
177
- );
178
-
179
- if (normalizedValue != null) {
180
- this.setState({
181
- selectedOption: normalizedValue,
182
- });
183
- }
184
- }
185
- }
186
-
187
168
  /**
188
169
  * Render method.
189
170
  * @method render
190
171
  * @returns {string} Markup for the component.
191
172
  */
192
173
  render() {
193
- const { id, choices, onChange } = this.props;
174
+ const { id, choices, value, intl, onChange } = this.props;
175
+ const normalizedValue = normalizeValue(choices, value, intl);
194
176
  // Make sure that both disabled and isDisabled (from the DX layout feat work)
195
177
  const disabled = this.props.disabled || this.props.isDisabled;
196
178
  const Select = this.props.reactSelect.default;
@@ -216,6 +198,10 @@ class SelectWidget extends Component {
216
198
  : []),
217
199
  ];
218
200
 
201
+ const isMulti = this.props.isMulti
202
+ ? this.props.isMulti
203
+ : id === 'roles' || id === 'groups';
204
+
219
205
  return (
220
206
  <FormFieldWrapper {...this.props}>
221
207
  <Select
@@ -226,11 +212,7 @@ class SelectWidget extends Component {
226
212
  isSearchable={true}
227
213
  className="react-select-container"
228
214
  classNamePrefix="react-select"
229
- isMulti={
230
- this.props.isMulti
231
- ? this.props.isMulti
232
- : id === 'roles' || id === 'groups'
233
- }
215
+ isMulti={isMulti}
234
216
  options={options}
235
217
  styles={customSelectStyles}
236
218
  theme={selectTheme}
@@ -240,12 +222,20 @@ class SelectWidget extends Component {
240
222
  }),
241
223
  DropdownIndicator,
242
224
  ClearIndicator,
243
- Option,
225
+ Option: this.props.customOptionStyling || Option,
244
226
  }}
245
- value={this.state.selectedOption}
246
- placeholder={this.props.intl.formatMessage(messages.select)}
227
+ value={normalizedValue}
228
+ placeholder={
229
+ this.props.placeholder ??
230
+ this.props.intl.formatMessage(messages.select)
231
+ }
247
232
  onChange={(selectedOption) => {
248
- this.setState({ selectedOption });
233
+ if (isMulti) {
234
+ return onChange(
235
+ id,
236
+ selectedOption.map((el) => el.value),
237
+ );
238
+ }
249
239
  return onChange(
250
240
  id,
251
241
  selectedOption && selectedOption.value !== 'no-value'
@@ -263,7 +253,6 @@ class SelectWidget extends Component {
263
253
  export const SelectWidgetComponent = injectIntl(SelectWidget);
264
254
 
265
255
  export default compose(
266
- injectIntl,
267
256
  injectLazyLibs(['reactSelect']),
268
257
  connect(
269
258
  (state, props) => {
@@ -298,4 +287,4 @@ export default compose(
298
287
  },
299
288
  { getVocabulary, getVocabularyTokenTitle },
300
289
  ),
301
- )(SelectWidget);
290
+ )(SelectWidgetComponent);
@@ -1,30 +1,24 @@
1
1
  import React from 'react';
2
- import { SelectWidgetComponent } from './SelectWidget';
3
- import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
4
- import { RealStoreWrapper as Wrapper } from '@plone/volto/storybook';
2
+ import SelectWidget, { SelectWidgetComponent } from './SelectWidget';
3
+ import WidgetStory from './story';
5
4
 
6
- const SelectComponent = injectLazyLibs(['reactSelect'])(SelectWidgetComponent);
5
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
6
+ import checkSVG from '@plone/volto/icons/check.svg';
7
+ import { Icon } from '@plone/volto/components';
7
8
 
8
- const Select = (args) => {
9
- const [value, setValue] = React.useState(args.value ?? '');
10
- const onChange = (block, value) => {
11
- // args.onChange({ value });
12
- setValue(value);
13
- };
9
+ import bellRingingSVG from '@plone/volto/icons/bell-ringing.svg';
10
+ import blogSVG from '@plone/volto/icons/blog.svg';
11
+ import bookSVG from '@plone/volto/icons/book.svg';
14
12
 
15
- return (
16
- <Wrapper>
17
- <SelectComponent {...args} onChange={onChange} value={value} />
18
- </Wrapper>
19
- );
20
- };
21
-
22
- export const Default = Select.bind({});
13
+ export const Default = WidgetStory.bind({
14
+ widget: SelectWidget,
15
+ });
23
16
  Default.args = {
24
17
  id: 'field-empty',
25
18
  title: 'field 1 title',
26
19
  description: 'Optional help text',
27
20
  placeholder: 'Type something…',
21
+ isMulti: false,
28
22
  choices: [
29
23
  ['Foo', 'Foo'],
30
24
  ['Bar', 'Bar'],
@@ -32,7 +26,9 @@ Default.args = {
32
26
  ],
33
27
  };
34
28
 
35
- export const Required = Select.bind({});
29
+ export const Required = WidgetStory.bind({
30
+ widget: SelectWidget,
31
+ });
36
32
  Required.args = {
37
33
  id: 'field-empty',
38
34
  title: 'field 1 title',
@@ -46,7 +42,9 @@ Required.args = {
46
42
  required: true,
47
43
  };
48
44
 
49
- export const Filled = Select.bind({});
45
+ export const Filled = WidgetStory.bind({
46
+ widget: SelectWidget,
47
+ });
50
48
  Filled.args = {
51
49
  id: 'field-filled',
52
50
  title: 'Filled field title',
@@ -61,7 +59,9 @@ Filled.args = {
61
59
  required: true,
62
60
  };
63
61
 
64
- export const Errored = Select.bind({});
62
+ export const Errored = WidgetStory.bind({
63
+ widget: SelectWidget,
64
+ });
65
65
  Errored.args = {
66
66
  id: 'field-errored',
67
67
  title: 'Errored field title',
@@ -88,7 +88,9 @@ Errored.args = {
88
88
  required: true,
89
89
  };
90
90
 
91
- export const NoPlaceholder = Select.bind({});
91
+ export const NoPlaceholder = WidgetStory.bind({
92
+ widget: SelectWidget,
93
+ });
92
94
  NoPlaceholder.args = {
93
95
  id: 'field-without-novalue',
94
96
  title: 'Field title',
@@ -101,7 +103,9 @@ NoPlaceholder.args = {
101
103
  required: true,
102
104
  };
103
105
 
104
- export const WithoutNoValueOption = Select.bind({});
106
+ export const WithoutNoValueOption = WidgetStory.bind({
107
+ widget: SelectWidget,
108
+ });
105
109
  WithoutNoValueOption.args = {
106
110
  id: 'field-without-novalue',
107
111
  title: 'Field title',
@@ -116,7 +120,9 @@ WithoutNoValueOption.args = {
116
120
  noValueOption: false,
117
121
  };
118
122
 
119
- export const VocabularyBased = Select.bind({});
123
+ export const VocabularyBased = WidgetStory.bind({
124
+ widget: SelectWidget,
125
+ });
120
126
  VocabularyBased.args = {
121
127
  id: 'field-vocab-based',
122
128
  title: 'field title',
@@ -143,7 +149,9 @@ VocabularyBased.args = {
143
149
  vocabBaseUrl: 'https://anapivocabularyURL',
144
150
  };
145
151
 
146
- export const Disabled = Select.bind({});
152
+ export const Disabled = WidgetStory.bind({
153
+ widget: SelectWidget,
154
+ });
147
155
  Disabled.args = {
148
156
  id: 'field-disabled',
149
157
  title: 'Disabled field title',
@@ -159,7 +167,9 @@ const getOptionsGenerator = (count) => {
159
167
  return options;
160
168
  };
161
169
 
162
- export const ManyOptions1000 = Select.bind({});
170
+ export const ManyOptions1000 = WidgetStory.bind({
171
+ widget: SelectWidget,
172
+ });
163
173
  ManyOptions1000.args = {
164
174
  id: 'field-empty',
165
175
  title: 'field 1 title',
@@ -168,7 +178,9 @@ ManyOptions1000.args = {
168
178
  choices: getOptionsGenerator(1000),
169
179
  };
170
180
 
171
- export const ManyOptions500 = Select.bind({});
181
+ export const ManyOptions500 = WidgetStory.bind({
182
+ widget: SelectWidget,
183
+ });
172
184
  ManyOptions500.args = {
173
185
  id: 'field-empty',
174
186
  title: 'field 1 title',
@@ -177,9 +189,65 @@ ManyOptions500.args = {
177
189
  choices: getOptionsGenerator(500),
178
190
  };
179
191
 
192
+ export const MultiSelection = WidgetStory.bind({
193
+ widget: SelectWidget,
194
+ });
195
+ MultiSelection.args = {
196
+ id: 'field-empty',
197
+ title: 'field 1 title',
198
+ description: 'Select multiple values',
199
+ placeholder: 'Type something…',
200
+ isMulti: true,
201
+ choices: [
202
+ ['Foo', 'Foo'],
203
+ ['Bar', 'Bar'],
204
+ ['FooBar', 'FooBar'],
205
+ ],
206
+ };
207
+
208
+ const Option = injectLazyLibs('reactSelect')((props) => {
209
+ const { Option } = props.reactSelect.components;
210
+ const icons = {
211
+ FooBar: bellRingingSVG,
212
+ Bar: blogSVG,
213
+ Foo: bookSVG,
214
+ };
215
+ return (
216
+ <Option {...props}>
217
+ <div>
218
+ {icons[props.value] && <Icon name={icons[props.value]} size="24px" />}
219
+ {props.label}
220
+ </div>
221
+ {props.isFocused && !props.isSelected && (
222
+ <Icon name={checkSVG} size="24px" color="#b8c6c8" />
223
+ )}
224
+ {props.isSelected && <Icon name={checkSVG} size="24px" color="#007bc1" />}
225
+ </Option>
226
+ );
227
+ });
228
+
229
+ export const CustomOptions = WidgetStory.bind({
230
+ widget: SelectWidget,
231
+ props: {
232
+ customOptionStyling: Option,
233
+ },
234
+ });
235
+ CustomOptions.args = {
236
+ id: 'field-empty',
237
+ title: 'field 1 title',
238
+ description: 'Select a value',
239
+ placeholder: 'Type something…',
240
+ isMulti: false,
241
+ choices: [
242
+ ['Foo', 'Foo'],
243
+ ['Bar', 'Bar'],
244
+ ['FooBar', 'FooBar'],
245
+ ],
246
+ };
247
+
180
248
  export default {
181
249
  title: 'Widgets/Select Widget',
182
- component: SelectComponent,
250
+ component: SelectWidgetComponent,
183
251
  decorators: [
184
252
  (Story) => (
185
253
  <div style={{ width: '400px' }}>
@@ -1,28 +1,11 @@
1
1
  import React from 'react';
2
2
  import TextWidget from './TextWidget';
3
- import Wrapper from '@plone/volto/storybook';
3
+ import WidgetStory from './story';
4
4
 
5
- const TextWidgetComponent = ({ 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
- <TextWidget
12
- {...args}
13
- id="field"
14
- title="Text"
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 Text = TextWidgetComponent.bind({});
5
+ export const Text = WidgetStory.bind({
6
+ props: { id: 'text', title: 'Text' },
7
+ widget: TextWidget,
8
+ });
26
9
 
27
10
  export default {
28
11
  title: 'Widgets/Text',
@@ -1,28 +1,11 @@
1
1
  import React from 'react';
2
2
  import TextareaWidget from './TextareaWidget';
3
- import Wrapper from '@plone/volto/storybook';
3
+ import WidgetStory from './story';
4
4
 
5
- const TextareaWidgetComponent = ({ 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
- <TextareaWidget
12
- {...args}
13
- id="field"
14
- title="Textarea"
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 Textarea = TextareaWidgetComponent.bind({});
5
+ export const Textarea = WidgetStory.bind({
6
+ props: { id: 'textarea', title: 'Text area', block: 'block' },
7
+ widget: TextareaWidget,
8
+ });
26
9
 
27
10
  export default {
28
11
  title: 'Widgets/Textarea',
@@ -1,6 +1,6 @@
1
1
  /**
2
- * ArrayWidget component.
3
- * @module components/manage/Widgets/ArrayWidget
2
+ * TokenWidget component.
3
+ * @module components/manage/Widgets/TokenWidget
4
4
  */
5
5
 
6
6
  import React, { Component } from 'react';
@@ -39,6 +39,11 @@ const messages = defineMessages({
39
39
 
40
40
  /**
41
41
  * TokenWidget component class.
42
+ *
43
+ * Because new terms are created through the web by using the widget, the token
44
+ * widget conflates the meaning of token, label and value and assumes they can
45
+ * be used interchangeably.
46
+ *
42
47
  * @class TokenWidget
43
48
  * @extends Component
44
49
  */
@@ -95,12 +100,6 @@ class TokenWidget extends Component {
95
100
  constructor(props) {
96
101
  super(props);
97
102
  this.handleChange = this.handleChange.bind(this);
98
-
99
- this.state = {
100
- selectedOption: props.value
101
- ? props.value.map((item) => ({ label: item, value: item }))
102
- : [],
103
- };
104
103
  }
105
104
 
106
105
  /**
@@ -109,11 +108,13 @@ class TokenWidget extends Component {
109
108
  * @returns {undefined}
110
109
  */
111
110
  componentDidMount() {
112
- this.props.getVocabulary({
113
- vocabNameOrURL: this.props.vocabBaseUrl,
114
- size: -1,
115
- subrequest: this.props.intl.locale,
116
- });
111
+ if (!this.props.choices?.length) {
112
+ this.props.getVocabulary({
113
+ vocabNameOrURL: this.props.vocabBaseUrl,
114
+ size: -1,
115
+ subrequest: this.props.intl.locale,
116
+ });
117
+ }
117
118
  }
118
119
 
119
120
  /**
@@ -124,7 +125,6 @@ class TokenWidget extends Component {
124
125
  * @returns {undefined}
125
126
  */
126
127
  handleChange(selectedOption) {
127
- this.setState({ selectedOption });
128
128
  this.props.onChange(
129
129
  this.props.id,
130
130
  selectedOption ? selectedOption.map((item) => item.label) : null,
@@ -137,11 +137,13 @@ class TokenWidget extends Component {
137
137
  * @returns {string} Markup for the component.
138
138
  */
139
139
  render() {
140
- const { selectedOption } = this.state;
140
+ const selectedOption = this.props.value
141
+ ? this.props.value.map((item) => ({ label: item, value: item }))
142
+ : [];
143
+
141
144
  const defaultOptions = (this.props.choices || [])
142
145
  .filter(
143
- (item) =>
144
- !this.state.selectedOption.find(({ label }) => label === item.label),
146
+ (item) => !selectedOption.find(({ label }) => label === item.label),
145
147
  )
146
148
  .map((item) => ({
147
149
  label: item.label || item.value,
@@ -0,0 +1,141 @@
1
+ import React from 'react';
2
+ import TokenWidget from './TokenWidget';
3
+
4
+ import WidgetStory from './story';
5
+
6
+ const vocabBaseUrl = 'https://anapivocabularyURL';
7
+
8
+ const defaults = {
9
+ widget: TokenWidget,
10
+ props: {
11
+ widgetOptions: {
12
+ vocabulary: {
13
+ '@id': vocabBaseUrl,
14
+ },
15
+ },
16
+ },
17
+ customStore: {
18
+ userSession: { token: '1234' },
19
+ intl: {
20
+ locale: 'en',
21
+ messages: {},
22
+ },
23
+ vocabularies: {
24
+ [vocabBaseUrl]: {
25
+ subrequests: {
26
+ en: {
27
+ items: [
28
+ { value: 'foo', label: 'Foo' },
29
+ { value: 'bar', label: 'Bar' },
30
+ { value: 'fooBar', label: 'FooBar' },
31
+ ],
32
+ },
33
+ },
34
+ },
35
+ },
36
+ },
37
+ };
38
+
39
+ export const Default = WidgetStory.bind(defaults);
40
+ Default.args = {
41
+ id: 'field-empty',
42
+ title: 'field 1 title',
43
+ description: 'Optional help text',
44
+ placeholder: 'Type something…',
45
+ vocabBaseUrl,
46
+ };
47
+
48
+ export const Required = WidgetStory.bind(defaults);
49
+ Required.args = {
50
+ id: 'field-empty',
51
+ title: 'field 1 title',
52
+ description: 'Optional help text',
53
+ placeholder: 'Type something…',
54
+ required: true,
55
+ vocabBaseUrl,
56
+ };
57
+
58
+ export const Filled = WidgetStory.bind(defaults);
59
+ Filled.args = {
60
+ id: 'field-filled',
61
+ title: 'Filled field title',
62
+ description: 'Optional help text',
63
+ value: ['Foo'],
64
+ placeholder: 'Type something…',
65
+ required: true,
66
+ vocabBaseUrl,
67
+ };
68
+
69
+ export const Errored = WidgetStory.bind(defaults);
70
+ Errored.args = {
71
+ id: 'field-errored',
72
+ title: 'Errored field title',
73
+ description: 'Optional help text',
74
+ placeholder: 'Type something…',
75
+ // Simplest example in Plone - a "hardcoded, hand made" vocab using SimpleVocabulary/SimpleTerm
76
+ // allow_discussion = schema.Choice(
77
+ // title=_(u'Allow discussion'),
78
+ // description=_(u'Allow discussion for this content object.'),
79
+ // vocabulary=SimpleVocabulary([
80
+ // SimpleTerm(value=True, title=_(u'Yes')),
81
+ // SimpleTerm(value=False, title=_(u'No')),
82
+ // ]),
83
+ // required=False,
84
+ // default=None,
85
+ // )
86
+ value: ['Foo'],
87
+ error: ['This is the error'],
88
+ required: true,
89
+ vocabBaseUrl,
90
+ };
91
+
92
+ export const NoPlaceholder = WidgetStory.bind(defaults);
93
+ NoPlaceholder.args = {
94
+ id: 'field-without-novalue',
95
+ title: 'Field title',
96
+ description: 'This field has no value option',
97
+ required: true,
98
+ vocabBaseUrl,
99
+ };
100
+
101
+ export const WithoutNoValueOption = WidgetStory.bind(defaults);
102
+ WithoutNoValueOption.args = {
103
+ id: 'field-without-novalue',
104
+ title: 'Field title',
105
+ description: 'This field has no value option',
106
+ placeholder: 'something…',
107
+ required: true,
108
+ noValueOption: false,
109
+ vocabBaseUrl,
110
+ };
111
+
112
+ export const Disabled = WidgetStory.bind(defaults);
113
+ Disabled.args = {
114
+ id: 'field-disabled',
115
+ title: 'Disabled field title',
116
+ description: 'This select field is disabled',
117
+ disabled: true,
118
+ vocabBaseUrl,
119
+ };
120
+
121
+ export default {
122
+ title: 'Widgets/Token',
123
+ component: TokenWidget,
124
+ decorators: [
125
+ (Story) => (
126
+ <div style={{ width: '400px' }}>
127
+ <Story />
128
+ </div>
129
+ ),
130
+ ],
131
+ argTypes: {
132
+ // controlled value prop
133
+ value: {
134
+ control: {
135
+ disable: true,
136
+ },
137
+ },
138
+ },
139
+ // excludeStories: ['searchResults'],
140
+ // subcomponents: { ArgsTable },
141
+ };
@@ -1,28 +1,12 @@
1
1
  import React from 'react';
2
2
  import UrlWidgetDefault, { UrlWidget } from './UrlWidget';
3
- import Wrapper from '@plone/volto/storybook';
4
3
 
5
- const UrlWidgetComponent = ({ 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
- <UrlWidgetDefault
12
- {...args}
13
- id="field"
14
- title="Url"
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
- };
4
+ import WidgetStory from './story';
24
5
 
25
- export const Url = UrlWidgetComponent.bind({});
6
+ export const Url = WidgetStory.bind({
7
+ props: { id: 'align', title: 'URL' },
8
+ widget: UrlWidgetDefault,
9
+ });
26
10
 
27
11
  export default {
28
12
  title: 'Widgets/Url',