From b6782039b9ad5305ac154b4f540d2ec2a5c433b9 Mon Sep 17 00:00:00 2001 From: "Roehl, Johann (extern)" Date: Thu, 8 Dec 2022 08:15:10 +0100 Subject: [PATCH] feat: add max-slug-length property to instance paramaters --- packages/slug/src/SlugEditor.test.tsx | 36 ++++++++++++++++++-------- packages/slug/src/SlugEditor.tsx | 4 +++ packages/slug/src/SlugEditorField.tsx | 11 +++++--- packages/slug/src/services/makeSlug.ts | 3 ++- packages/slug/src/services/slugify.ts | 7 ++--- 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/packages/slug/src/SlugEditor.test.tsx b/packages/slug/src/SlugEditor.test.tsx index a7d8d08826..2c7c92d74f 100644 --- a/packages/slug/src/SlugEditor.test.tsx +++ b/packages/slug/src/SlugEditor.test.tsx @@ -3,6 +3,7 @@ import identity from 'lodash/identity'; import { render, configure, cleanup, wait, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import { SlugEditor } from './SlugEditor'; +import { CF_GENERATED_SLUG_MAX_LENGTH } from './services/slugify'; import { createFakeFieldAPI, createFakeLocalesAPI } from '@contentful/field-editor-test-utils'; configure({ @@ -453,21 +454,34 @@ describe('SlugEditor', () => { }); }); - it('slug suggestion is limited to 75 symbols', async () => { - const { field, sdk } = createMocks({ - field: '', - titleField: '', - }); + [undefined, 0, 100].forEach((maxSlugLength) => { + it(`slug suggestion is limited to ${ + maxSlugLength || `${CF_GENERATED_SLUG_MAX_LENGTH} (default)` + } symbols`, async () => { + const { field, sdk } = createMocks({ + field: '', + titleField: '', + }); - render(); + render( + + ); - await wait(); + await wait(); - await sdk.entry.fields['title-id'].setValue('a'.repeat(80)); - await wait(); + await sdk.entry.fields['title-id'].setValue( + 'a'.repeat((maxSlugLength ?? CF_GENERATED_SLUG_MAX_LENGTH) + 10) + ); + await wait(); - const expectedSlug = 'a'.repeat(75); - expect(field.setValue).toHaveBeenLastCalledWith(expectedSlug); + const expectedSlug = 'a'.repeat(maxSlugLength ?? CF_GENERATED_SLUG_MAX_LENGTH); + expect(field.setValue).toHaveBeenLastCalledWith(expectedSlug); + }); }); it('slug suggestion does not contain cut-off words', async () => { diff --git a/packages/slug/src/SlugEditor.tsx b/packages/slug/src/SlugEditor.tsx index 3de6391305..c4740e4e81 100644 --- a/packages/slug/src/SlugEditor.tsx +++ b/packages/slug/src/SlugEditor.tsx @@ -20,6 +20,7 @@ export interface SlugEditorProps { parameters?: { instance: { trackingFieldId?: string; + maxSlugLength?: number; }; }; } @@ -50,6 +51,7 @@ function FieldConnectorCallback({ locale: FieldAPI['locale']; createdAt: string; performUniqueCheck: (value: string) => Promise; + maxSlugLength?: number; }) { // it is needed to silent permission errors // this happens when setValue is called on a field which is disabled for permission reasons @@ -90,6 +92,7 @@ export function SlugEditor(props: SlugEditorProps) { } const trackingFieldId = parameters?.instance?.trackingFieldId ?? undefined; + const maxSlugLength = parameters?.instance?.maxSlugLength ?? undefined; const entrySys = entry.getSys(); const isLocaleOptional = locales.optional[field.locale]; @@ -149,6 +152,7 @@ export function SlugEditor(props: SlugEditorProps) { createdAt={entrySys.createdAt} locale={field.locale} performUniqueCheck={performUniqueCheck} + maxSlugLength={maxSlugLength} key={`slug-editor-${externalReset}`} /> ); diff --git a/packages/slug/src/SlugEditorField.tsx b/packages/slug/src/SlugEditorField.tsx index c14b93ab2b..85f7187e5a 100644 --- a/packages/slug/src/SlugEditorField.tsx +++ b/packages/slug/src/SlugEditorField.tsx @@ -17,12 +17,13 @@ interface SlugEditorFieldProps { createdAt: string; setValue: (value: string | null | undefined) => void; performUniqueCheck: (value: string) => Promise; + maxSlugLength?: number; } type CheckerState = 'checking' | 'unique' | 'duplicate'; function useSlugUpdater(props: SlugEditorFieldProps, check: boolean) { - const { value, setValue, createdAt, locale, titleValue, isOptionalLocaleWithFallback } = props; + const { value, setValue, createdAt, locale, titleValue, isOptionalLocaleWithFallback, maxSlugLength } = props; React.useEffect(() => { if (check === false) { @@ -32,11 +33,12 @@ function useSlugUpdater(props: SlugEditorFieldProps, check: boolean) { isOptionalLocaleWithFallback, locale, createdAt, + maxSlugLength }); if (newSlug !== value) { setValue(newSlug); } - }, [value, titleValue, isOptionalLocaleWithFallback, check, createdAt, locale, setValue]); + }, [value, titleValue, isOptionalLocaleWithFallback, check, createdAt, locale, setValue, maxSlugLength]); } function useUniqueChecker(props: SlugEditorFieldProps) { @@ -111,16 +113,17 @@ export function SlugEditorFieldStatic( } export function SlugEditorField(props: SlugEditorFieldProps) { - const { titleValue, isOptionalLocaleWithFallback, locale, createdAt, value } = props; + const { titleValue, isOptionalLocaleWithFallback, locale, createdAt, value, maxSlugLength } = props; const areEqual = React.useCallback(() => { const potentialSlug = makeSlug(titleValue, { isOptionalLocaleWithFallback: isOptionalLocaleWithFallback, locale: locale, createdAt: createdAt, + maxSlugLength }); return value === potentialSlug; - }, [titleValue, isOptionalLocaleWithFallback, locale, createdAt, value]); + }, [titleValue, isOptionalLocaleWithFallback, locale, createdAt, value, maxSlugLength]); const [check, setCheck] = React.useState(() => { if (props.value) { diff --git a/packages/slug/src/services/makeSlug.ts b/packages/slug/src/services/makeSlug.ts index ec85b06e63..5602c6f1b3 100644 --- a/packages/slug/src/services/makeSlug.ts +++ b/packages/slug/src/services/makeSlug.ts @@ -4,6 +4,7 @@ type MakeSlugOptions = { locale: string; isOptionalLocaleWithFallback: boolean; createdAt: string; + maxSlugLength?: number; }; function formatTwoDigit(num: number) { @@ -32,5 +33,5 @@ function untitledSlug({ isOptionalLocaleWithFallback, createdAt }: MakeSlugOptio } export function makeSlug(title: string | null | undefined, options: MakeSlugOptions) { - return title ? slugify(title, options.locale) : untitledSlug(options); + return title ? slugify(title, options.locale, options.maxSlugLength) : untitledSlug(options); } diff --git a/packages/slug/src/services/slugify.ts b/packages/slug/src/services/slugify.ts index 1163dc435a..eba580c7ef 100644 --- a/packages/slug/src/services/slugify.ts +++ b/packages/slug/src/services/slugify.ts @@ -1,6 +1,6 @@ import getSlug from 'speakingurl'; -const CF_GENERATED_SLUG_MAX_LENGTH = 75; +export const CF_GENERATED_SLUG_MAX_LENGTH = 75; const languages = [ 'ar', @@ -51,13 +51,14 @@ function supportedLanguage(locale: string) { * * @param {string} text To be turned into a slug. * @param {string?} locale + * @param {number?} maxLength * @returns {string} Slug for provided text. */ -export function slugify(text: string, locale = 'en') { +export function slugify(text: string, locale = 'en', maxLength = CF_GENERATED_SLUG_MAX_LENGTH) { return getSlug(text, { separator: '-', lang: supportedLanguage(locale) || 'en', - truncate: CF_GENERATED_SLUG_MAX_LENGTH + 1, + truncate: maxLength + 1, custom: { "'": '', '`': '',