@contentful/field-editor-reference 6.17.0 → 6.19.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.
- package/dist/cjs/common/EntityStore.js +14 -7
- package/dist/cjs/components/CreateEntryLinkButton/CreateEntryMenuTrigger.js +36 -16
- package/dist/cjs/components/CreateEntryLinkButton/CreateEntryMenuTrigger.spec.js +3 -3
- package/dist/cjs/resources/Cards/ResourceCard.js +2 -1
- package/dist/cjs/resources/MultipleResourceReferenceEditor.js +1 -0
- package/dist/cjs/resources/MultipleResourceReferenceEditor.spec.js +2 -1
- package/dist/cjs/resources/SingleResourceReferenceEditor.js +1 -0
- package/dist/cjs/resources/SingleResourceReferenceEditor.spec.js +2 -1
- package/dist/cjs/resources/useResourceLinkActions.js +10 -5
- package/dist/esm/common/EntityStore.js +14 -7
- package/dist/esm/components/CreateEntryLinkButton/CreateEntryMenuTrigger.js +36 -16
- package/dist/esm/components/CreateEntryLinkButton/CreateEntryMenuTrigger.spec.js +3 -3
- package/dist/esm/resources/Cards/ResourceCard.js +2 -1
- package/dist/esm/resources/MultipleResourceReferenceEditor.js +1 -0
- package/dist/esm/resources/MultipleResourceReferenceEditor.spec.js +2 -1
- package/dist/esm/resources/SingleResourceReferenceEditor.js +1 -0
- package/dist/esm/resources/SingleResourceReferenceEditor.spec.js +2 -1
- package/dist/esm/resources/useResourceLinkActions.js +10 -5
- package/dist/types/common/EntityStore.d.ts +2 -2
- package/dist/types/components/CreateEntryLinkButton/CreateEntryMenuTrigger.d.ts +4 -11
- package/dist/types/resources/Cards/ResourceCard.d.ts +1 -0
- package/package.json +3 -3
|
@@ -214,7 +214,7 @@ async function fetchContentfulEntry({ urn, fetch, options }) {
|
|
|
214
214
|
contentType: contentType
|
|
215
215
|
};
|
|
216
216
|
}
|
|
217
|
-
async function fetchExternalResource({ urn, fetch, options, spaceId, environmentId, resourceType, locale }) {
|
|
217
|
+
async function fetchExternalResource({ urn, fetch, options, spaceId, environmentId, resourceType, locale, referencingEntryId }) {
|
|
218
218
|
let resourceFetchError;
|
|
219
219
|
const [resource, resourceTypes] = await Promise.all([
|
|
220
220
|
fetch([
|
|
@@ -223,14 +223,16 @@ async function fetchExternalResource({ urn, fetch, options, spaceId, environment
|
|
|
223
223
|
environmentId,
|
|
224
224
|
resourceType,
|
|
225
225
|
urn,
|
|
226
|
-
locale
|
|
226
|
+
locale,
|
|
227
|
+
referencingEntryId
|
|
227
228
|
], ({ cmaClient })=>cmaClient.resource.getMany({
|
|
228
229
|
spaceId,
|
|
229
230
|
environmentId,
|
|
230
231
|
resourceTypeId: resourceType,
|
|
231
232
|
query: {
|
|
232
233
|
'sys.urn[in]': urn,
|
|
233
|
-
locale
|
|
234
|
+
locale,
|
|
235
|
+
referencingEntryId
|
|
234
236
|
}
|
|
235
237
|
}).then(({ items })=>{
|
|
236
238
|
return items[0] ?? null;
|
|
@@ -426,7 +428,8 @@ const [InternalServiceProvider, useFetch, useEntityLoader, useCurrentIds] = (0,
|
|
|
426
428
|
'Resource',
|
|
427
429
|
resourceType,
|
|
428
430
|
urn,
|
|
429
|
-
options?.locale
|
|
431
|
+
options?.locale,
|
|
432
|
+
options?.referencingEntryId
|
|
430
433
|
];
|
|
431
434
|
return fetch(queryKey, ()=>{
|
|
432
435
|
if (resourceType === 'Contentful:Entry') {
|
|
@@ -443,6 +446,7 @@ const [InternalServiceProvider, useFetch, useEntityLoader, useCurrentIds] = (0,
|
|
|
443
446
|
fetch,
|
|
444
447
|
urn,
|
|
445
448
|
locale: options?.locale,
|
|
449
|
+
referencingEntryId: options?.referencingEntryId,
|
|
446
450
|
options,
|
|
447
451
|
resourceType,
|
|
448
452
|
spaceId: currentSpaceId,
|
|
@@ -581,20 +585,23 @@ function useEntity(entityType, entityId, options) {
|
|
|
581
585
|
currentEntity
|
|
582
586
|
};
|
|
583
587
|
}
|
|
584
|
-
function useResource(resourceType, urn, { locale, ...options } = {}) {
|
|
588
|
+
function useResource(resourceType, urn, { locale, referencingEntryId, ...options } = {}) {
|
|
585
589
|
if (resourceType.startsWith('Contentful:')) {
|
|
586
590
|
locale = undefined;
|
|
591
|
+
referencingEntryId = undefined;
|
|
587
592
|
}
|
|
588
593
|
const queryKey = [
|
|
589
594
|
'Resource',
|
|
590
595
|
resourceType,
|
|
591
596
|
urn,
|
|
592
|
-
locale
|
|
597
|
+
locale,
|
|
598
|
+
referencingEntryId
|
|
593
599
|
];
|
|
594
600
|
const { getResource } = useEntityLoader();
|
|
595
601
|
const { status, data, error } = (0, _queryClient.useQuery)(queryKey, ()=>getResource(resourceType, urn, {
|
|
596
602
|
...options,
|
|
597
|
-
locale
|
|
603
|
+
locale,
|
|
604
|
+
referencingEntryId
|
|
598
605
|
}), {
|
|
599
606
|
enabled: options?.enabled
|
|
600
607
|
});
|
|
@@ -12,6 +12,7 @@ const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
|
|
|
12
12
|
const _f36components = require("@contentful/f36-components");
|
|
13
13
|
const _f36icons = require("@contentful/f36-icons");
|
|
14
14
|
const _f36tokens = /*#__PURE__*/ _interop_require_default(require("@contentful/f36-tokens"));
|
|
15
|
+
const _core = require("@lingui/core");
|
|
15
16
|
const _emotion = require("emotion");
|
|
16
17
|
const _get = /*#__PURE__*/ _interop_require_default(require("lodash/get"));
|
|
17
18
|
function _interop_require_default(obj) {
|
|
@@ -100,7 +101,10 @@ const styles = {
|
|
|
100
101
|
borderColor: _f36tokens.default.gray200
|
|
101
102
|
})
|
|
102
103
|
};
|
|
103
|
-
const CreateEntryMenuTrigger = ({ contentTypes, suggestedContentTypeId, contentTypesLabel
|
|
104
|
+
const CreateEntryMenuTrigger = ({ contentTypes, suggestedContentTypeId, contentTypesLabel = _core.i18n._({
|
|
105
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.AllContentTypesLabel",
|
|
106
|
+
message: "All Content Types"
|
|
107
|
+
}), title, onSelect, testId = 'create-entry-button-menu-trigger', dropdownSettings = {
|
|
104
108
|
position: 'bottom-left'
|
|
105
109
|
}, customDropdownItems, children, menuProps, filterExperienceTypes = true })=>{
|
|
106
110
|
const [isOpen, setOpen] = (0, _react.useState)(false);
|
|
@@ -161,12 +165,21 @@ const CreateEntryMenuTrigger = ({ contentTypes, suggestedContentTypeId, contentT
|
|
|
161
165
|
isOpen
|
|
162
166
|
]);
|
|
163
167
|
const renderSearchResultsCount = (resultsLength)=>resultsLength ? /*#__PURE__*/ _react.default.createElement(_f36components.Menu.SectionTitle, {
|
|
164
|
-
testId: "add-
|
|
165
|
-
},
|
|
168
|
+
testId: "add-entry-menu-search-results"
|
|
169
|
+
}, _core.i18n._({
|
|
170
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.SearchContentTypeResultsLabel",
|
|
171
|
+
message: "{resultsLength, plural, one {# result} other {# results}}",
|
|
172
|
+
values: {
|
|
173
|
+
resultsLength: resultsLength
|
|
174
|
+
}
|
|
175
|
+
})) : null;
|
|
166
176
|
const isSearchable = filteredContentTypes.length > MAX_ITEMS_WITHOUT_SEARCH;
|
|
167
177
|
const maxDropdownHeight = suggestedContentTypeId ? 300 : 250;
|
|
168
178
|
const suggestedContentType = filteredContentTypes.find((ct)=>ct.sys.id === suggestedContentTypeId);
|
|
169
|
-
const searchFilteredContentTypes = filteredContentTypes.filter((ct)=>!searchInput || (0, _get.default)(ct, 'name',
|
|
179
|
+
const searchFilteredContentTypes = filteredContentTypes.filter((ct)=>!searchInput || (0, _get.default)(ct, 'name', _core.i18n._({
|
|
180
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.SearchContentTypeFallbackLabel",
|
|
181
|
+
message: "Untitled"
|
|
182
|
+
})).toLowerCase().includes(searchInput.toLowerCase()));
|
|
170
183
|
return /*#__PURE__*/ _react.default.createElement("span", {
|
|
171
184
|
className: styles.wrapper,
|
|
172
185
|
ref: wrapper,
|
|
@@ -184,38 +197,45 @@ const CreateEntryMenuTrigger = ({ contentTypes, suggestedContentTypeId, contentT
|
|
|
184
197
|
})), isOpen && /*#__PURE__*/ _react.default.createElement(_f36components.Menu.List, {
|
|
185
198
|
className: styles.dropdownList,
|
|
186
199
|
style: {
|
|
187
|
-
width: dropdownWidth
|
|
200
|
+
width: dropdownWidth !== undefined ? `${dropdownWidth}px` : undefined,
|
|
188
201
|
maxHeight: `${maxDropdownHeight}px`
|
|
189
202
|
},
|
|
190
203
|
ref: menuListRef,
|
|
191
204
|
testId: "add-entry-menu"
|
|
192
205
|
}, Boolean(customDropdownItems) && /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, customDropdownItems, /*#__PURE__*/ _react.default.createElement(_f36components.Menu.Divider, null)), title && /*#__PURE__*/ _react.default.createElement(_f36components.Menu.SectionTitle, {
|
|
193
206
|
className: styles.title
|
|
194
|
-
}, title), isSearchable && /*#__PURE__*/ _react.default.createElement(
|
|
207
|
+
}, title), isSearchable && /*#__PURE__*/ _react.default.createElement("div", {
|
|
195
208
|
ref: textField,
|
|
196
209
|
className: styles.inputWrapper
|
|
197
210
|
}, /*#__PURE__*/ _react.default.createElement(_f36components.TextInput, {
|
|
198
211
|
className: styles.searchInput,
|
|
199
|
-
placeholder:
|
|
212
|
+
placeholder: _core.i18n._({
|
|
213
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.SearchContentTypePlaceholder",
|
|
214
|
+
message: "Search content type"
|
|
215
|
+
}),
|
|
200
216
|
testId: "add-entry-menu-search",
|
|
201
217
|
value: searchInput,
|
|
202
218
|
onChange: (e)=>setSearchInput(e.target.value)
|
|
203
219
|
}), /*#__PURE__*/ _react.default.createElement(_f36icons.MagnifyingGlassIcon, {
|
|
204
220
|
size: "small",
|
|
205
221
|
className: styles.searchIcon
|
|
206
|
-
}))
|
|
222
|
+
})), searchInput && renderSearchResultsCount(searchFilteredContentTypes.length), suggestedContentType && !searchInput && /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/ _react.default.createElement(_f36components.Menu.SectionTitle, null, _core.i18n._({
|
|
223
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.SuggestedContentTypeLabel",
|
|
224
|
+
message: "Suggested Content Type"
|
|
225
|
+
})), /*#__PURE__*/ _react.default.createElement(_f36components.Menu.Item, {
|
|
207
226
|
testId: "suggested",
|
|
208
227
|
onClick: ()=>handleSelect(suggestedContentType)
|
|
209
228
|
}, (0, _get.default)(suggestedContentType, 'name')), /*#__PURE__*/ _react.default.createElement(_f36components.Menu.Divider, null)), !searchInput && /*#__PURE__*/ _react.default.createElement(_f36components.Menu.SectionTitle, null, contentTypesLabel), searchFilteredContentTypes.length ? searchFilteredContentTypes.map((contentType, i)=>/*#__PURE__*/ _react.default.createElement(_f36components.Menu.Item, {
|
|
210
229
|
testId: "contentType",
|
|
211
230
|
key: `${(0, _get.default)(contentType, 'name')}-${i}`,
|
|
212
231
|
onClick: ()=>handleSelect(contentType)
|
|
213
|
-
}, (0, _get.default)(contentType, 'name',
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
232
|
+
}, (0, _get.default)(contentType, 'name', _core.i18n._({
|
|
233
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.ContentTypeFallbackLabel",
|
|
234
|
+
message: "Untitled"
|
|
235
|
+
})))) : /*#__PURE__*/ _react.default.createElement(_f36components.Menu.Item, {
|
|
236
|
+
testId: "add-entry-menu-search-results"
|
|
237
|
+
}, _core.i18n._({
|
|
238
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.NoResultsLabel",
|
|
239
|
+
message: "No results found"
|
|
240
|
+
})))));
|
|
221
241
|
};
|
|
@@ -179,7 +179,7 @@ describe('CreateEntryMenuTrigger general', ()=>{
|
|
|
179
179
|
preventDefault: _noop.default
|
|
180
180
|
});
|
|
181
181
|
expect(getAllByTestId('contentType')).toHaveLength(10);
|
|
182
|
-
expect(getByTestId('add-
|
|
182
|
+
expect(getByTestId('add-entry-menu-search-results').textContent).toBe('10 results');
|
|
183
183
|
_react1.fireEvent.change(input, {
|
|
184
184
|
target: {
|
|
185
185
|
value: '3'
|
|
@@ -187,14 +187,14 @@ describe('CreateEntryMenuTrigger general', ()=>{
|
|
|
187
187
|
preventDefault: _noop.default
|
|
188
188
|
});
|
|
189
189
|
expect(getAllByTestId('contentType')).toHaveLength(1);
|
|
190
|
-
expect(getByTestId('add-
|
|
190
|
+
expect(getByTestId('add-entry-menu-search-results').textContent).toBe('1 result');
|
|
191
191
|
_react1.fireEvent.change(input, {
|
|
192
192
|
target: {
|
|
193
193
|
value: '4'
|
|
194
194
|
},
|
|
195
195
|
preventDefault: _noop.default
|
|
196
196
|
});
|
|
197
|
-
expect(getByTestId('add-
|
|
197
|
+
expect(getByTestId('add-entry-menu-search-results').textContent).toBe('No results found');
|
|
198
198
|
});
|
|
199
199
|
it('shows suggestedContentType in the list', ()=>{
|
|
200
200
|
const { getByTestId } = (0, _react1.render)(/*#__PURE__*/ _react.createElement(_CreateEntryMenuTrigger.CreateEntryMenuTrigger, {
|
|
@@ -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
|
|
@@ -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
|
|
41
|
-
locale: field.locale
|
|
42
|
+
allowedResources,
|
|
43
|
+
locale: field.locale,
|
|
44
|
+
referencingEntryId
|
|
42
45
|
}) : async ()=>[
|
|
43
46
|
await dialogs.selectSingleResourceEntity({
|
|
44
|
-
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
|
-
|
|
57
|
+
allowedResources,
|
|
54
58
|
field.locale,
|
|
59
|
+
referencingEntryId,
|
|
55
60
|
multiple,
|
|
56
61
|
onLinkedExisting
|
|
57
62
|
]);
|
|
@@ -126,7 +126,7 @@ async function fetchContentfulEntry({ urn, fetch, options }) {
|
|
|
126
126
|
contentType: contentType
|
|
127
127
|
};
|
|
128
128
|
}
|
|
129
|
-
async function fetchExternalResource({ urn, fetch, options, spaceId, environmentId, resourceType, locale }) {
|
|
129
|
+
async function fetchExternalResource({ urn, fetch, options, spaceId, environmentId, resourceType, locale, referencingEntryId }) {
|
|
130
130
|
let resourceFetchError;
|
|
131
131
|
const [resource, resourceTypes] = await Promise.all([
|
|
132
132
|
fetch([
|
|
@@ -135,14 +135,16 @@ async function fetchExternalResource({ urn, fetch, options, spaceId, environment
|
|
|
135
135
|
environmentId,
|
|
136
136
|
resourceType,
|
|
137
137
|
urn,
|
|
138
|
-
locale
|
|
138
|
+
locale,
|
|
139
|
+
referencingEntryId
|
|
139
140
|
], ({ cmaClient })=>cmaClient.resource.getMany({
|
|
140
141
|
spaceId,
|
|
141
142
|
environmentId,
|
|
142
143
|
resourceTypeId: resourceType,
|
|
143
144
|
query: {
|
|
144
145
|
'sys.urn[in]': urn,
|
|
145
|
-
locale
|
|
146
|
+
locale,
|
|
147
|
+
referencingEntryId
|
|
146
148
|
}
|
|
147
149
|
}).then(({ items })=>{
|
|
148
150
|
return items[0] ?? null;
|
|
@@ -338,7 +340,8 @@ const [InternalServiceProvider, useFetch, useEntityLoader, useCurrentIds] = cons
|
|
|
338
340
|
'Resource',
|
|
339
341
|
resourceType,
|
|
340
342
|
urn,
|
|
341
|
-
options?.locale
|
|
343
|
+
options?.locale,
|
|
344
|
+
options?.referencingEntryId
|
|
342
345
|
];
|
|
343
346
|
return fetch(queryKey, ()=>{
|
|
344
347
|
if (resourceType === 'Contentful:Entry') {
|
|
@@ -355,6 +358,7 @@ const [InternalServiceProvider, useFetch, useEntityLoader, useCurrentIds] = cons
|
|
|
355
358
|
fetch,
|
|
356
359
|
urn,
|
|
357
360
|
locale: options?.locale,
|
|
361
|
+
referencingEntryId: options?.referencingEntryId,
|
|
358
362
|
options,
|
|
359
363
|
resourceType,
|
|
360
364
|
spaceId: currentSpaceId,
|
|
@@ -493,20 +497,23 @@ export function useEntity(entityType, entityId, options) {
|
|
|
493
497
|
currentEntity
|
|
494
498
|
};
|
|
495
499
|
}
|
|
496
|
-
export function useResource(resourceType, urn, { locale, ...options } = {}) {
|
|
500
|
+
export function useResource(resourceType, urn, { locale, referencingEntryId, ...options } = {}) {
|
|
497
501
|
if (resourceType.startsWith('Contentful:')) {
|
|
498
502
|
locale = undefined;
|
|
503
|
+
referencingEntryId = undefined;
|
|
499
504
|
}
|
|
500
505
|
const queryKey = [
|
|
501
506
|
'Resource',
|
|
502
507
|
resourceType,
|
|
503
508
|
urn,
|
|
504
|
-
locale
|
|
509
|
+
locale,
|
|
510
|
+
referencingEntryId
|
|
505
511
|
];
|
|
506
512
|
const { getResource } = useEntityLoader();
|
|
507
513
|
const { status, data, error } = useQuery(queryKey, ()=>getResource(resourceType, urn, {
|
|
508
514
|
...options,
|
|
509
|
-
locale
|
|
515
|
+
locale,
|
|
516
|
+
referencingEntryId
|
|
510
517
|
}), {
|
|
511
518
|
enabled: options?.enabled
|
|
512
519
|
});
|
|
@@ -2,6 +2,7 @@ import React, { useState, useRef, useEffect, useMemo } from 'react';
|
|
|
2
2
|
import { TextInput, Menu } from '@contentful/f36-components';
|
|
3
3
|
import { MagnifyingGlassIcon } from '@contentful/f36-icons';
|
|
4
4
|
import tokens from '@contentful/f36-tokens';
|
|
5
|
+
import { i18n as $_i18n } from "@lingui/core";
|
|
5
6
|
import { css } from 'emotion';
|
|
6
7
|
import get from 'lodash/get';
|
|
7
8
|
const MAX_ITEMS_WITHOUT_SEARCH = 5;
|
|
@@ -44,7 +45,10 @@ const styles = {
|
|
|
44
45
|
borderColor: tokens.gray200
|
|
45
46
|
})
|
|
46
47
|
};
|
|
47
|
-
export const CreateEntryMenuTrigger = ({ contentTypes, suggestedContentTypeId, contentTypesLabel
|
|
48
|
+
export const CreateEntryMenuTrigger = ({ contentTypes, suggestedContentTypeId, contentTypesLabel = $_i18n._({
|
|
49
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.AllContentTypesLabel",
|
|
50
|
+
message: "All Content Types"
|
|
51
|
+
}), title, onSelect, testId = 'create-entry-button-menu-trigger', dropdownSettings = {
|
|
48
52
|
position: 'bottom-left'
|
|
49
53
|
}, customDropdownItems, children, menuProps, filterExperienceTypes = true })=>{
|
|
50
54
|
const [isOpen, setOpen] = useState(false);
|
|
@@ -105,12 +109,21 @@ export const CreateEntryMenuTrigger = ({ contentTypes, suggestedContentTypeId, c
|
|
|
105
109
|
isOpen
|
|
106
110
|
]);
|
|
107
111
|
const renderSearchResultsCount = (resultsLength)=>resultsLength ? /*#__PURE__*/ React.createElement(Menu.SectionTitle, {
|
|
108
|
-
testId: "add-
|
|
109
|
-
},
|
|
112
|
+
testId: "add-entry-menu-search-results"
|
|
113
|
+
}, $_i18n._({
|
|
114
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.SearchContentTypeResultsLabel",
|
|
115
|
+
message: "{resultsLength, plural, one {# result} other {# results}}",
|
|
116
|
+
values: {
|
|
117
|
+
resultsLength: resultsLength
|
|
118
|
+
}
|
|
119
|
+
})) : null;
|
|
110
120
|
const isSearchable = filteredContentTypes.length > MAX_ITEMS_WITHOUT_SEARCH;
|
|
111
121
|
const maxDropdownHeight = suggestedContentTypeId ? 300 : 250;
|
|
112
122
|
const suggestedContentType = filteredContentTypes.find((ct)=>ct.sys.id === suggestedContentTypeId);
|
|
113
|
-
const searchFilteredContentTypes = filteredContentTypes.filter((ct)=>!searchInput || get(ct, 'name',
|
|
123
|
+
const searchFilteredContentTypes = filteredContentTypes.filter((ct)=>!searchInput || get(ct, 'name', $_i18n._({
|
|
124
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.SearchContentTypeFallbackLabel",
|
|
125
|
+
message: "Untitled"
|
|
126
|
+
})).toLowerCase().includes(searchInput.toLowerCase()));
|
|
114
127
|
return /*#__PURE__*/ React.createElement("span", {
|
|
115
128
|
className: styles.wrapper,
|
|
116
129
|
ref: wrapper,
|
|
@@ -128,38 +141,45 @@ export const CreateEntryMenuTrigger = ({ contentTypes, suggestedContentTypeId, c
|
|
|
128
141
|
})), isOpen && /*#__PURE__*/ React.createElement(Menu.List, {
|
|
129
142
|
className: styles.dropdownList,
|
|
130
143
|
style: {
|
|
131
|
-
width: dropdownWidth
|
|
144
|
+
width: dropdownWidth !== undefined ? `${dropdownWidth}px` : undefined,
|
|
132
145
|
maxHeight: `${maxDropdownHeight}px`
|
|
133
146
|
},
|
|
134
147
|
ref: menuListRef,
|
|
135
148
|
testId: "add-entry-menu"
|
|
136
149
|
}, Boolean(customDropdownItems) && /*#__PURE__*/ React.createElement(React.Fragment, null, customDropdownItems, /*#__PURE__*/ React.createElement(Menu.Divider, null)), title && /*#__PURE__*/ React.createElement(Menu.SectionTitle, {
|
|
137
150
|
className: styles.title
|
|
138
|
-
}, title), isSearchable && /*#__PURE__*/ React.createElement(
|
|
151
|
+
}, title), isSearchable && /*#__PURE__*/ React.createElement("div", {
|
|
139
152
|
ref: textField,
|
|
140
153
|
className: styles.inputWrapper
|
|
141
154
|
}, /*#__PURE__*/ React.createElement(TextInput, {
|
|
142
155
|
className: styles.searchInput,
|
|
143
|
-
placeholder:
|
|
156
|
+
placeholder: $_i18n._({
|
|
157
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.SearchContentTypePlaceholder",
|
|
158
|
+
message: "Search content type"
|
|
159
|
+
}),
|
|
144
160
|
testId: "add-entry-menu-search",
|
|
145
161
|
value: searchInput,
|
|
146
162
|
onChange: (e)=>setSearchInput(e.target.value)
|
|
147
163
|
}), /*#__PURE__*/ React.createElement(MagnifyingGlassIcon, {
|
|
148
164
|
size: "small",
|
|
149
165
|
className: styles.searchIcon
|
|
150
|
-
}))
|
|
166
|
+
})), searchInput && renderSearchResultsCount(searchFilteredContentTypes.length), suggestedContentType && !searchInput && /*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(Menu.SectionTitle, null, $_i18n._({
|
|
167
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.SuggestedContentTypeLabel",
|
|
168
|
+
message: "Suggested Content Type"
|
|
169
|
+
})), /*#__PURE__*/ React.createElement(Menu.Item, {
|
|
151
170
|
testId: "suggested",
|
|
152
171
|
onClick: ()=>handleSelect(suggestedContentType)
|
|
153
172
|
}, get(suggestedContentType, 'name')), /*#__PURE__*/ React.createElement(Menu.Divider, null)), !searchInput && /*#__PURE__*/ React.createElement(Menu.SectionTitle, null, contentTypesLabel), searchFilteredContentTypes.length ? searchFilteredContentTypes.map((contentType, i)=>/*#__PURE__*/ React.createElement(Menu.Item, {
|
|
154
173
|
testId: "contentType",
|
|
155
174
|
key: `${get(contentType, 'name')}-${i}`,
|
|
156
175
|
onClick: ()=>handleSelect(contentType)
|
|
157
|
-
}, get(contentType, 'name',
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
176
|
+
}, get(contentType, 'name', $_i18n._({
|
|
177
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.ContentTypeFallbackLabel",
|
|
178
|
+
message: "Untitled"
|
|
179
|
+
})))) : /*#__PURE__*/ React.createElement(Menu.Item, {
|
|
180
|
+
testId: "add-entry-menu-search-results"
|
|
181
|
+
}, $_i18n._({
|
|
182
|
+
id: "FieldEditors.Reference.CreateEntryMenuTrigger.NoResultsLabel",
|
|
183
|
+
message: "No results found"
|
|
184
|
+
})))));
|
|
165
185
|
};
|
|
@@ -129,7 +129,7 @@ describe('CreateEntryMenuTrigger general', ()=>{
|
|
|
129
129
|
preventDefault: noop
|
|
130
130
|
});
|
|
131
131
|
expect(getAllByTestId('contentType')).toHaveLength(10);
|
|
132
|
-
expect(getByTestId('add-
|
|
132
|
+
expect(getByTestId('add-entry-menu-search-results').textContent).toBe('10 results');
|
|
133
133
|
fireEvent.change(input, {
|
|
134
134
|
target: {
|
|
135
135
|
value: '3'
|
|
@@ -137,14 +137,14 @@ describe('CreateEntryMenuTrigger general', ()=>{
|
|
|
137
137
|
preventDefault: noop
|
|
138
138
|
});
|
|
139
139
|
expect(getAllByTestId('contentType')).toHaveLength(1);
|
|
140
|
-
expect(getByTestId('add-
|
|
140
|
+
expect(getByTestId('add-entry-menu-search-results').textContent).toBe('1 result');
|
|
141
141
|
fireEvent.change(input, {
|
|
142
142
|
target: {
|
|
143
143
|
value: '4'
|
|
144
144
|
},
|
|
145
145
|
preventDefault: noop
|
|
146
146
|
});
|
|
147
|
-
expect(getByTestId('add-
|
|
147
|
+
expect(getByTestId('add-entry-menu-search-results').textContent).toBe('No results found');
|
|
148
148
|
});
|
|
149
149
|
it('shows suggestedContentType in the list', ()=>{
|
|
150
150
|
const { getByTestId } = render(/*#__PURE__*/ React.createElement(CreateEntryMenuTrigger, {
|
|
@@ -12,9 +12,10 @@ function ResourceCardSkeleton() {
|
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
14
|
function ExistingResourceCard(props) {
|
|
15
|
-
const { resourceLink, inView, index = 0, locale } = props;
|
|
15
|
+
const { resourceLink, inView, index = 0, locale, referencingEntryId } = props;
|
|
16
16
|
const resourceOptions = {
|
|
17
17
|
locale,
|
|
18
|
+
referencingEntryId,
|
|
18
19
|
priority: index * -1,
|
|
19
20
|
enabled: inView,
|
|
20
21
|
allowExternal: true
|
|
@@ -73,7 +73,8 @@ describe('Multiple resource editor', ()=>{
|
|
|
73
73
|
const options = dialogFn.mock.calls[0][0];
|
|
74
74
|
expect(options).toEqual({
|
|
75
75
|
allowedResources: fieldDefinition.allowedResources,
|
|
76
|
-
locale: 'en'
|
|
76
|
+
locale: 'en',
|
|
77
|
+
referencingEntryId: 'testEntry'
|
|
77
78
|
});
|
|
78
79
|
});
|
|
79
80
|
it('hides the action button when insufficient permissions', async ()=>{
|
|
@@ -20,6 +20,7 @@ export function SingleResourceReferenceEditor(props) {
|
|
|
20
20
|
onRemove: ()=>props.sdk.field.removeValue(),
|
|
21
21
|
resourceLink: value,
|
|
22
22
|
locale: props.sdk.field.locale,
|
|
23
|
+
referencingEntryId: props.sdk.ids.entry,
|
|
23
24
|
isDisabled: disabled,
|
|
24
25
|
getEntryRouteHref: props.getEntryRouteHref
|
|
25
26
|
}) : /*#__PURE__*/ React.createElement(CombinedLinkEntityActions, {
|
|
@@ -69,7 +69,8 @@ describe('Single resource editor', ()=>{
|
|
|
69
69
|
const options = dialogFn.mock.calls[0][0];
|
|
70
70
|
expect(options).toEqual({
|
|
71
71
|
allowedResources: fieldDefinition.allowedResources,
|
|
72
|
-
locale: 'en'
|
|
72
|
+
locale: 'en',
|
|
73
|
+
referencingEntryId: 'testEntry'
|
|
73
74
|
});
|
|
74
75
|
});
|
|
75
76
|
it('renders no the action button when permissions insufficient', async ()=>{
|
|
@@ -25,14 +25,18 @@ export function useResourceLinkActions({ parameters, sdk }) {
|
|
|
25
25
|
field
|
|
26
26
|
]);
|
|
27
27
|
const multiple = field.type === 'Array';
|
|
28
|
+
const referencingEntryId = sdk.ids.entry;
|
|
29
|
+
const allowedResources = field.allowedResources;
|
|
28
30
|
const onLinkExisting = useMemo(()=>{
|
|
29
31
|
const promptSelection = multiple ? async ()=>await dialogs.selectMultipleResourceEntities({
|
|
30
|
-
allowedResources
|
|
31
|
-
locale: field.locale
|
|
32
|
+
allowedResources,
|
|
33
|
+
locale: field.locale,
|
|
34
|
+
referencingEntryId
|
|
32
35
|
}) : async ()=>[
|
|
33
36
|
await dialogs.selectSingleResourceEntity({
|
|
34
|
-
allowedResources
|
|
35
|
-
locale: field.locale
|
|
37
|
+
allowedResources,
|
|
38
|
+
locale: field.locale,
|
|
39
|
+
referencingEntryId
|
|
36
40
|
})
|
|
37
41
|
];
|
|
38
42
|
return async ()=>{
|
|
@@ -40,8 +44,9 @@ export function useResourceLinkActions({ parameters, sdk }) {
|
|
|
40
44
|
};
|
|
41
45
|
}, [
|
|
42
46
|
dialogs,
|
|
43
|
-
|
|
47
|
+
allowedResources,
|
|
44
48
|
field.locale,
|
|
49
|
+
referencingEntryId,
|
|
45
50
|
multiple,
|
|
46
51
|
onLinkedExisting
|
|
47
52
|
]);
|
|
@@ -33,11 +33,11 @@ type QueryEntityResult<E> = Promise<E>;
|
|
|
33
33
|
type GetResourceOptions = GetOptions & {
|
|
34
34
|
allowExternal?: boolean;
|
|
35
35
|
locale?: string;
|
|
36
|
+
referencingEntryId?: string;
|
|
36
37
|
};
|
|
37
38
|
type QueryResourceResult<R extends Resource = Resource> = QueryEntityResult<ResourceInfo<R>>;
|
|
38
39
|
type UseResourceOptions = GetResourceOptions & {
|
|
39
40
|
enabled?: boolean;
|
|
40
|
-
locale?: string;
|
|
41
41
|
};
|
|
42
42
|
type UseEntityResult<E> = {
|
|
43
43
|
status: 'idle';
|
|
@@ -87,7 +87,7 @@ declare const useEntityLoader: () => {
|
|
|
87
87
|
getResourceProvider: (organizationId: string, appDefinitionId: string) => QueryEntityResult<ResourceProvider>;
|
|
88
88
|
};
|
|
89
89
|
export declare function useEntity<E extends FetchableEntity>(entityType: FetchableEntityType, entityId: string, options?: Omit<UseEntityOptions, 'releaseId'>): UseEntityResult<E>;
|
|
90
|
-
export declare function useResource<R extends Resource = Resource>(resourceType: string, urn: string, { locale, ...options }?: UseResourceOptions): {
|
|
90
|
+
export declare function useResource<R extends Resource = Resource>(resourceType: string, urn: string, { locale, referencingEntryId, ...options }?: UseResourceOptions): {
|
|
91
91
|
status: "error" | "success" | "loading";
|
|
92
92
|
data: ResourceInfo<R> | undefined;
|
|
93
93
|
error: unknown;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { MenuProps } from '@contentful/f36-components';
|
|
3
|
-
import { ContentType } from '../../types';
|
|
2
|
+
import { type MenuProps } from '@contentful/f36-components';
|
|
3
|
+
import type { ContentType } from '../../types';
|
|
4
4
|
type CreateEntryMenuTriggerChildProps = {
|
|
5
5
|
isOpen: boolean;
|
|
6
6
|
isSelecting: boolean;
|
|
7
7
|
};
|
|
8
8
|
export type CreateEntryMenuTriggerChild = (props: CreateEntryMenuTriggerChildProps) => React.ReactElement;
|
|
9
9
|
export type CreateCustomEntryMenuItems = ({ closeMenu, }: {
|
|
10
|
-
closeMenu:
|
|
10
|
+
closeMenu: () => void;
|
|
11
11
|
}) => React.ReactElement;
|
|
12
12
|
interface CreateEntryMenuTrigger {
|
|
13
13
|
contentTypes: ContentType[];
|
|
@@ -25,12 +25,5 @@ interface CreateEntryMenuTrigger {
|
|
|
25
25
|
menuProps?: Omit<MenuProps, 'children'>;
|
|
26
26
|
filterExperienceTypes?: boolean;
|
|
27
27
|
}
|
|
28
|
-
export declare const CreateEntryMenuTrigger: {
|
|
29
|
-
({ contentTypes, suggestedContentTypeId, contentTypesLabel, title, onSelect, testId, dropdownSettings, customDropdownItems, children, menuProps, filterExperienceTypes, }: CreateEntryMenuTrigger): React.JSX.Element;
|
|
30
|
-
defaultProps: {
|
|
31
|
-
testId: string;
|
|
32
|
-
contentTypesLabel: string;
|
|
33
|
-
filterExperienceTypes: boolean;
|
|
34
|
-
};
|
|
35
|
-
};
|
|
28
|
+
export declare const CreateEntryMenuTrigger: ({ contentTypes, suggestedContentTypeId, contentTypesLabel, title, onSelect, testId, dropdownSettings, customDropdownItems, children, menuProps, filterExperienceTypes, }: CreateEntryMenuTrigger) => React.JSX.Element;
|
|
36
29
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/field-editor-reference",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.19.0",
|
|
4
4
|
"main": "dist/cjs/index.js",
|
|
5
5
|
"module": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@dnd-kit/utilities": "^3.2.2",
|
|
47
47
|
"@tanstack/react-query": "^4.3.9",
|
|
48
48
|
"constate": "^3.3.2",
|
|
49
|
-
"contentful-management": "^11.
|
|
49
|
+
"contentful-management": "^11.66.0",
|
|
50
50
|
"emotion": "^10.0.17",
|
|
51
51
|
"lodash": "^4.17.15",
|
|
52
52
|
"moment": "^2.20.0",
|
|
@@ -68,5 +68,5 @@
|
|
|
68
68
|
"publishConfig": {
|
|
69
69
|
"registry": "https://npm.pkg.github.com/"
|
|
70
70
|
},
|
|
71
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "67068016b57ce3057ddddfb8dd3a53ef86499a02"
|
|
72
72
|
}
|