add copy button to kv secrets masked input (#5627)

This commit is contained in:
madalynrose
2018-10-31 13:13:57 -04:00
committed by GitHub
parent 51eb1406b6
commit abd6eb88c2
6 changed files with 75 additions and 44 deletions

View File

@@ -1,9 +1,11 @@
.masked-input { .masked-input {
position: relative; display: flex;
align-items: center;
} }
.masked-input .masked-value { .masked-input.masked.display-only,
padding-left: 2.5rem; .masked-input:not(.masked) {
align-items: start;
} }
// we want to style the boxes the same everywhere so they // we want to style the boxes the same everywhere so they
@@ -11,14 +13,17 @@
.masked-input.masked .masked-value { .masked-input.masked .masked-value {
font-size: 9px; font-size: 9px;
font-family: $family-primary; font-family: $family-primary;
}
.masked-input.masked .masked-value {
line-height: 2.5; line-height: 2.5;
} }
.masked-input.display-only .masked-value {
order: 1;
}
// aligns the boxes on the input page // aligns the boxes on the input page
.masked-input.masked:not(.display-only) .masked-value { .masked-input.masked:not(.display-only) .masked-value {
line-height: 3; line-height: 3;
border-radius: $radius 0 0 $radius;
} }
//override bulma's pre styling //override bulma's pre styling
@@ -27,48 +32,52 @@
font-size: 1rem; font-size: 1rem;
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
padding-left: $spacing-s;
} }
.masked-input-toggle { .button.masked-input-toggle,
background: transparent; .button.copy-button {
position: absolute; min-width: $spacing-xl;
height: auto; border-left: 0;
top: $size-6/4; border-radius: 0 $radius $radius 0;
bottom: $size-6/4; color: $grey;
left: 1px; }
line-height: 1rem;
min-width: 0;
max-height: 2rem;
padding: 0 $size-8;
z-index: 100;
border: 0;
box-shadow: none;
color: $blue;
&:active, .display-only {
&.is-active, .button.masked-input-toggle,
&:focus, .button.copy-button {
&.is-focused, background: transparent;
&:hover, height: auto;
&:focus:not(:active) { line-height: 1rem;
color: $blue; min-width: $spacing-l;
z-index: 100;
border: 0; border: 0;
box-shadow: none; box-shadow: none;
color: $grey-light;
padding-left: 0;
padding-right: 0;
&:active,
&.is-active,
&:focus,
&.is-focused,
&:hover,
&:focus:not(:active) {
color: $blue;
border: 0;
box-shadow: none;
}
} }
} }
.masked-input.display-only .masked-input-toggle { .masked-input.masked .masked-value {
top: 0; color: $grey-light;
font-size: 0.5rem; }
height: 1rem;
padding-left: 0; .masked-input:not(.masked) .masked-input-toggle {
color: $blue;
} }
.masked-input .input:focus + .masked-input-toggle { .masked-input .input:focus + .masked-input-toggle {
background: rgba($white, 0.95); background: rgba($white, 0.95);
} }
.masked-input.masked .masked-value,
.masked-input.masked .masked-input-toggle {
color: $grey-light;
}

View File

@@ -1,4 +1,4 @@
<div class="masked-input {{if shouldObscure "masked"}} {{if displayOnly "display-only"}}" data-test-masked-input> <div class="masked-input {{if shouldObscure "masked"}} {{if displayOnly "display-only"}} {{if allowCopy "allow-copy"}}" data-test-masked-input>
{{#if displayOnly}} {{#if displayOnly}}
<pre class="masked-value display-only is-word-break">{{displayValue}}</pre> <pre class="masked-value display-only is-word-break">{{displayValue}}</pre>
{{else}} {{else}}
@@ -15,7 +15,16 @@
data-test-textarea data-test-textarea
/> />
{{/if}} {{/if}}
<button {{action "toggleMask"}} class="{{if (eq value "") "has-text-grey"}} masked-input-toggle button is-compact" data-test-button> {{#if allowCopy}}
<CopyButton
@clipboardText={{value}}
@class="copy-button button is-compact"
data-test-copy-button
>
<ICon @glyph="copy" aria-hidden="true" @size=16 />
</CopyButton>
{{/if}}
<button {{action "toggleMask"}} class="{{if (eq value "") "has-text-grey"}} masked-input-toggle button {{if displayOnly "is-compact"}}" data-test-button>
{{i-con glyph=(if shouldObscure "hidden" "visible") aria-hidden="true" size=16}} {{i-con glyph=(if shouldObscure "hidden" "visible") aria-hidden="true" size=16}}
</button> </button>
</div> </div>

View File

@@ -52,7 +52,7 @@
@onChange={{@editActions.handleChange}} @onChange={{@editActions.handleChange}}
@value={{secret.value}} @value={{secret.value}}
data-test-secret-value="true" data-test-secret-value="true"
/> />
</div> </div>
<div class="column is-narrow info-table-row-edit"> <div class="column is-narrow info-table-row-edit">
{{#if (eq @secretData.length (inc index))}} {{#if (eq @secretData.length (inc index))}}
@@ -66,7 +66,7 @@
{{action @editActions.deleteRow secret.name}} {{action @editActions.deleteRow secret.name}}
aria-label="Delete row" aria-label="Delete row"
> >
<ICon <ICon
@size="22" @size="22"
@glyph="trash-a" @glyph="trash-a"
@excludeIconClass={{true}} @excludeIconClass={{true}}
@@ -78,4 +78,4 @@
</div> </div>
{{/each}} {{/each}}
</div> </div>
{{/if}} {{/if}}

View File

@@ -27,7 +27,7 @@
</div> </div>
{{#each-in modelForData.secretData as |key value|}} {{#each-in modelForData.secretData as |key value|}}
{{#info-table-row label=key value=value alwaysRender=true}} {{#info-table-row label=key value=value alwaysRender=true}}
{{masked-input value=value displayOnly=true}} {{masked-input value=value displayOnly=true allowCopy=true}}
{{/info-table-row}} {{/info-table-row}}
{{/each-in}} {{/each-in}}
{{/if}} {{/if}}

View File

@@ -40,6 +40,18 @@ module('Integration | Component | masked input', function(hooks) {
assert.notOk(component.textareaIsPresent); assert.notOk(component.textareaIsPresent);
}); });
test('it renders a copy button when allowCopy is true', async function(assert) {
await render(hbs`{{masked-input allowCopy=true}}`);
assert.ok(component.copyButtonIsPresent);
});
test('it does not render a copy button when allowCopy is false', async function(assert) {
await render(hbs`{{masked-input allowCopy=false}}`);
assert.notOk(component.copyButtonIsPresent);
});
test('it unmasks text on focus', async function(assert) { test('it unmasks text on focus', async function(assert) {
this.set('value', 'value'); this.set('value', 'value');
await render(hbs`{{masked-input value=value}}`); await render(hbs`{{masked-input value=value}}`);

View File

@@ -5,6 +5,7 @@ export default {
wrapperClass: attribute('class', '[data-test-masked-input]'), wrapperClass: attribute('class', '[data-test-masked-input]'),
enterText: fillable('[data-test-textarea]'), enterText: fillable('[data-test-textarea]'),
textareaIsPresent: isPresent('[data-test-textarea]'), textareaIsPresent: isPresent('[data-test-textarea]'),
copyButtonIsPresent: isPresent('[data-test-copy-button]'),
toggleMasked: clickable('[data-test-button]'), toggleMasked: clickable('[data-test-button]'),
async focusField() { async focusField() {
return focus('[data-test-textarea]'); return focus('[data-test-textarea]');