From 62d00722e1a1cce45ffb4d1c8e4d84512e0aa447 Mon Sep 17 00:00:00 2001 From: Jarek Radosz Date: Tue, 17 Sep 2024 13:49:35 +0200 Subject: [PATCH] DEV: Convert highlighted-code to glimmer/gjs (#28914) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the radical change in the implementation doesn't stem from the glimmer migration, but rather the fact that previously the component was single-use – changing any of its args didn't (and couldn't) be reflected because hljs was replacing the nodes so all the ember bookkeeping was gone. Co-authored-by: David Taylor --- .../addon/components/highlighted-code.gjs | 25 ++++++++ .../addon/components/highlighted-code.hbs | 1 - .../addon/components/highlighted-code.js | 11 ---- .../components/highlighted-code-test.gjs | 63 +++++++++++++++++++ .../components/highlighted-code-test.js | 61 ------------------ 5 files changed, 88 insertions(+), 73 deletions(-) create mode 100644 app/assets/javascripts/admin/addon/components/highlighted-code.gjs delete mode 100644 app/assets/javascripts/admin/addon/components/highlighted-code.hbs delete mode 100644 app/assets/javascripts/admin/addon/components/highlighted-code.js create mode 100644 app/assets/javascripts/discourse/tests/integration/components/highlighted-code-test.gjs delete mode 100644 app/assets/javascripts/discourse/tests/integration/components/highlighted-code-test.js diff --git a/app/assets/javascripts/admin/addon/components/highlighted-code.gjs b/app/assets/javascripts/admin/addon/components/highlighted-code.gjs new file mode 100644 index 00000000000..935e8c76a7d --- /dev/null +++ b/app/assets/javascripts/admin/addon/components/highlighted-code.gjs @@ -0,0 +1,25 @@ +import Component from "@glimmer/component"; +import { service } from "@ember/service"; +import { modifier } from "ember-modifier"; +import highlightSyntax from "discourse/lib/highlight-syntax"; + +export default class HighlightedCode extends Component { + @service session; + @service siteSettings; + + highlight = modifier(async (element) => { + const code = document.createElement("code"); + code.classList.add(`lang-${this.args.lang}`); + code.textContent = this.args.code; + + const pre = document.createElement("pre"); + pre.appendChild(code); + + element.replaceChildren(pre); + await highlightSyntax(pre, this.siteSettings, this.session); + }); + + +} diff --git a/app/assets/javascripts/admin/addon/components/highlighted-code.hbs b/app/assets/javascripts/admin/addon/components/highlighted-code.hbs deleted file mode 100644 index bbe184349e4..00000000000 --- a/app/assets/javascripts/admin/addon/components/highlighted-code.hbs +++ /dev/null @@ -1 +0,0 @@ -
{{this.code}}
\ No newline at end of file diff --git a/app/assets/javascripts/admin/addon/components/highlighted-code.js b/app/assets/javascripts/admin/addon/components/highlighted-code.js deleted file mode 100644 index 65208dc6eff..00000000000 --- a/app/assets/javascripts/admin/addon/components/highlighted-code.js +++ /dev/null @@ -1,11 +0,0 @@ -import Component from "@ember/component"; -import { observes, on } from "@ember-decorators/object"; -import highlightSyntax from "discourse/lib/highlight-syntax"; - -export default class HighlightedCode extends Component { - @on("didInsertElement") - @observes("code") - _refresh() { - highlightSyntax(this.element, this.siteSettings, this.session); - } -} diff --git a/app/assets/javascripts/discourse/tests/integration/components/highlighted-code-test.gjs b/app/assets/javascripts/discourse/tests/integration/components/highlighted-code-test.gjs new file mode 100644 index 00000000000..d1c91f1b313 --- /dev/null +++ b/app/assets/javascripts/discourse/tests/integration/components/highlighted-code-test.gjs @@ -0,0 +1,63 @@ +import { tracked } from "@glimmer/tracking"; +import { render, settled } from "@ember/test-helpers"; +import { module, test } from "qunit"; +import { setupRenderingTest } from "discourse/tests/helpers/component-test"; +import HighlightedCode from "admin/components/highlighted-code"; + +module("Integration | Component | highlighted-code", function (hooks) { + setupRenderingTest(hooks); + + test("highlighting code", async function (assert) { + await render(); + + assert.dom("code.lang-ruby.hljs .hljs-keyword").hasText("def"); + }); + + test("large code blocks are not highlighted", async function (assert) { + const longCodeBlock = "puts a\n".repeat(15000); + + await render(); + + assert.dom("pre code").hasText(longCodeBlock); + }); + + test("highlighting code with lang=auto", async function (assert) { + await render(); + + assert.dom("code.hljs").hasNoClass("lang-auto", "lang-auto is removed"); + assert.dom("code.hljs").hasClass(/language-/, "language is detected"); + + assert + .dom("code.hljs") + .hasNoAttribute( + "data-unknown-hljs-lang", + "language is found from language- class" + ); + }); + + test("re-highlights the code when it changes", async function (assert) { + class State { + @tracked code = "def foo; end"; + } + + const testState = new State(); + + await render(); + + assert.dom("code.lang-ruby.hljs .hljs-title").hasText("foo"); + + testState.code = "def bar; end"; + await settled(); + + assert.dom("code.lang-ruby.hljs .hljs-title").hasText("bar"); + }); +}); diff --git a/app/assets/javascripts/discourse/tests/integration/components/highlighted-code-test.js b/app/assets/javascripts/discourse/tests/integration/components/highlighted-code-test.js deleted file mode 100644 index c13009107ff..00000000000 --- a/app/assets/javascripts/discourse/tests/integration/components/highlighted-code-test.js +++ /dev/null @@ -1,61 +0,0 @@ -import { render } from "@ember/test-helpers"; -import { hbs } from "ember-cli-htmlbars"; -import { module, test } from "qunit"; -import highlightSyntax from "discourse/lib/highlight-syntax"; -import { setupRenderingTest } from "discourse/tests/helpers/component-test"; -import { query } from "discourse/tests/helpers/qunit-helpers"; - -const LONG_CODE_BLOCK = "puts a\n".repeat(15000); - -module("Integration | Component | highlighted-code", function (hooks) { - setupRenderingTest(hooks); - - test("highlighting code", async function (assert) { - this.set("code", "def test; end"); - - await render(hbs``); - - assert.strictEqual( - query("code.lang-ruby.hljs .hljs-keyword").innerText.trim(), - "def" - ); - }); - - test("large code blocks are not highlighted", async function (assert) { - this.set("code", LONG_CODE_BLOCK); - - await render(hbs``); - - assert.strictEqual(query("code").innerText.trim(), LONG_CODE_BLOCK.trim()); - }); - - test("highlighting code with lang=auto", async function (assert) { - this.set("code", "def test; end"); - - await render(hbs``); - - const codeElement = query("code.hljs"); - - assert.notOk( - codeElement.classList.contains("lang-auto"), - "lang-auto is removed" - ); - assert.ok( - Array.from(codeElement.classList).some((className) => { - return className.startsWith("language-"); - }), - "language is detected" - ); - - await highlightSyntax( - codeElement.parentElement, //
-      this.siteSettings,
-      this.session
-    );
-
-    assert.notOk(
-      codeElement.dataset.unknownHljsLang,
-      "language is found from language- class"
-    );
-  });
-});