Skip to content

Commit ba04766

Browse files
authored
fix: overview table column mapping
2 parents e2e240f + 7a10dee commit ba04766

12 files changed

Lines changed: 323 additions & 91 deletions

File tree

lib/public/app.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.cell-xs { width: 2rem; }
2+
.cell-s { width: 4rem; }
3+
.cell-m { width: 8rem; }
4+
.cell-l { width: 16rem; }
5+
.cell-xl { width: 32rem; }
6+
7+
/* last column fills space */
8+
.cell-xs:last-child, .cell-s:last-child, .cell-m:last-child, .cell-l:last-child, .cell-xl:last-child { width: initial; }
9+
.cell-xs:last-child .resizeWidth, .cell-s:last-child .resizeWidth, .cell-m:last-child .resizeWidth, .cell-l:last-child .resizeWidth, .cell-xl:last-child .resizeWidth { visibility: hidden; }
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE O2. This software is
4+
* distributed under the terms of the GNU General Public License v3 (GPL
5+
* Version 3), copied verbatim in the file "COPYING".
6+
*
7+
* See http://alice-o2.web.cern.ch/license for full licensing information.
8+
*
9+
* In applying this license CERN does not waive the privileges and immunities
10+
* granted to it by virtue of its status as an Intergovernmental Organization
11+
* or submit itself to any jurisdiction.
12+
*/
13+
import { h } from '/js/src/index.js';
14+
15+
/**
16+
* Renders a single row with content
17+
* @param {Object} value The key value potentially containing a formatter function
18+
* @param {String} text The content text
19+
* @return {vnode} A single table cell containing (formatted) text
20+
*/
21+
const row = (value, text) => {
22+
const formatted = value && value.format ? value.format(text) : text;
23+
return h('td#', formatted);
24+
};
25+
26+
/**
27+
* Renders a list of rows with content
28+
* @param {Array} data The full collection of API data corresponding to the keys
29+
* @param {Object} keys The full collection of API keys and their corresponding header values
30+
* @param {Function} params Additional element parameters, wrapped in a function
31+
* @return {vnode} A filled array of rows based on the given data and keys
32+
*/
33+
const content = (data, keys, params) => {
34+
const idKey = Object.keys(keys).find((key) => keys[key] && keys[key].primary);
35+
return data.map((entry) =>
36+
h(`tr#row${entry[idKey]}`, params(entry), Object.entries(keys).map(([key, value]) => row(value, entry[key]))));
37+
};
38+
39+
export default content;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE O2. This software is
4+
* distributed under the terms of the GNU General Public License v3 (GPL
5+
* Version 3), copied verbatim in the file "COPYING".
6+
*
7+
* See http://alice-o2.web.cern.ch/license for full licensing information.
8+
*
9+
* In applying this license CERN does not waive the privileges and immunities
10+
* granted to it by virtue of its status as an Intergovernmental Organization
11+
* or submit itself to any jurisdiction.
12+
*/
13+
import { h } from '/js/src/index.js';
14+
15+
/**
16+
* Renders the header row
17+
* @param {Object} keys The full collection of API keys and their corresponding header values
18+
* @return {vnode} An array of rows containing all given header values with specific cell sizes
19+
*/
20+
const headers = (keys) =>
21+
h('tr#headers', Object.values(keys).map((value) => {
22+
const size = value.size || 'cell-m';
23+
return h(`th.${size}`, value.name);
24+
}));
25+
26+
export default headers;

lib/public/components/Table/index.js

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,35 @@
1111
* or submit itself to any jurisdiction.
1212
*/
1313
import { h } from '/js/src/index.js';
14+
import headers from './headers.js';
15+
import content from './content.js';
1416

1517
/**
16-
* Table row header
17-
* @param {String} header The text of the header elements
18-
* @return {vnode} Return a single row header element
18+
* Selectively removes the API keys based on their visibility value
19+
* @param {Object} keys The full collection of API keys and their corresponding header values
20+
* @returns {Object} A filtered collection of keys based on visibility
1921
*/
20-
const rowHeader = (header) => [h('th', { scope: 'col' }, header)];
21-
22-
/**
23-
* Table data row
24-
* @param {Object} data The data to be rendered in the child element of the row
25-
* @return {vnode} Return a row of data in the table
26-
*/
27-
const rowData = (data) => [h('td', data)];
22+
const filterKeysByVisibility = (keys) => {
23+
Object.entries(keys).forEach(([key, value]) => {
24+
if (!value.visible) {
25+
delete keys[key];
26+
}
27+
});
28+
};
2829

2930
/**
3031
* Renders the table
31-
* @param {Array} data The data array containing the objects with the data per row
32-
* @param {Array} headers The array of of the headers to be rendered in the table
33-
* @param {Object} model Model passed for use with routing to the correct detail view
32+
* @param {Array} data An object array, with every object representing a to be rendered row
33+
* @param {Object} keys The full collection of API keys and their corresponding header values
34+
* @param {Function} params Additional element parameters, wrapped in a function
3435
* @returns {vnode} Return the total view of the table to rendered
3536
*/
36-
const table = (data, headers, model) => h('table.table.shadow-level1.mh3', { style: { 'margin-bottom': 0 } }, [
37-
h('thead', [h('tr', [headers.map((header) => rowHeader(header))])]),
38-
h('tbody', [
39-
data.map((entry, index) => h(`tr#row${index + 1}`, {
40-
style: 'cursor: pointer;',
41-
onclick: () => model.router.go(`?page=entry&id=${entry[0]}`),
42-
}, [Object.keys(entry).map((subItem) => rowData(entry[subItem]))])),
43-
]),
44-
45-
]);
37+
const table = (data, keys, params = () => null) => {
38+
filterKeysByVisibility(keys);
39+
return h('table.table.shadow-level1.mh3', [
40+
headers(keys),
41+
content(data, keys, params),
42+
]);
43+
};
4644

4745
export { table };
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE O2. This software is
4+
* distributed under the terms of the GNU General Public License v3 (GPL
5+
* Version 3), copied verbatim in the file "COPYING".
6+
*
7+
* See http://alice-o2.web.cern.ch/license for full licensing information.
8+
*
9+
* In applying this license CERN does not waive the privileges and immunities
10+
* granted to it by virtue of its status as an Intergovernmental Organization
11+
* or submit itself to any jurisdiction.
12+
*/
13+
14+
const ACTIVE_COLUMNS = {
15+
title: {
16+
name: 'Title',
17+
visible: true,
18+
size: 'cell-l',
19+
},
20+
entryId: {
21+
name: 'Entry ID',
22+
visible: true,
23+
size: 'cell-l',
24+
primary: true,
25+
},
26+
authorID: {
27+
name: 'Author',
28+
visible: true,
29+
size: 'cell-l',
30+
},
31+
creationTime: {
32+
name: 'Creation Time',
33+
visible: true,
34+
size: 'cell-l',
35+
format: (date) => new Date(date).toLocaleString(),
36+
},
37+
origin: {
38+
name: 'Origin',
39+
visible: true,
40+
size: 'cell-m',
41+
},
42+
subtype: {
43+
name: 'Subtype',
44+
visible: true,
45+
size: 'cell-m',
46+
},
47+
};
48+
49+
export default ACTIVE_COLUMNS;

lib/public/views/Logs/Details/page.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@ import PostBox from '../../../components/Post/index.js';
2020
*/
2121
const logDetailScreen = (model) => {
2222
const data = model.logs.getData();
23+
const error = model.logs.didErrorOccur();
2324

2425
if (data && data.length !== 0) {
2526
const id = parseInt(model.router.params.id);
2627
const log = data.find((entry) => entry && entry.entryId === id);
27-
return h('.w-100.flex-column', [log.content.map((post, index) => h('.w-100', PostBox(post, index + 1)))]);
28-
} else {
28+
return h('', [
29+
h('.f3', log.title),
30+
h('.w-100.flex-column', [log.content.map((post, index) => h('.w-100', PostBox(post, index + 1)))]),
31+
]);
32+
} else if (error) {
2933
return h('', [
3034
h('.danger', 'This log could not be found.'),
3135
h('button.btn.btn-primary.mv3', {

lib/public/views/Logs/Logs.js

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,8 @@ export default class Overview extends Observable {
2626
this.model = model;
2727
this.filterCriteria = [];
2828
this.data = [];
29-
this.filtered = [];
29+
this.filteredData = [];
3030
this.error = false;
31-
this.headers = ['ID', 'Author ID', 'Title', 'Creation Time'];
3231
}
3332

3433
/**
@@ -41,7 +40,7 @@ export default class Overview extends Observable {
4140

4241
if (result.data) {
4342
this.data = result.data;
44-
this.filtered = [...result.data];
43+
this.filteredData = result.data;
4544
this.error = false;
4645
} else {
4746
this.error = true;
@@ -63,6 +62,7 @@ export default class Overview extends Observable {
6362

6463
if (result.data) {
6564
this.data = [result.data];
65+
this.filteredData = [result.data];
6666
this.error = false;
6767
} else {
6868
this.error = true;
@@ -72,42 +72,20 @@ export default class Overview extends Observable {
7272
}
7373
}
7474

75-
/**
76-
* Get the headers from the Overview class
77-
* @returns {Array} Returns the headers
78-
*/
79-
getHeaders() {
80-
return this.headers;
81-
}
82-
8375
/**
8476
* Getter for all the data
8577
* @returns {Array} Returns all of the data
8678
*/
8779
getData() {
88-
return this.error ? null : this.data;
80+
return this.filteredData;
8981
}
9082

9183
/**
92-
* Get the table data
93-
* @returns {Array} The data without the tags to be rendered in a table
84+
* Indicates if there was an error during log fetching
85+
* @returns {Boolean} Returns if an error occured during log fetching
9486
*/
95-
getDataWithoutTags() {
96-
const subentries = this.filtered.map((entry) => {
97-
const columnData = Object.keys(entry).map((subkey) => {
98-
// Filter out the field not needed for the table
99-
if (subkey !== 'tags' && subkey !== 'content' && subkey !== 'origin' && subkey !== 'subtype') {
100-
if (subkey === 'creationTime') {
101-
return new Date(entry[subkey]).toLocaleString();
102-
}
103-
104-
return entry[subkey];
105-
}
106-
});
107-
return columnData.filter((item) => item !== undefined);
108-
});
109-
110-
return subentries;
87+
didErrorOccur() {
88+
return this.error;
11189
}
11290

11391
/**
@@ -117,7 +95,7 @@ export default class Overview extends Observable {
11795
*/
11896
addFilter(tag) {
11997
this.filterCriteria = [...this.filterCriteria, tag];
120-
this.getFilteredData();
98+
this.setFilteredData();
12199
}
122100

123101
/**
@@ -127,17 +105,17 @@ export default class Overview extends Observable {
127105
*/
128106
removeFilter(condition) {
129107
this.filterCriteria = this.filterCriteria.filter((tag) => tag !== condition);
130-
this.getFilteredData();
108+
this.setFilteredData();
131109
}
132110

133111
/**
134112
* Filter the data
135113
* @returns {Array} Sets the data according to the amount of applied filters
136114
*/
137-
getFilteredData() {
115+
setFilteredData() {
138116
this.filterCriteria.length !== 0
139117
? this.filterByTags()
140-
: this.filtered = [...this.data];
118+
: this.filteredData = this.data;
141119

142120
this.notify();
143121
}
@@ -147,7 +125,7 @@ export default class Overview extends Observable {
147125
* @returns {Array} Sets the filtered data on the criteria applied by the user
148126
*/
149127
filterByTags() {
150-
this.filtered = this.data.filter((entry) => this.checkExistingTag(entry));
128+
this.filteredData = this.data.filter((entry) => this.checkExistingTag(entry));
151129
}
152130

153131
/**

lib/public/views/Logs/Overview/page.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,23 @@
1313
import { h } from '/js/src/index.js';
1414
import filters from '../../../components/Filters/index.js';
1515
import { table } from '../../../components/Table/index.js';
16+
import ACTIVE_COLUMNS from '../ActiveColumns/index.js';
1617

1718
/**
1819
* Table row header
1920
* @param {object} model Pass the model to access the defined functions
2021
* @return {vnode} Return the view of the table with the filtering options
2122
*/
2223
const logOverviewScreen = (model) => {
23-
const headers = model.logs.getHeaders();
24-
const data = model.logs.getDataWithoutTags();
24+
const data = model.logs.getData();
2525
const tags = model.logs.getTagCounts();
2626

2727
return h('.w-100.flex-row', [
2828
filters(model, tags),
29-
h('.w-75', [table(data, headers, model)]),
29+
h('.w-75', table(data, ACTIVE_COLUMNS, (entry) => ({
30+
style: 'cursor: pointer;',
31+
onclick: () => model.router.go(`?page=entry&id=${entry.entryId}`),
32+
}))),
3033
]);
3134
};
3235

test/public/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* or submit itself to any jurisdiction.
1212
*/
1313

14-
const LogsSuite = require('./logs.test');
14+
const LogsSuite = require('./logs');
1515

1616
module.exports = () => {
1717
describe('Logs', LogsSuite);

0 commit comments

Comments
 (0)