mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 18:48:08 +00:00 
			
		
		
		
	UI: glimmerize masked input (#20431)
* Glimmerize masked-input * Update secret-create-or-update to change masked-input value * Use maskedInput for ssh configure privateKey * Add download button to masked input and v2 secrets. Resolves #6364 * Add changelog
This commit is contained in:
		
							
								
								
									
										3
									
								
								changelog/20431.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/20431.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | ```release-note:improvement | ||||||
|  | ui: Add download button for each secret value in KV v2 | ||||||
|  | ``` | ||||||
| @@ -253,22 +253,16 @@ export default class SecretCreateOrUpdate extends Component { | |||||||
|     this.codemirrorString = this.args.secretData.toJSONString(true); |     this.codemirrorString = this.args.secretData.toJSONString(true); | ||||||
|   } |   } | ||||||
|   @action |   @action | ||||||
|  |   handleMaskedInputChange(secret, index, value) { | ||||||
|  |     const row = { ...secret, value }; | ||||||
|  |     set(this.args.secretData, index, row); | ||||||
|  |     this.handleChange(); | ||||||
|  |   } | ||||||
|  |   @action | ||||||
|   handleChange() { |   handleChange() { | ||||||
|     this.codemirrorString = this.args.secretData.toJSONString(true); |     this.codemirrorString = this.args.secretData.toJSONString(true); | ||||||
|     set(this.args.modelForData, 'secretData', this.args.secretData.toJSON()); |     set(this.args.modelForData, 'secretData', this.args.secretData.toJSON()); | ||||||
|   } |   } | ||||||
|   //submit on shift + enter |  | ||||||
|   @action |  | ||||||
|   handleKeyDown(e) { |  | ||||||
|     e.stopPropagation(); |  | ||||||
|     if (!(e.keyCode === keys.ENTER && e.metaKey)) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     const $form = this.element.querySelector('form'); |  | ||||||
|     if ($form.length) { |  | ||||||
|       $form.submit(); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   @action |   @action | ||||||
|   updateValidationErrorCount(errorCount) { |   updateValidationErrorCount(errorCount) { | ||||||
|     this.validationErrorCount = errorCount; |     this.validationErrorCount = errorCount; | ||||||
|   | |||||||
| @@ -49,14 +49,16 @@ | |||||||
| } | } | ||||||
|  |  | ||||||
| .button.masked-input-toggle, | .button.masked-input-toggle, | ||||||
| .button.copy-button { | .button.copy-button, | ||||||
|  | .button.download-button { | ||||||
|   min-width: $spacing-xl; |   min-width: $spacing-xl; | ||||||
|   border-left: 0; |   border-left: 0; | ||||||
|   color: $grey; |   color: $grey; | ||||||
|   box-shadow: 0 3px 1px 0px rgba(10, 10, 10, 0.12); |   box-shadow: 0 3px 1px 0px rgba(10, 10, 10, 0.12); | ||||||
| } | } | ||||||
|  |  | ||||||
| .button.copy-button { | .button.copy-button, | ||||||
|  | .button.download-button { | ||||||
|   border-radius: 0; |   border-radius: 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -66,7 +68,8 @@ | |||||||
|  |  | ||||||
| .display-only { | .display-only { | ||||||
|   .button.masked-input-toggle, |   .button.masked-input-toggle, | ||||||
|   .button.copy-button { |   .button.copy-button, | ||||||
|  |   .button.download-button { | ||||||
|     background: transparent; |     background: transparent; | ||||||
|     height: auto; |     height: auto; | ||||||
|     line-height: 1rem; |     line-height: 1rem; | ||||||
|   | |||||||
| @@ -46,7 +46,13 @@ | |||||||
|           Private key |           Private key | ||||||
|         </label> |         </label> | ||||||
|         <div class="control"> |         <div class="control"> | ||||||
|           <Textarea name="privateKey" id="privateKey" class="input" @value={{@model.privateKey}} /> |           <MaskedInput | ||||||
|  |             @name="privateKey" | ||||||
|  |             id="privateKey" | ||||||
|  |             class="input" | ||||||
|  |             @value={{@model.privateKey}} | ||||||
|  |             @onChange={{mut @model.privateKey}} | ||||||
|  |           /> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <div class="field"> |       <div class="field"> | ||||||
|   | |||||||
| @@ -75,8 +75,7 @@ | |||||||
|             <div class="column info-table-row-edit"> |             <div class="column info-table-row-edit"> | ||||||
|               <MaskedInput |               <MaskedInput | ||||||
|                 @name={{secret.name}} |                 @name={{secret.name}} | ||||||
|                 @onKeyDown={{action "handleKeyDown"}} |                 @onChange={{fn this.handleMaskedInputChange secret index}} | ||||||
|                 @onChange={{action "handleChange"}} |  | ||||||
|                 @value={{secret.value}} |                 @value={{secret.value}} | ||||||
|                 data-test-secret-value="true" |                 data-test-secret-value="true" | ||||||
|               /> |               /> | ||||||
| @@ -212,8 +211,7 @@ | |||||||
|               <div class="column"> |               <div class="column"> | ||||||
|                 <MaskedInput |                 <MaskedInput | ||||||
|                   @name={{secret.name}} |                   @name={{secret.name}} | ||||||
|                   @onKeyDown={{action "handleKeyDown"}} |                   @onChange={{fn this.handleMaskedInputChange secret index}} | ||||||
|                   @onChange={{action "handleChange"}} |  | ||||||
|                   @value={{secret.value}} |                   @value={{secret.value}} | ||||||
|                   data-test-secret-value="true" |                   data-test-secret-value="true" | ||||||
|                 /> |                 /> | ||||||
|   | |||||||
| @@ -71,8 +71,6 @@ | |||||||
|                       @displayOnly={{true}} |                       @displayOnly={{true}} | ||||||
|                       @allowCopy={{true}} |                       @allowCopy={{true}} | ||||||
|                       @value={{this.wrappedData}} |                       @value={{this.wrappedData}} | ||||||
|                       @success={{action "handleCopySuccess"}} |  | ||||||
|                       @error={{action "handleCopyError"}} |  | ||||||
|                     /> |                     /> | ||||||
|                   {{/if}} |                   {{/if}} | ||||||
|                 </li> |                 </li> | ||||||
|   | |||||||
| @@ -66,7 +66,13 @@ | |||||||
|       {{#each @modelForData.secretKeyAndValue as |secret|}} |       {{#each @modelForData.secretKeyAndValue as |secret|}} | ||||||
|         <InfoTableRow @label={{secret.key}} @value={{secret.value}} @alwaysRender={{true}}> |         <InfoTableRow @label={{secret.key}} @value={{secret.value}} @alwaysRender={{true}}> | ||||||
|           {{#if secret.value}} |           {{#if secret.value}} | ||||||
|             <MaskedInput @value={{secret.value}} @displayOnly={{true}} @allowCopy={{true}} /> |             <MaskedInput | ||||||
|  |               @name={{secret.key}} | ||||||
|  |               @value={{secret.value}} | ||||||
|  |               @displayOnly={{true}} | ||||||
|  |               @allowCopy={{true}} | ||||||
|  |               @allowDownload={{@isV2}} | ||||||
|  |             /> | ||||||
|           {{else}} |           {{else}} | ||||||
|             <Icon @name="minus" /> |             <Icon @name="minus" /> | ||||||
|           {{/if}} |           {{/if}} | ||||||
|   | |||||||
| @@ -216,12 +216,11 @@ | |||||||
|       @subText="{{@attr.options.subText}} Add one item per row." |       @subText="{{@attr.options.subText}} Add one item per row." | ||||||
|     /> |     /> | ||||||
|   {{else if (eq @attr.options.sensitive true)}} |   {{else if (eq @attr.options.sensitive true)}} | ||||||
|     {{! Masked Input }} |  | ||||||
|     <MaskedInput |     <MaskedInput | ||||||
|  |       @name={{@attr.name}} | ||||||
|       @value={{or (get @model this.valuePath) @attr.options.defaultValue}} |       @value={{or (get @model this.valuePath) @attr.options.defaultValue}} | ||||||
|       @allowCopy="true" |       @allowCopy="true" | ||||||
|       @onChange={{this.setAndBroadcast}} |       @onChange={{this.setAndBroadcast}} | ||||||
|       @name={{@attr.name}} |  | ||||||
|       @onKeyUp={{@onKeyUp}} |       @onKeyUp={{@onKeyUp}} | ||||||
|     /> |     /> | ||||||
|     {{#if this.validationError}} |     {{#if this.validationError}} | ||||||
|   | |||||||
							
								
								
									
										58
									
								
								ui/lib/core/addon/components/masked-input.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								ui/lib/core/addon/components/masked-input.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | <div | ||||||
|  |   class="masked-input {{if @displayOnly 'display-only'}} {{if @allowCopy 'allow-copy'}}" | ||||||
|  |   data-test-masked-input | ||||||
|  |   data-test-field | ||||||
|  |   ...attributes | ||||||
|  | > | ||||||
|  |   {{#if @displayOnly}} | ||||||
|  |     {{#if this.showValue}} | ||||||
|  |       <pre class="masked-value display-only is-word-break">{{@value}}</pre> | ||||||
|  |     {{else}} | ||||||
|  |       <pre class="masked-value display-only masked-font">***********</pre> | ||||||
|  |     {{/if}} | ||||||
|  |   {{else}} | ||||||
|  |     <Textarea | ||||||
|  |       id={{this.textareaId}} | ||||||
|  |       name={{@name}} | ||||||
|  |       @value={{@value}} | ||||||
|  |       class="input masked-value {{unless this.showValue 'masked-font'}}" | ||||||
|  |       rows={{1}} | ||||||
|  |       wrap="off" | ||||||
|  |       spellcheck="false" | ||||||
|  |       {{on "change" this.onChange}} | ||||||
|  |       {{on "keyup" (fn this.handleKeyUp @name @value)}} | ||||||
|  |       data-test-textarea | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if @allowCopy}} | ||||||
|  |     <CopyButton | ||||||
|  |       @clipboardText={{@value}} | ||||||
|  |       @success={{action (set-flash-message "Data copied!")}} | ||||||
|  |       class="copy-button button {{if @displayOnly 'is-compact'}}" | ||||||
|  |       data-test-copy-button | ||||||
|  |     > | ||||||
|  |       <Icon @name="clipboard-copy" aria-hidden="Copy value" /> | ||||||
|  |     </CopyButton> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if @allowDownload}} | ||||||
|  |     <DownloadButton | ||||||
|  |       class="button download-button" | ||||||
|  |       @filename={{or @name "secret-value"}} | ||||||
|  |       @data={{@value}} | ||||||
|  |       @stringify={{true}} | ||||||
|  |       aria-label="Download secret value" | ||||||
|  |     > | ||||||
|  |       <Icon @name="download" /> | ||||||
|  |     </DownloadButton> | ||||||
|  |   {{/if}} | ||||||
|  |   <button | ||||||
|  |     onclick={{this.toggleMask}} | ||||||
|  |     type="button" | ||||||
|  |     aria-label={{if this.showValue "mask value" "show value"}} | ||||||
|  |     title={{if this.showValue "mask value" "show value"}} | ||||||
|  |     class="{{if (eq @value '') 'has-text-grey'}} masked-input-toggle button" | ||||||
|  |     data-test-button="toggle-masked" | ||||||
|  |   > | ||||||
|  |     <Icon @name={{if this.showValue "eye" "eye-off"}} /> | ||||||
|  |   </button> | ||||||
|  | </div> | ||||||
| @@ -3,9 +3,12 @@ | |||||||
|  * SPDX-License-Identifier: MPL-2.0 |  * SPDX-License-Identifier: MPL-2.0 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| import Component from '@ember/component'; | import { debug } from '@ember/debug'; | ||||||
|  | import { action } from '@ember/object'; | ||||||
|  | import { guidFor } from '@ember/object/internals'; | ||||||
|  | import Component from '@glimmer/component'; | ||||||
|  | import { tracked } from '@glimmer/tracking'; | ||||||
| import autosize from 'autosize'; | import autosize from 'autosize'; | ||||||
| import layout from '../templates/components/masked-input'; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @module MaskedInput |  * @module MaskedInput | ||||||
| @@ -13,53 +16,50 @@ import layout from '../templates/components/masked-input'; | |||||||
|  * |  * | ||||||
|  * @example |  * @example | ||||||
|  * <MaskedInput |  * <MaskedInput | ||||||
|  *  @value={{attr.options.defaultValue}} |  *  @value={{get @model attr.name}} | ||||||
|  *  @allowCopy={{true}} |  *  @allowCopy={{true}} | ||||||
|  *  @onChange={{action "someAction"}} |  *  @allowDownload={{true}} | ||||||
|  *  @onKeyUp={{action "onKeyUp"}} |  *  @onChange={{this.handleChange}} | ||||||
|  |  *  @onKeyUp={{this.handleKeyUp}} | ||||||
|  * /> |  * /> | ||||||
|  * |  * | ||||||
|  * @param [value] {String} - The value to display in the input. |  * @param value {String} - The value to display in the input. | ||||||
|  * @param [allowCopy=null] {bool} - Whether or not the input should render with a copy button. |  * @param name {String} - The key correlated to the value. Used for the download file name. | ||||||
|  |  * @param [onChange=Callback] {Function|action} - Callback triggered on change, sends new value. Must set the value of @value | ||||||
|  |  * @param [allowCopy=false] {bool} - Whether or not the input should render with a copy button. | ||||||
|  * @param [displayOnly=false] {bool} - Whether or not to display the value as a display only `pre` element or as an input. |  * @param [displayOnly=false] {bool} - Whether or not to display the value as a display only `pre` element or as an input. | ||||||
|  * @param [onChange=Function.prototype] {Function|action} - A function to call when the value of the input changes. |  | ||||||
|  * @param [onKeyUp=Function.prototype] {Function|action} - A function to call whenever on the dom event onkeyup. Generally passed down from higher level parent. |  | ||||||
|  * @param [isCertificate=false] {bool} - If certificate display the label and icons differently. |  | ||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| export default Component.extend({ | export default class MaskedInputComponent extends Component { | ||||||
|   layout, |   textareaId = 'textarea-' + guidFor(this); | ||||||
|   value: null, |   @tracked showValue = false; | ||||||
|   showValue: false, |  | ||||||
|   didInsertElement() { |   constructor() { | ||||||
|     this._super(...arguments); |     super(...arguments); | ||||||
|     autosize(this.element.querySelector('textarea')); |     if (!this.args.onChange && !this.args.displayOnly) { | ||||||
|   }, |       debug('onChange is required for editable Masked Input!'); | ||||||
|   didUpdate() { |     } | ||||||
|     this._super(...arguments); |     this.updateSize(); | ||||||
|     autosize.update(this.element.querySelector('textarea')); |   } | ||||||
|   }, |  | ||||||
|   willDestroyElement() { |   updateSize() { | ||||||
|     this._super(...arguments); |     autosize(document.getElementById(this.textareaId)); | ||||||
|     autosize.destroy(this.element.querySelector('textarea')); |   } | ||||||
|   }, |  | ||||||
|   displayOnly: false, |   @action onChange(evt) { | ||||||
|   onKeyDown() {}, |     const value = evt.target.value; | ||||||
|   onKeyUp() {}, |     if (this.args.onChange) { | ||||||
|   onChange() {}, |       this.args.onChange(value); | ||||||
|   actions: { |     } | ||||||
|     toggleMask() { |   } | ||||||
|       this.toggleProperty('showValue'); |  | ||||||
|     }, |   @action handleKeyUp(name, value) { | ||||||
|     updateValue(e) { |     this.updateSize(); | ||||||
|       const value = e.target.value; |  | ||||||
|       this.set('value', value); |  | ||||||
|       this.onChange(value); |  | ||||||
|     }, |  | ||||||
|     handleKeyUp(name, value) { |  | ||||||
|     if (this.onKeyUp) { |     if (this.onKeyUp) { | ||||||
|       this.onKeyUp(name, value); |       this.onKeyUp(name, value); | ||||||
|     } |     } | ||||||
|     }, |   } | ||||||
|   }, |   @action toggleMask() { | ||||||
| }); |     this.showValue = !this.showValue; | ||||||
|  |   } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,56 +0,0 @@ | |||||||
| <div |  | ||||||
|   class="masked-input {{if this.displayOnly 'display-only'}} {{if this.allowCopy 'allow-copy'}}" |  | ||||||
|   data-test-masked-input |  | ||||||
|   data-test-field |  | ||||||
| > |  | ||||||
|   {{#if this.displayOnly}} |  | ||||||
|     {{#if this.showValue}} |  | ||||||
|       <pre class="masked-value display-only is-word-break">{{this.value}}</pre> |  | ||||||
|     {{else}} |  | ||||||
|       <pre class="masked-value display-only masked-font">***********</pre> |  | ||||||
|     {{/if}} |  | ||||||
|   {{else if this.inputField}} |  | ||||||
|     <input |  | ||||||
|       autocomplete="off" |  | ||||||
|       spellcheck="false" |  | ||||||
|       value={{this.value}} |  | ||||||
|       class="input {{unless this.showValue 'masked-font'}}" |  | ||||||
|       onchange={{action "updateValue"}} |  | ||||||
|       data-test-input |  | ||||||
|     /> |  | ||||||
|   {{else}} |  | ||||||
|     {{! template-lint-disable no-down-event-binding }} |  | ||||||
|     <textarea |  | ||||||
|       class="input masked-value {{unless this.showValue 'masked-font'}}" |  | ||||||
|       rows={{1}} |  | ||||||
|       wrap="off" |  | ||||||
|       onkeydown={{action this.onKeyDown}} |  | ||||||
|       onchange={{action "updateValue"}} |  | ||||||
|       onkeyup={{action (action "handleKeyUp" this.name) value="target.value"}} |  | ||||||
|       value={{this.value}} |  | ||||||
|       spellcheck="false" |  | ||||||
|       data-test-textarea |  | ||||||
|     ></textarea> |  | ||||||
|     {{! template-lint-enable no-down-event-binding }} |  | ||||||
|   {{/if}} |  | ||||||
|   {{#if this.allowCopy}} |  | ||||||
|     <CopyButton |  | ||||||
|       @clipboardText={{this.value}} |  | ||||||
|       @success={{action (set-flash-message "Data copied!")}} |  | ||||||
|       class="copy-button button {{if this.displayOnly 'is-compact'}}" |  | ||||||
|       data-test-copy-button |  | ||||||
|     > |  | ||||||
|       <Icon @name="clipboard-copy" aria-hidden="Copy value" /> |  | ||||||
|     </CopyButton> |  | ||||||
|   {{/if}} |  | ||||||
|   <button |  | ||||||
|     onclick={{action "toggleMask"}} |  | ||||||
|     type="button" |  | ||||||
|     aria-label={{if this.showValue "mask value" "show value"}} |  | ||||||
|     title={{if this.showValue "mask value" "show value"}} |  | ||||||
|     class="{{if (eq this.value '') 'has-text-grey'}} masked-input-toggle button" |  | ||||||
|     data-test-button="toggle-masked" |  | ||||||
|   > |  | ||||||
|     <Icon @name={{if this.showValue "eye" "eye-off"}} /> |  | ||||||
|   </button> |  | ||||||
| </div> |  | ||||||
| @@ -16,68 +16,40 @@ module('Integration | Component | masked input', function (hooks) { | |||||||
|   setupRenderingTest(hooks); |   setupRenderingTest(hooks); | ||||||
|  |  | ||||||
|   test('it renders', async function (assert) { |   test('it renders', async function (assert) { | ||||||
|     await render(hbs`{{masked-input}}`); |     await render(hbs`<MaskedInput />`); | ||||||
|     assert.dom('[data-test-masked-input]').exists('shows expiration beacon'); |     assert.dom('[data-test-masked-input]').exists('shows masked input'); | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   test('it renders a textarea', async function (assert) { |  | ||||||
|     await render(hbs`{{masked-input}}`); |  | ||||||
|  |  | ||||||
|     assert.ok(component.textareaIsPresent); |     assert.ok(component.textareaIsPresent); | ||||||
|  |     assert.dom('[data-test-textarea]').hasClass('masked-font', 'it renders an input with obscure font'); | ||||||
|  |     assert.notOk(component.copyButtonIsPresent, 'does not render copy button by default'); | ||||||
|  |     assert.notOk(component.downloadButtonIsPresent, 'does not render download button by default'); | ||||||
|  |  | ||||||
|  |     await component.toggleMasked(); | ||||||
|  |     assert.dom('.masked-value').doesNotHaveClass('masked-font', 'it unmasks when show button is clicked'); | ||||||
|  |     await component.toggleMasked(); | ||||||
|  |     assert.dom('.masked-value').hasClass('masked-font', 'it remasks text when button is clicked'); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it renders an input with obscure font', async function (assert) { |   test('it renders correctly when displayOnly', async function (assert) { | ||||||
|     await render(hbs`{{masked-input}}`); |  | ||||||
|  |  | ||||||
|     assert.dom('[data-test-textarea]').hasClass('masked-font', 'loading class with correct font'); |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   test('it renders obscure font when displayOnly', async function (assert) { |  | ||||||
|     this.set('value', 'value'); |     this.set('value', 'value'); | ||||||
|     await render(hbs`{{masked-input displayOnly=true value=this.value}}`); |     await render(hbs`<MaskedInput @displayOnly={{true}} @value={{this.value}} />`); | ||||||
|  |  | ||||||
|     assert.dom('.masked-value').hasClass('masked-font', 'loading class with correct font'); |     assert.dom('.masked-value').hasClass('masked-font', 'value has obscured font'); | ||||||
|   }); |     assert.notOk(component.textareaIsPresent, 'it does not render a textarea when displayOnly is true'); | ||||||
|  |  | ||||||
|   test('it does not render a textarea when displayOnly is true', async function (assert) { |  | ||||||
|     await render(hbs`{{masked-input displayOnly=true}}`); |  | ||||||
|  |  | ||||||
|     assert.notOk(component.textareaIsPresent); |  | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it renders a copy button when allowCopy is true', async function (assert) { |   test('it renders a copy button when allowCopy is true', async function (assert) { | ||||||
|     await render(hbs`{{masked-input allowCopy=true}}`); |     await render(hbs`<MaskedInput @allowCopy={{true}} />`); | ||||||
|  |  | ||||||
|     assert.ok(component.copyButtonIsPresent); |     assert.ok(component.copyButtonIsPresent); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it does not render a copy button when allowCopy is false', async function (assert) { |   test('it renders a download button when allowDownload is true', async function (assert) { | ||||||
|     await render(hbs`{{masked-input allowCopy=false}}`); |     await render(hbs`<MaskedInput @allowDownload={{true}} />`); | ||||||
|  |     assert.ok(component.downloadButtonIsPresent); | ||||||
|     assert.notOk(component.copyButtonIsPresent); |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   test('it unmasks text when button is clicked', async function (assert) { |  | ||||||
|     this.set('value', 'value'); |  | ||||||
|     await render(hbs`{{masked-input value=this.value}}`); |  | ||||||
|     await component.toggleMasked(); |  | ||||||
|  |  | ||||||
|     assert.dom('.masked-value').doesNotHaveClass('masked-font'); |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   test('it remasks text when button is clicked', async function (assert) { |  | ||||||
|     this.set('value', 'value'); |  | ||||||
|     await render(hbs`{{masked-input value=this.value}}`); |  | ||||||
|  |  | ||||||
|     await component.toggleMasked(); |  | ||||||
|     await component.toggleMasked(); |  | ||||||
|  |  | ||||||
|     assert.dom('.masked-value').hasClass('masked-font'); |  | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it shortens all outputs when displayOnly and masked', async function (assert) { |   test('it shortens all outputs when displayOnly and masked', async function (assert) { | ||||||
|     this.set('value', '123456789-123456789-123456789'); |     this.set('value', '123456789-123456789-123456789'); | ||||||
|     await render(hbs`{{masked-input value=this.value displayOnly=true}}`); |     await render(hbs`<MaskedInput @value={{this.value}} @displayOnly={{true}} />`); | ||||||
|     const maskedValue = document.querySelector('.masked-value').innerText; |     const maskedValue = document.querySelector('.masked-value').innerText; | ||||||
|     assert.strictEqual(maskedValue.length, 11); |     assert.strictEqual(maskedValue.length, 11); | ||||||
|  |  | ||||||
| @@ -88,7 +60,7 @@ module('Integration | Component | masked input', function (hooks) { | |||||||
|  |  | ||||||
|   test('it does not unmask text on focus', async function (assert) { |   test('it does not unmask text on focus', async function (assert) { | ||||||
|     this.set('value', '123456789-123456789-123456789'); |     this.set('value', '123456789-123456789-123456789'); | ||||||
|     await render(hbs`{{masked-input value=this.value}}`); |     await render(hbs`<MaskedInput @value={{this.value}} />`); | ||||||
|     assert.dom('.masked-value').hasClass('masked-font'); |     assert.dom('.masked-value').hasClass('masked-font'); | ||||||
|     await focus('.masked-value'); |     await focus('.masked-value'); | ||||||
|     assert.dom('.masked-value').hasClass('masked-font'); |     assert.dom('.masked-value').hasClass('masked-font'); | ||||||
| @@ -96,7 +68,7 @@ module('Integration | Component | masked input', function (hooks) { | |||||||
|  |  | ||||||
|   test('it does not remove value on tab', async function (assert) { |   test('it does not remove value on tab', async function (assert) { | ||||||
|     this.set('value', 'hello'); |     this.set('value', 'hello'); | ||||||
|     await render(hbs`{{masked-input value=this.value}}`); |     await render(hbs`<MaskedInput @value={{this.value}} />`); | ||||||
|     await triggerKeyEvent('[data-test-textarea]', 'keydown', 9); |     await triggerKeyEvent('[data-test-textarea]', 'keydown', 9); | ||||||
|     await component.toggleMasked(); |     await component.toggleMasked(); | ||||||
|     const unMaskedValue = document.querySelector('.masked-value').value; |     const unMaskedValue = document.querySelector('.masked-value').value; | ||||||
|   | |||||||
| @@ -8,5 +8,6 @@ import { clickable, isPresent } from 'ember-cli-page-object'; | |||||||
| export default { | export default { | ||||||
|   textareaIsPresent: isPresent('[data-test-textarea]'), |   textareaIsPresent: isPresent('[data-test-textarea]'), | ||||||
|   copyButtonIsPresent: isPresent('[data-test-copy-button]'), |   copyButtonIsPresent: isPresent('[data-test-copy-button]'), | ||||||
|  |   downloadButtonIsPresent: isPresent('[data-test-download-button]'), | ||||||
|   toggleMasked: clickable('[data-test-button="toggle-masked"]'), |   toggleMasked: clickable('[data-test-button="toggle-masked"]'), | ||||||
| }; | }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Chelsea Shaw
					Chelsea Shaw