From 771973a7133791cd6bc35cd47fbc4b7d38b625a0 Mon Sep 17 00:00:00 2001 From: John Cowen Date: Wed, 12 Feb 2020 17:46:29 +0000 Subject: [PATCH] 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 --- ui-v2/app/components/consul-service-list.js | 65 +++++++++++++++++++ ui-v2/app/controllers/dc/services/index.js | 58 ----------------- .../components/consul-service-list.hbs | 37 +++++++++++ ui-v2/app/templates/dc/services/index.hbs | 35 +--------- .../components/consul-service-list-test.js | 24 +++++++ 5 files changed, 127 insertions(+), 92 deletions(-) create mode 100644 ui-v2/app/components/consul-service-list.js create mode 100644 ui-v2/app/templates/components/consul-service-list.hbs create mode 100644 ui-v2/tests/integration/components/consul-service-list-test.js diff --git a/ui-v2/app/components/consul-service-list.js b/ui-v2/app/components/consul-service-list.js new file mode 100644 index 0000000000..98d3278c4d --- /dev/null +++ b/ui-v2/app/components/consul-service-list.js @@ -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'))); + }), +}); diff --git a/ui-v2/app/controllers/dc/services/index.js b/ui-v2/app/controllers/dc/services/index.js index 58c92d465c..aa8eeafb76 100644 --- a/ui-v2/app/controllers/dc/services/index.js +++ b/ui-v2/app/controllers/dc/services/index.js @@ -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)); - }), }); diff --git a/ui-v2/app/templates/components/consul-service-list.hbs b/ui-v2/app/templates/components/consul-service-list.hbs new file mode 100644 index 0000000000..2e1ef136de --- /dev/null +++ b/ui-v2/app/templates/components/consul-service-list.hbs @@ -0,0 +1,37 @@ +{{#if (gt items.length 0)}} + {{#tabular-collection items=items as |item index|}} + {{#block-slot name='header'}} + Service + + Health Checks + + The number of health checks for the service on all nodes + + + Tags + {{/block-slot}} + {{#block-slot name='row'}} + + + {{#let (service/external-source item) as |externalSource| }} + {{#if externalSource }} + + {{else}} + + {{/if}} + {{/let}} + {{item.Name}} + + + + {{healthcheck-info + passing=item.ChecksPassing warning=item.ChecksWarning critical=item.ChecksCritical + passingWidth=passingWidth warningWidth=warningWidth criticalWidth=criticalWidth + }} + + + {{tag-list items=item.Tags}} + + {{/block-slot}} + {{/tabular-collection}} +{{/if}} \ No newline at end of file diff --git a/ui-v2/app/templates/dc/services/index.hbs b/ui-v2/app/templates/dc/services/index.hbs index 348b6cd00d..cbc8ac365b 100644 --- a/ui-v2/app/templates/dc/services/index.hbs +++ b/ui-v2/app/templates/dc/services/index.hbs @@ -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'}} - Service - Health ChecksThe number of health checks for the service on all nodes - Tags - {{/block-slot}} - {{#block-slot name='row'}} - - - {{#let (service/external-source item) as |externalSource| }} - {{#if externalSource }} - - {{else}} - - {{/if}} - {{/let}} - {{item.Name}} - - - - {{healthcheck-info - passing=item.ChecksPassing warning=item.ChecksWarning critical=item.ChecksCritical - passingWidth=passingWidth warningWidth=warningWidth criticalWidth=criticalWidth - }} - - - {{tag-list items=item.Tags}} - - {{/block-slot}} - {{/tabular-collection}} + {{consul-service-list routeName="dc.services.show" items=filtered}} {{/block-slot}} {{#block-slot name='empty'}}

diff --git a/ui-v2/tests/integration/components/consul-service-list-test.js b/ui-v2/tests/integration/components/consul-service-list-test.js new file mode 100644 index 0000000000..dec98c3929 --- /dev/null +++ b/ui-v2/tests/integration/components/consul-service-list-test.js @@ -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(), ''); + }); +});