diff --git a/changelog/15852.txt b/changelog/15852.txt
new file mode 100644
index 0000000000..8ed97dcc30
--- /dev/null
+++ b/changelog/15852.txt
@@ -0,0 +1,3 @@
+```release-note:improvement
+ui: Changed the tokenBoundCidrs tooltip content to clarify that comma separated values are not accepted in this field.
+```
\ No newline at end of file
diff --git a/ui/lib/core/addon/components/form-field.hbs b/ui/lib/core/addon/components/form-field.hbs
index 161354cc60..a877a6269d 100644
--- a/ui/lib/core/addon/components/form-field.hbs
+++ b/ui/lib/core/addon/components/form-field.hbs
@@ -196,12 +196,13 @@
{{else if (eq @attr.options.editType "stringArray")}}
{{else if (eq @attr.options.sensitive true)}}
{{! Masked Input }}
diff --git a/ui/lib/core/addon/components/string-list.hbs b/ui/lib/core/addon/components/string-list.hbs
new file mode 100644
index 0000000000..5ad86eb5cb
--- /dev/null
+++ b/ui/lib/core/addon/components/string-list.hbs
@@ -0,0 +1,56 @@
+
\ No newline at end of file
diff --git a/ui/lib/core/addon/components/string-list.js b/ui/lib/core/addon/components/string-list.js
index 197411e0ae..cacdfed5b6 100644
--- a/ui/lib/core/addon/components/string-list.js
+++ b/ui/lib/core/addon/components/string-list.js
@@ -1,75 +1,32 @@
import ArrayProxy from '@ember/array/proxy';
-import Component from '@ember/component';
-import { set, computed } from '@ember/object';
+import Component from '@glimmer/component';
import autosize from 'autosize';
-import layout from '../templates/components/string-list';
+import { action } from '@ember/object';
+import { set } from '@ember/object';
-export default Component.extend({
- layout,
- 'data-test-component': 'string-list',
- attributeBindings: ['data-test-component', 'data-test-input'],
- classNames: ['field', 'string-list', 'form-section'],
+/**
+ * @module StringList
+ *
+ * @example
+ * ```js
+ *
+ * ```
+ * @param {string} label - Text displayed in the header above all the inputs.
+ * @param {function} onChange - Function called when any of the inputs change.
+ * @param {string} inputValue - A string or an array of strings.
+ * @param {string} warning - Text displayed as a warning.
+ * @param {string} helpText - Text displayed as a tooltip.
+ * @param {string} type=array - Optional type for inputValue.
+ * @param {string} attrName - We use this to check the type so we can modify the tooltip content.
+ */
- /*
- * @public
- * @param String
- *
- * Optional - Text displayed in the header above all of the inputs
- *
- */
- label: null,
+export default class StringList extends Component {
+ constructor() {
+ super(...arguments);
- /*
- * @public
- * @param Function
- *
- * Function called when any of the inputs change
- * accepts a single param `value` that is the
- * result of calling `toVal()`.
- *
- */
- onChange: () => {},
-
- /*
- * @public
- * @param String | Array
- * A comma-separated string or an array of strings.
- * Defaults to an empty array.
- *
- */
- inputValue: computed(function () {
- return [];
- }),
-
- /*
- *
- * @public
- * @param String - ['array'|'string]
- *
- * Optional type for `inputValue` - defaults to `'array'`
- * Needs to match type of `inputValue` because it is set by the component on init.
- *
- */
- type: 'array',
-
- /*
- *
- * @private
- * @param Ember.ArrayProxy
- *
- * mutable array that contains objects in the form of
- * {
- * value: 'somestring',
- * }
- *
- * used to track the state of values bound to the various inputs
- *
- */
- /* eslint-disable ember/no-side-effects */
- inputList: computed('content', function () {
- return ArrayProxy.create({
- content: [],
+ this.inputList = ArrayProxy.create({
// trim the `value` when accessing objects
+ content: [],
objectAtContent: function (idx) {
const obj = this.content.objectAt(idx);
if (obj && obj.value) {
@@ -78,32 +35,28 @@ export default Component.extend({
return obj;
},
});
- }),
-
- init() {
- this._super(...arguments);
+ this.type = this.args.type || 'array';
this.setType();
this.toList();
- this.send('addInput');
- },
-
- didInsertElement() {
- this._super(...arguments);
- autosize(this.element.querySelector('textarea'));
- },
-
- didUpdate() {
- this._super(...arguments);
- autosize.update(this.element.querySelector('textarea'));
- },
+ this.addInput();
+ }
setType() {
const list = this.inputList;
if (!list) {
return;
}
- this.set('type', typeof list);
- },
+ this.type = typeof list;
+ }
+
+ toList() {
+ let input = this.args.inputValue || [];
+ const inputList = this.inputList;
+ if (typeof input === 'string') {
+ input = input.split(',');
+ }
+ inputList.addObjects(input.map((value) => ({ value })));
+ }
toVal() {
const inputs = this.inputList.filter((x) => x.value).mapBy('value');
@@ -111,37 +64,47 @@ export default Component.extend({
return inputs.join(',');
}
return inputs;
- },
+ }
- toList() {
- let input = this.inputValue || [];
- const inputList = this.inputList;
- if (typeof input === 'string') {
- input = input.split(',');
+ get helpText() {
+ if (this.args.attrName === 'tokenBoundCidrs') {
+ return 'Specifies the blocks of IP addresses which are allowed to use the generated token. One entry per row.';
+ } else {
+ return this.args.helpText;
}
- inputList.addObjects(input.map((value) => ({ value })));
- },
+ }
- actions: {
- inputChanged(idx, event) {
- const inputObj = this.inputList.objectAt(idx);
- const onChange = this.onChange;
- set(inputObj, 'value', event.target.value);
- onChange(this.toVal());
- },
+ @action
+ autoSize(element) {
+ autosize(element.querySelector('textarea'));
+ }
- addInput() {
- const inputList = this.inputList;
- if (inputList.get('lastObject.value') !== '') {
- inputList.pushObject({ value: '' });
- }
- },
+ @action
+ autoSizeUpdate(element) {
+ autosize.update(element.querySelector('textarea'));
+ }
- removeInput(idx) {
- const onChange = this.onChange;
- const inputs = this.inputList;
- inputs.removeObject(inputs.objectAt(idx));
- onChange(this.toVal());
- },
- },
-});
+ @action
+ inputChanged(idx, event) {
+ const inputObj = this.inputList.objectAt(idx);
+ const onChange = this.args.onChange;
+ set(inputObj, 'value', event.target.value);
+ onChange(this.toVal());
+ }
+
+ @action
+ addInput() {
+ const inputList = this.inputList;
+ if (inputList.get('lastObject.value') !== '') {
+ inputList.pushObject({ value: '' });
+ }
+ }
+
+ @action
+ removeInput(idx) {
+ const onChange = this.args.onChange;
+ const inputs = this.inputList;
+ inputs.removeObject(inputs.objectAt(idx));
+ onChange(this.toVal());
+ }
+}
diff --git a/ui/lib/core/addon/templates/components/string-list.hbs b/ui/lib/core/addon/templates/components/string-list.hbs
deleted file mode 100644
index f417222147..0000000000
--- a/ui/lib/core/addon/templates/components/string-list.hbs
+++ /dev/null
@@ -1,42 +0,0 @@
-{{#if this.label}}
-
-{{/if}}
-{{#if this.warning}}
-
-{{/if}}
-{{#each this.inputList as |data index|}}
-
-
-
-
-
- {{#if (eq (inc index) this.inputList.length)}}
-
- {{else}}
-
- {{/if}}
-
-
-{{/each}}
\ No newline at end of file
diff --git a/ui/lib/core/package.json b/ui/lib/core/package.json
index 3aa8729d25..b7418f2b56 100644
--- a/ui/lib/core/package.json
+++ b/ui/lib/core/package.json
@@ -17,6 +17,7 @@
"ember-composable-helpers": "*",
"ember-concurrency": "*",
"ember-maybe-in-element": "*",
+ "ember/render-modifiers": "*",
"ember-power-select": "*",
"ember-router-helpers": "*",
"ember-svg-jar": "*",
diff --git a/ui/tests/integration/components/string-list-test.js b/ui/tests/integration/components/string-list-test.js
index 2dfe3a7bfa..827075f01f 100644
--- a/ui/tests/integration/components/string-list-test.js
+++ b/ui/tests/integration/components/string-list-test.js
@@ -1,11 +1,16 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
-import { render, click, fillIn, triggerKeyEvent } from '@ember/test-helpers';
+import { render, click, fillIn, triggerKeyEvent, triggerEvent } from '@ember/test-helpers';
+import sinon from 'sinon';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | string list', function (hooks) {
setupRenderingTest(hooks);
+ hooks.beforeEach(function () {
+ this.spy = sinon.spy();
+ });
+
const assertBlank = function (assert) {
assert.dom('[data-test-string-list-input]').exists({ count: 1 }, 'renders 1 input');
assert.dom('[data-test-string-list-input]').hasValue('', 'the input is blank');
@@ -26,56 +31,55 @@ module('Integration | Component | string list', function (hooks) {
test('it renders the label', async function (assert) {
assert.expect(4);
- await render(hbs`{{string-list label="foo"}}`);
+ await render(hbs``);
assert.dom('[data-test-string-list-label]').hasText('foo', 'renders the label when provided');
-
- await render(hbs`{{string-list}}`);
+ await render(hbs``);
assert.dom('[data-test-string-list-label]').doesNotExist('does not render the label');
assertBlank(assert);
});
test('it renders inputValue from empty string', async function (assert) {
assert.expect(2);
- await render(hbs`{{string-list inputValue=""}}`);
+ await render(hbs``);
assertBlank(assert);
});
test('it renders inputValue from string with one value', async function (assert) {
assert.expect(3);
- await render(hbs`{{string-list inputValue="foo"}}`);
+ await render(hbs``);
assertFoo(assert);
});
test('it renders inputValue from comma-separated string', async function (assert) {
assert.expect(4);
- await render(hbs`{{string-list inputValue="foo,bar"}}`);
+ await render(hbs``);
assertFooBar(assert);
});
test('it renders inputValue from a blank array', async function (assert) {
assert.expect(2);
this.set('inputValue', []);
- await render(hbs`{{string-list inputValue=inputValue}}`);
+ await render(hbs``);
assertBlank(assert);
});
test('it renders inputValue array with a single item', async function (assert) {
assert.expect(3);
this.set('inputValue', ['foo']);
- await render(hbs`{{string-list inputValue=inputValue}}`);
+ await render(hbs``);
assertFoo(assert);
});
test('it renders inputValue array with a multiple items', async function (assert) {
assert.expect(4);
this.set('inputValue', ['foo', 'bar']);
- await render(hbs`{{string-list inputValue=inputValue}}`);
+ await render(hbs``);
assertFooBar(assert);
});
test('it adds a new row only when the last row is not blank', async function (assert) {
assert.expect(5);
- await render(hbs`{{string-list inputValue=""}}`);
+ await render(hbs``);
await click('[data-test-string-list-button="add"]');
assertBlank(assert);
await fillIn('[data-test-string-list-input="0"]', 'foo');
@@ -85,7 +89,7 @@ module('Integration | Component | string list', function (hooks) {
});
test('it trims input values', async function (assert) {
- await render(hbs`{{string-list inputValue=""}}`);
+ await render(hbs``);
await fillIn('[data-test-string-list-input="0"]', 'foo');
await triggerKeyEvent('[data-test-string-list-input="0"]', 'keyup', 14);
assert.dom('[data-test-string-list-input="0"]').hasValue('foo');
@@ -97,7 +101,7 @@ module('Integration | Component | string list', function (hooks) {
this.set('onChange', function (val) {
assert.deepEqual(val, ['foo', 'bar'], 'calls onChange with expected value');
});
- await render(hbs`{{string-list inputValue=inputValue onChange=(action onChange)}}`);
+ await render(hbs``);
await fillIn('[data-test-string-list-input="1"]', 'bar');
await triggerKeyEvent('[data-test-string-list-input="1"]', 'keyup', 14);
});
@@ -108,7 +112,7 @@ module('Integration | Component | string list', function (hooks) {
this.set('onChange', function (val) {
assert.equal(val, 'foo,bar', 'calls onChange with expected value');
});
- await render(hbs`{{string-list inputValue=inputValue onChange=(action onChange)}}`);
+ await render(hbs``);
await fillIn('[data-test-string-list-input="1"]', 'bar');
await triggerKeyEvent('[data-test-string-list-input="1"]', 'keyup', 14);
});
@@ -119,11 +123,23 @@ module('Integration | Component | string list', function (hooks) {
this.set('onChange', function (val) {
assert.equal(val, 'bar', 'calls onChange with expected value');
});
- await render(hbs`{{string-list inputValue=inputValue onChange=(action onChange)}}`);
+ await render(hbs``);
await click('[data-test-string-list-row="0"] [data-test-string-list-button="delete"]');
assert.dom('[data-test-string-list-input]').exists({ count: 2 }, 'renders 2 inputs');
assert.dom('[data-test-string-list-input="0"]').hasValue('bar');
assert.dom('[data-test-string-list-input="1"]').hasValue('');
});
+
+ test('it replaces helpText if name is tokenBoundCidrs', async function (assert) {
+ assert.expect(1);
+ await render(hbs``);
+ let tooltipTrigger = document.querySelector('[data-test-tool-tip-trigger]');
+ await triggerEvent(tooltipTrigger, 'mouseenter');
+ assert
+ .dom('[data-test-info-tooltip-content]')
+ .hasText(
+ 'Specifies the blocks of IP addresses which are allowed to use the generated token. One entry per row.'
+ );
+ });
});