@contentful/field-editor-slug 2.1.4 → 2.1.5-canary.6
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 +4 -1
- package/dist/cjs/SlugEditor.test.js +35 -0
- package/dist/cjs/SlugEditorField.js +13 -3
- package/dist/esm/SlugEditor.js +4 -1
- package/dist/esm/SlugEditor.test.js +35 -0
- package/dist/esm/SlugEditorField.js +13 -3
- package/dist/types/SlugEditorField.d.ts +1 -0
- package/package.json +3 -2
package/dist/cjs/SlugEditor.js
CHANGED
|
@@ -56,7 +56,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
56
56
|
function isSupportedFieldTypes(val) {
|
|
57
57
|
return val === 'Symbol';
|
|
58
58
|
}
|
|
59
|
-
function FieldConnectorCallback({ Component, value, disabled, setValue, errors, titleValue, isOptionalLocaleWithFallback, locale, createdAt, performUniqueCheck, id }) {
|
|
59
|
+
function FieldConnectorCallback({ Component, value, disabled, setValue, errors, titleValue, isOptionalLocaleWithFallback, locale, createdAt, performUniqueCheck, isUniqueValidationEnabled, id }) {
|
|
60
60
|
const safeSetValue = _react.useCallback(async (...args)=>{
|
|
61
61
|
try {
|
|
62
62
|
await setValue(...args);
|
|
@@ -70,6 +70,7 @@ function FieldConnectorCallback({ Component, value, disabled, setValue, errors,
|
|
|
70
70
|
locale: locale,
|
|
71
71
|
createdAt: createdAt,
|
|
72
72
|
performUniqueCheck: performUniqueCheck,
|
|
73
|
+
isUniqueValidationEnabled: isUniqueValidationEnabled,
|
|
73
74
|
hasError: errors.length > 0,
|
|
74
75
|
value: value,
|
|
75
76
|
isOptionalLocaleWithFallback: isOptionalLocaleWithFallback,
|
|
@@ -87,6 +88,7 @@ function SlugEditor(props) {
|
|
|
87
88
|
}
|
|
88
89
|
const trackingFieldId = parameters?.instance?.trackingFieldId ?? undefined;
|
|
89
90
|
const entrySys = entry.getSys();
|
|
91
|
+
const isUniqueValidationEnabled = (field.validations || []).some((validation)=>'unique' in validation && validation.unique === true);
|
|
90
92
|
const isLocaleOptional = locales.optional[field.locale];
|
|
91
93
|
const localeFallbackCode = locales.fallbacks[field.locale];
|
|
92
94
|
const isOptionalFieldLocale = Boolean(!field.required || isLocaleOptional);
|
|
@@ -135,6 +137,7 @@ function SlugEditor(props) {
|
|
|
135
137
|
createdAt: entrySys.createdAt,
|
|
136
138
|
locale: field.locale,
|
|
137
139
|
performUniqueCheck: performUniqueCheck,
|
|
140
|
+
isUniqueValidationEnabled: isUniqueValidationEnabled,
|
|
138
141
|
key: `slug-editor-${externalReset}`,
|
|
139
142
|
id: id
|
|
140
143
|
});
|
|
@@ -60,6 +60,11 @@ function createMocks(initialValues = {}) {
|
|
|
60
60
|
const [field] = (0, _fieldeditortestutils.createFakeFieldAPI)((field)=>({
|
|
61
61
|
...field,
|
|
62
62
|
id: 'slug-id',
|
|
63
|
+
validations: [
|
|
64
|
+
{
|
|
65
|
+
unique: true
|
|
66
|
+
}
|
|
67
|
+
],
|
|
63
68
|
onValueChanged: jest.fn().mockImplementation(field.onValueChanged),
|
|
64
69
|
setValue: jest.fn().mockImplementation(field.setValue)
|
|
65
70
|
}), initialValues.field || '');
|
|
@@ -269,6 +274,36 @@ describe('SlugEditor', ()=>{
|
|
|
269
274
|
expect(queryByText('This slug has already been published in another entry')).not.toBeInTheDocument();
|
|
270
275
|
});
|
|
271
276
|
});
|
|
277
|
+
it('shows warning instead of error when unique validation is disabled on content model', async ()=>{
|
|
278
|
+
const { field, sdk } = createMocks({
|
|
279
|
+
titleField: 'Slug value',
|
|
280
|
+
field: 'slug-value'
|
|
281
|
+
});
|
|
282
|
+
field.validations = [];
|
|
283
|
+
sdk.entry.getSys.mockReturnValue({
|
|
284
|
+
id: 'entry-id',
|
|
285
|
+
publishedVersion: undefined,
|
|
286
|
+
contentType: {
|
|
287
|
+
sys: {
|
|
288
|
+
id: 'content-type-id'
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
sdk.cma.entry.getMany.mockResolvedValue({
|
|
293
|
+
total: 2
|
|
294
|
+
});
|
|
295
|
+
const { queryByText, getByTestId } = (0, _react1.render)(/*#__PURE__*/ _react.createElement(_SlugEditor.SlugEditor, {
|
|
296
|
+
field: field,
|
|
297
|
+
baseSdk: sdk,
|
|
298
|
+
isInitiallyDisabled: false
|
|
299
|
+
}));
|
|
300
|
+
await (0, _react1.waitFor)(()=>{
|
|
301
|
+
expect(sdk.cma.entry.getMany).toHaveBeenCalledTimes(1);
|
|
302
|
+
expect(queryByText('Warning: This slug has already been published in another entry. Enable "Unique" validation in the content model to enforce this as an error.')).toBeInTheDocument();
|
|
303
|
+
expect(queryByText('This slug has already been published in another entry')).not.toBeInTheDocument();
|
|
304
|
+
expect(getByTestId('cf-ui-text-input')).not.toHaveAttribute('aria-invalid');
|
|
305
|
+
});
|
|
306
|
+
});
|
|
272
307
|
});
|
|
273
308
|
describe('should react to title changes', ()=>{
|
|
274
309
|
it('when field is disabled', async ()=>{
|
|
@@ -19,6 +19,7 @@ _export(exports, {
|
|
|
19
19
|
const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
|
|
20
20
|
const _f36components = require("@contentful/f36-components");
|
|
21
21
|
const _f36icons = require("@contentful/f36-icons");
|
|
22
|
+
const _f36note = require("@contentful/f36-note");
|
|
22
23
|
const _core = require("@lingui/core");
|
|
23
24
|
const _usedebounce = require("use-debounce");
|
|
24
25
|
const _makeSlug = require("./services/makeSlug");
|
|
@@ -110,15 +111,17 @@ function useUniqueChecker(props) {
|
|
|
110
111
|
return status;
|
|
111
112
|
}
|
|
112
113
|
function SlugEditorFieldStatic(props) {
|
|
113
|
-
const { hasError, isDisabled, value, setValue, onChange, onBlur, id } = props;
|
|
114
|
+
const { hasError, isDisabled, value, setValue, onChange, onBlur, isUniqueValidationEnabled, id } = props;
|
|
114
115
|
const status = useUniqueChecker(props);
|
|
116
|
+
const hasDuplicate = status === 'duplicate';
|
|
117
|
+
const shouldShowDuplicateAsError = hasDuplicate && isUniqueValidationEnabled;
|
|
115
118
|
return /*#__PURE__*/ _react.createElement("div", {
|
|
116
119
|
className: _styles.inputContainer
|
|
117
120
|
}, /*#__PURE__*/ _react.createElement(_f36icons.LinkSimpleIcon, {
|
|
118
121
|
className: _styles.icon
|
|
119
122
|
}), /*#__PURE__*/ _react.createElement(_f36components.TextInput, {
|
|
120
123
|
className: _styles.input,
|
|
121
|
-
isInvalid: hasError ||
|
|
124
|
+
isInvalid: hasError || shouldShowDuplicateAsError,
|
|
122
125
|
isDisabled: isDisabled,
|
|
123
126
|
value: value || '',
|
|
124
127
|
id: id,
|
|
@@ -137,12 +140,19 @@ function SlugEditorFieldStatic(props) {
|
|
|
137
140
|
className: _styles.spinnerContainer
|
|
138
141
|
}, /*#__PURE__*/ _react.createElement(_f36components.Spinner, {
|
|
139
142
|
testId: "slug-editor-spinner"
|
|
140
|
-
})),
|
|
143
|
+
})), hasDuplicate && isUniqueValidationEnabled && /*#__PURE__*/ _react.createElement(_f36components.ValidationMessage, {
|
|
141
144
|
testId: "slug-editor-duplicate-error",
|
|
142
145
|
className: _styles.uniqueValidationError
|
|
143
146
|
}, _core.i18n._({
|
|
144
147
|
id: "FieldEditors.Slug.SlugEditorField.DuplicateSlugError",
|
|
145
148
|
message: "This slug has already been published in another entry"
|
|
149
|
+
})), hasDuplicate && !isUniqueValidationEnabled && /*#__PURE__*/ _react.createElement(_f36note.Note, {
|
|
150
|
+
variant: "warning",
|
|
151
|
+
testId: "slug-editor-duplicate-warning",
|
|
152
|
+
className: _styles.uniqueValidationError
|
|
153
|
+
}, _core.i18n._({
|
|
154
|
+
id: "FieldEditors.Slug.SlugEditorField.DuplicateSlugWarning",
|
|
155
|
+
message: 'Warning: This slug has already been published in another entry. Enable "Unique" validation in the content model to enforce this as an error.'
|
|
146
156
|
})));
|
|
147
157
|
}
|
|
148
158
|
function SlugEditorField(props) {
|
package/dist/esm/SlugEditor.js
CHANGED
|
@@ -5,7 +5,7 @@ import { TrackingFieldConnector } from './TrackingFieldConnector';
|
|
|
5
5
|
function isSupportedFieldTypes(val) {
|
|
6
6
|
return val === 'Symbol';
|
|
7
7
|
}
|
|
8
|
-
function FieldConnectorCallback({ Component, value, disabled, setValue, errors, titleValue, isOptionalLocaleWithFallback, locale, createdAt, performUniqueCheck, id }) {
|
|
8
|
+
function FieldConnectorCallback({ Component, value, disabled, setValue, errors, titleValue, isOptionalLocaleWithFallback, locale, createdAt, performUniqueCheck, isUniqueValidationEnabled, id }) {
|
|
9
9
|
const safeSetValue = React.useCallback(async (...args)=>{
|
|
10
10
|
try {
|
|
11
11
|
await setValue(...args);
|
|
@@ -19,6 +19,7 @@ function FieldConnectorCallback({ Component, value, disabled, setValue, errors,
|
|
|
19
19
|
locale: locale,
|
|
20
20
|
createdAt: createdAt,
|
|
21
21
|
performUniqueCheck: performUniqueCheck,
|
|
22
|
+
isUniqueValidationEnabled: isUniqueValidationEnabled,
|
|
22
23
|
hasError: errors.length > 0,
|
|
23
24
|
value: value,
|
|
24
25
|
isOptionalLocaleWithFallback: isOptionalLocaleWithFallback,
|
|
@@ -36,6 +37,7 @@ export function SlugEditor(props) {
|
|
|
36
37
|
}
|
|
37
38
|
const trackingFieldId = parameters?.instance?.trackingFieldId ?? undefined;
|
|
38
39
|
const entrySys = entry.getSys();
|
|
40
|
+
const isUniqueValidationEnabled = (field.validations || []).some((validation)=>'unique' in validation && validation.unique === true);
|
|
39
41
|
const isLocaleOptional = locales.optional[field.locale];
|
|
40
42
|
const localeFallbackCode = locales.fallbacks[field.locale];
|
|
41
43
|
const isOptionalFieldLocale = Boolean(!field.required || isLocaleOptional);
|
|
@@ -84,6 +86,7 @@ export function SlugEditor(props) {
|
|
|
84
86
|
createdAt: entrySys.createdAt,
|
|
85
87
|
locale: field.locale,
|
|
86
88
|
performUniqueCheck: performUniqueCheck,
|
|
89
|
+
isUniqueValidationEnabled: isUniqueValidationEnabled,
|
|
87
90
|
key: `slug-editor-${externalReset}`,
|
|
88
91
|
id: id
|
|
89
92
|
});
|
|
@@ -15,6 +15,11 @@ function createMocks(initialValues = {}) {
|
|
|
15
15
|
const [field] = createFakeFieldAPI((field)=>({
|
|
16
16
|
...field,
|
|
17
17
|
id: 'slug-id',
|
|
18
|
+
validations: [
|
|
19
|
+
{
|
|
20
|
+
unique: true
|
|
21
|
+
}
|
|
22
|
+
],
|
|
18
23
|
onValueChanged: jest.fn().mockImplementation(field.onValueChanged),
|
|
19
24
|
setValue: jest.fn().mockImplementation(field.setValue)
|
|
20
25
|
}), initialValues.field || '');
|
|
@@ -224,6 +229,36 @@ describe('SlugEditor', ()=>{
|
|
|
224
229
|
expect(queryByText('This slug has already been published in another entry')).not.toBeInTheDocument();
|
|
225
230
|
});
|
|
226
231
|
});
|
|
232
|
+
it('shows warning instead of error when unique validation is disabled on content model', async ()=>{
|
|
233
|
+
const { field, sdk } = createMocks({
|
|
234
|
+
titleField: 'Slug value',
|
|
235
|
+
field: 'slug-value'
|
|
236
|
+
});
|
|
237
|
+
field.validations = [];
|
|
238
|
+
sdk.entry.getSys.mockReturnValue({
|
|
239
|
+
id: 'entry-id',
|
|
240
|
+
publishedVersion: undefined,
|
|
241
|
+
contentType: {
|
|
242
|
+
sys: {
|
|
243
|
+
id: 'content-type-id'
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
sdk.cma.entry.getMany.mockResolvedValue({
|
|
248
|
+
total: 2
|
|
249
|
+
});
|
|
250
|
+
const { queryByText, getByTestId } = render(/*#__PURE__*/ React.createElement(SlugEditor, {
|
|
251
|
+
field: field,
|
|
252
|
+
baseSdk: sdk,
|
|
253
|
+
isInitiallyDisabled: false
|
|
254
|
+
}));
|
|
255
|
+
await waitFor(()=>{
|
|
256
|
+
expect(sdk.cma.entry.getMany).toHaveBeenCalledTimes(1);
|
|
257
|
+
expect(queryByText('Warning: This slug has already been published in another entry. Enable "Unique" validation in the content model to enforce this as an error.')).toBeInTheDocument();
|
|
258
|
+
expect(queryByText('This slug has already been published in another entry')).not.toBeInTheDocument();
|
|
259
|
+
expect(getByTestId('cf-ui-text-input')).not.toHaveAttribute('aria-invalid');
|
|
260
|
+
});
|
|
261
|
+
});
|
|
227
262
|
});
|
|
228
263
|
describe('should react to title changes', ()=>{
|
|
229
264
|
it('when field is disabled', async ()=>{
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { Spinner, TextInput, ValidationMessage } from '@contentful/f36-components';
|
|
3
3
|
import { LinkSimpleIcon } from '@contentful/f36-icons';
|
|
4
|
+
import { Note } from '@contentful/f36-note';
|
|
4
5
|
import { i18n as $_i18n } from "@lingui/core";
|
|
5
6
|
import { useDebounce } from 'use-debounce';
|
|
6
7
|
import { makeSlug } from './services/makeSlug';
|
|
@@ -51,15 +52,17 @@ function useUniqueChecker(props) {
|
|
|
51
52
|
return status;
|
|
52
53
|
}
|
|
53
54
|
export function SlugEditorFieldStatic(props) {
|
|
54
|
-
const { hasError, isDisabled, value, setValue, onChange, onBlur, id } = props;
|
|
55
|
+
const { hasError, isDisabled, value, setValue, onChange, onBlur, isUniqueValidationEnabled, id } = props;
|
|
55
56
|
const status = useUniqueChecker(props);
|
|
57
|
+
const hasDuplicate = status === 'duplicate';
|
|
58
|
+
const shouldShowDuplicateAsError = hasDuplicate && isUniqueValidationEnabled;
|
|
56
59
|
return /*#__PURE__*/ React.createElement("div", {
|
|
57
60
|
className: styles.inputContainer
|
|
58
61
|
}, /*#__PURE__*/ React.createElement(LinkSimpleIcon, {
|
|
59
62
|
className: styles.icon
|
|
60
63
|
}), /*#__PURE__*/ React.createElement(TextInput, {
|
|
61
64
|
className: styles.input,
|
|
62
|
-
isInvalid: hasError ||
|
|
65
|
+
isInvalid: hasError || shouldShowDuplicateAsError,
|
|
63
66
|
isDisabled: isDisabled,
|
|
64
67
|
value: value || '',
|
|
65
68
|
id: id,
|
|
@@ -78,12 +81,19 @@ export function SlugEditorFieldStatic(props) {
|
|
|
78
81
|
className: styles.spinnerContainer
|
|
79
82
|
}, /*#__PURE__*/ React.createElement(Spinner, {
|
|
80
83
|
testId: "slug-editor-spinner"
|
|
81
|
-
})),
|
|
84
|
+
})), hasDuplicate && isUniqueValidationEnabled && /*#__PURE__*/ React.createElement(ValidationMessage, {
|
|
82
85
|
testId: "slug-editor-duplicate-error",
|
|
83
86
|
className: styles.uniqueValidationError
|
|
84
87
|
}, $_i18n._({
|
|
85
88
|
id: "FieldEditors.Slug.SlugEditorField.DuplicateSlugError",
|
|
86
89
|
message: "This slug has already been published in another entry"
|
|
90
|
+
})), hasDuplicate && !isUniqueValidationEnabled && /*#__PURE__*/ React.createElement(Note, {
|
|
91
|
+
variant: "warning",
|
|
92
|
+
testId: "slug-editor-duplicate-warning",
|
|
93
|
+
className: styles.uniqueValidationError
|
|
94
|
+
}, $_i18n._({
|
|
95
|
+
id: "FieldEditors.Slug.SlugEditorField.DuplicateSlugWarning",
|
|
96
|
+
message: 'Warning: This slug has already been published in another entry. Enable "Unique" validation in the content model to enforce this as an error.'
|
|
87
97
|
})));
|
|
88
98
|
}
|
|
89
99
|
export function SlugEditorField(props) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/field-editor-slug",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5-canary.6+2bae425d",
|
|
4
4
|
"main": "dist/cjs/index.js",
|
|
5
5
|
"module": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@contentful/f36-components": "^5.4.1",
|
|
40
40
|
"@contentful/f36-icons": "^5.4.1",
|
|
41
|
+
"@contentful/f36-note": "^5.4.1",
|
|
41
42
|
"@contentful/f36-tokens": "^5.1.0",
|
|
42
43
|
"@contentful/field-editor-shared": "^2.18.1",
|
|
43
44
|
"@types/speakingurl": "^13.0.2",
|
|
@@ -58,5 +59,5 @@
|
|
|
58
59
|
"publishConfig": {
|
|
59
60
|
"registry": "https://npm.pkg.github.com/"
|
|
60
61
|
},
|
|
61
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "2bae425d639a833aea33f11ef8bd4082644a1250"
|
|
62
63
|
}
|