Website: Add copy button to code blocks in articles, handbook, and osquery table documentation pages (#44778)

Closes: https://github.com/fleetdm/confidential/issues/15273

Changes:
- Added a new component `<code-block-copy-buttons>`, a component that
adds copy buttons to code blocks on pages it is added to.
- Added the new component to the handbook, articles, and osquery table
documentation pages


Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
This commit is contained in:
Eric
2026-05-05 16:20:47 -05:00
committed by GitHub
co-authored by Copilot Autofix powered by AI
parent 00d30f28a0
commit ad3ab6b613
7 changed files with 89 additions and 0 deletions
@@ -0,0 +1,42 @@
/**
* <code-block-copy-buttons>
* -----------------------------------------------------------------------------
* A hovering copy button injected into code blocks that copies the text content when clicked.
*
* @type {Component}
*
* -----------------------------------------------------------------------------
*/
parasails.registerComponent('code-block-copy-buttons', {
// ╦ ╦╔╦╗╔╦╗╦
// ╠═╣ ║ ║║║║
// ╩ ╩ ╩ ╩ ╩╩═╝
template: `<span></span>`,
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
// ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣
// ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
mounted: async function(){
// Only add copy buttons if the clipboard method is available.
if(typeof navigator.clipboard !== 'undefined' && typeof navigator.clipboard.writeText === 'function'){
// Prepend all <pre><code> elements with a code-block-copy-button element, and add the has-copy-button class.
$('pre:has(code)').not('.has-copy-button')
.prepend('<button type="button" purpose="code-block-copy-button" aria-label="Copy code" title="Copy code"></button>')
.addClass('has-copy-button');// Note: we set this bootstrap class to correctly position the copy button without needing to adjust the styles of the page it is added to.
// Now add click events to each code-block-copy-button element that copies the text content of the neighboring <code> element.
$('[purpose="code-block-copy-button"]').on('click', async function() {
// Get the text content of the closest <code> element to the copy button.
let code = $(this).closest('pre').find('code').text();
// Add the copied class to the copy button (which replaces the icon with a checkmark).
$(this).addClass('copied');
// Remove the copied class after 2 seconds.
setTimeout(()=>{
$(this).removeClass('copied');
}, 2000);
navigator.clipboard.writeText(code);
});
}
},
});
@@ -0,0 +1,42 @@
/**
* <code-block-copy-buttons>
*
*/
[purpose='code-block-copy-button'] {
position: absolute;
top: 10px;
right: 10px;
border-radius: 8px;
height: 32px;
width: 32px;
background: url('/images/icon-copy-16x16@2x.png');
background-color: #F9FAFC;
background-size: 14px 14px;
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
outline: none;
border: none;
&.copied {
background: url('/images/icon-copy-clicked-checkmark-no-background-32x32@2x.png');
background-size: 32px 32px;
background-repeat: no-repeat;
background-position: center;
border: none;
}
&:hover {
background-color: #E9EEF5;
}
&:focus {
outline: none;
background-color: #E9EEF5;
}
}
pre.has-copy-button {
padding-right: 42px !important;//lesshint-disable-line importantRule
position: relative;
}
+1
View File
@@ -31,6 +31,7 @@
@import 'components/docs-nav-and-search.component.less';
@import 'components/multifield.component.less';
@import 'components/bubble.component.less';
@import 'components/code-block-copy-buttons.component.less';
// Per-page styles
@import 'pages/homepage.less';
+1
View File
@@ -883,6 +883,7 @@
<script src="/js/components/bubble.component.js"></script>
<script src="/js/components/call-to-action.component.js"></script>
<script src="/js/components/cloud-error.component.js"></script>
<script src="/js/components/code-block-copy-buttons.component.js"></script>
<script src="/js/components/docs-nav-and-search.component.js"></script>
<script src="/js/components/js-timestamp.component.js"></script>
<script src="/js/components/logo-carousel.component.js"></script>
+1
View File
@@ -139,5 +139,6 @@
</div>
</div>
</div>
<code-block-copy-buttons></code-block-copy-buttons>
</div>
<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>
+1
View File
@@ -94,5 +94,6 @@
</div>
</modal>
</div>
<code-block-copy-buttons></code-block-copy-buttons>
</div>
<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>
+1
View File
@@ -155,5 +155,6 @@
<p class="mb-0"><img alt="An arrow pointing up" class="d-inline" src="/images/icon-arrow-up-12x13@2x.png">Back to top</p>
</div>
</div>
<code-block-copy-buttons></code-block-copy-buttons>
</div>
<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>