Skip to content

Commit

Permalink
Merge pull request #1623 from briehl/log-viewer
Browse files Browse the repository at this point in the history
remove jquery from job log viewer, other minor fixes
  • Loading branch information
briehl committed Apr 8, 2020
2 parents eb563fa + 0506272 commit eb7f02e
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 64 deletions.
10 changes: 5 additions & 5 deletions kbase-extension/static/kbase/css/kbaseJobLog.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@
.kblog-header {
display: flex;
font-family: monospace;
font-size: 85%;
font-size: 85%;
}

.kblog-line {
display: flex;
font-family: monospace;
font-size: 85%;
}

.kblog-line:hover {
Expand All @@ -31,11 +29,13 @@

.kblog-num-wrapper {
font-size: 85%;
display: flex;
flex-direction: row;
}

.kblog-line-num {
flex-shrink: 0;
width: 9ex;
width: 3rem;
text-align: right;
color: #999;
white-space: nowrap;
Expand Down Expand Up @@ -72,4 +72,4 @@
#kblog-err {
margin-top: 5px;
/* color: #660000; */
}
}
142 changes: 83 additions & 59 deletions kbase-extension/static/kbase/js/util/jobLogViewer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
/*global define*/
/*jslint white:true,browser:true*/
/**
* Usage:
* let viewer = JobLogViewer.make();
* viewer.start({
* jobId: <some job id>,
* node: <a DOM node>
* })
*/
define([
'bluebird',
'common/runtime',
Expand All @@ -8,7 +16,6 @@ define([
'common/events',
'common/fsm',
'kb_common/html',
'jquery',
'css!kbase/css/kbaseJobLog.css'
], function(
Promise,
Expand All @@ -17,12 +24,11 @@ define([
UI,
Events,
Fsm,
html,
$
html
) {
'use strict';

var t = html.tag,
let t = html.tag,
div = t('div'),
button = t('button'),
span = t('span'),
Expand Down Expand Up @@ -332,12 +338,17 @@ define([
}
];

function factory(config) {
var config = config || {},
runtime = Runtime.make(),
/**
* The entrypoint to this widget. This creates the job log viewer and initializes it.
* Starting it is left as a lifecycle method for the calling object.
*
*/
function factory() {
let runtime = Runtime.make(),
bus = runtime.bus().makeChannelBus({ description: 'Log Viewer Bus' }),
container,
jobId,
panelId,
model,
ui,
startingLine = 0,
Expand Down Expand Up @@ -414,7 +425,7 @@ define([
}

function requestLatestJobLog() {
// only while job is running
// only while job is running
// load numLines at a time
// otherwise load entire log
let autoState = fsm.getCurrentState().state.auto;
Expand Down Expand Up @@ -455,31 +466,27 @@ define([
})
}

function test(){
if(panelHeight === smallPanelHeight){
panelHeight = largePanelHeight;
}else{
panelHeight = smallPanelHeight;
}
$(ui.getElements('panel')[0]).animate({height: panelHeight}, 500);
function toggleViewerSize() {
panelHeight = panelHeight === smallPanelHeight ? largePanelHeight : smallPanelHeight;
getPanelNode().style.height = panelHeight;
}

// VIEW
/**
* builds contents of panel-heading div
* @param {??} events
* @param {??} events
*/
function renderControls(events) {
function renderControls(events) {
return div({ dataElement: 'header', style: { margin: '0 0 10px 0' } }, [
button({
class: 'btn btn-sm btn-default',
dataButton: 'expand',
dataToggle: 'tooltip',
dataPlacement: 'top',
title: 'Start fetching logs',
title: 'Toggle log viewer size',
id: events.addEvent({
type: 'click',
handler: test
handler: toggleViewerSize
})
}, [
span({ class: 'fa fa-expand' })
Expand Down Expand Up @@ -545,10 +552,10 @@ define([

/**
* builds contents of panel-body class
* @param {number} jobId
* @param {string} panelId
*/
function renderLayout(jobId) {
var events = Events.make(),
function renderLayout(panelId) {
const events = Events.make(),
content = div({ dataElement: 'kb-log', style: { marginTop: '10px'}}, [
div({ class: 'kblog-header' }, [
div({ class: 'kblog-num-wrapper' }, [
Expand All @@ -558,10 +565,13 @@ define([
renderControls(events) // header
])
]),
div({ dataElement: 'panel', class: jobId,
div({ dataElement: 'panel', id: panelId,
style: {
'overflow-y': 'scroll', height: panelHeight
} })
'overflow-y': 'scroll',
height: panelHeight,
transition: 'height 0.5s'
}
})
]);

return {
Expand Down Expand Up @@ -597,47 +607,49 @@ define([
* <span class="kblog-text">foobarbaz</span>
* </div>
* </div>
* @param {object} line
* @param {object} line
*/
function buildLine(line) {
// kblog-line wrapper div
const errorClass = line.isError ? ' kb-error' : '';
const kblogLine = document.createElement('div')
kblogLine.setAttribute('class', 'kblog-line' + errorClass);
// kblog-num-wrapper div
const warpperDiv = document.createElement('div');
warpperDiv.setAttribute('class', 'kblog-num-wrapper');
// number
const numSpan = document.createElement('span');
numSpan.setAttribute('class', 'kblog-line-num');
const wrapperDiv = document.createElement('div');
wrapperDiv.setAttribute('class', 'kblog-num-wrapper');
// number
const numDiv = document.createElement('div');
numDiv.setAttribute('class', 'kblog-line-num');
const lineNumber = document.createTextNode(line.lineNumber);
numSpan.appendChild(lineNumber);
// text
const textSpan = document.createElement('span');
textSpan.setAttribute('class', 'kblog-text');
numDiv.appendChild(lineNumber);
// text
const textDiv = document.createElement('div');
textDiv.setAttribute('class', 'kblog-text');
const lineText = document.createTextNode(line.text)
textSpan.appendChild(lineText);
// append line number and text
warpperDiv.appendChild(numSpan);
warpperDiv.appendChild(textSpan);
textDiv.appendChild(lineText);
// append line number and text
wrapperDiv.appendChild(numDiv);
wrapperDiv.appendChild(textDiv);
// append wrapper to line div
kblogLine.appendChild(warpperDiv)
kblogLine.appendChild(wrapperDiv);

return kblogLine;
}

/**
* Append div that displays job log lines
* to the panel
* @param {array} lines
* @param {array} lines
*/
function makeLogChunkDiv(lines) {
for (let i=0; i<lines.length; i+= 1){
panel.appendChild(buildLine(lines[i]))
}
}

// onUpdate callback function (under model)
/**
* onUpdate callback function (under model)
*/
function render() {
const lines = model.getItem('lines');

Expand All @@ -659,18 +671,13 @@ define([
};
});

var autoState = fsm.getCurrentState().state.auto;
if (!autoState){
// not sure when this happens
makeLogChunkDiv(viewLines)
} else {
makeLogChunkDiv(viewLines)
const lastChildElement = panel.lastElementChild
lastChildElement.scrollIntoView({
makeLogChunkDiv(viewLines);
if (fsm.getCurrentState().state.auto) {
panel.lastElementChild.scrollIntoView({
alignToTop: false,
behavior: 'smooth',
block: 'center'
})
});
}
} else {
ui.setContent('panel', 'Sorry, no log yet...');
Expand Down Expand Up @@ -953,7 +960,6 @@ define([
}
}

// I'm not sure where this shows up
function doOnQueued(message) {
const noLogYet = {
lineNumber: undefined,
Expand Down Expand Up @@ -1027,19 +1033,37 @@ define([
}
}

function getPanelNode() {
return document.getElementById(panelId);
}

/**
* The main lifecycle event, called when its container node exists, and we want to start
* running this widget.
* This detaches itself first, if it exists, then recreates itself in its host node.
* @param {object} arg - should have attributes:
* - node - a DOM node where it will be hosted.
* - jobId - string, a job id for this log
*/
function start(arg) {
detach(); // if we're alive, remove ourselves before restarting
var hostNode = arg.node;
if (!hostNode) {
throw new Error('Requires a node to start');
}
jobId = arg.jobId;
if (!jobId) {
throw new Error('Requires a job id to start');
}

container = hostNode.appendChild(document.createElement('div'));
ui = UI.make({ node: container });

jobId = arg.jobId;
// passing jobId allows the panel to be found by Job ID
// with getElementsByClassName
var layout = renderLayout(jobId);
panelId = html.genId();
var layout = renderLayout(panelId);
container.innerHTML = layout.content;
layout.events.attachEvents(container);
panel = document.getElementsByClassName(jobId)[0]
panel = getPanelNode();

initializeFSM();
renderFSM();
Expand Down Expand Up @@ -1070,7 +1094,7 @@ define([
function detach() {
stop();
if (container) {
container.innerHTML = '';
container.remove();
}
}

Expand Down Expand Up @@ -1101,7 +1125,7 @@ define([

return {
make: function(config) {
return factory(config);
return factory();
}
};
});
64 changes: 64 additions & 0 deletions test/unit/spec/Util/jobLogViewerSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*global define, describe, it, expect, jasmine, beforeEach, afterEach*/
/*jslint white: true*/
define([
'util/jobLogViewer'
], (
JobLogViewer
) => {
describe('Test the job log viewer module', () => {
let hostNode = null;
beforeEach(() => {
hostNode = document.createElement('div');
document.body.appendChild(hostNode);
});

afterEach(() => {
hostNode.remove();
});

it('Should load the module code successfully', () => {
expect(JobLogViewer).toBeDefined();
});

it('Should have the factory method', () => {
expect(JobLogViewer.make).toBeDefined();
expect(JobLogViewer.make).toEqual(jasmine.any(Function));
});

it('Should be created', () => {
let viewer = JobLogViewer.make();
expect(viewer.start).toEqual(jasmine.any(Function));
expect(viewer.stop).toEqual(jasmine.any(Function));
expect(viewer.detach).toEqual(jasmine.any(Function));
});

it('Should fail to start without a node', () => {
let viewer = JobLogViewer.make();
const jobId = 'fakejob';
let arg = {
jobId: jobId
};
expect(() => {viewer.start(arg)}).toThrow(new Error('Requires a node to start'));
});

it('Should fail to start without a jobId', () => {
let viewer = JobLogViewer.make();
let arg = {
node: hostNode
};
expect(() => {viewer.start(arg)}).toThrow(new Error('Requires a job id to start'));
});

it('Should start as expected with inputs, and be stoppable and detachable', () => {
let viewer = JobLogViewer.make();
let arg = {
node: hostNode,
jobId: 'someFakeJob'
};
viewer.start(arg);
expect(hostNode.querySelector('div[data-element="kb-log"]')).toBeDefined();
viewer.detach();
expect(hostNode.innerHTML).toBe('');
});
});
})

0 comments on commit eb7f02e

Please sign in to comment.