Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ Import the optional `<micro-lighter>` custom element:
| `language` | Language to use. Overrides language metadata on `<pre>` or `<code>` |
| `controls="copy"` | Adds a copy button |
| `line-numbers` | Adds a line number gutter without changing copied code |
| `copy-label` | Text for the copy button. Defaults to `Copy` |
| `copied-label` | Text the copy button shows after a click. Defaults to `Copied` |
| `copied-announcement` | Message announced to screen readers after a click. Defaults to `Copied to clipboard` |

Style the controls with `::part(copy-button)` and `::part(line-numbers)`.

Expand Down
37 changes: 33 additions & 4 deletions src/micro-lighter-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,14 @@ template.innerHTML = `
`;

export class MicroLighter extends HTMLElement {
static observedAttributes = ["controls", "language", "line-numbers"];
static observedAttributes = [
"controls",
"copied-announcement",
"copied-label",
"copy-label",
"language",
"line-numbers"
];

/** @type {HTMLButtonElement} */
#button;
Expand All @@ -109,6 +116,10 @@ export class MicroLighter extends HTMLElement {
/** @type {ReturnType<typeof setTimeout> | undefined} */
#resetCopyLabel;
#resizeObserver;
/** True while the button is showing its post-copy label, so `#update` does
* not overwrite it back to the copy label while the reset timer is still
* pending. */
#showingCopied = false;

constructor() {
super();
Expand Down Expand Up @@ -151,15 +162,32 @@ export class MicroLighter extends HTMLElement {
if (!this.#codeBlock) return;

await navigator.clipboard.writeText(this.#codeBlock.textContent ?? "");
this.#button.textContent = "Copied";
this.#button.ariaNotify?.("Copied to clipboard");
this.#showingCopied = true;
this.#button.textContent = this.#copiedLabel();
this.#button.ariaNotify?.(this.#copiedAnnouncement());

clearTimeout(this.#resetCopyLabel);
this.#resetCopyLabel = setTimeout(() => {
this.#button.textContent = "Copy";
this.#showingCopied = false;
this.#button.textContent = this.#copyLabel();
}, 2000);
}

/** @returns {string} */
#copyLabel() {
return this.getAttribute("copy-label") || "Copy";
}

/** @returns {string} */
#copiedLabel() {
return this.getAttribute("copied-label") || "Copied";
}

/** @returns {string} */
#copiedAnnouncement() {
return this.getAttribute("copied-announcement") || "Copied to clipboard";
}

#update() {
const codeBlock = /** @type {HTMLElement | null} */ (
this.querySelector(":scope > pre > code")
Expand All @@ -178,6 +206,7 @@ export class MicroLighter extends HTMLElement {
}

this.#button.hidden = !this.#hasControl("copy") || !codeBlock;
if (!this.#showingCopied) this.#button.textContent = this.#copyLabel();

const language = this.getAttribute("language");
if (codeBlock && language) {
Expand Down
18 changes: 18 additions & 0 deletions test/custom-element.spec.pw.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ const secondLine = false;</code></pre>
<micro-lighter id="many-lines" line-numbers>
<pre><code></code></pre>
</micro-lighter>
<micro-lighter
id="localized"
controls="copy"
copy-label="Copier"
copied-label="Copié"
copied-announcement="Copié dans le presse-papiers"
>
<pre><code>const localized = true;</code></pre>
</micro-lighter>
`);
document.querySelector("#many-lines code").textContent = Array.from(
{ length: 1000 },
Expand Down Expand Up @@ -149,6 +158,15 @@ const secondLine = false;</code></pre>
});
await expect(page.locator("#inferred button")).toBeHidden();

// copy-label / copied-label / copied-announcement let a page translate the
// copy button instead of being stuck with the hardcoded English strings.
await expect(page.locator("#localized button")).toHaveText("Copier");
await page.locator("#localized button").click();
await expect(page.locator("#localized button")).toHaveText("Copié");
expect(await page.evaluate(() => window.notifications)).toContain(
"Copié dans le presse-papiers"
);

expect(await page.locator("#inferred").evaluate(element => {
const gutter = element.shadowRoot.querySelector(".line-numbers");
const pre = element.querySelector("pre");
Expand Down