@designcrowd/library.brand-page 6.0.6-json-prereq-5 → 6.0.6-json-prereq-7

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.
Files changed (35) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-CZmzW2kQ.js → CreatedOnBrandCrowd-de-de-SPVLPi8n.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-D2tXzTdS.js → CreatedOnBrandCrowd-en-us-CvtrCPHd.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-BDXmktFP.js → CreatedOnBrandCrowd-es-es-BSsac7jB.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-BniZQPhE.js → CreatedOnBrandCrowd-fr-ca-BauENV2a.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-BniZQPhE.js → CreatedOnBrandCrowd-fr-fr-BauENV2a.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-mngAiD6i.js → CreatedOnBrandCrowd-pt-br-Be0hawmr.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-mngAiD6i.js → CreatedOnBrandCrowd-pt-pt-Be0hawmr.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-Bacn6DYk.js → CreatedOnDcom-de-de-CTjE2Tmn.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-CtQMBrOW.js → CreatedOnDcom-en-us-DnZRp5Pg.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-ndMBMTWL.js → CreatedOnDcom-es-es-J-9qvjy8.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-dQuX1eXb.js → CreatedOnDcom-fr-ca-BDNHqm4G.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-74uI3jen.js → CreatedOnDcom-fr-fr-B_2-ceuO.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-BAr2KEUU.js → CreatedOnDcom-pt-br-76lJVrAY.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-CIOnL7w9.js → CreatedOnDcom-pt-pt-TQp3CNcu.js} +1 -1
  15. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.d.ts +14 -0
  16. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.js +39 -0
  17. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.js +1 -1
  18. package/lib/src/brand-page-maker/validators/field.validator.js +10 -7
  19. package/lib/src/{bundled-translations-DY44n5a1.js → bundled-translations-BUyt4scr.js} +1 -1
  20. package/lib/src/bundles/bundled-translations.json +0 -1
  21. package/lib/src/css/library.brand-page-brandCrowd.min.css +36 -36
  22. package/lib/src/css/library.brand-page-brandPage.min.css +36 -36
  23. package/lib/src/css/library.brand-page-designCom.min.css +36 -36
  24. package/lib/src/css/library.brand-page-preview.min.css +36 -36
  25. package/lib/src/index.mjs +1 -1
  26. package/lib/src/{lib-CfYQhXJl.js → lib-Bh9Y5_-x.js} +8597 -8588
  27. package/package.json +1 -1
  28. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +0 -19
  29. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.test.ts +52 -0
  30. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.ts +42 -0
  31. package/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.ts +1 -1
  32. package/src/brand-page-maker/validators/field.validator.test.ts +15 -10
  33. package/src/brand-page-maker/validators/field.validator.ts +10 -7
  34. package/src/bundles/bundled-translations.json +0 -1
  35. package/src/shared/content-settings/components/content-settings/i18n/content-settings.json +0 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@designcrowd/library.brand-page",
3
3
  "description": "A collection of Maker/Designer applications",
4
- "version": "6.0.6-json-prereq-5",
4
+ "version": "6.0.6-json-prereq-7",
5
5
  "author": "Design.com Eng",
6
6
  "license": "UNLICENSED",
7
7
  "type": "module",
@@ -342,15 +342,7 @@
342
342
  <TextInput
343
343
  v-model="markedText"
344
344
  :placeholder="contentSettingsTr('displayTextPlaceholder')"
345
- :error="isDisplayTextTooLong"
346
345
  />
347
- <p
348
- v-if="isDisplayTextTooLong"
349
- class="tw-text-error-500 tw-text-sm"
350
- data-test-tag="hyperlink-display-text-error"
351
- >
352
- {{ contentSettingsTr('displayTextMaxLimitError') }}
353
- </p>
354
346
 
355
347
  <FieldSubHeader :title="contentSettingsTr('linkToLabel')" />
356
348
  <ButtonGroup
@@ -669,18 +661,7 @@ const urlValidationSettings = computed(() => {
669
661
  };
670
662
  });
671
663
 
672
- // BP-5859: the hyperlink display text is inserted into the RTE, so it must
673
- // respect the same max-char limit. Without this the over-limit text was
674
- // silently truncated on insert with no feedback.
675
- const isDisplayTextTooLong = computed(
676
- () => markedText.value.length > props.maxLength,
677
- );
678
-
679
664
  const isAddLinkButtonDisabled = computed(() => {
680
- if (isDisplayTextTooLong.value) {
681
- return true;
682
- }
683
-
684
665
  if (selectedLinkType.value === HYPERLINK_TYPE.url) {
685
666
  const urlValue = hyperlinkUrlValue.value;
686
667
  // Check if URL is invalid OR doesn't have https protocol
@@ -0,0 +1,52 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { Editor } from '@tiptap/vue-3';
3
+ import { getRteExtensions } from '../getRteExtensions';
4
+ import { sanitizeRteJson } from '@brand-page-maker/utils/rte-sanitize.util';
5
+
6
+ /**
7
+ * BP-5787 — multi-word font families ("Open Sans") are quoted in CSS, so
8
+ * re-parsing HTML (pasting RTE-styled text between editors) previously yielded
9
+ * a value WITH quote chars, which the native sanitiser then dropped — losing
10
+ * the font. The custom FontFamily extension strips the quotes on parse.
11
+ */
12
+ describe('FontFamily — quote-stripping parse (BP-5787)', () => {
13
+ let editors: Editor[] = [];
14
+ const mk = () => {
15
+ const e = new Editor({
16
+ extensions: getRteExtensions({ restrictStarterKit: true }),
17
+ });
18
+
19
+ editors.push(e);
20
+
21
+ return e;
22
+ };
23
+
24
+ afterEach(() => {
25
+ editors.forEach((e) => e.destroy());
26
+ editors = [];
27
+ });
28
+
29
+ const fontFamilyOf = (json: any) =>
30
+ json.content?.[0]?.content?.[0]?.marks?.find(
31
+ (m: any) => m.type === 'textStyle',
32
+ )?.attrs?.fontFamily;
33
+
34
+ for (const font of ['Montserrat', 'Open Sans', 'Playwrite AU']) {
35
+ it(`"${font}" survives an HTML re-parse + sanitise unquoted`, () => {
36
+ const src = mk();
37
+
38
+ src.commands.setContent('<p>Hello world</p>');
39
+ src.commands.selectAll();
40
+ src.commands.setFontFamily(font);
41
+
42
+ // Re-parse the serialised HTML — mirrors an RTE→RTE paste.
43
+ const tgt = mk();
44
+
45
+ tgt.commands.setContent(src.getHTML());
46
+
47
+ expect(fontFamilyOf(tgt.getJSON())).toBe(font);
48
+ // and the sanitiser must keep it (isSafeCssValue rejects quote chars).
49
+ expect(fontFamilyOf(sanitizeRteJson(tgt.getJSON()))).toBe(font);
50
+ });
51
+ }
52
+ });
@@ -0,0 +1,42 @@
1
+ import TiptapFontFamily from '@tiptap/extension-font-family';
2
+
3
+ /**
4
+ * BP-5787 — font-family with quote-stripping parse.
5
+ *
6
+ * The stock extension stores whatever `element.style.fontFamily` returns. For
7
+ * multi-word families (e.g. "Open Sans", "Playwrite AU") the browser quotes the
8
+ * value, so parsing HTML — pasting RTE-styled text between editors, or the
9
+ * HTML→JSON migration — yields a value that still contains the quote chars
10
+ * (`"Open Sans"`). That value then (a) no longer matches the font list and,
11
+ * worse, (b) is dropped by the native RTE sanitiser (`sanitizeRteJson`), whose
12
+ * `isSafeCssValue` rejects any value containing `"` `'` `;` — so the font is
13
+ * silently lost. Mirror the FontSize/LetterSpacing extensions and strip the
14
+ * quotes on parse so the stored value is always the bare family name.
15
+ */
16
+ export const FontFamily = TiptapFontFamily.extend({
17
+ addGlobalAttributes() {
18
+ return [
19
+ {
20
+ types: this.options.types,
21
+ attributes: {
22
+ fontFamily: {
23
+ default: null,
24
+ // Strip quotes only — keep '' for an absent value so the stored
25
+ // JSON shape matches the other textStyle attrs (FontSize etc.).
26
+ parseHTML: (element) =>
27
+ element.style.fontFamily?.replace(/['"]+/g, ''),
28
+ renderHTML: (attributes) => {
29
+ if (!attributes.fontFamily) {
30
+ return {};
31
+ }
32
+
33
+ return {
34
+ style: `font-family: ${attributes.fontFamily}`,
35
+ };
36
+ },
37
+ },
38
+ },
39
+ },
40
+ ];
41
+ },
42
+ });
@@ -3,11 +3,11 @@ import StarterKit from '@tiptap/starter-kit';
3
3
  import Underline from '@tiptap/extension-underline';
4
4
  import TextStyle from '@tiptap/extension-text-style';
5
5
  import TextAlign from '@tiptap/extension-text-align';
6
- import FontFamily from '@tiptap/extension-font-family';
7
6
  import ColorExtension from '@tiptap/extension-color';
8
7
  import Highlight from '@tiptap/extension-highlight';
9
8
  import Placeholder from '@tiptap/extension-placeholder';
10
9
  import Link from '@tiptap/extension-link';
10
+ import { FontFamily } from './extensions/FontFamily';
11
11
  import { FontSize } from './extensions/FontSize';
12
12
  import { LetterSpacing } from './extensions/LetterSpacing';
13
13
  import { LineHeight } from './extensions/LineHeight';
@@ -2,11 +2,12 @@ import { describe, it, expect } from 'vitest';
2
2
  import { validatorDict } from './field.validator';
3
3
 
4
4
  /**
5
- * BP-5857 — the `title` field validator must accept RTE content in both forms.
6
- * After BP-5787, switchAllContentToRTE converts section titles (Payments, Store,
7
- * Tipping, Social Links) from a plain string to a Tiptap JSON doc. The old
8
- * `typeof value === 'string'` check then flagged a title that clearly HAS text
9
- * as empty ("Please enter a value"). The validator now dual-reads the value.
5
+ * BP-5857 — the `title` validator must preserve pre-JSON (staging) semantics.
6
+ * An RTE field's value used to be inline HTML: an empty editor was "<p></p>",
7
+ * a non-empty string, so the required check always PASSED for RTE fields — it
8
+ * only failed for genuinely empty PLAIN inputs. RTE fields are now Tiptap JSON
9
+ * docs, so any doc must likewise count as "present" (valid); a plain string is
10
+ * still required to be non-empty.
10
11
  */
11
12
  describe('field.validator — title (nonEmptyString)', () => {
12
13
  const titleValidator = validatorDict.title;
@@ -23,22 +24,26 @@ describe('field.validator — title (nonEmptyString)', () => {
23
24
  ],
24
25
  });
25
26
 
26
- it('accepts a non-empty legacy string', () => {
27
+ it('accepts a non-empty plain string', () => {
27
28
  expect(titleValidator.validate('title', 'Payments').isValid).toBe(true);
28
29
  });
29
30
 
30
- it('rejects an empty string', () => {
31
+ it('rejects an empty plain string (required still enforced for plain inputs)', () => {
31
32
  expect(titleValidator.validate('title', '').isValid).toBe(false);
32
33
  });
33
34
 
34
- it('accepts a Tiptap JSON doc that has visible text (BP-5857)', () => {
35
+ it('accepts legacy empty RTE HTML "<p></p>" (non-empty string — staging parity)', () => {
36
+ expect(titleValidator.validate('title', '<p></p>').isValid).toBe(true);
37
+ });
38
+
39
+ it('accepts a Tiptap JSON doc with visible text', () => {
35
40
  expect(titleValidator.validate('title', jsonDoc('Payments')).isValid).toBe(
36
41
  true,
37
42
  );
38
43
  });
39
44
 
40
- it('rejects an empty Tiptap JSON doc', () => {
41
- expect(titleValidator.validate('title', jsonDoc('')).isValid).toBe(false);
45
+ it('accepts an empty Tiptap JSON doc (RTE parity — empty editor is "present")', () => {
46
+ expect(titleValidator.validate('title', jsonDoc('')).isValid).toBe(true);
42
47
  });
43
48
 
44
49
  it('rejects null/undefined without throwing', () => {
@@ -1,6 +1,6 @@
1
1
  import { isURL, isEmail, isTel } from '@utils/validator.util';
2
2
  import type { ItemFieldType, Validator } from '@brand-page-maker/models';
3
- import { YOUTUBE_URL_RE, getPlainTextFromRTE } from '@brand-page-maker/utils';
3
+ import { YOUTUBE_URL_RE, isTiptapDocument } from '@brand-page-maker/utils';
4
4
  import { validatorTr } from '@src/composables/useTranslate';
5
5
 
6
6
  const addressValueValidator: Validator = {
@@ -76,12 +76,15 @@ export const dummyValidator: Validator = {
76
76
 
77
77
  const nonEmptyStringValidator: Validator = {
78
78
  validate: (name, value: any) => {
79
- // BP-5787/BP-5857: `title` fields may now hold Tiptap JSON (an RTE doc)
80
- // rather than a plain string once switchAllContentToRTE converts them.
81
- // Validate the visible text either way — a doc with text is "entered",
82
- // even though `typeof value` is 'object'. getPlainTextFromRTE dual-reads
83
- // both forms (string → stripped text, JSON → concatenated text).
84
- const isValid = getPlainTextFromRTE(value).trim().length > 0;
79
+ // BP-5787/BP-5857: match legacy (pre-JSON) validation semantics.
80
+ // An RTE field's stored value used to be inline HTML — an empty editor was
81
+ // "<p></p>", a NON-empty string, so the required check always passed for RTE
82
+ // fields (it only ever failed for genuinely empty PLAIN inputs). Now RTE
83
+ // fields are Tiptap JSON docs; treat any doc as "present" so behaviour
84
+ // stays identical. A plain string is still required to be non-empty.
85
+ const isValid = isTiptapDocument(value)
86
+ ? true
87
+ : typeof value === 'string' && value.length > 0;
85
88
 
86
89
  return {
87
90
  isValid,
@@ -1609,7 +1609,6 @@
1609
1609
  "linkToLabel": "Link to",
1610
1610
  "addLinkButtonText": "Add Link",
1611
1611
  "enterValidUrlError": "Please enter a valid URL",
1612
- "displayTextMaxLimitError": "Display text exceeds the maximum character limit",
1613
1612
  "enterTextHerePlaceholder": "Enter text here...",
1614
1613
  "bookNowLabel": "Book Now",
1615
1614
  "shippingLabel": "Enable Shipping Address",
@@ -203,7 +203,6 @@
203
203
  "linkToLabel": "Link to",
204
204
  "addLinkButtonText": "Add Link",
205
205
  "enterValidUrlError": "Please enter a valid URL",
206
- "displayTextMaxLimitError": "Display text exceeds the maximum character limit",
207
206
  "enterTextHerePlaceholder": "Enter text here...",
208
207
  "bookNowLabel": "Book Now",
209
208
  "shippingLabel": "Enable Shipping Address",