UI: External Source markers (#4640)
1. Addition of external source icons for services marked as such. 2. New %with-tooltip css component (wip) 3. New 'no healthcheck' icon as external sources might not have healthchecks, also minus icon on node cards in the service detail view 4. If a service doesn't have healthchecks, we use the [Services] tabs as the default instead of the [Health Checks] tab in the Service detail page. 5. `css-var` helper. The idea here is that it will eventually be replaced with pure css custom properties instead of having to use JS. It would be nice to be able to build the css variables into the JS at build time (you'd probably still want to specify in config which variables you wanted available in JS), but that's possible future work. Lastly there is probably a tiny bit more testing edits here than usual, I noticed that there was an area where the dynamic mocking wasn't happening, it was just using the mocks from consul-api-double, the mocks I was 'dynamically' setting happened to be the same as the ones in consul-api-double. I've fixed this here also but it wasn't effecting anything until actually made certain values dynamic.
This commit is contained in:
parent
b1d83f98b0
commit
b279f23372
|
@ -15,7 +15,12 @@ export default Controller.extend(WithFiltering, {
|
||||||
},
|
},
|
||||||
setProperties: function() {
|
setProperties: function() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
set(this, 'selectedTab', 'health-checks');
|
// the default selected tab depends on whether you have any healthchecks or not
|
||||||
|
// so check the length here.
|
||||||
|
// This method is called immediately after `Route::setupController`, and done here rather than there
|
||||||
|
// as this is a variable used purely for view level things, if the view was different we might not
|
||||||
|
// need this variable
|
||||||
|
set(this, 'selectedTab', get(this.item, 'Checks.length') > 0 ? 'health-checks' : 'services');
|
||||||
},
|
},
|
||||||
filter: function(item, { s = '' }) {
|
filter: function(item, { s = '' }) {
|
||||||
const term = s.toLowerCase();
|
const term = s.toLowerCase();
|
||||||
|
|
|
@ -37,17 +37,17 @@ export default Controller.extend(WithHealthFiltering, {
|
||||||
item.hasStatus(status)
|
item.hasStatus(status)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
totalWidth: computed('{maxPassing,maxWarning,maxCritical}', function() {
|
maxWidth: computed('{maxPassing,maxWarning,maxCritical}', function() {
|
||||||
const PADDING = 32 * 3 + 13;
|
const PADDING = 32 * 3 + 13;
|
||||||
return ['maxPassing', 'maxWarning', 'maxCritical'].reduce((prev, item) => {
|
return ['maxPassing', 'maxWarning', 'maxCritical'].reduce((prev, item) => {
|
||||||
return prev + width(get(this, item));
|
return prev + width(get(this, item));
|
||||||
}, PADDING);
|
}, PADDING);
|
||||||
}),
|
}),
|
||||||
thWidth: computed('totalWidth', function() {
|
totalWidth: computed('maxWidth', function() {
|
||||||
return widthDeclaration(get(this, 'totalWidth'));
|
return widthDeclaration(get(this, 'maxWidth'));
|
||||||
}),
|
}),
|
||||||
remainingWidth: computed('totalWidth', function() {
|
remainingWidth: computed('maxWidth', function() {
|
||||||
return htmlSafe(`width: calc(50% - ${Math.round(get(this, 'totalWidth') / 2)}px)`);
|
return htmlSafe(`width: calc(50% - ${Math.round(get(this, 'maxWidth') / 2)}px)`);
|
||||||
}),
|
}),
|
||||||
maxPassing: computed('items', function() {
|
maxPassing: computed('items', function() {
|
||||||
return max(get(this, 'items'), 'ChecksPassing');
|
return max(get(this, 'items'), 'ChecksPassing');
|
||||||
|
|
12
ui-v2/app/helpers/css-var.js
Normal file
12
ui-v2/app/helpers/css-var.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { helper } from '@ember/component/helper';
|
||||||
|
const cssVars = {
|
||||||
|
'--kubernetes-color-svg': `url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 21 20" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" stroke="%23FFF" fill="none"><path d="M10.21 1.002a1.241 1.241 0 0 0-.472.12L3.29 4.201a1.225 1.225 0 0 0-.667.83l-1.591 6.922a1.215 1.215 0 0 0 .238 1.035l4.463 5.55c.234.29.59.46.964.46l7.159-.002c.375 0 .73-.168.964-.459l4.462-5.55c.234-.292.322-.673.238-1.036l-1.593-6.921a1.225 1.225 0 0 0-.667-.83l-6.45-3.08a1.242 1.242 0 0 0-.598-.12z" fill="%23326CE5"/><path d="M10.275 3.357c-.213 0-.386.192-.386.429v.11c.005.136.035.24.052.367.033.27.06.492.043.7a.421.421 0 0 1-.125.2l-.01.163a4.965 4.965 0 0 0-3.22 1.548 6.47 6.47 0 0 1-.138-.099c-.07.01-.139.03-.23-.022-.172-.117-.33-.277-.52-.47-.087-.093-.15-.181-.254-.27L5.4 5.944a.46.46 0 0 0-.269-.101.372.372 0 0 0-.307.136c-.133.167-.09.422.094.57l.006.003.08.065c.11.08.21.122.32.187.231.142.422.26.574.403.06.063.07.175.078.223l.123.11a4.995 4.995 0 0 0-.787 3.483l-.162.047c-.042.055-.103.141-.166.167-.198.063-.422.086-.692.114-.126.01-.236.004-.37.03-.03.005-.07.016-.103.023l-.003.001-.006.002c-.228.055-.374.264-.327.47.047.206.27.331.498.282h.006c.003-.001.005-.003.008-.003l.1-.022c.131-.036.227-.088.346-.133.255-.092.467-.168.673-.198.086-.007.177.053.222.078l.168-.029a5.023 5.023 0 0 0 2.226 2.78l-.07.168c.025.065.053.154.034.218-.075.195-.203.4-.35.628-.07.106-.142.188-.206.309l-.05.104c-.099.212-.026.456.165.548.191.092.43-.005.532-.218h.001v-.001c.015-.03.036-.07.048-.098.055-.126.073-.233.111-.354.102-.257.159-.526.3-.694.038-.046.1-.063.166-.08l.087-.159a4.987 4.987 0 0 0 3.562.01l.083.148c.066.021.138.032.197.12.105.179.177.391.265.648.038.121.057.229.112.354.012.029.033.069.048.099.102.213.341.311.533.219.19-.092.264-.337.164-.549l-.05-.104c-.064-.12-.136-.202-.207-.307-.146-.23-.267-.419-.342-.613-.032-.1.005-.163.03-.228-.015-.017-.047-.111-.065-.156a5.023 5.023 0 0 0 2.225-2.8l.165.03c.058-.039.112-.088.216-.08.206.03.418.106.673.198.12.045.215.098.347.133.028.008.068.015.1.022l.007.002.006.001c.229.05.45-.076.498-.282.047-.206-.1-.415-.327-.47l-.112-.027c-.134-.025-.243-.019-.37-.03-.27-.027-.494-.05-.692-.113-.081-.031-.139-.128-.167-.167l-.156-.046a4.997 4.997 0 0 0-.804-3.474l.137-.123c.006-.069.001-.142.073-.218.151-.143.343-.261.574-.404.11-.064.21-.106.32-.187.025-.018.06-.047.086-.068.185-.148.227-.403.094-.57-.133-.166-.39-.182-.575-.034-.027.02-.062.048-.086.068-.104.09-.168.178-.255.27-.19.194-.348.355-.52.471-.075.044-.185.029-.235.026l-.146.104A5.059 5.059 0 0 0 10.7 5.328a9.325 9.325 0 0 1-.009-.172c-.05-.048-.11-.09-.126-.193-.017-.208.011-.43.044-.7.018-.126.047-.23.053-.367l-.001-.11c0-.237-.173-.429-.386-.429zM9.79 6.351l-.114 2.025-.009.004a.34.34 0 0 1-.54.26l-.003.002-1.66-1.177A3.976 3.976 0 0 1 9.79 6.351zm.968 0a4.01 4.01 0 0 1 2.313 1.115l-1.65 1.17-.006-.003a.34.34 0 0 1-.54-.26h-.003L10.76 6.35zm-3.896 1.87l1.516 1.357-.002.008a.34.34 0 0 1-.134.585l-.001.006-1.944.561a3.975 3.975 0 0 1 .565-2.516zm6.813.001a4.025 4.025 0 0 1 .582 2.51l-1.954-.563-.001-.008a.34.34 0 0 1-.134-.585v-.004l1.507-1.35zm-3.712 1.46h.62l.387.483-.139.602-.557.268-.56-.269-.138-.602.387-.482zm1.99 1.652a.339.339 0 0 1 .08.005l.002-.004 2.01.34a3.98 3.98 0 0 1-1.609 2.022l-.78-1.885.002-.003a.34.34 0 0 1 .296-.475zm-3.375.008a.34.34 0 0 1 .308.474l.005.007-.772 1.866a3.997 3.997 0 0 1-1.604-2.007l1.993-.339.003.005a.345.345 0 0 1 .067-.006zm1.683.817a.338.338 0 0 1 .312.179h.008l.982 1.775a3.991 3.991 0 0 1-2.57-.002l.979-1.772h.001a.34.34 0 0 1 .288-.18z" stroke-width=".25" fill="%23FFF"/></g></svg>')`,
|
||||||
|
'--terraform-color-svg': `url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 16 18" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="%235C4EE5" d="M5.51 3.15l4.886 2.821v5.644L5.509 8.792z"/><path fill="%234040B2" d="M10.931 5.971v5.644l4.888-2.823V3.15z"/><path fill="%235C4EE5" d="M.086 0v5.642l4.887 2.823V2.82zM5.51 15.053l4.886 2.823v-5.644l-4.887-2.82z"/></g></svg>')`,
|
||||||
|
'--nomad-color-svg': `url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 16 18" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><path fill="%231F9967" d="M11.569 6.871v2.965l-2.064 1.192-1.443-.894v7.74l.04.002 7.78-4.47V4.48h-.145z"/><path fill="%2325BA81" d="M7.997 0L.24 4.481l5.233 3.074 1.06-.645 2.57 1.435v-2.98l2.465-1.481v2.987l4.314-2.391v-.011z"/><path fill="%2325BA81" d="M7.02 9.54v2.976l-2.347 1.488V8.05l.89-.548L.287 4.48.24 4.48v8.926l7.821 4.467v-7.74z"/></g></svg>')`,
|
||||||
|
'--consul-color-svg': `url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M8.693 10.707a1.862 1.862 0 1 1-.006-3.724 1.862 1.862 0 0 1 .006 3.724" fill="%23961D59"/><path d="M12.336 9.776a.853.853 0 1 1 0-1.707.853.853 0 0 1 0 1.707M15.639 10.556a.853.853 0 1 1 .017-.07c-.01.022-.01.044-.017.07M14.863 8.356a.855.855 0 0 1-.925-1.279.855.855 0 0 1 1.559.255c.024.11.027.222.009.333a.821.821 0 0 1-.642.691M17.977 10.467a.849.849 0 1 1-1.67-.296.849.849 0 0 1 .982-.692c.433.073.74.465.709.905a.221.221 0 0 0-.016.076M17.286 8.368a.853.853 0 1 1-.279-1.684.853.853 0 0 1 .279 1.684M16.651 13.371a.853.853 0 1 1-1.492-.828.853.853 0 0 1 1.492.828M16.325 5.631a.853.853 0 1 1-.84-1.485.853.853 0 0 1 .84 1.485" fill="%23D62783"/><path d="M8.842 17.534c-4.798 0-8.687-3.855-8.687-8.612C.155 4.166 4.045.31 8.842.31a8.645 8.645 0 0 1 5.279 1.77l-1.056 1.372a6.987 6.987 0 0 0-7.297-.709 6.872 6.872 0 0 0 0 12.356 6.987 6.987 0 0 0 7.297-.709l1.056 1.374a8.66 8.66 0 0 1-5.279 1.77z" fill="%23D62783" fill-rule="nonzero"/></g></svg>')`,
|
||||||
|
};
|
||||||
|
export function cssVar(params, hash) {
|
||||||
|
return typeof cssVars[params[0]] !== 'undefined' ? cssVars[params[0]] : params[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default helper(cssVar);
|
16
ui-v2/app/helpers/service/external-source.js
Normal file
16
ui-v2/app/helpers/service/external-source.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { helper } from '@ember/component/helper';
|
||||||
|
import { get } from '@ember/object';
|
||||||
|
|
||||||
|
export function serviceExternalSource(params, hash) {
|
||||||
|
let source = get(params[0], 'ExternalSources.firstObject');
|
||||||
|
if (!source) {
|
||||||
|
source = get(params[0], 'Meta.external-source');
|
||||||
|
}
|
||||||
|
const prefix = typeof hash.prefix === 'undefined' ? '' : hash.prefix;
|
||||||
|
if (source) {
|
||||||
|
return `${prefix}${source}`;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default helper(serviceExternalSource);
|
|
@ -14,6 +14,8 @@ export default Model.extend({
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
Kind: attr('string'),
|
Kind: attr('string'),
|
||||||
|
ExternalSources: attr(),
|
||||||
|
Meta: attr(),
|
||||||
Address: attr('string'),
|
Address: attr('string'),
|
||||||
Port: attr('number'),
|
Port: attr('number'),
|
||||||
EnableTagOverride: attr('boolean'),
|
EnableTagOverride: attr('boolean'),
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
@import 'components/confirmation-dialog';
|
@import 'components/confirmation-dialog';
|
||||||
@import 'components/feedback-dialog';
|
@import 'components/feedback-dialog';
|
||||||
@import 'components/notice';
|
@import 'components/notice';
|
||||||
|
@import 'components/with-tooltip';
|
||||||
|
|
||||||
@import 'core/typography';
|
@import 'core/typography';
|
||||||
@import 'core/layout';
|
@import 'core/layout';
|
||||||
|
|
3
ui-v2/app/styles/base/icons/index.scss
Normal file
3
ui-v2/app/styles/base/icons/index.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
$consul-color-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M8.693 10.707a1.862 1.862 0 1 1-.006-3.724 1.862 1.862 0 0 1 .006 3.724" fill="%23961D59"/><path d="M12.336 9.776a.853.853 0 1 1 0-1.707.853.853 0 0 1 0 1.707M15.639 10.556a.853.853 0 1 1 .017-.07c-.01.022-.01.044-.017.07M14.863 8.356a.855.855 0 0 1-.925-1.279.855.855 0 0 1 1.559.255c.024.11.027.222.009.333a.821.821 0 0 1-.642.691M17.977 10.467a.849.849 0 1 1-1.67-.296.849.849 0 0 1 .982-.692c.433.073.74.465.709.905a.221.221 0 0 0-.016.076M17.286 8.368a.853.853 0 1 1-.279-1.684.853.853 0 0 1 .279 1.684M16.651 13.371a.853.853 0 1 1-1.492-.828.853.853 0 0 1 1.492.828M16.325 5.631a.853.853 0 1 1-.84-1.485.853.853 0 0 1 .84 1.485" fill="%23D62783"/><path d="M8.842 17.534c-4.798 0-8.687-3.855-8.687-8.612C.155 4.166 4.045.31 8.842.31a8.645 8.645 0 0 1 5.279 1.77l-1.056 1.372a6.987 6.987 0 0 0-7.297-.709 6.872 6.872 0 0 0 0 12.356 6.987 6.987 0 0 0 7.297-.709l1.056 1.374a8.66 8.66 0 0 1-5.279 1.77z" fill="%23D62783" fill-rule="nonzero"/></g></svg>');
|
||||||
|
$nomad-color-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 16 18" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><path fill="%231F9967" d="M11.569 6.871v2.965l-2.064 1.192-1.443-.894v7.74l.04.002 7.78-4.47V4.48h-.145z"/><path fill="%2325BA81" d="M7.997 0L.24 4.481l5.233 3.074 1.06-.645 2.57 1.435v-2.98l2.465-1.481v2.987l4.314-2.391v-.011z"/><path fill="%2325BA81" d="M7.02 9.54v2.976l-2.347 1.488V8.05l.89-.548L.287 4.48.24 4.48v8.926l7.821 4.467v-7.74z"/></g></svg>');
|
||||||
|
$terraform-color-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 16 18" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="%235C4EE5" d="M5.51 3.15l4.886 2.821v5.644L5.509 8.792z"/><path fill="%234040B2" d="M10.931 5.971v5.644l4.888-2.823V3.15z"/><path fill="%235C4EE5" d="M.086 0v5.642l4.887 2.823V2.82zM5.51 15.053l4.886 2.823v-5.644l-4.887-2.82z"/></g></svg>');
|
|
@ -1,3 +1,4 @@
|
||||||
@import './decoration/index';
|
@import './decoration/index';
|
||||||
@import './color/index';
|
@import './color/index';
|
||||||
@import './typography/index';
|
@import './typography/index';
|
||||||
|
@import './icons/index';
|
||||||
|
|
|
@ -12,10 +12,6 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
%healthchecked-resource,
|
%healthchecked-resource,
|
||||||
%healthchecked-resource li {
|
|
||||||
border-color: $ui-gray-200;
|
|
||||||
}
|
|
||||||
%healthchecked-resource,
|
|
||||||
%healthchecked-resource header,
|
%healthchecked-resource header,
|
||||||
%healthchecked-resource li {
|
%healthchecked-resource li {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -76,3 +72,10 @@
|
||||||
.healthy .healthchecked-resource li:only-child strong {
|
.healthy .healthchecked-resource li:only-child strong {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
%healthchecked-resource ul:empty {
|
||||||
|
position: absolute;
|
||||||
|
top: 18px;
|
||||||
|
right: 20px;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
%healthchecked-resource {
|
||||||
|
border: $decor-border-100;
|
||||||
|
box-shadow: 0 4px 8px 0 rgba($ui-black, 0.05);
|
||||||
|
}
|
||||||
|
%healthchecked-resource li {
|
||||||
|
border-top: $decor-border-100;
|
||||||
|
}
|
||||||
|
%healthchecked-resource,
|
||||||
|
%healthchecked-resource li {
|
||||||
|
border-color: $ui-gray-200;
|
||||||
|
}
|
||||||
%healthchecked-resource li.passing {
|
%healthchecked-resource li.passing {
|
||||||
color: $ui-color-success;
|
color: $ui-color-success;
|
||||||
}
|
}
|
||||||
|
@ -7,17 +18,16 @@
|
||||||
%healthchecked-resource li.critical {
|
%healthchecked-resource li.critical {
|
||||||
color: $ui-color-failure;
|
color: $ui-color-failure;
|
||||||
}
|
}
|
||||||
%healthchecked-resource {
|
|
||||||
border: $decor-border-100;
|
|
||||||
box-shadow: 0 4px 8px 0 rgba($ui-black, 0.05);
|
|
||||||
}
|
|
||||||
%healthchecked-resource:hover,
|
%healthchecked-resource:hover,
|
||||||
%healthchecked-resource:focus {
|
%healthchecked-resource:focus {
|
||||||
box-shadow: 0 8px 10px 0 rgba($ui-black, 0.1);
|
box-shadow: 0 8px 10px 0 rgba($ui-black, 0.1);
|
||||||
}
|
}
|
||||||
%healthchecked-resource li {
|
|
||||||
border-top: $decor-border-100;
|
|
||||||
}
|
|
||||||
%healthchecked-resource {
|
%healthchecked-resource {
|
||||||
border-radius: $radius-small;
|
border-radius: $radius-small;
|
||||||
}
|
}
|
||||||
|
%healthchecked-resource ul:empty {
|
||||||
|
@extend %with-no-healthchecks;
|
||||||
|
}
|
||||||
|
%healthchecked-resource ul:empty::before {
|
||||||
|
color: $ui-gray-400;
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +1,35 @@
|
||||||
%pseudo-icon {
|
/*TODO: The old pseudo-icon was to specific */
|
||||||
width: 1em;
|
/* make a temporary one with the -- prefix */
|
||||||
height: 1em;
|
/* to make it more reusable temporarily */
|
||||||
position: absolute;
|
%--pseudo-icon {
|
||||||
top: 50%;
|
|
||||||
margin-top: -0.6em;
|
|
||||||
display: block;
|
display: block;
|
||||||
content: '';
|
content: '';
|
||||||
|
visibility: visible;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
|
}
|
||||||
|
%pseudo-icon-bg-img {
|
||||||
|
@extend %--pseudo-icon;
|
||||||
|
background-size: contain;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
%pseudo-icon-css {
|
||||||
|
@extend %--pseudo-icon;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
margin-top: -0.6em;
|
||||||
background-color: currentColor;
|
background-color: currentColor;
|
||||||
visibility: visible;
|
}
|
||||||
|
%pseudo-icon {
|
||||||
|
@extend %pseudo-icon-css;
|
||||||
|
}
|
||||||
|
%with-external-source-icon {
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
}
|
}
|
||||||
%with-dot {
|
%with-dot {
|
||||||
content: '';
|
content: '';
|
||||||
|
@ -135,6 +155,10 @@
|
||||||
@extend %pseudo-icon;
|
@extend %pseudo-icon;
|
||||||
background-image: url('data:image/svg+xml;charset=UTF-8,<svg width="8" height="8" xmlns="http://www.w3.org/2000/svg"><path d="M4 5.064L1.064 8 0 6.936 2.936 4 0 1.064 1.064 0 4 2.936 6.936 0 8 1.064 5.064 4 8 6.936 6.936 8 4 5.064z" fill="%23FFF"/></svg>');
|
background-image: url('data:image/svg+xml;charset=UTF-8,<svg width="8" height="8" xmlns="http://www.w3.org/2000/svg"><path d="M4 5.064L1.064 8 0 6.936 2.936 4 0 1.064 1.064 0 4 2.936 6.936 0 8 1.064 5.064 4 8 6.936 6.936 8 4 5.064z" fill="%23FFF"/></svg>');
|
||||||
}
|
}
|
||||||
|
%with-minus {
|
||||||
|
@extend %pseudo-icon;
|
||||||
|
background-image: url('data:image/svg+xml;charset=UTF-8,<svg width="9" height="2" viewBox="0 0 9 2" xmlns="http://www.w3.org/2000/svg"><path fill="%23FFF" fill-rule="nonzero" d="M0 0h8v2H0z"/></svg>');
|
||||||
|
}
|
||||||
%with-warning-icon-orange {
|
%with-warning-icon-orange {
|
||||||
@extend %pseudo-icon;
|
@extend %pseudo-icon;
|
||||||
background-image: url('data:image/svg+xml;charset=UTF-8,<svg width="14" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M13.645 10.092c.24.409.365.88.365 1.37 0 1.392-1.027 2.527-2.294 2.538H2.322c-.824 0-1.592-.487-2.004-1.27a2.761 2.761 0 0 1 0-2.538l4.686-8.904C5.416.505 6.184.018 7.008.018c.824 0 1.592.487 2.004 1.27l4.633 8.804zm-5.989 1.264V9.607H6.344v1.749h1.312zm0-3.048v-4.37H6.344v4.37h1.312z" fill="%23fa8f37"/></svg>');
|
background-image: url('data:image/svg+xml;charset=UTF-8,<svg width="14" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M13.645 10.092c.24.409.365.88.365 1.37 0 1.392-1.027 2.527-2.294 2.538H2.322c-.824 0-1.592-.487-2.004-1.27a2.761 2.761 0 0 1 0-2.538l4.686-8.904C5.416.505 6.184.018 7.008.018c.824 0 1.592.487 2.004 1.27l4.633 8.804zm-5.989 1.264V9.607H6.344v1.749h1.312zm0-3.048v-4.37H6.344v4.37h1.312z" fill="%23fa8f37"/></svg>');
|
||||||
|
@ -183,3 +207,7 @@
|
||||||
@extend %with-cross;
|
@extend %with-cross;
|
||||||
border-radius: 20%;
|
border-radius: 20%;
|
||||||
}
|
}
|
||||||
|
%with-no-healthchecks::before {
|
||||||
|
@extend %with-minus;
|
||||||
|
border-radius: 20%;
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
}
|
}
|
||||||
.healthy > div {
|
.healthy > div {
|
||||||
width: calc(100% + 23px);
|
width: calc(100% + 23px);
|
||||||
|
min-height: 500px;
|
||||||
}
|
}
|
||||||
.unhealthy > div {
|
.unhealthy > div {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
|
@ -30,6 +30,9 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
%app-view h1 span {
|
||||||
|
@extend %with-external-source-icon;
|
||||||
|
}
|
||||||
%app-view {
|
%app-view {
|
||||||
margin-top: 50px;
|
margin-top: 50px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,12 @@ td dt.warning {
|
||||||
td dt.critical {
|
td dt.critical {
|
||||||
@extend %with-critical;
|
@extend %with-critical;
|
||||||
}
|
}
|
||||||
|
td span.zero {
|
||||||
|
@extend %with-no-healthchecks;
|
||||||
|
display: block;
|
||||||
|
text-indent: 20px;
|
||||||
|
color: $ui-gray-400;
|
||||||
|
}
|
||||||
table:not(.sessions) tr {
|
table:not(.sessions) tr {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,17 @@ table tr > * {
|
||||||
tr > * dl {
|
tr > * dl {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
/* TODO: putting this here is less than ideal */
|
||||||
|
/* but this is another area where I am specifically */
|
||||||
|
/* targetting table-like things. This is now a prime */
|
||||||
|
/* area for a bit of refactoring/reorganizing */
|
||||||
|
html.template-service.template-list td:first-child a span,
|
||||||
|
html.template-node.template-show #services td:first-child a span {
|
||||||
|
@extend %with-external-source-icon;
|
||||||
|
float: left;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
html.template-service.template-list main table tr {
|
html.template-service.template-list main table tr {
|
||||||
@extend %services-row;
|
@extend %services-row;
|
||||||
}
|
}
|
||||||
|
@ -34,7 +45,6 @@ html.template-node.template-show main table tr {
|
||||||
html.template-node.template-show main table.sessions tr {
|
html.template-node.template-show main table.sessions tr {
|
||||||
@extend %node-sessions-row;
|
@extend %node-sessions-row;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media #{$--horizontal-session-list} {
|
@media #{$--horizontal-session-list} {
|
||||||
%node-sessions-row > * {
|
%node-sessions-row > * {
|
||||||
// (100% / 7) - (300px / 6) - (120px / 6)
|
// (100% / 7) - (300px / 6) - (120px / 6)
|
||||||
|
|
9
ui-v2/app/styles/components/with-tooltip.scss
Normal file
9
ui-v2/app/styles/components/with-tooltip.scss
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
@import './with-tooltip/index';
|
||||||
|
%app-view h1 span {
|
||||||
|
@extend %with-tooltip;
|
||||||
|
}
|
||||||
|
%app-view h1 span {
|
||||||
|
text-indent: -9000px;
|
||||||
|
font-size: 0;
|
||||||
|
top: -9px;
|
||||||
|
}
|
2
ui-v2/app/styles/components/with-tooltip/index.scss
Normal file
2
ui-v2/app/styles/components/with-tooltip/index.scss
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
@import './skin';
|
||||||
|
@import './layout';
|
39
ui-v2/app/styles/components/with-tooltip/layout.scss
Normal file
39
ui-v2/app/styles/components/with-tooltip/layout.scss
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
%with-tooltip {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
%with-tooltip::before,
|
||||||
|
%with-tooltip::after {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
%with-tooltip::before {
|
||||||
|
padding: 10px;
|
||||||
|
bottom: calc(100% + 5px);
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
content: attr(data-tooltip);
|
||||||
|
// incase you are using text-indent to hide the
|
||||||
|
// text of the element %with-tooltip
|
||||||
|
text-indent: 0;
|
||||||
|
}
|
||||||
|
%with-tooltip::after {
|
||||||
|
content: '';
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -5px;
|
||||||
|
top: -10px;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
%with-tooltip::after,
|
||||||
|
%with-tooltip::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
%with-tooltip:hover::after,
|
||||||
|
%with-tooltip:hover::before,
|
||||||
|
%with-tooltip:focus::after,
|
||||||
|
%with-tooltip:focus::before {
|
||||||
|
display: block;
|
||||||
|
}
|
9
ui-v2/app/styles/components/with-tooltip/skin.scss
Normal file
9
ui-v2/app/styles/components/with-tooltip/skin.scss
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
%with-tooltip::before,
|
||||||
|
%with-tooltip::after {
|
||||||
|
color: $ui-white;
|
||||||
|
background-color: $ui-gray-800;
|
||||||
|
}
|
||||||
|
%with-tooltip::before {
|
||||||
|
border-radius: $decor-radius-200;
|
||||||
|
box-shadow: 0 3px 1px 0 rgba($ui-black, 0.12);
|
||||||
|
}
|
|
@ -48,7 +48,8 @@ td a {
|
||||||
th,
|
th,
|
||||||
%breadcrumbs a,
|
%breadcrumbs a,
|
||||||
%action-group a,
|
%action-group a,
|
||||||
%tab-nav {
|
%tab-nav,
|
||||||
|
%with-tooltip::before {
|
||||||
font-weight: $typo-weight-medium;
|
font-weight: $typo-weight-medium;
|
||||||
}
|
}
|
||||||
main label a[rel*='help'],
|
main label a[rel*='help'],
|
||||||
|
@ -86,6 +87,7 @@ td {
|
||||||
font-size: $typo-size-600;
|
font-size: $typo-size-600;
|
||||||
}
|
}
|
||||||
th,
|
th,
|
||||||
|
%with-tooltip::before,
|
||||||
%healthchecked-resource strong,
|
%healthchecked-resource strong,
|
||||||
%footer {
|
%footer {
|
||||||
font-size: $typo-size-700;
|
font-size: $typo-size-700;
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<header class={{if service 'with-service' }}>
|
<header class={{if service 'with-service' }}>
|
||||||
<strong>{{address}}</strong>
|
<strong>{{address}}</strong>
|
||||||
<a href={{href}}>
|
<a href={{href}}>
|
||||||
<span>{{name}}</span>
|
<span>{{name}}</span>
|
||||||
<em>{{service}}</em>
|
<em>{{service}}</em>
|
||||||
</a>
|
</a>
|
||||||
</header>
|
</header>
|
||||||
<ul>
|
{{! its important to keep this <ul> with no whitespace so we can use :empty in css }}
|
||||||
{{#if status }}
|
<ul>{{#if status }}
|
||||||
<li class={{status}}>
|
<li class={{status}}>
|
||||||
<a href={{href}}>
|
<a href={{href}}>
|
||||||
<strong>{{status}}</strong>
|
<strong>{{status}}</strong>
|
||||||
|
@ -29,5 +29,4 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}</ul>
|
||||||
</ul>
|
|
|
@ -1,6 +1,11 @@
|
||||||
|
{{#if (gt item.Checks.length 0) }}
|
||||||
<ul data-test-node-healthchecks>
|
<ul data-test-node-healthchecks>
|
||||||
{{#each (sort-by (action 'sortChecksByImportance') item.Checks) as |check| }}
|
{{#each (sort-by (action 'sortChecksByImportance') item.Checks) as |check| }}
|
||||||
{{healthcheck-status data-test-node-healthcheck=check.Name tagName='li' name=check.Name class=check.Status status=check.Status notes=check.Notes output=check.Output}}
|
{{healthcheck-status data-test-node-healthcheck=check.Name tagName='li' name=check.Name class=check.Status status=check.Status notes=check.Notes output=check.Output}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
|
{{else}}
|
||||||
|
<p>
|
||||||
|
This node has no health checks.
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
|
|
@ -15,7 +15,10 @@
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot 'row'}}
|
{{#block-slot 'row'}}
|
||||||
<td data-test-service-name="{{item.Service}}">
|
<td data-test-service-name="{{item.Service}}">
|
||||||
<a href={{href-to 'dc.services.show' item.Service }}>{{item.Service}}{{#if (not-eq item.ID item.Service) }} <em data-test-service-id="{{item.ID}}">({{item.ID}})</em>{{/if}}</a>
|
<a href={{href-to 'dc.services.show' item.Service}}>
|
||||||
|
<span data-test-external-source="{{service/external-source item}}" style="background-image: {{css-var (concat '--' (service/external-source item) '-color-svg') 'none'}}"></span>
|
||||||
|
{{item.Service}}{{#if (not-eq item.ID item.Service) }}<em data-test-service-id="{{item.ID}}">({{item.ID}})</em>{{/if}}
|
||||||
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td data-test-service-port="{{item.Port}}" class="port">
|
<td data-test-service-port="{{item.Port}}" class="port">
|
||||||
{{item.Port}}
|
{{item.Port}}
|
||||||
|
|
|
@ -10,13 +10,18 @@
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot 'header'}}
|
{{#block-slot 'header'}}
|
||||||
<h1>
|
<h1>
|
||||||
{{ item.Node }}
|
{{ item.Node }}
|
||||||
</h1>
|
</h1>
|
||||||
{{tab-nav
|
{{tab-nav
|
||||||
items=(compact
|
items=(compact
|
||||||
(array 'Health Checks' 'Services' (if tomography 'Round Trip Time' '') 'Lock Sessions')
|
(array
|
||||||
|
'Health Checks'
|
||||||
|
'Services'
|
||||||
|
(if tomography 'Round Trip Time' '')
|
||||||
|
'Lock Sessions'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
selected=(if selectedTab selectedTab 'health-checks')
|
selected=selectedTab
|
||||||
}}
|
}}
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot 'actions'}}
|
{{#block-slot 'actions'}}
|
||||||
|
@ -49,7 +54,7 @@
|
||||||
)
|
)
|
||||||
) as |panel|
|
) as |panel|
|
||||||
}}
|
}}
|
||||||
{{#tab-section id=panel.id selected=(eq (if selectedTab selectedTab 'health-checks') panel.id) onchange=(action "change")}}
|
{{#tab-section id=panel.id selected=(eq (if selectedTab selectedTab '') panel.id) onchange=(action "change")}}
|
||||||
{{partial panel.partial}}
|
{{partial panel.partial}}
|
||||||
{{/tab-section}}
|
{{/tab-section}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
@ -22,14 +22,21 @@
|
||||||
}}
|
}}
|
||||||
{{#block-slot 'header'}}
|
{{#block-slot 'header'}}
|
||||||
<th style={{remainingWidth}}>Service</th>
|
<th style={{remainingWidth}}>Service</th>
|
||||||
<th style={{thWidth}}>Node Health</th>
|
<th style={{totalWidth}}>Node Health</th>
|
||||||
<th style={{remainingWidth}}>Tags</th>
|
<th style={{remainingWidth}}>Tags</th>
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot 'row'}}
|
{{#block-slot 'row'}}
|
||||||
|
|
||||||
<td data-test-service="{{item.Name}}" style={{remainingWidth}}>
|
<td data-test-service="{{item.Name}}" style={{remainingWidth}}>
|
||||||
<a href={{href-to 'dc.services.show' item.Name}}>{{item.Name}}</a>
|
<a href={{href-to 'dc.services.show' item.Name}}>
|
||||||
|
<span data-test-external-source="{{service/external-source item}}" style="background-image: {{css-var (concat '--' (service/external-source item) '-color-svg') 'none'}}"></span>
|
||||||
|
{{item.Name}}
|
||||||
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td style={{totalWidth}}>
|
||||||
|
{{#if (and (lt item.ChecksPassing 1) (lt item.ChecksWarning 1) (lt item.ChecksCritical 1) )}}
|
||||||
|
<span title="No Healthchecks" class="zero">0</span>
|
||||||
|
{{else}}
|
||||||
<dl>
|
<dl>
|
||||||
<dt title="Passing" class="passing{{if (lt item.ChecksPassing 1) ' zero'}}">Healthchecks Passing</dt>
|
<dt title="Passing" class="passing{{if (lt item.ChecksPassing 1) ' zero'}}">Healthchecks Passing</dt>
|
||||||
<dd title="Passing" class={{if (lt item.ChecksPassing 1) 'zero'}} style={{passingWidth}}>{{format_number item.ChecksPassing}}</dd>
|
<dd title="Passing" class={{if (lt item.ChecksPassing 1) 'zero'}} style={{passingWidth}}>{{format_number item.ChecksPassing}}</dd>
|
||||||
|
@ -38,6 +45,7 @@
|
||||||
<dt title="Critical" class="critical{{if (lt item.ChecksCritical 1) ' zero'}}">Healthchecks Critical</dt>
|
<dt title="Critical" class="critical{{if (lt item.ChecksCritical 1) ' zero'}}">Healthchecks Critical</dt>
|
||||||
<dd title="Critical" class={{if (lt item.ChecksCritical 1) 'zero'}} style={{criticalWidth}}>{{format_number item.ChecksCritical}}</dd>
|
<dd title="Critical" class={{if (lt item.ChecksCritical 1) 'zero'}} style={{criticalWidth}}>{{format_number item.ChecksCritical}}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
<td class="tags" style={{remainingWidth}}>
|
<td class="tags" style={{remainingWidth}}>
|
||||||
{{#if (gt item.Tags.length 0)}}
|
{{#if (gt item.Tags.length 0)}}
|
||||||
|
|
|
@ -5,9 +5,10 @@
|
||||||
</ol>
|
</ol>
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot 'header'}}
|
{{#block-slot 'header'}}
|
||||||
<h1>
|
<h1>
|
||||||
{{ item.Service.Service }}
|
{{ item.Service.Service }}
|
||||||
</h1>
|
<span data-test-external-source="{{service/external-source item.Service}}" style="background-image: {{css-var (concat '--' (service/external-source item.Service) '-color-svg') 'none'}}" data-tooltip="Registered via {{service/external-source item.Service}}">Registered via {{service/external-source item.Service}}</span>
|
||||||
|
</h1>
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot 'toolbar'}}
|
{{#block-slot 'toolbar'}}
|
||||||
{{#if (gt items.length 0) }}
|
{{#if (gt items.length 0) }}
|
||||||
|
|
|
@ -61,9 +61,22 @@ Feature: components / catalog-filter
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
Scenario: Filtering [Model] in [Page]
|
Scenario: Filtering [Model] in [Page]
|
||||||
Given 1 datacenter model with the value "dc1"
|
Given 1 datacenter model with the value "dc1"
|
||||||
And 2 node models from yaml
|
And 1 node model from yaml
|
||||||
---
|
---
|
||||||
- ID: node-0
|
ID: node-0
|
||||||
|
Services:
|
||||||
|
- ID: 'service-0-with-id'
|
||||||
|
Port: 65535
|
||||||
|
Service: 'service-0'
|
||||||
|
Tags: ['monitor', 'two', 'three']
|
||||||
|
- ID: 'service-1'
|
||||||
|
Port: 0
|
||||||
|
Service: 'service-1'
|
||||||
|
Tags: ['hard drive', 'monitor', 'three']
|
||||||
|
- ID: 'service-2'
|
||||||
|
Port: 1
|
||||||
|
Service: 'service-2'
|
||||||
|
Tags: ['one', 'two', 'three']
|
||||||
---
|
---
|
||||||
When I visit the [Page] page for yaml
|
When I visit the [Page] page for yaml
|
||||||
---
|
---
|
||||||
|
@ -71,7 +84,6 @@ Feature: components / catalog-filter
|
||||||
node: node-0
|
node: node-0
|
||||||
---
|
---
|
||||||
# And I see 3 healthcheck model with the name "Disk Util"
|
# And I see 3 healthcheck model with the name "Disk Util"
|
||||||
# And then pause for 5000
|
|
||||||
When I click services on the tabs
|
When I click services on the tabs
|
||||||
And I see servicesIsSelected on the tabs
|
And I see servicesIsSelected on the tabs
|
||||||
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
@setupApplicationTest
|
|
||||||
Feature: Search services within nodes by name and port
|
|
||||||
Scenario: Given 1 node
|
|
||||||
Given 1 datacenter model with the value "dc1"
|
|
||||||
And 1 node models from yaml
|
|
||||||
---
|
|
||||||
- ID: node-0
|
|
||||||
---
|
|
||||||
When I visit the node page for yaml
|
|
||||||
---
|
|
||||||
dc: dc1
|
|
||||||
node: node-0
|
|
||||||
---
|
|
||||||
When I click services on the tabs
|
|
||||||
And I see servicesIsSelected on the tabs
|
|
||||||
|
|
53
ui-v2/tests/acceptance/dc/nodes/services/list.feature
Normal file
53
ui-v2/tests/acceptance/dc/nodes/services/list.feature
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
@setupApplicationTest
|
||||||
|
Feature: dc / nodes / services / list: Node > Services Listing
|
||||||
|
Scenario: Given 1 node
|
||||||
|
Given 1 datacenter model with the value "dc1"
|
||||||
|
And 1 node model from yaml
|
||||||
|
---
|
||||||
|
ID: node-0
|
||||||
|
Services:
|
||||||
|
- ID: 'service-0-with-id'
|
||||||
|
Port: 65535
|
||||||
|
Service: 'service-0'
|
||||||
|
Tags: ['monitor', 'two', 'three']
|
||||||
|
Meta:
|
||||||
|
external-source: consul
|
||||||
|
- ID: 'service-1'
|
||||||
|
Port: 0
|
||||||
|
Service: 'service-1'
|
||||||
|
Tags: ['hard drive', 'monitor', 'three']
|
||||||
|
Meta:
|
||||||
|
external-source: nomad
|
||||||
|
- ID: 'service-2'
|
||||||
|
Port: 1
|
||||||
|
Service: 'service-2'
|
||||||
|
Tags: ['one', 'two', 'three']
|
||||||
|
Meta:
|
||||||
|
external-source: terraform
|
||||||
|
- ID: 'service-3'
|
||||||
|
Port: 2
|
||||||
|
Service: 'service-3'
|
||||||
|
Tags: []
|
||||||
|
Meta:
|
||||||
|
external-source: kubernetes
|
||||||
|
- ID: 'service-4'
|
||||||
|
Port: 3
|
||||||
|
Service: 'service-4'
|
||||||
|
Tags: []
|
||||||
|
Meta: ~
|
||||||
|
---
|
||||||
|
When I visit the node page for yaml
|
||||||
|
---
|
||||||
|
dc: dc1
|
||||||
|
node: node-0
|
||||||
|
---
|
||||||
|
When I click services on the tabs
|
||||||
|
And I see servicesIsSelected on the tabs
|
||||||
|
And I see externalSource on the services like yaml
|
||||||
|
---
|
||||||
|
- consul
|
||||||
|
- nomad
|
||||||
|
- terraform
|
||||||
|
- kubernetes
|
||||||
|
- ~
|
||||||
|
---
|
|
@ -7,7 +7,7 @@ Feature: dc / nodes / sessions / invalidate: Invalidate Lock Sessions
|
||||||
Given 1 datacenter model with the value "dc1"
|
Given 1 datacenter model with the value "dc1"
|
||||||
And 1 node model from yaml
|
And 1 node model from yaml
|
||||||
---
|
---
|
||||||
- ID: node-0
|
ID: node-0
|
||||||
---
|
---
|
||||||
And 2 session models from yaml
|
And 2 session models from yaml
|
||||||
---
|
---
|
||||||
|
|
|
@ -7,7 +7,7 @@ Feature: dc / nodes / sessions / list: List Lock Sessions
|
||||||
Given 1 datacenter model with the value "dc1"
|
Given 1 datacenter model with the value "dc1"
|
||||||
And 1 node model from yaml
|
And 1 node model from yaml
|
||||||
---
|
---
|
||||||
- ID: node-0
|
ID: node-0
|
||||||
---
|
---
|
||||||
And 2 session models from yaml
|
And 2 session models from yaml
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
Feature: Show node
|
Feature: dc / nodes / show: Show node
|
||||||
Scenario: Given 2 nodes all the tabs are visible and clickable
|
Scenario: Given 2 nodes all the tabs are visible and clickable
|
||||||
Given 1 datacenter model with the value "dc1"
|
Given 1 datacenter model with the value "dc1"
|
||||||
And 2 node models from yaml
|
And 2 node models from yaml
|
||||||
---
|
|
||||||
- ID: node-0
|
|
||||||
- ID: node-1
|
|
||||||
---
|
|
||||||
When I visit the node page for yaml
|
When I visit the node page for yaml
|
||||||
---
|
---
|
||||||
dc: dc1
|
dc: dc1
|
||||||
|
@ -22,12 +18,11 @@ Feature: Show node
|
||||||
|
|
||||||
When I click lockSessions on the tabs
|
When I click lockSessions on the tabs
|
||||||
And I see lockSessionsIsSelected on the tabs
|
And I see lockSessionsIsSelected on the tabs
|
||||||
@ignore
|
|
||||||
Scenario: Given 1 node all the tabs are visible and clickable and the RTT one isn't there
|
Scenario: Given 1 node all the tabs are visible and clickable and the RTT one isn't there
|
||||||
Given 1 datacenter model with the value "dc1"
|
Given 1 datacenter model with the value "dc1"
|
||||||
And 1 node models from yaml
|
And 1 node models from yaml
|
||||||
---
|
---
|
||||||
- ID: node-0
|
ID: node-0
|
||||||
---
|
---
|
||||||
When I visit the node page for yaml
|
When I visit the node page for yaml
|
||||||
---
|
---
|
||||||
|
@ -43,4 +38,20 @@ Feature: Show node
|
||||||
|
|
||||||
When I click lockSessions on the tabs
|
When I click lockSessions on the tabs
|
||||||
And I see lockSessionsIsSelected on the tabs
|
And I see lockSessionsIsSelected on the tabs
|
||||||
|
Scenario: Given 1 node with no checks all the tabs are visible but the Services tab is selected
|
||||||
|
Given 1 datacenter model with the value "dc1"
|
||||||
|
And 1 node models from yaml
|
||||||
|
---
|
||||||
|
ID: node-0
|
||||||
|
Checks: []
|
||||||
|
---
|
||||||
|
When I visit the node page for yaml
|
||||||
|
---
|
||||||
|
dc: dc1
|
||||||
|
node: node-0
|
||||||
|
---
|
||||||
|
And I see healthChecks on the tabs
|
||||||
|
And I see services on the tabs
|
||||||
|
And I see roundTripTime on the tabs
|
||||||
|
And I see lockSessions on the tabs
|
||||||
|
And I see servicesIsSelected on the tabs
|
||||||
|
|
|
@ -1,11 +1,36 @@
|
||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
Feature: Services
|
Feature: dc / services: List Services
|
||||||
Scenario:
|
Scenario:
|
||||||
Given 1 datacenter model with the value "dc-1"
|
Given 1 datacenter model with the value "dc-1"
|
||||||
And 3 service models
|
And 5 service models from yaml
|
||||||
|
---
|
||||||
|
- Name: Service 1
|
||||||
|
Meta:
|
||||||
|
external-source: consul
|
||||||
|
- Name: Service 2
|
||||||
|
Meta:
|
||||||
|
external-source: nomad
|
||||||
|
- Name: Service 3
|
||||||
|
Meta:
|
||||||
|
external-source: terraform
|
||||||
|
- Name: Service 4
|
||||||
|
Meta:
|
||||||
|
external-source: kubernetes
|
||||||
|
- Name: Service 5
|
||||||
|
Meta: ~
|
||||||
|
---
|
||||||
When I visit the services page for yaml
|
When I visit the services page for yaml
|
||||||
---
|
---
|
||||||
dc: dc-1
|
dc: dc-1
|
||||||
---
|
---
|
||||||
Then the url should be /dc-1/services
|
Then the url should be /dc-1/services
|
||||||
Then I see 3 service models
|
Then I see 5 service models
|
||||||
|
And I see externalSource on the services like yaml
|
||||||
|
---
|
||||||
|
- consul
|
||||||
|
- nomad
|
||||||
|
- terraform
|
||||||
|
- kubernetes
|
||||||
|
- ~
|
||||||
|
---
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,21 @@
|
||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
Feature: dc / services / show: Show Service
|
Feature: dc / services / show: Show Service
|
||||||
|
Scenario: Given a service with an external source, the logo is displayed
|
||||||
|
Given 1 datacenter model with the value "dc1"
|
||||||
|
And 1 node models
|
||||||
|
And 1 service model from yaml
|
||||||
|
---
|
||||||
|
- Service:
|
||||||
|
Tags: ['Tag1', 'Tag2']
|
||||||
|
Meta:
|
||||||
|
external-source: consul
|
||||||
|
---
|
||||||
|
When I visit the service page for yaml
|
||||||
|
---
|
||||||
|
dc: dc1
|
||||||
|
service: service-0
|
||||||
|
---
|
||||||
|
Then I see externalSource like "consul"
|
||||||
Scenario: Given various services with various tags, all tags are displayed
|
Scenario: Given various services with various tags, all tags are displayed
|
||||||
Given 1 datacenter model with the value "dc1"
|
Given 1 datacenter model with the value "dc1"
|
||||||
And 3 node models
|
And 3 node models
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import steps from '../../steps';
|
import steps from '../../../steps';
|
||||||
|
|
||||||
// step definitions that are shared between features should be moved to the
|
// step definitions that are shared between features should be moved to the
|
||||||
// tests/acceptance/steps/steps.js file
|
// tests/acceptance/steps/steps.js file
|
||||||
|
|
||||||
export default function(assert) {
|
export default function(assert) {
|
||||||
return steps(assert).then('I should find a file', function() {
|
return steps(assert).then('I should find a file', function() {
|
||||||
assert.ok(true, this.step);
|
assert.ok(true, this.step);
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -8,7 +8,7 @@ export default function(type) {
|
||||||
requests = ['/v1/internal/ui/services', '/v1/health/service/'];
|
requests = ['/v1/internal/ui/services', '/v1/health/service/'];
|
||||||
break;
|
break;
|
||||||
case 'node':
|
case 'node':
|
||||||
requests = ['/v1/internal/ui/nodes'];
|
requests = ['/v1/internal/ui/nodes', '/v1/internal/ui/node/'];
|
||||||
break;
|
break;
|
||||||
case 'kv':
|
case 'kv':
|
||||||
requests = ['/v1/kv/'];
|
requests = ['/v1/kv/'];
|
||||||
|
|
32
ui-v2/tests/integration/helpers/css-var-test.js
Normal file
32
ui-v2/tests/integration/helpers/css-var-test.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { moduleForComponent, test } from 'ember-qunit';
|
||||||
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
|
|
||||||
|
moduleForComponent('css-var', 'helper:css-var', {
|
||||||
|
integration: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Replace this with your real tests.
|
||||||
|
test("it renders nothing if the variable doesn't exist", function(assert) {
|
||||||
|
this.set('inputValue', '1234');
|
||||||
|
|
||||||
|
this.render(hbs`{{css-var inputValue}}`);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
this.$()
|
||||||
|
.text()
|
||||||
|
.trim(),
|
||||||
|
''
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test("it renders a default if the variable doesn't exist", function(assert) {
|
||||||
|
this.set('inputValue', '1234');
|
||||||
|
|
||||||
|
this.render(hbs`{{css-var inputValue 'none'}}`);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
this.$()
|
||||||
|
.text()
|
||||||
|
.trim(),
|
||||||
|
'none'
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { moduleForComponent, test } from 'ember-qunit';
|
||||||
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
|
|
||||||
|
moduleForComponent('service/external-source', 'helper:service/external-source', {
|
||||||
|
integration: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Replace this with your real tests.
|
||||||
|
test('it renders', function(assert) {
|
||||||
|
this.set('inputValue', { Meta: { 'external-source': 'consul' } });
|
||||||
|
|
||||||
|
this.render(hbs`{{service/external-source inputValue}}`);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
this.$()
|
||||||
|
.text()
|
||||||
|
.trim(),
|
||||||
|
'consul'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('it renders prefixed', function(assert) {
|
||||||
|
this.set('inputValue', { Meta: { 'external-source': 'consul' } });
|
||||||
|
|
||||||
|
this.render(hbs`{{service/external-source inputValue prefix='external-source-'}}`);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
this.$()
|
||||||
|
.text()
|
||||||
|
.trim(),
|
||||||
|
'external-source-consul'
|
||||||
|
);
|
||||||
|
});
|
|
@ -9,6 +9,7 @@ export default function(visitable, deletable, clickable, attribute, collection,
|
||||||
id: attribute('data-test-service-id', '[data-test-service-id]'),
|
id: attribute('data-test-service-id', '[data-test-service-id]'),
|
||||||
name: attribute('data-test-service-name', '[data-test-service-name]'),
|
name: attribute('data-test-service-name', '[data-test-service-name]'),
|
||||||
port: attribute('data-test-service-port', '.port'),
|
port: attribute('data-test-service-port', '.port'),
|
||||||
|
externalSource: attribute('data-test-external-source', 'a span'),
|
||||||
}),
|
}),
|
||||||
sessions: collection(
|
sessions: collection(
|
||||||
'#lock-sessions [data-test-tabular-row]',
|
'#lock-sessions [data-test-tabular-row]',
|
||||||
|
|
|
@ -4,6 +4,7 @@ export default function(visitable, clickable, attribute, collection, page, filte
|
||||||
services: collection('[data-test-service]', {
|
services: collection('[data-test-service]', {
|
||||||
name: attribute('data-test-service'),
|
name: attribute('data-test-service'),
|
||||||
service: clickable('a'),
|
service: clickable('a'),
|
||||||
|
externalSource: attribute('data-test-external-source', 'a span'),
|
||||||
}),
|
}),
|
||||||
dcs: collection('[data-test-datacenter-picker]'),
|
dcs: collection('[data-test-datacenter-picker]'),
|
||||||
navigation: page.navigation,
|
navigation: page.navigation,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export default function(visitable, attribute, collection, text, filter) {
|
export default function(visitable, attribute, collection, text, filter) {
|
||||||
return {
|
return {
|
||||||
visit: visitable('/:dc/services/:service'),
|
visit: visitable('/:dc/services/:service'),
|
||||||
|
externalSource: attribute('data-test-external-source', 'h1 span'),
|
||||||
nodes: collection('[data-test-node]', {
|
nodes: collection('[data-test-node]', {
|
||||||
name: attribute('data-test-node'),
|
name: attribute('data-test-node'),
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -317,7 +317,11 @@ export default function(assert) {
|
||||||
// this will catch if we get aren't managing to select a component
|
// this will catch if we get aren't managing to select a component
|
||||||
assert.ok(iterator.length > 0);
|
assert.ok(iterator.length > 0);
|
||||||
iterator.forEach(function(item, i, arr) {
|
iterator.forEach(function(item, i, arr) {
|
||||||
const actual = _component.objectAt(i)[property];
|
const actual =
|
||||||
|
typeof _component.objectAt(i)[property] === 'undefined'
|
||||||
|
? null
|
||||||
|
: _component.objectAt(i)[property];
|
||||||
|
|
||||||
// anything coming from the DOM is going to be text/strings
|
// anything coming from the DOM is going to be text/strings
|
||||||
// if the yaml has numbers, cast them to strings
|
// if the yaml has numbers, cast them to strings
|
||||||
// TODO: This would get problematic for deeper objects
|
// TODO: This would get problematic for deeper objects
|
||||||
|
@ -380,6 +384,13 @@ export default function(assert) {
|
||||||
.then(['I see $property'], function(property) {
|
.then(['I see $property'], function(property) {
|
||||||
assert.ok(currentPage[property], `Expected to see ${property}`);
|
assert.ok(currentPage[property], `Expected to see ${property}`);
|
||||||
})
|
})
|
||||||
|
.then(['I see $property like "$value"'], function(property, value) {
|
||||||
|
assert.equal(
|
||||||
|
currentPage[property],
|
||||||
|
value,
|
||||||
|
`Expected to see ${property}, was ${currentPage[property]}`
|
||||||
|
);
|
||||||
|
})
|
||||||
.then(['I see the text "$text" in "$selector"'], function(text, selector) {
|
.then(['I see the text "$text" in "$selector"'], function(text, selector) {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
find(selector).textContent.indexOf(text) !== -1,
|
find(selector).textContent.indexOf(text) !== -1,
|
||||||
|
|
|
@ -82,12 +82,12 @@
|
||||||
js-yaml "^3.10.0"
|
js-yaml "^3.10.0"
|
||||||
|
|
||||||
"@hashicorp/consul-api-double@^1.4.0":
|
"@hashicorp/consul-api-double@^1.4.0":
|
||||||
version "1.4.3"
|
version "1.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-1.4.3.tgz#0d08e167b1163200885636e6d368585004db1c98"
|
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-1.5.1.tgz#73ce7696dc4475f69a59462e6690611b73ec6ced"
|
||||||
|
|
||||||
"@hashicorp/ember-cli-api-double@^1.3.0":
|
"@hashicorp/ember-cli-api-double@^1.3.0":
|
||||||
version "1.5.1"
|
version "1.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-1.5.1.tgz#92789eaf2073b5871d859700bc696e9552bb835b"
|
resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-1.6.0.tgz#b40eae09d14ae8491516598f881cf51cbe7e2787"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@hashicorp/api-double" "^1.3.0"
|
"@hashicorp/api-double" "^1.3.0"
|
||||||
array-range "^1.0.1"
|
array-range "^1.0.1"
|
||||||
|
@ -5343,9 +5343,9 @@ invert-kv@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
|
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
|
||||||
|
|
||||||
ipaddr.js@1.6.0:
|
ipaddr.js@1.8.0:
|
||||||
version "1.6.0"
|
version "1.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b"
|
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e"
|
||||||
|
|
||||||
is-accessor-descriptor@^0.1.6:
|
is-accessor-descriptor@^0.1.6:
|
||||||
version "0.1.6"
|
version "0.1.6"
|
||||||
|
@ -6700,9 +6700,9 @@ miller-rabin@^4.0.0:
|
||||||
version "1.33.0"
|
version "1.33.0"
|
||||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
|
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
|
||||||
|
|
||||||
mime-db@~1.35.0:
|
mime-db@~1.36.0:
|
||||||
version "1.35.0"
|
version "1.36.0"
|
||||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47"
|
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397"
|
||||||
|
|
||||||
mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.17, mime-types@~2.1.7:
|
mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.17, mime-types@~2.1.7:
|
||||||
version "2.1.18"
|
version "2.1.18"
|
||||||
|
@ -6711,10 +6711,10 @@ mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.17, mime-types@~2.1.7:
|
||||||
mime-db "~1.33.0"
|
mime-db "~1.33.0"
|
||||||
|
|
||||||
mime-types@~2.1.18:
|
mime-types@~2.1.18:
|
||||||
version "2.1.19"
|
version "2.1.20"
|
||||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.19.tgz#71e464537a7ef81c15f2db9d97e913fc0ff606f0"
|
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19"
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-db "~1.35.0"
|
mime-db "~1.36.0"
|
||||||
|
|
||||||
mime@1.4.1:
|
mime@1.4.1:
|
||||||
version "1.4.1"
|
version "1.4.1"
|
||||||
|
@ -7548,11 +7548,11 @@ promised-io@*:
|
||||||
resolved "https://registry.yarnpkg.com/promised-io/-/promised-io-0.3.5.tgz#4ad217bb3658bcaae9946b17a8668ecd851e1356"
|
resolved "https://registry.yarnpkg.com/promised-io/-/promised-io-0.3.5.tgz#4ad217bb3658bcaae9946b17a8668ecd851e1356"
|
||||||
|
|
||||||
proxy-addr@~2.0.3:
|
proxy-addr@~2.0.3:
|
||||||
version "2.0.3"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
|
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93"
|
||||||
dependencies:
|
dependencies:
|
||||||
forwarded "~0.1.2"
|
forwarded "~0.1.2"
|
||||||
ipaddr.js "1.6.0"
|
ipaddr.js "1.8.0"
|
||||||
|
|
||||||
pseudomap@^1.0.2:
|
pseudomap@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
|
@ -8109,8 +8109,8 @@ rollup@^0.58.1:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
route-recognizer@^0.3.3:
|
route-recognizer@^0.3.3:
|
||||||
version "0.3.3"
|
version "0.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.3.3.tgz#1d365e27fa6995e091675f7dc940a8c00353bd29"
|
resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.3.4.tgz#39ab1ffbce1c59e6d2bdca416f0932611e4f3ca3"
|
||||||
|
|
||||||
rsvp@^3.0.14, rsvp@^3.0.16, rsvp@^3.0.17, rsvp@^3.0.18, rsvp@^3.0.21, rsvp@^3.0.6, rsvp@^3.1.0, rsvp@^3.2.1, rsvp@^3.3.3, rsvp@^3.5.0:
|
rsvp@^3.0.14, rsvp@^3.0.16, rsvp@^3.0.17, rsvp@^3.0.18, rsvp@^3.0.21, rsvp@^3.0.6, rsvp@^3.1.0, rsvp@^3.2.1, rsvp@^3.3.3, rsvp@^3.5.0:
|
||||||
version "3.6.2"
|
version "3.6.2"
|
||||||
|
|
Loading…
Reference in a new issue