Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data table prototyping #1128

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/public/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { registerFrontLinkListener } from './components/common/navigation/frontL
import { EosReportModel } from './views/EosReport/EosReportModel.js';
import { AboutModel } from './views/About/About.js';
import { StatisticsPageModel } from './views/Statistics/StatisticsPageModel.js';
import { CoffeeOverview } from './views/Coffee/Overview/index.js';
import { CoffeeModel } from './views/Coffee/Coffee.js';

/**
* Root of model tree
Expand Down Expand Up @@ -65,6 +67,8 @@ export default class Model extends Observable {

// Models

this.coffee = new CoffeeModel()

/**
* @type {LogModel}
*/
Expand Down Expand Up @@ -153,6 +157,8 @@ export default class Model extends Observable {
this.logs.fetchAllLogs();
this.runs.fetchAllRuns();
break;
case 'coffee':
break;
case 'env-overview':
this.envs.loadOverview();
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @template V
* @typedef {(V) => vnode} CellRenderer
*/

// TODO does this V reference the output of ColumnModel.getValue ?
// (defined in Coffee.js:6)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/**
* @typedef {Map<String, CellRenderer} CellRenderers
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @typedef {Object} TableColumn
* @property {String} header
* @property {TableModel} model
* @property {CellRenderer} renderer
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/**
* @typedef {Map<String, ColumnModel>} TableModel
*/
34 changes: 34 additions & 0 deletions lib/public/components/common/newTable/table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { h } from '/js/src/index.js';

/**
* @template T
* @param {T[]} data array of data to display
* @param {Map<String, TableColumn>} columns
* @param {(T) => String} getRowKey
* @returns {vnode}
*/
const tableRow = (row, columns, getRowKey) => {
const rowKey = getRowKey(row)

return h('tr', {key: rowKey}, Object.entries(columns).map(([columnKey, column]) => {
console.log(columnKey, column)
const cellData = column.model.getValue(row)
const cellKey = `${rowKey}-${columnKey}`
return h('td', {key: cellKey}, column.renderer(cellData))
}))
}

/**
* @template T
* @param {T[]} data array of data to display
* @param {Map<String, TableColumn>} columns
* @param {(T) => String} getRowKey
* @returns {vnode}
*/
export const table = (data, columns, getRowKey) => {
const tableRows = data.map(row => tableRow(row, columns, getRowKey))
return h('table', [
h('tr', Object.values(columns).map(({header}) => h('td', header))),
...tableRows
])
}
3 changes: 3 additions & 0 deletions lib/public/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { LogTreeViewPage } from './views/Logs/Details/LogTreeViewPage.js';
import { TagDetailsPage } from './views/Tags/Details/TagDetailsPage.js';
import { EosReportCreationPage } from './views/EosReport/create/EosReportCreationPage.js';
import { StatisticsPage } from './views/Statistics/StatisticsPage.js';
import { CoffeeOverview } from './views/Coffee/Overview/index.js';

/**
* Main view layout
Expand All @@ -45,6 +46,8 @@ export default (model) => {
const pages = {
home: HomeOverview,

'coffee': CoffeeOverview,

'log-overview': LogsOverview,
'log-detail': LogTreeViewPage,
'log-create': LogCreationPage,
Expand Down
98 changes: 98 additions & 0 deletions lib/public/views/Coffee/Coffee.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { Observable } from '/js/src/index.js';

/**
* @interface
*/
class ColumnModel {
/**
* @template T, V
* @param {T} row
* @returns {V} data to be displayed in the column
*/
getValue(row) {}
}

/**
* @implements {ColumnModel}
*/
export class PassThroughColumnmodel extends ColumnModel {
constructor(key) {
super()
this.key = key
}

getValue(coffeeData) {
return coffeeData[this.key]
}
}

/**
* @implements {ColumnModel}
*/
class CoffeeScoreColumnModel extends ColumnModel {
constructor() {
super()
}

getValue({ rating }) {
return {rating, good: (rating > 2.5)}
}
}

/**
* @type {TableModel}
*/
class CoffeeTableModel {
constructor() {
this._title = new PassThroughColumnmodel('title')
this._coffeeScore = new CoffeeScoreColumnModel()
this._comments = new PassThroughColumnmodel('comments')
}

/**
* @returns {ColumnModel}
*/
get title() {
return this._title
}

/**
* @returns {ColumnModel}
*/
get coffeeScore() {
return this._coffeeScore
}

/**
* @returns {ColumnModel}
*/
get comments() {
return this._comments
}
}

export class CoffeeModel extends Observable {
constructor(model) {
super(model)

this._coffees = [
{id: 1, title: 'Intenso', rating: 5, comments: 'Very intense!'},
{id: 2, title: 'Forte', rating: 4, comments: 'Well rounded and unoffensive'},
{id: 3, title: 'Decaffeinato', rating: 2, comments: 'It\'s ok'}
]

/** @type {TableModel} */
this._tableModel = new CoffeeTableModel()
}

/**
* @returns {Coffee[]}
*/
get coffees() {
return this._coffees
}

get tableModel() {
return this._tableModel
}
}
27 changes: 27 additions & 0 deletions lib/public/views/Coffee/Overview/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { table } from "../../../components/common/newTable/table.js";
import { h } from '/js/src/index.js';

/**
* @type {CellRenderers}
*/
const coffeeTableRenderers = {
title: (title) => title,
comments: (comments) => comments,
coffeeScore: (coffeeScore) => h('.flex-row.g1', [
coffeeScore.rating,
coffeeScore.good ? h('.badge.white.bg-success', 'Good') : h('.badge.white.bg-danger', 'Bad')
])
}

const getCoffeeTableRowKey = (row) => row.id

export const CoffeeOverview = ({coffee: coffeeModel}) => {
const coffees = coffeeModel.coffees
const tableModel = coffeeModel.tableModel

return table(coffees, {
title: {header: 'Coffee Name', model: tableModel.title, renderer: coffeeTableRenderers.title},
comments: {header: 'Comments', model: tableModel.comments, renderer: coffeeTableRenderers.comments},
coffeeScore: {header: 'Score', model: tableModel.coffeeScore, renderer: coffeeTableRenderers.coffeeScore},
}, getCoffeeTableRowKey);
}
7 changes: 7 additions & 0 deletions lib/public/views/Coffee/structures/coffee.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @typedef Coffee
* @property {number} id
* @property {string} title
* @property {number} rating
* @property {string} comments
*/
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import { PaginationModel } from '../../../components/Pagination/PaginationModel.js';
import { Observable, RemoteData } from '/js/src/index.js';
import { getRemoteDataSlice } from '../../../utilities/fetch/getRemoteDataSlice.js';
import { PassThroughColumnmodel } from '../../Coffee/Coffee.js';

/**
* Environment overview page model
Expand All @@ -30,6 +31,8 @@ export class EnvironmentOverviewModel extends Observable {
this._pagination.itemsPerPageSelector$.observe(() => this.notify());

this._environments = RemoteData.NotAsked();

this._environmentTableModel = new EnvironmentTableModel()
}

/**
Expand Down Expand Up @@ -91,4 +94,57 @@ export class EnvironmentOverviewModel extends Observable {
get environments() {
return this._environments;
}

get environmentTableModel() {
return this._environmentTableModel
}
}

class EnvironmentTableModel {
constructor() {
this._id = new PassThroughColumnmodel('id')
this._updatedAt = new PassThroughColumnmodel('updatedAt')
this._createdAt = new PassThroughColumnmodel('createdAt')
this._status = new EnvirontmentStatusColumnModel('status')
this._statusMessage = new PassThroughColumnmodel('statusMessage')
this._runs = new PassThroughColumnmodel('runs')
}

get id() {
return this._id
}

get updatedAt() {
return this._updatedAt
}

get createdAt() {
return this._createdAt
}

get status() {
return this._status
}

get statusMessage() {
return this._statusMessage
}

get runs() {
return this._runs
}
}

class EnvirontmentStatusColumnModel {
constructor() {}

_getTooltipMessage(status) {
if (status === 'ERROR') return 'Environment has been in ERROR state'
if (status === 'DANGER') return 'Environment has been in ERROR state'
return null
}

getValue({ status, historyItems }) {
return { status, historyItems }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ export const EnvironmentOverviewPage = ({ envs }) => h(
{
onremove: () => envs.clearOverview(),
},
environmentOverviewComponent(envs.overviewModel.pagination, envs.overviewModel.environments),
environmentOverviewComponent(envs.overviewModel.pagination, envs.overviewModel.environments, envs.overviewModel.environmentTableModel),
);
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,64 @@
*/

import { h } from '/js/src/index.js';
import { table } from '../../../components/common/table/table.js';
import { environmentsActiveColumns } from '../ActiveColumns/environmentsActiveColumns.js';
import { table } from '../../../components/common/newTable/table.js';
import { estimateDisplayableRowsCount } from '../../../utilities/estimateDisplayableRowsCount.js';
import { paginationComponent } from '../../../components/Pagination/paginationComponent.js';
import { displayEnvironmentStatus } from '../format/displayEnvironmentStatus.js';
import { formatRunsList } from '../../Runs/format/formatRunsList.js';
import { formatTimestamp } from '../../../utilities/formatting/formatTimestamp.js';

import spinner from '../../../components/common/spinner.js';

const TABLEROW_HEIGHT = 46;
// Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes;
const PAGE_USED_HEIGHT = 215;

/**
* @type {CellRenderers}
*/
const evironmentTableRenderers = {
id: (id) => id,
updatedAt: (timestamp) => formatTimestamp(timestamp),
createdAt: (timestamp) => formatTimestamp(timestamp),
status: ({ status, historyItems }) => displayEnvironmentStatus({ status, historyItems }),
statusMessage: (message) => message,
runs: formatRunsList
}

const getEnvironmentTableRowKey = (row) => row.id

/**
* The shows the environment table
* @param {PaginationModel} pagination the environment's pagination model
* @param {RemoteData} envs Environment objects.
* @returns {Object} Html page
*/
export const environmentOverviewComponent = (pagination, envs) => {
export const environmentOverviewComponent = (pagination, envs, tableModel) => {
pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(
TABLEROW_HEIGHT,
PAGE_USED_HEIGHT,
));

// TODO: This is temporary. Should table deal with remote data?
if (!envs.isSuccess()) {
return spinner({ size: 5, absolute: false })
}

envs = envs.payload

return [
h('.w-100.flex-column', [
h('.header-container.pv2'),
table(envs, environmentsActiveColumns, { classes: getRowsClasses }),
// table(envs, environmentsActiveColumns, { classes: getRowsClasses }),
table(envs, {
id: {header: 'id', model: tableModel.id, renderer: evironmentTableRenderers.id},
updatedAt: {header: 'Updated At', model: tableModel.updatedAt, renderer: evironmentTableRenderers.updatedAt},
createdAt: {header: 'Created At', model: tableModel.createdAt, renderer: evironmentTableRenderers.createdAt},
status: {header: 'Status', model: tableModel.status, renderer: evironmentTableRenderers.status},
statusMessage: {header: 'Status Message', model: tableModel.statusMessage, renderer: evironmentTableRenderers.statusMessage},
runs: {header: 'Runs', model: tableModel.runs, renderer: evironmentTableRenderers.runs},
}, getEnvironmentTableRowKey),
paginationComponent(pagination),
]),
];
Expand Down
Loading