feat: 🎸 create Host Modal for connecting to specific modal (#1898)
✅ Closes: ICU-10432
This commit is contained in:
1 parent
02ce3341bd
commit
c5cffd5137
9 files changed
+285
-3
No files matched your search
@@ -5,6 +5,7 @@
|
||||
|
||||
import GeneratedHostModel from '../generated/models/host';
|
||||
import { attr } from '@ember-data/model';
|
||||
import { pluginTypes } from './host-catalog';
|
||||
|
||||
export default class HostModel extends GeneratedHostModel {
|
||||
// =attributes
|
||||
@@ -58,6 +59,14 @@ export default class HostModel extends GeneratedHostModel {
|
||||
return this.type === 'plugin';
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the host catalog is an unknown type.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isUnknown() {
|
||||
return this.isPlugin && !pluginTypes.includes(this.plugin?.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the host plugin type is AWS.
|
||||
* @type {boolean}
|
||||
|
||||
@@ -48,6 +48,28 @@ module('Unit | Model | host', function (hooks) {
|
||||
assert.false(modelStatic.isPlugin);
|
||||
});
|
||||
|
||||
test('it has isUnknown and returns the expected values', async function (assert) {
|
||||
assert.expect(4);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const modelAws = store.createRecord('host', {
|
||||
type: 'plugin',
|
||||
plugin: { name: 'aws' },
|
||||
});
|
||||
const modelAzure = store.createRecord('host', {
|
||||
type: 'plugin',
|
||||
plugin: { name: 'azure' },
|
||||
});
|
||||
const modelRandom = store.createRecord('host', {
|
||||
type: 'plugin',
|
||||
plugin: { name: 'random' },
|
||||
});
|
||||
|
||||
assert.strictEqual(typeof modelRandom.isUnknown, 'boolean');
|
||||
assert.true(modelRandom.isUnknown);
|
||||
assert.false(modelAws.isUnknown);
|
||||
assert.false(modelAzure.isUnknown);
|
||||
});
|
||||
|
||||
test('it has isAWS and returns the expected values', async function (assert) {
|
||||
assert.expect(3);
|
||||
const store = this.owner.lookup('service:store');
|
||||
|
||||
@@ -295,6 +295,8 @@ session:
|
||||
actions:
|
||||
connect: Connect
|
||||
end: End session
|
||||
host: Choose a Host
|
||||
quick-connect: Quick Connect
|
||||
credential:
|
||||
title: Credential
|
||||
title_plural: Credentials
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
}}
|
||||
|
||||
{{#if @showModal}}
|
||||
<Hds::Modal data-test-host-modal @onClose={{@toggle}} as |M|>
|
||||
<M.Header @tagline={{@target.displayName}}>
|
||||
{{t 'resources.session.actions.host'}}
|
||||
</M.Header>
|
||||
<M.Body>
|
||||
<ul class='host-list-container'>
|
||||
{{#each @hosts as |host|}}
|
||||
<li class='host-container'>
|
||||
<button
|
||||
data-test-host-connect
|
||||
type='button'
|
||||
{{on 'click' (fn @connect @target host)}}
|
||||
>
|
||||
<div class='host-info-container'>
|
||||
<Hds::IconTile
|
||||
@color='boundary'
|
||||
@icon='network-alt'
|
||||
@size='small'
|
||||
/>
|
||||
<div class='host-info'>
|
||||
<p
|
||||
class='hds-typography-body-200 hds-font-weight-semibold hds-foreground-primary'
|
||||
>
|
||||
{{host.displayName}}
|
||||
</p>
|
||||
{{#if host.isStatic}}
|
||||
<p class='hds-typography-body-100 hds-foreground-faint'>
|
||||
{{host.address}}
|
||||
</p>
|
||||
{{/if}}
|
||||
{{#if host.isPlugin}}
|
||||
{{#if host.isUnknown}}
|
||||
<Hds::Badge
|
||||
@text={{t 'resources.host-catalog.types.unknown'}}
|
||||
/>
|
||||
{{else}}
|
||||
<Hds::Badge
|
||||
@text={{t 'resources.host-catalog.types.plugin'}}
|
||||
@icon={{host.plugin.name}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if host.description}}
|
||||
<p class='hds-typography-body-100 hds-foreground-faint'>
|
||||
{{host.description}}
|
||||
</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<FlightIcon class='host-arrow' @name='arrow-right' />
|
||||
</button>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</M.Body>
|
||||
<M.Footer>
|
||||
<Hds::Button
|
||||
data-test-host-quick-connect
|
||||
type='button'
|
||||
@icon='entry-point'
|
||||
@iconPosition='trailing'
|
||||
@text={{t 'resources.session.actions.quick-connect'}}
|
||||
{{on 'click' (fn @connect @target)}}
|
||||
/>
|
||||
</M.Footer>
|
||||
</Hds::Modal>
|
||||
{{/if}}
|
||||
@@ -4,11 +4,20 @@
|
||||
*/
|
||||
|
||||
import Controller from '@ember/controller';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class ScopesScopeProjectsTargetsTargetController extends Controller {
|
||||
// =attributes
|
||||
|
||||
queryParams = [{ isConnecting: { type: 'boolean' } }];
|
||||
|
||||
isConnecting = false;
|
||||
@tracked isConnecting = false;
|
||||
|
||||
// =methods
|
||||
|
||||
@action
|
||||
toggle() {
|
||||
this.isConnecting = !this.isConnecting;
|
||||
}
|
||||
}
|
||||
@@ -71,9 +71,11 @@ export default class ScopesScopeProjectsTargetsTargetRoute extends Route {
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
async preConnect(model) {
|
||||
async preConnect(model, toggleModal) {
|
||||
if (model.target.address || model.hosts.length === 1) {
|
||||
await this.connect(model.target);
|
||||
} else {
|
||||
toggleModal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -653,6 +653,38 @@ $chevron-down: url("data:image/svg+xml;utf-8,<svg viewBox='0 0 24 24' fill='%231
|
||||
}
|
||||
}
|
||||
|
||||
// Host Modal
|
||||
.host-list-container {
|
||||
border: 1px solid var(--token-color-border-primary);
|
||||
padding: 0.25rem;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.host-container {
|
||||
button {
|
||||
background: unset;
|
||||
border: unset;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
|
||||
.host-info-container {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
|
||||
.host-info {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.host-arrow {
|
||||
color: var(--token-color-foreground-faint);
|
||||
}
|
||||
}
|
||||
|
||||
#terminal-container {
|
||||
.terminal.xterm .xterm-viewport {
|
||||
border-radius: 8px;
|
||||
|
||||
@@ -20,12 +20,19 @@
|
||||
@text={{t 'resources.session.actions.connect'}}
|
||||
@icon='entry-point'
|
||||
@iconPosition='trailing'
|
||||
{{on 'click' (route-action 'preConnect' @model)}}
|
||||
{{on 'click' (route-action 'preConnect' @model this.toggle)}}
|
||||
/>
|
||||
</page.actions>
|
||||
|
||||
<page.body class='details-screen-body'>
|
||||
<Rose::Layout::BodyContent as |bc|>
|
||||
<Target::HostModal
|
||||
@showModal={{this.isConnecting}}
|
||||
@toggle={{this.toggle}}
|
||||
@connect={{route-action 'connect'}}
|
||||
@hosts={{@model.hosts}}
|
||||
@target={{@model.target}}
|
||||
/>
|
||||
<bc.Body class='target-details-body'>
|
||||
<Hds::ApplicationState as |A|>
|
||||
<A.Header
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'desktop/tests/helpers';
|
||||
import { render, click } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { setupIntl } from 'ember-intl/test-support';
|
||||
|
||||
module('Integration | Component | target/host-modal', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
setupIntl(hooks);
|
||||
|
||||
let store;
|
||||
let target;
|
||||
let hostA;
|
||||
let hostB;
|
||||
let connect;
|
||||
let toggle;
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
connect = () => {
|
||||
this.set('called', true);
|
||||
};
|
||||
toggle = () => {
|
||||
this.set('called', true);
|
||||
};
|
||||
store = this.owner.lookup('service:store');
|
||||
target = store.createRecord('target', {
|
||||
name: 'User',
|
||||
});
|
||||
hostA = store.createRecord('host', {
|
||||
name: 'Host A',
|
||||
compositeType: 'static',
|
||||
address: '188e:68a9:b342:c05c:2595:2f46:499c:759f',
|
||||
});
|
||||
hostB = store.createRecord('host', {
|
||||
name: 'Host B',
|
||||
compositeType: 'static',
|
||||
description: 'Description of host B',
|
||||
address: '188e:68a9:b342:c05c:2595:499c:759f:2f46',
|
||||
});
|
||||
});
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.set('connect', connect);
|
||||
this.set('toggle', toggle);
|
||||
this.set('hosts', [hostA, hostB]);
|
||||
this.set('target', target);
|
||||
|
||||
await render(hbs`<Target::HostModal
|
||||
@showModal={{true}}
|
||||
@toggle={{this.toggle}}
|
||||
@connect={{this.connect}}
|
||||
@hosts={{this.hosts}}
|
||||
@target={{this.target}} />`);
|
||||
|
||||
assert.dom('.hds-modal__tagline').hasText(target.displayName);
|
||||
});
|
||||
|
||||
test('it closes model when dismissed', async function (assert) {
|
||||
assert.expect(2);
|
||||
const toggleModal = () => {
|
||||
this.set('showModal', false);
|
||||
};
|
||||
this.set('connect', connect);
|
||||
this.set('toggle', toggleModal);
|
||||
this.set('showModal', true);
|
||||
this.set('hosts', [hostA, hostB]);
|
||||
this.set('target', target);
|
||||
|
||||
await render(hbs`<Target::HostModal
|
||||
@showModal={{this.showModal}}
|
||||
@toggle={{this.toggle}}
|
||||
@connect={{this.connect}}
|
||||
@hosts={{this.hosts}}
|
||||
@target={{this.target}} />`);
|
||||
|
||||
assert.dom('.hds-modal__tagline').hasText(target.displayName);
|
||||
|
||||
await click('.hds-dismiss-button');
|
||||
|
||||
assert.dom('.hds-modal__tagline').doesNotExist();
|
||||
});
|
||||
|
||||
test('it calls connect when Quick Connect button clicked', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.set('connect', connect);
|
||||
this.set('toggle', toggle);
|
||||
this.set('hosts', [hostA, hostB]);
|
||||
this.set('target', target);
|
||||
|
||||
await render(hbs`<Target::HostModal
|
||||
@showModal={{true}}
|
||||
@toggle={{this.toggle}}
|
||||
@connect={{this.connect}}
|
||||
@hosts={{this.hosts}}
|
||||
@target={{this.target}} />`);
|
||||
|
||||
await click('[data-test-host-quick-connect]');
|
||||
|
||||
assert.true(this.called);
|
||||
});
|
||||
|
||||
test('it calls connect when host item is clicked', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.set('connect', connect);
|
||||
this.set('toggle', toggle);
|
||||
this.set('hosts', [hostA, hostB]);
|
||||
this.set('target', target);
|
||||
|
||||
await render(hbs`<Target::HostModal
|
||||
@showModal={{true}}
|
||||
@toggle={{this.toggle}}
|
||||
@connect={{this.connect}}
|
||||
@hosts={{this.hosts}}
|
||||
@target={{this.target}} />`);
|
||||
|
||||
await click('[data-test-host-connect]');
|
||||
|
||||
assert.true(this.called);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user