ui: Renames CopyButtonFeedback to CopyButton and use it everywhere (#7834)

* ui: Renames CopyButtonFeedback to CopyButton and use it everywhere

* Uncapitalize output

* Remove the ability to set the contents via an attr, and..

..change the attribute for the string that gets copied to be called
'value' so it feels like HTML
This commit is contained in:
John Cowen 2020-05-12 13:36:03 +01:00 committed by John Cowen
parent df1ae18a41
commit 1ddffb4162
15 changed files with 103 additions and 165 deletions

View File

@ -1,15 +0,0 @@
<FeedbackDialog @type="inline">
<BlockSlot @name="action" as |success error|>
<CopyButton @success={{action success}} @error={{action error}} @clipboardText={{copy}} @title={{concat "Copy " name " to the clipboard"}}>{{#if hasBlock }}{{yield}}{{else}}{{value}}{{/if}}</CopyButton>
</BlockSlot>
<BlockSlot @name="success" as |transition|>
<p class={{transition}}>
Copied {{name}}!
</p>
</BlockSlot>
<BlockSlot @name="error" as |transition|>
<p class={{transition}}>
Sorry, something went wrong!
</p>
</BlockSlot>
</FeedbackDialog>

View File

@ -1,5 +0,0 @@
import Component from '@ember/component';
export default Component.extend({
tagName: '',
});

View File

@ -0,0 +1,32 @@
## CopyButton
```handlebars
{{! inline }}
<CopyButton
@value={{stringToCopy}}
@name="Thing"
/>
<CopyButton
@value={{stringToCopy}}
@name="Thing"
>
Copy me!
</CopyButton>
```
### Arguments
| Argument | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `String` | | The string to be copied to the clipboard on click |
| `name` | `String` | | The 'Name' of the string to be copied. Mainly used for giving feedback to the user |
This component renders a simple button, when clicked copies the value (the `@value` attribute) to the users clipboard. A simple piece of feedback is given to the user in the form of a tooltip. When used inline an empty button is rendered.
### See
- [Component Source Code](./index.js)
- [Template Source Code](./index.hbs)
---

View File

@ -1,2 +1,17 @@
{{! this overriding template makes sure you can add button:empty's }} <FeedbackDialog @type="inline">
{{~yield~}} <BlockSlot @name="action" as |success error|>
<Ref @target={{this}} @name="success" @value={{success}} />
<Ref @target={{this}} @name="error" @value={{error}} />
<button id={{guid}} title={{concat "Copy " name " to the clipboard"}} ...attributes type="button" class="copy-btn" data-clipboard-text={{value}}>{{~yield~}}</button>
</BlockSlot>
<BlockSlot @name="success" as |transition|>
<p class={{transition}}>
Copied {{name}}!
</p>
</BlockSlot>
<BlockSlot @name="error" as |transition|>
<p class={{transition}}>
Sorry, something went wrong!
</p>
</BlockSlot>
</FeedbackDialog>

View File

@ -1,38 +1,29 @@
import Component from '@ember/component'; import Component from '@ember/component';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import WithListeners from 'consul-ui/mixins/with-listeners'; export default Component.extend({
export default Component.extend(WithListeners, {
clipboard: service('clipboard/os'), clipboard: service('clipboard/os'),
tagName: 'button', dom: service('dom'),
classNames: ['copy-btn'], tagName: '',
buttonType: 'button', init: function() {
disabled: false, this._super(...arguments);
error: function() {}, this.guid = this.dom.guid(this);
success: function() {}, this._listeners = this.dom.listeners();
attributeBindings: [ },
'clipboardText:data-clipboard-text', willDestroyElement: function() {
'clipboardTarget:data-clipboard-target', this._super(...arguments);
'clipboardAction:data-clipboard-action', this._listeners.remove();
'buttonType:type', },
'disabled',
'aria-label',
'title',
],
delegateClickEvent: true,
didInsertElement: function() { didInsertElement: function() {
this._super(...arguments); this._super(...arguments);
const clipboard = this.clipboard.execute( const component = this;
this.delegateClickEvent ? `#${this.elementId}` : this.element this._listeners.add(this.clipboard.execute(`#${this.guid}`), {
); success: function() {
['success', 'error'].map(event => { component.success(...arguments);
return this.listen(clipboard, event, () => { },
if (!this.disabled) { error: function() {
this[event](...arguments); component.error(...arguments);
} },
});
}); });
}, },
}); });

View File

@ -37,21 +37,7 @@
<dt>Output</dt> <dt>Output</dt>
<dd> <dd>
<pre><code>{{item.Output}}</code></pre> <pre><code>{{item.Output}}</code></pre>
<FeedbackDialog @type="inline"> <CopyButton @value={{item.Output}} @name="output" />
<BlockSlot @name="action" as |success error|>
<CopyButton @success={{action success}} @error={{action error}} @clipboardText={{item.Output}} @title="copy output to clipboard" />
</BlockSlot>
<BlockSlot @name="success" as |transition|>
<p class={{transition}}>
Copied output!
</p>
</BlockSlot>
<BlockSlot @name="error" as |transition|>
<p class={{transition}}>
Sorry, something went wrong!
</p>
</BlockSlot>
</FeedbackDialog>
</dd> </dd>
{{/if}} {{/if}}
</dl> </dl>

View File

@ -41,11 +41,11 @@
/* this is only for pseudo tooltips be want to avoid */ /* this is only for pseudo tooltips be want to avoid */
/* specifying pseudo in this file */ /* specifying pseudo in this file */
%tooltip::after { %tooltip::after {
bottom: calc(100% - 7px); bottom: calc(100% - 8px);
} }
%tooltip-bottom::before { %tooltip-bottom::before {
bottom: auto; bottom: auto;
top: calc(100% + 7px); top: calc(100% + 8px);
} }
%tooltip-bottom::after { %tooltip-bottom::after {
bottom: -12px; bottom: -12px;

View File

@ -1,27 +1,29 @@
%tooltip-bubble, %tooltip-bubble {
%tooltip-tail {
color: $white; color: $white;
background-color: $gray-500; background-color: $gray-700;
}
%tooltip-tail {
background-color: $transparent;
border-color: $transparent;
border-top-color: $gray-700;
border-bottom-color: $gray-700;
} }
/* borders here are used to draw a triangle in CSS */ /* borders here are used to draw a triangle in CSS */
/* the are not actual borders */ /* they are not actual borders */
%tooltip-tail { %tooltip-tail {
background-color: transparent !important; border-style: solid;
border-left: 9px solid transparent; border-bottom-width: 0;
border-right: 9px solid transparent; border-top-width: 18px;
border-left-width: 9px;
border-top: 18px solid $gray-500; border-right-width: 9px;
} }
%tooltip-bottom::after { %tooltip-bottom::after {
border-top: 0; border-top-width: 0;
border-bottom: 18px solid $gray-500; border-bottom-width: 18px;
} }
%tooltip-bubble { %tooltip-bubble {
border-radius: $decor-radius-200; border-radius: $decor-radius-200;
/* this isn't quite like the values in structure */
/* but this looks closer visually */
/* TODO: try and get this closer to structure */
box-shadow: $decor-elevation-400; box-shadow: $decor-elevation-400;
} }

View File

@ -14,7 +14,7 @@
.actions .with-feedback p::after { .actions .with-feedback p::after {
bottom: auto; bottom: auto;
top: -13px !important; top: -13px !important;
border-bottom: 18px solid $gray-800; border-bottom-width: 18px;
border-top: 0; border-top-width: 0;
} }
} }

View File

@ -18,23 +18,9 @@
</BlockSlot> </BlockSlot>
<BlockSlot @name="actions"> <BlockSlot @name="actions">
{{#if (not create) }} {{#if (not create) }}
<FeedbackDialog @type="inline"> <CopyButton @value={{item.ID}} @name="token ID">
<BlockSlot @name="action" as |success error|>
<CopyButton @success={{action success}} @error={{action error}} @clipboardText={{item.ID}} @title="copy token ID to clipboard">
Copy token ID Copy token ID
</CopyButton> </CopyButton>
</BlockSlot>
<BlockSlot @name="success" as |transition|>
<p class={{transition}}>
Copied token ID!
</p>
</BlockSlot>
<BlockSlot @name="error" as |transition|>
<p class={{transition}}>
Sorry, something went wrong!
</p>
</BlockSlot>
</FeedbackDialog>
<button type="button" {{ action "clone" item }}>Clone token</button> <button type="button" {{ action "clone" item }}>Clone token</button>
<ConfirmationDialog @message="Are you sure you want to use this ACL token?"> <ConfirmationDialog @message="Are you sure you want to use this ACL token?">
<BlockSlot @name="action" as |confirm|> <BlockSlot @name="action" as |confirm|>

View File

@ -70,11 +70,11 @@
<dl> <dl>
<dt>AccessorID</dt> <dt>AccessorID</dt>
<dd> <dd>
<CopyButtonFeedback @title="Copy AccessorID to the clipboard" @copy={{item.AccessorID}} @name="AccessorID" /> {{item.AccessorID}} <CopyButton @value={{item.AccessorID}} @name="AccessorID" /> {{item.AccessorID}}
</dd> </dd>
<dt>Token</dt> <dt>Token</dt>
<dd> <dd>
<CopyButtonFeedback @title="Copy SecretID to the clipboard" @copy={{item.SecretID}} @name="Token" /> <SecretButton>{{item.SecretID}}</SecretButton> <CopyButton @value={{item.SecretID}} @name="Token" /> <SecretButton>{{item.SecretID}}</SecretButton>
</dd> </dd>
{{#if (and (not (token/is-legacy item)) (not create))}} {{#if (and (not (token/is-legacy item)) (not create))}}
<dt>Scope</dt> <dt>Scope</dt>

View File

@ -31,23 +31,9 @@
</BlockSlot> </BlockSlot>
<BlockSlot @name="actions"> <BlockSlot @name="actions">
{{#if (not create) }} {{#if (not create) }}
<FeedbackDialog @type="inline"> <CopyButton @value={{item.ID}} @name="UUID">
<BlockSlot @name="action" as |success error|>
<CopyButton @success={{action success}} @error={{action error}} @clipboardText={{item.ID}} @title="copy UUID to clipboard">
Copy UUID Copy UUID
</CopyButton> </CopyButton>
</BlockSlot>
<BlockSlot @name="success" as |transition|>
<p class={{transition}}>
Copied UUID!
</p>
</BlockSlot>
<BlockSlot @name="error" as |transition|>
<p class={{transition}}>
Sorry, something went wrong!
</p>
</BlockSlot>
</FeedbackDialog>
{{/if}} {{/if}}
</BlockSlot> </BlockSlot>
<BlockSlot @name="content"> <BlockSlot @name="content">

View File

@ -28,23 +28,7 @@
}}/> }}/>
</BlockSlot> </BlockSlot>
<BlockSlot @name="actions"> <BlockSlot @name="actions">
<FeedbackDialog @type="inline"> <CopyButton @value={{item.Address}} @name="Address">{{item.Address}}</CopyButton>
<BlockSlot @name="action" as |success error|>
<CopyButton @success={{action success}} @error={{action error}} @clipboardText={{item.Address}} @title="copy IP address to clipboard">
{{item.Address}}
</CopyButton>
</BlockSlot>
<BlockSlot @name="success" as |transition|>
<p class={{transition}}>
Copied IP Address!
</p>
</BlockSlot>
<BlockSlot @name="error" as |transition|>
<p class={{transition}}>
Sorry, something went wrong!
</p>
</BlockSlot>
</FeedbackDialog>
</BlockSlot> </BlockSlot>
<BlockSlot @name="content"> <BlockSlot @name="content">
{{outlet}} {{outlet}}

View File

@ -32,8 +32,8 @@
{{#if (gt item.LocalBindPort 0)}} {{#if (gt item.LocalBindPort 0)}}
{{#let (concat (or item.LocalBindAddress '127.0.0.1') ':' item.LocalBindPort) as |combinedAddress| }} {{#let (concat (or item.LocalBindAddress '127.0.0.1') ':' item.LocalBindPort) as |combinedAddress| }}
<li class="port"> <li class="port">
<CopyButtonFeedback <CopyButton
@copy={{combinedAddress}} @value={{combinedAddress}}
@name="Address" @name="Address"
/> />
<span>{{combinedAddress}}</span> <span>{{combinedAddress}}</span>
@ -80,7 +80,7 @@
<td class="combined-address"> <td class="combined-address">
{{#if combinedAddress}} {{#if combinedAddress}}
<span data-test-combined-address>{{combinedAddress}}</span> <span data-test-combined-address>{{combinedAddress}}</span>
<CopyButtonFeedback @copy={{combinedAddress}} @name="Combined Address" /> <CopyButton @copy={{combinedAddress}} @name="Combined Address" />
{{else}} {{else}}
{{'-'}} {{'-'}}
{{/if}} {{/if}}

View File

@ -1,24 +0,0 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | copy button feedback', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
await render(hbs`{{copy-button-feedback value='Click Me'}}`);
assert.dom('*').hasText('Click Me');
// Template block usage:
await render(hbs`
{{#copy-button-feedback}}Click Me{{/copy-button-feedback}}
`);
assert.dom('*').hasText('Click Me');
});
});