@strapi/admin 4.11.0-exp.9xg4-3qfm-9w8f.1 → 4.11.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 (112) hide show
  1. package/admin/src/components/Providers/index.js +32 -32
  2. package/admin/src/components/Theme/index.js +5 -3
  3. package/admin/src/content-manager/components/ComponentIcon/ComponentIcon.js +16 -26
  4. package/admin/src/content-manager/components/ComponentIcon/constants.js +133 -0
  5. package/admin/src/content-manager/components/DynamicTable/BulkActionsBar/index.js +307 -0
  6. package/admin/src/content-manager/components/DynamicTable/index.js +20 -4
  7. package/admin/src/content-manager/components/DynamicZone/components/AddComponentButton.js +32 -95
  8. package/admin/src/content-manager/components/DynamicZone/components/ComponentCard.js +10 -2
  9. package/admin/src/content-manager/components/DynamicZone/components/ComponentCategory.js +63 -15
  10. package/admin/src/content-manager/components/DynamicZone/components/ComponentPicker.js +50 -63
  11. package/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +132 -58
  12. package/admin/src/content-manager/components/DynamicZone/components/DynamicZoneLabel.js +29 -37
  13. package/admin/src/content-manager/components/DynamicZone/index.js +131 -83
  14. package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +12 -4
  15. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +18 -6
  16. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/index.js +0 -1
  17. package/admin/src/content-manager/components/Inputs/index.js +18 -11
  18. package/admin/src/content-manager/components/Inputs/utils/index.js +0 -1
  19. package/admin/src/content-manager/pages/EditSettingsView/components/DynamicZoneList.js +7 -1
  20. package/admin/src/content-manager/pages/EditView/index.js +1 -1
  21. package/admin/src/content-manager/pages/ListView/index.js +118 -2
  22. package/admin/src/content-manager/utils/index.js +2 -0
  23. package/admin/src/content-manager/{components/EditViewDataManagerProvider/utils → utils}/schema.js +1 -1
  24. package/admin/src/injectionZones.js +6 -1
  25. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventTable/EventTableCE.js +13 -0
  26. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventTable/index.js +3 -0
  27. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/Events/index.js +331 -0
  28. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/HeadersInput/Combobox.js +54 -4
  29. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/HeadersInput/index.js +12 -23
  30. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/WebhookForm/index.js +129 -116
  31. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/WebhookForm/utils/makeWebhookValidationSchema.js +62 -0
  32. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/index.js +59 -64
  33. package/admin/src/translations/en.json +11 -1
  34. package/build/3562.e0b1a0b3.chunk.js +50 -0
  35. package/build/371.6e4e2c1f.chunk.js +71 -0
  36. package/build/5297.31c9ffdc.chunk.js +39 -0
  37. package/build/{5563.79950369.chunk.js → 5563.badbffde.chunk.js} +2 -2
  38. package/build/{3081.7e9329cb.chunk.js → 6691.f7a3b5ac.chunk.js} +2 -2
  39. package/build/6970.d456705f.chunk.js +1 -0
  40. package/build/{7259.5cc67413.chunk.js → 7259.5d0de931.chunk.js} +1 -1
  41. package/build/8976.0022f5a3.chunk.js +50 -0
  42. package/build/{1657.3f2b2c11.chunk.js → 9932.5ef475c5.chunk.js} +54 -54
  43. package/build/Admin-authenticatedApp.ab2b5910.chunk.js +79 -0
  44. package/build/{Admin_InternalErrorPage.96ceaae1.chunk.js → Admin_InternalErrorPage.f25f04f3.chunk.js} +1 -1
  45. package/build/{Admin_homePage.94dc81b1.chunk.js → Admin_homePage.05063e43.chunk.js} +16 -16
  46. package/build/{Admin_marketplace.1b0c3d3b.chunk.js → Admin_marketplace.005d2d5b.chunk.js} +11 -11
  47. package/build/{Admin_pluginsPage.a28b96d5.chunk.js → Admin_pluginsPage.5d9d4060.chunk.js} +1 -1
  48. package/build/{Admin_profilePage.a8fa3a56.chunk.js → Admin_profilePage.ab7b94d7.chunk.js} +2 -2
  49. package/build/{Admin_settingsPage.ee76d19e.chunk.js → Admin_settingsPage.07a6a5f0.chunk.js} +2 -2
  50. package/build/{Upload_ConfigureTheView.aa64ed9a.chunk.js → Upload_ConfigureTheView.121deffb.chunk.js} +1 -1
  51. package/build/{admin-app.bd209f08.chunk.js → admin-app.7cc667be.chunk.js} +10 -10
  52. package/build/{admin-edit-roles-page.0d12b741.chunk.js → admin-edit-roles-page.bfe3304d.chunk.js} +3 -3
  53. package/build/{admin-edit-users.f9ce7844.chunk.js → admin-edit-users.6efe0382.chunk.js} +2 -2
  54. package/build/{admin-roles-list.af53b372.chunk.js → admin-roles-list.b2577370.chunk.js} +1 -1
  55. package/build/{admin-users.0fc77b35.chunk.js → admin-users.4af49ccf.chunk.js} +2 -2
  56. package/build/{api-tokens-create-page.973d2816.chunk.js → api-tokens-create-page.65411a36.chunk.js} +1 -1
  57. package/build/{api-tokens-edit-page.29725c5e.chunk.js → api-tokens-edit-page.60312cb6.chunk.js} +1 -1
  58. package/build/{api-tokens-list-page.66c4fbdd.chunk.js → api-tokens-list-page.01a4d5cd.chunk.js} +2 -2
  59. package/build/audit-logs-settings-page.b165679b.chunk.js +16 -0
  60. package/build/content-manager.8cc6c3f9.chunk.js +1094 -0
  61. package/build/content-type-builder-list-view.58f9ed20.chunk.js +211 -0
  62. package/build/{content-type-builder-translation-en-json.af293c9e.chunk.js → content-type-builder-translation-en-json.f592325b.chunk.js} +1 -1
  63. package/build/content-type-builder.baeb0413.chunk.js +132 -0
  64. package/build/{email-settings-page.63f269ff.chunk.js → email-settings-page.8caad83f.chunk.js} +1 -1
  65. package/build/en-json.a8f34002.chunk.js +1 -0
  66. package/build/i18n-settings-page.579d5eab.chunk.js +9 -0
  67. package/build/i18n-translation-en-json.1ec7becf.chunk.js +1 -0
  68. package/build/index.html +1 -1
  69. package/build/main.1d678f7b.js +2926 -0
  70. package/build/{review-workflows-settings.56cab253.chunk.js → review-workflows-settings.3a7bae25.chunk.js} +2 -2
  71. package/build/{runtime~main.0dfc909e.js → runtime~main.9dbd4553.js} +2 -2
  72. package/build/{sso-settings-page.265e3d72.chunk.js → sso-settings-page.4bb073e0.chunk.js} +1 -1
  73. package/build/{transfer-tokens-create-page.170acee6.chunk.js → transfer-tokens-create-page.9ec277d7.chunk.js} +1 -1
  74. package/build/{transfer-tokens-edit-page.6cf23295.chunk.js → transfer-tokens-edit-page.fa5ade14.chunk.js} +1 -1
  75. package/build/{transfer-tokens-list-page.c3fec4c1.chunk.js → transfer-tokens-list-page.5d68d590.chunk.js} +2 -2
  76. package/build/{upload-settings.1d187578.chunk.js → upload-settings.2c1565d6.chunk.js} +1 -1
  77. package/build/{upload.bc340679.chunk.js → upload.257b2aef.chunk.js} +1 -1
  78. package/build/{users-advanced-settings-page.7b4bf63a.chunk.js → users-advanced-settings-page.ddd29040.chunk.js} +1 -1
  79. package/build/{users-email-settings-page.035a026c.chunk.js → users-email-settings-page.717e2a51.chunk.js} +1 -1
  80. package/build/{users-providers-settings-page.6873dce9.chunk.js → users-providers-settings-page.c7eae829.chunk.js} +1 -1
  81. package/build/{users-roles-settings-page.2549794b.chunk.js → users-roles-settings-page.63cf2838.chunk.js} +1 -1
  82. package/build/webhook-edit-page.8576742e.chunk.js +31 -0
  83. package/build/{webhook-list-page.0861d3e9.chunk.js → webhook-list-page.abf5747c.chunk.js} +1 -1
  84. package/ee/admin/pages/SettingsPage/pages/Webhooks/EditView/components/EventTable/EventTableEE.js +23 -0
  85. package/ee/admin/pages/SettingsPage/pages/Webhooks/EditView/components/EventTable/index.js +3 -0
  86. package/package.json +13 -13
  87. package/admin/src/content-manager/components/DynamicTable/ConfirmDialogDeleteAll/index.js +0 -73
  88. package/admin/src/content-manager/components/DynamicZone/utils/connect.js +0 -12
  89. package/admin/src/content-manager/components/DynamicZone/utils/select.js +0 -53
  90. package/admin/src/content-manager/components/Inputs/utils/getStep.js +0 -13
  91. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventInput/EventRow.js +0 -70
  92. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventInput/index.js +0 -174
  93. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/HeadersInput/keys.js +0 -39
  94. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/utils/fieldsRegex.js +0 -4
  95. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/utils/schema.js +0 -35
  96. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/reducer.js +0 -100
  97. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/utils/formatData.js +0 -20
  98. package/build/462.6f8cbd19.chunk.js +0 -71
  99. package/build/617.0518c0ba.chunk.js +0 -155
  100. package/build/6858.85d76858.chunk.js +0 -50
  101. package/build/6970.6a329e15.chunk.js +0 -1
  102. package/build/9036.f7ce35cc.chunk.js +0 -211
  103. package/build/Admin-authenticatedApp.e7ca2959.chunk.js +0 -79
  104. package/build/audit-logs-settings-page.6bc76e7d.chunk.js +0 -121
  105. package/build/content-manager.8bfce7f0.chunk.js +0 -1123
  106. package/build/content-type-builder-list-view.26aab6f3.chunk.js +0 -215
  107. package/build/content-type-builder.b10576e7.chunk.js +0 -126
  108. package/build/en-json.ba3290b8.chunk.js +0 -1
  109. package/build/i18n-settings-page.2ac4ca58.chunk.js +0 -114
  110. package/build/i18n-translation-en-json.60af6722.chunk.js +0 -1
  111. package/build/main.aca47de6.js +0 -2633
  112. package/build/webhook-edit-page.0bc97587.chunk.js +0 -128
@@ -12,67 +12,19 @@ import {
12
12
  IconButton,
13
13
  Box,
14
14
  Flex,
15
+ VisuallyHidden,
15
16
  } from '@strapi/design-system';
17
+ import { Menu, MenuItem } from '@strapi/design-system/v2';
16
18
  import { useCMEditViewDataManager } from '@strapi/helper-plugin';
17
- import { Trash, Drag } from '@strapi/icons';
19
+ import { Trash, Drag, More } from '@strapi/icons';
18
20
 
19
21
  import { useContentTypeLayout, useDragAndDrop } from '../../../hooks';
20
22
  import { composeRefs, getTrad, ItemTypes } from '../../../utils';
21
23
 
22
24
  import FieldComponent from '../../FieldComponent';
25
+ import { ComponentIcon } from '../../ComponentIcon';
23
26
 
24
- const ActionsFlex = styled(Flex)`
25
- /*
26
- we need to remove the background from the button but we can't
27
- wrap the element in styled because it breaks the forwardedAs which
28
- we need for drag handler to work on firefox
29
- */
30
- div[role='button'] {
31
- background: transparent;
32
- }
33
- `;
34
-
35
- const IconButtonCustom = styled(IconButton)`
36
- background-color: transparent;
37
-
38
- svg path {
39
- fill: ${({ theme, expanded }) =>
40
- expanded ? theme.colors.primary600 : theme.colors.neutral600};
41
- }
42
- `;
43
-
44
- // TODO: Delete once https://github.com/strapi/design-system/pull/858
45
- // is merged and released.
46
- const StyledBox = styled(Box)`
47
- > div:first-child {
48
- box-shadow: ${({ theme }) => theme.shadows.tableShadow};
49
- }
50
- `;
51
-
52
- const AccordionContentRadius = styled(Box)`
53
- border-radius: 0 0 ${({ theme }) => theme.spaces[1]} ${({ theme }) => theme.spaces[1]};
54
- `;
55
-
56
- const Rectangle = styled(Box)`
57
- width: ${({ theme }) => theme.spaces[2]};
58
- height: ${({ theme }) => theme.spaces[4]};
59
- `;
60
-
61
- const Preview = styled.span`
62
- display: block;
63
- background-color: ${({ theme }) => theme.colors.primary100};
64
- outline: 1px dashed ${({ theme }) => theme.colors.primary500};
65
- outline-offset: -1px;
66
- padding: ${({ theme }) => theme.spaces[6]};
67
- `;
68
-
69
- const ComponentContainer = styled(Box)`
70
- list-style: none;
71
- padding: 0;
72
- margin: 0;
73
- `;
74
-
75
- const DynamicZoneComponent = ({
27
+ export const DynamicComponent = ({
76
28
  componentUid,
77
29
  formErrors,
78
30
  index,
@@ -83,6 +35,8 @@ const DynamicZoneComponent = ({
83
35
  onGrabItem,
84
36
  onDropItem,
85
37
  onCancel,
38
+ dynamicComponentsByCategory,
39
+ onAddComponent,
86
40
  }) => {
87
41
  const [isOpen, setIsOpen] = useState(true);
88
42
  const { formatMessage } = useIntl();
@@ -180,11 +134,70 @@ const DynamicZoneComponent = ({
180
134
  >
181
135
  <Drag />
182
136
  </IconButton>
137
+ <Menu.Root>
138
+ <Menu.Trigger size="S" endIcon={undefined} paddingLeft={2} paddingRight={2}>
139
+ <More aria-hidden focusable={false} />
140
+ <VisuallyHidden as="span">
141
+ {formatMessage({
142
+ id: getTrad('components.DynamicZone.more-actions'),
143
+ defaultMessage: 'More actions',
144
+ })}
145
+ </VisuallyHidden>
146
+ </Menu.Trigger>
147
+ <Menu.Content>
148
+ <Menu.SubRoot>
149
+ <Menu.SubTrigger>
150
+ {formatMessage({
151
+ id: getTrad('components.DynamicZone.add-item-above'),
152
+ defaultMessage: 'Add component above',
153
+ })}
154
+ </Menu.SubTrigger>
155
+ <Menu.SubContent>
156
+ {Object.entries(dynamicComponentsByCategory).map(([category, components]) => (
157
+ <React.Fragment key={category}>
158
+ <Menu.Label>{category}</Menu.Label>
159
+ {components.map(({ componentUid, info: { displayName } }) => (
160
+ <MenuItem
161
+ key={componentUid}
162
+ onSelect={() => onAddComponent(componentUid, index)}
163
+ >
164
+ {displayName}
165
+ </MenuItem>
166
+ ))}
167
+ </React.Fragment>
168
+ ))}
169
+ </Menu.SubContent>
170
+ </Menu.SubRoot>
171
+ <Menu.SubRoot>
172
+ <Menu.SubTrigger>
173
+ {formatMessage({
174
+ id: getTrad('components.DynamicZone.add-item-below'),
175
+ defaultMessage: 'Add component below',
176
+ })}
177
+ </Menu.SubTrigger>
178
+ <Menu.SubContent>
179
+ {Object.entries(dynamicComponentsByCategory).map(([category, components]) => (
180
+ <React.Fragment key={category}>
181
+ <Menu.Label>{category}</Menu.Label>
182
+ {components.map(({ componentUid, info: { displayName } }) => (
183
+ <MenuItem
184
+ key={componentUid}
185
+ onSelect={() => onAddComponent(componentUid, index + 1)}
186
+ >
187
+ {displayName}
188
+ </MenuItem>
189
+ ))}
190
+ </React.Fragment>
191
+ ))}
192
+ </Menu.SubContent>
193
+ </Menu.SubRoot>
194
+ </Menu.Content>
195
+ </Menu.Root>
183
196
  </ActionsFlex>
184
197
  );
185
198
 
186
199
  return (
187
- <ComponentContainer as="li">
200
+ <ComponentContainer as="li" width="100%">
188
201
  <Flex justifyContent="center">
189
202
  <Rectangle background="neutral200" />
190
203
  </Flex>
@@ -194,6 +207,7 @@ const DynamicZoneComponent = ({
194
207
  ) : (
195
208
  <Accordion expanded={isOpen} onToggle={handleToggle} size="S" error={errorMessage}>
196
209
  <AccordionToggle
210
+ startIcon={<ComponentIcon icon={icon} showBackground={false} size="S" />}
197
211
  action={accordionActions}
198
212
  title={`${friendlyName}${mainValue}`}
199
213
  togglePosition="left"
@@ -215,26 +229,86 @@ const DynamicZoneComponent = ({
215
229
  );
216
230
  };
217
231
 
218
- DynamicZoneComponent.defaultProps = {
232
+ const ActionsFlex = styled(Flex)`
233
+ /*
234
+ we need to remove the background from the button but we can't
235
+ wrap the element in styled because it breaks the forwardedAs which
236
+ we need for drag handler to work on firefox
237
+ */
238
+ div[role='button'] {
239
+ background: transparent;
240
+ }
241
+ `;
242
+
243
+ const IconButtonCustom = styled(IconButton)`
244
+ background-color: transparent;
245
+
246
+ svg path {
247
+ fill: ${({ theme, expanded }) =>
248
+ expanded ? theme.colors.primary600 : theme.colors.neutral600};
249
+ }
250
+ `;
251
+
252
+ // TODO: Delete once https://github.com/strapi/design-system/pull/858
253
+ // is merged and released.
254
+ const StyledBox = styled(Box)`
255
+ > div:first-child {
256
+ box-shadow: ${({ theme }) => theme.shadows.tableShadow};
257
+ }
258
+ `;
259
+
260
+ const AccordionContentRadius = styled(Box)`
261
+ border-radius: 0 0 ${({ theme }) => theme.spaces[1]} ${({ theme }) => theme.spaces[1]};
262
+ `;
263
+
264
+ const Rectangle = styled(Box)`
265
+ width: ${({ theme }) => theme.spaces[2]};
266
+ height: ${({ theme }) => theme.spaces[4]};
267
+ `;
268
+
269
+ const Preview = styled.span`
270
+ display: block;
271
+ background-color: ${({ theme }) => theme.colors.primary100};
272
+ outline: 1px dashed ${({ theme }) => theme.colors.primary500};
273
+ outline-offset: -1px;
274
+ padding: ${({ theme }) => theme.spaces[6]};
275
+ `;
276
+
277
+ const ComponentContainer = styled(Box)`
278
+ list-style: none;
279
+ padding: 0;
280
+ margin: 0;
281
+ `;
282
+
283
+ DynamicComponent.defaultProps = {
284
+ dynamicComponentsByCategory: {},
219
285
  formErrors: {},
220
286
  index: 0,
221
287
  isFieldAllowed: true,
288
+ onAddComponent: undefined,
222
289
  onGrabItem: undefined,
223
290
  onDropItem: undefined,
224
291
  onCancel: undefined,
225
292
  };
226
293
 
227
- DynamicZoneComponent.propTypes = {
294
+ DynamicComponent.propTypes = {
228
295
  componentUid: PropTypes.string.isRequired,
296
+ dynamicComponentsByCategory: PropTypes.shape({
297
+ components: PropTypes.arrayOf(
298
+ PropTypes.shape({
299
+ componentUid: PropTypes.string.isRequired,
300
+ info: PropTypes.object,
301
+ })
302
+ ),
303
+ }),
229
304
  formErrors: PropTypes.object,
230
305
  index: PropTypes.number,
231
306
  isFieldAllowed: PropTypes.bool,
232
307
  name: PropTypes.string.isRequired,
308
+ onAddComponent: PropTypes.func,
233
309
  onGrabItem: PropTypes.func,
234
310
  onDropItem: PropTypes.func,
235
311
  onCancel: PropTypes.func,
236
312
  onMoveComponent: PropTypes.func.isRequired,
237
313
  onRemoveComponentClick: PropTypes.func.isRequired,
238
314
  };
239
-
240
- export default DynamicZoneComponent;
@@ -7,15 +7,10 @@
7
7
  import React from 'react';
8
8
  import { useIntl } from 'react-intl';
9
9
  import PropTypes from 'prop-types';
10
- import styled from 'styled-components';
11
10
  import { pxToRem } from '@strapi/helper-plugin';
12
11
  import { Box, Flex, Typography } from '@strapi/design-system';
13
12
 
14
- const StyledBox = styled(Box)`
15
- border-radius: ${pxToRem(26)};
16
- `;
17
-
18
- const DynamicZoneLabel = ({
13
+ export const DynamicZoneLabel = ({
19
14
  label,
20
15
  labelAction,
21
16
  name,
@@ -28,36 +23,35 @@ const DynamicZoneLabel = ({
28
23
 
29
24
  return (
30
25
  <Flex justifyContent="center">
31
- <Box>
32
- <StyledBox
33
- paddingTop={3}
34
- paddingBottom={3}
35
- paddingRight={4}
36
- paddingLeft={4}
37
- background="neutral0"
38
- shadow="filterShadow"
39
- color="neutral500"
40
- >
41
- <Flex direction="column" justifyContent="center">
42
- <Flex maxWidth={pxToRem(356)}>
43
- <Typography variant="pi" textColor="neutral600" fontWeight="bold" ellipsis>
44
- {intlLabel}&nbsp;
45
- </Typography>
46
- <Typography variant="pi" textColor="neutral600" fontWeight="bold">
47
- ({numberOfComponents})
48
- </Typography>
49
- {required && <Typography textColor="danger600">*</Typography>}
50
- {labelAction && <Box paddingLeft={1}>{labelAction}</Box>}
51
- </Flex>
52
- {intlDescription && (
53
- <Box paddingTop={1} maxWidth={pxToRem(356)}>
54
- <Typography variant="pi" textColor="neutral600" ellipsis>
55
- {formatMessage(intlDescription)}
56
- </Typography>
57
- </Box>
58
- )}
26
+ <Box
27
+ paddingTop={3}
28
+ paddingBottom={3}
29
+ paddingRight={4}
30
+ paddingLeft={4}
31
+ borderRadius={26}
32
+ background="neutral0"
33
+ shadow="filterShadow"
34
+ color="neutral500"
35
+ >
36
+ <Flex direction="column" justifyContent="center">
37
+ <Flex maxWidth={pxToRem(356)}>
38
+ <Typography variant="pi" textColor="neutral600" fontWeight="bold" ellipsis>
39
+ {intlLabel}&nbsp;
40
+ </Typography>
41
+ <Typography variant="pi" textColor="neutral600" fontWeight="bold">
42
+ ({numberOfComponents})
43
+ </Typography>
44
+ {required && <Typography textColor="danger600">*</Typography>}
45
+ {labelAction && <Box paddingLeft={1}>{labelAction}</Box>}
59
46
  </Flex>
60
- </StyledBox>
47
+ {intlDescription && (
48
+ <Box paddingTop={1} maxWidth={pxToRem(356)}>
49
+ <Typography variant="pi" textColor="neutral600" ellipsis>
50
+ {formatMessage(intlDescription)}
51
+ </Typography>
52
+ </Box>
53
+ )}
54
+ </Flex>
61
55
  </Box>
62
56
  </Flex>
63
57
  );
@@ -82,5 +76,3 @@ DynamicZoneLabel.propTypes = {
82
76
  numberOfComponents: PropTypes.number,
83
77
  required: PropTypes.bool,
84
78
  };
85
-
86
- export default DynamicZoneLabel;
@@ -1,76 +1,113 @@
1
- import React, { memo, useMemo, useState } from 'react';
2
- import get from 'lodash/get';
3
- import isEqual from 'lodash/isEqual';
1
+ import React, { useMemo, useState } from 'react';
4
2
  import PropTypes from 'prop-types';
5
3
  import { Box, Flex, VisuallyHidden } from '@strapi/design-system';
6
- import { NotAllowedInput, useNotification } from '@strapi/helper-plugin';
4
+ import { NotAllowedInput, useNotification, useCMEditViewDataManager } from '@strapi/helper-plugin';
7
5
  import { useIntl } from 'react-intl';
8
6
 
9
7
  import { getTrad } from '../../utils';
10
8
 
11
- import connect from './utils/connect';
12
- import select from './utils/select';
13
-
14
- import DynamicZoneComponent from './components/DynamicComponent';
15
- import AddComponentButton from './components/AddComponentButton';
16
- import DynamicZoneLabel from './components/DynamicZoneLabel';
17
- import ComponentPicker from './components/ComponentPicker';
9
+ import { DynamicComponent } from './components/DynamicComponent';
10
+ import { AddComponentButton } from './components/AddComponentButton';
11
+ import { DynamicZoneLabel } from './components/DynamicZoneLabel';
12
+ import { ComponentPicker } from './components/ComponentPicker';
18
13
 
19
14
  import { useContentTypeLayout } from '../../hooks';
20
15
 
21
- const DynamicZone = ({
22
- name,
23
- // Passed with the select function
24
- addComponentToDynamicZone,
25
- formErrors,
26
- isCreatingEntry,
27
- isFieldAllowed,
28
- isFieldReadable,
29
- labelAction,
30
- moveComponentField,
31
- removeComponentFromDynamicZone,
32
- dynamicDisplayedComponents,
33
- fieldSchema,
34
- metadatas,
35
- }) => {
16
+ const DynamicZone = ({ name, labelAction, fieldSchema, metadatas }) => {
17
+ // We cannot use the default props here
18
+ const { max = Infinity, min = -Infinity, components = [], required = false } = fieldSchema;
19
+
36
20
  const [addComponentIsOpen, setAddComponentIsOpen] = useState(false);
37
21
  const [liveText, setLiveText] = useState('');
38
22
 
23
+ const {
24
+ addComponentToDynamicZone,
25
+ createActionAllowedFields,
26
+ isCreatingEntry,
27
+ formErrors,
28
+ modifiedData,
29
+ moveComponentField,
30
+ removeComponentFromDynamicZone,
31
+ readActionAllowedFields,
32
+ updateActionAllowedFields,
33
+ } = useCMEditViewDataManager();
34
+
35
+ const dynamicDisplayedComponents = useMemo(
36
+ () =>
37
+ (modifiedData?.[name] ?? []).map((data) => {
38
+ return {
39
+ componentUid: data.__component,
40
+ id: data.id ?? data.__temp_key__,
41
+ };
42
+ }),
43
+ [modifiedData, name]
44
+ );
45
+
46
+ const { getComponentLayout } = useContentTypeLayout();
47
+
48
+ /**
49
+ * @type {Record<string, Array<{category: string; info: unknown, attributes: Record<string, unknown>}>>}
50
+ */
51
+ const dynamicComponentsByCategory = useMemo(() => {
52
+ return components.reduce((acc, componentUid) => {
53
+ const { category, info, attributes } = getComponentLayout(componentUid);
54
+ const component = { componentUid, info, attributes };
55
+
56
+ if (!acc[category]) {
57
+ acc[category] = [];
58
+ }
59
+
60
+ acc[category] = [...acc[category], component];
61
+
62
+ return acc;
63
+ }, {});
64
+ }, [components, getComponentLayout]);
65
+
39
66
  const { formatMessage } = useIntl();
40
67
 
41
68
  const toggleNotification = useNotification();
42
- const { getComponentLayout, components } = useContentTypeLayout();
69
+
70
+ const isFieldAllowed = useMemo(() => {
71
+ const allowedFields = isCreatingEntry ? createActionAllowedFields : updateActionAllowedFields;
72
+
73
+ return allowedFields.includes(name);
74
+ }, [name, isCreatingEntry, createActionAllowedFields, updateActionAllowedFields]);
75
+
76
+ const isFieldReadable = useMemo(() => {
77
+ const allowedFields = isCreatingEntry ? [] : readActionAllowedFields;
78
+
79
+ return allowedFields.includes(name);
80
+ }, [name, isCreatingEntry, readActionAllowedFields]);
43
81
 
44
82
  const dynamicDisplayedComponentsLength = dynamicDisplayedComponents.length;
45
83
  const intlDescription = metadatas.description
46
84
  ? { id: metadatas.description, defaultMessage: metadatas.description }
47
85
  : null;
48
86
 
49
- // We cannot use the default props here
50
- const { max = Infinity, min = -Infinity } = fieldSchema;
51
- const dynamicZoneErrors = useMemo(() => {
52
- return Object.keys(formErrors)
53
- .filter((key) => {
54
- return key === name;
55
- })
56
- .map((key) => formErrors[key]);
57
- }, [formErrors, name]);
87
+ const dynamicZoneError = formErrors[name];
58
88
 
59
89
  const missingComponentNumber = min - dynamicDisplayedComponentsLength;
60
- const hasError = dynamicZoneErrors.length > 0;
61
-
62
- const hasMinError =
63
- dynamicZoneErrors.length > 0 && get(dynamicZoneErrors, [0, 'id'], '').includes('min');
90
+ const hasError = !!dynamicZoneError;
64
91
 
65
- const hasMaxError =
66
- hasError && get(dynamicZoneErrors, [0, 'id'], '') === 'components.Input.error.validation.max';
67
-
68
- const handleAddComponent = (componentUid) => {
92
+ const handleAddComponent = (componentUid, position) => {
69
93
  setAddComponentIsOpen(false);
70
94
 
71
95
  const componentLayoutData = getComponentLayout(componentUid);
72
96
 
73
- addComponentToDynamicZone(name, componentLayoutData, components, hasError);
97
+ const allComponents = Object.values(dynamicComponentsByCategory).reduce((acc, components) => {
98
+ const componentObjects = components.reduce((acc, { componentUid, attributes }) => {
99
+ acc[componentUid] = {
100
+ attributes,
101
+ uid: componentUid,
102
+ };
103
+
104
+ return acc;
105
+ }, {});
106
+
107
+ return { ...acc, ...componentObjects };
108
+ }, {});
109
+
110
+ addComponentToDynamicZone(name, componentLayoutData, allComponents, hasError, position);
74
111
  };
75
112
 
76
113
  const handleClickOpenPicker = () => {
@@ -160,6 +197,38 @@ const DynamicZone = ({
160
197
  removeComponentFromDynamicZone(name, currentIndex);
161
198
  };
162
199
 
200
+ const renderButtonLabel = () => {
201
+ if (addComponentIsOpen) {
202
+ return formatMessage({ id: 'app.utils.close-label', defaultMessage: 'Close' });
203
+ }
204
+
205
+ if (hasError && dynamicZoneError.id.includes('max')) {
206
+ return formatMessage({
207
+ id: 'components.Input.error.validation.max',
208
+ defaultMessage: 'The value is too high.',
209
+ });
210
+ }
211
+
212
+ if (hasError && dynamicZoneError.id.includes('min')) {
213
+ return formatMessage(
214
+ {
215
+ id: getTrad(`components.DynamicZone.missing-components`),
216
+ defaultMessage:
217
+ 'There {number, plural, =0 {are # missing components} one {is # missing component} other {are # missing components}}',
218
+ },
219
+ { number: missingComponentNumber }
220
+ );
221
+ }
222
+
223
+ return formatMessage(
224
+ {
225
+ id: getTrad('components.DynamicZone.add-component'),
226
+ defaultMessage: 'Add a component to {componentName}',
227
+ },
228
+ { componentName: metadatas.label || name }
229
+ );
230
+ };
231
+
163
232
  if (!isFieldAllowed && (isCreatingEntry || (!isFieldReadable && !isCreatingEntry))) {
164
233
  return (
165
234
  <NotAllowedInput
@@ -183,7 +252,7 @@ const DynamicZone = ({
183
252
  labelAction={labelAction}
184
253
  name={name}
185
254
  numberOfComponents={dynamicDisplayedComponentsLength}
186
- required={fieldSchema.required || false}
255
+ required={required}
187
256
  />
188
257
  <VisuallyHidden id={ariaDescriptionId}>
189
258
  {formatMessage({
@@ -194,7 +263,7 @@ const DynamicZone = ({
194
263
  <VisuallyHidden aria-live="assertive">{liveText}</VisuallyHidden>
195
264
  <ol aria-describedby={ariaDescriptionId}>
196
265
  {dynamicDisplayedComponents.map(({ componentUid, id }, index) => (
197
- <DynamicZoneComponent
266
+ <DynamicComponent
198
267
  componentUid={componentUid}
199
268
  formErrors={formErrors}
200
269
  key={`${componentUid}-${id}`}
@@ -206,26 +275,26 @@ const DynamicZone = ({
206
275
  onCancel={handleCancel}
207
276
  onDropItem={handleDropItem}
208
277
  onGrabItem={handleGrabItem}
278
+ onAddComponent={handleAddComponent}
279
+ dynamicComponentsByCategory={dynamicComponentsByCategory}
209
280
  />
210
281
  ))}
211
282
  </ol>
212
283
  </Box>
213
284
  )}
214
-
215
- <AddComponentButton
216
- hasError={hasError}
217
- hasMaxError={hasMaxError}
218
- hasMinError={hasMinError}
219
- isDisabled={!isFieldAllowed}
220
- label={metadatas.label}
221
- missingComponentNumber={missingComponentNumber}
222
- isOpen={addComponentIsOpen}
223
- name={name}
224
- onClick={handleClickOpenPicker}
225
- />
285
+ <Flex justifyContent="center">
286
+ <AddComponentButton
287
+ hasError={hasError}
288
+ isDisabled={!isFieldAllowed}
289
+ isOpen={addComponentIsOpen}
290
+ onClick={handleClickOpenPicker}
291
+ >
292
+ {renderButtonLabel()}
293
+ </AddComponentButton>
294
+ </Flex>
226
295
  <ComponentPicker
296
+ dynamicComponentsByCategory={dynamicComponentsByCategory}
227
297
  isOpen={addComponentIsOpen}
228
- components={fieldSchema.components ?? []}
229
298
  onClickAddComponent={handleAddComponent}
230
299
  />
231
300
  </Flex>
@@ -233,44 +302,23 @@ const DynamicZone = ({
233
302
  };
234
303
 
235
304
  DynamicZone.defaultProps = {
236
- dynamicDisplayedComponents: [],
237
- fieldSchema: {
238
- max: Infinity,
239
- min: -Infinity,
240
- },
305
+ fieldSchema: {},
241
306
  labelAction: null,
242
307
  };
243
308
 
244
309
  DynamicZone.propTypes = {
245
- addComponentToDynamicZone: PropTypes.func.isRequired,
246
- dynamicDisplayedComponents: PropTypes.arrayOf(
247
- PropTypes.shape({
248
- componentUid: PropTypes.string.isRequired,
249
- id: PropTypes.number.isRequired,
250
- })
251
- ),
252
310
  fieldSchema: PropTypes.shape({
253
- components: PropTypes.array.isRequired,
311
+ components: PropTypes.array,
254
312
  max: PropTypes.number,
255
313
  min: PropTypes.number,
256
314
  required: PropTypes.bool,
257
315
  }),
258
- formErrors: PropTypes.object.isRequired,
259
- isCreatingEntry: PropTypes.bool.isRequired,
260
- isFieldAllowed: PropTypes.bool.isRequired,
261
- isFieldReadable: PropTypes.bool.isRequired,
262
316
  labelAction: PropTypes.element,
263
317
  metadatas: PropTypes.shape({
264
318
  description: PropTypes.string,
265
319
  label: PropTypes.string,
266
320
  }).isRequired,
267
- moveComponentField: PropTypes.func.isRequired,
268
321
  name: PropTypes.string.isRequired,
269
- removeComponentFromDynamicZone: PropTypes.func.isRequired,
270
322
  };
271
323
 
272
- const Memoized = memo(DynamicZone, isEqual);
273
-
274
- export default connect(Memoized, select);
275
-
276
324
  export { DynamicZone };
@@ -22,12 +22,13 @@ import {
22
22
  getAPIInnerErrors,
23
23
  } from '@strapi/helper-plugin';
24
24
 
25
- import { getTrad } from '../../utils';
25
+ import { createYupSchema, getTrad } from '../../utils';
26
26
 
27
27
  import selectCrudReducer from '../../sharedReducers/crudReducer/selectors';
28
28
 
29
29
  import reducer, { initialState } from './reducer';
30
- import { cleanData, createYupSchema } from './utils';
30
+ import { cleanData } from './utils';
31
+
31
32
  import { clearSetModifiedDataOnly } from '../../sharedReducers/crudReducer/actions';
32
33
  import { usePrev } from '../../hooks';
33
34
 
@@ -195,14 +196,21 @@ const EditViewDataManagerProvider = ({
195
196
 
196
197
  const dispatchAddComponent = useCallback(
197
198
  (type) =>
198
- (keys, componentLayoutData, components, shouldCheckErrors = false) => {
199
+ (
200
+ keys,
201
+ componentLayoutData,
202
+ allComponents,
203
+ shouldCheckErrors = false,
204
+ position = undefined
205
+ ) => {
199
206
  trackUsageRef.current('didAddComponentToDynamicZone');
200
207
 
201
208
  dispatch({
202
209
  type,
203
210
  keys: keys.split('.'),
211
+ position,
204
212
  componentLayoutData,
205
- allComponents: components,
213
+ allComponents,
206
214
  shouldCheckErrors,
207
215
  });
208
216
  },