@contentful/field-editor-reference 5.8.6 → 5.10.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 (199) hide show
  1. package/dist/cjs/__fixtures__/FakeSdk.js +183 -0
  2. package/dist/cjs/__fixtures__/asset/index.js +37 -0
  3. package/dist/cjs/__fixtures__/content-type/index.js +16 -0
  4. package/dist/cjs/__fixtures__/entry/index.js +33 -0
  5. package/dist/cjs/__fixtures__/fixtures.js +71 -0
  6. package/dist/cjs/__fixtures__/locale/index.js +40 -0
  7. package/dist/cjs/__fixtures__/space/index.js +16 -0
  8. package/dist/cjs/assets/MultipleMediaEditor.js +86 -0
  9. package/dist/cjs/assets/SingleMediaEditor.js +69 -0
  10. package/dist/cjs/assets/WrappedAssetCard/AssetCardActions.js +125 -0
  11. package/dist/cjs/assets/WrappedAssetCard/FetchingWrappedAssetCard.js +171 -0
  12. package/dist/cjs/assets/WrappedAssetCard/WrappedAssetCard.js +159 -0
  13. package/dist/cjs/assets/WrappedAssetCard/WrappedAssetLink.js +130 -0
  14. package/dist/cjs/assets/index.js +24 -0
  15. package/dist/cjs/common/EntityStore.js +420 -0
  16. package/dist/cjs/common/MultipleReferenceEditor.js +164 -0
  17. package/dist/cjs/common/ReferenceEditor.js +74 -0
  18. package/dist/cjs/common/SingleReferenceEditor.js +118 -0
  19. package/dist/cjs/common/SortableLinkList.js +95 -0
  20. package/dist/cjs/common/customCardTypes.js +44 -0
  21. package/dist/cjs/common/useAccessApi.js +19 -0
  22. package/dist/cjs/common/useContentTypePermissions.js +54 -0
  23. package/dist/cjs/common/useEditorPermissions.js +77 -0
  24. package/dist/cjs/common/useEditorPermissions.spec.js +205 -0
  25. package/dist/cjs/components/AssetThumbnail/AssetThumbnail.js +62 -0
  26. package/dist/cjs/components/CreateEntryLinkButton/CreateEntryLinkButton.js +102 -0
  27. package/dist/cjs/components/CreateEntryLinkButton/CreateEntryLinkButton.spec.js +254 -0
  28. package/dist/cjs/components/CreateEntryLinkButton/CreateEntryMenuTrigger.js +199 -0
  29. package/dist/cjs/components/CreateEntryLinkButton/CreateEntryMenuTrigger.spec.js +190 -0
  30. package/dist/cjs/components/CreateEntryLinkButton/useGlobalMouseUp.js +19 -0
  31. package/dist/cjs/components/LinkActions/CombinedLinkActions.js +167 -0
  32. package/dist/cjs/components/LinkActions/LinkActions.js +123 -0
  33. package/dist/cjs/components/LinkActions/LinkEntityActions.js +186 -0
  34. package/dist/cjs/components/LinkActions/NoLinkPermissionsInfo.js +54 -0
  35. package/dist/cjs/components/LinkActions/helpers.js +78 -0
  36. package/dist/cjs/components/LinkActions/redesignStyles.js +44 -0
  37. package/dist/cjs/components/LinkActions/styles.js +33 -0
  38. package/dist/cjs/components/MissingEntityCard/MissingEntityCard.js +75 -0
  39. package/dist/cjs/components/MissingEntityCard/styles.js +29 -0
  40. package/dist/cjs/components/ScheduledIconWithTooltip/ScheduleTooltip.js +75 -0
  41. package/dist/cjs/components/ScheduledIconWithTooltip/ScheduledIconWithTooltip.js +81 -0
  42. package/dist/cjs/components/ScheduledIconWithTooltip/formatDateAndTime.js +45 -0
  43. package/dist/cjs/components/SpaceName/SpaceName.js +91 -0
  44. package/dist/cjs/components/index.js +44 -0
  45. package/dist/cjs/entries/MultipleEntryReferenceEditor.js +86 -0
  46. package/dist/cjs/entries/SingleEntryReferenceEditor.js +74 -0
  47. package/dist/cjs/entries/WrappedEntryCard/FetchingWrappedEntryCard.js +189 -0
  48. package/dist/cjs/entries/WrappedEntryCard/WrappedEntryCard.js +181 -0
  49. package/dist/cjs/entries/index.js +24 -0
  50. package/dist/cjs/index.js +92 -0
  51. package/dist/cjs/resources/Cards/ContentfulEntryCard.js +87 -0
  52. package/dist/cjs/resources/Cards/ResourceCard.js +111 -0
  53. package/dist/cjs/resources/Cards/UnsupportedEntityCard.js +64 -0
  54. package/dist/cjs/resources/MultipleResourceReferenceEditor.js +157 -0
  55. package/dist/cjs/resources/MultipleResourceReferenceEditor.spec.js +297 -0
  56. package/dist/cjs/resources/SingleResourceReferenceEditor.js +87 -0
  57. package/dist/cjs/resources/SingleResourceReferenceEditor.spec.js +161 -0
  58. package/dist/cjs/resources/index.js +19 -0
  59. package/dist/cjs/resources/testHelpers/resourceEditorHelpers.js +121 -0
  60. package/dist/cjs/resources/useResourceLinkActions.js +88 -0
  61. package/dist/cjs/types.js +22 -0
  62. package/dist/cjs/utils/fromFieldValidations.js +54 -0
  63. package/dist/esm/__fixtures__/FakeSdk.js +173 -0
  64. package/dist/esm/__fixtures__/asset/index.js +6 -0
  65. package/dist/esm/__fixtures__/content-type/index.js +2 -0
  66. package/dist/esm/__fixtures__/entry/index.js +5 -0
  67. package/dist/esm/__fixtures__/fixtures.js +6 -0
  68. package/dist/esm/__fixtures__/locale/index.js +15 -0
  69. package/dist/esm/__fixtures__/space/index.js +2 -0
  70. package/dist/esm/assets/MultipleMediaEditor.js +37 -0
  71. package/dist/esm/assets/SingleMediaEditor.js +20 -0
  72. package/dist/esm/assets/WrappedAssetCard/AssetCardActions.js +63 -0
  73. package/dist/esm/assets/WrappedAssetCard/FetchingWrappedAssetCard.js +122 -0
  74. package/dist/esm/assets/WrappedAssetCard/WrappedAssetCard.js +105 -0
  75. package/dist/esm/assets/WrappedAssetCard/WrappedAssetLink.js +76 -0
  76. package/dist/esm/assets/index.js +3 -0
  77. package/dist/esm/common/EntityStore.js +347 -0
  78. package/dist/esm/common/MultipleReferenceEditor.js +111 -0
  79. package/dist/esm/common/ReferenceEditor.js +20 -0
  80. package/dist/esm/common/SingleReferenceEditor.js +70 -0
  81. package/dist/esm/common/SortableLinkList.js +41 -0
  82. package/dist/esm/common/customCardTypes.js +1 -0
  83. package/dist/esm/common/useAccessApi.js +9 -0
  84. package/dist/esm/common/useContentTypePermissions.js +44 -0
  85. package/dist/esm/common/useEditorPermissions.js +67 -0
  86. package/dist/esm/common/useEditorPermissions.spec.js +201 -0
  87. package/dist/esm/components/AssetThumbnail/AssetThumbnail.js +13 -0
  88. package/dist/esm/components/CreateEntryLinkButton/CreateEntryLinkButton.js +48 -0
  89. package/dist/esm/components/CreateEntryLinkButton/CreateEntryLinkButton.spec.js +206 -0
  90. package/dist/esm/components/CreateEntryLinkButton/CreateEntryMenuTrigger.js +145 -0
  91. package/dist/esm/components/CreateEntryLinkButton/CreateEntryMenuTrigger.spec.js +142 -0
  92. package/dist/esm/components/CreateEntryLinkButton/useGlobalMouseUp.js +9 -0
  93. package/dist/esm/components/LinkActions/CombinedLinkActions.js +118 -0
  94. package/dist/esm/components/LinkActions/LinkActions.js +66 -0
  95. package/dist/esm/components/LinkActions/LinkEntityActions.js +127 -0
  96. package/dist/esm/components/LinkActions/NoLinkPermissionsInfo.js +5 -0
  97. package/dist/esm/components/LinkActions/helpers.js +57 -0
  98. package/dist/esm/components/LinkActions/redesignStyles.js +18 -0
  99. package/dist/esm/components/LinkActions/styles.js +10 -0
  100. package/dist/esm/components/MissingEntityCard/MissingEntityCard.js +26 -0
  101. package/dist/esm/components/MissingEntityCard/styles.js +11 -0
  102. package/dist/esm/components/ScheduledIconWithTooltip/ScheduleTooltip.js +18 -0
  103. package/dist/esm/components/ScheduledIconWithTooltip/ScheduledIconWithTooltip.js +32 -0
  104. package/dist/esm/components/ScheduledIconWithTooltip/formatDateAndTime.js +19 -0
  105. package/dist/esm/components/SpaceName/SpaceName.js +37 -0
  106. package/dist/esm/components/index.js +8 -0
  107. package/dist/esm/entries/MultipleEntryReferenceEditor.js +37 -0
  108. package/dist/esm/entries/SingleEntryReferenceEditor.js +25 -0
  109. package/dist/esm/entries/WrappedEntryCard/FetchingWrappedEntryCard.js +135 -0
  110. package/dist/esm/entries/WrappedEntryCard/WrappedEntryCard.js +127 -0
  111. package/dist/esm/entries/index.js +3 -0
  112. package/dist/esm/index.js +7 -0
  113. package/dist/esm/resources/Cards/ContentfulEntryCard.js +38 -0
  114. package/dist/esm/resources/Cards/ResourceCard.js +62 -0
  115. package/dist/esm/resources/Cards/UnsupportedEntityCard.js +15 -0
  116. package/dist/esm/resources/MultipleResourceReferenceEditor.js +104 -0
  117. package/dist/esm/resources/MultipleResourceReferenceEditor.spec.js +254 -0
  118. package/dist/esm/resources/SingleResourceReferenceEditor.js +33 -0
  119. package/dist/esm/resources/SingleResourceReferenceEditor.spec.js +118 -0
  120. package/dist/esm/resources/index.js +2 -0
  121. package/dist/esm/resources/testHelpers/resourceEditorHelpers.js +103 -0
  122. package/dist/esm/resources/useResourceLinkActions.js +78 -0
  123. package/dist/esm/types.js +1 -0
  124. package/dist/esm/utils/fromFieldValidations.js +39 -0
  125. package/dist/{__fixtures__ → types/__fixtures__}/FakeSdk.d.ts +8 -8
  126. package/dist/{__fixtures__ → types/__fixtures__}/asset/index.d.ts +6 -6
  127. package/dist/{__fixtures__ → types/__fixtures__}/content-type/index.d.ts +2 -2
  128. package/dist/{__fixtures__ → types/__fixtures__}/entry/index.d.ts +5 -5
  129. package/dist/{__fixtures__ → types/__fixtures__}/fixtures.d.ts +6 -6
  130. package/dist/{__fixtures__ → types/__fixtures__}/locale/index.d.ts +42 -42
  131. package/dist/{__fixtures__ → types/__fixtures__}/space/index.d.ts +2 -2
  132. package/dist/{assets → types/assets}/MultipleMediaEditor.d.ts +10 -10
  133. package/dist/types/assets/SingleMediaEditor.d.ts +10 -0
  134. package/dist/{assets → types/assets}/WrappedAssetCard/AssetCardActions.d.ts +11 -11
  135. package/dist/{assets → types/assets}/WrappedAssetCard/FetchingWrappedAssetCard.d.ts +17 -17
  136. package/dist/{assets → types/assets}/WrappedAssetCard/WrappedAssetCard.d.ts +24 -24
  137. package/dist/{assets → types/assets}/WrappedAssetCard/WrappedAssetLink.d.ts +16 -16
  138. package/dist/{assets → types/assets}/index.d.ts +3 -3
  139. package/dist/{common → types/common}/EntityStore.d.ts +62 -62
  140. package/dist/{common → types/common}/MultipleReferenceEditor.d.ts +25 -25
  141. package/dist/{common → types/common}/ReferenceEditor.d.ts +46 -46
  142. package/dist/{common → types/common}/SingleReferenceEditor.d.ts +24 -24
  143. package/dist/{common → types/common}/SortableLinkList.d.ts +19 -19
  144. package/dist/{common → types/common}/customCardTypes.d.ts +29 -29
  145. package/dist/types/common/useAccessApi.d.ts +16 -0
  146. package/dist/{common → types/common}/useContentTypePermissions.d.ts +17 -17
  147. package/dist/{common → types/common}/useEditorPermissions.d.ts +17 -17
  148. package/dist/types/common/useEditorPermissions.spec.d.ts +1 -0
  149. package/dist/{components → types/components}/AssetThumbnail/AssetThumbnail.d.ts +7 -7
  150. package/dist/{components → types/components}/CreateEntryLinkButton/CreateEntryLinkButton.d.ts +19 -19
  151. package/dist/types/components/CreateEntryLinkButton/CreateEntryLinkButton.spec.d.ts +1 -0
  152. package/dist/{components → types/components}/CreateEntryLinkButton/CreateEntryMenuTrigger.d.ts +31 -31
  153. package/dist/types/components/CreateEntryLinkButton/CreateEntryMenuTrigger.spec.d.ts +1 -0
  154. package/dist/{components → types/components}/CreateEntryLinkButton/useGlobalMouseUp.d.ts +1 -1
  155. package/dist/{components → types/components}/LinkActions/CombinedLinkActions.d.ts +10 -10
  156. package/dist/{components → types/components}/LinkActions/LinkActions.d.ts +26 -26
  157. package/dist/{components → types/components}/LinkActions/LinkEntityActions.d.ts +24 -24
  158. package/dist/types/components/LinkActions/NoLinkPermissionsInfo.d.ts +2 -0
  159. package/dist/{components → types/components}/LinkActions/helpers.d.ts +26 -26
  160. package/dist/{components → types/components}/LinkActions/redesignStyles.d.ts +3 -3
  161. package/dist/{components → types/components}/LinkActions/styles.d.ts +2 -2
  162. package/dist/{components → types/components}/MissingEntityCard/MissingEntityCard.d.ts +8 -8
  163. package/dist/{components → types/components}/MissingEntityCard/styles.d.ts +2 -2
  164. package/dist/{components → types/components}/ScheduledIconWithTooltip/ScheduleTooltip.d.ts +11 -11
  165. package/dist/{components → types/components}/ScheduledIconWithTooltip/ScheduledIconWithTooltip.d.ts +10 -10
  166. package/dist/{components → types/components}/ScheduledIconWithTooltip/formatDateAndTime.d.ts +15 -15
  167. package/dist/types/components/SpaceName/SpaceName.d.ts +6 -0
  168. package/dist/{components → types/components}/index.d.ts +9 -9
  169. package/dist/{entries → types/entries}/MultipleEntryReferenceEditor.d.ts +3 -3
  170. package/dist/{entries → types/entries}/SingleEntryReferenceEditor.d.ts +8 -8
  171. package/dist/{entries → types/entries}/WrappedEntryCard/FetchingWrappedEntryCard.d.ts +18 -18
  172. package/dist/{entries → types/entries}/WrappedEntryCard/WrappedEntryCard.d.ts +35 -35
  173. package/dist/{entries → types/entries}/index.d.ts +3 -3
  174. package/dist/{index.d.ts → types/index.d.ts} +9 -8
  175. package/dist/{resources → types/resources}/Cards/ContentfulEntryCard.d.ts +21 -21
  176. package/dist/{resources → types/resources}/Cards/ResourceCard.d.ts +12 -12
  177. package/dist/{resources → types/resources}/Cards/UnsupportedEntityCard.d.ts +4 -4
  178. package/dist/{resources → types/resources}/MultipleResourceReferenceEditor.d.ts +7 -7
  179. package/dist/types/resources/MultipleResourceReferenceEditor.spec.d.ts +1 -0
  180. package/dist/{resources → types/resources}/SingleResourceReferenceEditor.d.ts +7 -7
  181. package/dist/types/resources/SingleResourceReferenceEditor.spec.d.ts +1 -0
  182. package/dist/{resources → types/resources}/index.d.ts +2 -2
  183. package/dist/{resources → types/resources}/testHelpers/resourceEditorHelpers.d.ts +50 -50
  184. package/dist/{resources → types/resources}/useResourceLinkActions.d.ts +7 -7
  185. package/dist/{types.d.ts → types/types.d.ts} +104 -104
  186. package/dist/{utils → types/utils}/fromFieldValidations.d.ts +21 -21
  187. package/package.json +25 -11
  188. package/CHANGELOG.md +0 -854
  189. package/dist/assets/SingleMediaEditor.d.ts +0 -10
  190. package/dist/common/useAccessApi.d.ts +0 -16
  191. package/dist/components/LinkActions/NoLinkPermissionsInfo.d.ts +0 -2
  192. package/dist/components/SpaceName/SpaceName.d.ts +0 -6
  193. package/dist/field-editor-reference.cjs.development.js +0 -4395
  194. package/dist/field-editor-reference.cjs.development.js.map +0 -1
  195. package/dist/field-editor-reference.cjs.production.min.js +0 -2
  196. package/dist/field-editor-reference.cjs.production.min.js.map +0 -1
  197. package/dist/field-editor-reference.esm.js +0 -4369
  198. package/dist/field-editor-reference.esm.js.map +0 -1
  199. package/dist/index.js +0 -8
@@ -0,0 +1,127 @@
1
+ import * as React from 'react';
2
+ import { EntryCard, MenuItem, MenuDivider } from '@contentful/f36-components';
3
+ import { ClockIcon } from '@contentful/f36-icons';
4
+ import tokens from '@contentful/f36-tokens';
5
+ import { entityHelpers, isValidImage } from '@contentful/field-editor-shared';
6
+ import { css } from 'emotion';
7
+ import { AssetThumbnail, MissingEntityCard, ScheduledIconWithTooltip } from '../../components';
8
+ import { SpaceName } from '../../components/SpaceName/SpaceName';
9
+ const { getEntryTitle , getEntityDescription , getEntryStatus , getEntryImage } = entityHelpers;
10
+ const styles = {
11
+ scheduleIcon: css({
12
+ marginRight: tokens.spacing2Xs
13
+ })
14
+ };
15
+ const defaultProps = {
16
+ isClickable: true,
17
+ hasCardEditActions: true,
18
+ hasCardMoveActions: true,
19
+ hasCardRemoveActions: true
20
+ };
21
+ export function WrappedEntryCard(props) {
22
+ const [file, setFile] = React.useState(null);
23
+ const { contentType } = props;
24
+ React.useEffect(()=>{
25
+ if (props.entry) {
26
+ getEntryImage({
27
+ entry: props.entry,
28
+ contentType,
29
+ localeCode: props.localeCode,
30
+ defaultLocaleCode: props.defaultLocaleCode
31
+ }, props.getAsset).then((file)=>{
32
+ setFile(file);
33
+ }).catch(()=>{
34
+ setFile(null);
35
+ });
36
+ }
37
+ }, [
38
+ props.entry,
39
+ props.getAsset,
40
+ contentType,
41
+ props.localeCode,
42
+ props.defaultLocaleCode
43
+ ]);
44
+ const status = getEntryStatus(props.entry?.sys);
45
+ if (status === 'deleted') {
46
+ return React.createElement(MissingEntityCard, {
47
+ entityType: "Entry",
48
+ isDisabled: props.isDisabled,
49
+ onRemove: props.onRemove
50
+ });
51
+ }
52
+ const title = getEntryTitle({
53
+ entry: props.entry,
54
+ contentType,
55
+ localeCode: props.localeCode,
56
+ defaultLocaleCode: props.defaultLocaleCode,
57
+ defaultTitle: 'Untitled'
58
+ });
59
+ const description = getEntityDescription({
60
+ entity: props.entry,
61
+ contentType,
62
+ localeCode: props.localeCode,
63
+ defaultLocaleCode: props.defaultLocaleCode
64
+ });
65
+ return React.createElement(EntryCard, {
66
+ as: props.entryUrl ? 'a' : 'article',
67
+ href: props.entryUrl,
68
+ title: title,
69
+ description: description,
70
+ contentType: contentType?.name,
71
+ size: props.size,
72
+ isSelected: props.isSelected,
73
+ status: status,
74
+ icon: props.spaceName ? React.createElement(SpaceName, {
75
+ spaceName: props.spaceName
76
+ }) : React.createElement(ScheduledIconWithTooltip, {
77
+ getEntityScheduledActions: props.getEntityScheduledActions,
78
+ entityType: "Entry",
79
+ entityId: props.entry.sys.id
80
+ }, React.createElement(ClockIcon, {
81
+ className: styles.scheduleIcon,
82
+ size: "small",
83
+ variant: "muted",
84
+ testId: "schedule-icon"
85
+ })),
86
+ thumbnailElement: file && isValidImage(file) ? React.createElement(AssetThumbnail, {
87
+ file: file
88
+ }) : undefined,
89
+ dragHandleRender: props.renderDragHandle,
90
+ withDragHandle: !!props.renderDragHandle,
91
+ actions: props.onEdit || props.onRemove ? [
92
+ props.hasCardEditActions && props.onEdit ? React.createElement(MenuItem, {
93
+ key: "edit",
94
+ testId: "edit",
95
+ onClick: ()=>{
96
+ props.onEdit && props.onEdit();
97
+ }
98
+ }, "Edit") : null,
99
+ props.hasCardRemoveActions && props.onRemove ? React.createElement(MenuItem, {
100
+ key: "delete",
101
+ testId: "delete",
102
+ onClick: ()=>{
103
+ props.onRemove && props.onRemove();
104
+ }
105
+ }, "Remove") : null,
106
+ props.hasCardMoveActions && (props.onMoveTop || props.onMoveBottom) ? React.createElement(MenuDivider, {
107
+ key: "divider"
108
+ }) : null,
109
+ props.hasCardMoveActions && props.onMoveTop ? React.createElement(MenuItem, {
110
+ key: "move-top",
111
+ onClick: ()=>props.onMoveTop && props.onMoveTop(),
112
+ testId: "move-top"
113
+ }, "Move to top") : null,
114
+ props.hasCardMoveActions && props.onMoveBottom ? React.createElement(MenuItem, {
115
+ key: "move-bottom",
116
+ onClick: ()=>props.onMoveBottom && props.onMoveBottom(),
117
+ testId: "move-bottom"
118
+ }, "Move to bottom") : null
119
+ ].filter((item)=>item) : [],
120
+ onClick: props.isClickable ? (e)=>{
121
+ e.preventDefault();
122
+ if (props.onClick) return props.onClick(e);
123
+ props.onEdit && props.onEdit();
124
+ } : undefined
125
+ });
126
+ }
127
+ WrappedEntryCard.defaultProps = defaultProps;
@@ -0,0 +1,3 @@
1
+ export { SingleEntryReferenceEditor } from './SingleEntryReferenceEditor';
2
+ export { MultipleEntryReferenceEditor } from './MultipleEntryReferenceEditor';
3
+ export { WrappedEntryCard } from './WrappedEntryCard/WrappedEntryCard';
@@ -0,0 +1,7 @@
1
+ export { CreateEntryMenuTrigger, CreateEntryLinkButton, getScheduleTooltipContent, ScheduledIconWithTooltip, AssetThumbnail, MissingEntityCard, CombinedLinkActions } from './components';
2
+ export { SingleEntryReferenceEditor, MultipleEntryReferenceEditor, WrappedEntryCard } from './entries';
3
+ export { SingleMediaEditor, MultipleMediaEditor, WrappedAssetCard } from './assets';
4
+ export { SortableLinkList } from './common/SortableLinkList';
5
+ export { EntityProvider, useEntityLoader, useEntity, useResource } from './common/EntityStore';
6
+ export { SingleResourceReferenceEditor, MultipleResourceReferenceEditor } from './resources';
7
+ export * from './types';
@@ -0,0 +1,38 @@
1
+ import * as React from 'react';
2
+ import { WrappedEntryCard } from '../../entries';
3
+ const resolveAsset = ()=>Promise.resolve();
4
+ const resolveScheduledActions = ()=>Promise.resolve([]);
5
+ export function ContentfulEntryCard({ info , isDisabled , renderDragHandle , onRemove , onMoveTop , onMoveBottom , getEntryRouteHref }) {
6
+ const resourceSys = info.resource.sys;
7
+ const spaceId = resourceSys.space.sys.id;
8
+ const environmentId = resourceSys.environment.sys.id;
9
+ const entryId = resourceSys.id;
10
+ const resourceHref = getEntryRouteHref({
11
+ spaceId,
12
+ environmentId,
13
+ entryId
14
+ });
15
+ const openEntryDetail = ()=>{
16
+ window.open(resourceHref, '_blank', 'noopener,noreferrer');
17
+ };
18
+ return React.createElement(WrappedEntryCard, {
19
+ entry: info.resource,
20
+ isDisabled: isDisabled,
21
+ hasCardEditActions: false,
22
+ contentType: info.contentType,
23
+ localeCode: info.defaultLocaleCode,
24
+ defaultLocaleCode: info.defaultLocaleCode,
25
+ size: "small",
26
+ getAsset: resolveAsset,
27
+ getEntityScheduledActions: resolveScheduledActions,
28
+ spaceName: info.space.name,
29
+ renderDragHandle: renderDragHandle,
30
+ isClickable: true,
31
+ onEdit: openEntryDetail,
32
+ hasCardRemoveActions: Boolean(onRemove),
33
+ onRemove: onRemove,
34
+ onMoveBottom: onMoveBottom,
35
+ onMoveTop: onMoveTop,
36
+ entryUrl: resourceHref
37
+ });
38
+ }
@@ -0,0 +1,62 @@
1
+ import * as React from 'react';
2
+ import { useInView } from 'react-intersection-observer';
3
+ import { EntryCard } from '@contentful/f36-components';
4
+ import { isUnsupportedError, useResource } from '../../common/EntityStore';
5
+ import { MissingEntityCard } from '../../components';
6
+ import { ContentfulEntryCard } from './ContentfulEntryCard';
7
+ import { UnsupportedEntityCard } from './UnsupportedEntityCard';
8
+ function ResourceCardSkeleton() {
9
+ return React.createElement(EntryCard, {
10
+ size: "small",
11
+ isLoading: true
12
+ });
13
+ }
14
+ function ExistingResourceCard(props) {
15
+ const { resourceLink , inView , index =0 } = props;
16
+ const resourceOptions = {
17
+ priority: index * -1,
18
+ enabled: inView
19
+ };
20
+ const { data , error } = useResource(resourceLink.sys.linkType, resourceLink.sys.urn, resourceOptions);
21
+ if (!data && !error) {
22
+ return React.createElement(ResourceCardSkeleton, null);
23
+ }
24
+ if (data) {
25
+ return React.createElement(ContentfulEntryCard, {
26
+ info: data,
27
+ ...props
28
+ });
29
+ }
30
+ if (isUnsupportedError(error)) {
31
+ return React.createElement(UnsupportedEntityCard, {
32
+ entityType: resourceLink.sys.linkType
33
+ });
34
+ }
35
+ return React.createElement(MissingEntityCard, {
36
+ entityType: "Entry",
37
+ isDisabled: props.isDisabled,
38
+ onRemove: props.onRemove
39
+ });
40
+ }
41
+ function ResourceCardWrapper(props) {
42
+ if (!props.resourceLink) {
43
+ return null;
44
+ }
45
+ return React.createElement(ExistingResourceCard, {
46
+ ...props,
47
+ resourceLink: props.resourceLink,
48
+ getEntryRouteHref: props.getEntryRouteHref
49
+ });
50
+ }
51
+ export function ResourceCard(props) {
52
+ const { ref , inView } = useInView({
53
+ triggerOnce: true,
54
+ rootMargin: '300px 0px 0px 300px'
55
+ });
56
+ return React.createElement("div", {
57
+ ref: ref
58
+ }, React.createElement(ResourceCardWrapper, {
59
+ ...props,
60
+ inView: inView
61
+ }));
62
+ }
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+ import { Card, SectionHeading } from '@contentful/f36-components';
3
+ import { css } from 'emotion';
4
+ const styles = {
5
+ card: css({
6
+ position: 'relative'
7
+ })
8
+ };
9
+ export function UnsupportedEntityCard(props) {
10
+ return React.createElement(Card, {
11
+ className: styles.card
12
+ }, React.createElement(SectionHeading, {
13
+ marginBottom: "none"
14
+ }, "Resource type ", props.entityType, " is currently not supported"));
15
+ }
@@ -0,0 +1,104 @@
1
+ import * as React from 'react';
2
+ import { useCallback } from 'react';
3
+ import { FieldConnector } from '@contentful/field-editor-shared';
4
+ import arrayMove from 'array-move';
5
+ import deepEqual from 'deep-equal';
6
+ import { EntityProvider } from '../common/EntityStore';
7
+ import { SortableLinkList } from '../common/SortableLinkList';
8
+ import { CombinedLinkEntityActions } from '../components/LinkActions/LinkEntityActions';
9
+ import { ResourceCard } from './Cards/ResourceCard';
10
+ import { useResourceLinkActions } from './useResourceLinkActions';
11
+ function ResourceEditor(props) {
12
+ const { setValue , items , apiUrl } = props;
13
+ const onSortStart = useCallback((_, event)=>event.preventDefault(), []);
14
+ const onSortEnd = useCallback(({ oldIndex , newIndex })=>{
15
+ const newItems = arrayMove(items, oldIndex, newIndex);
16
+ setValue(newItems);
17
+ }, [
18
+ items,
19
+ setValue
20
+ ]);
21
+ const onMove = useCallback((oldIndex, newIndex)=>{
22
+ const newItems = arrayMove(items, oldIndex, newIndex);
23
+ setValue(newItems);
24
+ }, [
25
+ items,
26
+ setValue
27
+ ]);
28
+ const onRemoteItemAtIndex = useCallback((index)=>{
29
+ setValue(items.filter((_v, i)=>i !== index));
30
+ }, [
31
+ items,
32
+ setValue
33
+ ]);
34
+ const { dialogs , field } = props.sdk;
35
+ const linkActionsProps = useResourceLinkActions({
36
+ dialogs,
37
+ field,
38
+ apiUrl
39
+ });
40
+ return React.createElement(React.Fragment, null, props.children({
41
+ ...props,
42
+ onSortStart,
43
+ onSortEnd,
44
+ onMove,
45
+ onRemoteItemAtIndex
46
+ }), React.createElement(CombinedLinkEntityActions, {
47
+ ...linkActionsProps,
48
+ renderCustomActions: props.renderCustomActions
49
+ }));
50
+ }
51
+ function WithPerItemCallbacks({ listLength , index , onMove , onRemoteItemAtIndex , children }) {
52
+ const handleMoveTop = React.useMemo(()=>index > 0 ? ()=>onMove(index, 0) : undefined, [
53
+ index,
54
+ onMove
55
+ ]);
56
+ const handleMoveBottom = React.useMemo(()=>index < listLength - 1 ? ()=>onMove(index, listLength - 1) : undefined, [
57
+ index,
58
+ onMove,
59
+ listLength
60
+ ]);
61
+ const handleRemove = useCallback(()=>onRemoteItemAtIndex(index), [
62
+ index,
63
+ onRemoteItemAtIndex
64
+ ]);
65
+ return React.createElement(React.Fragment, null, children({
66
+ onMoveBottom: handleMoveBottom,
67
+ onMoveTop: handleMoveTop,
68
+ onRemove: handleRemove
69
+ }));
70
+ }
71
+ const EMPTY_ARRAY = [];
72
+ export function MultipleResourceReferenceEditor(props) {
73
+ return React.createElement(EntityProvider, {
74
+ sdk: props.sdk
75
+ }, React.createElement(FieldConnector, {
76
+ throttle: 0,
77
+ field: props.sdk.field,
78
+ isInitiallyDisabled: props.isInitiallyDisabled,
79
+ isEqualValues: deepEqual
80
+ }, ({ value , disabled , setValue , externalReset })=>{
81
+ return React.createElement(ResourceEditor, {
82
+ ...props,
83
+ items: value || EMPTY_ARRAY,
84
+ isDisabled: disabled,
85
+ setValue: setValue,
86
+ renderCustomActions: props.renderCustomActions,
87
+ key: `${externalReset}-list`
88
+ }, (editorProps)=>React.createElement(SortableLinkList, editorProps, ({ item , isDisabled , DragHandle , index })=>React.createElement(WithPerItemCallbacks, {
89
+ index: index,
90
+ onMove: editorProps.onMove,
91
+ onRemoteItemAtIndex: editorProps.onRemoteItemAtIndex,
92
+ listLength: value?.length || 0
93
+ }, ({ onMoveBottom , onMoveTop , onRemove })=>React.createElement(ResourceCard, {
94
+ index: index,
95
+ resourceLink: item,
96
+ isDisabled: isDisabled,
97
+ renderDragHandle: DragHandle,
98
+ onMoveTop: onMoveTop,
99
+ onMoveBottom: onMoveBottom,
100
+ onRemove: onRemove,
101
+ getEntryRouteHref: props.getEntryRouteHref
102
+ }))));
103
+ }));
104
+ }
@@ -0,0 +1,254 @@
1
+ import * as React from 'react';
2
+ import '@testing-library/jest-dom/extend-expect';
3
+ import { fireEvent, render, screen } from '@testing-library/react';
4
+ import { useResource } from '../common/EntityStore';
5
+ import { MultipleResourceReferenceEditor } from './MultipleResourceReferenceEditor';
6
+ import { createFakeEntryResource, mockSdkForField } from './testHelpers/resourceEditorHelpers';
7
+ let mockedResources = {};
8
+ jest.mock('../common/EntityStore', ()=>{
9
+ const module = jest.requireActual('../common/EntityStore');
10
+ return {
11
+ ...module,
12
+ useResource: jest.fn((linkType, urn, apiUrl)=>({
13
+ data: mockedResources[`${linkType}.${urn}`],
14
+ status: 'success',
15
+ apiUrl
16
+ }))
17
+ };
18
+ });
19
+ jest.mock('react-intersection-observer', ()=>{
20
+ const module = jest.requireActual('react-intersection-observer');
21
+ return {
22
+ ...module,
23
+ useInView: ()=>({
24
+ inView: true
25
+ })
26
+ };
27
+ });
28
+ const fieldDefinition = {
29
+ type: 'Array',
30
+ items: {
31
+ type: 'ResourceLink'
32
+ },
33
+ id: 'foo',
34
+ name: 'Foo',
35
+ allowedResources: [
36
+ {
37
+ source: 'foo',
38
+ contentTypes: [
39
+ 'bar'
40
+ ]
41
+ }
42
+ ],
43
+ required: true,
44
+ validations: []
45
+ };
46
+ describe('Multiple resource editor', ()=>{
47
+ it('renders the action button when no value is set', async ()=>{
48
+ const sdk = mockSdkForField(fieldDefinition);
49
+ render(React.createElement(MultipleResourceReferenceEditor, {
50
+ isInitiallyDisabled: false,
51
+ sdk: sdk,
52
+ hasCardEditActions: true,
53
+ viewType: "card",
54
+ parameters: {}
55
+ }));
56
+ const button = await screen.findByText('Add existing content');
57
+ expect(button).toBeDefined();
58
+ fireEvent.click(button);
59
+ const dialogFn = sdk.dialogs.selectMultipleResourceEntries;
60
+ expect(dialogFn).toHaveBeenCalledTimes(1);
61
+ const options = dialogFn.mock.calls[0][0];
62
+ expect(options).toEqual({
63
+ allowedResources: fieldDefinition.allowedResources
64
+ });
65
+ });
66
+ it('renders custom actions when passed', async ()=>{
67
+ const sdk = mockSdkForField(fieldDefinition);
68
+ render(React.createElement(MultipleResourceReferenceEditor, {
69
+ isInitiallyDisabled: false,
70
+ sdk: sdk,
71
+ hasCardEditActions: true,
72
+ viewType: "card",
73
+ parameters: {},
74
+ renderCustomActions: ()=>React.createElement("div", {
75
+ "data-testid": "custom-actions"
76
+ })
77
+ }));
78
+ const customActions = await screen.findByTestId('custom-actions');
79
+ expect(customActions).toBeDefined();
80
+ });
81
+ describe('with value', ()=>{
82
+ it('renders the cards', async ()=>{
83
+ const { entryLinks , entryInfos } = generateMultipleTestResources();
84
+ mockedResources = {};
85
+ for (const [spaceId, link] of Object.entries(entryLinks)){
86
+ mockedResources[`${link.sys.linkType}.${link.sys.urn}`] = entryInfos[spaceId];
87
+ }
88
+ const sdk = mockSdkForField(fieldDefinition, Object.values(entryLinks));
89
+ render(React.createElement(MultipleResourceReferenceEditor, {
90
+ isInitiallyDisabled: false,
91
+ sdk: sdk,
92
+ hasCardEditActions: true,
93
+ viewType: "card",
94
+ apiUrl: "test-contentful",
95
+ getEntryRouteHref: ()=>'',
96
+ parameters: {}
97
+ }));
98
+ expect(useResource).toHaveBeenCalledTimes(Object.values(entryInfos).length);
99
+ const cards = [];
100
+ const entriesArray = Object.values(entryInfos);
101
+ for (const info of Array.from(entriesArray.values())){
102
+ const entryTitle = info.resource.fields.title[info.defaultLocaleCode];
103
+ const spaceName = info.space.name;
104
+ const card = await expectEntryCard(entryTitle, spaceName);
105
+ cards.push(card);
106
+ }
107
+ });
108
+ describe('card actions', ()=>{
109
+ it('should have a move to top button', async ()=>{
110
+ const { entryLinks , entryInfos } = generateMultipleTestResources();
111
+ mockedResources = {};
112
+ for (const [spaceId, link] of Object.entries(entryLinks)){
113
+ mockedResources[`${link.sys.linkType}.${link.sys.urn}`] = entryInfos[spaceId];
114
+ }
115
+ const sdk = mockSdkForField(fieldDefinition, Object.values(entryLinks));
116
+ render(React.createElement(MultipleResourceReferenceEditor, {
117
+ isInitiallyDisabled: false,
118
+ sdk: sdk,
119
+ hasCardEditActions: true,
120
+ viewType: "card",
121
+ apiUrl: "test-contentful",
122
+ getEntryRouteHref: ()=>'',
123
+ parameters: {}
124
+ }));
125
+ const linkExistingBtn = screen.queryByText('Add existing content');
126
+ expect(linkExistingBtn).toBeInTheDocument();
127
+ const entriesArray = Object.values(entryInfos);
128
+ const firstItem = entriesArray[0];
129
+ await expectToNotHaveMoveButton(firstItem, 'Move to top');
130
+ const allButFirst = entriesArray.slice(1);
131
+ for (const info of allButFirst){
132
+ await expectToHaveMoveButton(info, 'Move to top');
133
+ }
134
+ });
135
+ it('should have a move to bottom button', async ()=>{
136
+ const { entryLinks , entryInfos } = generateMultipleTestResources();
137
+ mockedResources = {};
138
+ for (const [spaceId, link] of Object.entries(entryLinks)){
139
+ mockedResources[`${link.sys.linkType}.${link.sys.urn}`] = entryInfos[spaceId];
140
+ }
141
+ const sdk = mockSdkForField(fieldDefinition, Object.values(entryLinks));
142
+ render(React.createElement(MultipleResourceReferenceEditor, {
143
+ isInitiallyDisabled: false,
144
+ sdk: sdk,
145
+ hasCardEditActions: true,
146
+ viewType: "card",
147
+ apiUrl: "test-contentful",
148
+ getEntryRouteHref: ()=>'',
149
+ parameters: {}
150
+ }));
151
+ const linkExistingBtn = screen.queryByText('Add existing content');
152
+ expect(linkExistingBtn).toBeInTheDocument();
153
+ const entriesArray = Object.values(entryInfos);
154
+ const lastItem = entriesArray[entriesArray.length - 1];
155
+ await expectToNotHaveMoveButton(lastItem, 'Move to bottom');
156
+ const allButLast = entriesArray.slice(0, -1);
157
+ for (const info of allButLast){
158
+ await expectToHaveMoveButton(info, 'Move to bottom');
159
+ }
160
+ });
161
+ it('works when using remove action', async ()=>{
162
+ const { entryLinks , entryInfos } = generateMultipleTestResources();
163
+ mockedResources = {};
164
+ for (const [spaceId, link] of Object.entries(entryLinks)){
165
+ mockedResources[`${link.sys.linkType}.${link.sys.urn}`] = entryInfos[spaceId];
166
+ }
167
+ const sdk = mockSdkForField(fieldDefinition, Object.values(entryLinks));
168
+ render(React.createElement(MultipleResourceReferenceEditor, {
169
+ isInitiallyDisabled: false,
170
+ sdk: sdk,
171
+ hasCardEditActions: true,
172
+ viewType: "card",
173
+ apiUrl: "test-contentful",
174
+ getEntryRouteHref: ()=>'',
175
+ parameters: {}
176
+ }));
177
+ const linkExistingBtn = screen.queryByText('Add existing content');
178
+ expect(linkExistingBtn).toBeInTheDocument();
179
+ const entriesArray = Object.values(entryInfos);
180
+ for (const info of entriesArray){
181
+ await clickCardActionsButton(info);
182
+ const removeBtn = await screen.findByText('Remove', {
183
+ selector: '[role="menuitem"]'
184
+ });
185
+ fireEvent.click(removeBtn);
186
+ }
187
+ expect(sdk.field.setValue).toHaveBeenCalledTimes(3);
188
+ expect(sdk.field.setValue).toHaveBeenCalledWith([]);
189
+ });
190
+ });
191
+ });
192
+ });
193
+ async function expectToHaveMoveButton(info, buttonString) {
194
+ await clickCardActionsButton(info);
195
+ await screen.findByText(buttonString, {
196
+ selector: '[role="menuitem"]'
197
+ });
198
+ }
199
+ async function expectToNotHaveMoveButton(info, buttonString) {
200
+ await clickCardActionsButton(info);
201
+ expect(screen.queryByText(buttonString, {
202
+ selector: '[role="menuitem"]'
203
+ })).toBeNull();
204
+ }
205
+ async function clickCardActionsButton(info) {
206
+ fireEvent.click(document.body);
207
+ const entryTitle = info.resource.fields.title[info.defaultLocaleCode];
208
+ const spaceName = info.space.name;
209
+ const card = await expectEntryCard(entryTitle, spaceName);
210
+ const actionsBtn = card.querySelector('[data-test-id="cf-ui-card-actions"]');
211
+ expect(actionsBtn).toBeInTheDocument();
212
+ fireEvent.click(actionsBtn);
213
+ }
214
+ async function expectEntryCard(entryTitle, spaceName) {
215
+ const title = await screen.findByText(entryTitle);
216
+ await screen.findByText(spaceName);
217
+ const theCard = title.closest('[data-test-id="cf-ui-entry-card"]');
218
+ expect(theCard).toBeDefined();
219
+ const actionsBtn = theCard?.querySelector('[data-test-id="cf-ui-card-actions"]');
220
+ expect(actionsBtn).toBeDefined();
221
+ return theCard;
222
+ }
223
+ function generateMultipleTestResources() {
224
+ return [
225
+ 'Space A',
226
+ 'Space B',
227
+ 'Space C'
228
+ ].reduce((acc, spaceName)=>{
229
+ const spaceId = spaceName.toLowerCase().replace(' ', '-');
230
+ const entryInfo = {
231
+ title: `An entry in ${spaceName}`,
232
+ id: `linkedEntryId`
233
+ };
234
+ acc.entryLinks[spaceId] = {
235
+ sys: {
236
+ type: 'ResourceLink',
237
+ linkType: 'Contentful:Entry',
238
+ urn: `crn:test:::content:spaces/${spaceId}/entries/${entryInfo.id}`
239
+ }
240
+ };
241
+ acc.entryInfos[spaceId] = createFakeEntryResource({
242
+ title: entryInfo.title,
243
+ id: entryInfo.id,
244
+ space: {
245
+ id: spaceId,
246
+ name: spaceName
247
+ }
248
+ });
249
+ return acc;
250
+ }, {
251
+ entryLinks: {},
252
+ entryInfos: {}
253
+ });
254
+ }
@@ -0,0 +1,33 @@
1
+ import * as React from 'react';
2
+ import { FieldConnector } from '@contentful/field-editor-shared';
3
+ import deepEqual from 'deep-equal';
4
+ import { EntityProvider } from '../common/EntityStore';
5
+ import { CombinedLinkEntityActions } from '../components/LinkActions/LinkEntityActions';
6
+ import { ResourceCard } from './Cards/ResourceCard';
7
+ import { useResourceLinkActions } from './useResourceLinkActions';
8
+ export function SingleResourceReferenceEditor(props) {
9
+ const { dialogs , field } = props.sdk;
10
+ const linkActionsProps = useResourceLinkActions({
11
+ dialogs,
12
+ field,
13
+ apiUrl: props.apiUrl
14
+ });
15
+ return React.createElement(EntityProvider, {
16
+ sdk: props.sdk
17
+ }, React.createElement(FieldConnector, {
18
+ throttle: 0,
19
+ field: props.sdk.field,
20
+ isInitiallyDisabled: props.isInitiallyDisabled,
21
+ isEqualValues: deepEqual
22
+ }, ({ value , disabled })=>{
23
+ return value ? React.createElement(ResourceCard, {
24
+ onRemove: ()=>props.sdk.field.removeValue(),
25
+ resourceLink: value,
26
+ isDisabled: disabled,
27
+ getEntryRouteHref: props.getEntryRouteHref
28
+ }) : React.createElement(CombinedLinkEntityActions, {
29
+ ...linkActionsProps,
30
+ renderCustomActions: props.renderCustomActions
31
+ });
32
+ }));
33
+ }