@plone/volto 19.0.0-alpha.32 → 19.0.0-alpha.34

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 (33) hide show
  1. package/AGENTS.md +47 -0
  2. package/CHANGELOG.md +27 -0
  3. package/README.md +0 -1
  4. package/package.json +16 -19
  5. package/src/actions/controlpanels/controlpanels.js +7 -12
  6. package/src/actions/controlpanels/controlpanels.test.js +2 -3
  7. package/src/components/manage/Contents/Contents.jsx +410 -323
  8. package/src/components/manage/Contents/Contents.test.jsx +1 -1
  9. package/src/components/manage/Contents/ContentsIndexHeader.jsx +47 -81
  10. package/src/components/manage/Contents/ContentsIndexHeader.test.jsx +10 -3
  11. package/src/components/manage/Contents/ContentsItem.jsx +226 -278
  12. package/src/components/manage/Contents/ContentsItem.test.jsx +10 -6
  13. package/src/components/manage/Controlpanels/ContentType.jsx +131 -222
  14. package/src/components/manage/Controlpanels/Controlpanel.jsx +122 -218
  15. package/src/components/manage/Controlpanels/Controlpanel.test.jsx +1 -29
  16. package/src/components/manage/Form/Field.jsx +1 -69
  17. package/src/components/manage/Widgets/ArrayWidget.jsx +111 -88
  18. package/src/components/manage/Widgets/ArrayWidget.test.jsx +0 -6
  19. package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthIndexField.jsx +56 -50
  20. package/src/components/manage/Widgets/SelectStyling.jsx +52 -20
  21. package/src/config/Loadables.jsx +1 -5
  22. package/src/server.jsx +7 -1
  23. package/theme/themes/default/globals/site.variables +3 -3
  24. package/theme/themes/pastanaga/extras/contents.less +0 -4
  25. package/theme/themes/pastanaga/globals/site.variables +0 -3
  26. package/types/components/manage/Contents/Contents.d.ts +1 -1
  27. package/types/components/manage/Contents/ContentsIndexHeader.d.ts +6 -11
  28. package/types/components/manage/Contents/ContentsItem.d.ts +3 -10
  29. package/types/components/manage/Controlpanels/ContentType.d.ts +2 -2
  30. package/types/components/manage/Controlpanels/Controlpanel.d.ts +2 -5
  31. package/types/components/manage/Controlpanels/index.d.ts +2 -2
  32. package/types/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthIndexField.d.ts +22 -5
  33. package/types/components/manage/Widgets/SelectStyling.d.ts +1 -0
@@ -90,228 +90,242 @@ export const ContentsItemComponent = ({
90
90
  onDelete,
91
91
  onMoveToTop,
92
92
  onMoveToBottom,
93
- connectDragPreview,
94
- connectDragSource,
95
- connectDropTarget,
96
- isDragging,
97
93
  order,
94
+ dndKitSortable,
95
+ dndKitUtilities,
98
96
  }) => {
99
97
  const intl = useIntl();
98
+ const { useSortable } = dndKitSortable;
99
+ const { CSS } = dndKitUtilities;
100
+ const {
101
+ attributes,
102
+ listeners,
103
+ setNodeRef,
104
+ transform,
105
+ transition,
106
+ isDragging,
107
+ } = useSortable({
108
+ id: item['@id'],
109
+ data: {
110
+ type: 'item',
111
+ id: item['@id'],
112
+ },
113
+ });
100
114
 
101
- return connectDropTarget(
102
- connectDragPreview(
103
- <tr
104
- key={item['@id']}
105
- className={cx('', { 'dragging-row': isDragging })}
106
- aria-label={item['@id']}
107
- >
108
- <Table.Cell className={cx('', { 'dragging-cell': isDragging })}>
109
- {connectDragSource(
110
- <div style={{ display: 'inline-block' }}>
111
- <Button icon basic>
112
- <Icon
113
- name={dragSVG}
114
- size="20px"
115
- color="#878f93"
116
- className="content drag handle"
117
- />
115
+ const style = {
116
+ transform: CSS.Transform.toString(transform),
117
+ transition,
118
+ };
119
+
120
+ return (
121
+ <tr
122
+ key={item['@id']}
123
+ ref={setNodeRef}
124
+ style={style}
125
+ className={cx('', { 'dragging-row': isDragging })}
126
+ aria-label={item['@id']}
127
+ >
128
+ <Table.Cell className={cx('', { 'dragging-cell': isDragging })}>
129
+ <div style={{ display: 'inline-block' }}>
130
+ <Button icon basic {...attributes} {...listeners}>
131
+ <Icon
132
+ name={dragSVG}
133
+ size="20px"
134
+ color="#878f93"
135
+ className="content drag handle"
136
+ />
137
+ </Button>
138
+ </div>
139
+ </Table.Cell>
140
+ <Table.Cell className={cx('', { 'dragging-cell': isDragging })}>
141
+ {selected ? (
142
+ <Button
143
+ icon
144
+ basic
145
+ aria-label="Unchecked"
146
+ onClick={(e) => onClick(e, item['@id'])}
147
+ >
148
+ <Icon
149
+ name={checkboxCheckedSVG}
150
+ color="#007eb1"
151
+ size="24px"
152
+ className="checked"
153
+ />
154
+ </Button>
155
+ ) : (
156
+ <Button
157
+ icon
158
+ basic
159
+ aria-label="Checked"
160
+ onClick={(e) => onClick(e, item['@id'])}
161
+ >
162
+ <Icon
163
+ name={checkboxUncheckedSVG}
164
+ color="#826a6a"
165
+ size="24px"
166
+ className="unchecked"
167
+ />
168
+ </Button>
169
+ )}
170
+ </Table.Cell>
171
+ <Table.Cell className={cx('', { 'dragging-cell': isDragging })}>
172
+ <Link
173
+ className="icon-align-name"
174
+ to={`${item['@id']}${item.is_folderish ? '/contents' : ''}`}
175
+ >
176
+ <div className="expire-align">
177
+ <Icon
178
+ name={getContentIcon(item['@type'], item.is_folderish)}
179
+ size="20px"
180
+ className="icon-margin"
181
+ color="#878f93"
182
+ title={item['Type'] || item['@type']}
183
+ />{' '}
184
+ <span title={item.title}> {item.title}</span>
185
+ </div>
186
+ {item.ExpirationDate !== 'None' &&
187
+ new Date(item.ExpirationDate).getTime() < new Date().getTime() && (
188
+ <Button className="button-margin expired-past" size="mini">
189
+ <FormattedMessage id="Expired" defaultMessage="Expired" />
118
190
  </Button>
119
- </div>,
120
- )}
121
- </Table.Cell>
122
- <Table.Cell className={cx('', { 'dragging-cell': isDragging })}>
123
- {selected ? (
124
- <Button
125
- icon
126
- basic
127
- aria-label="Unchecked"
128
- onClick={(e) => onClick(e, item['@id'])}
129
- >
130
- <Icon
131
- name={checkboxCheckedSVG}
132
- color="#007eb1"
133
- size="24px"
134
- className="checked"
135
- />
136
- </Button>
137
- ) : (
138
- <Button
139
- icon
140
- basic
141
- aria-label="Checked"
142
- onClick={(e) => onClick(e, item['@id'])}
143
- >
144
- <Icon
145
- name={checkboxUncheckedSVG}
146
- color="#826a6a"
147
- size="24px"
148
- className="unchecked"
191
+ )}
192
+ {item.EffectiveDate !== 'None' &&
193
+ new Date(item.EffectiveDate).getTime() > new Date().getTime() && (
194
+ <Button className="button-margin effective-future" size="mini">
195
+ <FormattedMessage id="Scheduled" defaultMessage="Scheduled" />
196
+ </Button>
197
+ )}
198
+ {item.is_working_copy && (
199
+ <Button className="button-margin working-copy" size="mini">
200
+ <FormattedMessage
201
+ id="Working copy"
202
+ defaultMessage="Working copy"
149
203
  />
150
204
  </Button>
151
205
  )}
152
- </Table.Cell>
153
- <Table.Cell className={cx('', { 'dragging-cell': isDragging })}>
154
- <Link
155
- className="icon-align-name"
156
- to={`${item['@id']}${item.is_folderish ? '/contents' : ''}`}
157
- >
158
- <div className="expire-align">
159
- <Icon
160
- name={getContentIcon(item['@type'], item.is_folderish)}
161
- size="20px"
162
- className="icon-margin"
163
- color="#878f93"
164
- title={item['Type'] || item['@type']}
165
- />{' '}
166
- <span title={item.title}> {item.title}</span>
206
+ </Link>
207
+ </Table.Cell>
208
+ {map(indexes, (index) => (
209
+ <Table.Cell
210
+ className={cx('', { 'dragging-cell': isDragging })}
211
+ key={index.id}
212
+ >
213
+ {index.type === 'boolean' &&
214
+ (item[index.id] ? (
215
+ <FormattedMessage id="Yes" defaultMessage="Yes" />
216
+ ) : (
217
+ <FormattedMessage id="No" defaultMessage="No" />
218
+ ))}
219
+ {index.type === 'string' &&
220
+ index.id !== 'review_state' &&
221
+ item[index.id]}
222
+ {index.id === 'review_state' && (
223
+ <div>
224
+ <span>
225
+ <Circle color={getColor(item[index.id])} size="15px" />
226
+ </span>
227
+ {messages[item[index.id]]
228
+ ? intl.formatMessage(messages[item[index.id]])
229
+ : item['review_title'] ||
230
+ item['review_state'] ||
231
+ intl.formatMessage(messages.no_workflow_state)}
167
232
  </div>
168
- {item.ExpirationDate !== 'None' &&
169
- new Date(item.ExpirationDate).getTime() <
170
- new Date().getTime() && (
171
- <Button className="button-margin expired-past" size="mini">
172
- <FormattedMessage id="Expired" defaultMessage="Expired" />
173
- </Button>
174
- )}
175
- {item.EffectiveDate !== 'None' &&
176
- new Date(item.EffectiveDate).getTime() > new Date().getTime() && (
177
- <Button className="button-margin effective-future" size="mini">
178
- <FormattedMessage id="Scheduled" defaultMessage="Scheduled" />
179
- </Button>
233
+ )}
234
+ {index.type === 'date' && (
235
+ <>
236
+ {item[index?.id] && item[index.id] !== 'None' ? (
237
+ <FormattedDate date={item[index.id]} />
238
+ ) : (
239
+ intl.formatMessage(messages.none)
180
240
  )}
181
- {item.is_working_copy && (
182
- <Button className="button-margin working-copy" size="mini">
183
- <FormattedMessage
184
- id="Working copy"
185
- defaultMessage="Working copy"
186
- />
187
- </Button>
188
- )}
189
- </Link>
241
+ </>
242
+ )}
243
+ {index.type === 'array' && <span>{item[index.id]?.join(', ')}</span>}
190
244
  </Table.Cell>
191
- {map(indexes, (index) => (
192
- <Table.Cell
193
- className={cx('', { 'dragging-cell': isDragging })}
194
- key={index.id}
195
- >
196
- {index.type === 'boolean' &&
197
- (item[index.id] ? (
198
- <FormattedMessage id="Yes" defaultMessage="Yes" />
199
- ) : (
200
- <FormattedMessage id="No" defaultMessage="No" />
201
- ))}
202
- {index.type === 'string' &&
203
- index.id !== 'review_state' &&
204
- item[index.id]}
205
- {index.id === 'review_state' && (
206
- <div>
207
- <span>
208
- <Circle color={getColor(item[index.id])} size="15px" />
209
- </span>
210
- {messages[item[index.id]]
211
- ? intl.formatMessage(messages[item[index.id]])
212
- : item['review_title'] ||
213
- item['review_state'] ||
214
- intl.formatMessage(messages.no_workflow_state)}
215
- </div>
216
- )}
217
- {index.type === 'date' && (
218
- <>
219
- {item[index?.id] && item[index.id] !== 'None' ? (
220
- <FormattedDate date={item[index.id]} />
221
- ) : (
222
- intl.formatMessage(messages.none)
223
- )}
224
- </>
225
- )}
226
- {index.type === 'array' && (
227
- <span>{item[index.id]?.join(', ')}</span>
228
- )}
229
- </Table.Cell>
230
- ))}
231
- <Table.Cell
232
- className={cx('', { 'dragging-cell': isDragging })}
233
- textAlign="right"
245
+ ))}
246
+ <Table.Cell
247
+ className={cx('', { 'dragging-cell': isDragging })}
248
+ textAlign="right"
249
+ >
250
+ <Popup
251
+ menu={true}
252
+ position="bottom right"
253
+ flowing={true}
254
+ basic={true}
255
+ on="click"
256
+ popper={{
257
+ className: 'dropdown-popup',
258
+ }}
259
+ trigger={
260
+ <Icon
261
+ name={moreSVG}
262
+ className="dropdown-popup-trigger"
263
+ size="24px"
264
+ color="#007eb1"
265
+ />
266
+ }
234
267
  >
235
- <Popup
236
- menu={true}
237
- position="bottom right"
238
- flowing={true}
239
- basic={true}
240
- on="click"
241
- popper={{
242
- className: 'dropdown-popup',
243
- }}
244
- trigger={
245
- <Icon
246
- name={moreSVG}
247
- className="dropdown-popup-trigger"
248
- size="24px"
249
- color="#007eb1"
268
+ <Menu vertical borderless fluid>
269
+ <Link className="item icon-align" to={`${item['@id']}/edit`}>
270
+ <Icon name={editingSVG} color="#007eb1" size="24px" />{' '}
271
+ <FormattedMessage id="Edit" defaultMessage="Edit" />
272
+ </Link>
273
+ <Link className="item right-dropdown icon-align" to={item['@id']}>
274
+ <Icon name={showSVG} color="#007eb1" size="24px" />{' '}
275
+ <FormattedMessage id="View" defaultMessage="View" />
276
+ </Link>
277
+ <Divider />
278
+ <Menu.Item
279
+ onClick={onCut}
280
+ value={item['@id']}
281
+ className="right-dropdown icon-align"
282
+ >
283
+ <Icon name={cutSVG} color="#007eb1" size="24px" />{' '}
284
+ <FormattedMessage id="Cut" defaultMessage="Cut" />
285
+ </Menu.Item>
286
+ <Menu.Item
287
+ onClick={onCopy}
288
+ value={item['@id']}
289
+ className="right-dropdown icon-align"
290
+ >
291
+ <Icon name={copySVG} color="#007eb1" size="24px" />{' '}
292
+ <FormattedMessage id="Copy" defaultMessage="Copy" />
293
+ </Menu.Item>
294
+ <Menu.Item
295
+ onClick={onDelete}
296
+ value={item['@id']}
297
+ className="right-dropdown icon-align"
298
+ >
299
+ <Icon name={deleteSVG} color="#e40166" size="24px" />{' '}
300
+ <FormattedMessage id="Delete" defaultMessage="Delete" />
301
+ </Menu.Item>
302
+ <Divider />
303
+ <Menu.Item
304
+ onClick={onMoveToTop}
305
+ value={order}
306
+ className="right-dropdown icon-align"
307
+ >
308
+ <Icon name={moveUpSVG} color="#007eb1" size="24px" />{' '}
309
+ <FormattedMessage
310
+ id="Move to top of folder"
311
+ defaultMessage="Move to top of folder"
250
312
  />
251
- }
252
- >
253
- <Menu vertical borderless fluid>
254
- <Link className="item icon-align" to={`${item['@id']}/edit`}>
255
- <Icon name={editingSVG} color="#007eb1" size="24px" />{' '}
256
- <FormattedMessage id="Edit" defaultMessage="Edit" />
257
- </Link>
258
- <Link className="item right-dropdown icon-align" to={item['@id']}>
259
- <Icon name={showSVG} color="#007eb1" size="24px" />{' '}
260
- <FormattedMessage id="View" defaultMessage="View" />
261
- </Link>
262
- <Divider />
263
- <Menu.Item
264
- onClick={onCut}
265
- value={item['@id']}
266
- className="right-dropdown icon-align"
267
- >
268
- <Icon name={cutSVG} color="#007eb1" size="24px" />{' '}
269
- <FormattedMessage id="Cut" defaultMessage="Cut" />
270
- </Menu.Item>
271
- <Menu.Item
272
- onClick={onCopy}
273
- value={item['@id']}
274
- className="right-dropdown icon-align"
275
- >
276
- <Icon name={copySVG} color="#007eb1" size="24px" />{' '}
277
- <FormattedMessage id="Copy" defaultMessage="Copy" />
278
- </Menu.Item>
279
- <Menu.Item
280
- onClick={onDelete}
281
- value={item['@id']}
282
- className="right-dropdown icon-align"
283
- >
284
- <Icon name={deleteSVG} color="#e40166" size="24px" />{' '}
285
- <FormattedMessage id="Delete" defaultMessage="Delete" />
286
- </Menu.Item>
287
- <Divider />
288
- <Menu.Item
289
- onClick={onMoveToTop}
290
- value={order}
291
- className="right-dropdown icon-align"
292
- >
293
- <Icon name={moveUpSVG} color="#007eb1" size="24px" />{' '}
294
- <FormattedMessage
295
- id="Move to top of folder"
296
- defaultMessage="Move to top of folder"
297
- />
298
- </Menu.Item>
299
- <Menu.Item
300
- onClick={onMoveToBottom}
301
- value={order}
302
- className="right-dropdown icon-align"
303
- >
304
- <Icon name={moveDownSVG} color="#007eb1" size="24px" />{' '}
305
- <FormattedMessage
306
- id="Move to bottom of folder"
307
- defaultMessage="Move to bottom of folder"
308
- />
309
- </Menu.Item>
310
- </Menu>
311
- </Popup>
312
- </Table.Cell>
313
- </tr>,
314
- ),
313
+ </Menu.Item>
314
+ <Menu.Item
315
+ onClick={onMoveToBottom}
316
+ value={order}
317
+ className="right-dropdown icon-align"
318
+ >
319
+ <Icon name={moveDownSVG} color="#007eb1" size="24px" />{' '}
320
+ <FormattedMessage
321
+ id="Move to bottom of folder"
322
+ defaultMessage="Move to bottom of folder"
323
+ />
324
+ </Menu.Item>
325
+ </Menu>
326
+ </Popup>
327
+ </Table.Cell>
328
+ </tr>
315
329
  );
316
330
  };
317
331
 
@@ -340,75 +354,9 @@ ContentsItemComponent.propTypes = {
340
354
  onDelete: PropTypes.func.isRequired,
341
355
  onMoveToTop: PropTypes.func.isRequired,
342
356
  onMoveToBottom: PropTypes.func.isRequired,
343
- connectDragPreview: PropTypes.func.isRequired,
344
- connectDragSource: PropTypes.func.isRequired,
345
- connectDropTarget: PropTypes.func.isRequired,
346
- isDragging: PropTypes.bool.isRequired,
347
357
  order: PropTypes.number.isRequired,
348
- onOrderItem: PropTypes.func.isRequired,
349
- };
350
-
351
- const DragDropConnector = (props) => {
352
- const { DropTarget, DragSource } = props.reactDnd;
353
-
354
- const DndConnectedContentsItem = React.useMemo(
355
- () =>
356
- DropTarget(
357
- 'item',
358
- {
359
- hover(props, monitor) {
360
- const id = monitor.getItem().id;
361
- const dragOrder = monitor.getItem().order;
362
- const hoverOrder = props.order;
363
-
364
- if (dragOrder === hoverOrder) {
365
- return;
366
- }
367
-
368
- props.onOrderItem(id, dragOrder, hoverOrder - dragOrder, false);
369
-
370
- monitor.getItem().order = hoverOrder;
371
- },
372
- drop(props, monitor) {
373
- const id = monitor.getItem().id;
374
- const dragOrder = monitor.getItem().startOrder;
375
- const dropOrder = props.order;
376
-
377
- if (dragOrder === dropOrder) {
378
- return;
379
- }
380
-
381
- props.onOrderItem(id, dragOrder, dropOrder - dragOrder, true);
382
-
383
- monitor.getItem().order = dropOrder;
384
- },
385
- },
386
- (connect) => ({
387
- connectDropTarget: connect.dropTarget(),
388
- }),
389
- )(
390
- DragSource(
391
- 'item',
392
- {
393
- beginDrag(props) {
394
- return {
395
- id: props.item['@id'],
396
- order: props.order,
397
- startOrder: props.order,
398
- };
399
- },
400
- },
401
- (connect, monitor) => ({
402
- connectDragSource: connect.dragSource(),
403
- connectDragPreview: connect.dragPreview(),
404
- isDragging: monitor.isDragging(),
405
- }),
406
- )(ContentsItemComponent),
407
- ),
408
- [DragSource, DropTarget],
409
- );
410
-
411
- return <DndConnectedContentsItem {...props} />;
412
358
  };
413
359
 
414
- export default injectLazyLibs('reactDnd')(DragDropConnector);
360
+ export default injectLazyLibs(['dndKitSortable', 'dndKitUtilities'])(
361
+ ContentsItemComponent,
362
+ );
@@ -4,10 +4,19 @@ import configureStore from 'redux-mock-store';
4
4
  import { Provider } from 'react-intl-redux';
5
5
  import { MemoryRouter } from 'react-router-dom';
6
6
 
7
- import { ContentsItemComponent as ContentsItem } from './ContentsItem';
7
+ import ContentsItem from './ContentsItem';
8
8
 
9
9
  const mockStore = configureStore();
10
10
 
11
+ vi.mock('@plone/volto/helpers/Loadable/Loadable');
12
+
13
+ beforeAll(async () => {
14
+ const { __setLoadables } = await import(
15
+ '@plone/volto/helpers/Loadable/Loadable'
16
+ );
17
+ await __setLoadables();
18
+ });
19
+
11
20
  describe('ContentsItem', () => {
12
21
  it('renders a contents item component', () => {
13
22
  const store = mockStore({
@@ -40,12 +49,7 @@ describe('ContentsItem', () => {
40
49
  onDelete={() => {}}
41
50
  onMoveToTop={() => {}}
42
51
  onMoveToBottom={() => {}}
43
- onOrderItem={() => {}}
44
- connectDragSource={(x) => x}
45
- connectDragPreview={(x) => x}
46
- connectDropTarget={(x) => x}
47
52
  order={1}
48
- isDragging={false}
49
53
  />
50
54
  </MemoryRouter>
51
55
  </Provider>,