mirror of
https://github.com/status-im/consul.git
synced 2025-01-11 06:16:08 +00:00
Merge pull request #8080 from hashicorp/ui-staging
ui: UI Release Merge (1.8-beta-3: ui-staging merge)
This commit is contained in:
commit
4bde23b9e9
@ -1,11 +1,93 @@
|
|||||||
{{#if item.Kind}}
|
{{#if item.Kind}}
|
||||||
|
{{#let (titleize (humanize item.Kind)) as |Name|}}
|
||||||
{{#if (has-block)}}
|
{{#if (has-block)}}
|
||||||
{{yield
|
{{yield
|
||||||
(component 'consul-kind' item=item)
|
(component 'consul-kind' item=item withInfo=withInfo)
|
||||||
}}
|
}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
{{#if withInfo}}
|
||||||
|
<dl class="tooltip-panel">
|
||||||
|
<dt>
|
||||||
<span data-test-kind={{item.Kind}} class="consul-kind gateway">
|
<span data-test-kind={{item.Kind}} class="consul-kind gateway">
|
||||||
<span>{{titleize (humanize item.Kind)}}</span>
|
<span>{{Name}}</span>
|
||||||
|
</span>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
<MenuPanel @position="left">
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
{{#if (eq item.Kind 'ingress-gateway')}}
|
||||||
|
Ingress gateways enable ingress traffic from services outside the Consul service mesh to services inside the Consul service mesh.
|
||||||
|
{{else if (eq item.Kind 'terminating-gateway')}}
|
||||||
|
Terminating gateways allow connect-enabled services in Consul service mesh to communicate with services outside the service mesh.
|
||||||
|
{{else}}
|
||||||
|
Mesh gateways enable routing of Connect traffic between different Consul datacenters.
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="menu">
|
||||||
|
<li role="separator">
|
||||||
|
{{#if (eq item.Kind 'ingress-gateway')}}
|
||||||
|
About Ingress gateways
|
||||||
|
{{else if (eq item.Kind 'terminating-gateway')}}
|
||||||
|
About Terminating gateways
|
||||||
|
{{else}}
|
||||||
|
About Mesh gateways
|
||||||
|
{{/if}}
|
||||||
|
</li>
|
||||||
|
{{#let (from-entries
|
||||||
|
(array 'ingress-gateway' '/consul/developer-mesh/ingress-gateways')
|
||||||
|
(array 'terminating-gateway' '/consul/developer-mesh/understand-terminating-gateways')
|
||||||
|
(array 'mesh-gateway' '/consul/developer-mesh/connect-gateways')
|
||||||
|
) as |link|}}
|
||||||
|
<li role="none" class="learn-link">
|
||||||
|
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_LEARN_URL') (get link item.Kind)}} rel="noopener noreferrer" target="_blank">
|
||||||
|
Learn guides
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{/let}}
|
||||||
|
{{#let (from-entries
|
||||||
|
(array 'ingress-gateway' '/connect/ingress_gateway.html')
|
||||||
|
(array 'terminating-gateway' '/connect/terminating_gateway.html')
|
||||||
|
(array 'mesh-gateway' '/connect/mesh_gateway.html')
|
||||||
|
) as |link|}}
|
||||||
|
<li role="none" class="docs-link">
|
||||||
|
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_URL') (get link item.Kind)}} rel="noopener noreferrer" target="_blank">
|
||||||
|
Documentation
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li role="separator">
|
||||||
|
Other gateway types
|
||||||
|
</li>
|
||||||
|
{{#if (not-eq item.Kind 'mesh-gateway')}}
|
||||||
|
<li role="none" class="docs-link">
|
||||||
|
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_URL') (get link 'mesh-gateway')}} rel="noopener noreferrer" target="_blank">
|
||||||
|
Mesh gateways
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (not-eq item.Kind 'terminating-gateway')}}
|
||||||
|
<li role="none" class="docs-link">
|
||||||
|
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_URL') (get link 'terminating-gateway')}} rel="noopener noreferrer" target="_blank">
|
||||||
|
Terminating gateways
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (not-eq item.Kind 'ingress-gateway')}}
|
||||||
|
<li role="none" class="docs-link">
|
||||||
|
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_URL') (get link 'ingress-gateway')}} rel="noopener noreferrer" target="_blank">
|
||||||
|
Ingress gateways
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</MenuPanel>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
{{else}}
|
||||||
|
<span data-test-kind={{item.Kind}} class="consul-kind gateway">
|
||||||
|
<span>{{Name}}</span>
|
||||||
</span>
|
</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{/let}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -1,9 +1,19 @@
|
|||||||
{{yield}}
|
{{yield}}
|
||||||
{{#if (gt items.length 0)}}
|
{{#if (gt items.length 0)}}
|
||||||
<ListCollection @items={{items}} class="consul-service-list" as |item index|>
|
<ListCollection @items={{items}} class="consul-service-list" as |item index|>
|
||||||
<a data-test-service-name href={{href-to routeName item.Name}} class={{service/health-checks item}}>
|
{{#if (eq item.Kind 'terminating-gateway')}}
|
||||||
|
<a data-test-service-name href={{href-to "dc.services.show.services" item.Name}} class={{service/health-checks item}}>
|
||||||
{{item.Name}}
|
{{item.Name}}
|
||||||
</a>
|
</a>
|
||||||
|
{{else if (eq item.Kind 'ingress-gateway')}}
|
||||||
|
<a data-test-service-name href={{href-to "dc.services.show.upstreams" item.Name}} class={{service/health-checks item}}>
|
||||||
|
{{item.Name}}
|
||||||
|
</a>
|
||||||
|
{{else}}
|
||||||
|
<a data-test-service-name href={{href-to "dc.services.show.instances" item.Name}} class={{service/health-checks item}}>
|
||||||
|
{{item.Name}}
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
<ul>
|
<ul>
|
||||||
{{#if (and nspace (env 'CONSUL_NSPACES_ENABLED'))}}
|
{{#if (and nspace (env 'CONSUL_NSPACES_ENABLED'))}}
|
||||||
{{#if (not-eq item.Namespace nspace)}}
|
{{#if (not-eq item.Namespace nspace)}}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
{{#if dc}}
|
{{#if dc}}
|
||||||
<ul>
|
<ul>
|
||||||
{{#if (and (env 'CONSUL_NSPACES_ENABLED') (gt nspaces.length 0))}}
|
{{#if (and (env 'CONSUL_NSPACES_ENABLED') (gt nspaces.length 0))}}
|
||||||
<li data-test-nspace-menu>
|
<li class="nspaces" data-test-nspace-menu>
|
||||||
{{#if (and (eq nspaces.length 1) (not canManageNspaces)) }}
|
{{#if (and (eq nspaces.length 1) (not canManageNspaces)) }}
|
||||||
<span data-test-nspace-selected={{nspace.Name}}>{{nspace.Name}}</span>
|
<span data-test-nspace-selected={{nspace.Name}}>{{nspace.Name}}</span>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
|
15
ui-v2/app/components/menu-panel/index.hbs
Normal file
15
ui-v2/app/components/menu-panel/index.hbs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{{yield}}
|
||||||
|
<div class="menu-panel {{position}}">
|
||||||
|
<YieldSlot @name="controls">
|
||||||
|
{{yield}}
|
||||||
|
</YieldSlot>
|
||||||
|
{{#yield-slot name="header"}}
|
||||||
|
<div>{{yield}}</div>
|
||||||
|
{{else}}
|
||||||
|
{{/yield-slot}}
|
||||||
|
<ul role="menu" ...attributes>
|
||||||
|
<YieldSlot @name="menu">
|
||||||
|
{{yield}}
|
||||||
|
</YieldSlot>
|
||||||
|
</ul>
|
||||||
|
</div>
|
7
ui-v2/app/components/menu-panel/index.js
Normal file
7
ui-v2/app/components/menu-panel/index.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
import Slotted from 'block-slots';
|
||||||
|
|
||||||
|
export default Component.extend(Slotted, {
|
||||||
|
tagName: '',
|
||||||
|
});
|
@ -11,21 +11,22 @@
|
|||||||
</YieldSlot>
|
</YieldSlot>
|
||||||
</button>
|
</button>
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
<div class={{position}}>
|
<MenuPanel @position={{position}} id={{ariaControls}} aria-labelledby={{ariaLabelledBy}} aria-expanded={{ariaExpanded}}>
|
||||||
|
<BlockSlot @name="controls">
|
||||||
<input type="checkbox" id={{concat 'popover-menu-' guid '-'}} />
|
<input type="checkbox" id={{concat 'popover-menu-' guid '-'}} />
|
||||||
{{#each submenus as |sub|}}
|
{{#each submenus as |sub|}}
|
||||||
<input type="checkbox" id={{concat 'popover-menu-' guid '-' sub}} />
|
<input type="checkbox" id={{concat 'popover-menu-' guid '-' sub}} />
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{#yield-slot name='header'}}
|
</BlockSlot>
|
||||||
<div>
|
{{#if hasHeader}}
|
||||||
{{yield}}
|
<BlockSlot @name="header">
|
||||||
</div>
|
{{#yield-slot name="header"}}{{yield}}{{else}}{{/yield-slot}}
|
||||||
{{else}}
|
</BlockSlot>
|
||||||
{{/yield-slot}}
|
{{/if}}
|
||||||
<ul role="menu" id={{ariaControls}} aria-labelledby={{ariaLabelledBy}} aria-expanded={{ariaExpanded}}>
|
<BlockSlot @name="menu">
|
||||||
<YieldSlot @name="menu" @params={{block-params (concat "popover-menu-" guid "-") send keypressClick this.toggle.click}}>
|
<YieldSlot @name="menu" @params={{block-params (concat "popover-menu-" guid "-") send keypressClick this.toggle.click}}>
|
||||||
{{yield}}
|
{{yield}}
|
||||||
</YieldSlot>
|
</YieldSlot>
|
||||||
</ul>
|
</BlockSlot>
|
||||||
</div>
|
</MenuPanel>
|
||||||
</AriaMenu>
|
</AriaMenu>
|
@ -2,6 +2,7 @@
|
|||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import Slotted from 'block-slots';
|
import Slotted from 'block-slots';
|
||||||
|
import { set } from '@ember/object';
|
||||||
|
|
||||||
export default Component.extend(Slotted, {
|
export default Component.extend(Slotted, {
|
||||||
tagName: '',
|
tagName: '',
|
||||||
@ -16,6 +17,9 @@ export default Component.extend(Slotted, {
|
|||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
this.guid = this.dom.guid(this);
|
this.guid = this.dom.guid(this);
|
||||||
},
|
},
|
||||||
|
willRender: function() {
|
||||||
|
set(this, 'hasHeader', this._isRegistered('header'));
|
||||||
|
},
|
||||||
actions: {
|
actions: {
|
||||||
change: function(e) {
|
change: function(e) {
|
||||||
if (!e.target.checked) {
|
if (!e.target.checked) {
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
<input id={{concat 'sort-control-value-' elementId}} type="radio" value="Name" name={{concat 'sort-control-' name}} onchange={{action 'change'}} />
|
|
||||||
{{#if checked}}
|
|
||||||
<input id={{concat 'sort-control-direction-' elementId}} type="checkbox" onchange={{action 'change'}} />
|
|
||||||
{{/if}}
|
|
||||||
<label for={{if checked (concat 'sort-control-direction-' elementId) (concat 'sort-control-value-' elementId)}}>
|
|
||||||
{{yield}}
|
|
||||||
</label>
|
|
@ -1,15 +0,0 @@
|
|||||||
import Component from '@ember/component';
|
|
||||||
import { set } from '@ember/object';
|
|
||||||
export default Component.extend({
|
|
||||||
classNames: ['sort-control'],
|
|
||||||
direction: 'asc',
|
|
||||||
onchange: function() {},
|
|
||||||
actions: {
|
|
||||||
change: function(e) {
|
|
||||||
if (e.target.type === 'checkbox') {
|
|
||||||
set(this, 'direction', e.target.checked ? 'desc' : 'asc');
|
|
||||||
}
|
|
||||||
this.onchange({ target: { value: `${this.value}:${this.direction}` } });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
5
ui-v2/app/helpers/from-entries.js
Normal file
5
ui-v2/app/helpers/from-entries.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { helper } from '@ember/component/helper';
|
||||||
|
|
||||||
|
export default helper(function fromEntries(params /*, hash*/) {
|
||||||
|
return Object.fromEntries(params);
|
||||||
|
});
|
@ -12,4 +12,5 @@ export default Model.extend({
|
|||||||
// TODO: Are these required?
|
// TODO: Are these required?
|
||||||
Services: hasMany('service'),
|
Services: hasMany('service'),
|
||||||
Nodes: hasMany('node'),
|
Nodes: hasMany('node'),
|
||||||
|
MeshEnabled: attr('boolean', { defaultValue: true }),
|
||||||
});
|
});
|
||||||
|
@ -30,23 +30,7 @@ export default Route.extend({
|
|||||||
.catch(function() {
|
.catch(function() {
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
chain: this.chainRepo.findBySlug(params.name, dc, nspace).catch(function(e) {
|
chain: this.chainRepo.findBySlug(params.name, dc, nspace),
|
||||||
const code = get(e, 'errors.firstObject.status');
|
|
||||||
// Currently we are specifically catching a 500, but we return null
|
|
||||||
// by default, so null for all errors.
|
|
||||||
// The extra code here is mainly for documentation purposes
|
|
||||||
// and for if we need to perform different actions based on the error code
|
|
||||||
// in the future
|
|
||||||
switch (code) {
|
|
||||||
case '500':
|
|
||||||
// connect is likely to be disabled
|
|
||||||
// we just return a null to hide the tab
|
|
||||||
// `Connect must be enabled in order to use this endpoint`
|
|
||||||
return null;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
proxies: this.proxyRepo.findAllBySlug(params.name, dc, nspace),
|
proxies: this.proxyRepo.findAllBySlug(params.name, dc, nspace),
|
||||||
...model,
|
...model,
|
||||||
});
|
});
|
||||||
|
@ -39,6 +39,9 @@ export default Service.extend({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
peekOne: function(id) {
|
||||||
|
return this.store.peekRecord(this.getModelName(), id);
|
||||||
|
},
|
||||||
findAllByDatacenter: function(dc, nspace, configuration = {}) {
|
findAllByDatacenter: function(dc, nspace, configuration = {}) {
|
||||||
const query = {
|
const query = {
|
||||||
dc: dc,
|
dc: dc,
|
||||||
|
@ -1,8 +1,31 @@
|
|||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import { get, set } from '@ember/object';
|
||||||
import RepositoryService from 'consul-ui/services/repository';
|
import RepositoryService from 'consul-ui/services/repository';
|
||||||
|
|
||||||
const modelName = 'discovery-chain';
|
const modelName = 'discovery-chain';
|
||||||
|
const ERROR_MESH_DISABLED = 'Connect must be enabled in order to use this endpoint';
|
||||||
export default RepositoryService.extend({
|
export default RepositoryService.extend({
|
||||||
|
dcs: service('repository/dc'),
|
||||||
getModelName: function() {
|
getModelName: function() {
|
||||||
return modelName;
|
return modelName;
|
||||||
},
|
},
|
||||||
|
findBySlug: function(slug, dc, nspace, configuration = {}) {
|
||||||
|
const datacenter = this.dcs.peekOne(dc);
|
||||||
|
if (datacenter !== null && !get(datacenter, 'MeshEnabled')) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return this._super(...arguments).catch(e => {
|
||||||
|
const code = get(e, 'errors.firstObject.status');
|
||||||
|
const body = get(e, 'errors.firstObject.detail').trim();
|
||||||
|
switch (code) {
|
||||||
|
case '500':
|
||||||
|
if (datacenter !== null && body === ERROR_MESH_DISABLED) {
|
||||||
|
set(datacenter, 'MeshEnabled', false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
@ -36,8 +36,8 @@ const createProxy = function(repo, find, settings, cache, serialize = JSON.strin
|
|||||||
// original find... with configuration now added
|
// original find... with configuration now added
|
||||||
return repo[find](...args)
|
return repo[find](...args)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (!configuration.settings.enabled) {
|
if (!configuration.settings.enabled || typeof res === 'undefined') {
|
||||||
// blocking isn't enabled, immediately close
|
// blocking isn't enabled, or we got no data, immediately close
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
@extend %user-select-none;
|
@extend %user-select-none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
%button:disabled,
|
%button:disabled,
|
||||||
%internal-button:disabled {
|
%internal-button:disabled {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
%menu-panel {
|
%menu-panel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
%menu-panel [type='checkbox'] {
|
%menu-panel [type='checkbox'] {
|
||||||
display: none;
|
display: none;
|
||||||
@ -15,8 +14,14 @@
|
|||||||
/* or be hardcoded */
|
/* or be hardcoded */
|
||||||
/* min-height: 143px; */
|
/* min-height: 143px; */
|
||||||
}
|
}
|
||||||
%menu-panel [role='menuitem']::after {
|
%menu-panel [role='menuitem'] {
|
||||||
float: right;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
%menu-panel [role='menuitem']:after {
|
||||||
|
@extend %as-pseudo;
|
||||||
|
display: block !important;
|
||||||
|
background-position: center right !important;
|
||||||
}
|
}
|
||||||
%menu-panel-sub-panel {
|
%menu-panel-sub-panel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -70,7 +75,7 @@
|
|||||||
}
|
}
|
||||||
%menu-panel-header {
|
%menu-panel-header {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
padding-left: 36px;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
/* here the !important is only needed for what seems to be a difference */
|
/* here the !important is only needed for what seems to be a difference */
|
||||||
/* with the CSS before and after compression */
|
/* with the CSS before and after compression */
|
||||||
|
@ -22,12 +22,12 @@
|
|||||||
border-top: $decor-border-100;
|
border-top: $decor-border-100;
|
||||||
border-color: $gray-300;
|
border-color: $gray-300;
|
||||||
}
|
}
|
||||||
%menu-panel-header {
|
|
||||||
background-color: $gray-050;
|
%menu-panel .docs-link a::after {
|
||||||
|
@extend %with-docs-mask, %as-pseudo;
|
||||||
}
|
}
|
||||||
%menu-panel-header::before {
|
%menu-panel .learn-link a::after {
|
||||||
@extend %with-info-circle-fill-color-icon, %as-pseudo;
|
@extend %with-learn-mask, %as-pseudo;
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
}
|
||||||
%menu-panel .is-active > *::after {
|
%menu-panel .is-active > *::after {
|
||||||
@extend %with-check-plain-mask, %as-pseudo;
|
@extend %with-check-plain-mask, %as-pseudo;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
border-radius: $decor-radius-100;
|
border-radius: $decor-radius-100;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
|
display: inline-block;
|
||||||
line-height: 0.7rem;
|
line-height: 0.7rem;
|
||||||
}
|
}
|
||||||
%reduced-pill > span {
|
%reduced-pill > span {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
%popover-menu + label > * {
|
%popover-menu + label > * {
|
||||||
@extend %toggle-button;
|
@extend %toggle-button;
|
||||||
}
|
}
|
||||||
|
%more-popover-menu-panel,
|
||||||
%popover-menu-panel {
|
%popover-menu-panel {
|
||||||
@extend %menu-panel;
|
@extend %menu-panel;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
@extend %toggle-button;
|
@extend %toggle-button;
|
||||||
}
|
}
|
||||||
%more-popover-menu-panel {
|
%more-popover-menu-panel {
|
||||||
@extend %menu-panel;
|
overflow: hidden;
|
||||||
width: 192px;
|
width: 192px;
|
||||||
}
|
}
|
||||||
%more-popover-menu + label + div {
|
%more-popover-menu + label + div {
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
%app-view-actions {
|
%app-view-actions {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
@import './list-collection';
|
@import './list-collection';
|
||||||
@import './grid-collection';
|
@import './grid-collection';
|
||||||
@import './popover-select';
|
@import './popover-select';
|
||||||
|
@import './tooltip-panel';
|
||||||
|
@import './menu-panel';
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
@extend %with-learn-icon, %as-pseudo;
|
@extend %with-learn-icon, %as-pseudo;
|
||||||
}
|
}
|
||||||
%main-nav-horizontal .feedback-link a::after {
|
%main-nav-horizontal .feedback-link a::after {
|
||||||
@extend %with-logo-github-monochrome-icon, %as-pseudo;
|
@extend %with-logo-github-monochrome-mask, %as-pseudo;
|
||||||
}
|
}
|
||||||
%main-header-horizontal::before {
|
%main-header-horizontal::before {
|
||||||
background-color: var(--swatch-brand-600, $black);
|
background-color: var(--swatch-brand-600, $black);
|
||||||
|
@ -10,6 +10,15 @@
|
|||||||
%secondary-nav {
|
%secondary-nav {
|
||||||
@extend %main-nav-horizontal;
|
@extend %main-nav-horizontal;
|
||||||
}
|
}
|
||||||
|
%primary-nav .nspaces .menu-panel > div {
|
||||||
|
background-color: $gray-050;
|
||||||
|
padding-left: 36px;
|
||||||
|
}
|
||||||
|
%primary-nav .nspaces .menu-panel > div::before {
|
||||||
|
@extend %with-info-circle-fill-mask, %as-pseudo;
|
||||||
|
color: $blue-500;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
%main-header-horizontal > div {
|
%main-header-horizontal > div {
|
||||||
@extend %main-nav-horizontal-panel;
|
@extend %main-nav-horizontal-panel;
|
||||||
|
3
ui-v2/app/styles/components/menu-panel.scss
Normal file
3
ui-v2/app/styles/components/menu-panel.scss
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.menu-panel {
|
||||||
|
@extend %menu-panel;
|
||||||
|
}
|
4
ui-v2/app/styles/components/tooltip-panel.scss
Normal file
4
ui-v2/app/styles/components/tooltip-panel.scss
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@import './tooltip-panel/index';
|
||||||
|
.tooltip-panel {
|
||||||
|
@extend %tooltip-panel;
|
||||||
|
}
|
2
ui-v2/app/styles/components/tooltip-panel/index.scss
Normal file
2
ui-v2/app/styles/components/tooltip-panel/index.scss
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@import './skin';
|
||||||
|
@import './layout';
|
21
ui-v2/app/styles/components/tooltip-panel/layout.scss
Normal file
21
ui-v2/app/styles/components/tooltip-panel/layout.scss
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
%tooltip-panel {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
%tooltip-panel dd > div {
|
||||||
|
top: auto !important;
|
||||||
|
}
|
||||||
|
%tooltip-panel dt {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
%tooltip-panel dd {
|
||||||
|
display: none;
|
||||||
|
position: relative;
|
||||||
|
padding-top: 10px;
|
||||||
|
margin-bottom: -10px;
|
||||||
|
}
|
||||||
|
%tooltip-panel:hover dd {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
%tooltip-panel dd > div {
|
||||||
|
width: 250px;
|
||||||
|
}
|
13
ui-v2/app/styles/components/tooltip-panel/skin.scss
Normal file
13
ui-v2/app/styles/components/tooltip-panel/skin.scss
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/* This is the top chevron */
|
||||||
|
%tooltip-panel dd > div::before {
|
||||||
|
@extend %as-pseudo;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: white;
|
||||||
|
border-top: 1px solid $gray-300;
|
||||||
|
border-right: 1px solid $gray-300;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
position: absolute;
|
||||||
|
left: 16px;
|
||||||
|
top: -7px;
|
||||||
|
}
|
@ -34,7 +34,7 @@
|
|||||||
{{#let (sort-by (comparator 'service' sort.selected.key) services) as |sorted|}}
|
{{#let (sort-by (comparator 'service' sort.selected.key) services) as |sorted|}}
|
||||||
<ChangeableSet @dispatcher={{searchable 'service' sorted}} @terms={{search}}>
|
<ChangeableSet @dispatcher={{searchable 'service' sorted}} @terms={{search}}>
|
||||||
<BlockSlot @name="set" as |filtered|>
|
<BlockSlot @name="set" as |filtered|>
|
||||||
<ConsulServiceList @routeName="dc.services.show" @items={{filtered}} @proxies={{proxies}}/>
|
<ConsulServiceList @items={{filtered}} @proxies={{proxies}}/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="empty">
|
<BlockSlot @name="empty">
|
||||||
<EmptyState @allowLogin={{true}}>
|
<EmptyState @allowLogin={{true}}>
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
{{ item.ID }}
|
{{ item.ID }}
|
||||||
</h1>
|
</h1>
|
||||||
<ConsulExternalSource @item={{item}} />
|
<ConsulExternalSource @item={{item}} />
|
||||||
<ConsulKind @item={{item}} />
|
<ConsulKind @item={{item}} @withInfo={{true}} />
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="nav">
|
<BlockSlot @name="nav">
|
||||||
<dl>
|
<dl>
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
<div class="tab-section">
|
<div class="tab-section">
|
||||||
<div role="tabpanel">
|
<div role="tabpanel">
|
||||||
{{#if (or (gt proxy.ServiceChecks.length 0) (gt proxy.NodeChecks.length 0))}}
|
|
||||||
<section>
|
|
||||||
<h3>Proxy health checks</h3>
|
|
||||||
<HealthcheckList data-test-proxy-checks @items={{append proxy.ServiceChecks proxy.NodeChecks}} />
|
|
||||||
</section>
|
|
||||||
{{/if}}
|
|
||||||
{{#if (gt proxy.Proxy.Upstreams.length 0)}}
|
{{#if (gt proxy.Proxy.Upstreams.length 0)}}
|
||||||
<section class="proxy-upstreams">
|
<section class="proxy-upstreams">
|
||||||
<h3> Upstreams</h3>
|
<h3> Upstreams</h3>
|
||||||
@ -103,5 +97,11 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#if (or (gt proxy.ServiceChecks.length 0) (gt proxy.NodeChecks.length 0))}}
|
||||||
|
<section>
|
||||||
|
<h3>Proxy health</h3>
|
||||||
|
<HealthcheckList data-test-proxy-checks @items={{append proxy.ServiceChecks proxy.NodeChecks}} />
|
||||||
|
</section>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -14,24 +14,24 @@
|
|||||||
{{item.Service.Service}}
|
{{item.Service.Service}}
|
||||||
</h1>
|
</h1>
|
||||||
<ConsulExternalSource @item={{item.Service}} />
|
<ConsulExternalSource @item={{item.Service}} />
|
||||||
<ConsulKind @item={{item.Service}} />
|
<ConsulKind @item={{item.Service}} @withInfo={{true}} />
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="nav">
|
<BlockSlot @name="nav">
|
||||||
{{#if (not-eq item.Service.Kind 'mesh-gateway')}}
|
{{#if (not-eq item.Service.Kind 'mesh-gateway')}}
|
||||||
<TabNav @items={{
|
<TabNav @items={{
|
||||||
compact
|
compact
|
||||||
(array
|
(array
|
||||||
(hash label="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
|
|
||||||
(if (eq item.Service.Kind 'terminating-gateway')
|
(if (eq item.Service.Kind 'terminating-gateway')
|
||||||
(hash label="Linked Services" href=(href-to "dc.services.show.services") selected=(is-href "dc.services.show.services"))
|
(hash label="Linked Services" href=(href-to "dc.services.show.services") selected=(is-href "dc.services.show.services"))
|
||||||
'')
|
'')
|
||||||
(if (eq item.Service.Kind 'ingress-gateway')
|
(if (eq item.Service.Kind 'ingress-gateway')
|
||||||
(hash label="Upstreams" href=(href-to "dc.services.show.upstreams") selected=(is-href "dc.services.show.upstreams"))
|
(hash label="Upstreams" href=(href-to "dc.services.show.upstreams") selected=(is-href "dc.services.show.upstreams"))
|
||||||
'')
|
'')
|
||||||
|
(hash label="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
|
||||||
(if (not item.Service.Kind)
|
(if (not item.Service.Kind)
|
||||||
(hash label="Intentions" href=(href-to "dc.services.show.intentions") selected=(is-href "dc.services.show.intentions"))
|
(hash label="Intentions" href=(href-to "dc.services.show.intentions") selected=(is-href "dc.services.show.intentions"))
|
||||||
'')
|
'')
|
||||||
(if chain
|
(if chain.Chain
|
||||||
(hash label="Routing" href=(href-to "dc.services.show.routing") selected=(is-href "dc.services.show.routing"))
|
(hash label="Routing" href=(href-to "dc.services.show.routing") selected=(is-href "dc.services.show.routing"))
|
||||||
'')
|
'')
|
||||||
(if (not item.Service.Kind)
|
(if (not item.Service.Kind)
|
||||||
|
@ -57,7 +57,7 @@
|
|||||||
"@glimmer/component": "^1.0.0",
|
"@glimmer/component": "^1.0.0",
|
||||||
"@glimmer/tracking": "^1.0.0",
|
"@glimmer/tracking": "^1.0.0",
|
||||||
"@hashicorp/consul-api-double": "^2.6.2",
|
"@hashicorp/consul-api-double": "^2.6.2",
|
||||||
"@hashicorp/ember-cli-api-double": "^3.0.2",
|
"@hashicorp/ember-cli-api-double": "^3.1.0",
|
||||||
"@xstate/fsm": "^1.4.0",
|
"@xstate/fsm": "^1.4.0",
|
||||||
"babel-eslint": "^10.0.3",
|
"babel-eslint": "^10.0.3",
|
||||||
"base64-js": "^1.3.0",
|
"base64-js": "^1.3.0",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
Feature: dc / kvs / trailing-slash
|
Feature: dc / kvs / trailing-slash
|
||||||
Scenario: I have 10 folders
|
Scenario: I have 10 folders and I visit without a trailing slash
|
||||||
Given 1 datacenter model with the value "datacenter"
|
Given 1 datacenter model with the value "datacenter"
|
||||||
And 10 kv models from yaml
|
And 10 kv models from yaml
|
||||||
When I visit the kvs page for yaml
|
When I visit the kvs page for yaml
|
||||||
@ -10,6 +10,9 @@ Feature: dc / kvs / trailing-slash
|
|||||||
---
|
---
|
||||||
Then the url should be /datacenter/kv/foo/bar/
|
Then the url should be /datacenter/kv/foo/bar/
|
||||||
And a GET request was made to "/v1/kv/foo/bar/?keys&dc=datacenter&separator=%2F&ns=@namespace"
|
And a GET request was made to "/v1/kv/foo/bar/?keys&dc=datacenter&separator=%2F&ns=@namespace"
|
||||||
|
Scenario: I have 10 folders and I visit with a trailing slash
|
||||||
|
Given 1 datacenter model with the value "datacenter"
|
||||||
|
And 10 kv models from yaml
|
||||||
When I visit the kvs page for yaml
|
When I visit the kvs page for yaml
|
||||||
---
|
---
|
||||||
dc: datacenter
|
dc: datacenter
|
||||||
|
@ -17,13 +17,20 @@ Feature: dc / services / show-routing: Show Routing for Service
|
|||||||
And the title should be "service-0 - Consul"
|
And the title should be "service-0 - Consul"
|
||||||
And I see routing on the tabs
|
And I see routing on the tabs
|
||||||
Scenario: Given connect is disabled, the Routing tab should not display or error
|
Scenario: Given connect is disabled, the Routing tab should not display or error
|
||||||
Given 1 datacenter model with the value "dc1"
|
Given 2 datacenter models from yaml
|
||||||
|
---
|
||||||
|
- dc1
|
||||||
|
- dc2
|
||||||
|
---
|
||||||
And 1 node models
|
And 1 node models
|
||||||
And 1 service model from yaml
|
And 2 service model from yaml
|
||||||
---
|
---
|
||||||
- Service:
|
- Service:
|
||||||
Name: service-0
|
Name: service-0
|
||||||
ID: service-0-with-id
|
ID: service-0-with-id
|
||||||
|
- Service:
|
||||||
|
Name: service-1
|
||||||
|
ID: service-1-with-id
|
||||||
---
|
---
|
||||||
And the url "/v1/discovery-chain/service-0?dc=dc1&ns=@namespace" responds with from yaml
|
And the url "/v1/discovery-chain/service-0?dc=dc1&ns=@namespace" responds with from yaml
|
||||||
---
|
---
|
||||||
@ -37,4 +44,17 @@ Feature: dc / services / show-routing: Show Routing for Service
|
|||||||
---
|
---
|
||||||
And I don't see routing on the tabs
|
And I don't see routing on the tabs
|
||||||
And I don't see the "[data-test-error]" element
|
And I don't see the "[data-test-error]" element
|
||||||
|
And I visit the service page for yaml
|
||||||
|
---
|
||||||
|
dc: dc2
|
||||||
|
service: service-1
|
||||||
|
---
|
||||||
|
And I see routing on the tabs
|
||||||
|
And I visit the service page for yaml
|
||||||
|
---
|
||||||
|
dc: dc1
|
||||||
|
service: service-0
|
||||||
|
---
|
||||||
|
Then a GET request wasn't made to "/v1/discovery-chain/service-0?dc=dc1&ns=@namespace"
|
||||||
|
And I don't see routing on the tabs
|
||||||
|
And I don't see the "[data-test-error]" element
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
import { module, test } from 'qunit';
|
|
||||||
import { setupRenderingTest } from 'ember-qunit';
|
|
||||||
import { render } from '@ember/test-helpers';
|
|
||||||
import hbs from 'htmlbars-inline-precompile';
|
|
||||||
|
|
||||||
module('Integration | Component | sort control', function(hooks) {
|
|
||||||
setupRenderingTest(hooks);
|
|
||||||
|
|
||||||
hooks.beforeEach(function() {
|
|
||||||
this.actions = {};
|
|
||||||
this.send = (actionName, ...args) => this.actions[actionName].apply(this, args);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it renders', async function(assert) {
|
|
||||||
// Set any properties with this.set('myProperty', 'value');
|
|
||||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
|
||||||
|
|
||||||
await render(hbs`{{sort-control}}`);
|
|
||||||
|
|
||||||
assert.dom('*').hasText('');
|
|
||||||
|
|
||||||
// Template block usage:
|
|
||||||
await render(hbs`
|
|
||||||
{{#sort-control}}
|
|
||||||
template block text
|
|
||||||
{{/sort-control}}
|
|
||||||
`);
|
|
||||||
|
|
||||||
assert.dom('*').hasText('template block text');
|
|
||||||
});
|
|
||||||
test('it changes direction and calls onchange when clicked/activated', async function(assert) {
|
|
||||||
assert.expect(2);
|
|
||||||
let count = 0;
|
|
||||||
this.actions.change = e => {
|
|
||||||
if (count === 0) {
|
|
||||||
assert.equal(e.target.value, 'sort:desc');
|
|
||||||
} else {
|
|
||||||
assert.equal(e.target.value, 'sort:asc');
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
};
|
|
||||||
await render(hbs`{{sort-control checked=true value='sort' onchange=(action 'change')}}`);
|
|
||||||
const $label = this.$('label');
|
|
||||||
$label.trigger('click');
|
|
||||||
$label.trigger('click');
|
|
||||||
});
|
|
||||||
});
|
|
@ -21,9 +21,11 @@ import assertForm from './steps/assertions/form';
|
|||||||
const pluralize = function(str) {
|
const pluralize = function(str) {
|
||||||
return Inflector.inflector.pluralize(str);
|
return Inflector.inflector.pluralize(str);
|
||||||
};
|
};
|
||||||
const getLastNthRequest = function(arr) {
|
const getLastNthRequest = function(getRequests) {
|
||||||
return function(n, method) {
|
return function(n, method) {
|
||||||
let requests = arr.slice(0).reverse();
|
let requests = getRequests()
|
||||||
|
.slice(0)
|
||||||
|
.reverse();
|
||||||
if (method) {
|
if (method) {
|
||||||
requests = requests.filter(function(item) {
|
requests = requests.filter(function(item) {
|
||||||
return item.method === method;
|
return item.method === method;
|
||||||
@ -82,7 +84,7 @@ export default function(assert, library) {
|
|||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const lastNthRequest = getLastNthRequest(api.server.history);
|
const lastNthRequest = getLastNthRequest(() => api.server.history);
|
||||||
const create = function(number, name, value) {
|
const create = function(number, name, value) {
|
||||||
// don't return a promise here as
|
// don't return a promise here as
|
||||||
// I don't need it to wait
|
// I don't need it to wait
|
||||||
@ -99,6 +101,7 @@ export default function(assert, library) {
|
|||||||
return currentPage;
|
return currentPage;
|
||||||
};
|
};
|
||||||
const setCurrentPage = function(page) {
|
const setCurrentPage = function(page) {
|
||||||
|
api.server.clearHistory();
|
||||||
currentPage = page;
|
currentPage = page;
|
||||||
return page;
|
return page;
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
const not = `(n't| not)?`;
|
||||||
export default function(scenario, assert, lastNthRequest) {
|
export default function(scenario, assert, lastNthRequest) {
|
||||||
// lastNthRequest should return a
|
// lastNthRequest should return a
|
||||||
// {
|
// {
|
||||||
@ -21,12 +22,17 @@ export default function(scenario, assert, lastNthRequest) {
|
|||||||
);
|
);
|
||||||
assert.equal(diff.size, 0, `Expected requests "${[...diff].join(', ')}"`);
|
assert.equal(diff.size, 0, `Expected requests "${[...diff].join(', ')}"`);
|
||||||
})
|
})
|
||||||
.then('a $method request was made to "$endpoint"', function(method, url) {
|
.then(`a $method request was${not} made to "$endpoint"`, function(method, negative, url) {
|
||||||
|
const isNegative = typeof negative !== 'undefined';
|
||||||
const requests = lastNthRequest(null, method);
|
const requests = lastNthRequest(null, method);
|
||||||
const request = requests.find(function(item) {
|
const request = requests.some(function(item) {
|
||||||
return method === item.method && url === item.url;
|
return method === item.method && url === item.url;
|
||||||
});
|
});
|
||||||
|
if (isNegative) {
|
||||||
|
assert.notOk(request, `Didn't expect a ${method} request url to ${url}`);
|
||||||
|
} else {
|
||||||
assert.ok(request, `Expected a ${method} request url to ${url}`);
|
assert.ok(request, `Expected a ${method} request url to ${url}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.then('a $method request was made to "$endpoint" with no body', function(method, url) {
|
.then('a $method request was made to "$endpoint" with no body', function(method, url) {
|
||||||
const requests = lastNthRequest(null, method);
|
const requests = lastNthRequest(null, method);
|
||||||
|
@ -1215,10 +1215,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.15.2.tgz#e2c34a348b9959fcc95ffad797c1fed9644a41bd"
|
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.15.2.tgz#e2c34a348b9959fcc95ffad797c1fed9644a41bd"
|
||||||
integrity sha512-VNdwsL3ut4SubCtwWfqX4prD9R/RczKtWUID6s6K9h1TCdzTgpZQhbb+gdzaYGqzCE3Mrw416JzclxVTIFIUFw==
|
integrity sha512-VNdwsL3ut4SubCtwWfqX4prD9R/RczKtWUID6s6K9h1TCdzTgpZQhbb+gdzaYGqzCE3Mrw416JzclxVTIFIUFw==
|
||||||
|
|
||||||
"@hashicorp/ember-cli-api-double@^3.0.2":
|
"@hashicorp/ember-cli-api-double@^3.1.0":
|
||||||
version "3.0.2"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-3.0.2.tgz#4f66f22e4b54293c46fe16dc24568267526c8acd"
|
resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-3.1.0.tgz#ce228ac5c8a46c7a10112f5bc0fb782c47775b60"
|
||||||
integrity sha512-NmcA+jBcBO8tzCfbqWzrQdHvTUeaj71Gdu9phxaULMtM9Zw7BZtHlvb2P1ivknGGw92w9Se50pDNjkB57ww22A==
|
integrity sha512-G8dDSewInFZOeD5sprdZPw7ZKUYlkJ9bJxPkEaMRPbC6ZN4ZHqeFWB1xXeq2ROtR07J6Xbs+BrFIE6GHTshpEg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@hashicorp/api-double" "^1.6.1"
|
"@hashicorp/api-double" "^1.6.1"
|
||||||
array-range "^1.0.1"
|
array-range "^1.0.1"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user