chore: 🤖 remove listwrapper usage (#3294)
This commit is contained in:
1 parent
0e580e3c9f
commit
35377e8466
14 files changed
-1317
No files matched your search
@@ -1,33 +0,0 @@
|
||||
{{!
|
||||
Copyright IBM Corp. 2021, 2026
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Hds::Form::Fieldset
|
||||
@layout={{@layout}}
|
||||
@isRequired={{@isRequired}}
|
||||
@isOptional={{@isOptional}}
|
||||
as |F|
|
||||
>
|
||||
|
||||
{{yield F to='fieldset'}}
|
||||
|
||||
<F.Control>
|
||||
{{yield
|
||||
(hash
|
||||
KeyValue=(component
|
||||
'form/field/list-wrapper/key-value' disabled=@disabled
|
||||
)
|
||||
TextInput=(component
|
||||
'form/field/list-wrapper/text-input' disabled=@disabled
|
||||
)
|
||||
Textarea=(component
|
||||
'form/field/list-wrapper/textarea' disabled=@disabled
|
||||
)
|
||||
Select=(component 'form/field/list-wrapper/select' disabled=@disabled)
|
||||
)
|
||||
to='field'
|
||||
}}
|
||||
</F.Control>
|
||||
|
||||
</Hds::Form::Fieldset>
|
||||
@@ -1,202 +0,0 @@
|
||||
{{!
|
||||
Copyright IBM Corp. 2021, 2026
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
{{#let (unique-id) (unique-id) as |keyHeaderID valueHeaderID|}}
|
||||
<Hds::Table class='list-wrapper-field' name={{@name}}>
|
||||
<:head as |H|>
|
||||
<H.Tr>
|
||||
<H.Th id={{keyHeaderID}}>
|
||||
{{#if @keyLabel}}
|
||||
{{@keyLabel}}
|
||||
{{else}}
|
||||
{{t 'form.key.label'}}
|
||||
{{/if}}</H.Th>
|
||||
<H.Th id={{valueHeaderID}}>
|
||||
{{#if @valueLabel}}
|
||||
{{@valueLabel}}
|
||||
{{else}}
|
||||
{{t 'form.value.label'}}
|
||||
{{/if}}
|
||||
</H.Th>
|
||||
<H.Th>
|
||||
{{t 'titles.actions'}}
|
||||
</H.Th>
|
||||
</H.Tr>
|
||||
</:head>
|
||||
<:body as |B|>
|
||||
{{#each this.options as |option index|}}
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
{{yield
|
||||
(hash
|
||||
text=(component
|
||||
'form/field/list-wrapper/key-value/text'
|
||||
disabled=@disabled
|
||||
value=option.key
|
||||
ariaLabelledBy=keyHeaderID
|
||||
setContext=(set-from-event option 'key')
|
||||
)
|
||||
select=(component
|
||||
'form/field/list-wrapper/key-value/select'
|
||||
disabled=@disabled
|
||||
value=option.key
|
||||
ariaLabelledBy=keyHeaderID
|
||||
setContext=(set-from-event option 'key')
|
||||
width=@width
|
||||
)
|
||||
)
|
||||
to='key'
|
||||
}}
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
{{yield
|
||||
(hash
|
||||
text=(component
|
||||
'form/field/list-wrapper/key-value/text'
|
||||
disabled=@disabled
|
||||
value=option.value
|
||||
ariaLabelledBy=valueHeaderID
|
||||
setContext=(set-from-event option 'value')
|
||||
)
|
||||
select=(component
|
||||
'form/field/list-wrapper/key-value/select'
|
||||
disabled=@disabled
|
||||
value=option.value
|
||||
ariaLabelledBy=valueHeaderID
|
||||
width=@width
|
||||
setContext=(set-from-event option 'value')
|
||||
)
|
||||
)
|
||||
to='value'
|
||||
}}
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
{{#if this.removeOptionByIndex}}
|
||||
<Hds::Button
|
||||
data-test-remove-button
|
||||
@text={{t 'actions.remove'}}
|
||||
@color='critical'
|
||||
@icon='trash'
|
||||
@isIconOnly={{true}}
|
||||
disabled={{@disabled}}
|
||||
{{on 'click' (fn this.removeOptionByIndex index)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
{{/each}}
|
||||
{{#if this.showNewRow}}
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
{{#if (has-block 'newKey')}}
|
||||
{{yield
|
||||
(hash
|
||||
text=(component
|
||||
'form/field/list-wrapper/key-value/text'
|
||||
disabled=@disabled
|
||||
value=this.newOptionKey
|
||||
ariaLabelledBy=keyHeaderID
|
||||
setContext=(set-from-event this 'newOptionKey')
|
||||
)
|
||||
select=(component
|
||||
'form/field/list-wrapper/key-value/select'
|
||||
disabled=@disabled
|
||||
value=this.newOptionKey
|
||||
ariaLabelledBy=keyHeaderID
|
||||
width=@width
|
||||
setContext=(set-from-event this 'newOptionKey')
|
||||
)
|
||||
)
|
||||
to='newKey'
|
||||
}}
|
||||
|
||||
{{else}}
|
||||
{{yield
|
||||
(hash
|
||||
text=(component
|
||||
'form/field/list-wrapper/key-value/text'
|
||||
disabled=@disabled
|
||||
value=this.newOptionKey
|
||||
ariaLabelledBy=keyHeaderID
|
||||
setContext=(set-from-event this 'newOptionKey')
|
||||
)
|
||||
select=(component
|
||||
'form/field/list-wrapper/key-value/select'
|
||||
disabled=@disabled
|
||||
value=this.newOptionKey
|
||||
ariaLabelledBy=keyHeaderID
|
||||
width=@width
|
||||
setContext=(set-from-event this 'newOptionKey')
|
||||
)
|
||||
)
|
||||
to='key'
|
||||
}}
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
{{#if (has-block 'newValue')}}
|
||||
{{yield
|
||||
(hash
|
||||
text=(component
|
||||
'form/field/list-wrapper/key-value/text'
|
||||
disabled=@disabled
|
||||
value=this.newOptionValue
|
||||
ariaLabelledBy=valueHeaderID
|
||||
setContext=(set-from-event this 'newOptionValue')
|
||||
)
|
||||
select=(component
|
||||
'form/field/list-wrapper/key-value/select'
|
||||
disabled=@disabled
|
||||
value=this.newOptionValue
|
||||
ariaLabelledBy=valueHeaderID
|
||||
width=@width
|
||||
setContext=(set-from-event this 'newOptionValue')
|
||||
)
|
||||
)
|
||||
to='newValue'
|
||||
}}
|
||||
|
||||
{{else}}
|
||||
{{yield
|
||||
(hash
|
||||
text=(component
|
||||
'form/field/list-wrapper/key-value/text'
|
||||
disabled=@disabled
|
||||
value=this.newOptionValue
|
||||
ariaLabelledBy=valueHeaderID
|
||||
setContext=(set-from-event this 'newOptionValue')
|
||||
)
|
||||
select=(component
|
||||
'form/field/list-wrapper/key-value/select'
|
||||
disabled=@disabled
|
||||
value=this.newOptionValue
|
||||
ariaLabelledBy=valueHeaderID
|
||||
width=@width
|
||||
setContext=(set-from-event this 'newOptionValue')
|
||||
)
|
||||
)
|
||||
to='value'
|
||||
}}
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
|
||||
<Hds::Button
|
||||
@text={{t 'actions.add'}}
|
||||
@color='secondary'
|
||||
type='button'
|
||||
disabled={{or @disabled (not this.newOptionKey)}}
|
||||
{{on 'click' this.addOption}}
|
||||
/>
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
{{/if}}
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
{{/let}}
|
||||
@@ -1,94 +0,0 @@
|
||||
/**
|
||||
* Copyright IBM Corp. 2021, 2026
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { set } from '@ember/object';
|
||||
|
||||
export default class MappingListComponent extends Component {
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
@tracked newOptionKey = '';
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
@tracked newOptionValue = '';
|
||||
|
||||
/**
|
||||
* Returns an array of key/value pair that the user enters
|
||||
* @type {object}
|
||||
*/
|
||||
|
||||
get options() {
|
||||
return this.args?.options || this.args?.model?.[this.args.name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if we need to show an empty row to the users to enter more key/value pairs based on removeDuplicates arg,
|
||||
* by default it is true
|
||||
* @type {object}
|
||||
*/
|
||||
get showNewRow() {
|
||||
if (this.args.showNewRow) {
|
||||
return this.args.showNewRow();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* If a new key value is entered and an addOption method was specified,
|
||||
* calls addOption with the new key and value. Resets key and value.
|
||||
* Otherwise use the model argument to create the array and update the model
|
||||
*/
|
||||
|
||||
@action
|
||||
addOption() {
|
||||
if (this.args.addOption) {
|
||||
if (this.newOptionKey) {
|
||||
this.args.addOption({
|
||||
key: this.newOptionKey,
|
||||
value: this.newOptionValue,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const field = this.args.name;
|
||||
const existingArray = this.args.model[field] ?? [];
|
||||
|
||||
const newArray = [
|
||||
...existingArray,
|
||||
{ key: this.newOptionKey, value: this.newOptionValue },
|
||||
];
|
||||
set(this.args.model, field, newArray);
|
||||
}
|
||||
|
||||
this.newOptionKey = '';
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{{!
|
||||
Copyright IBM Corp. 2021, 2026
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Hds::Form::Select::Base
|
||||
@value={{@value}}
|
||||
@width={{@width}}
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{@ariaLabelledBy}}
|
||||
{{on 'change' @setContext}}
|
||||
as |F|
|
||||
>
|
||||
<F.Options>
|
||||
<option hidden selected value=''>
|
||||
{{t 'titles.choose-an-option'}}
|
||||
</option>
|
||||
{{#each @selectOptions as |selectOption|}}
|
||||
<option value={{selectOption}} selected={{eq selectOption @value}}>
|
||||
{{selectOption}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</F.Options>
|
||||
</Hds::Form::Select::Base>
|
||||
@@ -1,11 +0,0 @@
|
||||
{{!
|
||||
Copyright IBM Corp. 2021, 2026
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{@value}}
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{@ariaLabelledBy}}
|
||||
{{on 'input' @setContext}}
|
||||
/>
|
||||
@@ -1,116 +0,0 @@
|
||||
{{!
|
||||
Copyright IBM Corp. 2021, 2026
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
{{#let (unique-id) (unique-id) as |keyHeaderID valueHeaderID|}}
|
||||
<Hds::Table class='list-wrapper-field' name={{@name}}>
|
||||
<:head as |H|>
|
||||
<H.Tr>
|
||||
<H.Th id={{keyHeaderID}}>{{t 'form.key.label'}}</H.Th>
|
||||
<H.Th id={{valueHeaderID}}>{{t 'form.value.label'}}</H.Th>
|
||||
<H.Th>
|
||||
{{t 'titles.actions'}}
|
||||
|
||||
</H.Th>
|
||||
</H.Tr>
|
||||
</:head>
|
||||
<:body as |B|>
|
||||
{{#each-in this.options as |select val|}}
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Form::Select::Base
|
||||
@value={{select}}
|
||||
disabled={{@disabled}}
|
||||
@width={{@width}}
|
||||
aria-labelledby={{keyHeaderID}}
|
||||
{{on 'change' (fn this.selectChange select val)}}
|
||||
as |F|
|
||||
>
|
||||
<F.Options>
|
||||
{{#each-in @selectOptions as |key value|}}
|
||||
<option value={{key}} selected={{eq select key}}>
|
||||
{{value}}
|
||||
</option>
|
||||
{{/each-in}}
|
||||
</F.Options>
|
||||
</Hds::Form::Select::Base>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{val}}
|
||||
@type='text'
|
||||
@width={{@width}}
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{valueHeaderID}}
|
||||
{{on 'change' (fn this.updateInput select)}}
|
||||
/>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
{{#if this.removeOptionByKey}}
|
||||
<Hds::Button
|
||||
data-test-remove-button={{select}}
|
||||
@text={{t 'actions.remove'}}
|
||||
@color='critical'
|
||||
@icon='trash'
|
||||
@isIconOnly={{true}}
|
||||
disabled={{@disabled}}
|
||||
{{on 'click' (fn this.removeOptionByKey select)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
{{/each-in}}
|
||||
|
||||
{{#if this.showNewRow}}
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Form::Select::Base
|
||||
@value={{this.newOptionKey}}
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{keyHeaderID}}
|
||||
@width={{@width}}
|
||||
{{on 'change' (set-from-event this 'newOptionKey')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Options>
|
||||
|
||||
<option hidden selected value=''>
|
||||
{{t 'titles.choose-an-option'}}
|
||||
</option>
|
||||
{{#each-in this.selectOptions as |key value|}}
|
||||
<option value={{key}} selected={{eq key this.newOptionKey}}>
|
||||
{{value}}
|
||||
</option>
|
||||
{{/each-in}}
|
||||
</F.Options>
|
||||
</Hds::Form::Select::Base>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{this.newOptionValue}}
|
||||
@type='text'
|
||||
@width={{@width}}
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{valueHeaderID}}
|
||||
{{on 'input' (set-from-event this 'newOptionValue')}}
|
||||
/>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
<Hds::Button
|
||||
data-test-select-text-add-btn
|
||||
@text={{t 'actions.add'}}
|
||||
@color='secondary'
|
||||
type='button'
|
||||
disabled={{or @disabled (not this.newOptionKey)}}
|
||||
{{on 'click' this.addOption}}
|
||||
/>
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
{{/if}}
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
{{/let}}
|
||||
@@ -1,137 +0,0 @@
|
||||
/**
|
||||
* Copyright IBM Corp. 2021, 2026
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { set } from '@ember/object';
|
||||
|
||||
export default class MappingListComponent extends Component {
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
@tracked newOptionKey = '';
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
@tracked newOptionValue = '';
|
||||
|
||||
/**
|
||||
* Returns an object of key/value pair that the user enters
|
||||
* @type {object}
|
||||
*/
|
||||
get options() {
|
||||
return this.args?.options || this.args?.model?.[this.args.name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if we need to show an empty row to the users to enter more key/value pairs based on removeDuplicates arg,
|
||||
* by default it is true
|
||||
* @type {object}
|
||||
*/
|
||||
get showNewRow() {
|
||||
if (this.args.removeDuplicates) {
|
||||
return (
|
||||
Object.keys(this.options || {}).length !==
|
||||
Object.keys(this.args.selectOptions || {}).length
|
||||
);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents users from selecting duplicate keys from the select list if there's the arg is set to true
|
||||
* @type {object}
|
||||
*/
|
||||
get selectOptions() {
|
||||
const previouslySelectedKeys = Object.keys(this.options || {});
|
||||
if (this.args.removeDuplicates && previouslySelectedKeys.length) {
|
||||
const newObj = { ...this.args.selectOptions };
|
||||
for (const key of Object.keys(newObj)) {
|
||||
if (previouslySelectedKeys.includes(key)) {
|
||||
delete newObj[key];
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
} else {
|
||||
return this.args.selectOptions;
|
||||
}
|
||||
}
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Handles when then user makes changes to the select list
|
||||
* @param oldkey {string}
|
||||
* @param oldVal {string}
|
||||
*/
|
||||
@action
|
||||
selectChange(oldkey, oldVal, { target: { value: newKey } }) {
|
||||
const field = this.args.name;
|
||||
const newObj = { ...this.args.model[field], [newKey]: oldVal };
|
||||
delete newObj[oldkey];
|
||||
set(this.args.model, field, newObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles input changes
|
||||
* @param key {string}
|
||||
*/
|
||||
@action
|
||||
updateInput(key, { target: { value: newValue } }) {
|
||||
const field = this.args.name;
|
||||
this.args.model[field] = {
|
||||
...this.args.model[field],
|
||||
[key]: newValue,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* If a new key value is entered and an addOption method was specified,
|
||||
* calls addOption with the new key and value. Resets key and value.
|
||||
* Otherwise use the model argument to create the array and update the model
|
||||
*/
|
||||
|
||||
@action
|
||||
addOption() {
|
||||
if (this.args.addOption) {
|
||||
if (this.newOptionKey) {
|
||||
this.args.addOption({
|
||||
key: this.newOptionKey,
|
||||
value: this.newOptionValue,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const field = this.args.name;
|
||||
this.args.model[field] = {
|
||||
...this.args.model[field],
|
||||
[this.newOptionKey]: this.newOptionValue,
|
||||
};
|
||||
}
|
||||
|
||||
this.newOptionKey = '';
|
||||
this.newOptionValue = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* If removeOptionByIndex method was passed, use that.
|
||||
* Otherwise, use the model to remove an option by key
|
||||
* @param index {number}
|
||||
*/
|
||||
@action
|
||||
removeOptionByKey(selectedKey) {
|
||||
if (this.args.removeOptionByKey) {
|
||||
this.args.removeOptionByKey(selectedKey);
|
||||
} else {
|
||||
const field = this.args.name;
|
||||
const newObj = { ...this.args.model[field] };
|
||||
delete newObj[selectedKey];
|
||||
set(this.args.model, field, newObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
{{!
|
||||
Copyright IBM Corp. 2021, 2026
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Hds::Table class='list-wrapper-field' name={{@name}}>
|
||||
|
||||
<:body as |B|>
|
||||
|
||||
{{#each this.options as |select index|}}
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Form::Select::Base
|
||||
@value={{select.value}}
|
||||
@width={{@width}}
|
||||
disabled={{@disabled}}
|
||||
aria-label={{t 'titles.value'}}
|
||||
{{on 'change' (set-from-event select 'value')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Options>
|
||||
{{#each @selectOptions as |selectOption|}}
|
||||
<option
|
||||
value={{selectOption}}
|
||||
selected={{eq select.value selectOption}}
|
||||
>
|
||||
{{selectOption}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</F.Options>
|
||||
</Hds::Form::Select::Base>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
{{#if this.removeOptionByIndex}}
|
||||
<Hds::Button
|
||||
data-test-remove-option-button={{select.value}}
|
||||
@text={{t 'actions.remove'}}
|
||||
@color='critical'
|
||||
@icon='trash'
|
||||
@isIconOnly={{true}}
|
||||
disabled={{@disabled}}
|
||||
{{on 'click' (fn this.removeOptionByIndex index)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
{{/each}}
|
||||
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Form::Select::Base
|
||||
@value={{this.newOptionValue}}
|
||||
@width={{@width}}
|
||||
disabled={{@disabled}}
|
||||
aria-label={{t 'titles.value'}}
|
||||
{{on 'change' (set-from-event this 'newOptionValue')}}
|
||||
as |F|
|
||||
>
|
||||
|
||||
<F.Options>
|
||||
<option disabled={{@disabled}} hidden selected value=''>
|
||||
{{t 'titles.choose-an-option'}}
|
||||
</option>
|
||||
{{#each @selectOptions as |selectOption|}}
|
||||
<option
|
||||
value={{selectOption}}
|
||||
selected={{eq selectOption this.newOptionValue}}
|
||||
>
|
||||
{{selectOption}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</F.Options>
|
||||
|
||||
</Hds::Form::Select::Base>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
<Hds::Button
|
||||
data-test-add-option-button
|
||||
@text={{t 'actions.add'}}
|
||||
@color='secondary'
|
||||
type='button'
|
||||
disabled={{or @disabled (not this.newOptionValue)}}
|
||||
{{on 'click' this.addOption}}
|
||||
/>
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Copyright IBM Corp. 2021, 2026
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
{{!
|
||||
Copyright IBM Corp. 2021, 2026
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Hds::Table class='list-wrapper-field' name={{@name}}>
|
||||
|
||||
<:body as |B|>
|
||||
|
||||
{{#each @options as |option index|}}
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{option.value}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-label={{t 'titles.value'}}
|
||||
{{on 'input' (set-from-event option 'value')}}
|
||||
/>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
{{#if this.removeOptionByIndex}}
|
||||
<Hds::Button
|
||||
data-test-remove-button
|
||||
@text={{t 'actions.remove'}}
|
||||
@color='critical'
|
||||
@icon='trash'
|
||||
@isIconOnly={{true}}
|
||||
disabled={{@disabled}}
|
||||
{{on 'click' (fn this.removeOptionByIndex index)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
{{/each}}
|
||||
|
||||
<B.Tr>
|
||||
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{this.newOptionValue}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-label={{t 'titles.value'}}
|
||||
{{on 'input' (set-from-event this 'newOptionValue')}}
|
||||
/>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
<Hds::Button
|
||||
@text={{t 'actions.add'}}
|
||||
@color='secondary'
|
||||
type='button'
|
||||
disabled={{or @disabled (not this.newOptionValue)}}
|
||||
{{on 'click' this.addOption}}
|
||||
/>
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Copyright IBM Corp. 2021, 2026
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { set } from '@ember/object';
|
||||
|
||||
export default class MappingListTextInputComponent extends Component {
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
@tracked newOptionValue = '';
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
{{!
|
||||
Copyright IBM Corp. 2021, 2026
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Hds::Table class='list-wrapper-field' name={{@name}}>
|
||||
|
||||
<:body as |B|>
|
||||
|
||||
{{#each @options as |option index|}}
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Form::Textarea::Field
|
||||
@value={{option.value}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-label={{t 'titles.value'}}
|
||||
/>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
{{#if this.removeOptionByIndex}}
|
||||
<Hds::Button
|
||||
data-test-remove-button
|
||||
@text={{t 'actions.remove'}}
|
||||
@color='critical'
|
||||
@icon='trash'
|
||||
@isIconOnly={{true}}
|
||||
disabled={{@disabled}}
|
||||
{{on 'click' (fn this.removeOptionByIndex index)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
{{/each}}
|
||||
|
||||
<B.Tr>
|
||||
|
||||
<B.Td>
|
||||
<Hds::Form::Textarea::Field
|
||||
@value={{this.newOptionValue}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-label={{t 'titles.value'}}
|
||||
/>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
<Hds::Button
|
||||
@text={{t 'actions.add'}}
|
||||
@color='secondary'
|
||||
type='button'
|
||||
disabled={{or @disabled (not this.newOptionValue)}}
|
||||
{{on 'click' this.addOption}}
|
||||
/>
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Copyright IBM Corp. 2021, 2026
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { set } from '@ember/object';
|
||||
|
||||
export default class MappingListTextAreaComponent extends Component {
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
@tracked newOptionValue = '';
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,305 +0,0 @@
|
||||
/**
|
||||
* Copyright IBM Corp. 2021, 2026
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { click, findAll, render, select } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { setupIntl } from 'ember-intl/test-support';
|
||||
|
||||
module('Integration | Component | list-wrapper', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
setupIntl(hooks, 'en-us');
|
||||
|
||||
test('it renders fields', async function (assert) {
|
||||
this.set('label', 'Label');
|
||||
this.set('helperText', 'Help');
|
||||
this.set('error', 'Error!');
|
||||
|
||||
await render(hbs`
|
||||
<Form::Field::ListWrapper>
|
||||
<:fieldset as |F|>
|
||||
<F.Legend>
|
||||
{{this.label}}
|
||||
</F.Legend>
|
||||
<F.HelperText>
|
||||
{{this.helperText}}
|
||||
</F.HelperText>
|
||||
<F.Error as |E|>
|
||||
<E.Message>{{this.error}}</E.Message>
|
||||
</F.Error>
|
||||
</:fieldset>
|
||||
<:field as |F|>
|
||||
<F.KeyValue>
|
||||
</F.KeyValue>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
`);
|
||||
|
||||
assert.dom('tbody tr').exists({ count: 1 });
|
||||
assert.dom('legend').exists().hasText('Label');
|
||||
assert.dom('.hds-form-helper-text').exists().hasText('Help');
|
||||
|
||||
assert.dom('.hds-form-error__message').exists().hasText('Error!');
|
||||
assert.dom('button').exists().hasText('Add');
|
||||
});
|
||||
|
||||
test('it renders multiple options with key value pair', async function (assert) {
|
||||
this.options = [
|
||||
{ key: 'one', value: 'two' },
|
||||
{ key: 'three', value: 'four' },
|
||||
];
|
||||
|
||||
await render(hbs`
|
||||
<Form::Field::ListWrapper>
|
||||
<:field as |F|>
|
||||
<F.KeyValue @options={{this.options}}>
|
||||
<:key as |K|>
|
||||
<K.text />
|
||||
</:key>
|
||||
<:value as |V|>
|
||||
<V.text />
|
||||
</:value>
|
||||
</F.KeyValue>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
`);
|
||||
|
||||
assert.dom('tbody tr').exists({ count: 3 });
|
||||
});
|
||||
|
||||
test('it renders multiple options with a single text field', async function (assert) {
|
||||
this.options = [{ value: 'one' }, { value: 'three' }];
|
||||
|
||||
await render(hbs`
|
||||
|
||||
<Form::Field::ListWrapper>
|
||||
<:field as |F|>
|
||||
<F.TextInput
|
||||
@options={{this.options}}
|
||||
>
|
||||
</F.TextInput>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
`);
|
||||
|
||||
assert
|
||||
.dom('.list-wrapper-field [data-test-remove-button]')
|
||||
.exists({ count: 2 });
|
||||
|
||||
assert.dom('tbody tr').exists({ count: 3 });
|
||||
});
|
||||
|
||||
test('it renders multiple options with a single textarea field', async function (assert) {
|
||||
this.options = [{ value: 'one' }, { value: 'three' }];
|
||||
|
||||
await render(hbs`
|
||||
|
||||
<Form::Field::ListWrapper>
|
||||
<:field as |F|>
|
||||
<F.Textarea
|
||||
@options={{this.options}}
|
||||
>
|
||||
</F.Textarea>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
`);
|
||||
|
||||
assert
|
||||
.dom('.list-wrapper-field [data-test-remove-button]')
|
||||
.exists({ count: 2 });
|
||||
|
||||
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`
|
||||
|
||||
<Form::Field::ListWrapper>
|
||||
<:field as |F|>
|
||||
<F.Select
|
||||
@name="select"
|
||||
@selectOptions={{this.options}}
|
||||
@options={{this.model.select}}
|
||||
@model={{this.model}}
|
||||
@width='100%'
|
||||
/>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
`);
|
||||
|
||||
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`
|
||||
|
||||
<Form::Field::ListWrapper>
|
||||
<:field as |F|>
|
||||
<F.Select
|
||||
@name="select"
|
||||
@selectOptions={{this.options}}
|
||||
@options={{this.model.select}}
|
||||
@model={{this.model}}
|
||||
@width='100%'
|
||||
/>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
`);
|
||||
|
||||
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 });
|
||||
});
|
||||
|
||||
test('it renders multiple options with select and text input as key value pair', async function (assert) {
|
||||
this.options = [
|
||||
{
|
||||
key: 'username_attribute',
|
||||
value: 'user',
|
||||
},
|
||||
{
|
||||
key: 'password_attribute',
|
||||
value: 'pass',
|
||||
},
|
||||
];
|
||||
|
||||
this.selectOptions = ['username_attribute', 'password_attribute'];
|
||||
|
||||
await render(hbs`
|
||||
<Form::Field::ListWrapper>
|
||||
<:field as |F|>
|
||||
<F.KeyValue @name="credential_mapping_overrides">
|
||||
<:key as |K|>
|
||||
<K.select @selectOptions={{this.selectOptions}}/>
|
||||
</:key>
|
||||
<:value as |V|>
|
||||
<V.text/>
|
||||
</:value>
|
||||
</F.KeyValue>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
`);
|
||||
|
||||
assert.dom('tbody tr').exists({ count: 1 });
|
||||
});
|
||||
|
||||
test('it does not render new rows when the select option limit is reached when showNewRow is passed', async function (assert) {
|
||||
this.options = [
|
||||
{ key: 'username_attribute', value: 'user' },
|
||||
{ key: 'password_attribute', value: 'pass' },
|
||||
];
|
||||
|
||||
this.selectOptions = ['username_attribute', 'password_attribute'];
|
||||
this.showNewRow = () => false;
|
||||
await render(hbs`
|
||||
<Form::Field::ListWrapper>
|
||||
<:field as |F|>
|
||||
<F.KeyValue @name="credential_mapping_overrides" @options={{this.options}} @showNewRow={{this.showNewRow}}>
|
||||
<:key as |K|>
|
||||
<K.select @selectOptions={{this.selectOptions}}/>
|
||||
</:key>
|
||||
<:value as |V|>
|
||||
<V.text/>
|
||||
</:value>
|
||||
</F.KeyValue>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
`);
|
||||
|
||||
assert.dom('tbody tr').exists({ count: 2 });
|
||||
});
|
||||
|
||||
test('it does render unlimited new rows when @showNewRow is not passed', async function (assert) {
|
||||
this.options = [
|
||||
{
|
||||
key: 'username_attribute',
|
||||
value: 'user',
|
||||
},
|
||||
{
|
||||
key: 'password_attribute',
|
||||
value: 'pass',
|
||||
},
|
||||
];
|
||||
this.selectOptions = ['username_attribute', 'password_attribute'];
|
||||
await render(hbs`
|
||||
<Form::Field::ListWrapper>
|
||||
<:field as |F|>
|
||||
<F.KeyValue @name="credential_mapping_overrides" @options={{this.options}} >
|
||||
<:key as |K|>
|
||||
<K.text/>
|
||||
</:key>
|
||||
<:value as |V|>
|
||||
<V.text/>
|
||||
</:value>
|
||||
</F.KeyValue>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
`);
|
||||
|
||||
assert.dom('tbody tr').exists({ count: 3 });
|
||||
});
|
||||
|
||||
test('it renders `newKey` and `newValue` named blocks when passed instead of key and value blocks', async function (assert) {
|
||||
this.options = ['option1', 'option2', 'option3'];
|
||||
this.selectOptionsNewValue = [
|
||||
'username_attribute_new_val',
|
||||
'password_attribute_new_val',
|
||||
];
|
||||
|
||||
this.selectOptionsNewKey = [
|
||||
'username_attribute_new_key',
|
||||
'password_attribute_new_key',
|
||||
];
|
||||
await render(hbs`
|
||||
<Form::Field::ListWrapper>
|
||||
<:field as |F|>
|
||||
<F.KeyValue @name="credential_mapping_overrides">
|
||||
<:key as |K|>
|
||||
<K.select @selectOptions = {{this.options}}/>
|
||||
</:key>
|
||||
<:newKey as |N|>
|
||||
<N.select @selectOptions= {{this.selectOptionsNewKey}}/>
|
||||
</:newKey>
|
||||
<:newValue as |S|>
|
||||
<S.select @selectOptions= {{this.selectOptionsNewValue}}/>
|
||||
</:newValue>
|
||||
<:value as |V|>
|
||||
<V.select @selectOptions = {{this.options}}/>
|
||||
</:value>
|
||||
</F.KeyValue>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
`);
|
||||
|
||||
assert.strictEqual(
|
||||
findAll('.list-wrapper-field tbody td:nth-of-type(1) select option')
|
||||
.length,
|
||||
3,
|
||||
);
|
||||
assert.strictEqual(
|
||||
findAll('.list-wrapper-field tbody td:nth-of-type(2) select option')
|
||||
.length,
|
||||
3,
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user