From 55675b8eed209ec7fb6578a1fd89e67b65b44612 Mon Sep 17 00:00:00 2001 From: mikespudwebb Date: Thu, 1 Oct 2026 20:28:06 -0400 Subject: [PATCH] feat(code snippet): allow code to be rendered as is * Add `skipTrim` property to code-snippet component. When true, the code will not call trim() on the code block. * Update caniuse to latest version. --- .../src/code-snippet/code-snippet.spec.ts | 29 ++++++++++++++- .../src/code-snippet/code-snippet.stories.js | 37 +++++++++++++------ .../src/code-snippet/code-snippet.ts | 9 +++-- package-lock.json | 17 +++++---- 4 files changed, 69 insertions(+), 23 deletions(-) diff --git a/libs/components/src/code-snippet/code-snippet.spec.ts b/libs/components/src/code-snippet/code-snippet.spec.ts index 5680acec03..7f13ab5262 100644 --- a/libs/components/src/code-snippet/code-snippet.spec.ts +++ b/libs/components/src/code-snippet/code-snippet.spec.ts @@ -1,11 +1,38 @@ /** * @vitest-environment jsdom */ -import { it, describe, expect } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { CovalentCodeSnippet } from './code-snippet'; describe('Code snippet', () => { it('should work', () => { expect(new CovalentCodeSnippet()).toBeDefined(); }); + + it('should trim surrounding whitespace by default', async () => { + const snippet = new CovalentCodeSnippet(); + (snippet as any)._code = ' const answer = 42; '; + document.body.appendChild(snippet); + + await snippet.updateComplete; + + const code = snippet.shadowRoot?.querySelector('code'); + expect(code?.textContent).toBe('const answer = 42;'); + + document.body.removeChild(snippet); + }); + + it('should preserve surrounding whitespace when skipTrim is true', async () => { + const snippet = new CovalentCodeSnippet(); + (snippet as any)._code = ' const answer = 42; '; + snippet.skipTrim = true; + document.body.appendChild(snippet); + + await snippet.updateComplete; + + const code = snippet.shadowRoot?.querySelector('code'); + expect(code?.textContent).toBe(' const answer = 42; '); + + document.body.removeChild(snippet); + }); }); diff --git a/libs/components/src/code-snippet/code-snippet.stories.js b/libs/components/src/code-snippet/code-snippet.stories.js index a054936d44..de385ebd82 100644 --- a/libs/components/src/code-snippet/code-snippet.stories.js +++ b/libs/components/src/code-snippet/code-snippet.stories.js @@ -33,11 +33,12 @@ SELECT * FROM load_to_teradata ( `; const codeSnippetTemplate = ({ - inline, content, - hideHeader, label, language, + hideHeader, + inline, + skipTrim, maxHeight, }) => { document.addEventListener( @@ -60,17 +61,19 @@ const codeSnippetTemplate = ({ }, { once: true }, ); + // do not add extra spaces in the HTML code return ` - - - ${content} - `; + label="${label}" + maxHeight="${maxHeight}" + language="${language}" + ${hideHeader ? 'hideHeader' : ''} + ${inline ? 'inline' : ''} + ${skipTrim ? 'skipTrim' : ''} + >${content}`; }; const dialogTemplate = (args) => { @@ -101,7 +104,8 @@ export default { args: { hideHeader: false, inline: false, - label: 'Example.sql', + skipTrim: false, + label: 'Explain plan', language: 'sql', content: sqlContent, maxHeight: 0, @@ -129,6 +133,15 @@ export const HiddenHeader = { }, }; +export const SkipTrim = { + args: { + content: + ' 1) First, we lock TABLE_NAME in DB_NAME for\n\n access, and we lock TABLE_NAME2 in DB_NAME2 for access.\n\n 2) Next, we do an all-AMPs JOIN step in DB_NAME from\n\n TABLE_NAME by way of an all-rows scan with no\n', + language: 'plaintext', + skipTrim: true, + }, +}; + export const Dialog = { render: dialogTemplate, args: { diff --git a/libs/components/src/code-snippet/code-snippet.ts b/libs/components/src/code-snippet/code-snippet.ts index eeb2bdc125..cfd9dbc6d7 100644 --- a/libs/components/src/code-snippet/code-snippet.ts +++ b/libs/components/src/code-snippet/code-snippet.ts @@ -1,8 +1,8 @@ -import { css, LitElement, html, unsafeCSS } from 'lit'; +import hljs from 'highlight.js/lib/common'; +import { css, html, LitElement, unsafeCSS } from 'lit'; import { customElement, property, queryAssignedNodes } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import styles from './code-snippet.scss?inline'; -import hljs from 'highlight.js/lib/common'; declare global { interface HTMLElementTagNameMap { @@ -30,6 +30,9 @@ export class CovalentCodeSnippet extends LitElement { @property({ type: Boolean, reflect: true }) hideHeader = false; + @property({ type: Boolean, reflect: true }) + skipTrim = false; + @queryAssignedNodes({}) _codeItems!: Array; @@ -68,7 +71,7 @@ export class CovalentCodeSnippet extends LitElement { }; classes[`language-${this.language}`] = true; const container = document.createElement('div'); - container.innerHTML = this._code.trim(); + container.innerHTML = this.skipTrim ? this._code : this._code.trim(); let styleHeight; if (this.maxHeight && this.maxHeight > 0) { diff --git a/package-lock.json b/package-lock.json index c76411c696..0300be0da8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25548,12 +25548,15 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "version": "2.11.27", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.27.tgz", + "integrity": "sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ==", "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/basic-auth": { @@ -26426,9 +26429,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001766", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", - "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", + "version": "1.0.30001814", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001814.tgz", + "integrity": "sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA==", "funding": [ { "type": "opencollective",