Copy issue reference to clipboard enhancement
Tags: webdev, opensource, javascript, frontend
Published: 2023-11-09 00:19:55

---
title: Copy issue reference to clipboard enhancement
published: true
description: Adding a button to copy a reference to the current issue into the clipboard.
tags: webdev, opensource, javascript, frontend
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
---

On the [Roundup Issue Tracker mailing list](https://sourceforge.net/p/roundup/mailman/search/?q=Feature+Request+%3A+Copy+Issue+id+and+title+separated+by+%27%3B+%27+to+the+clipboard&mail_list=roundup-users) a user asked for a mechanism to create a reference for the current issue and copy it to the clipboard.


The request was for the reference to look like:
```
  issue2345: title of issue
```
In Roundup, object designators like `issue2345` are automatically hyperlinked to the corresponding object.

The code I recommended used a button to trigger this operation. It also used the [clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API). When clicking on the button (or hitting `space` or `return` while focused on the button) the reference format above will be copied to the clipboard. Triggering the clipboard API must be done by a user interaction. For example: activating a button. If the browser does not support the clipboard API, a simple `alert()` is shown.

In addition to copying to the clipboard, the user gets feedback when the text of the button changes to "Reference copied". Then it resets to the original message after 2 seconds. Since clicking the button multiple times is idempotent, there is no sense in disabling or debouncing the button.

Note, this is unlikely to be a11y compliant as I don't think the change of button text is announced. If anybody has some ideas on how to make this more compliant, leave them in the comments. Maybe using `aria-live="polite"` or `"assertive"` on the `button` element would do the trick?

Here is the code I suggested:
```html
<button id="copyreference" type="button"
            tal:condition="context/is_edit_ok">
   Copy Reference
</button>

<script tal:attributes="nonce request/client/client_nonce">
  (function () {
    "use strict";

    let crb = document.querySelector("#copyreference");
    if ( ! crb ) return
    crb.addEventListener("click", async (e) => {
        e.preventDefault()
        if ( ! navigator.clipboard ) {
            alert("Clipboard is not available")
            return
        }

        let issueDesignator = new URL(document.URL)
            .pathname
            .split("/")
            .pop()
        let issueTitleText = document.querySelector("#title").value;

        await navigator.clipboard.writeText(`${issueDesignator}: ${issueTitleText}`)

        let originalCrbInnerText = crb.innerText
        crb.innerText = "Reference copied"
        setTimeout(() => {
            crb.innerText = originalCrbInnerText}, 2000)
        }
    );
  })();
```

The [flexibility of Roundup](https://www.roundup-tracker.org/docs/features.html) allows the administrator to rewrite all of the HTML used in the web interface. As a result, I based my guess of CSS selectors on the [HTML generated by the classic `issue.item.html` TAL template](https://sourceforge.net/p/roundup/code/ci/default/tree/share/roundup/templates/classic/html/issue.item.html).

The button could be placed anywhere on the page and the script (including the nonce required by the [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)) would be placed anywhere after the button. Probably at the end of the page so it doesn't block rendering.

The classic structure of the issue display page for users with editing capability included an input with the id `title`. This is retrieved using the id and included in the string written to the clipboard.

A user without editing capability for the issue (or without editing capability for the title attribute of an issue) does not have an input with `id="title"`.
In these cases, the admin would have to modify the `issue.item.html` template to add an id of title to the enclosing element. Then the code above would be modified to replace:

```js
let issueTitleText = document.querySelector("#title").value;
```
with:
```js
 let issueTitle = document.querySelector("input#title");
   if ( ! issueTitle ) {
     issueTitle = document.querySelector("#title");
     issueTitleText = issueTitle.innerText
   } else {
     issueTitleText = issueTitle.value
   }
```

In my original example, the button element is generated only if the user can edit the issue. Removing the `tal:condition` attribute would always display the button.

There is nothing with an id or CSS selector that contains the object designator. I use the final element of the path of the URL to get the designator.

I chose to use the [IIFE](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression) code structure. This allows me to [use the early return pattern](https://gomakethings.com/the-early-return-pattern-in-javascript/) if the button is not found. If anybody knows how to do the equivalent of an early return without an IIFE or other function in a script tag, leave your trick in the comments.

If you use the Roundup Issue Tracker and have your own enhancements, feel free to mention them in the comments.

{% embed https://www.roundup-tracker.org/?ref=dev_to %}