@contentful/field-editor-reference 6.18.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 (55) 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 +59 -33
  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.js +2 -1
  17. package/dist/cjs/resources/Cards/ResourceCard.spec.js +28 -2
  18. package/dist/cjs/resources/MultipleResourceReferenceEditor.js +1 -0
  19. package/dist/cjs/resources/MultipleResourceReferenceEditor.spec.js +2 -1
  20. package/dist/cjs/resources/SingleResourceReferenceEditor.js +1 -0
  21. package/dist/cjs/resources/SingleResourceReferenceEditor.spec.js +2 -1
  22. package/dist/cjs/resources/useResourceLinkActions.js +10 -5
  23. package/dist/esm/__fixtures__/FakeSdk.js +13 -20
  24. package/dist/esm/assets/WrappedAssetCard/AssetCardActions.js +11 -1
  25. package/dist/esm/assets/WrappedAssetCard/FetchingWrappedAssetCard.js +10 -1
  26. package/dist/esm/assets/WrappedAssetCard/WrappedAssetCard.js +4 -2
  27. package/dist/esm/common/EntityStore.js +59 -33
  28. package/dist/esm/common/MultipleReferenceEditor.js +6 -1
  29. package/dist/esm/common/SingleReferenceEditor.js +8 -2
  30. package/dist/esm/common/queryClient.js +1 -47
  31. package/dist/esm/common/useContentTypePermissions.js +6 -9
  32. package/dist/esm/common/useEditorPermissions.js +5 -1
  33. package/dist/esm/components/LinkActions/LinkEntityActions.js +2 -0
  34. package/dist/esm/entries/MultipleEntryReferenceEditor.js +2 -1
  35. package/dist/esm/entries/SingleEntryReferenceEditor.js +1 -0
  36. package/dist/esm/entries/WrappedEntryCard/FetchingWrappedEntryCard.js +10 -1
  37. package/dist/esm/entries/WrappedEntryCard/WrappedEntryCard.js +13 -2
  38. package/dist/esm/resources/Cards/ResourceCard.js +2 -1
  39. package/dist/esm/resources/Cards/ResourceCard.spec.js +28 -2
  40. package/dist/esm/resources/MultipleResourceReferenceEditor.js +1 -0
  41. package/dist/esm/resources/MultipleResourceReferenceEditor.spec.js +2 -1
  42. package/dist/esm/resources/SingleResourceReferenceEditor.js +1 -0
  43. package/dist/esm/resources/SingleResourceReferenceEditor.spec.js +2 -1
  44. package/dist/esm/resources/useResourceLinkActions.js +10 -5
  45. package/dist/types/assets/WrappedAssetCard/AssetCardActions.d.ts +1 -0
  46. package/dist/types/assets/WrappedAssetCard/FetchingWrappedAssetCard.d.ts +5 -1
  47. package/dist/types/assets/WrappedAssetCard/WrappedAssetCard.d.ts +2 -1
  48. package/dist/types/common/EntityStore.d.ts +6 -4
  49. package/dist/types/common/ReferenceEditor.d.ts +5 -1
  50. package/dist/types/common/customCardTypes.d.ts +1 -0
  51. package/dist/types/common/queryClient.d.ts +3 -7
  52. package/dist/types/entries/WrappedEntryCard/FetchingWrappedEntryCard.d.ts +5 -1
  53. package/dist/types/entries/WrappedEntryCard/WrappedEntryCard.d.ts +2 -1
  54. package/dist/types/resources/Cards/ResourceCard.d.ts +1 -0
  55. package/package.json +5 -5
@@ -63,9 +63,10 @@ function ResourceCardSkeleton() {
63
63
  });
64
64
  }
65
65
  function ExistingResourceCard(props) {
66
- const { resourceLink, inView, index = 0, locale } = props;
66
+ const { resourceLink, inView, index = 0, locale, referencingEntryId } = props;
67
67
  const resourceOptions = {
68
68
  locale,
69
+ referencingEntryId,
69
70
  priority: index * -1,
70
71
  enabled: inView,
71
72
  allowExternal: true
@@ -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({
@@ -149,6 +149,7 @@ function MultipleResourceReferenceEditor(props) {
149
149
  index: index,
150
150
  resourceLink: item,
151
151
  locale: props.sdk.field.locale,
152
+ referencingEntryId: props.sdk.ids.entry,
152
153
  isDisabled: isDisabled,
153
154
  renderDragHandle: DragHandle,
154
155
  onMoveTop: onMoveTop,
@@ -118,7 +118,8 @@ describe('Multiple resource editor', ()=>{
118
118
  const options = dialogFn.mock.calls[0][0];
119
119
  expect(options).toEqual({
120
120
  allowedResources: fieldDefinition.allowedResources,
121
- locale: 'en'
121
+ locale: 'en',
122
+ referencingEntryId: 'testEntry'
122
123
  });
123
124
  });
124
125
  it('hides the action button when insufficient permissions', async ()=>{
@@ -71,6 +71,7 @@ function SingleResourceReferenceEditor(props) {
71
71
  onRemove: ()=>props.sdk.field.removeValue(),
72
72
  resourceLink: value,
73
73
  locale: props.sdk.field.locale,
74
+ referencingEntryId: props.sdk.ids.entry,
74
75
  isDisabled: disabled,
75
76
  getEntryRouteHref: props.getEntryRouteHref
76
77
  }) : /*#__PURE__*/ _react.createElement(_LinkEntityActions.CombinedLinkEntityActions, {
@@ -114,7 +114,8 @@ describe('Single resource editor', ()=>{
114
114
  const options = dialogFn.mock.calls[0][0];
115
115
  expect(options).toEqual({
116
116
  allowedResources: fieldDefinition.allowedResources,
117
- locale: 'en'
117
+ locale: 'en',
118
+ referencingEntryId: 'testEntry'
118
119
  });
119
120
  });
120
121
  it('renders no the action button when permissions insufficient', async ()=>{
@@ -35,14 +35,18 @@ function useResourceLinkActions({ parameters, sdk }) {
35
35
  field
36
36
  ]);
37
37
  const multiple = field.type === 'Array';
38
+ const referencingEntryId = sdk.ids.entry;
39
+ const allowedResources = field.allowedResources;
38
40
  const onLinkExisting = (0, _react.useMemo)(()=>{
39
41
  const promptSelection = multiple ? async ()=>await dialogs.selectMultipleResourceEntities({
40
- allowedResources: field.allowedResources,
41
- locale: field.locale
42
+ allowedResources,
43
+ locale: field.locale,
44
+ referencingEntryId
42
45
  }) : async ()=>[
43
46
  await dialogs.selectSingleResourceEntity({
44
- allowedResources: field.allowedResources,
45
- locale: field.locale
47
+ allowedResources,
48
+ locale: field.locale,
49
+ referencingEntryId
46
50
  })
47
51
  ];
48
52
  return async ()=>{
@@ -50,8 +54,9 @@ function useResourceLinkActions({ parameters, sdk }) {
50
54
  };
51
55
  }, [
52
56
  dialogs,
53
- field.allowedResources,
57
+ allowedResources,
54
58
  field.locale,
59
+ referencingEntryId,
55
60
  multiple,
56
61
  onLinkedExisting
57
62
  ]);
@@ -35,15 +35,6 @@ export function newReferenceEditorFakeSdk(props) {
35
35
  const delay = (ms)=>{
36
36
  return new Promise((resolve)=>setTimeout(resolve, ms));
37
37
  };
38
- const localizeContentTypes = (contentTypes)=>{
39
- return contentTypes.map((contentType)=>({
40
- ...contentType,
41
- fields: contentType.fields.map((field)=>({
42
- ...field,
43
- localized: true
44
- }))
45
- }));
46
- };
47
38
  const sdk = {
48
39
  field,
49
40
  locales,
@@ -96,6 +87,19 @@ export function newReferenceEditorFakeSdk(props) {
96
87
  return contentTypes.published;
97
88
  }
98
89
  return Promise.reject({});
90
+ },
91
+ getMany: async ()=>{
92
+ return Promise.resolve({
93
+ items: [
94
+ contentTypes.published
95
+ ],
96
+ total: 1,
97
+ skip: 0,
98
+ limit: 1000,
99
+ sys: {
100
+ type: 'Array'
101
+ }
102
+ });
99
103
  }
100
104
  },
101
105
  Locale: {
@@ -104,17 +108,6 @@ export function newReferenceEditorFakeSdk(props) {
104
108
  },
105
109
  space: {
106
110
  ...space,
107
- getCachedContentTypes () {
108
- return localizeContentTypes(space.getCachedContentTypes());
109
- },
110
- getContentTypes () {
111
- return Promise.resolve(space.getContentTypes().then((response)=>{
112
- return {
113
- ...response,
114
- items: localizeContentTypes(response.items)
115
- };
116
- }));
117
- },
118
111
  async getEntityScheduledActions () {
119
112
  return [];
120
113
  }
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { MenuItem, Text, MenuSectionTitle } from '@contentful/f36-components';
3
+ import { PlusIcon } from '@contentful/f36-icons';
3
4
  import tokens from '@contentful/f36-tokens';
4
5
  import { shortenStorageUnit } from '@contentful/field-editor-shared';
5
6
  import { css } from 'emotion';
@@ -70,7 +71,7 @@ export function renderAssetInfo(props) {
70
71
  ];
71
72
  }
72
73
  export function renderActions(props) {
73
- const { entityFile, isDisabled, onEdit, onRemove } = props;
74
+ const { entityFile, isDisabled, onEdit, onRemove, onAddToReleaseAction } = props;
74
75
  return [
75
76
  /*#__PURE__*/ React.createElement(MenuSectionTitle, {
76
77
  key: "section-title"
@@ -80,6 +81,15 @@ export function renderActions(props) {
80
81
  onClick: onEdit,
81
82
  testId: "card-action-edit"
82
83
  }, "Edit") : null,
84
+ onAddToReleaseAction ? /*#__PURE__*/ React.createElement(MenuItem, {
85
+ key: "add-to-release",
86
+ testId: "add-to-release",
87
+ onClick: ()=>{
88
+ onAddToReleaseAction();
89
+ }
90
+ }, /*#__PURE__*/ React.createElement(PlusIcon, {
91
+ size: "tiny"
92
+ }), "Add to release") : null,
83
93
  entityFile ? /*#__PURE__*/ React.createElement(MenuItem, {
84
94
  key: "download",
85
95
  onClick: ()=>{
@@ -21,6 +21,14 @@ export function FetchingWrappedAssetCard(props) {
21
21
  locales: props.sdk.locales,
22
22
  isReference: true
23
23
  });
24
+ const onAddToRelease = ()=>{
25
+ if (asset && props.addReferenceToRelease) {
26
+ void props.addReferenceToRelease(asset, props.sdk.field.locale, {
27
+ openModalForVersionSelection: true,
28
+ skipNestedReferencesPrompt: true
29
+ });
30
+ }
31
+ };
24
32
  React.useEffect(()=>{
25
33
  if (asset) {
26
34
  props.onAction && props.onAction({
@@ -87,7 +95,8 @@ export function FetchingWrappedAssetCard(props) {
87
95
  activeLocales,
88
96
  releaseStatusMap,
89
97
  release: props.sdk.release,
90
- releaseEntityStatus
98
+ releaseEntityStatus,
99
+ onAddToRelease
91
100
  };
92
101
  if (status === 'loading') {
93
102
  return props.viewType === 'link' ? /*#__PURE__*/ React.createElement(EntryCard, {
@@ -31,7 +31,7 @@ function getFileType(file) {
31
31
  return groupToIconMap[groupName] || 'archive';
32
32
  }
33
33
  const THUMBNAIL_SIZE = 150;
34
- export const WrappedAssetCard = ({ asset, className, size, localeCode, defaultLocaleCode, activeLocales, localesStatusMap, isDisabled, isSelected, isClickable, useLocalizedEntityStatus, renderDragHandle, getEntityScheduledActions, onEdit, getAssetUrl, onRemove, releaseEntityStatus, releaseStatusMap, release })=>{
34
+ export const WrappedAssetCard = ({ asset, className, size, localeCode, defaultLocaleCode, activeLocales, localesStatusMap, isDisabled, isSelected, isClickable, useLocalizedEntityStatus, renderDragHandle, getEntityScheduledActions, onEdit, getAssetUrl, onRemove, releaseEntityStatus, releaseStatusMap, release, onAddToRelease })=>{
35
35
  const status = entityHelpers.getEntityStatus(asset.sys, useLocalizedEntityStatus ? localeCode : undefined);
36
36
  const entityFile = asset.fields.file ? asset.fields.file[localeCode] || asset.fields.file[defaultLocaleCode] : undefined;
37
37
  const imageUrl = React.useMemo(()=>{
@@ -64,6 +64,7 @@ export const WrappedAssetCard = ({ asset, className, size, localeCode, defaultLo
64
64
  defaultTitle: 'Untitled'
65
65
  });
66
66
  const href = getAssetUrl ? getAssetUrl(asset.sys.id) : undefined;
67
+ const onAddToReleaseAction = releaseEntityStatus === 'notInRelease' && release !== undefined && onAddToRelease !== undefined && !isDisabled ? onAddToRelease : undefined;
67
68
  return /*#__PURE__*/ React.createElement(AssetCard, {
68
69
  as: isClickable && href ? 'a' : 'article',
69
70
  type: getFileType(entityFile),
@@ -102,7 +103,8 @@ export const WrappedAssetCard = ({ asset, className, size, localeCode, defaultLo
102
103
  entityFile,
103
104
  isDisabled: isDisabled,
104
105
  onEdit,
105
- onRemove
106
+ onRemove,
107
+ onAddToReleaseAction
106
108
  }),
107
109
  ...entityFile ? renderAssetInfo({
108
110
  entityFile
@@ -12,6 +12,7 @@ function _define_property(obj, key, value) {
12
12
  return obj;
13
13
  }
14
14
  import React, { useCallback, useEffect, useMemo, useRef } from 'react';
15
+ import { createGetContentTypeKey, createGetEntryKey, createGetSpaceKey } from '@contentful/field-editor-shared';
15
16
  import constate from 'constate';
16
17
  import { fetchAll } from 'contentful-management';
17
18
  import { get } from 'lodash';
@@ -61,7 +62,7 @@ function handleResourceFetchError(resourceFetchError, resourceTypeEntity) {
61
62
  throw resourceFetchError;
62
63
  }
63
64
  const isEntityQueryKey = (queryKey)=>{
64
- return Array.isArray(queryKey) && (queryKey[0] === 'Entry' || queryKey[0] === 'Asset') && queryKey.length === 4;
65
+ return Array.isArray(queryKey) && (queryKey[0] === 'Entry' || queryKey[0] === 'Asset') && (queryKey.length === 4 || queryKey.length === 5);
65
66
  };
66
67
  async function fetchContentfulEntry({ urn, fetch, options }) {
67
68
  const resourceId = urn.split(':', 6)[5];
@@ -74,18 +75,10 @@ async function fetchContentfulEntry({ urn, fetch, options }) {
74
75
  const environmentId = resourceIdMatch?.groups?.environmentId || 'master';
75
76
  const entryId = resourceIdMatch.groups.entityId;
76
77
  const [space, entry] = await Promise.all([
77
- fetch([
78
- 'space',
79
- spaceId
80
- ], ({ cmaClient })=>cmaClient.space.get({
78
+ fetch(createGetSpaceKey(spaceId), ({ cmaClient })=>cmaClient.space.get({
81
79
  spaceId
82
80
  }), options),
83
- fetch([
84
- 'entry',
85
- spaceId,
86
- environmentId,
87
- entryId
88
- ], ({ cmaClient })=>cmaClient.entry.get({
81
+ fetch(createGetEntryKey(spaceId, environmentId, entryId), ({ cmaClient })=>cmaClient.entry.get({
89
82
  spaceId,
90
83
  environmentId,
91
84
  entryId
@@ -93,12 +86,7 @@ async function fetchContentfulEntry({ urn, fetch, options }) {
93
86
  ]);
94
87
  const contentTypeId = entry.sys.contentType.sys.id;
95
88
  const [contentType, defaultLocaleCode] = await Promise.all([
96
- fetch([
97
- 'contentType',
98
- spaceId,
99
- environmentId,
100
- contentTypeId
101
- ], ({ cmaClient })=>cmaClient.contentType.get({
89
+ fetch(createGetContentTypeKey(spaceId, environmentId, contentTypeId), ({ cmaClient })=>cmaClient.contentType.get({
102
90
  contentTypeId,
103
91
  spaceId,
104
92
  environmentId
@@ -126,7 +114,7 @@ async function fetchContentfulEntry({ urn, fetch, options }) {
126
114
  contentType: contentType
127
115
  };
128
116
  }
129
- async function fetchExternalResource({ urn, fetch, options, spaceId, environmentId, resourceType, locale }) {
117
+ async function fetchExternalResource({ urn, fetch, options, spaceId, environmentId, resourceType, locale, referencingEntryId }) {
130
118
  let resourceFetchError;
131
119
  const [resource, resourceTypes] = await Promise.all([
132
120
  fetch([
@@ -135,14 +123,16 @@ async function fetchExternalResource({ urn, fetch, options, spaceId, environment
135
123
  environmentId,
136
124
  resourceType,
137
125
  urn,
138
- locale
126
+ locale,
127
+ referencingEntryId
139
128
  ], ({ cmaClient })=>cmaClient.resource.getMany({
140
129
  spaceId,
141
130
  environmentId,
142
131
  resourceTypeId: resourceType,
143
132
  query: {
144
133
  'sys.urn[in]': urn,
145
- locale
134
+ locale,
135
+ referencingEntryId
146
136
  }
147
137
  }).then(({ items })=>{
148
138
  return items[0] ?? null;
@@ -338,7 +328,8 @@ const [InternalServiceProvider, useFetch, useEntityLoader, useCurrentIds] = cons
338
328
  'Resource',
339
329
  resourceType,
340
330
  urn,
341
- options?.locale
331
+ options?.locale,
332
+ options?.referencingEntryId
342
333
  ];
343
334
  return fetch(queryKey, ()=>{
344
335
  if (resourceType === 'Contentful:Entry') {
@@ -355,6 +346,7 @@ const [InternalServiceProvider, useFetch, useEntityLoader, useCurrentIds] = cons
355
346
  fetch,
356
347
  urn,
357
348
  locale: options?.locale,
349
+ referencingEntryId: options?.referencingEntryId,
358
350
  options,
359
351
  resourceType,
360
352
  spaceId: currentSpaceId,
@@ -378,25 +370,53 @@ const [InternalServiceProvider, useFetch, useEntityLoader, useCurrentIds] = cons
378
370
  const onSlideInNavigation = props.sdk.navigator.onSlideInNavigation;
379
371
  useEffect(()=>{
380
372
  function findSameSpaceQueries() {
381
- return queryCache.findAll({
382
- type: 'active',
373
+ const queries = queryCache.findAll({
383
374
  predicate: (query)=>isSameSpaceEntityQueryKey(query.queryKey)
384
375
  });
376
+ return queries;
385
377
  }
386
378
  if (typeof onEntityChanged !== 'function') {
387
- return onSlideInNavigation(({ oldSlideLevel, newSlideLevel })=>{
379
+ return onSlideInNavigation(async ({ oldSlideLevel, newSlideLevel })=>{
388
380
  if (oldSlideLevel > newSlideLevel) {
389
- findSameSpaceQueries().forEach((query)=>{
390
- void queryClient.invalidateQueries(query.queryKey);
391
- });
381
+ const queries = findSameSpaceQueries();
382
+ await Promise.all(queries.map(async (query)=>{
383
+ const [entityType, entityId, spaceId, environmentId, releaseId] = query.queryKey;
384
+ try {
385
+ let freshData;
386
+ if (entityType === 'Entry') {
387
+ freshData = await cmaClient.entry.get({
388
+ entryId: entityId,
389
+ spaceId: spaceId,
390
+ environmentId: environmentId,
391
+ releaseId: releaseId
392
+ });
393
+ } else if (entityType === 'Asset') {
394
+ freshData = await cmaClient.asset.get({
395
+ assetId: entityId,
396
+ spaceId: spaceId,
397
+ environmentId: environmentId,
398
+ releaseId: releaseId
399
+ });
400
+ } else {
401
+ await queryClient.invalidateQueries(query.queryKey);
402
+ return;
403
+ }
404
+ queryClient.setQueryData(query.queryKey, freshData);
405
+ } catch (error) {
406
+ await queryClient.invalidateQueries(query.queryKey);
407
+ }
408
+ }));
392
409
  }
393
410
  });
394
411
  }
395
412
  const subscribeQuery = ({ queryKey, queryHash })=>{
396
413
  const [entityType, entityId, , , releaseId] = queryKey;
397
414
  entityChangeUnsubscribers.current[queryHash] = onEntityChanged(entityType, entityId, (data)=>{
398
- if (get(data, 'sys.release.id') === releaseId) {
415
+ const dataReleaseId = get(data, 'sys.release.id');
416
+ if (dataReleaseId === releaseId) {
399
417
  queryClient.setQueryData(queryKey, data);
418
+ } else if (releaseId && !dataReleaseId) {
419
+ void queryClient.invalidateQueries(queryKey);
400
420
  }
401
421
  });
402
422
  };
@@ -428,7 +448,8 @@ const [InternalServiceProvider, useFetch, useEntityLoader, useCurrentIds] = cons
428
448
  isSameSpaceEntityQueryKey,
429
449
  queryClient,
430
450
  getEntity,
431
- onSlideInNavigation
451
+ onSlideInNavigation,
452
+ cmaClient
432
453
  ]);
433
454
  const getResourceProvider = useCallback(function getResourceProvider(organizationId, appDefinitionId) {
434
455
  const queryKey = [
@@ -493,20 +514,23 @@ export function useEntity(entityType, entityId, options) {
493
514
  currentEntity
494
515
  };
495
516
  }
496
- export function useResource(resourceType, urn, { locale, ...options } = {}) {
517
+ export function useResource(resourceType, urn, { locale, referencingEntryId, ...options } = {}) {
497
518
  if (resourceType.startsWith('Contentful:')) {
498
519
  locale = undefined;
520
+ referencingEntryId = undefined;
499
521
  }
500
522
  const queryKey = [
501
523
  'Resource',
502
524
  resourceType,
503
525
  urn,
504
- locale
526
+ locale,
527
+ referencingEntryId
505
528
  ];
506
529
  const { getResource } = useEntityLoader();
507
530
  const { status, data, error } = useQuery(queryKey, ()=>getResource(resourceType, urn, {
508
531
  ...options,
509
- locale
532
+ locale,
533
+ referencingEntryId
510
534
  }), {
511
535
  enabled: options?.enabled
512
536
  });
@@ -531,6 +555,8 @@ export function useResourceProvider(organizationId, appDefinitionId) {
531
555
  };
532
556
  }
533
557
  function EntityProvider({ children, ...props }) {
534
- return /*#__PURE__*/ React.createElement(SharedQueryClientProvider, null, /*#__PURE__*/ React.createElement(InternalServiceProvider, props, children));
558
+ return /*#__PURE__*/ React.createElement(SharedQueryClientProvider, {
559
+ client: props.queryClient
560
+ }, /*#__PURE__*/ React.createElement(InternalServiceProvider, props, children));
535
561
  }
536
562
  export { EntityProvider, useEntityLoader };
@@ -1,9 +1,11 @@
1
1
  import * as React from 'react';
2
2
  import { useCallback } from 'react';
3
+ import { useContentTypes } from '@contentful/field-editor-shared';
3
4
  import { arrayMove } from '@dnd-kit/sortable';
4
5
  import { LinkEntityActions } from '../components';
5
6
  import { useLinkActionsProps } from '../components/LinkActions/LinkEntityActions';
6
7
  import { useSortIDs } from '../utils/useSortIDs';
8
+ import { SharedQueryClientProvider } from './queryClient';
7
9
  import { ReferenceEditor } from './ReferenceEditor';
8
10
  import { useEditorPermissions } from './useEditorPermissions';
9
11
  function onLinkOrCreate(setValue, entityType, items, ids, index = items.length) {
@@ -95,7 +97,10 @@ function Editor(props) {
95
97
  }));
96
98
  }
97
99
  export function MultipleReferenceEditor(props) {
98
- const allContentTypes = props.sdk.space.getCachedContentTypes();
100
+ return /*#__PURE__*/ React.createElement(SharedQueryClientProvider, null, /*#__PURE__*/ React.createElement(MultipleReferenceEditorInner, props));
101
+ }
102
+ function MultipleReferenceEditorInner(props) {
103
+ const { contentTypes: allContentTypes } = useContentTypes(props.sdk);
99
104
  return /*#__PURE__*/ React.createElement(ReferenceEditor, props, ({ value, disabled, setValue, externalReset })=>{
100
105
  return /*#__PURE__*/ React.createElement(Editor, {
101
106
  ...props,
@@ -1,7 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import { useCallback } from 'react';
3
+ import { useContentTypes } from '@contentful/field-editor-shared';
3
4
  import { LinkEntityActions } from '../components';
4
5
  import { useLinkActionsProps } from '../components/LinkActions/LinkEntityActions';
6
+ import { SharedQueryClientProvider } from './queryClient';
5
7
  import { ReferenceEditor } from './ReferenceEditor';
6
8
  import { useEditorPermissions } from './useEditorPermissions';
7
9
  function Editor(props) {
@@ -48,11 +50,15 @@ function Editor(props) {
48
50
  }
49
51
  return props.children({
50
52
  ...props,
51
- renderCustomCard: props.renderCustomCard && customCardRenderer
53
+ renderCustomCard: props.renderCustomCard && customCardRenderer,
54
+ addReferenceToRelease: props.addReferenceToRelease
52
55
  });
53
56
  }
54
57
  export function SingleReferenceEditor(props) {
55
- const allContentTypes = props.sdk.space.getCachedContentTypes();
58
+ return /*#__PURE__*/ React.createElement(SharedQueryClientProvider, null, /*#__PURE__*/ React.createElement(SingleReferenceEditorInner, props));
59
+ }
60
+ function SingleReferenceEditorInner(props) {
61
+ const { contentTypes: allContentTypes } = useContentTypes(props.sdk);
56
62
  return /*#__PURE__*/ React.createElement(ReferenceEditor, props, ({ value, setValue, disabled, externalReset })=>{
57
63
  return /*#__PURE__*/ React.createElement(Editor, {
58
64
  ...props,
@@ -1,47 +1 @@
1
- import * as React from 'react';
2
- import { QueryClient, useQuery as useRQ, useQueryClient as useHostQueryClient } from '@tanstack/react-query';
3
- const clientContext = /*#__PURE__*/ React.createContext(undefined);
4
- function useMaybeHostQueryClient() {
5
- try {
6
- return useHostQueryClient();
7
- } catch {
8
- return undefined;
9
- }
10
- }
11
- export function useQueryClient() {
12
- const client = React.useContext(clientContext);
13
- const hostClient = useMaybeHostQueryClient();
14
- return React.useMemo(()=>{
15
- if (client) {
16
- return client;
17
- }
18
- if (hostClient) return hostClient;
19
- return new QueryClient({
20
- defaultOptions: {
21
- queries: {
22
- useErrorBoundary: false,
23
- refetchOnWindowFocus: false,
24
- refetchOnReconnect: true,
25
- refetchOnMount: false,
26
- staleTime: Infinity,
27
- retry: false
28
- }
29
- }
30
- });
31
- }, [
32
- client,
33
- hostClient
34
- ]);
35
- }
36
- export const useQuery = (key, fn, opt)=>{
37
- return useRQ(key, fn, {
38
- ...opt,
39
- context: clientContext
40
- });
41
- };
42
- export function SharedQueryClientProvider({ children }) {
43
- const client = useQueryClient();
44
- return /*#__PURE__*/ React.createElement(clientContext.Provider, {
45
- value: client
46
- }, children);
47
- }
1
+ export { SharedQueryClientProvider, useQueryClient, useQuery } from '@contentful/field-editor-shared';
@@ -11,6 +11,9 @@ export function useContentTypePermissions({ entityType, validations, sdk, allCon
11
11
  if (entityType === 'Asset') {
12
12
  return [];
13
13
  }
14
+ if (validations.contentTypes && allContentTypes.length === 0) {
15
+ return [];
16
+ }
14
17
  if (validations.contentTypes) {
15
18
  return allContentTypes.filter((ct)=>validations.contentTypes?.includes(ct.sys.id));
16
19
  }
@@ -23,21 +26,15 @@ export function useContentTypePermissions({ entityType, validations, sdk, allCon
23
26
  const [creatableContentTypes, setCreatableContentTypes] = useState(availableContentTypes);
24
27
  const { canPerformActionOnEntryOfType } = useAccessApi(sdk.access);
25
28
  useEffect(()=>{
26
- function getContentTypes(action) {
27
- return filter(availableContentTypes, (ct)=>canPerformActionOnEntryOfType(action, ct.sys.id));
28
- }
29
29
  async function checkContentTypeAccess() {
30
- const creatable = await getContentTypes('create');
31
- if (!isEqual(creatable, creatableContentTypes)) {
32
- setCreatableContentTypes(creatable);
33
- }
30
+ const creatable = await filter(availableContentTypes, (ct)=>canPerformActionOnEntryOfType('create', ct.sys.id));
31
+ setCreatableContentTypes((creatableContentTypes)=>isEqual(creatable, creatableContentTypes) ? creatableContentTypes : creatable);
34
32
  }
35
33
  if (availableContentTypes.length > 0) {
36
34
  void checkContentTypeAccess();
37
35
  }
38
36
  }, [
39
- availableContentTypes,
40
- creatableContentTypes
37
+ availableContentTypes
41
38
  ]);
42
39
  return {
43
40
  creatableContentTypes,
@@ -3,8 +3,12 @@ import { fromFieldValidations } from '../utils/fromFieldValidations';
3
3
  import { useAccessApi } from './useAccessApi';
4
4
  import { useContentTypePermissions } from './useContentTypePermissions';
5
5
  export function useEditorPermissions({ sdk, entityType, parameters, allContentTypes }) {
6
+ const fieldValidations = sdk.field.validations;
7
+ const itemsValidations = sdk.field.type === 'Array' ? sdk.field.items?.validations : undefined;
6
8
  const validations = useMemo(()=>fromFieldValidations(sdk.field), [
7
- sdk.field
9
+ sdk.field,
10
+ JSON.stringify(fieldValidations),
11
+ JSON.stringify(itemsValidations)
8
12
  ]);
9
13
  const [canCreateEntity, setCanCreateEntity] = useState(true);
10
14
  const [canLinkEntity, setCanLinkEntity] = useState(true);
@@ -69,6 +69,7 @@ export function useLinkActionsProps(props) {
69
69
  }, [
70
70
  sdk,
71
71
  entityType,
72
+ editorPermissions,
72
73
  onLinkedExisting
73
74
  ]);
74
75
  const onLinkSeveralExisting = React.useCallback(async (index)=>{
@@ -84,6 +85,7 @@ export function useLinkActionsProps(props) {
84
85
  }, [
85
86
  sdk,
86
87
  entityType,
88
+ editorPermissions,
87
89
  onLinkedExisting
88
90
  ]);
89
91
  return useMemo(()=>({
@@ -34,7 +34,8 @@ export function MultipleEntryReferenceEditor(props) {
34
34
  onMoveTop: index !== 0 ? ()=>childrenProps.onMove(index, 0) : undefined,
35
35
  onMoveBottom: index !== lastIndex ? ()=>childrenProps.onMove(index, lastIndex) : undefined,
36
36
  renderDragHandle: DragHandle,
37
- isBeingDragged: index === indexToUpdate
37
+ isBeingDragged: index === indexToUpdate,
38
+ addReferenceToRelease: props.addReferenceToRelease
38
39
  });
39
40
  }));
40
41
  }