Skip to content

Commit

Permalink
[O2B-845] display original log when replying (#1223)
Browse files Browse the repository at this point in the history
* add parent log text

* modify height

* revoke unfounded changes

* add logDetailsButton

* extend log component capabilities

* extend logCreationModel

* use common component to display parentLog in log replay page

* refactor, rename, docs

* docs

* preserve replay content if returning with the same parentLogId

* refactordocs

* amend comparison condition

* simplify condition

* change if

* add test

* revoke unfound changes

* improvement

* rename

* amend test

* docs

* docs

* refactor

* refactor

* refactor

* rename

* refactor

* rename

* docs

* replace abbreviation

* refactor

* put stronger condition in LogCreationModel#isReusable

* rename

* remove feature
  • Loading branch information
xsalonx committed Nov 10, 2023
1 parent e14cc43 commit 1206b2b
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 19 deletions.
15 changes: 10 additions & 5 deletions lib/public/components/Log/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { logLinkButton } from './logLinkButton.js';
import { logReplyButton } from './logReplyButton.js';
import { logText } from './logText.js';
import { primaryButton } from '../common/primaryButton.js';
import { logDetailsButton } from './logDetailsButton.js';

/**
* Provides formatting for each log field to display that field on the webpage
Expand Down Expand Up @@ -92,7 +93,10 @@ const expandedLogFields = ['runs', 'environments', 'lhcFills', 'attachments'];
* @param {function} onCollapse function called when the log should be collapsed
* @param {function} onExpand function called when the log should be expanded
* @param {function} onCopyUrlSuccess function called when the url of the log has been copied to the clipboard
* @param {string} authenticationToken token to authenticate the session with the api
* @param {Object} options other options
* @param {string} [options.authenticationToken] token to authenticate the session with the api
* @param {Object} [options.logMarkdownDisplayOpts] markdown display opts @see logText opts argument
* @param {Partial<MarkdownBoxSize>} [options.logMarkdownDisplayOpts.boxSize] size of markdown display
* @return {Component} the log's display component
*/
export const logComponent = (
Expand All @@ -104,10 +108,10 @@ export const logComponent = (
onCollapse,
onExpand,
onCopyUrlSuccess,
authenticationToken,
options = {},
) => {
const { title = '', id = '' } = log;
const fieldFormatting = logFields(authenticationToken);
const fieldFormatting = logFields(options?.authenticationToken);
const logTitle = showLogTitle ? h(`h4#log-${id}-title`, title) : '';
const displayedLogFields = isCollapsed ? [] : expandedLogFields;
const logViewButton = isCollapsed
Expand All @@ -128,11 +132,12 @@ export const logComponent = (
]),
h('div.flex-row.flex-shrink-0.flex-wrap.items-start.g1', [
logLinkButton(log, showCopyUrlSuccessContent, onCopyUrlSuccess),
logReplyButton(log),
(options.replayButton ?? true) && logReplyButton(log),
(options.goToDetailsButton ?? false) && logDetailsButton(log),
logViewButton,
]),
]),
h('.bt1.b-gray-light'),
logText(log, isCollapsed),
logText(log, isCollapsed, options?.logMarkdownDisplayOpts),
);
};
30 changes: 30 additions & 0 deletions lib/public/components/Log/logDetailsButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* @license
* Copyright CERN and copyright holders of ALICE O2. This software is
* distributed under the terms of the GNU General Public License v3 (GPL
* Version 3), copied verbatim in the file "COPYING".
*
* See http://alice-o2.web.cern.ch/license for full licensing information.
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

import { frontLink } from '../common/navigation/frontLink.js';

/**
* Returns a reply to Log button.
*
* @param {Log} log the log to which reply is created
* @return {Component} The reply button.
*/
export const logDetailsButton = (log) => frontLink(
'Details',
'log-detail',
{ id: log.id },
{
id: `details-of-${log.id}`,
class: 'btn btn-primary',
},
);
6 changes: 4 additions & 2 deletions lib/public/components/Log/logText.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ import { markdownDisplay } from '../common/markdown/markdown.js';
*
* @param {Log} log the log to display
* @param {Boolean} isCollapsed an indicator as to whether the log is collapsed
* @param {Object} options log text options
* @param {Partial<MarkdownBoxSize>} [options.boxSize] markdown size definition
* @return {Component} the log's text display
*/
export const logText = (log, isCollapsed) => h(
export const logText = (log, isCollapsed, options = {}) => h(
`div#log-id-${log.id}`,
isCollapsed
? h(`.text-ellipsis.w-100#log-text-${log.id}`, log.text)
: markdownDisplay(log.text, {
classes: 'w-100',
id: `log-text-${log.id}`,
}),
}, options.boxSize),
);
57 changes: 54 additions & 3 deletions lib/public/views/Logs/Create/LogCreationModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { tagToOption } from '../../../components/tag/tagToOption.js';
import { getRemoteData } from '../../../utilities/fetch/getRemoteData.js';
import { TagPickerModel } from '../../../components/tag/TagPickerModel.js';

const DATA_DISPLAY_DELIMITER = ',';

/**
* Model to cary log creation state
*/
Expand All @@ -41,9 +43,9 @@ export class LogCreationModel extends Observable {
this._creationTagsPickerModel.bubbleTo(this);
this._creationTagsPickerModel.visualChange$.bubbleTo(this);

this._runNumbers = runNumbers.join(', ');
this._lhcFills = lhcFillNumbers.join(', ');
this._environments = environmentIds.join(', ');
this._runNumbers = runNumbers.join(DATA_DISPLAY_DELIMITER);
this._lhcFills = lhcFillNumbers.join(DATA_DISPLAY_DELIMITER);
this._environments = environmentIds.join(DATA_DISPLAY_DELIMITER);
this._parentLogId = parentLogId;
if (parentLogId) {
this._parentLog = RemoteData.loading();
Expand All @@ -59,6 +61,8 @@ export class LogCreationModel extends Observable {
this._parentLog = RemoteData.success(null);
}

this._isParentLogCollapsed = true;

this._attachments = [];

this._createdLog = RemoteData.notAsked();
Expand All @@ -67,6 +71,53 @@ export class LogCreationModel extends Observable {
this._textEditor = null;
}

/**
* A callback given to the copy url component so that it can rerender after a timeout
* @returns {void}
*/
onCopyUrlSuccess() {
this._showCopyUrlSuccessContent = true;
this.notify();
setTimeout(() => {
this._showCopyUrlSuccessContent = false;
this.notify();
}, 2000);
}

/**
* Show parent log entry collapsed
* @returns {void}
*/
collapseParentLog() {
this._isParentLogCollapsed = true;
}

/**
* Show full parent log entry
* @returns {void}
*/
showFullParentLog() {
this._isParentLogCollapsed = false;
}

/**
* Returns whether parent log is collapsed
*
* @return {boolean} true if collapsed
*/
get isParentLogCollapsed() {
return this._isParentLogCollapsed;
}

/**
* Returns whether sucessfuly copied parent log link
*
* @return {boolean} true if link was copied successfuly
*/
get showCopyUrlSuccessContent() {
return this._showCopyUrlSuccessContent;
}

/**
* Create the log with the variables set in the model, handling errors appropriately
* Adds the title of the parent log to a log reply if the title is empty.
Expand Down
4 changes: 3 additions & 1 deletion lib/public/views/Logs/Create/LogCreationPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ export const LogCreationPage = (model) => {
});
}

return logCreationComponent(model.logs.creationModel);
return logCreationComponent(model.logs.creationModel, {
authenicationToken: model.session.token,
parentLogMarkdownDisplayOptions: { boxSize: { height: '10rem' } } });
};
43 changes: 37 additions & 6 deletions lib/public/views/Logs/Create/logCreationComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,25 @@ import errorAlert from '../../../components/common/errorAlert.js';
import { tagPicker } from '../../../components/tag/tagPicker.js';
import { markdownInput } from '../../../components/common/markdown/markdown.js';
import spinner from '../../../components/common/spinner.js';
import { logComponent } from '../../../components/Log/log.js';

/**
* A function to construct the create log screen
*
* @param {LogCreationModel} creationModel - Log creation model
* @param {Object} options component creation options
* @param {string} [options.authenticationToken] token to authenticate the session with the api
* @param {Object} [options.parentLogMarkdownDisplayOptions] markdown display options @see logText options argument
* @return {vnode} the log creation component
*/
export const logCreationComponent = (creationModel) => {
export const logCreationComponent = (creationModel, options) => {
const data = creationModel.createdLog;

return [
data.isFailure() && data.payload.map(errorAlert),
creationModel.parentLog.match({
NotAsked: () => errorAlert([{ title: 'Missing parent log' }]),
Loading: () => spinner(),
Success: (parentLog) => logCreationForm(creationModel, parentLog),
Success: (parentLog) => logCreationForm(creationModel, parentLog, options),
Failure: (errors) => errorAlert(errors),
}),
];
Expand All @@ -43,16 +46,44 @@ export const logCreationComponent = (creationModel) => {
*
* @param {LogCreationModel} creationModel the creation model
* @param {Log|null} parentLog - The parent of the log being created
* @param {Object} options component creation options
* @param {string} [options.authenticationToken] token to authenticate the session with the api
* @param {Object} [options.parentLogMarkdownDisplayOptions] markdown display options @see logText options argument
* @return {vnode} the form component
*/
const logCreationForm = (creationModel, parentLog) => [
const logCreationForm = (creationModel, parentLog, options) => [
h('.w-100.flex-row.mv2.justify-between', [
h('.f3.flex-row.g2', parentLog ? ['Reply to: ', h('strong', parentLog.title)] : 'New log'),
h('.f3.flex-row.g2', [
parentLog
? [
'Reply to: ',
h('strong', parentLog.title),
]
: 'New log',

]),
postLogPanel(creationModel),
]),
h('.w-100.flex-row.g3.flex-grow', [
h('.w-60.flex-column.g3', [
parentLog === null && titlePanel(creationModel),
parentLog === null
? titlePanel(creationModel)
: logComponent(
parentLog,
false,
false,
creationModel.isParentLogCollapsed,
creationModel.showCopyUrlSuccessContent,
() => creationModel.collapseParentLog(),
() => creationModel.showFullParentLog(),
() => creationModel.onCopyUrlSuccess(),
{ ...options,
logMarkdownDisplayOpts: options.parentLogMarkdownDisplayOptions,
goToDetailsButton: true,
replayButton: false,
},
),

contentPanel(creationModel),
]),
h('.w-40.flex-column.g3', [
Expand Down
4 changes: 2 additions & 2 deletions lib/public/views/Logs/Logs.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export default class LogModel extends Observable {
* @return {void}
*/
loadCreation(queryParams) {
const { parentLogId } = queryParams;
const parentLogId = parseInt(queryParams.parentLogId, 10);
let { runNumbers, lhcFillNumbers, environmentIds } = queryParams;

if (!Array.isArray(runNumbers)) {
Expand All @@ -107,7 +107,7 @@ export default class LogModel extends Observable {

this._creationModel = new LogCreationModel(
(logId) => this.model.router.go(`/?page=log-detail&id=${logId}`),
parseInt(parentLogId, 10),
parentLogId,
{ runNumbers, lhcFillNumbers, environmentIds },
);
this._creationModel.bubbleTo(this);
Expand Down
13 changes: 13 additions & 0 deletions test/public/logs/create.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ module.exports = () => {

const header = await page.$eval('.f3', (element) => element.textContent);
expect(header).to.equal('Reply to: First entry');

const showCollapseBtn = await page.$('#show-collapse-1');
expect(showCollapseBtn).to.exist;
await showCollapseBtn.evaluate((button) => button.click());

const parentLogText = await page.$eval('span[role="presentation"]:first-of-type', (span) => span.innerText);
expect(parentLogText).to.equal('Power interruption due to unplugged wire.');

const log1DetailsBtn = await page.$('#details-of-1');
expect(log1DetailsBtn).to.exist;

await log1DetailsBtn.evaluate((button) => button.click());
expect(new URL(page.url()).search).to.equal('?page=log-detail&id=1');
});

it('can disable submit with invalid data', async () => {
Expand Down

0 comments on commit 1206b2b

Please sign in to comment.