Merge pull request #241 from hashicorp/copy-button-using-clipboardjs
Copy component using ClipboardJS
This commit is contained in:
8 files changed
+141
-3
No files matched your search
@@ -0,0 +1,22 @@
|
||||
{{#if this.isClipboardLibrarySupported}}
|
||||
<span
|
||||
id={{this.id}}
|
||||
aria-labelledby={{this.copyableButtonId}}
|
||||
class="copyable"
|
||||
{{did-insert this.register}}
|
||||
{{will-destroy this.destroy}}
|
||||
>
|
||||
<span class="copyable-content">
|
||||
{{yield}}
|
||||
</span>
|
||||
|
||||
<Rose::Button
|
||||
id={{this.copyableButtonId}}
|
||||
class="copyable-button"
|
||||
@style="ghost"
|
||||
@iconOnly={{this.icon}}
|
||||
>{{@text}}</Rose::Button>
|
||||
</span>
|
||||
{{else}}
|
||||
{{yield}}
|
||||
{{/if}}
|
||||
@@ -0,0 +1,79 @@
|
||||
import Component from '@glimmer/component';
|
||||
import ClipboardJS from 'clipboard';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { generateComponentID } from 'rose/utilities/component-auto-id';
|
||||
import { task, timeout } from 'ember-concurrency';
|
||||
import { set } from '@ember/object';
|
||||
import { bind } from '@ember/runloop';
|
||||
|
||||
export default class CopyableComponent extends Component {
|
||||
|
||||
// =attributes
|
||||
|
||||
id = generateComponentID();
|
||||
copyableButtonId = `copyable-button-${this.id}`;
|
||||
icon = 'copy-action';
|
||||
|
||||
@tracked text;
|
||||
@tracked clipboard;
|
||||
|
||||
/**
|
||||
* A Ember Concurrency-based task that updates copy icon to a success state
|
||||
* and back after 1 second. This icon flash indicates success on
|
||||
* copy to clipboard action.
|
||||
*
|
||||
* NOTE: tasks are sort of attributes and sort of methods, but they are not
|
||||
* language-level constructs. Thus we annotate this task as if it
|
||||
* is an attribute.
|
||||
* @type {Task}
|
||||
*/
|
||||
@task(function * () {
|
||||
set(this, 'icon', 'copy-success');
|
||||
yield timeout(1000);
|
||||
set(this, 'icon', 'copy-action');
|
||||
}).drop() successIconTimer;
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Checks for ClipboardJS support.
|
||||
* @return {boolean}
|
||||
*/
|
||||
get isClipboardLibrarySupported() {
|
||||
return ClipboardJS.isSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* When copy to clipboard is successful, begin timer to show success icon.
|
||||
*/
|
||||
onCopySuccess(clipboardEvent) {
|
||||
clipboardEvent.clearSelection();
|
||||
this.successIconTimer.perform();
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Initialize ClipboardJS library and register for it's events.
|
||||
*/
|
||||
@action
|
||||
register() {
|
||||
if(!this.clipboard) {
|
||||
this.clipboard = new ClipboardJS(`#${this.copyableButtonId}`, {
|
||||
text: () => this.args.text,
|
||||
});
|
||||
this.clipboard.on('success', bind(this, this.onCopySuccess));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy ClipboardJS library instance and cancel icon timer.
|
||||
*/
|
||||
@action
|
||||
destroy() {
|
||||
this.clipboard?.destroy();
|
||||
this.successIconTimer.cancelAll();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -80,3 +80,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.copyable .copyable-button {
|
||||
padding: 0rem;
|
||||
}
|
||||
@@ -20,6 +20,14 @@ module.exports = function (defaults) {
|
||||
},
|
||||
});
|
||||
|
||||
// Only import when in development or test mode
|
||||
if (app.env.match(/(development)|(test)/i)) {
|
||||
app.import('node_modules/clipboard/dist/clipboard.js');
|
||||
}
|
||||
else {
|
||||
app.import('node_modules/clipboard/dist/clipboard.min.js');
|
||||
}
|
||||
|
||||
// Use `app.import` to add additional libraries to the generated
|
||||
// output files.
|
||||
//
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.6",
|
||||
"@ember/optional-features": "^1.3.0",
|
||||
"@ember/render-modifiers": "^1.0.2",
|
||||
"@glimmer/component": "^1.0.0",
|
||||
"@glimmer/tracking": "^1.0.0",
|
||||
"api": "*",
|
||||
@@ -36,6 +37,7 @@
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-loader": "^8.1.0",
|
||||
"broccoli-asset-rev": "^3.0.0",
|
||||
"clipboard": "^2.0.6",
|
||||
"doctoc": "^1.4.0",
|
||||
"ember-a11y-testing": "^3.0.0",
|
||||
"ember-auto-import": "^1.5.3",
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, find } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | copyable', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
await render(hbs`<Copyable @text="Copy me">text</Copyable>`);
|
||||
assert.ok(find('.copyable'));
|
||||
assert.equal(find('.copyable-content').textContent.trim(), 'text');
|
||||
assert.equal(find('.copyable-button').textContent.trim(), 'Copy me');
|
||||
});
|
||||
});
|
||||
@@ -27,4 +27,4 @@ add-members: Add Members
|
||||
add-hosts: Add Hosts
|
||||
set-password: Set Password
|
||||
change-password: Change Password
|
||||
edit-form: Edit Form
|
||||
edit-form: Edit Form
|
||||
@@ -2543,6 +2543,14 @@
|
||||
ember-cli-babel "^6.16.0"
|
||||
ember-compatibility-helpers "^1.1.1"
|
||||
|
||||
"@ember/render-modifiers@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@ember/render-modifiers/-/render-modifiers-1.0.2.tgz#2e87c48db49d922ce4850d707215caaac60d8444"
|
||||
integrity sha512-6tEnHl5+62NTSAG2mwhGMFPhUrJQjoVqV+slsn+rlTknm2Zik+iwxBQEbwaiQOU1FUYxkS8RWcieovRNMR8inQ==
|
||||
dependencies:
|
||||
ember-cli-babel "^7.10.0"
|
||||
ember-modifier-manager-polyfill "^1.1.0"
|
||||
|
||||
"@ember/test-helpers@^1.7.0", "@ember/test-helpers@^1.7.1":
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@ember/test-helpers/-/test-helpers-1.7.1.tgz#cc22a954b3b46856518f034bd492a74e0482389f"
|
||||
@@ -7307,7 +7315,7 @@ cli-width@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48"
|
||||
integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==
|
||||
|
||||
clipboard@^2.0.0:
|
||||
clipboard@^2.0.0, clipboard@^2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376"
|
||||
integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==
|
||||
@@ -9601,7 +9609,7 @@ ember-maybe-import-regenerator@^0.1.5, ember-maybe-import-regenerator@^0.1.6:
|
||||
ember-cli-babel "^6.0.0-beta.4"
|
||||
regenerator-runtime "^0.9.5"
|
||||
|
||||
ember-modifier-manager-polyfill@^1.2.0:
|
||||
ember-modifier-manager-polyfill@^1.1.0, ember-modifier-manager-polyfill@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-modifier-manager-polyfill/-/ember-modifier-manager-polyfill-1.2.0.tgz#cf4444e11a42ac84f5c8badd85e635df57565dda"
|
||||
integrity sha512-bnaKF1LLKMkBNeDoetvIJ4vhwRPKIIumWr6dbVuW6W6p4QV8ZiO+GdF8J7mxDNlog9CeL9Z/7wam4YS86G8BYA==
|
||||
|
||||
Reference in new issue
Block a user