@plone/volto 14.0.1 → 14.2.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 (218) hide show
  1. package/CHANGELOG.md +41 -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/Add/Add.jsx +1 -0
  32. package/src/components/manage/Blocks/HeroImageLeft/Edit.jsx +1 -1
  33. package/src/components/manage/Blocks/Listing/getAsyncData.js +1 -1
  34. package/src/components/manage/Blocks/Text/Edit.jsx +19 -0
  35. package/src/components/manage/Edit/Edit.jsx +1 -0
  36. package/src/components/manage/Form/Form.jsx +32 -6
  37. package/src/components/manage/Form/UndoToolbar.jsx +78 -0
  38. package/src/components/manage/Multilingual/TranslationObject.jsx +1 -0
  39. package/src/components/manage/Widgets/AlignWidget.stories.jsx +5 -21
  40. package/src/components/manage/Widgets/ArrayWidget.jsx +88 -88
  41. package/src/components/manage/Widgets/ArrayWidget.stories.jsx +37 -64
  42. package/src/components/manage/Widgets/CheckboxWidget.stories.jsx +5 -22
  43. package/src/components/manage/Widgets/DatetimeWidget.jsx +65 -74
  44. package/src/components/manage/Widgets/DatetimeWidget.stories.jsx +7 -23
  45. package/src/components/manage/Widgets/DatetimeWidget.test.jsx +17 -15
  46. package/src/components/manage/Widgets/EmailWidget.stories.jsx +5 -22
  47. package/src/components/manage/Widgets/FileWidget.stories.jsx +5 -22
  48. package/src/components/manage/Widgets/NumberWidget.stories.jsx +5 -23
  49. package/src/components/manage/Widgets/ObjectBrowserWidget.stories.js +24 -32
  50. package/src/components/manage/Widgets/ObjectListWidget.stories.js +44 -44
  51. package/src/components/manage/Widgets/ObjectWidget.stories.jsx +13 -28
  52. package/src/components/manage/Widgets/PasswordWidget.stories.jsx +5 -22
  53. package/src/components/manage/Widgets/QueryWidget.jsx +2 -2
  54. package/src/components/manage/Widgets/QueryWidget.stories.jsx +1637 -22
  55. package/src/components/manage/Widgets/SelectAutoComplete.jsx +79 -48
  56. package/src/components/manage/Widgets/SelectAutoComplete.test.jsx +16 -0
  57. package/src/components/manage/Widgets/SelectAutocompleteWidget.stories.jsx +161 -0
  58. package/src/components/manage/Widgets/SelectUtils.js +90 -30
  59. package/src/components/manage/Widgets/SelectUtils.test.jsx +76 -1
  60. package/src/components/manage/Widgets/SelectWidget.jsx +26 -37
  61. package/src/components/manage/Widgets/SelectWidget.stories.jsx +96 -28
  62. package/src/components/manage/Widgets/TextWidget.stories.jsx +5 -22
  63. package/src/components/manage/Widgets/TextareaWidget.stories.jsx +5 -22
  64. package/src/components/manage/Widgets/TokenWidget.jsx +19 -17
  65. package/src/components/manage/Widgets/TokenWidget.stories.jsx +141 -0
  66. package/src/components/manage/Widgets/UrlWidget.stories.jsx +5 -21
  67. package/src/components/manage/Widgets/VocabularyTermsWidget.stories.js +27 -64
  68. package/src/components/manage/Widgets/WysiwygWidget.stories.jsx +5 -22
  69. package/src/components/manage/Widgets/story.jsx +38 -0
  70. package/src/components/theme/ContactForm/ContactForm.jsx +1 -0
  71. package/src/components/theme/ContactForm/ContactForm.stories.jsx +126 -0
  72. package/src/components/theme/CorsError/CorsError.jsx +2 -2
  73. package/src/config/Blocks.jsx +8 -1
  74. package/src/config/Loadables.jsx +2 -0
  75. package/src/config/index.js +3 -0
  76. package/src/helpers/Html/Html.jsx +2 -12
  77. package/src/helpers/UndoManager/useUndoManager.js +102 -0
  78. package/src/helpers/index.js +1 -0
  79. package/src/reducers/vocabularies/vocabularies.js +13 -2
  80. package/src/store.js +1 -1
  81. package/src/storybook.jsx +55 -0
  82. package/theme/themes/pastanaga/extras/time-picker-overrides.less +1 -1
  83. package/include/python3.8/Python-ast.h +0 -715
  84. package/include/python3.8/Python.h +0 -160
  85. package/include/python3.8/abstract.h +0 -844
  86. package/include/python3.8/asdl.h +0 -46
  87. package/include/python3.8/ast.h +0 -37
  88. package/include/python3.8/bitset.h +0 -23
  89. package/include/python3.8/bltinmodule.h +0 -14
  90. package/include/python3.8/boolobject.h +0 -34
  91. package/include/python3.8/bytearrayobject.h +0 -62
  92. package/include/python3.8/bytes_methods.h +0 -69
  93. package/include/python3.8/bytesobject.h +0 -224
  94. package/include/python3.8/cellobject.h +0 -29
  95. package/include/python3.8/ceval.h +0 -231
  96. package/include/python3.8/classobject.h +0 -59
  97. package/include/python3.8/code.h +0 -180
  98. package/include/python3.8/codecs.h +0 -240
  99. package/include/python3.8/compile.h +0 -106
  100. package/include/python3.8/complexobject.h +0 -69
  101. package/include/python3.8/context.h +0 -84
  102. package/include/python3.8/cpython/abstract.h +0 -319
  103. package/include/python3.8/cpython/dictobject.h +0 -94
  104. package/include/python3.8/cpython/fileobject.h +0 -24
  105. package/include/python3.8/cpython/initconfig.h +0 -434
  106. package/include/python3.8/cpython/interpreteridobject.h +0 -19
  107. package/include/python3.8/cpython/object.h +0 -470
  108. package/include/python3.8/cpython/objimpl.h +0 -113
  109. package/include/python3.8/cpython/pyerrors.h +0 -188
  110. package/include/python3.8/cpython/pylifecycle.h +0 -78
  111. package/include/python3.8/cpython/pymem.h +0 -108
  112. package/include/python3.8/cpython/pystate.h +0 -252
  113. package/include/python3.8/cpython/sysmodule.h +0 -21
  114. package/include/python3.8/cpython/traceback.h +0 -22
  115. package/include/python3.8/cpython/tupleobject.h +0 -36
  116. package/include/python3.8/cpython/unicodeobject.h +0 -1239
  117. package/include/python3.8/datetime.h +0 -259
  118. package/include/python3.8/descrobject.h +0 -108
  119. package/include/python3.8/dictobject.h +0 -94
  120. package/include/python3.8/dtoa.h +0 -19
  121. package/include/python3.8/dynamic_annotations.h +0 -499
  122. package/include/python3.8/enumobject.h +0 -17
  123. package/include/python3.8/errcode.h +0 -38
  124. package/include/python3.8/eval.h +0 -37
  125. package/include/python3.8/fileobject.h +0 -49
  126. package/include/python3.8/fileutils.h +0 -185
  127. package/include/python3.8/floatobject.h +0 -130
  128. package/include/python3.8/frameobject.h +0 -92
  129. package/include/python3.8/funcobject.h +0 -104
  130. package/include/python3.8/genobject.h +0 -109
  131. package/include/python3.8/graminit.h +0 -94
  132. package/include/python3.8/grammar.h +0 -77
  133. package/include/python3.8/import.h +0 -149
  134. package/include/python3.8/internal/pycore_accu.h +0 -39
  135. package/include/python3.8/internal/pycore_atomic.h +0 -558
  136. package/include/python3.8/internal/pycore_ceval.h +0 -37
  137. package/include/python3.8/internal/pycore_code.h +0 -27
  138. package/include/python3.8/internal/pycore_condvar.h +0 -95
  139. package/include/python3.8/internal/pycore_context.h +0 -42
  140. package/include/python3.8/internal/pycore_fileutils.h +0 -54
  141. package/include/python3.8/internal/pycore_getopt.h +0 -22
  142. package/include/python3.8/internal/pycore_gil.h +0 -50
  143. package/include/python3.8/internal/pycore_hamt.h +0 -116
  144. package/include/python3.8/internal/pycore_initconfig.h +0 -166
  145. package/include/python3.8/internal/pycore_object.h +0 -81
  146. package/include/python3.8/internal/pycore_pathconfig.h +0 -75
  147. package/include/python3.8/internal/pycore_pyerrors.h +0 -62
  148. package/include/python3.8/internal/pycore_pyhash.h +0 -10
  149. package/include/python3.8/internal/pycore_pylifecycle.h +0 -118
  150. package/include/python3.8/internal/pycore_pymem.h +0 -212
  151. package/include/python3.8/internal/pycore_pystate.h +0 -326
  152. package/include/python3.8/internal/pycore_traceback.h +0 -96
  153. package/include/python3.8/internal/pycore_tupleobject.h +0 -19
  154. package/include/python3.8/internal/pycore_warnings.h +0 -25
  155. package/include/python3.8/interpreteridobject.h +0 -17
  156. package/include/python3.8/intrcheck.h +0 -33
  157. package/include/python3.8/iterobject.h +0 -25
  158. package/include/python3.8/listobject.h +0 -81
  159. package/include/python3.8/longintrepr.h +0 -99
  160. package/include/python3.8/longobject.h +0 -242
  161. package/include/python3.8/marshal.h +0 -28
  162. package/include/python3.8/memoryobject.h +0 -72
  163. package/include/python3.8/methodobject.h +0 -131
  164. package/include/python3.8/modsupport.h +0 -248
  165. package/include/python3.8/moduleobject.h +0 -90
  166. package/include/python3.8/namespaceobject.h +0 -19
  167. package/include/python3.8/node.h +0 -48
  168. package/include/python3.8/object.h +0 -753
  169. package/include/python3.8/objimpl.h +0 -284
  170. package/include/python3.8/odictobject.h +0 -43
  171. package/include/python3.8/opcode.h +0 -148
  172. package/include/python3.8/osdefs.h +0 -51
  173. package/include/python3.8/osmodule.h +0 -17
  174. package/include/python3.8/parsetok.h +0 -110
  175. package/include/python3.8/patchlevel.h +0 -35
  176. package/include/python3.8/picklebufobject.h +0 -31
  177. package/include/python3.8/py_curses.h +0 -100
  178. package/include/python3.8/pyarena.h +0 -64
  179. package/include/python3.8/pycapsule.h +0 -59
  180. package/include/python3.8/pyconfig.h +0 -1665
  181. package/include/python3.8/pyctype.h +0 -39
  182. package/include/python3.8/pydebug.h +0 -40
  183. package/include/python3.8/pydtrace.h +0 -59
  184. package/include/python3.8/pydtrace_probes.h +0 -228
  185. package/include/python3.8/pyerrors.h +0 -335
  186. package/include/python3.8/pyexpat.h +0 -55
  187. package/include/python3.8/pyfpe.h +0 -12
  188. package/include/python3.8/pyhash.h +0 -145
  189. package/include/python3.8/pylifecycle.h +0 -75
  190. package/include/python3.8/pymacconfig.h +0 -102
  191. package/include/python3.8/pymacro.h +0 -106
  192. package/include/python3.8/pymath.h +0 -230
  193. package/include/python3.8/pymem.h +0 -150
  194. package/include/python3.8/pyport.h +0 -850
  195. package/include/python3.8/pystate.h +0 -136
  196. package/include/python3.8/pystrcmp.h +0 -23
  197. package/include/python3.8/pystrhex.h +0 -22
  198. package/include/python3.8/pystrtod.h +0 -45
  199. package/include/python3.8/pythonrun.h +0 -210
  200. package/include/python3.8/pythread.h +0 -161
  201. package/include/python3.8/pytime.h +0 -246
  202. package/include/python3.8/rangeobject.h +0 -27
  203. package/include/python3.8/setobject.h +0 -108
  204. package/include/python3.8/sliceobject.h +0 -65
  205. package/include/python3.8/structmember.h +0 -74
  206. package/include/python3.8/structseq.h +0 -49
  207. package/include/python3.8/symtable.h +0 -123
  208. package/include/python3.8/sysmodule.h +0 -41
  209. package/include/python3.8/token.h +0 -92
  210. package/include/python3.8/traceback.h +0 -28
  211. package/include/python3.8/tracemalloc.h +0 -38
  212. package/include/python3.8/tupleobject.h +0 -48
  213. package/include/python3.8/typeslots.h +0 -85
  214. package/include/python3.8/ucnhash.h +0 -36
  215. package/include/python3.8/unicodeobject.h +0 -1044
  216. package/include/python3.8/warnings.h +0 -67
  217. package/include/python3.8/weakrefobject.h +0 -86
  218. 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',