ui: Add consul-service-list presentational component (#7279)
This commit moves our service list into a new presentational component, and is therefore mainly just moving things around. The main thing moved here is the logic required to resizing columns correctly is now moved to a component instead of the controller
This commit is contained in:
parent
231a84962a
commit
f5ff096d78
|
@ -0,0 +1,65 @@
|
|||
import Component from '@ember/component';
|
||||
import { get, computed } from '@ember/object';
|
||||
import { htmlSafe } from '@ember/string';
|
||||
|
||||
const max = function(arr, prop) {
|
||||
return arr.reduce(function(prev, item) {
|
||||
return Math.max(prev, get(item, prop));
|
||||
}, 0);
|
||||
};
|
||||
const chunk = function(str, size) {
|
||||
const num = Math.ceil(str.length / size);
|
||||
const chunks = new Array(num);
|
||||
for (let i = 0, o = 0; i < num; ++i, o += size) {
|
||||
chunks[i] = str.substr(o, size);
|
||||
}
|
||||
return chunks;
|
||||
};
|
||||
const width = function(num) {
|
||||
const str = num.toString();
|
||||
const len = str.length;
|
||||
const commas = chunk(str, 3).length - 1;
|
||||
return commas * 4 + len * 10;
|
||||
};
|
||||
const widthDeclaration = function(num) {
|
||||
return htmlSafe(`width: ${num}px`);
|
||||
};
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
onchange: function() {},
|
||||
maxWidth: computed('{maxPassing,maxWarning,maxCritical}', function() {
|
||||
const PADDING = 32 * 3 + 13;
|
||||
return ['maxPassing', 'maxWarning', 'maxCritical'].reduce((prev, item) => {
|
||||
return prev + width(get(this, item));
|
||||
}, PADDING);
|
||||
}),
|
||||
totalWidth: computed('maxWidth', function() {
|
||||
return widthDeclaration(get(this, 'maxWidth'));
|
||||
}),
|
||||
remainingWidth: computed('maxWidth', function() {
|
||||
// maxWidth is the maximum width of the healthchecks column
|
||||
// there are currently 2 other columns so divide it by 2 and
|
||||
// take that off 50% (100% / number of fluid columns)
|
||||
// also we added a Type column which we've currently fixed to 100px
|
||||
// so again divide that by 2 and take it off each fluid column
|
||||
return htmlSafe(`width: calc(50% - 50px - ${Math.round(get(this, 'maxWidth') / 2)}px)`);
|
||||
}),
|
||||
maxPassing: computed('items.[]', function() {
|
||||
return max(get(this, 'items'), 'ChecksPassing');
|
||||
}),
|
||||
maxWarning: computed('items.[]', function() {
|
||||
return max(get(this, 'items'), 'ChecksWarning');
|
||||
}),
|
||||
maxCritical: computed('items.[]', function() {
|
||||
return max(get(this, 'items'), 'ChecksCritical');
|
||||
}),
|
||||
passingWidth: computed('maxPassing', function() {
|
||||
return widthDeclaration(width(get(this, 'maxPassing')));
|
||||
}),
|
||||
warningWidth: computed('maxWarning', function() {
|
||||
return widthDeclaration(width(get(this, 'maxWarning')));
|
||||
}),
|
||||
criticalWidth: computed('maxCritical', function() {
|
||||
return widthDeclaration(width(get(this, 'maxCritical')));
|
||||
}),
|
||||
});
|
|
@ -1,30 +1,7 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { get, computed } from '@ember/object';
|
||||
import { htmlSafe } from '@ember/string';
|
||||
import WithEventSource from 'consul-ui/mixins/with-event-source';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
const max = function(arr, prop) {
|
||||
return arr.reduce(function(prev, item) {
|
||||
return Math.max(prev, get(item, prop));
|
||||
}, 0);
|
||||
};
|
||||
const chunk = function(str, size) {
|
||||
const num = Math.ceil(str.length / size);
|
||||
const chunks = new Array(num);
|
||||
for (let i = 0, o = 0; i < num; ++i, o += size) {
|
||||
chunks[i] = str.substr(o, size);
|
||||
}
|
||||
return chunks;
|
||||
};
|
||||
const width = function(num) {
|
||||
const str = num.toString();
|
||||
const len = str.length;
|
||||
const commas = chunk(str, 3).length - 1;
|
||||
return commas * 4 + len * 10;
|
||||
};
|
||||
const widthDeclaration = function(num) {
|
||||
return htmlSafe(`width: ${num}px`);
|
||||
};
|
||||
export default Controller.extend(WithEventSource, WithSearching, {
|
||||
queryParams: {
|
||||
s: {
|
||||
|
@ -42,39 +19,4 @@ export default Controller.extend(WithEventSource, WithSearching, {
|
|||
.add(this.items)
|
||||
.search(this.terms);
|
||||
}),
|
||||
maxWidth: computed('{maxPassing,maxWarning,maxCritical}', function() {
|
||||
const PADDING = 32 * 3 + 13;
|
||||
return ['maxPassing', 'maxWarning', 'maxCritical'].reduce((prev, item) => {
|
||||
return prev + width(get(this, item));
|
||||
}, PADDING);
|
||||
}),
|
||||
totalWidth: computed('maxWidth', function() {
|
||||
return widthDeclaration(this.maxWidth);
|
||||
}),
|
||||
remainingWidth: computed('maxWidth', function() {
|
||||
// maxWidth is the maximum width of the healthchecks column
|
||||
// there are currently 2 other columns so divide it by 2 and
|
||||
// take that off 50% (100% / number of fluid columns)
|
||||
// also we added a Type column which we've currently fixed to 100px
|
||||
// so again divide that by 2 and take it off each fluid column
|
||||
return htmlSafe(`width: calc(50% - ${Math.round(this.maxWidth / 2)}px)`);
|
||||
}),
|
||||
maxPassing: computed('items.[]', function() {
|
||||
return max(this.items, 'ChecksPassing');
|
||||
}),
|
||||
maxWarning: computed('items.[]', function() {
|
||||
return max(this.items, 'ChecksWarning');
|
||||
}),
|
||||
maxCritical: computed('items.[]', function() {
|
||||
return max(this.items, 'ChecksCritical');
|
||||
}),
|
||||
passingWidth: computed('maxPassing', function() {
|
||||
return widthDeclaration(width(this.maxPassing));
|
||||
}),
|
||||
warningWidth: computed('maxWarning', function() {
|
||||
return widthDeclaration(width(this.maxWarning));
|
||||
}),
|
||||
criticalWidth: computed('maxCritical', function() {
|
||||
return widthDeclaration(width(this.maxCritical));
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
{{#if (gt items.length 0)}}
|
||||
{{#tabular-collection items=items as |item index|}}
|
||||
{{#block-slot name='header'}}
|
||||
<th style={{remainingWidth}}>Service</th>
|
||||
<th style={{totalWidth}}>
|
||||
Health Checks
|
||||
<span>
|
||||
<em role="tooltip">The number of health checks for the service on all nodes</em>
|
||||
</span>
|
||||
</th>
|
||||
<th style={{remainingWidth}}>Tags</th>
|
||||
{{/block-slot}}
|
||||
{{#block-slot name='row'}}
|
||||
<td data-test-service={{item.Name}} style={{remainingWidth}}>
|
||||
<a href={{href-to routeName item.Name}}>
|
||||
{{#let (service/external-source item) as |externalSource| }}
|
||||
{{#if externalSource }}
|
||||
<span data-test-external-source={{externalSource}} style={{concat 'background-image: var(--' externalSource '-icon)'}}></span>
|
||||
{{else}}
|
||||
<span></span>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{item.Name}}
|
||||
</a>
|
||||
</td>
|
||||
<td style={{totalWidth}}>
|
||||
{{healthcheck-info
|
||||
passing=item.ChecksPassing warning=item.ChecksWarning critical=item.ChecksCritical
|
||||
passingWidth=passingWidth warningWidth=warningWidth criticalWidth=criticalWidth
|
||||
}}
|
||||
</td>
|
||||
<td style={{remainingWidth}}>
|
||||
{{tag-list items=item.Tags}}
|
||||
</td>
|
||||
{{/block-slot}}
|
||||
{{/tabular-collection}}
|
||||
{{/if}}
|
|
@ -22,40 +22,7 @@
|
|||
{{#block-slot name='content'}}
|
||||
{{#changeable-set dispatcher=searchable}}
|
||||
{{#block-slot name='set' as |filtered|}}
|
||||
{{#tabular-collection
|
||||
route='dc.services.show'
|
||||
key='Name'
|
||||
items=filtered as |item index|
|
||||
}}
|
||||
{{#block-slot name='header'}}
|
||||
<th style={{remainingWidth}}>Service</th>
|
||||
<th style={{totalWidth}}>Health Checks<span><em role="tooltip">The number of health checks for the service on all nodes</em></span></th>
|
||||
<th style={{remainingWidth}}>Tags</th>
|
||||
{{/block-slot}}
|
||||
{{#block-slot name='row'}}
|
||||
<td data-test-service="{{item.Name}}" style={{remainingWidth}}>
|
||||
<a href={{href-to 'dc.services.show' item.Name}}>
|
||||
{{#let (service/external-source item) as |externalSource| }}
|
||||
{{#if externalSource }}
|
||||
<span data-test-external-source={{externalSource}} style={{concat 'background-image: var(--' externalSource '-icon)'}}></span>
|
||||
{{else}}
|
||||
<span></span>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{item.Name}}
|
||||
</a>
|
||||
</td>
|
||||
<td style={{totalWidth}}>
|
||||
{{healthcheck-info
|
||||
passing=item.ChecksPassing warning=item.ChecksWarning critical=item.ChecksCritical
|
||||
passingWidth=passingWidth warningWidth=warningWidth criticalWidth=criticalWidth
|
||||
}}
|
||||
</td>
|
||||
<td style={{remainingWidth}}>
|
||||
{{tag-list items=item.Tags}}
|
||||
</td>
|
||||
{{/block-slot}}
|
||||
{{/tabular-collection}}
|
||||
{{consul-service-list routeName="dc.services.show" items=filtered}}
|
||||
{{/block-slot}}
|
||||
{{#block-slot name='empty'}}
|
||||
<p>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
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 | consul-service-list', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.set('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`{{consul-service-list}}`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), '');
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
{{#consul-service-list}}{{/consul-service-list}}
|
||||
`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), '');
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue