@contentful/field-editor-reference 5.13.3 → 5.14.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.
@@ -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',
@@ -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",
@@ -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
  });
@@ -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',
@@ -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",
@@ -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
  });
@@ -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 {};
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.14.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": "538cc684c7cea792773969ab12cff42301c4ab9e"
67
67
  }