diff --git a/ui/admin/app/components/form/auth-method/oidc/index.hbs b/ui/admin/app/components/form/auth-method/oidc/index.hbs index b80a63d12..cec2c67eb 100644 --- a/ui/admin/app/components/form/auth-method/oidc/index.hbs +++ b/ui/admin/app/components/form/auth-method/oidc/index.hbs @@ -144,115 +144,33 @@ {{/unless}} {{! Signing Algorithms }} - - <:title>{{t 'form.signing_algorithms.label'}} - <:description>{{t 'form.signing_algorithms.help'}} - <:body> + + <:fieldset as |F|> + + {{t 'form.signing_algorithms.label'}} + + + {{t 'form.signing_algorithms.help'}} + + {{#if @model.errors.signing_algorithms}} - + {{#each @model.errors.signing_algorithms as |error|}} - {{error.message}} + {{error.message}} {{/each}} - + {{/if}} - - - - {{t 'titles.value'}} - {{t 'titles.actions'}} - - - - {{#each @model.signing_algorithms as |algorithm i|}} - - + - - {{t - 'form.algorithm.label' - }} - - {{#each this.signingAlgorithms as |algorithmOption|}} - - {{/each}} - - - - - - - {{t 'actions.remove'}} - - - - {{/each}} - - - - - - {{t - 'resources.auth-method.titles.new-signing-algorithm' - }} - - {{#each this.signingAlgorithms as |algorithmOption|}} - - {{/each}} - - - - - - - {{t 'actions.add'}} - - - - - - - + <:field as |F|> + + + {{! Allowed Audiences }} + + <:body as |B|> + + {{#each this.options as |select index|}} + + + + + {{#each @selectOptions as |selectOption|}} + + {{/each}} + + + + + + {{#if this.removeOptionByIndex}} + + {{/if}} + + + {{/each}} + + + + + + + + {{#each @selectOptions as |selectOption|}} + + {{/each}} + + + + + + + + + + + \ No newline at end of file diff --git a/ui/admin/app/components/form/field/list-wrapper/select/index.js b/ui/admin/app/components/form/field/list-wrapper/select/index.js new file mode 100644 index 000000000..2bcdbbb67 --- /dev/null +++ b/ui/admin/app/components/form/field/list-wrapper/select/index.js @@ -0,0 +1,56 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; +import { set } from '@ember/object'; + +export default class MappingListSelectComponent extends Component { + // =attributes + + /** + * @type {string} + */ + @tracked newOptionValue = ''; + + get options() { + return this.args.options || this.args.model[this.args.name]; + } + + /** + * If a new input is entered and an addOption method was specified, + * calls addOption with the new input. Resets previous value. + * Otherwise use the model argument to create the array and update the model + */ + + @action + addOption() { + if (this.args.addOption && this.newOptionValue) { + this.args.addOption({ + value: this.newOptionValue, + }); + } else { + const field = this.args.name; + const existingArray = this.args.model[field] ?? []; + const newArray = [...existingArray, { value: this.newOptionValue }]; + set(this.args.model, field, newArray); + } + + this.newOptionValue = ''; + } + + /** + * If removeOptionByIndex method was passed, use that. + * Otherwise, use the model to remove an option by index. We recreate a new array after + * splicing out the item so that ember is aware that the array has been modified. + * @param index {number} + */ + @action + removeOptionByIndex(index) { + if (this.args.removeOptionByIndex) { + this.args.removeOptionByIndex(index); + } else { + const field = this.args.name; + const newArray = this.args.model[field].filter((_, i) => i !== index); + set(this.args.model, field, newArray); + } + } +} diff --git a/ui/admin/tests/acceptance/auth-methods/create-test.js b/ui/admin/tests/acceptance/auth-methods/create-test.js index 257954efc..8ff402617 100644 --- a/ui/admin/tests/acceptance/auth-methods/create-test.js +++ b/ui/admin/tests/acceptance/auth-methods/create-test.js @@ -118,7 +118,7 @@ module('Acceptance | auth-methods | create', function (hooks) { await fillIn('[name="client_id"]', 'client_id'); await fillIn('[name="client_secret"]', 'client_secret'); await select('form fieldset:nth-of-type(1) select', 'RS384'); - await click('form fieldset:nth-of-type(1) [title="Add"]'); + await click('[data-test-add-option-button]'); await fillIn(ALLOWED_AUDIENCES_INPUT_SELECTOR, 'allowed_audiences'); await click(ALLOWED_AUDIENCES_BTN_SELECTOR, 'allowed_audiences'); await fillIn(CLAIMS_SCOPES_INPUT_SELECTOR, 'claims_scopes'); diff --git a/ui/admin/tests/acceptance/auth-methods/update-test.js b/ui/admin/tests/acceptance/auth-methods/update-test.js index 29fa83e18..7ff7e80f2 100644 --- a/ui/admin/tests/acceptance/auth-methods/update-test.js +++ b/ui/admin/tests/acceptance/auth-methods/update-test.js @@ -53,6 +53,12 @@ module('Acceptance | auth-methods | update', function (hooks) { const CLAIMS_SCOPES_INPUT_SELECTOR = '[name="claims_scopes"] input'; const TOGGLE_SELECTOR = '[name="prompts"]'; + const SIGNING_ALGORITHMS_REMOVE_BTN_SELECTOR = + '[data-test-remove-option-button]'; + const SIGNING_ALGORITHMS_INPUT_SELECTOR = + '.list-wrapper-field tbody tr:last-child select'; + const SIGNING_ALGORITHMS_ADD_BTN_SELECTOR = '[data-test-add-option-button]'; + const instances = { scopes: { global: null, @@ -123,14 +129,16 @@ module('Acceptance | auth-methods | update', function (hooks) { await fillIn('[name="issuer"]', 'issuer'); await fillIn('[name="client_id"]', 'client_id'); await fillIn('[name="client_secret"]', 'client_secret'); + // Remove all signing algorithms - await Promise.all( - findAll('form fieldset:nth-of-type(1) [title="Remove"]').map((element) => - click(element), - ), - ); - await select('form fieldset:nth-of-type(1) select', 'RS384'); - await click('form fieldset:nth-of-type(1) [title="Add"]'); + let removeButtons = findAll(SIGNING_ALGORITHMS_REMOVE_BTN_SELECTOR); + + for (const element of removeButtons) { + await click(element); + } + await select(SIGNING_ALGORITHMS_INPUT_SELECTOR, 'RS384'); + await click(SIGNING_ALGORITHMS_ADD_BTN_SELECTOR); + // Remove all allowed audiences const allowedAudiencesList = findAll(ALLOWED_AUDIENCES_REMOVE_BTN_SELECTOR); diff --git a/ui/admin/tests/integration/components/form/field/list-wrapper-test.js b/ui/admin/tests/integration/components/form/field/list-wrapper-test.js index 3b90cf012..904cc172f 100644 --- a/ui/admin/tests/integration/components/form/field/list-wrapper-test.js +++ b/ui/admin/tests/integration/components/form/field/list-wrapper-test.js @@ -5,7 +5,7 @@ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; -import { render } from '@ember/test-helpers'; +import { click, render, select } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; import { setupIntl } from 'ember-intl/test-support'; @@ -106,4 +106,61 @@ module('Integration | Component | list-wrapper', function (hooks) { assert.dom('tbody tr').exists({ count: 3 }); }); + + test('it renders multiple options with a single select field', async function (assert) { + this.set('options', ['option1', 'option2', 'option3']); + this.set('model', { select: [{ value: 'option2' }, { value: 'option3' }] }); + await render(hbs` + + + <:field as |F|> + + + + `); + + assert + .dom( + `.list-wrapper-field [data-test-remove-option-button=${this.model.select[0].value}]`, + ) + .exists({ count: 1 }); + + assert.dom('tbody tr').exists({ count: 3 }); + }); + + test('select field allows you to add and remove options', async function (assert) { + this.set('options', ['option1', 'option2', 'option3']); + this.set('model', { select: [{ value: 'option2' }, { value: 'option3' }] }); + await render(hbs` + + + <:field as |F|> + + + + `); + + assert.dom('tbody tr').exists({ count: 3 }); + + // Add an option + await select('.list-wrapper-field tbody tr:last-child select', 'option1'); + await click('.list-wrapper-field [data-test-add-option-button]'); + assert.dom('tbody tr').exists({ count: 4 }); + + // Remove an option + await click('.list-wrapper-field [data-test-remove-option-button=option1]'); + assert.dom('tbody tr').exists({ count: 3 }); + }); });