From 8b861742267b131eb5beff3c9529d3fd36341afc Mon Sep 17 00:00:00 2001 From: Alexandre Rousseau Date: Fri, 24 Jan 2025 16:51:35 +0100 Subject: [PATCH 1/3] feat(ui): support inline markdown in `AnnotatedText` - WF-169 Use `BaseMarkdown` in `CoreAnnotatedText` to support inline markdown syntax. I also reworked a bit `BaseMarkdown` to have an inline mode. --- .../components/core/base/BaseMarkdown.spec.ts | 47 +++++++++++++++++++ .../src/components/core/base/BaseMarkdown.vue | 40 ++++++++++------ .../__snapshots__/BaseMarkdown.spec.ts.snap | 37 +++++++++++++++ .../core/content/CoreAnnotatedText.vue | 14 ++++-- 4 files changed, 119 insertions(+), 19 deletions(-) create mode 100644 src/ui/src/components/core/base/BaseMarkdown.spec.ts create mode 100644 src/ui/src/components/core/base/__snapshots__/BaseMarkdown.spec.ts.snap diff --git a/src/ui/src/components/core/base/BaseMarkdown.spec.ts b/src/ui/src/components/core/base/BaseMarkdown.spec.ts new file mode 100644 index 00000000..c08236bd --- /dev/null +++ b/src/ui/src/components/core/base/BaseMarkdown.spec.ts @@ -0,0 +1,47 @@ +import { beforeEach, describe, expect, it, Mock, vi } from "vitest"; +import { flushPromises, shallowMount } from "@vue/test-utils"; +import VueDOMPurifyHTML from "vue-dompurify-html"; +import BaseMarkdown from "./BaseMarkdown.vue"; +import { parse, parseInline } from "marked"; + +vi.mock("marked", () => ({ + parse: vi.fn().mockImplementation((v) => `
${v}
`), + parseInline: vi.fn().mockImplementation((v) => `${v}`), +})); + +describe("BaseMarkdown", () => { + const rawText = "# hello\nI'm **MD** "; + + beforeEach(() => { + (parse as unknown as Mock).mockClear(); + (parseInline as unknown as Mock).mockClear(); + }); + + it("should render markdown as block", async () => { + const wrapper = shallowMount(BaseMarkdown, { + props: { rawText }, + global: { plugins: [VueDOMPurifyHTML] }, + }); + await flushPromises(); + expect(wrapper.element).toMatchSnapshot(); + expect(parse).toHaveBeenCalledOnce(); + + wrapper.setProps({ rawText: "updated" }); + await flushPromises(); + expect(parse).toHaveBeenCalledTimes(2); + }); + + it("should render markdown as inline", async () => { + const wrapper = shallowMount(BaseMarkdown, { + props: { rawText, inline: true }, + global: { plugins: [VueDOMPurifyHTML] }, + }); + await flushPromises(); + expect(wrapper.element).toMatchSnapshot(); + expect(parseInline).toHaveBeenCalledOnce(); + + wrapper.setProps({ rawText: "updated" }); + await flushPromises(); + expect(parseInline).toHaveBeenCalledTimes(2); + }); +}); diff --git a/src/ui/src/components/core/base/BaseMarkdown.vue b/src/ui/src/components/core/base/BaseMarkdown.vue index 5c3b9f6f..25da884b 100644 --- a/src/ui/src/components/core/base/BaseMarkdown.vue +++ b/src/ui/src/components/core/base/BaseMarkdown.vue @@ -1,25 +1,35 @@ From a7ed96dd65172a09c2fcaadbbb790fccff80af8e Mon Sep 17 00:00:00 2001 From: Alexandre Rousseau Date: Mon, 27 Jan 2025 11:01:20 +0100 Subject: [PATCH 2/3] Revert "feat(ui): support inline markdown in `AnnotatedText` - WF-169" This reverts commit 8b861742267b131eb5beff3c9529d3fd36341afc. --- .../components/core/base/BaseMarkdown.spec.ts | 47 ------------------- .../src/components/core/base/BaseMarkdown.vue | 40 ++++++---------- .../__snapshots__/BaseMarkdown.spec.ts.snap | 37 --------------- .../core/content/CoreAnnotatedText.vue | 14 ++---- 4 files changed, 19 insertions(+), 119 deletions(-) delete mode 100644 src/ui/src/components/core/base/BaseMarkdown.spec.ts delete mode 100644 src/ui/src/components/core/base/__snapshots__/BaseMarkdown.spec.ts.snap diff --git a/src/ui/src/components/core/base/BaseMarkdown.spec.ts b/src/ui/src/components/core/base/BaseMarkdown.spec.ts deleted file mode 100644 index c08236bd..00000000 --- a/src/ui/src/components/core/base/BaseMarkdown.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { beforeEach, describe, expect, it, Mock, vi } from "vitest"; -import { flushPromises, shallowMount } from "@vue/test-utils"; -import VueDOMPurifyHTML from "vue-dompurify-html"; -import BaseMarkdown from "./BaseMarkdown.vue"; -import { parse, parseInline } from "marked"; - -vi.mock("marked", () => ({ - parse: vi.fn().mockImplementation((v) => `
${v}
`), - parseInline: vi.fn().mockImplementation((v) => `${v}`), -})); - -describe("BaseMarkdown", () => { - const rawText = "# hello\nI'm **MD** "; - - beforeEach(() => { - (parse as unknown as Mock).mockClear(); - (parseInline as unknown as Mock).mockClear(); - }); - - it("should render markdown as block", async () => { - const wrapper = shallowMount(BaseMarkdown, { - props: { rawText }, - global: { plugins: [VueDOMPurifyHTML] }, - }); - await flushPromises(); - expect(wrapper.element).toMatchSnapshot(); - expect(parse).toHaveBeenCalledOnce(); - - wrapper.setProps({ rawText: "updated" }); - await flushPromises(); - expect(parse).toHaveBeenCalledTimes(2); - }); - - it("should render markdown as inline", async () => { - const wrapper = shallowMount(BaseMarkdown, { - props: { rawText, inline: true }, - global: { plugins: [VueDOMPurifyHTML] }, - }); - await flushPromises(); - expect(wrapper.element).toMatchSnapshot(); - expect(parseInline).toHaveBeenCalledOnce(); - - wrapper.setProps({ rawText: "updated" }); - await flushPromises(); - expect(parseInline).toHaveBeenCalledTimes(2); - }); -}); diff --git a/src/ui/src/components/core/base/BaseMarkdown.vue b/src/ui/src/components/core/base/BaseMarkdown.vue index 25da884b..5c3b9f6f 100644 --- a/src/ui/src/components/core/base/BaseMarkdown.vue +++ b/src/ui/src/components/core/base/BaseMarkdown.vue @@ -1,35 +1,25 @@ From a9c044227fd06aefeabb90325a9b84841f736f88 Mon Sep 17 00:00:00 2001 From: Alexandre Rousseau Date: Mon, 27 Jan 2025 14:21:01 +0100 Subject: [PATCH 3/3] feat(ui): add `useMarkown` to `CoreAnnotatedText` - WF-169 --- .../src/components/core/base/BaseMarkdown.vue | 95 +--------- .../components/core/base/BaseMarkdownRaw.vue | 100 +++++++++++ .../core/content/CoreAnnotatedText.spec.ts | 91 ++++++++++ .../core/content/CoreAnnotatedText.vue | 165 ++++++++++++------ .../CoreAnnotatedText.spec.ts.snap | 123 +++++++++++++ 5 files changed, 427 insertions(+), 147 deletions(-) create mode 100644 src/ui/src/components/core/base/BaseMarkdownRaw.vue create mode 100644 src/ui/src/components/core/content/CoreAnnotatedText.spec.ts create mode 100644 src/ui/src/components/core/content/__snapshots__/CoreAnnotatedText.spec.ts.snap diff --git a/src/ui/src/components/core/base/BaseMarkdown.vue b/src/ui/src/components/core/base/BaseMarkdown.vue index 5c3b9f6f..6033be12 100644 --- a/src/ui/src/components/core/base/BaseMarkdown.vue +++ b/src/ui/src/components/core/base/BaseMarkdown.vue @@ -1,13 +1,14 @@ - - diff --git a/src/ui/src/components/core/base/BaseMarkdownRaw.vue b/src/ui/src/components/core/base/BaseMarkdownRaw.vue new file mode 100644 index 00000000..fd2ccfe0 --- /dev/null +++ b/src/ui/src/components/core/base/BaseMarkdownRaw.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/src/ui/src/components/core/content/CoreAnnotatedText.spec.ts b/src/ui/src/components/core/content/CoreAnnotatedText.spec.ts new file mode 100644 index 00000000..0c5ff93f --- /dev/null +++ b/src/ui/src/components/core/content/CoreAnnotatedText.spec.ts @@ -0,0 +1,91 @@ +import { describe, expect, it } from "vitest"; +import BaseMarkdownRaw from "../base/BaseMarkdownRaw.vue"; +import CoreAnnotatedText from "./CoreAnnotatedText.vue"; +import VueDOMPurifyHTML from "vue-dompurify-html"; +import injectionKeys from "@/injectionKeys"; +import { buildMockCore, mockProvides } from "@/tests/mocks"; +import { flushPromises, shallowMount } from "@vue/test-utils"; +import { ref } from "vue"; +import { WdsColor } from "@/wds/tokens"; + +describe("CoreAnnotatedText", async () => { + const text = [ + "# This\n\n", + ["**is**", "Verb", "red"], + " some ", + ["_annotated_", "Adjective"], + ["text", "Noun"], + ". ", + "## title 2\n\n", + "And [here](https://google.com)'s paragraph 2", + ]; + + it("should render in non-markdown mode", async () => { + const { core } = buildMockCore(); + + const wrapper = shallowMount(CoreAnnotatedText, { + global: { + plugins: [VueDOMPurifyHTML], + provide: { + ...mockProvides, + [injectionKeys.core as symbol]: core, + [injectionKeys.isBeingEdited as symbol]: ref(false), + [injectionKeys.evaluatedFields as symbol]: { + text: ref(text), + seed: ref(1), + useMarkdown: ref("no"), + rotateHue: ref("yes"), + referenceColor: ref(WdsColor.Blue5), + copyButtons: ref("yes"), + }, + }, + }, + }); + + await flushPromises(); + + const annotations = wrapper.findAll(".CoreAnnotatedText__annotation"); + expect(annotations).toHaveLength(text.filter(Array.isArray).length); + + // should use the value provided + expect(annotations.at(0).attributes().style).toBe( + "background-color: red;", + ); + + // should generate the color + expect(annotations.at(1).attributes().style).toMatchInlineSnapshot( + `"background-color: rgb(180, 237, 238);"`, + ); + + expect(wrapper.element).toMatchSnapshot(); + }); + + it("should render in markdown mode", async () => { + const { core } = buildMockCore(); + + const wrapper = shallowMount(CoreAnnotatedText, { + global: { + plugins: [VueDOMPurifyHTML], + provide: { + ...mockProvides, + [injectionKeys.core as symbol]: core, + [injectionKeys.isBeingEdited as symbol]: ref(false), + [injectionKeys.evaluatedFields as symbol]: { + text: ref(text), + seed: ref(1), + useMarkdown: ref("yes"), + rotateHue: ref("yes"), + referenceColor: ref(WdsColor.Blue5), + copyButtons: ref("yes"), + }, + }, + }, + }); + + await flushPromises(); + + expect( + wrapper.getComponent(BaseMarkdownRaw).props().rawMarkdown, + ).toMatchSnapshot(); + }); +}); diff --git a/src/ui/src/components/core/content/CoreAnnotatedText.vue b/src/ui/src/components/core/content/CoreAnnotatedText.vue index b89a9bb0..f543e9c0 100644 --- a/src/ui/src/components/core/content/CoreAnnotatedText.vue +++ b/src/ui/src/components/core/content/CoreAnnotatedText.vue @@ -1,27 +1,36 @@