diff --git a/ui/app/templates/components/copy-button.hbs b/ui/app/templates/components/copy-button.hbs
new file mode 100644
index 00000000000..344c77f3588
--- /dev/null
+++ b/ui/app/templates/components/copy-button.hbs
@@ -0,0 +1,22 @@
+{{#if (eq state 'success')}}
+
+
+ {{x-icon 'copy-success'}}
+
+
+{{else if (eq state 'error')}}
+
+
+ {{x-icon 'alert-triangle'}}
+
+
+{{else}}
+ {{#addon-copy-button
+ class='button is-borderless is-small'
+ clipboardText=clipboardText
+ success=(perform indicateSuccess)
+ error=(action (mut state) 'error')
+ }}
+ {{x-icon 'copy-action'}}
+ {{/addon-copy-button}}
+{{/if}}
\ No newline at end of file
diff --git a/ui/app/templates/components/freestyle/sg-copy-button.hbs b/ui/app/templates/components/freestyle/sg-copy-button.hbs
new file mode 100644
index 00000000000..f3af7cf96c8
--- /dev/null
+++ b/ui/app/templates/components/freestyle/sg-copy-button.hbs
@@ -0,0 +1,6 @@
+{{#freestyle-usage "copy-button" title="Copy Button"}}
+
+ e8c898a0-794b-9063-7a7f-bf0c4a405f83
+ {{copy-button clipboardText="e8c898a0-794b-9063-7a7f-bf0c4a405f83"}}
+
+{{/freestyle-usage}}
\ No newline at end of file
diff --git a/ui/app/templates/freestyle.hbs b/ui/app/templates/freestyle.hbs
index a68982d1f3d..e6ccfd6836e 100644
--- a/ui/app/templates/freestyle.hbs
+++ b/ui/app/templates/freestyle.hbs
@@ -35,6 +35,10 @@
{{freestyle/sg-buttons}}
{{/section.subsection}}
+ {{#section.subsection name="Copy Button"}}
+ {{freestyle/sg-copy-button}}
+ {{/section.subsection}}
+
{{#section.subsection name="Diff Viewer"}}
{{freestyle/sg-diff-viewer}}
{{/section.subsection}}
diff --git a/ui/ember-cli-build.js b/ui/ember-cli-build.js
index 6e2fb01c596..cfa0fb99667 100644
--- a/ui/ember-cli-build.js
+++ b/ui/ember-cli-build.js
@@ -11,7 +11,7 @@ module.exports = function(defaults) {
blacklist: isProd ? ['ember-freestyle'] : [],
},
svg: {
- paths: ['public/images/icons'],
+ paths: ['node_modules/@hashicorp/structure-icons/dist', 'public/images/icons'],
optimize: {
plugins: [{ removeViewBox: false }],
},
diff --git a/ui/package.json b/ui/package.json
index 9f7e9ee36e5..aedf7edd58b 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -29,6 +29,7 @@
"@babel/plugin-proposal-object-rest-spread": "^7.4.3",
"@ember/jquery": "^0.6.0",
"@ember/optional-features": "^0.7.0",
+ "@hashicorp/structure-icons": "^1.3.0",
"broccoli-asset-rev": "^3.0.0",
"bulma": "0.6.1",
"core-js": "^2.4.1",
@@ -44,6 +45,7 @@
"ember-auto-import": "^1.2.21",
"ember-cli": "~3.4.4",
"ember-cli-babel": "^7.1.2",
+ "ember-cli-clipboard": "^0.13.0",
"ember-cli-dependency-checker": "^3.0.0",
"ember-cli-deprecation-workflow": "^1.0.1",
"ember-cli-eslint": "^5.1.0",
@@ -92,6 +94,7 @@
"lodash.intersection": "^4.4.0",
"prettier": "^1.4.4",
"query-string": "^5.0.0",
+ "qunit-dom": "^0.9.0",
"sass": "^1.17.3"
},
"engines": {
diff --git a/ui/tests/integration/components/copy-button-test.js b/ui/tests/integration/components/copy-button-test.js
new file mode 100644
index 00000000000..cdde40beaa3
--- /dev/null
+++ b/ui/tests/integration/components/copy-button-test.js
@@ -0,0 +1,45 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { click, render } from '@ember/test-helpers';
+import hbs from 'htmlbars-inline-precompile';
+
+import sinon from 'sinon';
+
+import { triggerCopyError, triggerCopySuccess } from 'ember-cli-clipboard/test-support';
+
+module('Integration | Component | copy-button', function(hooks) {
+ setupRenderingTest(hooks);
+
+ test('it shows the copy icon by default', async function(assert) {
+ await render(hbs`{{copy-button class='copy-button'}}`);
+
+ assert.dom('.copy-button .icon-is-copy-action').exists();
+ });
+
+ test('it shows the success icon on success and resets afterward', async function(assert) {
+ const clock = sinon.useFakeTimers();
+
+ await render(hbs`{{copy-button class='copy-button'}}`);
+
+ await click('.copy-button button');
+ await triggerCopySuccess('.copy-button button');
+
+ assert.dom('.copy-button .icon-is-copy-success').exists();
+
+ clock.runAll();
+
+ assert.dom('.copy-button .icon-is-copy-success').doesNotExist();
+ assert.dom('.copy-button .icon-is-copy-action').exists();
+
+ clock.restore();
+ });
+
+ test('it shows the error icon on error', async function(assert) {
+ await render(hbs`{{copy-button class='copy-button'}}`);
+
+ await click('.copy-button button');
+ await triggerCopyError('.copy-button button');
+
+ assert.dom('.copy-button .icon-is-alert-triangle').exists();
+ });
+});
diff --git a/ui/yarn.lock b/ui/yarn.lock
index 8d682c2b483..df23003fb17 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -766,6 +766,11 @@
dependencies:
"@glimmer/util" "^0.38.1"
+"@hashicorp/structure-icons@^1.3.0":
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/@hashicorp/structure-icons/-/structure-icons-1.3.0.tgz#1c7c1cb43a1c1aa92b073a7aa7956495ae14c3e0"
+ integrity sha512-wTKpdaAPphEY2kg5QbQTSUlhqLTpBBR1+1dXp4LYTN0PtMSpetyDDDhcSyvKE8i4h2nwPJBRRfeFlE1snaHd7w==
+
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -1614,6 +1619,13 @@ babel-plugin-ember-modules-api-polyfill@^2.6.0, babel-plugin-ember-modules-api-p
dependencies:
ember-rfc176-data "^0.3.8"
+babel-plugin-ember-modules-api-polyfill@^2.9.0:
+ version "2.9.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-ember-modules-api-polyfill/-/babel-plugin-ember-modules-api-polyfill-2.9.0.tgz#8503e7b4192aeb336b00265e6235258ff6b754aa"
+ integrity sha512-c03h50291phJ2gQxo/aIOvFQE2c6glql1A7uagE3XbPXpKVAJOUxtVDjvWG6UAB6BC5ynsJfMWvY0w4TPRKIHQ==
+ dependencies:
+ ember-rfc176-data "^0.3.9"
+
babel-plugin-feature-flags@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/babel-plugin-feature-flags/-/babel-plugin-feature-flags-0.3.1.tgz#9c827cf9a4eb9a19f725ccb239e85cab02036fc1"
@@ -3162,6 +3174,15 @@ cli-width@^2.0.0:
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
+clipboard@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d"
+ integrity sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==
+ dependencies:
+ good-listener "^1.2.2"
+ select "^1.1.2"
+ tiny-emitter "^2.0.0"
+
cliui@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
@@ -3854,6 +3875,11 @@ delayed-stream@~1.0.0:
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
+delegate@^3.1.2:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
+ integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
+
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
@@ -4117,6 +4143,33 @@ ember-cli-babel@^7.1.0, ember-cli-babel@^7.1.2, ember-cli-babel@^7.2.0, ember-cl
ensure-posix-path "^1.0.2"
semver "^5.5.0"
+ember-cli-babel@^7.7.3:
+ version "7.8.0"
+ resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.8.0.tgz#e596500eca0f5a7c9aaee755f803d1542f578acf"
+ integrity sha512-xUBgJQ81fqd7k/KIiGU+pjpoXhrmmRf9pUrqLenNSU5N+yeNFT5a1+w0b+p1F7oBphfXVwuxApdZxrmAHOdA3Q==
+ dependencies:
+ "@babel/core" "^7.0.0"
+ "@babel/plugin-proposal-class-properties" "^7.3.4"
+ "@babel/plugin-proposal-decorators" "^7.3.0"
+ "@babel/plugin-transform-modules-amd" "^7.0.0"
+ "@babel/plugin-transform-runtime" "^7.2.0"
+ "@babel/polyfill" "^7.0.0"
+ "@babel/preset-env" "^7.0.0"
+ "@babel/runtime" "^7.2.0"
+ amd-name-resolver "^1.2.1"
+ babel-plugin-debug-macros "^0.3.0"
+ babel-plugin-ember-modules-api-polyfill "^2.9.0"
+ babel-plugin-module-resolver "^3.1.1"
+ broccoli-babel-transpiler "^7.1.2"
+ broccoli-debug "^0.6.4"
+ broccoli-funnel "^2.0.1"
+ broccoli-source "^1.1.0"
+ clone "^2.1.2"
+ ember-cli-babel-plugin-helpers "^1.1.0"
+ ember-cli-version-checker "^2.1.2"
+ ensure-posix-path "^1.0.2"
+ semver "^5.5.0"
+
ember-cli-broccoli-sane-watcher@^2.1.1:
version "2.2.2"
resolved "https://registry.yarnpkg.com/ember-cli-broccoli-sane-watcher/-/ember-cli-broccoli-sane-watcher-2.2.2.tgz#9bb1b04ddeb2c086aecd8693cbaeca1d88dc160c"
@@ -4128,6 +4181,17 @@ ember-cli-broccoli-sane-watcher@^2.1.1:
rsvp "^3.0.18"
sane "^2.4.1"
+ember-cli-clipboard@^0.13.0:
+ version "0.13.0"
+ resolved "https://registry.yarnpkg.com/ember-cli-clipboard/-/ember-cli-clipboard-0.13.0.tgz#47d3de3aec09987409c162cbff36f966a2c138b7"
+ integrity sha512-AA2J5lliP/DXUFKnQ+r/D3e4xiN3ttlmN8W+8WfZg7K8VeOYlWpMyGcUjmuLa7inLUCMjLbtG6nXc20AQ5OjDg==
+ dependencies:
+ broccoli-funnel "^1.1.0"
+ clipboard "^2.0.0"
+ ember-cli-babel "^7.7.3"
+ ember-cli-htmlbars "^3.0.1"
+ fastboot-transform "^0.1.3"
+
ember-cli-dependency-checker@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/ember-cli-dependency-checker/-/ember-cli-dependency-checker-3.1.0.tgz#b39c6b537a1457d77892edf5ddcfa025cd1401e2"
@@ -4820,6 +4884,11 @@ ember-rfc176-data@^0.3.8:
resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.8.tgz#d46bbef9a0d57c803217b258cfd2e90d8e191848"
integrity sha512-SQup3iG7SDLZNuf7nMMx5BC5truO8AYKRi80gApeQ07NsbuXV4LH75i5eOaxF0i8l9+H1tzv34kGe6rEh0C1NQ==
+ember-rfc176-data@^0.3.9:
+ version "0.3.9"
+ resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.9.tgz#44b6e051ead6c044ea87bd551f402e2cf89a7e3d"
+ integrity sha512-EiTo5YQS0Duy0xp9gCP8ekzv9vxirNi7MnIB4zWs+thtWp/mEKgf5mkiiLU2+oo8C5DuavVHhoPQDmyxh8Io1Q==
+
ember-router-generator@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/ember-router-generator/-/ember-router-generator-1.2.3.tgz#8ed2ca86ff323363120fc14278191e9e8f1315ee"
@@ -5443,6 +5512,14 @@ fast-sourcemap-concat@^1.4.0:
source-map-url "^0.3.0"
sourcemap-validator "^1.1.0"
+fastboot-transform@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/fastboot-transform/-/fastboot-transform-0.1.3.tgz#7dea0b117594afd8772baa6c9b0919644e7f7dcd"
+ integrity sha512-6otygPIJw1ARp1jJb+6KVO56iKBjhO+5x59RSC9qiZTbZRrv+HZAuP00KD3s+nWMvcFDemtdkugki9DNFTTwCQ==
+ dependencies:
+ broccoli-stew "^1.5.0"
+ convert-source-map "^1.5.1"
+
faye-websocket@~0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
@@ -6005,6 +6082,13 @@ globule@^1.0.0:
lodash "~4.17.10"
minimatch "~3.0.2"
+good-listener@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
+ integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=
+ dependencies:
+ delegate "^3.1.2"
+
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6:
version "4.1.15"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
@@ -9002,6 +9086,14 @@ quick-temp@^0.1.2, quick-temp@^0.1.3, quick-temp@^0.1.5, quick-temp@^0.1.8:
rimraf "^2.5.4"
underscore.string "~3.3.4"
+qunit-dom@^0.9.0:
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/qunit-dom/-/qunit-dom-0.9.0.tgz#99d15fffbf06059e543bb93dae8fe0a3f42a27b9"
+ integrity sha512-MvVEoCcf8BHVPD3gXg5GBfNy3JMZ3U3yOha4MB1rFs698EpvxMprOfC+NMEGvOF9Epm6GrsA0BFOdCKHd8Orrw==
+ dependencies:
+ broccoli-funnel "^2.0.2"
+ broccoli-merge-trees "^3.0.1"
+
qunit@~2.6.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/qunit/-/qunit-2.6.2.tgz#551210c5cf857258a4fe39a7fe15d9e14dfef22c"
@@ -9621,6 +9713,11 @@ scss-tokenizer@^0.2.3:
js-base64 "^2.1.8"
source-map "^0.4.2"
+select@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
+ integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
+
semver-compare@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
@@ -10443,6 +10540,11 @@ timers-browserify@^2.0.4:
dependencies:
setimmediate "^1.0.4"
+tiny-emitter@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
+ integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
+
tiny-lr@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-1.1.1.tgz#9fa547412f238fedb068ee295af8b682c98b2aab"