From 416d8bde5d1ecc3f9b88280535053591c1ff94e3 Mon Sep 17 00:00:00 2001 From: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:25:55 -0800 Subject: [PATCH] UI: Create enable input component (#24427) * enable input component * add more stars * update css comments * Update ui/app/styles/helper-classes/flexbox-and-grid.scss * make attrOptions optional * add subtext to textfile * add docLink arg to form field textfile * update form field test * add test * add comment * update jsdoc * remove unused class * Update ui/tests/integration/components/enable-input-test.js Co-authored-by: Jordan Reimer --------- Co-authored-by: Jordan Reimer --- .../helper-classes/flexbox-and-grid.scss | 62 +++++++++++-------- ui/lib/core/addon/components/enable-input.hbs | 27 ++++++++ ui/lib/core/addon/components/enable-input.ts | 43 +++++++++++++ ui/lib/core/addon/components/form-field.hbs | 2 + ui/lib/core/addon/components/text-file.hbs | 28 ++++++++- ui/lib/core/app/components/enable-input.js | 6 ++ .../components/enable-input-test.js | 57 +++++++++++++++++ .../integration/components/form-field-test.js | 15 ++++- 8 files changed, 211 insertions(+), 29 deletions(-) create mode 100644 ui/lib/core/addon/components/enable-input.hbs create mode 100644 ui/lib/core/addon/components/enable-input.ts create mode 100644 ui/lib/core/app/components/enable-input.js create mode 100644 ui/tests/integration/components/enable-input-test.js diff --git a/ui/app/styles/helper-classes/flexbox-and-grid.scss b/ui/app/styles/helper-classes/flexbox-and-grid.scss index 659552f3c4..868552581f 100644 --- a/ui/app/styles/helper-classes/flexbox-and-grid.scss +++ b/ui/app/styles/helper-classes/flexbox-and-grid.scss @@ -6,6 +6,32 @@ /* Helpers that define anything with the CSS flexbox or CSS grid. */ /* Flexbox helpers */ + +// FLEX CONTAINER (child helpers at end of file) +// new flex classes, these do not use !important +.flex { + display: flex; + + // direction + &.row-wrap { + flex-flow: row wrap; + } + + // alignment + &.space-between { + justify-content: space-between; + } + + &.row-gap-8 { + row-gap: $spacing-8; + } + + &.column-gap-16 { + column-gap: $spacing-16; + } +} + +// avoid !important flex classes below .is-flex { display: flex !important; } @@ -104,28 +130,6 @@ flex: 50%; } -// moving away from !important, fresh flex styles below -.flex { - display: flex; - // direction - &.row-wrap { - flex-flow: row wrap; - } - - // alignment - &.space-between { - justify-content: space-between; - } - - &.row-gap-8 { - row-gap: $spacing-8; - } - - &.column-gap-16 { - column-gap: $spacing-16; - } -} - /* Flex Responsive */ @media screen and (min-width: 769px), print { .is-flex-v-centered-tablet { @@ -163,10 +167,6 @@ grid-template-columns: repeat(3, 1fr); } -.align-self-center { - align-self: center; -} - .is-medium-height { height: 125px; } @@ -178,3 +178,13 @@ .grid-align-items-start { align-items: start; } + +// CHILD ELEMENT HELPERS + +.align-self-center { + align-self: center; +} + +.align-self-end { + align-self: end; +} diff --git a/ui/lib/core/addon/components/enable-input.hbs b/ui/lib/core/addon/components/enable-input.hbs new file mode 100644 index 0000000000..370d0ec69d --- /dev/null +++ b/ui/lib/core/addon/components/enable-input.hbs @@ -0,0 +1,27 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: BUSL-1.1 +~}} + +{{#if this.enable}} + {{yield}} +{{else}} +
+
+ {{#if @attr}} + + {{else}} + + {{/if}} +
+
+ +
+
+{{/if}} \ No newline at end of file diff --git a/ui/lib/core/addon/components/enable-input.ts b/ui/lib/core/addon/components/enable-input.ts new file mode 100644 index 0000000000..b7b36de5d0 --- /dev/null +++ b/ui/lib/core/addon/components/enable-input.ts @@ -0,0 +1,43 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; + +interface Args { + attr?: AttrData; +} +interface AttrData { + name: string; // required if @attr is passed + options?: { + label?: string; + helpText?: string; + subText?: string; + possibleValues?: string[]; + }; +} + +/** + * @module EnableInput + * EnableInput components render a disabled input with a hardcoded masked value beside an "Edit" button to "enable" the input. + * Clicking "Edit" hides the disabled input and renders the yielded component. This way any data management is handled by the parent. + * These are useful for editing inputs of sensitive values not returned by the API. The extra click ensures the user is intentionally editing the field. + * + * @example + + + + +// without passing @attr + + + + + * @param {object} [attr] - used to generate label for `ReadonlyFormField`, `name` key is required. Can be an attribute from a model exported with expandAttributeMeta. + */ + +export default class EnableInputComponent extends Component { + @tracked enable = false; +} diff --git a/ui/lib/core/addon/components/form-field.hbs b/ui/lib/core/addon/components/form-field.hbs index 6932e445d6..d47ad19d10 100644 --- a/ui/lib/core/addon/components/form-field.hbs +++ b/ui/lib/core/addon/components/form-field.hbs @@ -104,7 +104,9 @@
diff --git a/ui/lib/core/addon/components/text-file.hbs b/ui/lib/core/addon/components/text-file.hbs index 6572357e82..6b1ffcc583 100644 --- a/ui/lib/core/addon/components/text-file.hbs +++ b/ui/lib/core/addon/components/text-file.hbs @@ -42,7 +42,19 @@ data-test-text-file-textarea as |F| > - Enter the value as text + + Enter the value as text. + {{#if @subText}} + {{@subText}} + {{/if}} + {{#if @docLink}} + See our + + documentation + + for help. + {{/if}} + {{else}} - Select a file from your computer + + Select a file from your computer. + {{#if @subText}} + {{@subText}} + {{/if}} + {{#if @docLink}} + See our + + documentation + + for help. + {{/if}} + {{#if (or @validationError this.uploadError)}} + + + `); + + assert.dom('input').isDisabled('input is disabled'); + assert.dom('input').hasValue('**********', 'disabled input renders asterisks'); + await click('button'); + assert.dom('[data-test-yielded-input]').isNotDisabled('toggles to enabled, yielded input'); + assert.dom('button').doesNotExist('button disappears when input is enabled'); + }); + + test('it renders passed attribute', async function (assert) { + assert.expect(6); + this.attr = { + name: 'specialClientCredentials', + type: 'string', + options: { + subText: 'This value is protected and not returned from the API. Enable input to update value.', + }, + }; + this.model = { specialClientCredentials: '' }; + await render(hbs` + + + + `); + + assert.dom(`[data-test-input="${this.attr.name}"]`).isDisabled('renders disabled ReadonlyFormField'); + assert + .dom(`[data-test-input="${this.attr.name}"]`) + .hasValue('**********', 'disabled input renders asterisks'); + assert.dom('[data-test-readonly-label]').hasText('Special client credentials'); + assert.dom('p.sub-text').hasText(this.attr.options.subText); + await click('button'); + assert + .dom(`[data-test-field="${this.attr.name}"] input`) + .isNotDisabled('toggles to enabled, yielded form field component'); + assert.dom('button').doesNotExist('button disappears when input is enabled'); + }); +}); diff --git a/ui/tests/integration/components/form-field-test.js b/ui/tests/integration/components/form-field-test.js index d090536ef1..0d33ee2da6 100644 --- a/ui/tests/integration/components/form-field-test.js +++ b/ui/tests/integration/components/form-field-test.js @@ -106,9 +106,22 @@ module('Integration | Component | form field', function (hooks) { }); test('it renders: editType file', async function (assert) { - await setup.call(this, createAttr('foo', 'string', { editType: 'file' })); + const subText = 'My subtext.'; + await setup.call(this, createAttr('foo', 'string', { editType: 'file', subText, docLink: '/docs' })); assert.ok(component.hasTextFile, 'renders the text-file component'); + assert + .dom('.hds-form-helper-text') + .hasText( + `Select a file from your computer. ${subText} See our documentation for help.`, + 'renders subtext' + ); + assert.dom('.hds-form-helper-text a').exists('renders doc link'); await click('[data-test-text-toggle]'); + // assert again after toggling because subtext is rendered differently for each input + assert + .dom('.hds-form-helper-text') + .hasText(`Enter the value as text. ${subText} See our documentation for help.`, 'renders subtext'); + assert.dom('.hds-form-helper-text a').exists('renders doc link'); await fillIn('[data-test-text-file-textarea]', 'hello world'); });