@contentful/field-editor-slug 1.2.0 → 1.3.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/SlugEditor.js +139 -0
- package/dist/cjs/SlugEditor.test.js +508 -0
- package/dist/cjs/SlugEditorField.js +186 -0
- package/dist/cjs/TrackingFieldConnector.js +137 -0
- package/dist/cjs/index.js +24 -0
- package/dist/cjs/services/makeSlug.js +42 -0
- package/dist/cjs/services/makeSlug.test.js +14 -0
- package/dist/cjs/services/slugify.js +64 -0
- package/dist/cjs/services/slugify.test.js +30 -0
- package/dist/cjs/styles.js +68 -0
- package/dist/esm/SlugEditor.js +90 -0
- package/dist/esm/SlugEditor.test.js +460 -0
- package/dist/esm/SlugEditorField.js +129 -0
- package/dist/esm/TrackingFieldConnector.js +88 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/services/makeSlug.js +24 -0
- package/dist/esm/services/makeSlug.test.js +10 -0
- package/dist/esm/services/slugify.js +49 -0
- package/dist/esm/services/slugify.test.js +26 -0
- package/dist/esm/styles.js +33 -0
- package/dist/{SlugEditor.d.ts → types/SlugEditor.d.ts} +24 -24
- package/dist/types/SlugEditor.test.d.ts +1 -0
- package/dist/{SlugEditorField.d.ts → types/SlugEditorField.d.ts} +18 -18
- package/dist/{TrackingFieldConnector.d.ts → types/TrackingFieldConnector.d.ts} +29 -29
- package/dist/{index.d.ts → types/index.d.ts} +3 -3
- package/dist/{services → types/services}/makeSlug.d.ts +8 -8
- package/dist/types/services/makeSlug.test.d.ts +1 -0
- package/dist/{services → types/services}/slugify.d.ts +12 -12
- package/dist/types/services/slugify.test.d.ts +1 -0
- package/dist/{styles.d.ts → types/styles.d.ts} +6 -6
- package/package.json +25 -11
- package/CHANGELOG.md +0 -206
- package/dist/field-editor-slug.cjs.development.js +0 -463
- package/dist/field-editor-slug.cjs.development.js.map +0 -1
- package/dist/field-editor-slug.cjs.production.min.js +0 -2
- package/dist/field-editor-slug.cjs.production.min.js.map +0 -1
- package/dist/field-editor-slug.esm.js +0 -454
- package/dist/field-editor-slug.esm.js.map +0 -1
- package/dist/index.js +0 -8
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "SlugEditor", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return SlugEditor;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _react = _interop_require_wildcard(require("react"));
|
|
12
|
+
const _fieldeditorshared = require("@contentful/field-editor-shared");
|
|
13
|
+
const _SlugEditorField = require("./SlugEditorField");
|
|
14
|
+
const _TrackingFieldConnector = require("./TrackingFieldConnector");
|
|
15
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
16
|
+
if (typeof WeakMap !== "function") return null;
|
|
17
|
+
var cacheBabelInterop = new WeakMap();
|
|
18
|
+
var cacheNodeInterop = new WeakMap();
|
|
19
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
20
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
21
|
+
})(nodeInterop);
|
|
22
|
+
}
|
|
23
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
24
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
25
|
+
return obj;
|
|
26
|
+
}
|
|
27
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
28
|
+
return {
|
|
29
|
+
default: obj
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
33
|
+
if (cache && cache.has(obj)) {
|
|
34
|
+
return cache.get(obj);
|
|
35
|
+
}
|
|
36
|
+
var newObj = {};
|
|
37
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
38
|
+
for(var key in obj){
|
|
39
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
40
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
41
|
+
if (desc && (desc.get || desc.set)) {
|
|
42
|
+
Object.defineProperty(newObj, key, desc);
|
|
43
|
+
} else {
|
|
44
|
+
newObj[key] = obj[key];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
newObj.default = obj;
|
|
49
|
+
if (cache) {
|
|
50
|
+
cache.set(obj, newObj);
|
|
51
|
+
}
|
|
52
|
+
return newObj;
|
|
53
|
+
}
|
|
54
|
+
function isSupportedFieldTypes(val) {
|
|
55
|
+
return val === 'Symbol';
|
|
56
|
+
}
|
|
57
|
+
function FieldConnectorCallback({ Component , value , disabled , setValue , errors , titleValue , isOptionalLocaleWithFallback , locale , createdAt , performUniqueCheck }) {
|
|
58
|
+
const safeSetValue = _react.useCallback(async (...args)=>{
|
|
59
|
+
try {
|
|
60
|
+
await setValue(...args);
|
|
61
|
+
} catch (e) {}
|
|
62
|
+
}, [
|
|
63
|
+
setValue
|
|
64
|
+
]);
|
|
65
|
+
return _react.createElement("div", {
|
|
66
|
+
"data-test-id": "slug-editor"
|
|
67
|
+
}, _react.createElement(Component, {
|
|
68
|
+
locale: locale,
|
|
69
|
+
createdAt: createdAt,
|
|
70
|
+
performUniqueCheck: performUniqueCheck,
|
|
71
|
+
hasError: errors.length > 0,
|
|
72
|
+
value: value,
|
|
73
|
+
isOptionalLocaleWithFallback: isOptionalLocaleWithFallback,
|
|
74
|
+
isDisabled: disabled,
|
|
75
|
+
titleValue: titleValue,
|
|
76
|
+
setValue: safeSetValue
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
function SlugEditor(props) {
|
|
80
|
+
const { field , parameters } = props;
|
|
81
|
+
const { locales , entry , space } = props.baseSdk;
|
|
82
|
+
if (!isSupportedFieldTypes(field.type)) {
|
|
83
|
+
throw new Error(`"${field.type}" field type is not supported by SlugEditor`);
|
|
84
|
+
}
|
|
85
|
+
const trackingFieldId = parameters?.instance?.trackingFieldId ?? undefined;
|
|
86
|
+
const entrySys = entry.getSys();
|
|
87
|
+
const isLocaleOptional = locales.optional[field.locale];
|
|
88
|
+
const localeFallbackCode = locales.fallbacks[field.locale];
|
|
89
|
+
const isOptionalFieldLocale = Boolean(!field.required || isLocaleOptional);
|
|
90
|
+
const isOptionalLocaleWithFallback = Boolean(isOptionalFieldLocale && localeFallbackCode && locales.available.includes(localeFallbackCode));
|
|
91
|
+
const performUniqueCheck = _react.useCallback((value)=>{
|
|
92
|
+
const searchQuery = {
|
|
93
|
+
content_type: entrySys?.contentType?.sys?.id,
|
|
94
|
+
[`fields.${field.id}.${field.locale}`]: value,
|
|
95
|
+
'sys.id[ne]': entrySys.id,
|
|
96
|
+
'sys.publishedAt[exists]': true,
|
|
97
|
+
limit: 0
|
|
98
|
+
};
|
|
99
|
+
return space.getEntries(searchQuery).then((res)=>{
|
|
100
|
+
return res.total === 0;
|
|
101
|
+
});
|
|
102
|
+
}, [
|
|
103
|
+
entrySys?.contentType?.sys?.id,
|
|
104
|
+
field.id,
|
|
105
|
+
field.locale,
|
|
106
|
+
entrySys.id,
|
|
107
|
+
space
|
|
108
|
+
]);
|
|
109
|
+
return _react.createElement(_TrackingFieldConnector.TrackingFieldConnector, {
|
|
110
|
+
sdk: props.baseSdk,
|
|
111
|
+
field: field,
|
|
112
|
+
defaultLocale: locales.default,
|
|
113
|
+
isOptionalLocaleWithFallback: isOptionalLocaleWithFallback,
|
|
114
|
+
trackingFieldId: trackingFieldId
|
|
115
|
+
}, ({ titleValue , isPublished , isSame })=>_react.createElement(_fieldeditorshared.FieldConnector, {
|
|
116
|
+
field: field,
|
|
117
|
+
isInitiallyDisabled: props.isInitiallyDisabled,
|
|
118
|
+
throttle: 0
|
|
119
|
+
}, ({ value , errors , disabled , setValue , externalReset })=>{
|
|
120
|
+
const shouldTrackTitle = isPublished === false && isSame === false;
|
|
121
|
+
const Component = shouldTrackTitle ? _SlugEditorField.SlugEditorField : _SlugEditorField.SlugEditorFieldStatic;
|
|
122
|
+
return _react.createElement(FieldConnectorCallback, {
|
|
123
|
+
Component: Component,
|
|
124
|
+
titleValue: titleValue,
|
|
125
|
+
value: value,
|
|
126
|
+
errors: errors,
|
|
127
|
+
disabled: disabled,
|
|
128
|
+
setValue: setValue,
|
|
129
|
+
isOptionalLocaleWithFallback: isOptionalLocaleWithFallback,
|
|
130
|
+
createdAt: entrySys.createdAt,
|
|
131
|
+
locale: field.locale,
|
|
132
|
+
performUniqueCheck: performUniqueCheck,
|
|
133
|
+
key: `slug-editor-${externalReset}`
|
|
134
|
+
});
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
SlugEditor.defaultProps = {
|
|
138
|
+
isInitiallyDisabled: true
|
|
139
|
+
};
|
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _react = _interop_require_wildcard(require("react"));
|
|
6
|
+
const _fieldeditortestutils = require("@contentful/field-editor-test-utils");
|
|
7
|
+
const _react1 = require("@testing-library/react");
|
|
8
|
+
const _identity = _interop_require_default(require("lodash/identity"));
|
|
9
|
+
require("@testing-library/jest-dom/extend-expect");
|
|
10
|
+
const _SlugEditor = require("./SlugEditor");
|
|
11
|
+
function _interop_require_default(obj) {
|
|
12
|
+
return obj && obj.__esModule ? obj : {
|
|
13
|
+
default: obj
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
17
|
+
if (typeof WeakMap !== "function") return null;
|
|
18
|
+
var cacheBabelInterop = new WeakMap();
|
|
19
|
+
var cacheNodeInterop = new WeakMap();
|
|
20
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
21
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
22
|
+
})(nodeInterop);
|
|
23
|
+
}
|
|
24
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
25
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
26
|
+
return obj;
|
|
27
|
+
}
|
|
28
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
29
|
+
return {
|
|
30
|
+
default: obj
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
34
|
+
if (cache && cache.has(obj)) {
|
|
35
|
+
return cache.get(obj);
|
|
36
|
+
}
|
|
37
|
+
var newObj = {};
|
|
38
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
39
|
+
for(var key in obj){
|
|
40
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
41
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
42
|
+
if (desc && (desc.get || desc.set)) {
|
|
43
|
+
Object.defineProperty(newObj, key, desc);
|
|
44
|
+
} else {
|
|
45
|
+
newObj[key] = obj[key];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
newObj.default = obj;
|
|
50
|
+
if (cache) {
|
|
51
|
+
cache.set(obj, newObj);
|
|
52
|
+
}
|
|
53
|
+
return newObj;
|
|
54
|
+
}
|
|
55
|
+
(0, _react1.configure)({
|
|
56
|
+
testIdAttribute: 'data-test-id'
|
|
57
|
+
});
|
|
58
|
+
jest.mock('lodash/throttle', ()=>({
|
|
59
|
+
default: _identity.default
|
|
60
|
+
}), {
|
|
61
|
+
virtual: true
|
|
62
|
+
});
|
|
63
|
+
jest.mock('use-debounce', ()=>({
|
|
64
|
+
useDebounce: (text)=>[
|
|
65
|
+
text
|
|
66
|
+
]
|
|
67
|
+
}));
|
|
68
|
+
function createMocks(initialValues = {}) {
|
|
69
|
+
const [field] = (0, _fieldeditortestutils.createFakeFieldAPI)((field)=>({
|
|
70
|
+
...field,
|
|
71
|
+
id: 'slug-id',
|
|
72
|
+
onValueChanged: jest.fn().mockImplementation(field.onValueChanged),
|
|
73
|
+
setValue: jest.fn().mockImplementation(field.setValue)
|
|
74
|
+
}), initialValues.field || '');
|
|
75
|
+
const [titleField] = (0, _fieldeditortestutils.createFakeFieldAPI)((field)=>({
|
|
76
|
+
...field,
|
|
77
|
+
id: 'title-id',
|
|
78
|
+
setValue: jest.fn().mockImplementation(field.setValue),
|
|
79
|
+
getValue: jest.fn().mockImplementation(field.getValue),
|
|
80
|
+
onValueChanged: jest.fn().mockImplementation(field.onValueChanged)
|
|
81
|
+
}), initialValues.titleField || '');
|
|
82
|
+
const [descriptionField] = (0, _fieldeditortestutils.createFakeFieldAPI)((field)=>({
|
|
83
|
+
...field,
|
|
84
|
+
id: 'description-id',
|
|
85
|
+
setValue: jest.fn().mockImplementation(field.setValue),
|
|
86
|
+
getValue: jest.fn().mockImplementation(field.getValue),
|
|
87
|
+
onValueChanged: jest.fn().mockImplementation(field.onValueChanged)
|
|
88
|
+
}), initialValues.descriptionField || '');
|
|
89
|
+
const sdk = {
|
|
90
|
+
locales: (0, _fieldeditortestutils.createFakeLocalesAPI)(),
|
|
91
|
+
space: {
|
|
92
|
+
getEntries: jest.fn().mockResolvedValue({
|
|
93
|
+
total: 0
|
|
94
|
+
})
|
|
95
|
+
},
|
|
96
|
+
entry: {
|
|
97
|
+
getSys: jest.fn().mockReturnValue({
|
|
98
|
+
id: 'entry-id',
|
|
99
|
+
publishedVersion: undefined,
|
|
100
|
+
createdAt: '2020-01-24T15:33:47.906Z',
|
|
101
|
+
contentType: {
|
|
102
|
+
sys: {
|
|
103
|
+
id: 'content-type-id'
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}),
|
|
107
|
+
onSysChanged: jest.fn(),
|
|
108
|
+
fields: {
|
|
109
|
+
'title-id': titleField,
|
|
110
|
+
'entry-id': field,
|
|
111
|
+
'description-id': descriptionField
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
contentType: {
|
|
115
|
+
displayField: 'title-id'
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
return {
|
|
119
|
+
field,
|
|
120
|
+
titleField,
|
|
121
|
+
descriptionField,
|
|
122
|
+
sdk
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
describe('SlugEditor', ()=>{
|
|
126
|
+
afterEach(_react1.cleanup);
|
|
127
|
+
describe('should not subscribe to title changes', ()=>{
|
|
128
|
+
it('when entry is published', async ()=>{
|
|
129
|
+
const { field , titleField , sdk } = createMocks();
|
|
130
|
+
sdk.entry.getSys.mockReturnValue({
|
|
131
|
+
publishedVersion: 2
|
|
132
|
+
});
|
|
133
|
+
(0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
134
|
+
field: field,
|
|
135
|
+
baseSdk: sdk,
|
|
136
|
+
isInitiallyDisabled: false
|
|
137
|
+
}));
|
|
138
|
+
await (0, _react1.wait)();
|
|
139
|
+
expect(field.setValue).not.toHaveBeenCalled();
|
|
140
|
+
expect(titleField.onValueChanged).toHaveBeenCalledWith('en-US', expect.any(Function));
|
|
141
|
+
expect(sdk.space.getEntries).not.toHaveBeenCalled();
|
|
142
|
+
expect(sdk.entry.fields['title-id'].getValue).toHaveBeenCalledTimes(1);
|
|
143
|
+
expect(sdk.entry.getSys).toHaveBeenCalledTimes(2);
|
|
144
|
+
});
|
|
145
|
+
it('when title and slug are the same field', async ()=>{
|
|
146
|
+
const { field , titleField , sdk } = createMocks();
|
|
147
|
+
sdk.contentType.displayField = 'entry-id';
|
|
148
|
+
(0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
149
|
+
field: field,
|
|
150
|
+
baseSdk: sdk,
|
|
151
|
+
isInitiallyDisabled: false
|
|
152
|
+
}));
|
|
153
|
+
await (0, _react1.wait)();
|
|
154
|
+
expect(titleField.onValueChanged).not.toHaveBeenCalled();
|
|
155
|
+
expect(field.setValue).not.toHaveBeenCalled();
|
|
156
|
+
});
|
|
157
|
+
it('when a saved slug is different from a title at the render', async ()=>{
|
|
158
|
+
const { field , titleField , sdk } = createMocks({
|
|
159
|
+
titleField: 'Hello world!',
|
|
160
|
+
field: 'something-different'
|
|
161
|
+
});
|
|
162
|
+
(0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
163
|
+
field: field,
|
|
164
|
+
baseSdk: sdk,
|
|
165
|
+
isInitiallyDisabled: false
|
|
166
|
+
}));
|
|
167
|
+
await (0, _react1.wait)();
|
|
168
|
+
expect(titleField.onValueChanged).toHaveBeenCalledWith('en-US', expect.any(Function));
|
|
169
|
+
expect(field.setValue).not.toHaveBeenCalled();
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
describe('should check for uniqueness', ()=>{
|
|
173
|
+
it('if it is published', async ()=>{
|
|
174
|
+
const { field , titleField , sdk } = createMocks({
|
|
175
|
+
titleField: 'Slug value',
|
|
176
|
+
field: 'slug-value'
|
|
177
|
+
});
|
|
178
|
+
sdk.entry.getSys.mockReturnValue({
|
|
179
|
+
id: 'entry-id',
|
|
180
|
+
publishedVersion: 2,
|
|
181
|
+
contentType: {
|
|
182
|
+
sys: {
|
|
183
|
+
id: 'content-type-id'
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
sdk.space.getEntries.mockResolvedValue({
|
|
188
|
+
total: 0
|
|
189
|
+
});
|
|
190
|
+
const { queryByTestId , queryByText } = (0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
191
|
+
field: field,
|
|
192
|
+
baseSdk: sdk,
|
|
193
|
+
isInitiallyDisabled: false
|
|
194
|
+
}));
|
|
195
|
+
await (0, _react1.wait)();
|
|
196
|
+
expect(titleField.onValueChanged).toHaveBeenCalledWith('en-US', expect.any(Function));
|
|
197
|
+
expect(sdk.space.getEntries).toHaveBeenLastCalledWith({
|
|
198
|
+
content_type: 'content-type-id',
|
|
199
|
+
'fields.slug-id.en-US': 'slug-value',
|
|
200
|
+
limit: 0,
|
|
201
|
+
'sys.id[ne]': 'entry-id',
|
|
202
|
+
'sys.publishedAt[exists]': true
|
|
203
|
+
});
|
|
204
|
+
expect(sdk.space.getEntries).toHaveBeenCalledTimes(1);
|
|
205
|
+
expect(queryByTestId('slug-editor-spinner')).not.toBeInTheDocument();
|
|
206
|
+
expect(queryByText('This slug has already been published in another entry')).not.toBeInTheDocument();
|
|
207
|
+
});
|
|
208
|
+
it('if it is not published', async ()=>{
|
|
209
|
+
const { field , titleField , sdk } = createMocks({
|
|
210
|
+
titleField: 'Slug value',
|
|
211
|
+
field: 'slug-value'
|
|
212
|
+
});
|
|
213
|
+
sdk.entry.getSys.mockReturnValue({
|
|
214
|
+
id: 'entry-id',
|
|
215
|
+
publishedVersion: undefined,
|
|
216
|
+
contentType: {
|
|
217
|
+
sys: {
|
|
218
|
+
id: 'content-type-id'
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
sdk.space.getEntries.mockResolvedValue({
|
|
223
|
+
total: 2
|
|
224
|
+
});
|
|
225
|
+
const { queryByTestId , queryByText , getByTestId } = (0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
226
|
+
field: field,
|
|
227
|
+
baseSdk: sdk,
|
|
228
|
+
isInitiallyDisabled: false
|
|
229
|
+
}));
|
|
230
|
+
await (0, _react1.wait)();
|
|
231
|
+
expect(titleField.onValueChanged).toHaveBeenCalledWith('en-US', expect.any(Function));
|
|
232
|
+
expect(sdk.space.getEntries).toHaveBeenLastCalledWith({
|
|
233
|
+
content_type: 'content-type-id',
|
|
234
|
+
'fields.slug-id.en-US': 'slug-value',
|
|
235
|
+
limit: 0,
|
|
236
|
+
'sys.id[ne]': 'entry-id',
|
|
237
|
+
'sys.publishedAt[exists]': true
|
|
238
|
+
});
|
|
239
|
+
expect(sdk.space.getEntries).toHaveBeenCalledTimes(1);
|
|
240
|
+
expect(queryByTestId('slug-editor-spinner')).not.toBeInTheDocument();
|
|
241
|
+
expect(queryByText('This slug has already been published in another entry')).toBeInTheDocument();
|
|
242
|
+
expect(getByTestId('cf-ui-text-input')).toHaveValue('slug-value');
|
|
243
|
+
sdk.space.getEntries.mockResolvedValue({
|
|
244
|
+
total: 0
|
|
245
|
+
});
|
|
246
|
+
_react1.fireEvent.change(getByTestId('cf-ui-text-input'), {
|
|
247
|
+
target: {
|
|
248
|
+
value: '123'
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
await (0, _react1.wait)();
|
|
252
|
+
expect(field.setValue).toHaveBeenCalledTimes(1);
|
|
253
|
+
expect(field.setValue).toHaveBeenCalledWith('123');
|
|
254
|
+
expect(sdk.space.getEntries).toHaveBeenCalledTimes(2);
|
|
255
|
+
expect(sdk.space.getEntries).toHaveBeenLastCalledWith({
|
|
256
|
+
content_type: 'content-type-id',
|
|
257
|
+
'fields.slug-id.en-US': '123',
|
|
258
|
+
limit: 0,
|
|
259
|
+
'sys.id[ne]': 'entry-id',
|
|
260
|
+
'sys.publishedAt[exists]': true
|
|
261
|
+
});
|
|
262
|
+
expect(queryByText('This slug has already been published in another entry')).not.toBeInTheDocument();
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
describe('should react to title changes', ()=>{
|
|
266
|
+
it('when field is disabled', async ()=>{
|
|
267
|
+
const { field , titleField , sdk } = createMocks({
|
|
268
|
+
field: '',
|
|
269
|
+
titleField: ''
|
|
270
|
+
});
|
|
271
|
+
(0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
272
|
+
field: field,
|
|
273
|
+
baseSdk: sdk,
|
|
274
|
+
isInitiallyDisabled: true
|
|
275
|
+
}));
|
|
276
|
+
await (0, _react1.wait)();
|
|
277
|
+
expect(field.setValue).toHaveBeenCalled();
|
|
278
|
+
expect(titleField.onValueChanged).toHaveBeenCalledWith('en-US', expect.any(Function));
|
|
279
|
+
expect(sdk.space.getEntries).toHaveBeenCalled();
|
|
280
|
+
expect(sdk.entry.fields['title-id'].getValue).toHaveBeenCalledTimes(1);
|
|
281
|
+
expect(sdk.entry.getSys).toHaveBeenCalledTimes(2);
|
|
282
|
+
});
|
|
283
|
+
it('should generate unique value with date if title is empty', async ()=>{
|
|
284
|
+
const { field , titleField , sdk } = createMocks({
|
|
285
|
+
field: '',
|
|
286
|
+
titleField: ''
|
|
287
|
+
});
|
|
288
|
+
(0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
289
|
+
field: field,
|
|
290
|
+
baseSdk: sdk,
|
|
291
|
+
isInitiallyDisabled: false
|
|
292
|
+
}));
|
|
293
|
+
await (0, _react1.wait)();
|
|
294
|
+
expect(titleField.onValueChanged).toHaveBeenCalledWith('en-US', expect.any(Function));
|
|
295
|
+
expect(field.setValue).toHaveBeenCalledTimes(1);
|
|
296
|
+
expect(field.setValue).toHaveBeenLastCalledWith('untitled-entry-2020-01-24-at-15-33-47');
|
|
297
|
+
await sdk.entry.fields['title-id'].setValue('Hello world!');
|
|
298
|
+
await (0, _react1.wait)();
|
|
299
|
+
expect(field.setValue).toHaveBeenCalledTimes(2);
|
|
300
|
+
expect(field.setValue).toHaveBeenLastCalledWith('hello-world');
|
|
301
|
+
expect(sdk.space.getEntries).toHaveBeenCalledTimes(2);
|
|
302
|
+
await sdk.entry.fields['title-id'].setValue('фраза написанная по русски');
|
|
303
|
+
await (0, _react1.wait)();
|
|
304
|
+
expect(field.setValue).toHaveBeenCalledTimes(3);
|
|
305
|
+
expect(field.setValue).toHaveBeenLastCalledWith('fraza-napisannaya-po-russki');
|
|
306
|
+
expect(sdk.space.getEntries).toHaveBeenCalledTimes(3);
|
|
307
|
+
});
|
|
308
|
+
it('should generate value from title if it is not empty', async ()=>{
|
|
309
|
+
const { field , titleField , sdk } = createMocks({
|
|
310
|
+
field: '',
|
|
311
|
+
titleField: 'This is initial title value'
|
|
312
|
+
});
|
|
313
|
+
(0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
314
|
+
field: field,
|
|
315
|
+
baseSdk: sdk,
|
|
316
|
+
isInitiallyDisabled: false
|
|
317
|
+
}));
|
|
318
|
+
await (0, _react1.wait)();
|
|
319
|
+
expect(titleField.onValueChanged).toHaveBeenCalledWith('en-US', expect.any(Function));
|
|
320
|
+
expect(field.setValue).toHaveBeenCalledTimes(1);
|
|
321
|
+
expect(field.setValue).toHaveBeenLastCalledWith('this-is-initial-title-value');
|
|
322
|
+
await sdk.entry.fields['title-id'].setValue('Hello world!');
|
|
323
|
+
await (0, _react1.wait)();
|
|
324
|
+
expect(field.setValue).toHaveBeenCalledTimes(2);
|
|
325
|
+
expect(field.setValue).toHaveBeenLastCalledWith('hello-world');
|
|
326
|
+
expect(sdk.space.getEntries).toHaveBeenCalledTimes(2);
|
|
327
|
+
});
|
|
328
|
+
it('should stop tracking value after user intentionally changes slug value', async ()=>{
|
|
329
|
+
const { field , titleField , sdk } = createMocks({
|
|
330
|
+
field: '',
|
|
331
|
+
titleField: ''
|
|
332
|
+
});
|
|
333
|
+
const { getByTestId } = (0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
334
|
+
field: field,
|
|
335
|
+
baseSdk: sdk,
|
|
336
|
+
isInitiallyDisabled: false
|
|
337
|
+
}));
|
|
338
|
+
await (0, _react1.wait)();
|
|
339
|
+
await sdk.entry.fields['title-id'].setValue('Hello world!');
|
|
340
|
+
await (0, _react1.wait)();
|
|
341
|
+
expect(titleField.onValueChanged).toHaveBeenCalledWith('en-US', expect.any(Function));
|
|
342
|
+
expect(field.setValue).toHaveBeenCalledTimes(2);
|
|
343
|
+
expect(field.setValue).toHaveBeenCalledWith('untitled-entry-2020-01-24-at-15-33-47');
|
|
344
|
+
expect(field.setValue).toHaveBeenLastCalledWith('hello-world');
|
|
345
|
+
expect(sdk.space.getEntries).toHaveBeenCalledTimes(2);
|
|
346
|
+
_react1.fireEvent.change(getByTestId('cf-ui-text-input'), {
|
|
347
|
+
target: {
|
|
348
|
+
value: 'new-custom-slug'
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
await (0, _react1.wait)();
|
|
352
|
+
expect(field.setValue).toHaveBeenCalledTimes(3);
|
|
353
|
+
expect(field.setValue).toHaveBeenLastCalledWith('new-custom-slug');
|
|
354
|
+
await sdk.entry.fields['title-id'].setValue('I decided to update my title');
|
|
355
|
+
await (0, _react1.wait)();
|
|
356
|
+
expect(field.setValue).toHaveBeenCalledTimes(3);
|
|
357
|
+
await sdk.entry.fields['title-id'].setValue('I decided to update my title again');
|
|
358
|
+
await (0, _react1.wait)();
|
|
359
|
+
expect(field.setValue).toHaveBeenCalledTimes(3);
|
|
360
|
+
});
|
|
361
|
+
it('should start tracking again after potential slug equals real one', async ()=>{
|
|
362
|
+
const { field , sdk } = createMocks({
|
|
363
|
+
field: '',
|
|
364
|
+
titleField: ''
|
|
365
|
+
});
|
|
366
|
+
const { getByTestId } = (0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
367
|
+
field: field,
|
|
368
|
+
baseSdk: sdk,
|
|
369
|
+
isInitiallyDisabled: false
|
|
370
|
+
}));
|
|
371
|
+
await (0, _react1.wait)();
|
|
372
|
+
await sdk.entry.fields['title-id'].setValue('ABC DEF');
|
|
373
|
+
await (0, _react1.wait)();
|
|
374
|
+
expect(field.setValue).toHaveBeenLastCalledWith('abc-def');
|
|
375
|
+
expect(field.setValue).toHaveBeenCalledTimes(2);
|
|
376
|
+
_react1.fireEvent.change(getByTestId('cf-ui-text-input'), {
|
|
377
|
+
target: {
|
|
378
|
+
value: 'abc'
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
await sdk.entry.fields['title-id'].setValue('ABC D');
|
|
382
|
+
await (0, _react1.wait)();
|
|
383
|
+
expect(field.setValue).toHaveBeenLastCalledWith('abc');
|
|
384
|
+
expect(field.setValue).toHaveBeenCalledTimes(3);
|
|
385
|
+
await sdk.entry.fields['title-id'].setValue('ABC');
|
|
386
|
+
await sdk.entry.fields['title-id'].setValue('ABC ABC');
|
|
387
|
+
await (0, _react1.wait)();
|
|
388
|
+
expect(field.setValue).toHaveBeenLastCalledWith('abc-abc');
|
|
389
|
+
expect(field.setValue).toHaveBeenCalledTimes(4);
|
|
390
|
+
await (0, _react1.wait)();
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
describe('for non default locales', ()=>{
|
|
394
|
+
it('locale is not optional and has no fallback then it should track default locale changes & current locale changes', async ()=>{
|
|
395
|
+
const { sdk , field , titleField } = createMocks();
|
|
396
|
+
field.locale = 'ru-RU';
|
|
397
|
+
field.required = false;
|
|
398
|
+
sdk.locales.available = [
|
|
399
|
+
'de-DE',
|
|
400
|
+
'ru-RU'
|
|
401
|
+
];
|
|
402
|
+
sdk.locales.default = 'de-DE';
|
|
403
|
+
sdk.locales.optional = {
|
|
404
|
+
'de-DE': false,
|
|
405
|
+
'ru-RU': false
|
|
406
|
+
};
|
|
407
|
+
sdk.locales.fallbacks = {
|
|
408
|
+
'de-DE': undefined,
|
|
409
|
+
'ru-RU': undefined
|
|
410
|
+
};
|
|
411
|
+
(0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
412
|
+
field: field,
|
|
413
|
+
baseSdk: sdk,
|
|
414
|
+
isInitiallyDisabled: false
|
|
415
|
+
}));
|
|
416
|
+
await (0, _react1.wait)();
|
|
417
|
+
expect(field.setValue).toHaveBeenCalledWith('untitled-entry-2020-01-24-at-15-33-47');
|
|
418
|
+
expect(titleField.onValueChanged).toHaveBeenCalledWith('ru-RU', expect.any(Function));
|
|
419
|
+
expect(titleField.onValueChanged).toHaveBeenCalledWith('de-DE', expect.any(Function));
|
|
420
|
+
});
|
|
421
|
+
it('locale is optional and has a fallback then it should track only current locale changes', async ()=>{
|
|
422
|
+
const { sdk , field , titleField } = createMocks();
|
|
423
|
+
field.locale = 'ru-RU';
|
|
424
|
+
field.required = false;
|
|
425
|
+
sdk.locales.available = [
|
|
426
|
+
'de-DE',
|
|
427
|
+
'ru-RU'
|
|
428
|
+
];
|
|
429
|
+
sdk.locales.default = 'de-DE';
|
|
430
|
+
sdk.locales.optional = {
|
|
431
|
+
'de-DE': false,
|
|
432
|
+
'ru-RU': true
|
|
433
|
+
};
|
|
434
|
+
sdk.locales.fallbacks = {
|
|
435
|
+
'de-DE': undefined,
|
|
436
|
+
'ru-RU': 'de-DE'
|
|
437
|
+
};
|
|
438
|
+
(0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
439
|
+
field: field,
|
|
440
|
+
baseSdk: sdk,
|
|
441
|
+
isInitiallyDisabled: false
|
|
442
|
+
}));
|
|
443
|
+
await (0, _react1.wait)();
|
|
444
|
+
expect(field.setValue).not.toHaveBeenCalled();
|
|
445
|
+
expect(titleField.onValueChanged).toHaveBeenCalledWith('ru-RU', expect.any(Function));
|
|
446
|
+
expect(titleField.onValueChanged).not.toHaveBeenCalledWith('de-DE', expect.any(Function));
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
it('slug suggestion is limited to 75 symbols', async ()=>{
|
|
450
|
+
const { field , sdk } = createMocks({
|
|
451
|
+
field: '',
|
|
452
|
+
titleField: ''
|
|
453
|
+
});
|
|
454
|
+
(0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
455
|
+
field: field,
|
|
456
|
+
baseSdk: sdk,
|
|
457
|
+
isInitiallyDisabled: false
|
|
458
|
+
}));
|
|
459
|
+
await (0, _react1.wait)();
|
|
460
|
+
await sdk.entry.fields['title-id'].setValue('a'.repeat(80));
|
|
461
|
+
await (0, _react1.wait)();
|
|
462
|
+
const expectedSlug = 'a'.repeat(75);
|
|
463
|
+
expect(field.setValue).toHaveBeenLastCalledWith(expectedSlug);
|
|
464
|
+
});
|
|
465
|
+
it('slug suggestion does not contain cut-off words', async ()=>{
|
|
466
|
+
const { field , sdk } = createMocks({
|
|
467
|
+
field: '',
|
|
468
|
+
titleField: ''
|
|
469
|
+
});
|
|
470
|
+
(0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
471
|
+
field: field,
|
|
472
|
+
baseSdk: sdk,
|
|
473
|
+
isInitiallyDisabled: false
|
|
474
|
+
}));
|
|
475
|
+
await (0, _react1.wait)();
|
|
476
|
+
await sdk.entry.fields['title-id'].setValue(`one two three ${'a'.repeat(80)}`);
|
|
477
|
+
await (0, _react1.wait)();
|
|
478
|
+
const expectedSlug = 'one-two-three';
|
|
479
|
+
expect(field.setValue).toHaveBeenLastCalledWith(expectedSlug);
|
|
480
|
+
});
|
|
481
|
+
it('should subscribe for changes in custom field id', async ()=>{
|
|
482
|
+
const { field , titleField , descriptionField , sdk } = createMocks({
|
|
483
|
+
field: '',
|
|
484
|
+
titleField: 'This is initial title value',
|
|
485
|
+
descriptionField: 'This is initial description value'
|
|
486
|
+
});
|
|
487
|
+
(0, _react1.render)(_react.createElement(_SlugEditor.SlugEditor, {
|
|
488
|
+
field: field,
|
|
489
|
+
baseSdk: sdk,
|
|
490
|
+
isInitiallyDisabled: false,
|
|
491
|
+
parameters: {
|
|
492
|
+
instance: {
|
|
493
|
+
trackingFieldId: 'description-id'
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}));
|
|
497
|
+
await (0, _react1.wait)();
|
|
498
|
+
expect(titleField.onValueChanged).not.toHaveBeenCalled();
|
|
499
|
+
expect(descriptionField.onValueChanged).toHaveBeenCalledWith('en-US', expect.any(Function));
|
|
500
|
+
expect(field.setValue).toHaveBeenCalledTimes(1);
|
|
501
|
+
expect(field.setValue).toHaveBeenLastCalledWith('this-is-initial-description-value');
|
|
502
|
+
await sdk.entry.fields['description-id'].setValue('Hello world!');
|
|
503
|
+
await (0, _react1.wait)();
|
|
504
|
+
expect(field.setValue).toHaveBeenCalledTimes(2);
|
|
505
|
+
expect(field.setValue).toHaveBeenLastCalledWith('hello-world');
|
|
506
|
+
expect(sdk.space.getEntries).toHaveBeenCalledTimes(2);
|
|
507
|
+
});
|
|
508
|
+
});
|