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 });
+ });
});