Remove ember-cli-clipboard (#3098)

* chore: 🤖 replace CopyButton usage with Hds::Copy::Button

* chore: 🤖 remove ember-cli-clipboard

* test: 💍 fix test by stubbing writeText

The hds copy button uses the browser clipboard api. The writeText
api can fail if the window is not active. This does not happen in
practice because the window has to be active to be clicked by the
user. But in tests with synthetic triggers this can fail.

Stubbing the writeText method is also how hds tests their
component:
https://github.com/hashicorp/design-system/blob/990981b5741f9a6bf28d100c330b7865ba74e506/showcase/tests/integration/components/hds/copy/button/index-test.gts#L20
This commit is contained in:
hashicc authored and GitHub committed 2025-12-08 12:00:32 -05:00
1 parent b5d33948dc
commit 9204c63b17
6 files changed
+15 -137

No files matched your search

@@ -21,14 +21,11 @@
data-test-code-editor-toolbar-menu-divider
></div>
{{/if}}
<CopyButton
@text={{@copyText}}
<Hds::Copy::Button
@text='Copy code'
@textToCopy={{@copyText}}
@onSuccess={{this.copied}}
class='rose-code-editor-toolbar__copy-button'
data-test-code-editor-toolbar-copy-button
>
<Hds::Icon @name={{this.copyIconType}} @isInline={{true}} />
Copy code
</CopyButton>
/>
</div>
</div>
@@ -4,27 +4,11 @@
*/
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { later } from '@ember/runloop';
export const COPY_ICON_TYPE = 'clipboard-copy';
export const COPIED_ICON_TYPE = 'clipboard-checked';
export default class RoseCodeEditorToolbarComponent extends Component {
@tracked copyIconType = COPY_ICON_TYPE;
@action
copied() {
let { onCopy } = this.args;
let originalIconType = this.copyIconType;
this.copyIconType = COPIED_ICON_TYPE;
if (onCopy) {
onCopy();
}
// eslint-disable-next-line ember/no-runloop
later(() => (this.copyIconType = originalIconType), 1000);
this.args.onCopy?.();
}
}
@@ -67,19 +67,4 @@
height: sizing.rems(xl);
}
}
&__copy-button {
border: none;
background-color: inherit;
cursor: pointer;
color: var(--ui-gray-starker-2);
margin-right: sizing.rems(xs);
margin-left: sizing.rems(s);
height: sizing.rems(xl);
white-space: nowrap;
.hds-icon {
vertical-align: top;
}
}
}
+1 -1
View File
@@ -37,7 +37,6 @@
"codemirror": "5.65.7",
"ember-auto-import": "^2.10.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-clipboard": "^1.2.1",
"ember-cli-htmlbars": "^6.3.0",
"ember-cli-sass": "^11.0.1",
"ember-focus-trap": "^1.0.1",
@@ -87,6 +86,7 @@
"prettier": "^3.3.3",
"qunit": "^2.22.0",
"qunit-dom": "^3.2.1",
"sinon": "^19.0.2",
"stylelint": "^15.11.0",
"stylelint-config-prettier-scss": "^0.0.1",
"stylelint-config-standard-scss": "^11.0.0",
@@ -7,6 +7,7 @@ import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { click, render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import sinon from 'sinon';
module('Integration | Component | rose/code-editor/toolbar', function (hooks) {
setupRenderingTest(hooks);
@@ -43,6 +44,9 @@ module('Integration | Component | rose/code-editor/toolbar', function (hooks) {
});
test('it calls onCopy callback', async function (assert) {
// `writeText` could fail if the document is not in focus which is common during test runs
sinon.stub(window.navigator.clipboard, 'writeText').resolves();
const onCopy = () => {
this.set('called', true);
};
@@ -53,8 +57,9 @@ module('Integration | Component | rose/code-editor/toolbar', function (hooks) {
assert.dom(toolbarSelector).isVisible();
assert.dom(copyButtonSelector).isVisible();
assert.dom(menuDividerSelector).doesNotExist();
await click(copyButtonSelector);
assert.true(this.called);
sinon.restore();
});
});
+3 -96
View File
@@ -565,9 +565,6 @@ importers:
ember-cli-babel:
specifier: ^8.2.0
version: 8.2.0(@babel/core@7.27.1)
ember-cli-clipboard:
specifier: ^1.2.1
version: 1.3.0(@babel/core@7.27.1)(@ember/test-helpers@5.2.2(@babel/core@7.27.1)(@glint/template@1.5.2))(@glint/template@1.5.2)(webpack@5.99.8(esbuild@0.25.9))
ember-cli-htmlbars:
specifier: ^6.3.0
version: 6.3.0
@@ -710,6 +707,9 @@ importers:
qunit-dom:
specifier: ^3.2.1
version: 3.4.0
sinon:
specifier: ^19.0.2
version: 19.0.5
stylelint:
specifier: ^15.11.0
version: 15.11.0(typescript@5.8.3)
@@ -3890,9 +3890,6 @@ packages:
clipboard-polyfill@4.1.1:
resolution: {integrity: sha512-nbvNLrcX0zviek5QHLFRAaLrx8y/s8+RF2stH43tuS+kP5XlHMrcD0UGBWq43Hwp6WuuK7KefRMP56S45ibZkA==}
clipboard@2.0.11:
resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==}
cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
@@ -4485,9 +4482,6 @@ packages:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
delegate@3.2.0:
resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==}
delegates@1.0.0:
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
@@ -4632,10 +4626,6 @@ packages:
qunit:
optional: true
ember-arg-types@1.1.0:
resolution: {integrity: sha512-hWpUz0eiNkWzi3FgHW5QU6LyCDyUlTWwuIROHluEKZoa9m6LJVXbb/EVFgIG3FkAib6a5Ie00WvkXEZFXxh3+A==}
engines: {node: 14.* || >= 16}
ember-assign-helper@0.5.1:
resolution: {integrity: sha512-dXHbwlBTJWVjG7k4dhVrT3Gh4nQt6rC2LjyltuPztIhQ+YcPYHMqAPJRJYLGZu16aPSJbaGF8K+u51i7CLzqlQ==}
@@ -4678,12 +4668,6 @@ packages:
resolution: {integrity: sha512-BbveJCyRvzzkaTH1llLW+MpHe/yzA5zpHOpMIg2vp/3JD9mban9zUm7lphaB0TSpPuMuby9rAhTI8pgXq0ifIA==}
engines: {node: 16.* || >= 18}
ember-cli-clipboard@1.3.0:
resolution: {integrity: sha512-GTX+zzfxhfGyDgk00PcFIEAT063QrpeB3F2UYrKQYZmJiIFFlyriSRw9LrVcXjhEROCVjrHoOVrhcqCATEDAKw==}
engines: {node: 14.* || >= 16}
peerDependencies:
'@ember/test-helpers': '>= 2.9.3'
ember-cli-code-coverage@1.0.3:
resolution: {integrity: sha512-tyWeQ22vxpDmfhIrRCMqZPq9Coppefg19hBgME4yb9Na2qslxCNK0USThigZhesb7hfw2ZgdrKJCrmCVNwkq7g==}
engines: {node: 10.* || >= 12}
@@ -5884,9 +5868,6 @@ packages:
globrex@0.1.2:
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
good-listener@1.2.2:
resolution: {integrity: sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
@@ -6740,10 +6721,6 @@ packages:
longest-streak@2.0.4:
resolution: {integrity: sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==}
loose-envify@1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
@@ -7731,9 +7708,6 @@ packages:
resolution: {integrity: sha512-KYcnXctWUWyVD3W3Ye0ZDuA1N8Szrh85cVCxpG6xYrOk/0CttRtYCmU30nWsUch0NuExQQ63QXvzRE6FLimZmg==}
engines: {node: 10.* || >= 12.*}
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
proper-lockfile@4.1.2:
resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==}
@@ -7814,9 +7788,6 @@ packages:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
read-binary-file-arch@1.0.6:
resolution: {integrity: sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==}
hasBin: true
@@ -8166,9 +8137,6 @@ packages:
resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==}
engines: {node: '>= 10.13.0'}
select@1.1.2:
resolution: {integrity: sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==}
semver-compare@1.0.0:
resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==}
@@ -8756,9 +8724,6 @@ packages:
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
tiny-emitter@2.1.0:
resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==}
tiny-glob@0.2.9:
resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==}
@@ -13011,12 +12976,6 @@ snapshots:
clipboard-polyfill@4.1.1: {}
clipboard@2.0.11:
dependencies:
good-listener: 1.2.2
select: 1.1.2
tiny-emitter: 2.1.0
cliui@8.0.1:
dependencies:
string-width: 4.2.3
@@ -13423,8 +13382,6 @@ snapshots:
delayed-stream@1.0.0: {}
delegate@3.2.0: {}
delegates@1.0.0: {}
depd@1.1.2: {}
@@ -13586,19 +13543,6 @@ snapshots:
- supports-color
- webpack
ember-arg-types@1.1.0(@glint/template@1.5.2)(webpack@5.99.8(esbuild@0.25.9)):
dependencies:
'@embroider/macros': 1.18.0(@glint/template@1.5.2)
ember-auto-import: 2.10.0(@glint/template@1.5.2)(webpack@5.99.8(esbuild@0.25.9))
ember-cli-babel: 7.26.11
ember-cli-typescript: 5.3.0
ember-get-config: 2.1.1(@glint/template@1.5.2)
prop-types: 15.8.1
transitivePeerDependencies:
- '@glint/template'
- supports-color
- webpack
ember-assign-helper@0.5.1:
dependencies:
'@embroider/addon-shim': 1.10.0
@@ -13765,23 +13709,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
ember-cli-clipboard@1.3.0(@babel/core@7.27.1)(@ember/test-helpers@5.2.2(@babel/core@7.27.1)(@glint/template@1.5.2))(@glint/template@1.5.2)(webpack@5.99.8(esbuild@0.25.9)):
dependencies:
'@ember/test-helpers': 5.2.2(@babel/core@7.27.1)(@glint/template@1.5.2)
'@embroider/macros': 1.18.0(@glint/template@1.5.2)
clipboard: 2.0.11
ember-arg-types: 1.1.0(@glint/template@1.5.2)(webpack@5.99.8(esbuild@0.25.9))
ember-auto-import: 2.10.0(@glint/template@1.5.2)(webpack@5.99.8(esbuild@0.25.9))
ember-cli-babel: 7.26.11
ember-cli-htmlbars: 6.3.0
ember-modifier: 4.2.2(@babel/core@7.27.1)
prop-types: 15.8.1
transitivePeerDependencies:
- '@babel/core'
- '@glint/template'
- supports-color
- webpack
ember-cli-code-coverage@1.0.3:
dependencies:
babel-plugin-istanbul: 6.1.1
@@ -15888,10 +15815,6 @@ snapshots:
globrex@0.1.2: {}
good-listener@1.2.2:
dependencies:
delegate: 3.2.0
gopd@1.2.0: {}
got@11.8.6:
@@ -16780,10 +16703,6 @@ snapshots:
longest-streak@2.0.4: {}
loose-envify@1.4.0:
dependencies:
js-tokens: 4.0.0
lower-case@2.0.2:
dependencies:
tslib: 2.8.1
@@ -17788,12 +17707,6 @@ snapshots:
promise.hash.helper@1.0.8: {}
prop-types@15.8.1:
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
react-is: 16.13.1
proper-lockfile@4.1.2:
dependencies:
graceful-fs: 4.2.11
@@ -17886,8 +17799,6 @@ snapshots:
minimist: 1.2.8
strip-json-comments: 2.0.1
react-is@16.13.1: {}
read-binary-file-arch@1.0.6:
dependencies:
debug: 4.4.1
@@ -18304,8 +18215,6 @@ snapshots:
ajv-formats: 2.1.1(ajv@8.17.1)
ajv-keywords: 5.1.0(ajv@8.17.1)
select@1.1.2: {}
semver-compare@1.0.0:
optional: true
@@ -19100,8 +19009,6 @@ snapshots:
through@2.3.8: {}
tiny-emitter@2.1.0: {}
tiny-glob@0.2.9:
dependencies:
globalyzer: 0.1.0