chore: 🤖 refactor key value list component, text input support (#1957)
refactor: 💡 refactor list wrapper component and fix test
This commit is contained in:
1 parent
3f8e39d0e1
commit
722acdc1fc
25 files changed
+689
-641
No files matched your search
@@ -83,7 +83,7 @@
|
||||
}}</F.HelperText>
|
||||
{{#let (unique-id) as |valueHeaderID|}}
|
||||
<F.Control>
|
||||
<Hds::Table class='key-value-list' name='certificates'>
|
||||
<Hds::Table class='list-wrapper-field' name='certificates'>
|
||||
<:head as |H|>
|
||||
<H.Tr>
|
||||
<H.Th id={{valueHeaderID}}>{{t 'titles.value'}}</H.Th>
|
||||
@@ -424,7 +424,7 @@
|
||||
}}</F.HelperText>
|
||||
{{#let (unique-id) (unique-id) as |fromHeaderID toHeaderID|}}
|
||||
<F.Control>
|
||||
<Hds::Table class='key-value-list' name='account_attribute_maps'>
|
||||
<Hds::Table class='list-wrapper-field' name='account_attribute_maps'>
|
||||
<:head as |H|>
|
||||
<H.Tr>
|
||||
<H.Th id={{fromHeaderID}}>{{t
|
||||
|
||||
@@ -236,55 +236,72 @@
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Form::Field::KeyValueList
|
||||
<Form::Field::ListWrapper
|
||||
@layout='horizontal'
|
||||
@isOptional={{true}}
|
||||
@name='critical_options'
|
||||
@options={{@model.critical_options}}
|
||||
@addOption={{fn this.addOption 'critical_options'}}
|
||||
@removeOptionByIndex={{fn this.removeOptionByIndex 'critical_options'}}
|
||||
@disabled={{form.disabled}}
|
||||
as |M|
|
||||
>
|
||||
<M.Legend>
|
||||
{{t 'resources.credential-library.form.critical_options.label'}}
|
||||
</M.Legend>
|
||||
<M.HelperText>
|
||||
{{t 'resources.credential-library.form.critical_options.help'}}
|
||||
</M.HelperText>
|
||||
{{#if @model.errors.critical_options}}
|
||||
<M.Error as |E|>
|
||||
{{#each @model.errors.critical_options as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</M.Error>
|
||||
{{/if}}
|
||||
</Form::Field::KeyValueList>
|
||||
<:fieldset as |F|>
|
||||
<F.Legend>
|
||||
{{t 'resources.credential-library.form.critical_options.label'}}
|
||||
</F.Legend>
|
||||
<F.HelperText>
|
||||
{{t 'resources.credential-library.form.critical_options.help'}}
|
||||
</F.HelperText>
|
||||
|
||||
<Form::Field::KeyValueList
|
||||
{{#if @model.errors.critical_options}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.critical_options as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</:fieldset>
|
||||
|
||||
<:field as |F|>
|
||||
<F.KeyValue
|
||||
@name='critical_options'
|
||||
@options={{@model.critical_options}}
|
||||
@addOption={{fn this.addOption 'critical_options'}}
|
||||
@removeOptionByIndex={{fn this.removeOptionByIndex 'critical_options'}}
|
||||
@disabled={{form.disabled}}
|
||||
/>
|
||||
</:field>
|
||||
|
||||
</Form::Field::ListWrapper>
|
||||
|
||||
<Form::Field::ListWrapper
|
||||
@layout='horizontal'
|
||||
@isOptional={{true}}
|
||||
@name='extensions'
|
||||
@options={{@model.extensions}}
|
||||
@addOption={{fn this.addOption 'extensions'}}
|
||||
@removeOptionByIndex={{fn this.removeOptionByIndex 'extensions'}}
|
||||
@disabled={{form.disabled}}
|
||||
as |M|
|
||||
>
|
||||
<M.Legend>
|
||||
{{t 'resources.credential-library.form.extensions.label'}}
|
||||
</M.Legend>
|
||||
<M.HelperText>
|
||||
{{t 'resources.credential-library.form.extensions.help'}}
|
||||
</M.HelperText>
|
||||
{{#if @model.errors.extensions}}
|
||||
<M.Error as |E|>
|
||||
{{#each @model.errors.extensions as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</M.Error>
|
||||
{{/if}}
|
||||
</Form::Field::KeyValueList>
|
||||
<:fieldset as |F|>
|
||||
<F.Legend>
|
||||
{{t 'resources.credential-library.form.extensions.label'}}
|
||||
</F.Legend>
|
||||
<F.HelperText>
|
||||
{{t 'resources.credential-library.form.extensions.help'}}
|
||||
</F.HelperText>
|
||||
|
||||
{{#if @model.errors.extensions}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.extensions as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</:fieldset>
|
||||
<:field as |F|>
|
||||
<F.KeyValue
|
||||
@name='extensions'
|
||||
@options={{@model.extensions}}
|
||||
@addOption={{fn this.addOption 'extensions'}}
|
||||
@removeOptionByIndex={{fn this.removeOptionByIndex 'extensions'}}
|
||||
@disabled={{form.disabled}}
|
||||
/>
|
||||
</:field>
|
||||
|
||||
</Form::Field::ListWrapper>
|
||||
|
||||
{{#if (can 'save model' @model)}}
|
||||
<form.actions
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
}}
|
||||
|
||||
<Hds::Form::Fieldset
|
||||
@layout={{@layout}}
|
||||
@isRequired={{@isRequired}}
|
||||
@isOptional={{@isOptional}}
|
||||
as |F|
|
||||
>
|
||||
|
||||
{{yield F}}
|
||||
|
||||
{{#let (unique-id) (unique-id) as |keyHeaderID valueHeaderID|}}
|
||||
<F.Control>
|
||||
<Hds::Table class='key-value-list' name={{@name}}>
|
||||
<:head as |H|>
|
||||
<H.Tr>
|
||||
<H.Th id={{keyHeaderID}}>{{t 'form.key.label'}}</H.Th>
|
||||
{{#unless @hasNoValue}}
|
||||
<H.Th id={{valueHeaderID}}>{{t 'form.value.label'}}</H.Th>
|
||||
{{/unless}}
|
||||
<H.Th>
|
||||
{{#if @removeOptionByIndex}}
|
||||
{{t 'titles.actions'}}
|
||||
{{/if}}
|
||||
</H.Th>
|
||||
</H.Tr>
|
||||
</:head>
|
||||
<:body as |B|>
|
||||
|
||||
{{#each @options as |option index|}}
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{option.key}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{keyHeaderID}}
|
||||
{{on 'input' (set-from-event option 'key')}}
|
||||
/>
|
||||
</B.Td>
|
||||
{{#unless @hasNoValue}}
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{option.value}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{valueHeaderID}}
|
||||
{{on 'input' (set-from-event option 'value')}}
|
||||
/>
|
||||
</B.Td>
|
||||
{{/unless}}
|
||||
|
||||
<B.Td>
|
||||
{{#if @removeOptionByIndex}}
|
||||
<Hds::Button
|
||||
@text={{t 'actions.remove'}}
|
||||
@color='critical'
|
||||
@icon='trash'
|
||||
@isIconOnly={{true}}
|
||||
disabled={{@disabled}}
|
||||
{{on 'click' (fn @removeOptionByIndex index)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
{{/each}}
|
||||
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{this.newOptionKey}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{keyHeaderID}}
|
||||
{{on 'input' (set-from-event this 'newOptionKey')}}
|
||||
/>
|
||||
</B.Td>
|
||||
{{#unless @hasNoValue}}
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{this.newOptionValue}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{valueHeaderID}}
|
||||
{{on 'input' (set-from-event this 'newOptionValue')}}
|
||||
/>
|
||||
</B.Td>
|
||||
{{/unless}}
|
||||
<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>
|
||||
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
</F.Control>
|
||||
{{/let}}
|
||||
</Hds::Form::Fieldset>
|
||||
@@ -0,0 +1,29 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
}}
|
||||
|
||||
<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
|
||||
)
|
||||
)
|
||||
to='field'
|
||||
}}
|
||||
</F.Control>
|
||||
|
||||
</Hds::Form::Fieldset>
|
||||
@@ -0,0 +1,94 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
}}
|
||||
{{#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>
|
||||
{{#if @removeOptionByIndex}}
|
||||
{{t 'titles.actions'}}
|
||||
{{/if}}
|
||||
</H.Th>
|
||||
</H.Tr>
|
||||
</:head>
|
||||
<:body as |B|>
|
||||
|
||||
{{#each @options as |option index|}}
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{option.key}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{keyHeaderID}}
|
||||
{{on 'input' (set-from-event option 'key')}}
|
||||
/>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{option.value}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{valueHeaderID}}
|
||||
{{on 'input' (set-from-event option 'value')}}
|
||||
/>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
{{#if @removeOptionByIndex}}
|
||||
<Hds::Button
|
||||
data-test-remove-button
|
||||
@text={{t 'actions.remove'}}
|
||||
@color='critical'
|
||||
@icon='trash'
|
||||
@isIconOnly={{true}}
|
||||
disabled={{@disabled}}
|
||||
{{on 'click' (fn @removeOptionByIndex index)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
{{/each}}
|
||||
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{this.newOptionKey}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{keyHeaderID}}
|
||||
{{on 'input' (set-from-event this 'newOptionKey')}}
|
||||
/>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{this.newOptionValue}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{valueHeaderID}}
|
||||
{{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.newOptionKey)}}
|
||||
{{on 'click' this.addOption}}
|
||||
/>
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
|
||||
{{/let}}
|
||||
+1
-1
@@ -30,7 +30,7 @@ export default class MappingListComponent extends Component {
|
||||
@action
|
||||
addOption() {
|
||||
assert(
|
||||
'[boundary-admin-key-value-list-field] `@addOption` is required.',
|
||||
'[boundary-admin-list-wrapper] `@addOption` is required.',
|
||||
this.args.addOption
|
||||
);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
}}
|
||||
|
||||
{{#let (unique-id) as |valueHeaderID|}}
|
||||
|
||||
<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-labelledby={{valueHeaderID}}
|
||||
{{on 'input' (set-from-event option 'value')}}
|
||||
/>
|
||||
</B.Td>
|
||||
|
||||
<B.Td>
|
||||
{{#if @removeOptionByIndex}}
|
||||
<Hds::Button
|
||||
data-test-remove-button
|
||||
@text={{t 'actions.remove'}}
|
||||
@color='critical'
|
||||
@icon='trash'
|
||||
@isIconOnly={{true}}
|
||||
disabled={{@disabled}}
|
||||
{{on 'click' (fn @removeOptionByIndex index)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
{{/each}}
|
||||
|
||||
<B.Tr>
|
||||
|
||||
<B.Td>
|
||||
<Hds::Form::TextInput::Field
|
||||
@value={{this.newOptionValue}}
|
||||
@type='text'
|
||||
disabled={{@disabled}}
|
||||
aria-labelledby={{valueHeaderID}}
|
||||
{{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>
|
||||
|
||||
{{/let}}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { assert } from '@ember/debug';
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
@action
|
||||
addOption() {
|
||||
assert(
|
||||
'[boundary-admin-list-wrapper] `@addOption` is required.',
|
||||
this.args.addOption
|
||||
);
|
||||
|
||||
if (this.newOptionValue) {
|
||||
this.args.addOption({
|
||||
value: this.newOptionValue,
|
||||
});
|
||||
}
|
||||
|
||||
this.newOptionValue = '';
|
||||
}
|
||||
}
|
||||
@@ -69,178 +69,65 @@
|
||||
</F.HelperText>
|
||||
</InfoField>
|
||||
|
||||
<Rose::Form::Fieldset
|
||||
@helperText={{t 'resources.host-set.form.preferred_endpoints.help'}}
|
||||
@linkText={{t 'actions.learn-more'}}
|
||||
@link={{doc-url 'host-set.preferred-endpoints'}}
|
||||
<Form::Field::ListWrapper
|
||||
@layout='horizontal'
|
||||
@isOptional={{true}}
|
||||
@disabled={{form.disabled}}
|
||||
>
|
||||
<:title>{{t 'resources.host-set.form.preferred_endpoints.label'}}</:title>
|
||||
<:body>
|
||||
<:fieldset as |F|>
|
||||
<F.Legend>
|
||||
{{t 'resources.host-set.form.preferred_endpoints.label'}}
|
||||
</F.Legend>
|
||||
<F.HelperText>
|
||||
{{t 'resources.host-set.form.preferred_endpoints.help'}}
|
||||
</F.HelperText>
|
||||
|
||||
{{#if @model.errors.preferred_endpoints}}
|
||||
<Rose::Form::Errors as |errors|>
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.preferred_endpoints as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</Rose::Form::Errors>
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
<Rose::Table @style='condensed' as |table|>
|
||||
<table.header as |header|>
|
||||
<header.row @hidden={{true}} as |row|>
|
||||
<row.headerCell>{{t 'titles.value'}}</row.headerCell>
|
||||
<row.headerCell />
|
||||
</header.row>
|
||||
</table.header>
|
||||
<table.body as |body|>
|
||||
{{#each @model.preferred_endpoints as |endpoint i|}}
|
||||
<body.row as |row|>
|
||||
<row.cell>
|
||||
<form.input
|
||||
@type='text'
|
||||
@name='preferred_endpoints'
|
||||
@value={{endpoint.value}}
|
||||
@error={{@model.errors.preferred_endpoints}}
|
||||
@contextual={{true}}
|
||||
placeholder='cidr:2001:4860:4860::8888/32'
|
||||
as |field|
|
||||
>
|
||||
<field.field />
|
||||
</form.input>
|
||||
</row.cell>
|
||||
<row.cell @shrink={{true}}>
|
||||
<Rose::Button
|
||||
@style='warning'
|
||||
@iconOnly='flight-icons/svg/trash-16'
|
||||
title={{t 'actions.remove'}}
|
||||
@disabled={{if
|
||||
@model.isNew
|
||||
false
|
||||
(if form.isEditable false true)
|
||||
}}
|
||||
{{on 'click' (fn @removeItemByIndex 'preferred_endpoints' i)}}
|
||||
>
|
||||
{{t 'actions.remove'}}
|
||||
</Rose::Button>
|
||||
</row.cell>
|
||||
</body.row>
|
||||
{{/each}}
|
||||
</:fieldset>
|
||||
<:field as |F|>
|
||||
<F.TextInput
|
||||
@name='preferred_endpoints'
|
||||
@options={{@model.preferred_endpoints}}
|
||||
@addOption={{fn @addOption 'preferred_endpoints'}}
|
||||
@removeOptionByIndex={{fn @removeOptionByIndex 'preferred_endpoints'}}
|
||||
@disabled={{form.disabled}}
|
||||
/>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
|
||||
<body.row as |row|>
|
||||
<row.cell>
|
||||
<form.input
|
||||
@type='text'
|
||||
@name='preferred_endpoints'
|
||||
@value={{@model.preferred_endpoint}}
|
||||
placeholder='cidr:2001:4860:4860::8888/32'
|
||||
/>
|
||||
</row.cell>
|
||||
<row.cell @shrink={{true}}>
|
||||
<Rose::Button
|
||||
@style='secondary'
|
||||
title={{t 'actions.add'}}
|
||||
@disabled={{if
|
||||
@model.isNew
|
||||
false
|
||||
(if form.isEditable false true)
|
||||
}}
|
||||
{{on
|
||||
'click'
|
||||
(fn
|
||||
@addStringItem
|
||||
'preferred_endpoints'
|
||||
@model.preferred_endpoint
|
||||
)
|
||||
}}
|
||||
>
|
||||
{{t 'actions.add'}}
|
||||
</Rose::Button>
|
||||
</row.cell>
|
||||
</body.row>
|
||||
</table.body>
|
||||
</Rose::Table>
|
||||
</:body>
|
||||
</Rose::Form::Fieldset>
|
||||
<Form::Field::ListWrapper @layout='horizontal' @disabled={{form.disabled}}>
|
||||
<:fieldset as |F|>
|
||||
<F.Legend>
|
||||
{{t 'resources.host-set.form.filter.label'}}
|
||||
</F.Legend>
|
||||
<F.HelperText>
|
||||
{{t 'resources.host-set.form.filter.aws.help'}}
|
||||
</F.HelperText>
|
||||
|
||||
<Rose::Form::Fieldset>
|
||||
<:title>{{t 'resources.host-set.form.filter.label'}}</:title>
|
||||
<:description>{{t 'resources.host-set.form.filter.aws.help'}}</:description>
|
||||
<:body>
|
||||
{{#if @model.errors.filters}}
|
||||
<Rose::Form::Errors as |errors|>
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.filters as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</Rose::Form::Errors>
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
<Rose::Table @style='condensed' as |table|>
|
||||
<table.header as |header|>
|
||||
<header.row @hidden={{true}} as |row|>
|
||||
<row.headerCell>{{t 'titles.value'}}</row.headerCell>
|
||||
<row.headerCell />
|
||||
</header.row>
|
||||
</table.header>
|
||||
<table.body as |body|>
|
||||
{{#each @model.filters as |filter i|}}
|
||||
<body.row as |row|>
|
||||
<row.cell>
|
||||
<form.input
|
||||
@type='text'
|
||||
@name='filters'
|
||||
@value={{filter.value}}
|
||||
@error={{@model.errors.filters}}
|
||||
@contextual={{true}}
|
||||
as |field|
|
||||
>
|
||||
<field.field />
|
||||
</form.input>
|
||||
</row.cell>
|
||||
<row.cell @shrink={{true}}>
|
||||
<Rose::Button
|
||||
@style='warning'
|
||||
@iconOnly='flight-icons/svg/trash-16'
|
||||
title={{t 'actions.remove'}}
|
||||
@disabled={{if
|
||||
@model.isNew
|
||||
false
|
||||
(if form.isEditable false true)
|
||||
}}
|
||||
{{on 'click' (fn @removeItemByIndex 'filters' i)}}
|
||||
>
|
||||
{{t 'actions.remove'}}
|
||||
</Rose::Button>
|
||||
</row.cell>
|
||||
</body.row>
|
||||
{{/each}}
|
||||
|
||||
<body.row as |row|>
|
||||
<row.cell>
|
||||
<form.input
|
||||
@type='text'
|
||||
@name='filters'
|
||||
@value={{@model.filter_string}}
|
||||
/>
|
||||
</row.cell>
|
||||
<row.cell @shrink={{true}}>
|
||||
<Rose::Button
|
||||
@style='secondary'
|
||||
title={{t 'actions.add'}}
|
||||
@disabled={{if
|
||||
@model.isNew
|
||||
false
|
||||
(if form.isEditable false true)
|
||||
}}
|
||||
{{on
|
||||
'click'
|
||||
(fn @addStringItem 'filters' @model.filter_string)
|
||||
}}
|
||||
>
|
||||
{{t 'actions.add'}}
|
||||
</Rose::Button>
|
||||
</row.cell>
|
||||
</body.row>
|
||||
</table.body>
|
||||
</Rose::Table>
|
||||
</:body>
|
||||
</Rose::Form::Fieldset>
|
||||
</:fieldset>
|
||||
<:field as |F|>
|
||||
<F.TextInput
|
||||
@name='filters'
|
||||
@options={{@model.filters}}
|
||||
@addOption={{fn @addOption 'filters'}}
|
||||
@removeOptionByIndex={{fn @removeOptionByIndex 'filters'}}
|
||||
@disabled={{form.disabled}}
|
||||
/>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
|
||||
<form.input
|
||||
@name='sync_interval_seconds'
|
||||
|
||||
@@ -69,96 +69,38 @@
|
||||
</F.HelperText>
|
||||
</InfoField>
|
||||
|
||||
<Rose::Form::Fieldset
|
||||
@helperText={{t 'resources.host-set.form.preferred_endpoints.help'}}
|
||||
@linkText={{t 'actions.learn-more'}}
|
||||
@link={{doc-url 'host-set.preferred-endpoints'}}
|
||||
<Form::Field::ListWrapper
|
||||
@layout='horizontal'
|
||||
@isOptional={{true}}
|
||||
@disabled={{form.disabled}}
|
||||
>
|
||||
<:title>{{t 'resources.host-set.form.preferred_endpoints.label'}}</:title>
|
||||
<:body>
|
||||
{{#if @model.errors.preferred_endpoints}}
|
||||
<Rose::Form::Errors as |errors|>
|
||||
{{#each @model.errors.preferred_endpoints as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</Rose::Form::Errors>
|
||||
{{/if}}
|
||||
<Rose::Table @style='condensed' as |table|>
|
||||
<table.header as |header|>
|
||||
<header.row @hidden={{true}} as |row|>
|
||||
<row.headerCell>{{t 'titles.value'}}</row.headerCell>
|
||||
<row.headerCell />
|
||||
</header.row>
|
||||
</table.header>
|
||||
<table.body as |body|>
|
||||
{{#each @model.preferred_endpoints as |endpoint i|}}
|
||||
<body.row as |row|>
|
||||
<row.cell>
|
||||
<form.input
|
||||
@type='text'
|
||||
@name='preferred_endpoints'
|
||||
@value={{endpoint.value}}
|
||||
@error={{@model.errors.preferred_endpoints}}
|
||||
@contextual={{true}}
|
||||
placeholder='cidr:2001:4860:4860::8888/32'
|
||||
as |field|
|
||||
>
|
||||
<field.field />
|
||||
</form.input>
|
||||
</row.cell>
|
||||
<row.cell @shrink={{true}}>
|
||||
<Rose::Button
|
||||
@style='warning'
|
||||
@iconOnly='flight-icons/svg/trash-16'
|
||||
title={{t 'actions.remove'}}
|
||||
@disabled={{if
|
||||
@model.isNew
|
||||
false
|
||||
(if form.isEditable false true)
|
||||
}}
|
||||
{{on 'click' (fn @removeItemByIndex 'preferred_endpoints' i)}}
|
||||
>
|
||||
{{t 'actions.remove'}}
|
||||
</Rose::Button>
|
||||
</row.cell>
|
||||
</body.row>
|
||||
{{/each}}
|
||||
<:fieldset as |F|>
|
||||
<F.Legend>
|
||||
{{t 'resources.host-set.form.preferred_endpoints.label'}}
|
||||
</F.Legend>
|
||||
<F.HelperText>
|
||||
{{t 'resources.host-set.form.preferred_endpoints.help'}}
|
||||
</F.HelperText>
|
||||
|
||||
<body.row as |row|>
|
||||
<row.cell>
|
||||
<form.input
|
||||
@type='text'
|
||||
@name='preferred_endpoints'
|
||||
@value={{@model.preferred_endpoint}}
|
||||
placeholder='cidr:2001:4860:4860::8888/32'
|
||||
/>
|
||||
</row.cell>
|
||||
<row.cell @shrink={{true}}>
|
||||
<Rose::Button
|
||||
@style='secondary'
|
||||
title={{t 'actions.add'}}
|
||||
@disabled={{if
|
||||
@model.isNew
|
||||
false
|
||||
(if form.isEditable false true)
|
||||
}}
|
||||
{{on
|
||||
'click'
|
||||
(fn
|
||||
@addStringItem
|
||||
'preferred_endpoints'
|
||||
@model.preferred_endpoint
|
||||
)
|
||||
}}
|
||||
>
|
||||
{{t 'actions.add'}}
|
||||
</Rose::Button>
|
||||
</row.cell>
|
||||
</body.row>
|
||||
</table.body>
|
||||
</Rose::Table>
|
||||
</:body>
|
||||
</Rose::Form::Fieldset>
|
||||
{{#if @model.errors.preferred_endpoints}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.preferred_endpoints as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</:fieldset>
|
||||
|
||||
<:field as |F|>
|
||||
<F.TextInput
|
||||
@name='preferred_endpoints'
|
||||
@options={{@model.preferred_endpoints}}
|
||||
@addOption={{fn @addOption 'preferred_endpoints'}}
|
||||
@removeOptionByIndex={{fn @removeOptionByIndex 'preferred_endpoints'}}
|
||||
@disabled={{form.disabled}}
|
||||
/>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
|
||||
<Hds::Form::Textarea::Field
|
||||
@value={{@model.filter_string}}
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
edit=@edit
|
||||
submit=@submit
|
||||
cancel=@cancel
|
||||
removeItemByIndex=@removeItemByIndex
|
||||
addStringItem=@addStringItem
|
||||
addOption=this.addOption
|
||||
removeOptionByIndex=this.removeOptionByIndex
|
||||
}}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { set } from '@ember/object';
|
||||
|
||||
export default class FormHostSetComponent extends Component {
|
||||
// =attributes
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Adds a string to the array. We recreate a new array after adding
|
||||
* so that ember is aware that the array has been modified.
|
||||
* @param field {string}
|
||||
* @param value {string}
|
||||
*/
|
||||
@action
|
||||
addOption(field, { value }) {
|
||||
const existingArray = this.args.model[field] ?? [];
|
||||
const newArray = [...existingArray, { value }];
|
||||
set(this.args.model, field, newArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes 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 field {string}
|
||||
* @param index {number}
|
||||
*/
|
||||
@action
|
||||
removeOptionByIndex(field, index) {
|
||||
const newArray = this.args.model[field].filter((_, i) => i !== index);
|
||||
set(this.args.model, field, newArray);
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@
|
||||
}}</F.HelperText>
|
||||
{{#let (unique-id) as |valueHeaderID|}}
|
||||
<F.Control>
|
||||
<Hds::Table class='key-value-list' name='group_names'>
|
||||
<Hds::Table class='list-wrapper-field' name='group_names'>
|
||||
<:head as |H|>
|
||||
<H.Tr>
|
||||
<H.Th id={{valueHeaderID}}>{{t 'titles.value'}}</H.Th>
|
||||
|
||||
@@ -366,30 +366,37 @@
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Form::Field::KeyValueList
|
||||
<Form::Field::ListWrapper
|
||||
@layout='horizontal'
|
||||
@isOptional={{true}}
|
||||
@name='role_tags'
|
||||
@options={{@model.role_tags}}
|
||||
@addOption={{fn this.addOption 'role_tags'}}
|
||||
@removeOptionByIndex={{fn this.removeOptionByIndex 'role_tags'}}
|
||||
@disabled={{form.disabled}}
|
||||
as |M|
|
||||
>
|
||||
<M.Legend>
|
||||
{{t 'resources.storage-bucket.form.role_tags.label'}}
|
||||
</M.Legend>
|
||||
<M.HelperText>
|
||||
{{t 'resources.storage-bucket.form.role_tags.help'}}
|
||||
</M.HelperText>
|
||||
{{#if @model.errors.role_tags}}
|
||||
<M.Error as |E|>
|
||||
{{#each @model.errors.role_tags as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</M.Error>
|
||||
{{/if}}
|
||||
</Form::Field::KeyValueList>
|
||||
<:fieldset as |F|>
|
||||
<F.Legend>
|
||||
{{t 'resources.storage-bucket.form.role_tags.label'}}
|
||||
</F.Legend>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.role_tags.label'}}
|
||||
</F.HelperText>
|
||||
|
||||
{{#if @model.errors.role_tags}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.role_tags as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</:fieldset>
|
||||
<:field as |F|>
|
||||
<F.TextInput
|
||||
@name='role_tags'
|
||||
@options={{@model.role_tags}}
|
||||
@addOption={{fn this.addOption 'role_tags'}}
|
||||
@removeOptionByIndex={{fn this.removeOptionByIndex 'role_tags'}}
|
||||
@disabled={{form.disabled}}
|
||||
/>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
{{/if}}
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
|
||||
@@ -44,29 +44,4 @@ export default class ScopesScopeHostCatalogsHostCatalogHostSetsHostSetRoute exte
|
||||
hostSet.filters = structuredClone(hostSet.filters);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a string item `{ value }` to array `property` on passed `hostSet`.
|
||||
* @param {hostSetModel} hostSet
|
||||
* @param {string} property
|
||||
* @param {string} value
|
||||
*/
|
||||
@action
|
||||
async addStringItem(hostSet, property, value) {
|
||||
const array = [...hostSet.get(property), { value }];
|
||||
hostSet.set(property, array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from array `property` at `index` on the
|
||||
* passed `hostSet`.
|
||||
* @param {hostSetModel} hostSet
|
||||
* @param {string} property
|
||||
* @param {number} index
|
||||
*/
|
||||
@action
|
||||
async removeItemByIndex(hostSet, property, index) {
|
||||
const array = hostSet.get(property).filter((item, i) => i !== index);
|
||||
hostSet.set(property, array);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
import Route from '@ember/routing/route';
|
||||
import { action } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class ScopesScopeHostCatalogsHostCatalogHostSetsNewRoute extends Route {
|
||||
@@ -27,30 +26,4 @@ export default class ScopesScopeHostCatalogsHostCatalogHostSetsNewRoute extends
|
||||
host_catalog_id,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a string item to array `property` on the passed `filter`.
|
||||
|
||||
* @param {hostSetModel} hostSet
|
||||
* @param {string} property
|
||||
* @param {string} value
|
||||
*/
|
||||
@action
|
||||
async addStringItem(hostSet, property, value) {
|
||||
const existingArray = hostSet[property] ?? [];
|
||||
const array = [...existingArray, { value }];
|
||||
hostSet.set(property, array);
|
||||
}
|
||||
/**
|
||||
* Removes an item from array `property` at `index` on the
|
||||
* passed `hostSet`.
|
||||
* @param {hostSetModel} hostSet
|
||||
* @param {string} property
|
||||
* @param {number} index
|
||||
*/
|
||||
@action
|
||||
async removeItemByIndex(hostSet, property, index) {
|
||||
const array = hostSet.get(property).filter((item, i) => i !== index);
|
||||
hostSet.set(property, array);
|
||||
}
|
||||
}
|
||||
@@ -687,7 +687,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.key-value-list {
|
||||
.list-wrapper-field {
|
||||
border: 0;
|
||||
|
||||
/* stylelint-disable selector-class-pattern */
|
||||
|
||||
-2
@@ -28,8 +28,6 @@
|
||||
@edit={{route-action 'edit' @model}}
|
||||
@submit={{route-action 'save' @model}}
|
||||
@cancel={{route-action 'cancel' @model}}
|
||||
@removeItemByIndex={{route-action 'removeItemByIndex' @model}}
|
||||
@addStringItem={{route-action 'addStringItem' @model}}
|
||||
/>
|
||||
</page.body>
|
||||
</Rose::Layout::Page>
|
||||
@@ -23,8 +23,6 @@
|
||||
@model={{@model}}
|
||||
@submit={{route-action 'save' @model}}
|
||||
@cancel={{route-action 'cancel' @model}}
|
||||
@removeItemByIndex={{route-action 'removeItemByIndex' @model}}
|
||||
@addStringItem={{route-action 'addStringItem' @model}}
|
||||
/>
|
||||
</page.body>
|
||||
</Rose::Layout::Page>
|
||||
@@ -19,6 +19,18 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
const PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR =
|
||||
'[name="preferred_endpoints"] input';
|
||||
const PREFERRED_ENDPOINT_BUTTON_SELECTOR =
|
||||
'[name="preferred_endpoints"] button';
|
||||
const FILTER_TEXT_INPUT_SELECTOR = '[name="filters"] input';
|
||||
const FILTER_BUTTON_SELECTOR = '[name="filters"] button';
|
||||
const AZURE_FILTER_SELECTOR = '[name="filter"]';
|
||||
const SYNC_INTERVAL_SELECTOR = '[name="sync_interval_seconds"]';
|
||||
const SUBMIT_BTN_SELECTOR = '.rose-form-actions [type="submit"]';
|
||||
const CANCEL_BTN_SELECTOR = '.rose-form-actions [type="button"]';
|
||||
const NAME_SELECTOR = '[name="name"]';
|
||||
|
||||
let getHostSetCount;
|
||||
|
||||
const instances = {
|
||||
@@ -79,8 +91,8 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) {
|
||||
assert.expect(1);
|
||||
const count = getHostSetCount();
|
||||
await visit(urls.newHostSet);
|
||||
await fillIn('[name="name"]', 'random string');
|
||||
await click('[type="submit"]');
|
||||
await fillIn(NAME_SELECTOR, 'random string');
|
||||
await click(SUBMIT_BTN_SELECTOR);
|
||||
assert.strictEqual(getHostSetCount(), count + 1);
|
||||
});
|
||||
|
||||
@@ -100,18 +112,19 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) {
|
||||
`${urls.hostCatalogs}/${instances.hostCatalog.id}/host-sets/new`
|
||||
);
|
||||
const name = 'aws host set';
|
||||
await fillIn('[name="name"]', name);
|
||||
await fillIn('[name="preferred_endpoints"]', 'endpoint');
|
||||
await click('form fieldset:nth-of-type(1) [title="Add"]');
|
||||
await fillIn('[name="filters"]', 'filter');
|
||||
await click('form fieldset:nth-of-type(2) [title="Add"]');
|
||||
await fillIn('[name="sync_interval_seconds"]', 10);
|
||||
await click('[type="submit"]');
|
||||
await fillIn(NAME_SELECTOR, name);
|
||||
await fillIn(PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR, 'endpoint');
|
||||
await click(PREFERRED_ENDPOINT_BUTTON_SELECTOR);
|
||||
await fillIn(FILTER_TEXT_INPUT_SELECTOR, 'filter_test');
|
||||
await click(FILTER_BUTTON_SELECTOR);
|
||||
|
||||
await fillIn(SYNC_INTERVAL_SELECTOR, 10);
|
||||
await click(SUBMIT_BTN_SELECTOR);
|
||||
|
||||
assert.strictEqual(getHostSetCount(), count + 1);
|
||||
const hostSet = this.server.schema.hostSets.findBy({ name });
|
||||
assert.deepEqual(hostSet.preferredEndpoints, ['endpoint']);
|
||||
assert.deepEqual(hostSet.attributes.filters, ['filter']);
|
||||
assert.deepEqual(hostSet.attributes.filters, ['filter_test']);
|
||||
assert.deepEqual(hostSet.syncIntervalSeconds, 10);
|
||||
});
|
||||
|
||||
@@ -131,12 +144,12 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) {
|
||||
`${urls.hostCatalogs}/${instances.hostCatalog.id}/host-sets/new`
|
||||
);
|
||||
const name = 'azure host set';
|
||||
await fillIn('[name="name"]', name);
|
||||
await fillIn('[name="preferred_endpoints"]', 'endpoint');
|
||||
await click('form fieldset:nth-of-type(1) [title="Add"]');
|
||||
await fillIn('[name="filter"]', 'filter');
|
||||
await fillIn('[name="sync_interval_seconds"]', 10);
|
||||
await click('[type="submit"]');
|
||||
await fillIn(NAME_SELECTOR, name);
|
||||
await fillIn(PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR, 'endpoint');
|
||||
await click(PREFERRED_ENDPOINT_BUTTON_SELECTOR);
|
||||
await fillIn(AZURE_FILTER_SELECTOR, 'filter');
|
||||
await fillIn(SYNC_INTERVAL_SELECTOR, 10);
|
||||
await click(SUBMIT_BTN_SELECTOR);
|
||||
|
||||
assert.strictEqual(getHostSetCount(), count + 1);
|
||||
const hostSet = this.server.schema.hostSets.findBy({ name });
|
||||
@@ -173,8 +186,8 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) {
|
||||
assert.expect(2);
|
||||
const count = getHostSetCount();
|
||||
await visit(urls.newHostSet);
|
||||
await fillIn('[name="name"]', 'random string');
|
||||
await click('.rose-form-actions [type="button"]');
|
||||
await fillIn(NAME_SELECTOR, 'random string');
|
||||
await click(CANCEL_BTN_SELECTOR);
|
||||
assert.strictEqual(currentURL(), urls.hostSets);
|
||||
assert.strictEqual(getHostSetCount(), count);
|
||||
});
|
||||
@@ -201,7 +214,7 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) {
|
||||
);
|
||||
});
|
||||
await visit(urls.newHostSet);
|
||||
await click('[type="submit"]');
|
||||
await click(SUBMIT_BTN_SELECTOR);
|
||||
assert.ok(
|
||||
find('[role="alert"]').textContent.trim(),
|
||||
'The request was invalid.'
|
||||
|
||||
@@ -26,6 +26,20 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
const PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR =
|
||||
'[name="preferred_endpoints"] input';
|
||||
const PREFERRED_ENDPOINT_BUTTON_SELECTOR =
|
||||
'[name="preferred_endpoints"] button';
|
||||
const PREFERRED_ENDPOINT_REMOVE_BUTTON_SELECTOR =
|
||||
'[name="preferred_endpoints"] [data-test-remove-button]';
|
||||
const FILTER_TEXT_INPUT_SELECTOR = '[name="filters"] input';
|
||||
const FILTER_BUTTON_SELECTOR = '[name="filters"] button';
|
||||
const FILTER_REMOVE_BUTTON_SELECTOR =
|
||||
'[name="filters"] [data-test-remove-button]';
|
||||
const SYNC_INTERVAL_SELECTOR = '[name="sync_interval_seconds"]';
|
||||
const SUBMIT_BTN_SELECTOR = '.rose-form-actions [type="submit"]';
|
||||
const AZURE_FILTER_SELECTOR = '[name="filter"]';
|
||||
const EDIT_BUTTON_SELECTOR = 'form .rose-form-actions [type="button"]';
|
||||
const instances = {
|
||||
scopes: {
|
||||
global: null,
|
||||
@@ -33,6 +47,10 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) {
|
||||
project: null,
|
||||
hostCatalog: null,
|
||||
host: null,
|
||||
awsHostCatalog: null,
|
||||
awsHostSet: null,
|
||||
azureHostCatalog: null,
|
||||
azureHostSet: null,
|
||||
},
|
||||
};
|
||||
const urls = {
|
||||
@@ -45,6 +63,8 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) {
|
||||
hostSet: null,
|
||||
unknownHostSet: null,
|
||||
newHostSet: null,
|
||||
awshostSet: null,
|
||||
azureHostSet: null,
|
||||
};
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
@@ -61,10 +81,36 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) {
|
||||
instances.hostCatalog = this.server.create('host-catalog', {
|
||||
scope: instances.scopes.project,
|
||||
});
|
||||
|
||||
instances.hostSet = this.server.create('host-set', {
|
||||
scope: instances.scopes.project,
|
||||
hostCatalog: instances.hostCatalog,
|
||||
});
|
||||
instances.awsHostCatalog = this.server.create('host-catalog', {
|
||||
scope: instances.scopes.project,
|
||||
type: 'plugin',
|
||||
plugin: {
|
||||
id: `plugin-id-1`,
|
||||
name: 'aws',
|
||||
},
|
||||
});
|
||||
instances.awsHostSet = this.server.create('host-set', {
|
||||
scope: instances.scopes.project,
|
||||
hostCatalog: instances.awsHostCatalog,
|
||||
});
|
||||
|
||||
instances.azureHostCatalog = this.server.create('host-catalog', {
|
||||
scope: instances.scopes.project,
|
||||
type: 'plugin',
|
||||
plugin: {
|
||||
id: `plugin-id-1`,
|
||||
name: 'azure',
|
||||
},
|
||||
});
|
||||
instances.azureHostSet = this.server.create('host-set', {
|
||||
scope: instances.scopes.project,
|
||||
hostCatalog: instances.azureHostCatalog,
|
||||
});
|
||||
// Generate route URLs for resources
|
||||
urls.globalScope = `/scopes/global/scopes`;
|
||||
urls.orgScope = `/scopes/${instances.scopes.org.id}/scopes`;
|
||||
@@ -75,6 +121,8 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) {
|
||||
urls.hostSet = `${urls.hostSets}/${instances.hostSet.id}`;
|
||||
urls.unknownHostSet = `${urls.hostSets}/foo`;
|
||||
urls.newHostSet = `${urls.hostSets}/new`;
|
||||
urls.awshostSet = `${urls.hostCatalogs}/${instances.awsHostCatalog.id}/host-sets/${instances.awsHostSet.id}`;
|
||||
urls.azureHostSet = `${urls.hostCatalogs}/${instances.azureHostCatalog.id}/host-sets/${instances.azureHostSet.id}`;
|
||||
// Generate resource couner
|
||||
authenticateSession({});
|
||||
});
|
||||
@@ -101,7 +149,7 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) {
|
||||
);
|
||||
});
|
||||
await visit(urls.newHostSet);
|
||||
await click('[type="submit"]');
|
||||
await click(SUBMIT_BTN_SELECTOR);
|
||||
assert.ok(
|
||||
find('[role="alert"]').textContent.trim(),
|
||||
'The request was invalid.'
|
||||
@@ -116,9 +164,9 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) {
|
||||
assert.expect(3);
|
||||
assert.notEqual(instances.hostSet.name, 'random string');
|
||||
await visit(urls.hostSet);
|
||||
await click('form [type="button"]', 'Activate edit mode');
|
||||
await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode');
|
||||
await fillIn('[name="name"]', 'random string');
|
||||
await click('.rose-form-actions [type="submit"]');
|
||||
await click(SUBMIT_BTN_SELECTOR);
|
||||
assert.strictEqual(currentURL(), urls.hostSet);
|
||||
assert.strictEqual(
|
||||
this.server.schema.hostSets.first().name,
|
||||
@@ -127,96 +175,89 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) {
|
||||
});
|
||||
|
||||
test('can save changes to an existing aws host-set', async function (assert) {
|
||||
assert.expect(5);
|
||||
instances.hostCatalog = this.server.create('host-catalog', {
|
||||
scope: instances.scopes.project,
|
||||
type: 'plugin',
|
||||
plugin: {
|
||||
id: `plugin-id-1`,
|
||||
name: 'aws',
|
||||
},
|
||||
});
|
||||
instances.hostSet = this.server.create('host-set', {
|
||||
scope: instances.scopes.project,
|
||||
hostCatalog: instances.hostCatalog,
|
||||
});
|
||||
urls.hostSet = `${urls.hostCatalogs}/${instances.hostCatalog.id}/host-sets/${instances.hostSet.id}`;
|
||||
await visit(urls.hostSet);
|
||||
assert.expect(7);
|
||||
|
||||
await click(
|
||||
'form .rose-form-actions [type="button"]',
|
||||
'Activate edit mode'
|
||||
);
|
||||
await visit(urls.awshostSet);
|
||||
|
||||
await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode');
|
||||
|
||||
const name = 'aws host set';
|
||||
await fillIn('[name="name"]', name);
|
||||
// Remove all the preferred endpoints
|
||||
await Promise.all(
|
||||
findAll('form fieldset:nth-of-type(1) [title="Remove"]').map((element) =>
|
||||
click(element)
|
||||
)
|
||||
);
|
||||
await fillIn('[name="preferred_endpoints"]', 'endpoint');
|
||||
await click('form fieldset:nth-of-type(1) [title="Add"]');
|
||||
// Remove all the filters
|
||||
await Promise.all(
|
||||
findAll('form fieldset:nth-of-type(2) [title="Remove"]').map((element) =>
|
||||
click(element)
|
||||
)
|
||||
);
|
||||
await fillIn('[name="filters"]', 'filter');
|
||||
await click('form fieldset:nth-of-type(2) [title="Add"]');
|
||||
await fillIn('[name="sync_interval_seconds"]', 10);
|
||||
await click('.rose-form-actions [type="submit"]');
|
||||
|
||||
assert.strictEqual(currentURL(), urls.hostSet);
|
||||
const endpointList = await Promise.all(
|
||||
findAll(PREFERRED_ENDPOINT_REMOVE_BUTTON_SELECTOR)
|
||||
);
|
||||
|
||||
for (const element of endpointList) {
|
||||
await click(element);
|
||||
}
|
||||
|
||||
assert.strictEqual(
|
||||
findAll(PREFERRED_ENDPOINT_REMOVE_BUTTON_SELECTOR).length,
|
||||
0
|
||||
);
|
||||
|
||||
await fillIn(PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR, 'sample endpoint');
|
||||
await click(PREFERRED_ENDPOINT_BUTTON_SELECTOR);
|
||||
|
||||
// Remove all the filters
|
||||
const filterList = await Promise.all(
|
||||
findAll(FILTER_REMOVE_BUTTON_SELECTOR)
|
||||
);
|
||||
for (const element of filterList) {
|
||||
await click(element);
|
||||
}
|
||||
|
||||
assert.strictEqual(findAll(FILTER_REMOVE_BUTTON_SELECTOR).length, 0);
|
||||
await fillIn(FILTER_TEXT_INPUT_SELECTOR, 'sample filters');
|
||||
await click(FILTER_BUTTON_SELECTOR);
|
||||
|
||||
await fillIn(SYNC_INTERVAL_SELECTOR, 10);
|
||||
|
||||
await click(SUBMIT_BTN_SELECTOR);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.awshostSet);
|
||||
const hostSet = this.server.schema.hostSets.findBy({ name });
|
||||
assert.strictEqual(hostSet.name, name);
|
||||
assert.deepEqual(hostSet.preferredEndpoints, ['endpoint']);
|
||||
assert.deepEqual(hostSet.attributes.filters, ['filter']);
|
||||
assert.deepEqual(hostSet.preferredEndpoints, ['sample endpoint']);
|
||||
assert.deepEqual(hostSet.attributes.filters, ['sample filters']);
|
||||
assert.deepEqual(hostSet.syncIntervalSeconds, 10);
|
||||
});
|
||||
|
||||
test('can save changes to an existing azure host-set', async function (assert) {
|
||||
assert.expect(5);
|
||||
instances.hostCatalog = this.server.create('host-catalog', {
|
||||
scope: instances.scopes.project,
|
||||
type: 'plugin',
|
||||
plugin: {
|
||||
id: `plugin-id-1`,
|
||||
name: 'azure',
|
||||
},
|
||||
});
|
||||
instances.hostSet = this.server.create('host-set', {
|
||||
scope: instances.scopes.project,
|
||||
hostCatalog: instances.hostCatalog,
|
||||
});
|
||||
urls.hostSet = `${urls.hostCatalogs}/${instances.hostCatalog.id}/host-sets/${instances.hostSet.id}`;
|
||||
await visit(urls.hostSet);
|
||||
assert.expect(6);
|
||||
|
||||
await click(
|
||||
'form .rose-form-actions [type="button"]',
|
||||
'Activate edit mode'
|
||||
);
|
||||
await visit(urls.azureHostSet);
|
||||
|
||||
await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode');
|
||||
|
||||
const name = 'azure host set';
|
||||
await fillIn('[name="name"]', name);
|
||||
// Remove all the preferred endpoints
|
||||
await Promise.all(
|
||||
findAll('form fieldset:nth-of-type(1) [title="Remove"]').map((element) =>
|
||||
click(element)
|
||||
)
|
||||
);
|
||||
await fillIn('[name="preferred_endpoints"]', 'endpoint');
|
||||
await click('form fieldset:nth-of-type(1) [title="Add"]');
|
||||
await fillIn('[name="filter"]', 'filter');
|
||||
await fillIn('[name="sync_interval_seconds"]', 10);
|
||||
await click('.rose-form-actions [type="submit"]');
|
||||
|
||||
assert.strictEqual(currentURL(), urls.hostSet);
|
||||
const endpointList = await Promise.all(
|
||||
findAll(PREFERRED_ENDPOINT_REMOVE_BUTTON_SELECTOR)
|
||||
);
|
||||
|
||||
for (const element of endpointList) {
|
||||
await click(element);
|
||||
}
|
||||
|
||||
assert.strictEqual(
|
||||
findAll(PREFERRED_ENDPOINT_REMOVE_BUTTON_SELECTOR).length,
|
||||
0
|
||||
);
|
||||
await fillIn(PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR, 'sample endpoints');
|
||||
await click(PREFERRED_ENDPOINT_BUTTON_SELECTOR);
|
||||
|
||||
await fillIn(AZURE_FILTER_SELECTOR, 'filter');
|
||||
await fillIn(SYNC_INTERVAL_SELECTOR, 10);
|
||||
await click(SUBMIT_BTN_SELECTOR);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.azureHostSet);
|
||||
const hostSet = this.server.schema.hostSets.findBy({ name });
|
||||
assert.strictEqual(hostSet.name, name);
|
||||
assert.deepEqual(hostSet.preferredEndpoints, ['endpoint']);
|
||||
assert.deepEqual(hostSet.preferredEndpoints, ['sample endpoints']);
|
||||
assert.deepEqual(hostSet.attributes.filter, 'filter');
|
||||
assert.deepEqual(hostSet.syncIntervalSeconds, 10);
|
||||
});
|
||||
@@ -232,7 +273,7 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) {
|
||||
test('can cancel changes to existing host-set', async function (assert) {
|
||||
assert.expect(2);
|
||||
await visit(urls.hostSet);
|
||||
await click('form [type="button"]', 'Activate edit mode');
|
||||
await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode');
|
||||
await fillIn('[name="name"]', 'random string');
|
||||
await click('.rose-form-actions [type="button"]');
|
||||
assert.notEqual(instances.hostSet.name, 'random string');
|
||||
@@ -261,9 +302,9 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) {
|
||||
);
|
||||
});
|
||||
await visit(urls.hostSet);
|
||||
await click('form [type="button"]', 'Activate edit mode');
|
||||
await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode');
|
||||
await fillIn('[name="name"]', 'random string');
|
||||
await click('[type="submit"]');
|
||||
await click(SUBMIT_BTN_SELECTOR);
|
||||
assert.ok(
|
||||
find('[role="alert"]').textContent.trim(),
|
||||
'The request was invalid.'
|
||||
@@ -280,7 +321,7 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) {
|
||||
confirmService.enabled = true;
|
||||
assert.notEqual(instances.hostSet.name, 'random string');
|
||||
await visit(urls.hostSet);
|
||||
await click('form [type="button"]', 'Activate edit mode');
|
||||
await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode');
|
||||
await fillIn('[name="name"]', 'random string');
|
||||
assert.strictEqual(currentURL(), urls.hostSet);
|
||||
try {
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { setupIntl } from 'ember-intl/test-support';
|
||||
|
||||
module('Integration | Component | key-value-list', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
setupIntl(hooks);
|
||||
|
||||
test('it renders fields', async function (assert) {
|
||||
await render(hbs`
|
||||
<Form::Field::KeyValueList as |M|>
|
||||
<M.Legend>Label</M.Legend>
|
||||
<M.HelperText>Help</M.HelperText>
|
||||
<M.Error as |E|>
|
||||
<E.Message>Error!</E.Message>
|
||||
</M.Error>
|
||||
</Form::Field::KeyValueList>
|
||||
`);
|
||||
|
||||
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', async function (assert) {
|
||||
this.options = [
|
||||
{ key: 'one', value: 'two' },
|
||||
{ key: 'three', value: 'four' },
|
||||
];
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<Form::Field::KeyValueList
|
||||
@options={{this.options}}
|
||||
/>
|
||||
`);
|
||||
|
||||
assert.dom('tbody tr').exists({ count: 3 });
|
||||
});
|
||||
|
||||
test('it renders multiple options with only keys', async function (assert) {
|
||||
this.options = [{ key: 'one' }, { key: 'three' }];
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<Form::Field::KeyValueList
|
||||
@options={{this.options}}
|
||||
@hasNoValue={{true}}
|
||||
/>
|
||||
`);
|
||||
|
||||
assert.dom('tbody tr:first-child td').exists({ count: 2 });
|
||||
assert.dom('tbody tr').exists({ count: 3 });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } 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);
|
||||
|
||||
test('it renders fields', async function (assert) {
|
||||
this.error = [
|
||||
{
|
||||
message: 'Error!',
|
||||
},
|
||||
];
|
||||
await render(hbs`
|
||||
<Form::Field::ListWrapper>
|
||||
<:fieldset as |F|>
|
||||
<F.Legend>
|
||||
Label
|
||||
</F.Legend>
|
||||
<F.HelperText>
|
||||
Help
|
||||
</F.HelperText>
|
||||
<F.Error as |E|>
|
||||
<E.Message>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}}></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' }];
|
||||
this.fn = () => {};
|
||||
|
||||
await render(hbs`
|
||||
|
||||
<Form::Field::ListWrapper>
|
||||
<:field as |F|>
|
||||
<F.TextInput
|
||||
@options={{this.options}}
|
||||
@removeOptionByIndex={{this.fn}}>
|
||||
</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 });
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user