@contentful/field-editor-reference 5.13.3 → 5.15.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.
@@ -29,10 +29,10 @@ _export(exports, {
29
29
  }
30
30
  });
31
31
  const _react = _interop_require_wildcard(require("react"));
32
- const _reactquery = require("@tanstack/react-query");
33
32
  const _constate = _interop_require_default(require("constate"));
34
33
  const _contentfulmanagement = require("contentful-management");
35
34
  const _pqueue = _interop_require_default(require("p-queue"));
35
+ const _queryClient = require("./queryClient");
36
36
  function _define_property(obj, key, value) {
37
37
  if (key in obj) {
38
38
  Object.defineProperty(obj, key, {
@@ -109,8 +109,14 @@ const isEntityQueryKey = (queryKey)=>{
109
109
  async function fetchContentfulEntry(params) {
110
110
  const { urn , fetch , options } = params;
111
111
  const resourceId = urn.split(':', 6)[5];
112
- const [, spaceId, , entryId] = resourceId.split('/');
113
- const environmentId = 'master';
112
+ const ENTITY_RESOURCE_ID_REGEX = RegExp("^spaces\\/(?<spaceId>[^/]+)(?:\\/environments\\/(?<environmentId>[^/]+))?\\/entries\\/(?<entityId>[^/]+)$");
113
+ const resourceIdMatch = resourceId.match(ENTITY_RESOURCE_ID_REGEX);
114
+ if (!resourceIdMatch || !resourceIdMatch?.groups?.spaceId || !resourceIdMatch?.groups?.entityId) {
115
+ throw new Error('Not a valid crn');
116
+ }
117
+ const spaceId = resourceIdMatch.groups.spaceId;
118
+ const environmentId = resourceIdMatch?.groups?.environmentId || 'master';
119
+ const entryId = resourceIdMatch.groups.entityId;
114
120
  const [space, entry] = await Promise.all([
115
121
  fetch([
116
122
  'space',
@@ -174,7 +180,7 @@ const [InternalServiceProvider, useFetch, useEntityLoader, useCurrentIds] = (0,
174
180
  props.sdk.ids.environmentAlias,
175
181
  props.sdk.ids.environment
176
182
  ]);
177
- const queryClient = (0, _reactquery.useQueryClient)();
183
+ const queryClient = (0, _queryClient.useQueryClient)();
178
184
  const queryCache = queryClient.getQueryCache();
179
185
  const entityChangeUnsubscribers = (0, _react.useRef)({});
180
186
  const cmaClient = (0, _react.useMemo)(()=>(0, _contentfulmanagement.createClient)({
@@ -372,7 +378,7 @@ function useEntity(entityType, entityId, options) {
372
378
  options?.spaceId ?? space,
373
379
  options?.environmentId ?? environment
374
380
  ];
375
- const { status , data } = (0, _reactquery.useQuery)(queryKey, ()=>getEntity(entityType, entityId, options), {
381
+ const { status , data } = (0, _queryClient.useQuery)(queryKey, ()=>getEntity(entityType, entityId, options), {
376
382
  enabled: options?.enabled
377
383
  });
378
384
  return {
@@ -387,7 +393,7 @@ function useResource(resourceType, urn, options) {
387
393
  urn
388
394
  ];
389
395
  const { getResource } = useEntityLoader();
390
- const { status , data , error } = (0, _reactquery.useQuery)(queryKey, ()=>getResource(resourceType, urn, options), {
396
+ const { status , data , error } = (0, _queryClient.useQuery)(queryKey, ()=>getResource(resourceType, urn, options), {
391
397
  enabled: options?.enabled
392
398
  });
393
399
  return {
@@ -397,24 +403,5 @@ function useResource(resourceType, urn, options) {
397
403
  };
398
404
  }
399
405
  function EntityProvider({ children , ...props }) {
400
- const reactQueryClient = (0, _react.useMemo)(()=>{
401
- const queryCache = new _reactquery.QueryCache();
402
- const queryClient = new _reactquery.QueryClient({
403
- queryCache,
404
- defaultOptions: {
405
- queries: {
406
- useErrorBoundary: false,
407
- refetchOnWindowFocus: false,
408
- refetchOnReconnect: true,
409
- refetchOnMount: false,
410
- staleTime: Infinity,
411
- retry: false
412
- }
413
- }
414
- });
415
- return queryClient;
416
- }, []);
417
- return _react.default.createElement(_reactquery.QueryClientProvider, {
418
- client: reactQueryClient
419
- }, _react.default.createElement(InternalServiceProvider, props, children));
406
+ return _react.default.createElement(_queryClient.SharedQueryClientProvider, null, _react.default.createElement(InternalServiceProvider, props, children));
420
407
  }
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: all[name]
9
+ });
10
+ }
11
+ _export(exports, {
12
+ useQueryClient: function() {
13
+ return useQueryClient;
14
+ },
15
+ useQuery: function() {
16
+ return useQuery;
17
+ },
18
+ SharedQueryClientProvider: function() {
19
+ return SharedQueryClientProvider;
20
+ }
21
+ });
22
+ const _react = _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
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
47
+ for(var key in obj){
48
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
49
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
50
+ if (desc && (desc.get || desc.set)) {
51
+ Object.defineProperty(newObj, key, desc);
52
+ } else {
53
+ newObj[key] = obj[key];
54
+ }
55
+ }
56
+ }
57
+ newObj.default = obj;
58
+ if (cache) {
59
+ cache.set(obj, newObj);
60
+ }
61
+ return newObj;
62
+ }
63
+ const clientContext = _react.createContext(undefined);
64
+ function useQueryClient() {
65
+ const client = _react.useContext(clientContext);
66
+ return _react.useMemo(()=>{
67
+ if (client) {
68
+ return client;
69
+ }
70
+ return new _reactquery.QueryClient({
71
+ defaultOptions: {
72
+ queries: {
73
+ useErrorBoundary: false,
74
+ refetchOnWindowFocus: false,
75
+ refetchOnReconnect: true,
76
+ refetchOnMount: false,
77
+ staleTime: Infinity,
78
+ retry: false
79
+ }
80
+ }
81
+ });
82
+ }, [
83
+ client
84
+ ]);
85
+ }
86
+ const useQuery = (key, fn, opt)=>{
87
+ return (0, _reactquery.useQuery)(key, fn, {
88
+ ...opt,
89
+ context: clientContext
90
+ });
91
+ };
92
+ function SharedQueryClientProvider({ children }) {
93
+ const client = useQueryClient();
94
+ return _react.createElement(clientContext.Provider, {
95
+ value: client
96
+ }, children);
97
+ }
@@ -60,12 +60,12 @@ function _interop_require_wildcard(obj, nodeInterop) {
60
60
  const styles = {
61
61
  spaceIcon: (0, _emotion.css)({
62
62
  flexShrink: 0,
63
- fill: _f36tokens.default.purple600
63
+ fill: _f36tokens.default.gray600
64
64
  }),
65
65
  spaceName: (0, _emotion.css)({
66
- color: _f36tokens.default.gray700,
66
+ color: _f36tokens.default.gray600,
67
67
  fontSize: _f36tokens.default.fontSizeS,
68
- fontWeight: _f36tokens.default.fontWeightDemiBold,
68
+ fontWeight: _f36tokens.default.fontWeightMedium,
69
69
  maxWidth: '80px',
70
70
  textOverflow: 'ellipsis',
71
71
  overflow: 'hidden',
@@ -73,10 +73,12 @@ const styles = {
73
73
  })
74
74
  };
75
75
  function SpaceName(props) {
76
+ let content = `Space: ${props.spaceName}`;
77
+ if (props.environmentName) content += ` (Env.: ${props.environmentName})`;
76
78
  return _react.createElement(_f36components.Tooltip, {
77
79
  placement: "top",
78
80
  as: "div",
79
- content: `Space: ${props.spaceName}`
81
+ content: content
80
82
  }, _react.createElement(_f36components.Flex, {
81
83
  alignItems: "center",
82
84
  gap: "spacingXs",
@@ -126,7 +126,8 @@ function WrappedEntryCard(props) {
126
126
  isSelected: props.isSelected,
127
127
  status: status,
128
128
  icon: props.spaceName ? _react.createElement(_SpaceName.SpaceName, {
129
- spaceName: props.spaceName
129
+ spaceName: props.spaceName,
130
+ environmentName: props.entry.sys.environment.sys.id
130
131
  }) : _react.createElement(_components.ScheduledIconWithTooltip, {
131
132
  getEntityScheduledActions: props.getEntityScheduledActions,
132
133
  entityType: "Entry",
package/dist/cjs/index.js CHANGED
@@ -66,6 +66,9 @@ _export(exports, {
66
66
  useResource: function() {
67
67
  return _EntityStore.useResource;
68
68
  },
69
+ EntityCacheProvider: function() {
70
+ return _queryClient.SharedQueryClientProvider;
71
+ },
69
72
  SingleResourceReferenceEditor: function() {
70
73
  return _resources.SingleResourceReferenceEditor;
71
74
  },
@@ -78,6 +81,7 @@ const _entries = require("./entries");
78
81
  const _assets = require("./assets");
79
82
  const _SortableLinkList = require("./common/SortableLinkList");
80
83
  const _EntityStore = require("./common/EntityStore");
84
+ const _queryClient = require("./common/queryClient");
81
85
  const _resources = require("./resources");
82
86
  _export_star(require("./types"), exports);
83
87
  function _export_star(from, to) {
@@ -7,6 +7,7 @@ require("@testing-library/jest-dom");
7
7
  const _fieldeditortestutils = require("@contentful/field-editor-test-utils");
8
8
  const _react1 = require("@testing-library/react");
9
9
  const _published_content_typejson = _interop_require_default(require("../../__fixtures__/content-type/published_content_type.json"));
10
+ const _published_entry_non_masterjson = _interop_require_default(require("../../__fixtures__/entry/published_entry_non_master.json"));
10
11
  const _published_entryjson = _interop_require_default(require("../../__fixtures__/entry/published_entry.json"));
11
12
  const _indifferent_spacejson = _interop_require_default(require("../../__fixtures__/space/indifferent_space.json"));
12
13
  const _EntityStore = require("../../common/EntityStore");
@@ -62,6 +63,8 @@ jest.mock('react-intersection-observer', ()=>({
62
63
  useInView: jest.fn().mockReturnValue({})
63
64
  }));
64
65
  const resolvableEntryUrn = 'crn:contentful:::content:spaces/space-id/entries/linked-entry-urn';
66
+ const resolvableEntryUrnWithExplicitMaster = 'crn:contentful:::content:spaces/space-id/environments/master/entries/linked-entry-urn';
67
+ const resolvableEntryUrnWithAnotherEnvironment = 'crn:contentful:::content:spaces/space-id/environments/my-test-environment/entries/linked-entry-urn';
65
68
  const unknownEntryUrn = 'crn:contentful:::content:spaces/space-id/entries/unknown-entry-urn';
66
69
  const sdk = {
67
70
  locales: {
@@ -72,10 +75,13 @@ const sdk = {
72
75
  get: jest.fn().mockReturnValue(_published_content_typejson.default)
73
76
  },
74
77
  Entry: {
75
- get: jest.fn().mockImplementation(({ entryId })=>{
76
- if (entryId === 'linked-entry-urn') {
78
+ get: jest.fn().mockImplementation(({ spaceId , environmentId , entryId })=>{
79
+ if (spaceId === 'space-id' && environmentId === 'master' && entryId === 'linked-entry-urn') {
77
80
  return Promise.resolve(_published_entryjson.default);
78
81
  }
82
+ if (spaceId === 'space-id' && environmentId === 'my-test-environment' && entryId === 'linked-entry-urn') {
83
+ return Promise.resolve(_published_entry_non_masterjson.default);
84
+ }
79
85
  return Promise.reject(new Error());
80
86
  })
81
87
  },
@@ -124,11 +130,36 @@ function renderResourceCard({ linkType ='Contentful:Entry' , entryUrn =resolvabl
124
130
  })));
125
131
  }
126
132
  describe('ResourceCard', ()=>{
127
- it('renders entry card', async ()=>{
133
+ it('renders entry card with implicit master crn', async ()=>{
128
134
  const { getByTestId , getByText } = renderResourceCard();
135
+ const tooltipContent = `Space: ${_indifferent_spacejson.default.name} (Env.: ${_published_entryjson.default.sys.environment.sys.id})`;
129
136
  await (0, _react1.waitFor)(()=>expect(getByTestId('cf-ui-entry-card')).toBeDefined());
130
137
  expect(getByText(_published_entryjson.default.fields.exField['en-US'])).toBeDefined();
131
138
  expect(getByText(_indifferent_spacejson.default.name)).toBeDefined();
139
+ _react1.fireEvent.mouseEnter(getByText(_indifferent_spacejson.default.name));
140
+ await (0, _react1.waitFor)(()=>expect(getByText(tooltipContent)).toBeDefined());
141
+ });
142
+ it('renders entry card with explicit master crn', async ()=>{
143
+ const { getByTestId , getByText } = renderResourceCard({
144
+ entryUrn: resolvableEntryUrnWithExplicitMaster
145
+ });
146
+ const tooltipContent = `Space: ${_indifferent_spacejson.default.name} (Env.: ${_published_entryjson.default.sys.environment.sys.id})`;
147
+ await (0, _react1.waitFor)(()=>expect(getByTestId('cf-ui-entry-card')).toBeDefined());
148
+ expect(getByText(_published_entryjson.default.fields.exField['en-US'])).toBeDefined();
149
+ expect(getByText(_indifferent_spacejson.default.name)).toBeDefined();
150
+ _react1.fireEvent.mouseEnter(getByText(_indifferent_spacejson.default.name));
151
+ await (0, _react1.waitFor)(()=>expect(getByText(tooltipContent)).toBeDefined());
152
+ });
153
+ it('renders entry card with a non master environment', async ()=>{
154
+ const { getByTestId , getByText } = renderResourceCard({
155
+ entryUrn: resolvableEntryUrnWithAnotherEnvironment
156
+ });
157
+ await (0, _react1.waitFor)(()=>expect(getByTestId('cf-ui-entry-card')).toBeDefined());
158
+ const tooltipContent = `Space: ${_indifferent_spacejson.default.name} (Env.: ${_published_entry_non_masterjson.default.sys.environment.sys.id})`;
159
+ expect(getByText(_published_entry_non_masterjson.default.fields.exField['en-US'])).toBeDefined();
160
+ expect(getByText(_indifferent_spacejson.default.name)).toBeDefined();
161
+ _react1.fireEvent.mouseEnter(getByText(_indifferent_spacejson.default.name));
162
+ await (0, _react1.waitFor)(()=>expect(getByText(tooltipContent)).toBeDefined());
132
163
  });
133
164
  it('renders skeleton when no data is provided', ()=>{
134
165
  const { getByTestId } = renderResourceCard();
@@ -146,4 +177,10 @@ describe('ResourceCard', ()=>{
146
177
  });
147
178
  await (0, _react1.waitFor)(()=>expect(getByTestId('cf-ui-missing-entry-card')).toBeDefined());
148
179
  });
180
+ it('renders missing entity card when crn is invalid', async ()=>{
181
+ const { getByTestId } = renderResourceCard({
182
+ entryUrn: ''
183
+ });
184
+ await (0, _react1.waitFor)(()=>expect(getByTestId('cf-ui-missing-entry-card')).toBeDefined());
185
+ });
149
186
  });
@@ -12,10 +12,10 @@ function _define_property(obj, key, value) {
12
12
  return obj;
13
13
  }
14
14
  import React, { useCallback, useEffect, useMemo, useRef } from 'react';
15
- import { QueryCache, QueryClient, QueryClientProvider, useQuery, useQueryClient } from '@tanstack/react-query';
16
15
  import constate from 'constate';
17
16
  import { createClient } from 'contentful-management';
18
17
  import PQueue from 'p-queue';
18
+ import { SharedQueryClientProvider, useQuery, useQueryClient } from './queryClient';
19
19
  const globalQueue = new PQueue({
20
20
  concurrency: 50
21
21
  });
@@ -35,8 +35,14 @@ const isEntityQueryKey = (queryKey)=>{
35
35
  async function fetchContentfulEntry(params) {
36
36
  const { urn , fetch , options } = params;
37
37
  const resourceId = urn.split(':', 6)[5];
38
- const [, spaceId, , entryId] = resourceId.split('/');
39
- const environmentId = 'master';
38
+ const ENTITY_RESOURCE_ID_REGEX = RegExp("^spaces\\/(?<spaceId>[^/]+)(?:\\/environments\\/(?<environmentId>[^/]+))?\\/entries\\/(?<entityId>[^/]+)$");
39
+ const resourceIdMatch = resourceId.match(ENTITY_RESOURCE_ID_REGEX);
40
+ if (!resourceIdMatch || !resourceIdMatch?.groups?.spaceId || !resourceIdMatch?.groups?.entityId) {
41
+ throw new Error('Not a valid crn');
42
+ }
43
+ const spaceId = resourceIdMatch.groups.spaceId;
44
+ const environmentId = resourceIdMatch?.groups?.environmentId || 'master';
45
+ const entryId = resourceIdMatch.groups.entityId;
40
46
  const [space, entry] = await Promise.all([
41
47
  fetch([
42
48
  'space',
@@ -323,25 +329,6 @@ export function useResource(resourceType, urn, options) {
323
329
  };
324
330
  }
325
331
  function EntityProvider({ children , ...props }) {
326
- const reactQueryClient = useMemo(()=>{
327
- const queryCache = new QueryCache();
328
- const queryClient = new QueryClient({
329
- queryCache,
330
- defaultOptions: {
331
- queries: {
332
- useErrorBoundary: false,
333
- refetchOnWindowFocus: false,
334
- refetchOnReconnect: true,
335
- refetchOnMount: false,
336
- staleTime: Infinity,
337
- retry: false
338
- }
339
- }
340
- });
341
- return queryClient;
342
- }, []);
343
- return React.createElement(QueryClientProvider, {
344
- client: reactQueryClient
345
- }, React.createElement(InternalServiceProvider, props, children));
332
+ return React.createElement(SharedQueryClientProvider, null, React.createElement(InternalServiceProvider, props, children));
346
333
  }
347
334
  export { EntityProvider, useEntityLoader };
@@ -0,0 +1,37 @@
1
+ import * as React from 'react';
2
+ import { QueryClient, useQuery as useRQ } from '@tanstack/react-query';
3
+ const clientContext = React.createContext(undefined);
4
+ export function useQueryClient() {
5
+ const client = React.useContext(clientContext);
6
+ return React.useMemo(()=>{
7
+ if (client) {
8
+ return client;
9
+ }
10
+ return new QueryClient({
11
+ defaultOptions: {
12
+ queries: {
13
+ useErrorBoundary: false,
14
+ refetchOnWindowFocus: false,
15
+ refetchOnReconnect: true,
16
+ refetchOnMount: false,
17
+ staleTime: Infinity,
18
+ retry: false
19
+ }
20
+ }
21
+ });
22
+ }, [
23
+ client
24
+ ]);
25
+ }
26
+ export const useQuery = (key, fn, opt)=>{
27
+ return useRQ(key, fn, {
28
+ ...opt,
29
+ context: clientContext
30
+ });
31
+ };
32
+ export function SharedQueryClientProvider({ children }) {
33
+ const client = useQueryClient();
34
+ return React.createElement(clientContext.Provider, {
35
+ value: client
36
+ }, children);
37
+ }
@@ -6,12 +6,12 @@ import { css } from 'emotion';
6
6
  const styles = {
7
7
  spaceIcon: css({
8
8
  flexShrink: 0,
9
- fill: tokens.purple600
9
+ fill: tokens.gray600
10
10
  }),
11
11
  spaceName: css({
12
- color: tokens.gray700,
12
+ color: tokens.gray600,
13
13
  fontSize: tokens.fontSizeS,
14
- fontWeight: tokens.fontWeightDemiBold,
14
+ fontWeight: tokens.fontWeightMedium,
15
15
  maxWidth: '80px',
16
16
  textOverflow: 'ellipsis',
17
17
  overflow: 'hidden',
@@ -19,10 +19,12 @@ const styles = {
19
19
  })
20
20
  };
21
21
  export function SpaceName(props) {
22
+ let content = `Space: ${props.spaceName}`;
23
+ if (props.environmentName) content += ` (Env.: ${props.environmentName})`;
22
24
  return React.createElement(Tooltip, {
23
25
  placement: "top",
24
26
  as: "div",
25
- content: `Space: ${props.spaceName}`
27
+ content: content
26
28
  }, React.createElement(Flex, {
27
29
  alignItems: "center",
28
30
  gap: "spacingXs",
@@ -72,7 +72,8 @@ export function WrappedEntryCard(props) {
72
72
  isSelected: props.isSelected,
73
73
  status: status,
74
74
  icon: props.spaceName ? React.createElement(SpaceName, {
75
- spaceName: props.spaceName
75
+ spaceName: props.spaceName,
76
+ environmentName: props.entry.sys.environment.sys.id
76
77
  }) : React.createElement(ScheduledIconWithTooltip, {
77
78
  getEntityScheduledActions: props.getEntityScheduledActions,
78
79
  entityType: "Entry",
package/dist/esm/index.js CHANGED
@@ -3,5 +3,6 @@ export { SingleEntryReferenceEditor, MultipleEntryReferenceEditor, WrappedEntryC
3
3
  export { SingleMediaEditor, MultipleMediaEditor, WrappedAssetCard } from './assets';
4
4
  export { SortableLinkList } from './common/SortableLinkList';
5
5
  export { EntityProvider, useEntityLoader, useEntity, useResource } from './common/EntityStore';
6
+ export { SharedQueryClientProvider as EntityCacheProvider } from './common/queryClient';
6
7
  export { SingleResourceReferenceEditor, MultipleResourceReferenceEditor } from './resources';
7
8
  export * from './types';
@@ -1,8 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import '@testing-library/jest-dom';
3
3
  import { createFakeCMAAdapter } from '@contentful/field-editor-test-utils';
4
- import { configure, render, waitFor } from '@testing-library/react';
4
+ import { configure, fireEvent, render, waitFor } from '@testing-library/react';
5
5
  import publishedCT from '../../__fixtures__/content-type/published_content_type.json';
6
+ import publishedEntryNonMasterEnvironment from '../../__fixtures__/entry/published_entry_non_master.json';
6
7
  import publishedEntry from '../../__fixtures__/entry/published_entry.json';
7
8
  import space from '../../__fixtures__/space/indifferent_space.json';
8
9
  import { EntityProvider } from '../../common/EntityStore';
@@ -14,6 +15,8 @@ jest.mock('react-intersection-observer', ()=>({
14
15
  useInView: jest.fn().mockReturnValue({})
15
16
  }));
16
17
  const resolvableEntryUrn = 'crn:contentful:::content:spaces/space-id/entries/linked-entry-urn';
18
+ const resolvableEntryUrnWithExplicitMaster = 'crn:contentful:::content:spaces/space-id/environments/master/entries/linked-entry-urn';
19
+ const resolvableEntryUrnWithAnotherEnvironment = 'crn:contentful:::content:spaces/space-id/environments/my-test-environment/entries/linked-entry-urn';
17
20
  const unknownEntryUrn = 'crn:contentful:::content:spaces/space-id/entries/unknown-entry-urn';
18
21
  const sdk = {
19
22
  locales: {
@@ -24,10 +27,13 @@ const sdk = {
24
27
  get: jest.fn().mockReturnValue(publishedCT)
25
28
  },
26
29
  Entry: {
27
- get: jest.fn().mockImplementation(({ entryId })=>{
28
- if (entryId === 'linked-entry-urn') {
30
+ get: jest.fn().mockImplementation(({ spaceId , environmentId , entryId })=>{
31
+ if (spaceId === 'space-id' && environmentId === 'master' && entryId === 'linked-entry-urn') {
29
32
  return Promise.resolve(publishedEntry);
30
33
  }
34
+ if (spaceId === 'space-id' && environmentId === 'my-test-environment' && entryId === 'linked-entry-urn') {
35
+ return Promise.resolve(publishedEntryNonMasterEnvironment);
36
+ }
31
37
  return Promise.reject(new Error());
32
38
  })
33
39
  },
@@ -76,11 +82,36 @@ function renderResourceCard({ linkType ='Contentful:Entry' , entryUrn =resolvabl
76
82
  })));
77
83
  }
78
84
  describe('ResourceCard', ()=>{
79
- it('renders entry card', async ()=>{
85
+ it('renders entry card with implicit master crn', async ()=>{
80
86
  const { getByTestId , getByText } = renderResourceCard();
87
+ const tooltipContent = `Space: ${space.name} (Env.: ${publishedEntry.sys.environment.sys.id})`;
81
88
  await waitFor(()=>expect(getByTestId('cf-ui-entry-card')).toBeDefined());
82
89
  expect(getByText(publishedEntry.fields.exField['en-US'])).toBeDefined();
83
90
  expect(getByText(space.name)).toBeDefined();
91
+ fireEvent.mouseEnter(getByText(space.name));
92
+ await waitFor(()=>expect(getByText(tooltipContent)).toBeDefined());
93
+ });
94
+ it('renders entry card with explicit master crn', async ()=>{
95
+ const { getByTestId , getByText } = renderResourceCard({
96
+ entryUrn: resolvableEntryUrnWithExplicitMaster
97
+ });
98
+ const tooltipContent = `Space: ${space.name} (Env.: ${publishedEntry.sys.environment.sys.id})`;
99
+ await waitFor(()=>expect(getByTestId('cf-ui-entry-card')).toBeDefined());
100
+ expect(getByText(publishedEntry.fields.exField['en-US'])).toBeDefined();
101
+ expect(getByText(space.name)).toBeDefined();
102
+ fireEvent.mouseEnter(getByText(space.name));
103
+ await waitFor(()=>expect(getByText(tooltipContent)).toBeDefined());
104
+ });
105
+ it('renders entry card with a non master environment', async ()=>{
106
+ const { getByTestId , getByText } = renderResourceCard({
107
+ entryUrn: resolvableEntryUrnWithAnotherEnvironment
108
+ });
109
+ await waitFor(()=>expect(getByTestId('cf-ui-entry-card')).toBeDefined());
110
+ const tooltipContent = `Space: ${space.name} (Env.: ${publishedEntryNonMasterEnvironment.sys.environment.sys.id})`;
111
+ expect(getByText(publishedEntryNonMasterEnvironment.fields.exField['en-US'])).toBeDefined();
112
+ expect(getByText(space.name)).toBeDefined();
113
+ fireEvent.mouseEnter(getByText(space.name));
114
+ await waitFor(()=>expect(getByText(tooltipContent)).toBeDefined());
84
115
  });
85
116
  it('renders skeleton when no data is provided', ()=>{
86
117
  const { getByTestId } = renderResourceCard();
@@ -98,4 +129,10 @@ describe('ResourceCard', ()=>{
98
129
  });
99
130
  await waitFor(()=>expect(getByTestId('cf-ui-missing-entry-card')).toBeDefined());
100
131
  });
132
+ it('renders missing entity card when crn is invalid', async ()=>{
133
+ const { getByTestId } = renderResourceCard({
134
+ entryUrn: ''
135
+ });
136
+ await waitFor(()=>expect(getByTestId('cf-ui-missing-entry-card')).toBeDefined());
137
+ });
101
138
  });
@@ -54,7 +54,7 @@ declare const useEntityLoader: () => {
54
54
  };
55
55
  export declare function useEntity<E extends FetchableEntity>(entityType: FetchableEntityType, entityId: string, options?: UseEntityOptions): UseEntityResult<E>;
56
56
  export declare function useResource(resourceType: ResourceType, urn: string, options?: UseResourceOptions): {
57
- status: "loading" | "error" | "success";
57
+ status: "error" | "success" | "loading";
58
58
  data: ResourceInfo<Resource> | undefined;
59
59
  error: unknown;
60
60
  };
@@ -0,0 +1,9 @@
1
+ import * as React from 'react';
2
+ import { QueryClient, useQuery as useRQ } from '@tanstack/react-query';
3
+ export declare function useQueryClient(): QueryClient;
4
+ export declare const useQuery: typeof useRQ;
5
+ /**
6
+ * Provides access to a query client either by sharing an existing client or
7
+ * creating a new one.
8
+ */
9
+ export declare function SharedQueryClientProvider({ children }: React.PropsWithChildren<{}>): React.JSX.Element;
@@ -1,6 +1,7 @@
1
1
  import * as React from 'react';
2
- interface SpaceNameProps {
2
+ interface SourceProps {
3
3
  spaceName: string;
4
+ environmentName?: string;
4
5
  }
5
- export declare function SpaceName(props: SpaceNameProps): React.JSX.Element;
6
+ export declare function SpaceName(props: SourceProps): React.JSX.Element;
6
7
  export {};
@@ -5,6 +5,7 @@ export type { CustomActionProps } from './common/ReferenceEditor';
5
5
  export type { CustomEntityCardProps, DefaultCardRenderer, MissingEntityCardProps, RenderCustomMissingEntityCard, } from './common/customCardTypes';
6
6
  export { SortableLinkList } from './common/SortableLinkList';
7
7
  export { EntityProvider, useEntityLoader, useEntity, useResource } from './common/EntityStore';
8
+ export { SharedQueryClientProvider as EntityCacheProvider } from './common/queryClient';
8
9
  export type { ResourceInfo } from './common/EntityStore';
9
10
  export { SingleResourceReferenceEditor, MultipleResourceReferenceEditor } from './resources';
10
11
  export * from './types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/field-editor-reference",
3
- "version": "5.13.3",
3
+ "version": "5.15.0",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/esm/index.js",
6
6
  "types": "dist/types/index.d.ts",
@@ -63,5 +63,5 @@
63
63
  "@contentful/app-sdk": "^4.2.0",
64
64
  "react": ">=16.8.0"
65
65
  },
66
- "gitHead": "68039530d579ba49330f49e4c6be37823a774a29"
66
+ "gitHead": "780d4eb1450f777c2d2396a86d0b1a10268b3c13"
67
67
  }