@contentful/field-editor-reference 6.3.2 → 6.4.1
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.
- package/dist/cjs/assets/WrappedAssetCard/WrappedAssetCard.js +16 -17
- package/dist/cjs/entries/WrappedEntryCard/WrappedEntryCard.js +46 -47
- package/dist/cjs/resources/MultipleResourceReferenceEditor.js +3 -5
- package/dist/cjs/resources/MultipleResourceReferenceEditor.spec.js +51 -23
- package/dist/cjs/resources/SingleResourceReferenceEditor.js +2 -4
- package/dist/cjs/resources/SingleResourceReferenceEditor.spec.js +27 -0
- package/dist/cjs/resources/useResourceLinkActions.js +10 -2
- package/dist/esm/assets/WrappedAssetCard/WrappedAssetCard.js +16 -17
- package/dist/esm/entries/WrappedEntryCard/WrappedEntryCard.js +46 -47
- package/dist/esm/resources/MultipleResourceReferenceEditor.js +3 -5
- package/dist/esm/resources/MultipleResourceReferenceEditor.spec.js +29 -1
- package/dist/esm/resources/SingleResourceReferenceEditor.js +2 -4
- package/dist/esm/resources/SingleResourceReferenceEditor.spec.js +27 -0
- package/dist/esm/resources/useResourceLinkActions.js +10 -2
- package/dist/types/assets/WrappedAssetCard/WrappedAssetCard.d.ts +1 -1
- package/dist/types/entries/WrappedEntryCard/WrappedEntryCard.d.ts +1 -1
- package/dist/types/resources/useResourceLinkActions.d.ts +4 -4
- package/package.json +2 -2
|
@@ -86,24 +86,23 @@ function getFileType(file) {
|
|
|
86
86
|
});
|
|
87
87
|
return groupToIconMap[groupName] || 'archive';
|
|
88
88
|
}
|
|
89
|
-
const WrappedAssetCard = (
|
|
90
|
-
const
|
|
91
|
-
const status = _fieldeditorshared.entityHelpers.getEntityStatus(props.asset.sys, props.useLocalizedEntityStatus ? props.localeCode : undefined);
|
|
89
|
+
const WrappedAssetCard = ({ asset, className, size, localeCode, defaultLocaleCode, activeLocales, localesStatusMap, isDisabled, isSelected, isClickable, useLocalizedEntityStatus, renderDragHandle, getEntityScheduledActions, onEdit, getAssetUrl, onRemove })=>{
|
|
90
|
+
const status = _fieldeditorshared.entityHelpers.getEntityStatus(asset.sys, useLocalizedEntityStatus ? localeCode : undefined);
|
|
92
91
|
if (status === 'deleted') {
|
|
93
92
|
return _react.createElement(_components.MissingAssetCard, {
|
|
94
93
|
asSquare: true,
|
|
95
|
-
isDisabled:
|
|
96
|
-
onRemove:
|
|
94
|
+
isDisabled: isDisabled,
|
|
95
|
+
onRemove: onRemove
|
|
97
96
|
});
|
|
98
97
|
}
|
|
99
98
|
const entityTitle = _fieldeditorshared.entityHelpers.getAssetTitle({
|
|
100
|
-
asset:
|
|
101
|
-
localeCode:
|
|
102
|
-
defaultLocaleCode:
|
|
99
|
+
asset: asset,
|
|
100
|
+
localeCode: localeCode,
|
|
101
|
+
defaultLocaleCode: defaultLocaleCode,
|
|
103
102
|
defaultTitle: 'Untitled'
|
|
104
103
|
});
|
|
105
|
-
const entityFile =
|
|
106
|
-
const href = getAssetUrl ? getAssetUrl(
|
|
104
|
+
const entityFile = asset.fields.file ? asset.fields.file[localeCode] || asset.fields.file[defaultLocaleCode] : undefined;
|
|
105
|
+
const href = getAssetUrl ? getAssetUrl(asset.sys.id) : undefined;
|
|
107
106
|
return _react.createElement(_f36components.AssetCard, {
|
|
108
107
|
as: href ? 'a' : 'article',
|
|
109
108
|
type: getFileType(entityFile),
|
|
@@ -112,13 +111,13 @@ const WrappedAssetCard = (props)=>{
|
|
|
112
111
|
isSelected: isSelected,
|
|
113
112
|
href: href,
|
|
114
113
|
badge: _react.createElement(_components.EntityStatusBadge, {
|
|
115
|
-
getEntityScheduledActions:
|
|
114
|
+
getEntityScheduledActions: getEntityScheduledActions,
|
|
116
115
|
entityType: "Asset",
|
|
117
116
|
status: status,
|
|
118
|
-
useLocalizedEntityStatus:
|
|
119
|
-
entity:
|
|
120
|
-
localesStatusMap:
|
|
121
|
-
activeLocales:
|
|
117
|
+
useLocalizedEntityStatus: useLocalizedEntityStatus,
|
|
118
|
+
entity: asset,
|
|
119
|
+
localesStatusMap: localesStatusMap,
|
|
120
|
+
activeLocales: activeLocales
|
|
122
121
|
}),
|
|
123
122
|
src: entityFile && entityFile.url ? size === 'small' ? `${entityFile.url}?w=150&h=150&fit=thumb` : `${entityFile.url}?h=300` : '',
|
|
124
123
|
onClick: isClickable ? (e)=>{
|
|
@@ -131,8 +130,8 @@ const WrappedAssetCard = (props)=>{
|
|
|
131
130
|
onEdit();
|
|
132
131
|
}
|
|
133
132
|
} : undefined,
|
|
134
|
-
dragHandleRender:
|
|
135
|
-
withDragHandle: !!
|
|
133
|
+
dragHandleRender: renderDragHandle,
|
|
134
|
+
withDragHandle: !!renderDragHandle,
|
|
136
135
|
actions: [
|
|
137
136
|
...(0, _AssetCardActions.renderActions)({
|
|
138
137
|
entityFile,
|
|
@@ -61,18 +61,17 @@ const defaultProps = {
|
|
|
61
61
|
hasCardMoveActions: true,
|
|
62
62
|
hasCardRemoveActions: true
|
|
63
63
|
};
|
|
64
|
-
function WrappedEntryCard(
|
|
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 }) {
|
|
65
65
|
const [file, setFile] = _react.useState(null);
|
|
66
|
-
const { contentType } = props;
|
|
67
66
|
_react.useEffect(()=>{
|
|
68
67
|
let mounted = true;
|
|
69
|
-
if (
|
|
68
|
+
if (entry) {
|
|
70
69
|
getEntryImage({
|
|
71
|
-
entry
|
|
70
|
+
entry,
|
|
72
71
|
contentType,
|
|
73
|
-
localeCode
|
|
74
|
-
defaultLocaleCode
|
|
75
|
-
},
|
|
72
|
+
localeCode,
|
|
73
|
+
defaultLocaleCode
|
|
74
|
+
}, getAsset).then((file)=>{
|
|
76
75
|
if (mounted) {
|
|
77
76
|
setFile(file);
|
|
78
77
|
}
|
|
@@ -86,92 +85,92 @@ function WrappedEntryCard(props) {
|
|
|
86
85
|
mounted = false;
|
|
87
86
|
};
|
|
88
87
|
}, [
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
entry,
|
|
89
|
+
getAsset,
|
|
91
90
|
contentType,
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
localeCode,
|
|
92
|
+
defaultLocaleCode
|
|
94
93
|
]);
|
|
95
|
-
const status = getEntityStatus(
|
|
94
|
+
const status = getEntityStatus(entry?.sys, useLocalizedEntityStatus ? localeCode : undefined);
|
|
96
95
|
if (status === 'deleted') {
|
|
97
96
|
return _react.createElement(_components.MissingEntityCard, {
|
|
98
|
-
isDisabled:
|
|
99
|
-
onRemove:
|
|
97
|
+
isDisabled: isDisabled,
|
|
98
|
+
onRemove: onRemove,
|
|
100
99
|
providerName: "Contentful"
|
|
101
100
|
});
|
|
102
101
|
}
|
|
103
102
|
const title = getEntryTitle({
|
|
104
|
-
entry
|
|
103
|
+
entry,
|
|
105
104
|
contentType,
|
|
106
|
-
localeCode
|
|
107
|
-
defaultLocaleCode
|
|
105
|
+
localeCode,
|
|
106
|
+
defaultLocaleCode,
|
|
108
107
|
defaultTitle: 'Untitled'
|
|
109
108
|
});
|
|
110
109
|
const description = getEntityDescription({
|
|
111
|
-
entity:
|
|
110
|
+
entity: entry,
|
|
112
111
|
contentType,
|
|
113
|
-
localeCode
|
|
114
|
-
defaultLocaleCode
|
|
112
|
+
localeCode,
|
|
113
|
+
defaultLocaleCode
|
|
115
114
|
});
|
|
116
115
|
return _react.createElement(_f36components.EntryCard, {
|
|
117
|
-
as:
|
|
118
|
-
href:
|
|
116
|
+
as: entryUrl ? 'a' : 'article',
|
|
117
|
+
href: entryUrl,
|
|
119
118
|
title: title,
|
|
120
119
|
description: description,
|
|
121
120
|
contentType: contentType?.name,
|
|
122
|
-
size:
|
|
123
|
-
isSelected:
|
|
121
|
+
size: size,
|
|
122
|
+
isSelected: isSelected,
|
|
124
123
|
badge: _react.createElement(_components.EntityStatusBadge, {
|
|
125
124
|
status: status,
|
|
126
125
|
entityType: "Entry",
|
|
127
|
-
getEntityScheduledActions:
|
|
128
|
-
useLocalizedEntityStatus:
|
|
129
|
-
entity:
|
|
130
|
-
localesStatusMap:
|
|
131
|
-
activeLocales:
|
|
126
|
+
getEntityScheduledActions: getEntityScheduledActions,
|
|
127
|
+
useLocalizedEntityStatus: useLocalizedEntityStatus,
|
|
128
|
+
entity: entry,
|
|
129
|
+
localesStatusMap: localesStatusMap,
|
|
130
|
+
activeLocales: activeLocales
|
|
132
131
|
}),
|
|
133
|
-
icon:
|
|
134
|
-
spaceName:
|
|
135
|
-
environmentName:
|
|
132
|
+
icon: spaceName ? _react.createElement(_SpaceName.SpaceName, {
|
|
133
|
+
spaceName: spaceName,
|
|
134
|
+
environmentName: entry.sys.environment.sys.id
|
|
136
135
|
}) : null,
|
|
137
136
|
thumbnailElement: file && (0, _fieldeditorshared.isValidImage)(file) ? _react.createElement(_components.AssetThumbnail, {
|
|
138
137
|
file: file
|
|
139
138
|
}) : undefined,
|
|
140
|
-
dragHandleRender:
|
|
141
|
-
withDragHandle: !!
|
|
142
|
-
actions:
|
|
143
|
-
|
|
139
|
+
dragHandleRender: renderDragHandle,
|
|
140
|
+
withDragHandle: !!renderDragHandle,
|
|
141
|
+
actions: onEdit || onRemove ? [
|
|
142
|
+
hasCardEditActions && onEdit ? _react.createElement(_f36components.MenuItem, {
|
|
144
143
|
key: "edit",
|
|
145
144
|
testId: "edit",
|
|
146
145
|
onClick: ()=>{
|
|
147
|
-
|
|
146
|
+
onEdit && onEdit();
|
|
148
147
|
}
|
|
149
148
|
}, "Edit") : null,
|
|
150
|
-
|
|
149
|
+
hasCardRemoveActions && onRemove ? _react.createElement(_f36components.MenuItem, {
|
|
151
150
|
key: "delete",
|
|
152
151
|
testId: "delete",
|
|
153
152
|
onClick: ()=>{
|
|
154
|
-
|
|
153
|
+
onRemove && onRemove();
|
|
155
154
|
}
|
|
156
155
|
}, "Remove") : null,
|
|
157
|
-
|
|
156
|
+
hasCardMoveActions && (onMoveTop || onMoveBottom) ? _react.createElement(_f36components.MenuDivider, {
|
|
158
157
|
key: "divider"
|
|
159
158
|
}) : null,
|
|
160
|
-
|
|
159
|
+
hasCardMoveActions && onMoveTop ? _react.createElement(_f36components.MenuItem, {
|
|
161
160
|
key: "move-top",
|
|
162
|
-
onClick: ()=>
|
|
161
|
+
onClick: ()=>onMoveTop && onMoveTop(),
|
|
163
162
|
testId: "move-top"
|
|
164
163
|
}, "Move to top") : null,
|
|
165
|
-
|
|
164
|
+
hasCardMoveActions && onMoveBottom ? _react.createElement(_f36components.MenuItem, {
|
|
166
165
|
key: "move-bottom",
|
|
167
|
-
onClick: ()=>
|
|
166
|
+
onClick: ()=>onMoveBottom && onMoveBottom(),
|
|
168
167
|
testId: "move-bottom"
|
|
169
168
|
}, "Move to bottom") : null
|
|
170
169
|
].filter((item)=>item) : [],
|
|
171
|
-
onClick:
|
|
170
|
+
onClick: isClickable ? (e)=>{
|
|
172
171
|
e.preventDefault();
|
|
173
|
-
if (
|
|
174
|
-
|
|
172
|
+
if (onClick) return onClick(e);
|
|
173
|
+
onEdit && onEdit();
|
|
175
174
|
} : undefined
|
|
176
175
|
});
|
|
177
176
|
}
|
|
@@ -64,7 +64,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
64
64
|
return newObj;
|
|
65
65
|
}
|
|
66
66
|
function ResourceEditor(props) {
|
|
67
|
-
const { setValue, items
|
|
67
|
+
const { setValue, items } = props;
|
|
68
68
|
const onSortStart = ()=>(0, _noop.default)();
|
|
69
69
|
const onSortEnd = (0, _react.useCallback)(({ oldIndex, newIndex })=>{
|
|
70
70
|
const newItems = (0, _sortable.arrayMove)(items, oldIndex, newIndex);
|
|
@@ -86,11 +86,9 @@ function ResourceEditor(props) {
|
|
|
86
86
|
items,
|
|
87
87
|
setValue
|
|
88
88
|
]);
|
|
89
|
-
const { dialogs, field } = props.sdk;
|
|
90
89
|
const linkActionsProps = (0, _useResourceLinkActions.useResourceLinkActions)({
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
apiUrl
|
|
90
|
+
sdk: props.sdk,
|
|
91
|
+
parameters: props.parameters
|
|
94
92
|
});
|
|
95
93
|
return _react.createElement(_react.Fragment, null, props.children({
|
|
96
94
|
...props,
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
const _react = _interop_require_wildcard(require("react"));
|
|
6
5
|
require("@testing-library/jest-dom/extend-expect");
|
|
7
|
-
const
|
|
6
|
+
const _react = require("@testing-library/react");
|
|
7
|
+
const _react1 = _interop_require_wildcard(require("react"));
|
|
8
8
|
const _EntityStore = require("../common/EntityStore");
|
|
9
|
+
const _useEditorPermissions = require("../common/useEditorPermissions");
|
|
9
10
|
const _MultipleResourceReferenceEditor = require("./MultipleResourceReferenceEditor");
|
|
10
11
|
const _resourceEditorHelpers = require("./testHelpers/resourceEditorHelpers");
|
|
11
12
|
function _getRequireWildcardCache(nodeInterop) {
|
|
@@ -61,6 +62,11 @@ jest.mock('../common/EntityStore', ()=>{
|
|
|
61
62
|
}))
|
|
62
63
|
};
|
|
63
64
|
});
|
|
65
|
+
jest.mock('../common/useEditorPermissions', ()=>{
|
|
66
|
+
return {
|
|
67
|
+
useEditorPermissions: jest.fn()
|
|
68
|
+
};
|
|
69
|
+
});
|
|
64
70
|
jest.mock('react-intersection-observer', ()=>{
|
|
65
71
|
const module = jest.requireActual('react-intersection-observer');
|
|
66
72
|
return {
|
|
@@ -88,19 +94,25 @@ const fieldDefinition = {
|
|
|
88
94
|
required: true,
|
|
89
95
|
validations: []
|
|
90
96
|
};
|
|
97
|
+
const mockedUseEditorPermissions = _useEditorPermissions.useEditorPermissions;
|
|
98
|
+
beforeEach(()=>{
|
|
99
|
+
mockedUseEditorPermissions.mockImplementation(()=>({
|
|
100
|
+
canLinkEntity: true
|
|
101
|
+
}));
|
|
102
|
+
});
|
|
91
103
|
describe('Multiple resource editor', ()=>{
|
|
92
104
|
it('renders the action button when no value is set', async ()=>{
|
|
93
105
|
const sdk = (0, _resourceEditorHelpers.mockSdkForField)(fieldDefinition);
|
|
94
|
-
(0,
|
|
106
|
+
(0, _react.render)(_react1.createElement(_MultipleResourceReferenceEditor.MultipleResourceReferenceEditor, {
|
|
95
107
|
isInitiallyDisabled: false,
|
|
96
108
|
sdk: sdk,
|
|
97
109
|
hasCardEditActions: true,
|
|
98
110
|
viewType: "card",
|
|
99
111
|
parameters: {}
|
|
100
112
|
}));
|
|
101
|
-
const button = await
|
|
113
|
+
const button = await _react.screen.findByText('Add existing content');
|
|
102
114
|
expect(button).toBeDefined();
|
|
103
|
-
|
|
115
|
+
_react.fireEvent.click(button);
|
|
104
116
|
const dialogFn = sdk.dialogs.selectMultipleResourceEntities;
|
|
105
117
|
expect(dialogFn).toHaveBeenCalledTimes(1);
|
|
106
118
|
const options = dialogFn.mock.calls[0][0];
|
|
@@ -108,19 +120,35 @@ describe('Multiple resource editor', ()=>{
|
|
|
108
120
|
allowedResources: fieldDefinition.allowedResources
|
|
109
121
|
});
|
|
110
122
|
});
|
|
123
|
+
it('hides the action button when insufficient permissions', async ()=>{
|
|
124
|
+
mockedUseEditorPermissions.mockImplementation(()=>({
|
|
125
|
+
canLinkEntity: false
|
|
126
|
+
}));
|
|
127
|
+
const sdk = (0, _resourceEditorHelpers.mockSdkForField)(fieldDefinition);
|
|
128
|
+
(0, _react.render)(_react1.createElement(_MultipleResourceReferenceEditor.MultipleResourceReferenceEditor, {
|
|
129
|
+
getEntryRouteHref: ()=>'',
|
|
130
|
+
isInitiallyDisabled: false,
|
|
131
|
+
sdk: sdk,
|
|
132
|
+
hasCardEditActions: true,
|
|
133
|
+
viewType: "card",
|
|
134
|
+
parameters: {}
|
|
135
|
+
}));
|
|
136
|
+
const noPermission = await _react.screen.findByText(/You don't have permission to view this content/);
|
|
137
|
+
expect(noPermission).toBeDefined();
|
|
138
|
+
});
|
|
111
139
|
it('renders custom actions when passed', async ()=>{
|
|
112
140
|
const sdk = (0, _resourceEditorHelpers.mockSdkForField)(fieldDefinition);
|
|
113
|
-
(0,
|
|
141
|
+
(0, _react.render)(_react1.createElement(_MultipleResourceReferenceEditor.MultipleResourceReferenceEditor, {
|
|
114
142
|
isInitiallyDisabled: false,
|
|
115
143
|
sdk: sdk,
|
|
116
144
|
hasCardEditActions: true,
|
|
117
145
|
viewType: "card",
|
|
118
146
|
parameters: {},
|
|
119
|
-
renderCustomActions: ()=>
|
|
147
|
+
renderCustomActions: ()=>_react1.createElement("div", {
|
|
120
148
|
"data-testid": "custom-actions"
|
|
121
149
|
})
|
|
122
150
|
}));
|
|
123
|
-
const customActions = await
|
|
151
|
+
const customActions = await _react.screen.findByTestId('custom-actions');
|
|
124
152
|
expect(customActions).toBeDefined();
|
|
125
153
|
});
|
|
126
154
|
describe('with value', ()=>{
|
|
@@ -131,7 +159,7 @@ describe('Multiple resource editor', ()=>{
|
|
|
131
159
|
mockedResources[`${link.sys.linkType}.${link.sys.urn}`] = entryInfos[spaceId];
|
|
132
160
|
}
|
|
133
161
|
const sdk = (0, _resourceEditorHelpers.mockSdkForField)(fieldDefinition, Object.values(entryLinks));
|
|
134
|
-
(0,
|
|
162
|
+
(0, _react.render)(_react1.createElement(_MultipleResourceReferenceEditor.MultipleResourceReferenceEditor, {
|
|
135
163
|
isInitiallyDisabled: false,
|
|
136
164
|
sdk: sdk,
|
|
137
165
|
hasCardEditActions: true,
|
|
@@ -158,7 +186,7 @@ describe('Multiple resource editor', ()=>{
|
|
|
158
186
|
mockedResources[`${link.sys.linkType}.${link.sys.urn}`] = entryInfos[spaceId];
|
|
159
187
|
}
|
|
160
188
|
const sdk = (0, _resourceEditorHelpers.mockSdkForField)(fieldDefinition, Object.values(entryLinks));
|
|
161
|
-
(0,
|
|
189
|
+
(0, _react.render)(_react1.createElement(_MultipleResourceReferenceEditor.MultipleResourceReferenceEditor, {
|
|
162
190
|
isInitiallyDisabled: false,
|
|
163
191
|
sdk: sdk,
|
|
164
192
|
hasCardEditActions: true,
|
|
@@ -167,7 +195,7 @@ describe('Multiple resource editor', ()=>{
|
|
|
167
195
|
getEntryRouteHref: ()=>'',
|
|
168
196
|
parameters: {}
|
|
169
197
|
}));
|
|
170
|
-
const linkExistingBtn =
|
|
198
|
+
const linkExistingBtn = _react.screen.queryByText('Add existing content');
|
|
171
199
|
expect(linkExistingBtn).toBeInTheDocument();
|
|
172
200
|
const entriesArray = Object.values(entryInfos);
|
|
173
201
|
const firstItem = entriesArray[0];
|
|
@@ -184,7 +212,7 @@ describe('Multiple resource editor', ()=>{
|
|
|
184
212
|
mockedResources[`${link.sys.linkType}.${link.sys.urn}`] = entryInfos[spaceId];
|
|
185
213
|
}
|
|
186
214
|
const sdk = (0, _resourceEditorHelpers.mockSdkForField)(fieldDefinition, Object.values(entryLinks));
|
|
187
|
-
(0,
|
|
215
|
+
(0, _react.render)(_react1.createElement(_MultipleResourceReferenceEditor.MultipleResourceReferenceEditor, {
|
|
188
216
|
isInitiallyDisabled: false,
|
|
189
217
|
sdk: sdk,
|
|
190
218
|
hasCardEditActions: true,
|
|
@@ -193,7 +221,7 @@ describe('Multiple resource editor', ()=>{
|
|
|
193
221
|
getEntryRouteHref: ()=>'',
|
|
194
222
|
parameters: {}
|
|
195
223
|
}));
|
|
196
|
-
const linkExistingBtn =
|
|
224
|
+
const linkExistingBtn = _react.screen.queryByText('Add existing content');
|
|
197
225
|
expect(linkExistingBtn).toBeInTheDocument();
|
|
198
226
|
const entriesArray = Object.values(entryInfos);
|
|
199
227
|
const lastItem = entriesArray[entriesArray.length - 1];
|
|
@@ -210,7 +238,7 @@ describe('Multiple resource editor', ()=>{
|
|
|
210
238
|
mockedResources[`${link.sys.linkType}.${link.sys.urn}`] = entryInfos[spaceId];
|
|
211
239
|
}
|
|
212
240
|
const sdk = (0, _resourceEditorHelpers.mockSdkForField)(fieldDefinition, Object.values(entryLinks));
|
|
213
|
-
(0,
|
|
241
|
+
(0, _react.render)(_react1.createElement(_MultipleResourceReferenceEditor.MultipleResourceReferenceEditor, {
|
|
214
242
|
isInitiallyDisabled: false,
|
|
215
243
|
sdk: sdk,
|
|
216
244
|
hasCardEditActions: true,
|
|
@@ -219,15 +247,15 @@ describe('Multiple resource editor', ()=>{
|
|
|
219
247
|
getEntryRouteHref: ()=>'',
|
|
220
248
|
parameters: {}
|
|
221
249
|
}));
|
|
222
|
-
const linkExistingBtn =
|
|
250
|
+
const linkExistingBtn = _react.screen.queryByText('Add existing content');
|
|
223
251
|
expect(linkExistingBtn).toBeInTheDocument();
|
|
224
252
|
const entriesArray = Object.values(entryInfos);
|
|
225
253
|
for (const info of entriesArray){
|
|
226
254
|
await clickCardActionsButton(info);
|
|
227
|
-
const removeBtn = await
|
|
255
|
+
const removeBtn = await _react.screen.findByText('Remove', {
|
|
228
256
|
selector: '[role="menuitem"]'
|
|
229
257
|
});
|
|
230
|
-
|
|
258
|
+
_react.fireEvent.click(removeBtn);
|
|
231
259
|
}
|
|
232
260
|
expect(sdk.field.setValue).toHaveBeenCalledTimes(3);
|
|
233
261
|
expect(sdk.field.setValue).toHaveBeenCalledWith([]);
|
|
@@ -237,28 +265,28 @@ describe('Multiple resource editor', ()=>{
|
|
|
237
265
|
});
|
|
238
266
|
async function expectToHaveMoveButton(info, buttonString) {
|
|
239
267
|
await clickCardActionsButton(info);
|
|
240
|
-
await
|
|
268
|
+
await _react.screen.findByText(buttonString, {
|
|
241
269
|
selector: '[role="menuitem"]'
|
|
242
270
|
});
|
|
243
271
|
}
|
|
244
272
|
async function expectToNotHaveMoveButton(info, buttonString) {
|
|
245
273
|
await clickCardActionsButton(info);
|
|
246
|
-
expect(
|
|
274
|
+
expect(_react.screen.queryByText(buttonString, {
|
|
247
275
|
selector: '[role="menuitem"]'
|
|
248
276
|
})).toBeNull();
|
|
249
277
|
}
|
|
250
278
|
async function clickCardActionsButton(info) {
|
|
251
|
-
|
|
279
|
+
_react.fireEvent.click(document.body);
|
|
252
280
|
const entryTitle = info.resource.fields.title[info.defaultLocaleCode];
|
|
253
281
|
const spaceName = info.space.name;
|
|
254
282
|
const card = await expectEntryCard(entryTitle, spaceName);
|
|
255
283
|
const actionsBtn = card.querySelector('[data-test-id="cf-ui-card-actions"]');
|
|
256
284
|
expect(actionsBtn).toBeInTheDocument();
|
|
257
|
-
|
|
285
|
+
_react.fireEvent.click(actionsBtn);
|
|
258
286
|
}
|
|
259
287
|
async function expectEntryCard(entryTitle, spaceName) {
|
|
260
|
-
const title = await
|
|
261
|
-
await
|
|
288
|
+
const title = await _react.screen.findByText(entryTitle);
|
|
289
|
+
await _react.screen.findByText(spaceName);
|
|
262
290
|
const theCard = title.closest('[data-test-id="cf-ui-entry-card"]');
|
|
263
291
|
expect(theCard).toBeDefined();
|
|
264
292
|
const actionsBtn = theCard?.querySelector('[data-test-id="cf-ui-card-actions"]');
|
|
@@ -56,11 +56,9 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
56
56
|
return newObj;
|
|
57
57
|
}
|
|
58
58
|
function SingleResourceReferenceEditor(props) {
|
|
59
|
-
const { dialogs, field } = props.sdk;
|
|
60
59
|
const linkActionsProps = (0, _useResourceLinkActions.useResourceLinkActions)({
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
apiUrl: props.apiUrl
|
|
60
|
+
sdk: props.sdk,
|
|
61
|
+
parameters: props.parameters
|
|
64
62
|
});
|
|
65
63
|
return _react.createElement(_EntityStore.EntityProvider, {
|
|
66
64
|
sdk: props.sdk
|
|
@@ -6,6 +6,7 @@ const _react = _interop_require_wildcard(require("react"));
|
|
|
6
6
|
require("@testing-library/jest-dom/extend-expect");
|
|
7
7
|
const _react1 = require("@testing-library/react");
|
|
8
8
|
const _EntityStore = require("../common/EntityStore");
|
|
9
|
+
const _useEditorPermissions = require("../common/useEditorPermissions");
|
|
9
10
|
const _SingleResourceReferenceEditor = require("./SingleResourceReferenceEditor");
|
|
10
11
|
const _resourceEditorHelpers = require("./testHelpers/resourceEditorHelpers");
|
|
11
12
|
function _getRequireWildcardCache(nodeInterop) {
|
|
@@ -69,6 +70,11 @@ jest.mock('react-intersection-observer', ()=>{
|
|
|
69
70
|
})
|
|
70
71
|
};
|
|
71
72
|
});
|
|
73
|
+
jest.mock('../common/useEditorPermissions', ()=>{
|
|
74
|
+
return {
|
|
75
|
+
useEditorPermissions: jest.fn()
|
|
76
|
+
};
|
|
77
|
+
});
|
|
72
78
|
const fieldDefinition = {
|
|
73
79
|
type: 'ResourceLink',
|
|
74
80
|
id: 'foo',
|
|
@@ -84,6 +90,12 @@ const fieldDefinition = {
|
|
|
84
90
|
required: true,
|
|
85
91
|
validations: []
|
|
86
92
|
};
|
|
93
|
+
const mockedUseEditorPermissions = _useEditorPermissions.useEditorPermissions;
|
|
94
|
+
beforeEach(()=>{
|
|
95
|
+
mockedUseEditorPermissions.mockImplementation(()=>({
|
|
96
|
+
canLinkEntity: true
|
|
97
|
+
}));
|
|
98
|
+
});
|
|
87
99
|
describe('Single resource editor', ()=>{
|
|
88
100
|
it('renders the action button when no value is set', async ()=>{
|
|
89
101
|
const sdk = (0, _resourceEditorHelpers.mockSdkForField)(fieldDefinition);
|
|
@@ -104,6 +116,21 @@ describe('Single resource editor', ()=>{
|
|
|
104
116
|
allowedResources: fieldDefinition.allowedResources
|
|
105
117
|
});
|
|
106
118
|
});
|
|
119
|
+
it('renders no the action button when permissions insufficient', async ()=>{
|
|
120
|
+
mockedUseEditorPermissions.mockImplementation(()=>({
|
|
121
|
+
canLinkEntity: false
|
|
122
|
+
}));
|
|
123
|
+
const sdk = (0, _resourceEditorHelpers.mockSdkForField)(fieldDefinition);
|
|
124
|
+
(0, _react1.render)(_react.createElement(_SingleResourceReferenceEditor.SingleResourceReferenceEditor, {
|
|
125
|
+
isInitiallyDisabled: false,
|
|
126
|
+
sdk: sdk,
|
|
127
|
+
hasCardEditActions: true,
|
|
128
|
+
viewType: "card",
|
|
129
|
+
parameters: {}
|
|
130
|
+
}));
|
|
131
|
+
const noPermission = await _react1.screen.findByText(/You don't have permission to view this content/);
|
|
132
|
+
expect(noPermission).toBeDefined();
|
|
133
|
+
});
|
|
107
134
|
it('renders custom actions when passed', async ()=>{
|
|
108
135
|
const sdk = (0, _resourceEditorHelpers.mockSdkForField)(fieldDefinition);
|
|
109
136
|
(0, _react1.render)(_react.createElement(_SingleResourceReferenceEditor.SingleResourceReferenceEditor, {
|
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "useResourceLinkActions", {
|
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
11
|
const _react = require("react");
|
|
12
|
+
const _useEditorPermissions = require("../common/useEditorPermissions");
|
|
12
13
|
const getUpdatedValue = (field, linkItems)=>{
|
|
13
14
|
const multiple = field.type === 'Array';
|
|
14
15
|
if (multiple) {
|
|
@@ -21,7 +22,8 @@ const getUpdatedValue = (field, linkItems)=>{
|
|
|
21
22
|
return linkItems[0];
|
|
22
23
|
}
|
|
23
24
|
};
|
|
24
|
-
function useResourceLinkActions({
|
|
25
|
+
function useResourceLinkActions({ parameters, sdk }) {
|
|
26
|
+
const { field, dialogs } = sdk;
|
|
25
27
|
const onLinkedExisting = (0, _react.useMemo)(()=>{
|
|
26
28
|
return (links)=>{
|
|
27
29
|
const updatedValue = getUpdatedValue(field, links);
|
|
@@ -50,6 +52,12 @@ function useResourceLinkActions({ dialogs, field }) {
|
|
|
50
52
|
multiple,
|
|
51
53
|
onLinkedExisting
|
|
52
54
|
]);
|
|
55
|
+
const { canLinkEntity } = (0, _useEditorPermissions.useEditorPermissions)({
|
|
56
|
+
entityType: 'Entry',
|
|
57
|
+
allContentTypes: [],
|
|
58
|
+
sdk,
|
|
59
|
+
parameters
|
|
60
|
+
});
|
|
53
61
|
return {
|
|
54
62
|
onLinkExisting,
|
|
55
63
|
onLinkedExisting,
|
|
@@ -57,7 +65,7 @@ function useResourceLinkActions({ dialogs, field }) {
|
|
|
57
65
|
contentTypes: [],
|
|
58
66
|
canCreateEntity: false,
|
|
59
67
|
canLinkMultiple: multiple,
|
|
60
|
-
canLinkEntity
|
|
68
|
+
canLinkEntity,
|
|
61
69
|
isDisabled: false,
|
|
62
70
|
isEmpty: false,
|
|
63
71
|
isFull: false,
|
|
@@ -30,24 +30,23 @@ function getFileType(file) {
|
|
|
30
30
|
});
|
|
31
31
|
return groupToIconMap[groupName] || 'archive';
|
|
32
32
|
}
|
|
33
|
-
export const WrappedAssetCard = (
|
|
34
|
-
const
|
|
35
|
-
const status = entityHelpers.getEntityStatus(props.asset.sys, props.useLocalizedEntityStatus ? props.localeCode : undefined);
|
|
33
|
+
export const WrappedAssetCard = ({ asset, className, size, localeCode, defaultLocaleCode, activeLocales, localesStatusMap, isDisabled, isSelected, isClickable, useLocalizedEntityStatus, renderDragHandle, getEntityScheduledActions, onEdit, getAssetUrl, onRemove })=>{
|
|
34
|
+
const status = entityHelpers.getEntityStatus(asset.sys, useLocalizedEntityStatus ? localeCode : undefined);
|
|
36
35
|
if (status === 'deleted') {
|
|
37
36
|
return React.createElement(MissingAssetCard, {
|
|
38
37
|
asSquare: true,
|
|
39
|
-
isDisabled:
|
|
40
|
-
onRemove:
|
|
38
|
+
isDisabled: isDisabled,
|
|
39
|
+
onRemove: onRemove
|
|
41
40
|
});
|
|
42
41
|
}
|
|
43
42
|
const entityTitle = entityHelpers.getAssetTitle({
|
|
44
|
-
asset:
|
|
45
|
-
localeCode:
|
|
46
|
-
defaultLocaleCode:
|
|
43
|
+
asset: asset,
|
|
44
|
+
localeCode: localeCode,
|
|
45
|
+
defaultLocaleCode: defaultLocaleCode,
|
|
47
46
|
defaultTitle: 'Untitled'
|
|
48
47
|
});
|
|
49
|
-
const entityFile =
|
|
50
|
-
const href = getAssetUrl ? getAssetUrl(
|
|
48
|
+
const entityFile = asset.fields.file ? asset.fields.file[localeCode] || asset.fields.file[defaultLocaleCode] : undefined;
|
|
49
|
+
const href = getAssetUrl ? getAssetUrl(asset.sys.id) : undefined;
|
|
51
50
|
return React.createElement(AssetCard, {
|
|
52
51
|
as: href ? 'a' : 'article',
|
|
53
52
|
type: getFileType(entityFile),
|
|
@@ -56,13 +55,13 @@ export const WrappedAssetCard = (props)=>{
|
|
|
56
55
|
isSelected: isSelected,
|
|
57
56
|
href: href,
|
|
58
57
|
badge: React.createElement(EntityStatusBadge, {
|
|
59
|
-
getEntityScheduledActions:
|
|
58
|
+
getEntityScheduledActions: getEntityScheduledActions,
|
|
60
59
|
entityType: "Asset",
|
|
61
60
|
status: status,
|
|
62
|
-
useLocalizedEntityStatus:
|
|
63
|
-
entity:
|
|
64
|
-
localesStatusMap:
|
|
65
|
-
activeLocales:
|
|
61
|
+
useLocalizedEntityStatus: useLocalizedEntityStatus,
|
|
62
|
+
entity: asset,
|
|
63
|
+
localesStatusMap: localesStatusMap,
|
|
64
|
+
activeLocales: activeLocales
|
|
66
65
|
}),
|
|
67
66
|
src: entityFile && entityFile.url ? size === 'small' ? `${entityFile.url}?w=150&h=150&fit=thumb` : `${entityFile.url}?h=300` : '',
|
|
68
67
|
onClick: isClickable ? (e)=>{
|
|
@@ -75,8 +74,8 @@ export const WrappedAssetCard = (props)=>{
|
|
|
75
74
|
onEdit();
|
|
76
75
|
}
|
|
77
76
|
} : undefined,
|
|
78
|
-
dragHandleRender:
|
|
79
|
-
withDragHandle: !!
|
|
77
|
+
dragHandleRender: renderDragHandle,
|
|
78
|
+
withDragHandle: !!renderDragHandle,
|
|
80
79
|
actions: [
|
|
81
80
|
...renderActions({
|
|
82
81
|
entityFile,
|
|
@@ -10,18 +10,17 @@ const defaultProps = {
|
|
|
10
10
|
hasCardMoveActions: true,
|
|
11
11
|
hasCardRemoveActions: true
|
|
12
12
|
};
|
|
13
|
-
export function WrappedEntryCard(
|
|
13
|
+
export 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 }) {
|
|
14
14
|
const [file, setFile] = React.useState(null);
|
|
15
|
-
const { contentType } = props;
|
|
16
15
|
React.useEffect(()=>{
|
|
17
16
|
let mounted = true;
|
|
18
|
-
if (
|
|
17
|
+
if (entry) {
|
|
19
18
|
getEntryImage({
|
|
20
|
-
entry
|
|
19
|
+
entry,
|
|
21
20
|
contentType,
|
|
22
|
-
localeCode
|
|
23
|
-
defaultLocaleCode
|
|
24
|
-
},
|
|
21
|
+
localeCode,
|
|
22
|
+
defaultLocaleCode
|
|
23
|
+
}, getAsset).then((file)=>{
|
|
25
24
|
if (mounted) {
|
|
26
25
|
setFile(file);
|
|
27
26
|
}
|
|
@@ -35,92 +34,92 @@ export function WrappedEntryCard(props) {
|
|
|
35
34
|
mounted = false;
|
|
36
35
|
};
|
|
37
36
|
}, [
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
entry,
|
|
38
|
+
getAsset,
|
|
40
39
|
contentType,
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
localeCode,
|
|
41
|
+
defaultLocaleCode
|
|
43
42
|
]);
|
|
44
|
-
const status = getEntityStatus(
|
|
43
|
+
const status = getEntityStatus(entry?.sys, useLocalizedEntityStatus ? localeCode : undefined);
|
|
45
44
|
if (status === 'deleted') {
|
|
46
45
|
return React.createElement(MissingEntityCard, {
|
|
47
|
-
isDisabled:
|
|
48
|
-
onRemove:
|
|
46
|
+
isDisabled: isDisabled,
|
|
47
|
+
onRemove: onRemove,
|
|
49
48
|
providerName: "Contentful"
|
|
50
49
|
});
|
|
51
50
|
}
|
|
52
51
|
const title = getEntryTitle({
|
|
53
|
-
entry
|
|
52
|
+
entry,
|
|
54
53
|
contentType,
|
|
55
|
-
localeCode
|
|
56
|
-
defaultLocaleCode
|
|
54
|
+
localeCode,
|
|
55
|
+
defaultLocaleCode,
|
|
57
56
|
defaultTitle: 'Untitled'
|
|
58
57
|
});
|
|
59
58
|
const description = getEntityDescription({
|
|
60
|
-
entity:
|
|
59
|
+
entity: entry,
|
|
61
60
|
contentType,
|
|
62
|
-
localeCode
|
|
63
|
-
defaultLocaleCode
|
|
61
|
+
localeCode,
|
|
62
|
+
defaultLocaleCode
|
|
64
63
|
});
|
|
65
64
|
return React.createElement(EntryCard, {
|
|
66
|
-
as:
|
|
67
|
-
href:
|
|
65
|
+
as: entryUrl ? 'a' : 'article',
|
|
66
|
+
href: entryUrl,
|
|
68
67
|
title: title,
|
|
69
68
|
description: description,
|
|
70
69
|
contentType: contentType?.name,
|
|
71
|
-
size:
|
|
72
|
-
isSelected:
|
|
70
|
+
size: size,
|
|
71
|
+
isSelected: isSelected,
|
|
73
72
|
badge: React.createElement(EntityStatusBadge, {
|
|
74
73
|
status: status,
|
|
75
74
|
entityType: "Entry",
|
|
76
|
-
getEntityScheduledActions:
|
|
77
|
-
useLocalizedEntityStatus:
|
|
78
|
-
entity:
|
|
79
|
-
localesStatusMap:
|
|
80
|
-
activeLocales:
|
|
75
|
+
getEntityScheduledActions: getEntityScheduledActions,
|
|
76
|
+
useLocalizedEntityStatus: useLocalizedEntityStatus,
|
|
77
|
+
entity: entry,
|
|
78
|
+
localesStatusMap: localesStatusMap,
|
|
79
|
+
activeLocales: activeLocales
|
|
81
80
|
}),
|
|
82
|
-
icon:
|
|
83
|
-
spaceName:
|
|
84
|
-
environmentName:
|
|
81
|
+
icon: spaceName ? React.createElement(SpaceName, {
|
|
82
|
+
spaceName: spaceName,
|
|
83
|
+
environmentName: entry.sys.environment.sys.id
|
|
85
84
|
}) : null,
|
|
86
85
|
thumbnailElement: file && isValidImage(file) ? React.createElement(AssetThumbnail, {
|
|
87
86
|
file: file
|
|
88
87
|
}) : undefined,
|
|
89
|
-
dragHandleRender:
|
|
90
|
-
withDragHandle: !!
|
|
91
|
-
actions:
|
|
92
|
-
|
|
88
|
+
dragHandleRender: renderDragHandle,
|
|
89
|
+
withDragHandle: !!renderDragHandle,
|
|
90
|
+
actions: onEdit || onRemove ? [
|
|
91
|
+
hasCardEditActions && onEdit ? React.createElement(MenuItem, {
|
|
93
92
|
key: "edit",
|
|
94
93
|
testId: "edit",
|
|
95
94
|
onClick: ()=>{
|
|
96
|
-
|
|
95
|
+
onEdit && onEdit();
|
|
97
96
|
}
|
|
98
97
|
}, "Edit") : null,
|
|
99
|
-
|
|
98
|
+
hasCardRemoveActions && onRemove ? React.createElement(MenuItem, {
|
|
100
99
|
key: "delete",
|
|
101
100
|
testId: "delete",
|
|
102
101
|
onClick: ()=>{
|
|
103
|
-
|
|
102
|
+
onRemove && onRemove();
|
|
104
103
|
}
|
|
105
104
|
}, "Remove") : null,
|
|
106
|
-
|
|
105
|
+
hasCardMoveActions && (onMoveTop || onMoveBottom) ? React.createElement(MenuDivider, {
|
|
107
106
|
key: "divider"
|
|
108
107
|
}) : null,
|
|
109
|
-
|
|
108
|
+
hasCardMoveActions && onMoveTop ? React.createElement(MenuItem, {
|
|
110
109
|
key: "move-top",
|
|
111
|
-
onClick: ()=>
|
|
110
|
+
onClick: ()=>onMoveTop && onMoveTop(),
|
|
112
111
|
testId: "move-top"
|
|
113
112
|
}, "Move to top") : null,
|
|
114
|
-
|
|
113
|
+
hasCardMoveActions && onMoveBottom ? React.createElement(MenuItem, {
|
|
115
114
|
key: "move-bottom",
|
|
116
|
-
onClick: ()=>
|
|
115
|
+
onClick: ()=>onMoveBottom && onMoveBottom(),
|
|
117
116
|
testId: "move-bottom"
|
|
118
117
|
}, "Move to bottom") : null
|
|
119
118
|
].filter((item)=>item) : [],
|
|
120
|
-
onClick:
|
|
119
|
+
onClick: isClickable ? (e)=>{
|
|
121
120
|
e.preventDefault();
|
|
122
|
-
if (
|
|
123
|
-
|
|
121
|
+
if (onClick) return onClick(e);
|
|
122
|
+
onEdit && onEdit();
|
|
124
123
|
} : undefined
|
|
125
124
|
});
|
|
126
125
|
}
|
|
@@ -9,7 +9,7 @@ import { CombinedLinkEntityActions } from '../components/LinkActions/LinkEntityA
|
|
|
9
9
|
import { ResourceCard } from './Cards/ResourceCard';
|
|
10
10
|
import { useResourceLinkActions } from './useResourceLinkActions';
|
|
11
11
|
function ResourceEditor(props) {
|
|
12
|
-
const { setValue, items
|
|
12
|
+
const { setValue, items } = props;
|
|
13
13
|
const onSortStart = ()=>noop();
|
|
14
14
|
const onSortEnd = useCallback(({ oldIndex, newIndex })=>{
|
|
15
15
|
const newItems = arrayMove(items, oldIndex, newIndex);
|
|
@@ -31,11 +31,9 @@ function ResourceEditor(props) {
|
|
|
31
31
|
items,
|
|
32
32
|
setValue
|
|
33
33
|
]);
|
|
34
|
-
const { dialogs, field } = props.sdk;
|
|
35
34
|
const linkActionsProps = useResourceLinkActions({
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
apiUrl
|
|
35
|
+
sdk: props.sdk,
|
|
36
|
+
parameters: props.parameters
|
|
39
37
|
});
|
|
40
38
|
return React.createElement(React.Fragment, null, props.children({
|
|
41
39
|
...props,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
1
|
import '@testing-library/jest-dom/extend-expect';
|
|
3
2
|
import { fireEvent, render, screen } from '@testing-library/react';
|
|
3
|
+
import * as React from 'react';
|
|
4
4
|
import { useResource } from '../common/EntityStore';
|
|
5
|
+
import { useEditorPermissions } from '../common/useEditorPermissions';
|
|
5
6
|
import { MultipleResourceReferenceEditor } from './MultipleResourceReferenceEditor';
|
|
6
7
|
import { createFakeEntryResource, mockSdkForField } from './testHelpers/resourceEditorHelpers';
|
|
7
8
|
let mockedResources = {};
|
|
@@ -16,6 +17,11 @@ jest.mock('../common/EntityStore', ()=>{
|
|
|
16
17
|
}))
|
|
17
18
|
};
|
|
18
19
|
});
|
|
20
|
+
jest.mock('../common/useEditorPermissions', ()=>{
|
|
21
|
+
return {
|
|
22
|
+
useEditorPermissions: jest.fn()
|
|
23
|
+
};
|
|
24
|
+
});
|
|
19
25
|
jest.mock('react-intersection-observer', ()=>{
|
|
20
26
|
const module = jest.requireActual('react-intersection-observer');
|
|
21
27
|
return {
|
|
@@ -43,6 +49,12 @@ const fieldDefinition = {
|
|
|
43
49
|
required: true,
|
|
44
50
|
validations: []
|
|
45
51
|
};
|
|
52
|
+
const mockedUseEditorPermissions = useEditorPermissions;
|
|
53
|
+
beforeEach(()=>{
|
|
54
|
+
mockedUseEditorPermissions.mockImplementation(()=>({
|
|
55
|
+
canLinkEntity: true
|
|
56
|
+
}));
|
|
57
|
+
});
|
|
46
58
|
describe('Multiple resource editor', ()=>{
|
|
47
59
|
it('renders the action button when no value is set', async ()=>{
|
|
48
60
|
const sdk = mockSdkForField(fieldDefinition);
|
|
@@ -63,6 +75,22 @@ describe('Multiple resource editor', ()=>{
|
|
|
63
75
|
allowedResources: fieldDefinition.allowedResources
|
|
64
76
|
});
|
|
65
77
|
});
|
|
78
|
+
it('hides the action button when insufficient permissions', async ()=>{
|
|
79
|
+
mockedUseEditorPermissions.mockImplementation(()=>({
|
|
80
|
+
canLinkEntity: false
|
|
81
|
+
}));
|
|
82
|
+
const sdk = mockSdkForField(fieldDefinition);
|
|
83
|
+
render(React.createElement(MultipleResourceReferenceEditor, {
|
|
84
|
+
getEntryRouteHref: ()=>'',
|
|
85
|
+
isInitiallyDisabled: false,
|
|
86
|
+
sdk: sdk,
|
|
87
|
+
hasCardEditActions: true,
|
|
88
|
+
viewType: "card",
|
|
89
|
+
parameters: {}
|
|
90
|
+
}));
|
|
91
|
+
const noPermission = await screen.findByText(/You don't have permission to view this content/);
|
|
92
|
+
expect(noPermission).toBeDefined();
|
|
93
|
+
});
|
|
66
94
|
it('renders custom actions when passed', async ()=>{
|
|
67
95
|
const sdk = mockSdkForField(fieldDefinition);
|
|
68
96
|
render(React.createElement(MultipleResourceReferenceEditor, {
|
|
@@ -5,11 +5,9 @@ import { CombinedLinkEntityActions } from '../components/LinkActions/LinkEntityA
|
|
|
5
5
|
import { ResourceCard } from './Cards/ResourceCard';
|
|
6
6
|
import { useResourceLinkActions } from './useResourceLinkActions';
|
|
7
7
|
export function SingleResourceReferenceEditor(props) {
|
|
8
|
-
const { dialogs, field } = props.sdk;
|
|
9
8
|
const linkActionsProps = useResourceLinkActions({
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
apiUrl: props.apiUrl
|
|
9
|
+
sdk: props.sdk,
|
|
10
|
+
parameters: props.parameters
|
|
13
11
|
});
|
|
14
12
|
return React.createElement(EntityProvider, {
|
|
15
13
|
sdk: props.sdk
|
|
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import '@testing-library/jest-dom/extend-expect';
|
|
3
3
|
import { fireEvent, render, screen } from '@testing-library/react';
|
|
4
4
|
import { useResource } from '../common/EntityStore';
|
|
5
|
+
import { useEditorPermissions } from '../common/useEditorPermissions';
|
|
5
6
|
import { SingleResourceReferenceEditor } from './SingleResourceReferenceEditor';
|
|
6
7
|
import { createFakeEntryResource, mockSdkForField } from './testHelpers/resourceEditorHelpers';
|
|
7
8
|
const mockedResources = {};
|
|
@@ -24,6 +25,11 @@ jest.mock('react-intersection-observer', ()=>{
|
|
|
24
25
|
})
|
|
25
26
|
};
|
|
26
27
|
});
|
|
28
|
+
jest.mock('../common/useEditorPermissions', ()=>{
|
|
29
|
+
return {
|
|
30
|
+
useEditorPermissions: jest.fn()
|
|
31
|
+
};
|
|
32
|
+
});
|
|
27
33
|
const fieldDefinition = {
|
|
28
34
|
type: 'ResourceLink',
|
|
29
35
|
id: 'foo',
|
|
@@ -39,6 +45,12 @@ const fieldDefinition = {
|
|
|
39
45
|
required: true,
|
|
40
46
|
validations: []
|
|
41
47
|
};
|
|
48
|
+
const mockedUseEditorPermissions = useEditorPermissions;
|
|
49
|
+
beforeEach(()=>{
|
|
50
|
+
mockedUseEditorPermissions.mockImplementation(()=>({
|
|
51
|
+
canLinkEntity: true
|
|
52
|
+
}));
|
|
53
|
+
});
|
|
42
54
|
describe('Single resource editor', ()=>{
|
|
43
55
|
it('renders the action button when no value is set', async ()=>{
|
|
44
56
|
const sdk = mockSdkForField(fieldDefinition);
|
|
@@ -59,6 +71,21 @@ describe('Single resource editor', ()=>{
|
|
|
59
71
|
allowedResources: fieldDefinition.allowedResources
|
|
60
72
|
});
|
|
61
73
|
});
|
|
74
|
+
it('renders no the action button when permissions insufficient', async ()=>{
|
|
75
|
+
mockedUseEditorPermissions.mockImplementation(()=>({
|
|
76
|
+
canLinkEntity: false
|
|
77
|
+
}));
|
|
78
|
+
const sdk = mockSdkForField(fieldDefinition);
|
|
79
|
+
render(React.createElement(SingleResourceReferenceEditor, {
|
|
80
|
+
isInitiallyDisabled: false,
|
|
81
|
+
sdk: sdk,
|
|
82
|
+
hasCardEditActions: true,
|
|
83
|
+
viewType: "card",
|
|
84
|
+
parameters: {}
|
|
85
|
+
}));
|
|
86
|
+
const noPermission = await screen.findByText(/You don't have permission to view this content/);
|
|
87
|
+
expect(noPermission).toBeDefined();
|
|
88
|
+
});
|
|
62
89
|
it('renders custom actions when passed', async ()=>{
|
|
63
90
|
const sdk = mockSdkForField(fieldDefinition);
|
|
64
91
|
render(React.createElement(SingleResourceReferenceEditor, {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
|
+
import { useEditorPermissions } from '../common/useEditorPermissions';
|
|
2
3
|
const getUpdatedValue = (field, linkItems)=>{
|
|
3
4
|
const multiple = field.type === 'Array';
|
|
4
5
|
if (multiple) {
|
|
@@ -11,7 +12,8 @@ const getUpdatedValue = (field, linkItems)=>{
|
|
|
11
12
|
return linkItems[0];
|
|
12
13
|
}
|
|
13
14
|
};
|
|
14
|
-
export function useResourceLinkActions({
|
|
15
|
+
export function useResourceLinkActions({ parameters, sdk }) {
|
|
16
|
+
const { field, dialogs } = sdk;
|
|
15
17
|
const onLinkedExisting = useMemo(()=>{
|
|
16
18
|
return (links)=>{
|
|
17
19
|
const updatedValue = getUpdatedValue(field, links);
|
|
@@ -40,6 +42,12 @@ export function useResourceLinkActions({ dialogs, field }) {
|
|
|
40
42
|
multiple,
|
|
41
43
|
onLinkedExisting
|
|
42
44
|
]);
|
|
45
|
+
const { canLinkEntity } = useEditorPermissions({
|
|
46
|
+
entityType: 'Entry',
|
|
47
|
+
allContentTypes: [],
|
|
48
|
+
sdk,
|
|
49
|
+
parameters
|
|
50
|
+
});
|
|
43
51
|
return {
|
|
44
52
|
onLinkExisting,
|
|
45
53
|
onLinkedExisting,
|
|
@@ -47,7 +55,7 @@ export function useResourceLinkActions({ dialogs, field }) {
|
|
|
47
55
|
contentTypes: [],
|
|
48
56
|
canCreateEntity: false,
|
|
49
57
|
canLinkMultiple: multiple,
|
|
50
|
-
canLinkEntity
|
|
58
|
+
canLinkEntity,
|
|
51
59
|
isDisabled: false,
|
|
52
60
|
isEmpty: false,
|
|
53
61
|
isFull: false,
|
|
@@ -22,7 +22,7 @@ export interface WrappedAssetCardProps {
|
|
|
22
22
|
activeLocales?: Pick<LocaleProps, 'code'>[];
|
|
23
23
|
}
|
|
24
24
|
export declare const WrappedAssetCard: {
|
|
25
|
-
(
|
|
25
|
+
({ asset, className, size, localeCode, defaultLocaleCode, activeLocales, localesStatusMap, isDisabled, isSelected, isClickable, useLocalizedEntityStatus, renderDragHandle, getEntityScheduledActions, onEdit, getAssetUrl, onRemove, }: WrappedAssetCardProps): React.JSX.Element;
|
|
26
26
|
defaultProps: {
|
|
27
27
|
isClickable: boolean;
|
|
28
28
|
};
|
|
@@ -29,7 +29,7 @@ export interface WrappedEntryCardProps {
|
|
|
29
29
|
localesStatusMap?: LocalePublishStatusMap;
|
|
30
30
|
activeLocales?: Pick<LocaleProps, 'code'>[];
|
|
31
31
|
}
|
|
32
|
-
export declare function WrappedEntryCard(
|
|
32
|
+
export declare 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, }: WrappedEntryCardProps): React.JSX.Element;
|
|
33
33
|
export declare namespace WrappedEntryCard {
|
|
34
34
|
var defaultProps: {
|
|
35
35
|
isClickable: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { EditorPermissionsProps } from '../common/useEditorPermissions';
|
|
2
2
|
import { LinkActionsProps } from '../components';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
3
|
+
type ResourceLinkActionProps = Pick<EditorPermissionsProps, 'parameters' | 'sdk'>;
|
|
4
|
+
export declare function useResourceLinkActions({ parameters, sdk, }: ResourceLinkActionProps): LinkActionsProps;
|
|
5
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/field-editor-reference",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.4.1",
|
|
4
4
|
"main": "dist/cjs/index.js",
|
|
5
5
|
"module": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"publishConfig": {
|
|
65
65
|
"registry": "https://npm.pkg.github.com/"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "4ab2348c09189a9b3bdc8c0fb7effaa5bfc8c6f7"
|
|
68
68
|
}
|