@contentful/field-editor-reference 6.19.0 → 6.19.1-canary.18

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 (42) hide show
  1. package/dist/cjs/__fixtures__/FakeSdk.js +13 -20
  2. package/dist/cjs/assets/WrappedAssetCard/AssetCardActions.js +11 -1
  3. package/dist/cjs/assets/WrappedAssetCard/FetchingWrappedAssetCard.js +10 -1
  4. package/dist/cjs/assets/WrappedAssetCard/WrappedAssetCard.js +4 -2
  5. package/dist/cjs/common/EntityStore.js +45 -26
  6. package/dist/cjs/common/MultipleReferenceEditor.js +6 -1
  7. package/dist/cjs/common/SingleReferenceEditor.js +8 -2
  8. package/dist/cjs/common/queryClient.js +4 -91
  9. package/dist/cjs/common/useContentTypePermissions.js +6 -9
  10. package/dist/cjs/common/useEditorPermissions.js +5 -1
  11. package/dist/cjs/components/LinkActions/LinkEntityActions.js +2 -0
  12. package/dist/cjs/entries/MultipleEntryReferenceEditor.js +2 -1
  13. package/dist/cjs/entries/SingleEntryReferenceEditor.js +1 -0
  14. package/dist/cjs/entries/WrappedEntryCard/FetchingWrappedEntryCard.js +10 -1
  15. package/dist/cjs/entries/WrappedEntryCard/WrappedEntryCard.js +13 -2
  16. package/dist/cjs/resources/Cards/ResourceCard.spec.js +28 -2
  17. package/dist/esm/__fixtures__/FakeSdk.js +13 -20
  18. package/dist/esm/assets/WrappedAssetCard/AssetCardActions.js +11 -1
  19. package/dist/esm/assets/WrappedAssetCard/FetchingWrappedAssetCard.js +10 -1
  20. package/dist/esm/assets/WrappedAssetCard/WrappedAssetCard.js +4 -2
  21. package/dist/esm/common/EntityStore.js +45 -26
  22. package/dist/esm/common/MultipleReferenceEditor.js +6 -1
  23. package/dist/esm/common/SingleReferenceEditor.js +8 -2
  24. package/dist/esm/common/queryClient.js +1 -47
  25. package/dist/esm/common/useContentTypePermissions.js +6 -9
  26. package/dist/esm/common/useEditorPermissions.js +5 -1
  27. package/dist/esm/components/LinkActions/LinkEntityActions.js +2 -0
  28. package/dist/esm/entries/MultipleEntryReferenceEditor.js +2 -1
  29. package/dist/esm/entries/SingleEntryReferenceEditor.js +1 -0
  30. package/dist/esm/entries/WrappedEntryCard/FetchingWrappedEntryCard.js +10 -1
  31. package/dist/esm/entries/WrappedEntryCard/WrappedEntryCard.js +13 -2
  32. package/dist/esm/resources/Cards/ResourceCard.spec.js +28 -2
  33. package/dist/types/assets/WrappedAssetCard/AssetCardActions.d.ts +1 -0
  34. package/dist/types/assets/WrappedAssetCard/FetchingWrappedAssetCard.d.ts +5 -1
  35. package/dist/types/assets/WrappedAssetCard/WrappedAssetCard.d.ts +2 -1
  36. package/dist/types/common/EntityStore.d.ts +4 -2
  37. package/dist/types/common/ReferenceEditor.d.ts +5 -1
  38. package/dist/types/common/customCardTypes.d.ts +1 -0
  39. package/dist/types/common/queryClient.d.ts +3 -7
  40. package/dist/types/entries/WrappedEntryCard/FetchingWrappedEntryCard.d.ts +5 -1
  41. package/dist/types/entries/WrappedEntryCard/WrappedEntryCard.d.ts +2 -1
  42. package/package.json +4 -4
@@ -45,15 +45,6 @@ function newReferenceEditorFakeSdk(props) {
45
45
  const delay = (ms)=>{
46
46
  return new Promise((resolve)=>setTimeout(resolve, ms));
47
47
  };
48
- const localizeContentTypes = (contentTypes)=>{
49
- return contentTypes.map((contentType)=>({
50
- ...contentType,
51
- fields: contentType.fields.map((field)=>({
52
- ...field,
53
- localized: true
54
- }))
55
- }));
56
- };
57
48
  const sdk = {
58
49
  field,
59
50
  locales,
@@ -106,6 +97,19 @@ function newReferenceEditorFakeSdk(props) {
106
97
  return _fixtures.contentTypes.published;
107
98
  }
108
99
  return Promise.reject({});
100
+ },
101
+ getMany: async ()=>{
102
+ return Promise.resolve({
103
+ items: [
104
+ _fixtures.contentTypes.published
105
+ ],
106
+ total: 1,
107
+ skip: 0,
108
+ limit: 1000,
109
+ sys: {
110
+ type: 'Array'
111
+ }
112
+ });
109
113
  }
110
114
  },
111
115
  Locale: {
@@ -114,17 +118,6 @@ function newReferenceEditorFakeSdk(props) {
114
118
  },
115
119
  space: {
116
120
  ...space,
117
- getCachedContentTypes () {
118
- return localizeContentTypes(space.getCachedContentTypes());
119
- },
120
- getContentTypes () {
121
- return Promise.resolve(space.getContentTypes().then((response)=>{
122
- return {
123
- ...response,
124
- items: localizeContentTypes(response.items)
125
- };
126
- }));
127
- },
128
121
  async getEntityScheduledActions () {
129
122
  return [];
130
123
  }
@@ -18,6 +18,7 @@ _export(exports, {
18
18
  });
19
19
  const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
20
20
  const _f36components = require("@contentful/f36-components");
21
+ const _f36icons = require("@contentful/f36-icons");
21
22
  const _f36tokens = /*#__PURE__*/ _interop_require_default(require("@contentful/f36-tokens"));
22
23
  const _fieldeditorshared = require("@contentful/field-editor-shared");
23
24
  const _emotion = require("emotion");
@@ -134,7 +135,7 @@ function renderAssetInfo(props) {
134
135
  ];
135
136
  }
136
137
  function renderActions(props) {
137
- const { entityFile, isDisabled, onEdit, onRemove } = props;
138
+ const { entityFile, isDisabled, onEdit, onRemove, onAddToReleaseAction } = props;
138
139
  return [
139
140
  /*#__PURE__*/ _react.createElement(_f36components.MenuSectionTitle, {
140
141
  key: "section-title"
@@ -144,6 +145,15 @@ function renderActions(props) {
144
145
  onClick: onEdit,
145
146
  testId: "card-action-edit"
146
147
  }, "Edit") : null,
148
+ onAddToReleaseAction ? /*#__PURE__*/ _react.createElement(_f36components.MenuItem, {
149
+ key: "add-to-release",
150
+ testId: "add-to-release",
151
+ onClick: ()=>{
152
+ onAddToReleaseAction();
153
+ }
154
+ }, /*#__PURE__*/ _react.createElement(_f36icons.PlusIcon, {
155
+ size: "tiny"
156
+ }), "Add to release") : null,
147
157
  entityFile ? /*#__PURE__*/ _react.createElement(_f36components.MenuItem, {
148
158
  key: "download",
149
159
  onClick: ()=>{
@@ -72,6 +72,14 @@ function FetchingWrappedAssetCard(props) {
72
72
  locales: props.sdk.locales,
73
73
  isReference: true
74
74
  });
75
+ const onAddToRelease = ()=>{
76
+ if (asset && props.addReferenceToRelease) {
77
+ void props.addReferenceToRelease(asset, props.sdk.field.locale, {
78
+ openModalForVersionSelection: true,
79
+ skipNestedReferencesPrompt: true
80
+ });
81
+ }
82
+ };
75
83
  _react.useEffect(()=>{
76
84
  if (asset) {
77
85
  props.onAction && props.onAction({
@@ -138,7 +146,8 @@ function FetchingWrappedAssetCard(props) {
138
146
  activeLocales,
139
147
  releaseStatusMap,
140
148
  release: props.sdk.release,
141
- releaseEntityStatus
149
+ releaseEntityStatus,
150
+ onAddToRelease
142
151
  };
143
152
  if (status === 'loading') {
144
153
  return props.viewType === 'link' ? /*#__PURE__*/ _react.createElement(_f36components.EntryCard, {
@@ -87,7 +87,7 @@ function getFileType(file) {
87
87
  return groupToIconMap[groupName] || 'archive';
88
88
  }
89
89
  const THUMBNAIL_SIZE = 150;
90
- const WrappedAssetCard = ({ asset, className, size, localeCode, defaultLocaleCode, activeLocales, localesStatusMap, isDisabled, isSelected, isClickable, useLocalizedEntityStatus, renderDragHandle, getEntityScheduledActions, onEdit, getAssetUrl, onRemove, releaseEntityStatus, releaseStatusMap, release })=>{
90
+ const WrappedAssetCard = ({ asset, className, size, localeCode, defaultLocaleCode, activeLocales, localesStatusMap, isDisabled, isSelected, isClickable, useLocalizedEntityStatus, renderDragHandle, getEntityScheduledActions, onEdit, getAssetUrl, onRemove, releaseEntityStatus, releaseStatusMap, release, onAddToRelease })=>{
91
91
  const status = _fieldeditorshared.entityHelpers.getEntityStatus(asset.sys, useLocalizedEntityStatus ? localeCode : undefined);
92
92
  const entityFile = asset.fields.file ? asset.fields.file[localeCode] || asset.fields.file[defaultLocaleCode] : undefined;
93
93
  const imageUrl = _react.useMemo(()=>{
@@ -120,6 +120,7 @@ const WrappedAssetCard = ({ asset, className, size, localeCode, defaultLocaleCod
120
120
  defaultTitle: 'Untitled'
121
121
  });
122
122
  const href = getAssetUrl ? getAssetUrl(asset.sys.id) : undefined;
123
+ const onAddToReleaseAction = releaseEntityStatus === 'notInRelease' && release !== undefined && onAddToRelease !== undefined && !isDisabled ? onAddToRelease : undefined;
123
124
  return /*#__PURE__*/ _react.createElement(_f36components.AssetCard, {
124
125
  as: isClickable && href ? 'a' : 'article',
125
126
  type: getFileType(entityFile),
@@ -158,7 +159,8 @@ const WrappedAssetCard = ({ asset, className, size, localeCode, defaultLocaleCod
158
159
  entityFile,
159
160
  isDisabled: isDisabled,
160
161
  onEdit,
161
- onRemove
162
+ onRemove,
163
+ onAddToReleaseAction
162
164
  }),
163
165
  ...entityFile ? (0, _AssetCardActions.renderAssetInfo)({
164
166
  entityFile
@@ -41,6 +41,7 @@ _export(exports, {
41
41
  }
42
42
  });
43
43
  const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
44
+ const _fieldeditorshared = require("@contentful/field-editor-shared");
44
45
  const _constate = /*#__PURE__*/ _interop_require_default(require("constate"));
45
46
  const _contentfulmanagement = require("contentful-management");
46
47
  const _lodash = require("lodash");
@@ -149,7 +150,7 @@ function handleResourceFetchError(resourceFetchError, resourceTypeEntity) {
149
150
  throw resourceFetchError;
150
151
  }
151
152
  const isEntityQueryKey = (queryKey)=>{
152
- return Array.isArray(queryKey) && (queryKey[0] === 'Entry' || queryKey[0] === 'Asset') && queryKey.length === 4;
153
+ return Array.isArray(queryKey) && (queryKey[0] === 'Entry' || queryKey[0] === 'Asset') && (queryKey.length === 4 || queryKey.length === 5);
153
154
  };
154
155
  async function fetchContentfulEntry({ urn, fetch, options }) {
155
156
  const resourceId = urn.split(':', 6)[5];
@@ -162,18 +163,10 @@ async function fetchContentfulEntry({ urn, fetch, options }) {
162
163
  const environmentId = resourceIdMatch?.groups?.environmentId || 'master';
163
164
  const entryId = resourceIdMatch.groups.entityId;
164
165
  const [space, entry] = await Promise.all([
165
- fetch([
166
- 'space',
167
- spaceId
168
- ], ({ cmaClient })=>cmaClient.space.get({
166
+ fetch((0, _fieldeditorshared.createGetSpaceKey)(spaceId), ({ cmaClient })=>cmaClient.space.get({
169
167
  spaceId
170
168
  }), options),
171
- fetch([
172
- 'entry',
173
- spaceId,
174
- environmentId,
175
- entryId
176
- ], ({ cmaClient })=>cmaClient.entry.get({
169
+ fetch((0, _fieldeditorshared.createGetEntryKey)(spaceId, environmentId, entryId), ({ cmaClient })=>cmaClient.entry.get({
177
170
  spaceId,
178
171
  environmentId,
179
172
  entryId
@@ -181,12 +174,7 @@ async function fetchContentfulEntry({ urn, fetch, options }) {
181
174
  ]);
182
175
  const contentTypeId = entry.sys.contentType.sys.id;
183
176
  const [contentType, defaultLocaleCode] = await Promise.all([
184
- fetch([
185
- 'contentType',
186
- spaceId,
187
- environmentId,
188
- contentTypeId
189
- ], ({ cmaClient })=>cmaClient.contentType.get({
177
+ fetch((0, _fieldeditorshared.createGetContentTypeKey)(spaceId, environmentId, contentTypeId), ({ cmaClient })=>cmaClient.contentType.get({
190
178
  contentTypeId,
191
179
  spaceId,
192
180
  environmentId
@@ -470,25 +458,53 @@ const [InternalServiceProvider, useFetch, useEntityLoader, useCurrentIds] = (0,
470
458
  const onSlideInNavigation = props.sdk.navigator.onSlideInNavigation;
471
459
  (0, _react.useEffect)(()=>{
472
460
  function findSameSpaceQueries() {
473
- return queryCache.findAll({
474
- type: 'active',
461
+ const queries = queryCache.findAll({
475
462
  predicate: (query)=>isSameSpaceEntityQueryKey(query.queryKey)
476
463
  });
464
+ return queries;
477
465
  }
478
466
  if (typeof onEntityChanged !== 'function') {
479
- return onSlideInNavigation(({ oldSlideLevel, newSlideLevel })=>{
467
+ return onSlideInNavigation(async ({ oldSlideLevel, newSlideLevel })=>{
480
468
  if (oldSlideLevel > newSlideLevel) {
481
- findSameSpaceQueries().forEach((query)=>{
482
- void queryClient.invalidateQueries(query.queryKey);
483
- });
469
+ const queries = findSameSpaceQueries();
470
+ await Promise.all(queries.map(async (query)=>{
471
+ const [entityType, entityId, spaceId, environmentId, releaseId] = query.queryKey;
472
+ try {
473
+ let freshData;
474
+ if (entityType === 'Entry') {
475
+ freshData = await cmaClient.entry.get({
476
+ entryId: entityId,
477
+ spaceId: spaceId,
478
+ environmentId: environmentId,
479
+ releaseId: releaseId
480
+ });
481
+ } else if (entityType === 'Asset') {
482
+ freshData = await cmaClient.asset.get({
483
+ assetId: entityId,
484
+ spaceId: spaceId,
485
+ environmentId: environmentId,
486
+ releaseId: releaseId
487
+ });
488
+ } else {
489
+ await queryClient.invalidateQueries(query.queryKey);
490
+ return;
491
+ }
492
+ queryClient.setQueryData(query.queryKey, freshData);
493
+ } catch (error) {
494
+ await queryClient.invalidateQueries(query.queryKey);
495
+ }
496
+ }));
484
497
  }
485
498
  });
486
499
  }
487
500
  const subscribeQuery = ({ queryKey, queryHash })=>{
488
501
  const [entityType, entityId, , , releaseId] = queryKey;
489
502
  entityChangeUnsubscribers.current[queryHash] = onEntityChanged(entityType, entityId, (data)=>{
490
- if ((0, _lodash.get)(data, 'sys.release.id') === releaseId) {
503
+ const dataReleaseId = (0, _lodash.get)(data, 'sys.release.id');
504
+ if (dataReleaseId === releaseId) {
491
505
  queryClient.setQueryData(queryKey, data);
506
+ } else if (releaseId && !dataReleaseId) {
507
+ void queryClient.invalidateQueries(queryKey);
492
508
  }
493
509
  });
494
510
  };
@@ -520,7 +536,8 @@ const [InternalServiceProvider, useFetch, useEntityLoader, useCurrentIds] = (0,
520
536
  isSameSpaceEntityQueryKey,
521
537
  queryClient,
522
538
  getEntity,
523
- onSlideInNavigation
539
+ onSlideInNavigation,
540
+ cmaClient
524
541
  ]);
525
542
  const getResourceProvider = (0, _react.useCallback)(function getResourceProvider(organizationId, appDefinitionId) {
526
543
  const queryKey = [
@@ -626,5 +643,7 @@ function useResourceProvider(organizationId, appDefinitionId) {
626
643
  };
627
644
  }
628
645
  function EntityProvider({ children, ...props }) {
629
- return /*#__PURE__*/ _react.default.createElement(_queryClient.SharedQueryClientProvider, null, /*#__PURE__*/ _react.default.createElement(InternalServiceProvider, props, children));
646
+ return /*#__PURE__*/ _react.default.createElement(_queryClient.SharedQueryClientProvider, {
647
+ client: props.queryClient
648
+ }, /*#__PURE__*/ _react.default.createElement(InternalServiceProvider, props, children));
630
649
  }
@@ -9,10 +9,12 @@ Object.defineProperty(exports, "MultipleReferenceEditor", {
9
9
  }
10
10
  });
11
11
  const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
12
+ const _fieldeditorshared = require("@contentful/field-editor-shared");
12
13
  const _sortable = require("@dnd-kit/sortable");
13
14
  const _components = require("../components");
14
15
  const _LinkEntityActions = require("../components/LinkActions/LinkEntityActions");
15
16
  const _useSortIDs = require("../utils/useSortIDs");
17
+ const _queryClient = require("./queryClient");
16
18
  const _ReferenceEditor = require("./ReferenceEditor");
17
19
  const _useEditorPermissions = require("./useEditorPermissions");
18
20
  function _getRequireWildcardCache(nodeInterop) {
@@ -145,7 +147,10 @@ function Editor(props) {
145
147
  }));
146
148
  }
147
149
  function MultipleReferenceEditor(props) {
148
- const allContentTypes = props.sdk.space.getCachedContentTypes();
150
+ return /*#__PURE__*/ _react.createElement(_queryClient.SharedQueryClientProvider, null, /*#__PURE__*/ _react.createElement(MultipleReferenceEditorInner, props));
151
+ }
152
+ function MultipleReferenceEditorInner(props) {
153
+ const { contentTypes: allContentTypes } = (0, _fieldeditorshared.useContentTypes)(props.sdk);
149
154
  return /*#__PURE__*/ _react.createElement(_ReferenceEditor.ReferenceEditor, props, ({ value, disabled, setValue, externalReset })=>{
150
155
  return /*#__PURE__*/ _react.createElement(Editor, {
151
156
  ...props,
@@ -9,8 +9,10 @@ Object.defineProperty(exports, "SingleReferenceEditor", {
9
9
  }
10
10
  });
11
11
  const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
12
+ const _fieldeditorshared = require("@contentful/field-editor-shared");
12
13
  const _components = require("../components");
13
14
  const _LinkEntityActions = require("../components/LinkActions/LinkEntityActions");
15
+ const _queryClient = require("./queryClient");
14
16
  const _ReferenceEditor = require("./ReferenceEditor");
15
17
  const _useEditorPermissions = require("./useEditorPermissions");
16
18
  function _getRequireWildcardCache(nodeInterop) {
@@ -98,11 +100,15 @@ function Editor(props) {
98
100
  }
99
101
  return props.children({
100
102
  ...props,
101
- renderCustomCard: props.renderCustomCard && customCardRenderer
103
+ renderCustomCard: props.renderCustomCard && customCardRenderer,
104
+ addReferenceToRelease: props.addReferenceToRelease
102
105
  });
103
106
  }
104
107
  function SingleReferenceEditor(props) {
105
- const allContentTypes = props.sdk.space.getCachedContentTypes();
108
+ return /*#__PURE__*/ _react.createElement(_queryClient.SharedQueryClientProvider, null, /*#__PURE__*/ _react.createElement(SingleReferenceEditorInner, props));
109
+ }
110
+ function SingleReferenceEditorInner(props) {
111
+ const { contentTypes: allContentTypes } = (0, _fieldeditorshared.useContentTypes)(props.sdk);
106
112
  return /*#__PURE__*/ _react.createElement(_ReferenceEditor.ReferenceEditor, props, ({ value, setValue, disabled, externalReset })=>{
107
113
  return /*#__PURE__*/ _react.createElement(Editor, {
108
114
  ...props,
@@ -10,100 +10,13 @@ function _export(target, all) {
10
10
  }
11
11
  _export(exports, {
12
12
  SharedQueryClientProvider: function() {
13
- return SharedQueryClientProvider;
13
+ return _fieldeditorshared.SharedQueryClientProvider;
14
14
  },
15
15
  useQuery: function() {
16
- return useQuery;
16
+ return _fieldeditorshared.useQuery;
17
17
  },
18
18
  useQueryClient: function() {
19
- return useQueryClient;
19
+ return _fieldeditorshared.useQueryClient;
20
20
  }
21
21
  });
22
- const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
23
- const _reactquery = require("@tanstack/react-query");
24
- function _getRequireWildcardCache(nodeInterop) {
25
- if (typeof WeakMap !== "function") return null;
26
- var cacheBabelInterop = new WeakMap();
27
- var cacheNodeInterop = new WeakMap();
28
- return (_getRequireWildcardCache = function(nodeInterop) {
29
- return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
30
- })(nodeInterop);
31
- }
32
- function _interop_require_wildcard(obj, nodeInterop) {
33
- if (!nodeInterop && obj && obj.__esModule) {
34
- return obj;
35
- }
36
- if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
37
- return {
38
- default: obj
39
- };
40
- }
41
- var cache = _getRequireWildcardCache(nodeInterop);
42
- if (cache && cache.has(obj)) {
43
- return cache.get(obj);
44
- }
45
- var newObj = {
46
- __proto__: null
47
- };
48
- var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
49
- for(var key in obj){
50
- if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
51
- var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
52
- if (desc && (desc.get || desc.set)) {
53
- Object.defineProperty(newObj, key, desc);
54
- } else {
55
- newObj[key] = obj[key];
56
- }
57
- }
58
- }
59
- newObj.default = obj;
60
- if (cache) {
61
- cache.set(obj, newObj);
62
- }
63
- return newObj;
64
- }
65
- const clientContext = /*#__PURE__*/ _react.createContext(undefined);
66
- function useMaybeHostQueryClient() {
67
- try {
68
- return (0, _reactquery.useQueryClient)();
69
- } catch {
70
- return undefined;
71
- }
72
- }
73
- function useQueryClient() {
74
- const client = _react.useContext(clientContext);
75
- const hostClient = useMaybeHostQueryClient();
76
- return _react.useMemo(()=>{
77
- if (client) {
78
- return client;
79
- }
80
- if (hostClient) return hostClient;
81
- return new _reactquery.QueryClient({
82
- defaultOptions: {
83
- queries: {
84
- useErrorBoundary: false,
85
- refetchOnWindowFocus: false,
86
- refetchOnReconnect: true,
87
- refetchOnMount: false,
88
- staleTime: Infinity,
89
- retry: false
90
- }
91
- }
92
- });
93
- }, [
94
- client,
95
- hostClient
96
- ]);
97
- }
98
- const useQuery = (key, fn, opt)=>{
99
- return (0, _reactquery.useQuery)(key, fn, {
100
- ...opt,
101
- context: clientContext
102
- });
103
- };
104
- function SharedQueryClientProvider({ children }) {
105
- const client = useQueryClient();
106
- return /*#__PURE__*/ _react.createElement(clientContext.Provider, {
107
- value: client
108
- }, children);
109
- }
22
+ const _fieldeditorshared = require("@contentful/field-editor-shared");
@@ -26,6 +26,9 @@ function useContentTypePermissions({ entityType, validations, sdk, allContentTyp
26
26
  if (entityType === 'Asset') {
27
27
  return [];
28
28
  }
29
+ if (validations.contentTypes && allContentTypes.length === 0) {
30
+ return [];
31
+ }
29
32
  if (validations.contentTypes) {
30
33
  return allContentTypes.filter((ct)=>validations.contentTypes?.includes(ct.sys.id));
31
34
  }
@@ -38,21 +41,15 @@ function useContentTypePermissions({ entityType, validations, sdk, allContentTyp
38
41
  const [creatableContentTypes, setCreatableContentTypes] = (0, _react.useState)(availableContentTypes);
39
42
  const { canPerformActionOnEntryOfType } = (0, _useAccessApi.useAccessApi)(sdk.access);
40
43
  (0, _react.useEffect)(()=>{
41
- function getContentTypes(action) {
42
- return filter(availableContentTypes, (ct)=>canPerformActionOnEntryOfType(action, ct.sys.id));
43
- }
44
44
  async function checkContentTypeAccess() {
45
- const creatable = await getContentTypes('create');
46
- if (!(0, _isEqual.default)(creatable, creatableContentTypes)) {
47
- setCreatableContentTypes(creatable);
48
- }
45
+ const creatable = await filter(availableContentTypes, (ct)=>canPerformActionOnEntryOfType('create', ct.sys.id));
46
+ setCreatableContentTypes((creatableContentTypes)=>(0, _isEqual.default)(creatable, creatableContentTypes) ? creatableContentTypes : creatable);
49
47
  }
50
48
  if (availableContentTypes.length > 0) {
51
49
  void checkContentTypeAccess();
52
50
  }
53
51
  }, [
54
- availableContentTypes,
55
- creatableContentTypes
52
+ availableContentTypes
56
53
  ]);
57
54
  return {
58
55
  creatableContentTypes,
@@ -13,8 +13,12 @@ const _fromFieldValidations = require("../utils/fromFieldValidations");
13
13
  const _useAccessApi = require("./useAccessApi");
14
14
  const _useContentTypePermissions = require("./useContentTypePermissions");
15
15
  function useEditorPermissions({ sdk, entityType, parameters, allContentTypes }) {
16
+ const fieldValidations = sdk.field.validations;
17
+ const itemsValidations = sdk.field.type === 'Array' ? sdk.field.items?.validations : undefined;
16
18
  const validations = (0, _react.useMemo)(()=>(0, _fromFieldValidations.fromFieldValidations)(sdk.field), [
17
- sdk.field
19
+ sdk.field,
20
+ JSON.stringify(fieldValidations),
21
+ JSON.stringify(itemsValidations)
18
22
  ]);
19
23
  const [canCreateEntity, setCanCreateEntity] = (0, _react.useState)(true);
20
24
  const [canLinkEntity, setCanLinkEntity] = (0, _react.useState)(true);
@@ -130,6 +130,7 @@ function useLinkActionsProps(props) {
130
130
  }, [
131
131
  sdk,
132
132
  entityType,
133
+ editorPermissions,
133
134
  onLinkedExisting
134
135
  ]);
135
136
  const onLinkSeveralExisting = _react.useCallback(async (index)=>{
@@ -145,6 +146,7 @@ function useLinkActionsProps(props) {
145
146
  }, [
146
147
  sdk,
147
148
  entityType,
149
+ editorPermissions,
148
150
  onLinkedExisting
149
151
  ]);
150
152
  return (0, _react.useMemo)(()=>({
@@ -85,7 +85,8 @@ function MultipleEntryReferenceEditor(props) {
85
85
  onMoveTop: index !== 0 ? ()=>childrenProps.onMove(index, 0) : undefined,
86
86
  onMoveBottom: index !== lastIndex ? ()=>childrenProps.onMove(index, lastIndex) : undefined,
87
87
  renderDragHandle: DragHandle,
88
- isBeingDragged: index === indexToUpdate
88
+ isBeingDragged: index === indexToUpdate,
89
+ addReferenceToRelease: props.addReferenceToRelease
89
90
  });
90
91
  }));
91
92
  }
@@ -68,6 +68,7 @@ function SingleEntryReferenceEditor(props) {
68
68
  hasCardEditActions: hasCardEditActions,
69
69
  hasCardRemoveActions: hasCardRemoveActions,
70
70
  activeLocales: activeLocales,
71
+ addReferenceToRelease: props.addReferenceToRelease,
71
72
  onRemove: ()=>{
72
73
  setValue(null);
73
74
  }
@@ -114,6 +114,14 @@ function FetchingWrappedEntryCard(props) {
114
114
  contentTypeId: entry?.sys?.contentType?.sys?.id ?? ''
115
115
  });
116
116
  };
117
+ const onAddToRelease = ()=>{
118
+ if (entry && props.addReferenceToRelease) {
119
+ void props.addReferenceToRelease(entry, props.sdk.field.locale, {
120
+ openModalForVersionSelection: true,
121
+ skipNestedReferencesPrompt: true
122
+ });
123
+ }
124
+ };
117
125
  _react.useEffect(()=>{
118
126
  if (entry) {
119
127
  props.onAction?.({
@@ -168,7 +176,8 @@ function FetchingWrappedEntryCard(props) {
168
176
  activeLocales: props.activeLocales,
169
177
  releaseStatusMap,
170
178
  release: props.sdk.release,
171
- releaseEntityStatus
179
+ releaseEntityStatus,
180
+ onAddToRelease
172
181
  };
173
182
  const { hasCardEditActions, hasCardMoveActions, hasCardRemoveActions } = props;
174
183
  function renderDefaultCard(props) {
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "WrappedEntryCard", {
10
10
  });
11
11
  const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
12
12
  const _f36components = require("@contentful/f36-components");
13
+ const _f36icons = require("@contentful/f36-icons");
13
14
  const _fieldeditorshared = require("@contentful/field-editor-shared");
14
15
  const _components = require("../../components");
15
16
  const _SpaceName = require("../../components/SpaceName/SpaceName");
@@ -61,7 +62,7 @@ const defaultProps = {
61
62
  hasCardMoveActions: true,
62
63
  hasCardRemoveActions: true
63
64
  };
64
- function WrappedEntryCard({ entry, entryUrl, contentType, activeLocales, localeCode, defaultLocaleCode, localesStatusMap, useLocalizedEntityStatus, size, spaceName, isClickable, isDisabled, isSelected, hasCardMoveActions, hasCardEditActions, hasCardRemoveActions, renderDragHandle, getAsset, getEntityScheduledActions, onClick, onEdit, onRemove, onMoveTop, onMoveBottom, releaseEntityStatus, releaseStatusMap, release }) {
65
+ function WrappedEntryCard({ entry, entryUrl, contentType, activeLocales, localeCode, defaultLocaleCode, localesStatusMap, useLocalizedEntityStatus, size, spaceName, isClickable, isDisabled, isSelected, hasCardMoveActions, hasCardEditActions, hasCardRemoveActions, renderDragHandle, getAsset, getEntityScheduledActions, onClick, onEdit, onRemove, onMoveTop, onMoveBottom, releaseEntityStatus, releaseStatusMap, release, onAddToRelease }) {
65
66
  const [file, setFile] = _react.useState(null);
66
67
  _react.useEffect(()=>{
67
68
  let mounted = true;
@@ -112,6 +113,7 @@ function WrappedEntryCard({ entry, entryUrl, contentType, activeLocales, localeC
112
113
  localeCode,
113
114
  defaultLocaleCode
114
115
  });
116
+ const showAddToReleaseAction = releaseEntityStatus === 'notInRelease' && release !== undefined && onAddToRelease !== undefined && !isDisabled;
115
117
  return /*#__PURE__*/ _react.createElement(_f36components.EntryCard, {
116
118
  as: isClickable && entryUrl ? 'a' : 'article',
117
119
  href: isClickable ? entryUrl : undefined,
@@ -142,7 +144,7 @@ function WrappedEntryCard({ entry, entryUrl, contentType, activeLocales, localeC
142
144
  dragHandleRender: renderDragHandle,
143
145
  withDragHandle: !!renderDragHandle && !isDisabled,
144
146
  draggable: !!renderDragHandle && !isDisabled,
145
- actions: onEdit || onRemove ? [
147
+ actions: onEdit || onRemove || showAddToReleaseAction ? [
146
148
  hasCardEditActions && onEdit ? /*#__PURE__*/ _react.createElement(_f36components.MenuItem, {
147
149
  key: "edit",
148
150
  testId: "edit",
@@ -157,6 +159,15 @@ function WrappedEntryCard({ entry, entryUrl, contentType, activeLocales, localeC
157
159
  onRemove && onRemove();
158
160
  }
159
161
  }, "Remove") : null,
162
+ showAddToReleaseAction ? /*#__PURE__*/ _react.createElement(_f36components.MenuItem, {
163
+ key: "add-to-release",
164
+ testId: "add-to-release",
165
+ onClick: ()=>{
166
+ onAddToRelease();
167
+ }
168
+ }, /*#__PURE__*/ _react.createElement(_f36icons.PlusIcon, {
169
+ size: "tiny"
170
+ }), "Add to release") : null,
160
171
  hasCardMoveActions && (onMoveTop || onMoveBottom) && !isDisabled ? /*#__PURE__*/ _react.createElement(_f36components.MenuDivider, {
161
172
  key: "divider"
162
173
  }) : null,
@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", {
4
4
  });
5
5
  const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
6
6
  require("@testing-library/jest-dom");
7
+ const _fieldeditorshared = require("@contentful/field-editor-shared");
8
+ const _fieldeditortestutils = require("@contentful/field-editor-test-utils");
7
9
  const _react1 = require("@testing-library/react");
8
10
  const _published_content_typejson = /*#__PURE__*/ _interop_require_default(require("../../__fixtures__/content-type/published_content_type.json"));
9
11
  const _published_entry_non_masterjson = /*#__PURE__*/ _interop_require_default(require("../../__fixtures__/entry/published_entry_non_master.json"));
@@ -217,9 +219,33 @@ describe('ResourceCard', ()=>{
217
219
  _react1.fireEvent.mouseEnter(getByText(_indifferent_spacejson.default.name));
218
220
  await (0, _react1.waitFor)(()=>expect(getByText(tooltipContent)).toBeDefined());
219
221
  });
220
- it('renders skeleton when no data is provided', ()=>{
221
- const { getByTestId } = renderResourceCard();
222
+ it('renders skeleton while data is loading', async ()=>{
223
+ const queryClient = (0, _fieldeditortestutils.createTestQueryClient)();
224
+ let resolveEntry;
225
+ const pendingPromise = new Promise((resolve)=>{
226
+ resolveEntry = resolve;
227
+ });
228
+ sdk.cma.entry.get.mockReturnValueOnce(pendingPromise);
229
+ const { getByTestId, queryByTestId } = (0, _react1.render)(/*#__PURE__*/ _react.createElement(_fieldeditorshared.SharedQueryClientProvider, {
230
+ client: queryClient
231
+ }, /*#__PURE__*/ _react.createElement(_EntityStore.EntityProvider, {
232
+ sdk: sdk
233
+ }, /*#__PURE__*/ _react.createElement(_ResourceCard.ResourceCard, {
234
+ isDisabled: false,
235
+ getEntryRouteHref: ()=>'',
236
+ resourceLink: {
237
+ sys: {
238
+ type: 'ResourceLink',
239
+ linkType: 'Contentful:Entry',
240
+ urn: resolvableEntryUrn
241
+ }
242
+ }
243
+ }))));
222
244
  expect(getByTestId('cf-ui-skeleton-form')).toBeDefined();
245
+ expect(queryByTestId('cf-ui-entry-card')).toBeNull();
246
+ resolveEntry(_published_entryjson.default);
247
+ await (0, _react1.waitFor)(()=>expect(getByTestId('cf-ui-entry-card')).toBeDefined());
248
+ expect(queryByTestId('cf-ui-skeleton-form')).toBeNull();
223
249
  });
224
250
  it('renders unsupported entity card when resource type is unknown', async ()=>{
225
251
  const { getByText } = renderResourceCard({