Skip to content

Commit

Permalink
[ui] Deployment History search (#16608)
Browse files Browse the repository at this point in the history
* Functioning searchbox

* Some nice animations for history items

* History search test

* Fixing up some old mirage conventions

* some a11y rule override to account for scss keyframes
  • Loading branch information
philrenaud authored Mar 24, 2023
1 parent 211ae60 commit 9102247
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 16 deletions.
29 changes: 22 additions & 7 deletions ui/app/components/job-status/deployment-history.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
<div class="deployment-history">
<h4 class="title is-5">Deployment History</h4>
<header>
<h4 class="title is-5">Deployment History</h4>
<SearchBox
data-test-history-search
@searchTerm={{mut this.searchTerm}}
@placeholder="Search events..."
/>
</header>
<ol class="timeline">
{{#each this.history as |deployment-log|}}
<li class="timeline-object {{if (eq deployment-log.exitCode 1) "error"}}">
Expand All @@ -19,12 +26,20 @@
</div>
</li>
{{else}}
{{#if this.deploymentAllocations}}
<li class="timeline-object">
<div class="boxed-section-head is-light">
<span>No deployment events yet</span>
</div>
</li>
{{#if this.deploymentAllocations.length}}
{{#if this.searchTerm}}
<li class="timeline-object" data-test-history-search-no-match>
<div class="boxed-section-head is-light">
<span>No events match {{this.searchTerm}}</span>
</div>
</li>
{{else}}
<li class="timeline-object">
<div class="boxed-section-head is-light">
<span>No deployment events yet</span>
</div>
</li>
{{/if}}
{{else}}
<li class="timeline-object">
<div class="boxed-section-head is-light">
Expand Down
22 changes: 22 additions & 0 deletions ui/app/components/job-status/deployment-history.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default class JobStatusDeploymentHistoryComponent extends Component {
.flat()
)
.flat()
.filter((a) => this.containsSearchTerm(a))
.sort((a, b) => a.get('time') - b.get('time'))
.reverse();
} catch (e) {
Expand All @@ -71,4 +72,25 @@ export default class JobStatusDeploymentHistoryComponent extends Component {
color: 'critical',
});
}

// #region search

/**
* @type { string }
*/
@tracked searchTerm = '';

/**
* @param { import('../../models/task-event').default } taskEvent
* @returns { boolean }
*/
containsSearchTerm(taskEvent) {
return (
taskEvent.message.toLowerCase().includes(this.searchTerm.toLowerCase()) ||
taskEvent.type.toLowerCase().includes(this.searchTerm.toLowerCase()) ||
taskEvent.state.allocation.shortId.includes(this.searchTerm.toLowerCase())
);
}

// #endregion search
}
48 changes: 47 additions & 1 deletion ui/app/styles/components/job-status-panel.scss
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,41 @@
display: grid;
grid-template-columns: 70% auto;
gap: 1rem;
margin-top: 1rem; // TODO: grid-ify the deployment panel generally and just use gap for this
margin-top: 2rem; // TODO: grid-ify the deployment panel generally and just use gap for this
}

.deployment-history {
& > header {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 1rem;
margin-bottom: 1rem;
align-items: end;
& > .search-box {
max-width: unset;
}
}
& > ol {
max-height: 300px;
overflow-y: auto;
}
& > ol > li {
@for $i from 1 through 50 {
&:nth-child(#{$i}) {
animation-name: historyItemSlide;
animation-duration: 0.2s;
animation-fill-mode: both;
animation-delay: 0.1s + (0.05 * $i);
}

&:nth-child(#{$i}) > div {
animation-name: historyItemShine;
animation-duration: 1s;
animation-fill-mode: both;
animation-delay: 0.1s + (0.05 * $i);
}
}

& > div {
gap: 0.5rem;
}
Expand Down Expand Up @@ -218,3 +244,23 @@
}
}
}

@keyframes historyItemSlide {
from {
opacity: 0;
top: 40px;
}
to {
opacity: 1;
top: 0px;
}
}

@keyframes historyItemShine {
from {
box-shadow: inset 0 0 0 100px rgba(255, 200, 0, 0.2);
}
to {
box-shadow: inset 0 0 0 100px rgba(255, 200, 0, 0);
}
}
2 changes: 1 addition & 1 deletion ui/mirage/factories/task-event.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ export default Factory.extend({
exitCode: () => null,
time: () => faker.date.past(2 / 365, REF_TIME) * 1000000,

displayMessage: () => faker.lorem.sentence(),
message: () => faker.lorem.sentence(),
});
2 changes: 1 addition & 1 deletion ui/tests/acceptance/allocation-detail-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ module('Acceptance | allocation detail', function (hooks) {

assert.equal(taskRow.name, task.name, 'Name');
assert.equal(taskRow.state, task.state, 'State');
assert.equal(taskRow.message, event.displayMessage, 'Event Message');
assert.equal(taskRow.message, event.message, 'Event Message');
assert.equal(
taskRow.time,
moment(event.time / 1000000).format("MMM DD, 'YY HH:mm:ss ZZ"),
Expand Down
72 changes: 71 additions & 1 deletion ui/tests/acceptance/job-status-panel-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';

import { click, visit, find, triggerEvent } from '@ember/test-helpers';
import {
click,
visit,
find,
findAll,
fillIn,
triggerEvent,
} from '@ember/test-helpers';

import { setupMirage } from 'ember-cli-mirage/test-support';
import faker from 'nomad-ui/mirage/faker';
Expand Down Expand Up @@ -400,4 +407,67 @@ module('Acceptance | job status panel', function (hooks) {
'Summary block has the correct number of grouped running allocs'
);
});

module('deployment history', function () {
test('Deployment history can be searched', async function (assert) {
faker.seed(1);

let groupTaskCount = 10;

let job = server.create('job', {
status: 'running',
datacenters: ['*'],
type: 'service',
resourceSpec: ['M: 256, C: 500'], // a single group
createAllocations: true,
allocStatusDistribution: {
running: 1,
failed: 0,
unknown: 0,
lost: 0,
},
groupTaskCount,
shallow: true,
activeDeployment: true,
version: 0,
});

let state = server.create('task-state');
state.events = server.schema.taskEvents.where({ taskStateId: state.id });

server.schema.allocations.where({ jobId: job.id }).update({
taskStateIds: [state.id],
jobVersion: 0,
});

await visit(`/jobs/${job.id}`);
assert.dom('.job-status-panel').exists();

const serverEvents = server.schema.taskEvents.where({
taskStateId: state.id,
});
const shownEvents = findAll('.timeline-object');
const jobAllocations = server.db.allocations.where({ jobId: job.id });
assert.equal(
shownEvents.length,
serverEvents.length * jobAllocations.length,
'All events are shown'
);

await fillIn(
'[data-test-history-search] input',
serverEvents.models[0].message
);
assert.equal(
findAll('.timeline-object').length,
jobAllocations.length,
'Only events matching the search are shown'
);

await fillIn('[data-test-history-search] input', 'foo bar baz');
assert
.dom('[data-test-history-search-no-match]')
.exists('No match message is shown');
});
});
});
2 changes: 1 addition & 1 deletion ui/tests/acceptance/task-detail-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ module('Acceptance | task detail', function (hooks) {
'Event timestamp'
);
assert.equal(recentEvent.type, event.type, 'Event type');
assert.equal(recentEvent.message, event.displayMessage, 'Event message');
assert.equal(recentEvent.message, event.message, 'Event message');
});

test('when the allocation is not found, the application errors', async function (assert) {
Expand Down
12 changes: 10 additions & 2 deletions ui/tests/integration/components/job-page/service-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,11 @@ module('Integration | Component | job-page/service', function (hooks) {
'The error message mentions ACLs'
);

await componentA11yAudit(this.element, assert);
await componentA11yAudit(
this.element,
assert,
'scrollable-region-focusable'
); //keyframe animation fades from opacity 0

await click('[data-test-job-error-close]');

Expand Down Expand Up @@ -335,7 +339,11 @@ module('Integration | Component | job-page/service', function (hooks) {
'The error message mentions ACLs'
);

await componentA11yAudit(this.element, assert);
await componentA11yAudit(
this.element,
assert,
'scrollable-region-focusable'
); //keyframe animation fades from opacity 0

await click('[data-test-job-error-close]');

Expand Down
7 changes: 5 additions & 2 deletions ui/tests/integration/components/job-status-panel-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,11 @@ module(
// deployment.get('statusDescription'),
// 'Status description is in the metrics block'
// );

await componentA11yAudit(this.element, assert);
await componentA11yAudit(
this.element,
assert,
'scrollable-region-focusable'
); //keyframe animation fades from opacity 0
});

test('when there is no running deployment, the latest deployment section shows up for the last deployment', async function (assert) {
Expand Down

0 comments on commit 9102247

Please sign in to comment.