@ministryofjustice/hmpps-digital-prison-reporting-frontend
Version:
The Digital Prison Reporting Frontend contains templates and code to help display data effectively in UI applications.
488 lines (476 loc) • 13 kB
text/typescript
import Dict = NodeJS.Dict
import { components } from '../../types/api'
import SectionedDataTableBuilder from './SectionedDataTableBuilder'
const data: Array<Dict<string>> = [
{
sectionOne: 'A',
sectionTwo: '01/02/03',
oranges: '5',
lemons: 'Yes',
},
{
sectionOne: 'A',
sectionTwo: '01/02/03',
oranges: '6',
lemons: 'No',
},
{
sectionOne: 'A',
sectionTwo: '02/01/03',
oranges: '7',
lemons: 'Yeah',
},
{
sectionOne: 'B',
sectionTwo: '02/01/03',
oranges: '8',
lemons: 'Nah',
},
]
const fields: Array<components['schemas']['FieldDefinition']> = [
{
name: 'sectionOne',
display: 'Section One',
sortable: true,
defaultsort: false,
type: 'string',
mandatory: false,
visible: false,
calculated: false,
},
{
name: 'sectionTwo',
display: 'Section Two',
sortable: true,
defaultsort: false,
type: 'string',
mandatory: false,
visible: false,
calculated: false,
},
{
name: 'oranges',
display: 'Oranges!',
sortable: true,
defaultsort: true,
type: 'long',
mandatory: false,
visible: true,
calculated: false,
},
{
name: 'lemons',
display: 'Lemons?!',
sortable: true,
defaultsort: false,
type: 'string',
mandatory: false,
visible: true,
calculated: false,
},
]
const specification: components['schemas']['Specification'] = {
fields,
template: 'list-section',
sections: ['sectionOne', 'sectionTwo'],
}
const headerResultRow = [
{
classes: 'govuk-table__header',
text: 'Oranges!',
},
{
classes: 'govuk-table__header',
text: 'Lemons?!',
},
]
const resultRows = [
[
{
classes: '',
fieldName: 'oranges',
format: 'numeric',
text: '5',
},
{
classes: '',
fieldName: 'lemons',
format: 'string',
text: 'Yes',
},
],
[
{
classes: '',
fieldName: 'oranges',
format: 'numeric',
text: '6',
},
{
classes: '',
fieldName: 'lemons',
format: 'string',
text: 'No',
},
],
[
{
classes: '',
fieldName: 'oranges',
format: 'numeric',
text: '7',
},
{
classes: '',
fieldName: 'lemons',
format: 'string',
text: 'Yeah',
},
],
[
{
classes: '',
fieldName: 'oranges',
format: 'numeric',
text: '8',
},
{
classes: '',
fieldName: 'lemons',
format: 'string',
text: 'Nah',
},
],
]
const sectionHeaders = [
[
[
{
classes: 'dpr-section-header-spacer',
colspan: 2,
text: '',
},
],
[
{
classes: 'dpr-section-header',
colspan: 2,
html: '<h2 class="govuk-heading-m">Section One: A, Section Two: 01/02/03 <span class=\'govuk-caption-m\'>2 results</span></h2>',
},
],
[
{
classes: 'dpr-section-header-spacer-bottom',
colspan: 2,
text: '',
},
],
],
[
[
{
classes: 'dpr-section-header',
colspan: 2,
html: '<h2 class="govuk-heading-m">Section One: A, Section Two: 02/01/03 <span class=\'govuk-caption-m\'>1 result</span></h2>',
},
],
[
{
classes: 'dpr-section-header-spacer-bottom',
colspan: 2,
text: '',
},
],
],
[
[
{
classes: 'dpr-section-header-spacer',
colspan: 2,
text: '',
},
],
[
{
classes: 'dpr-section-header',
colspan: 2,
html: '<h2 class="govuk-heading-m">Section One: B, Section Two: 02/01/03 <span class=\'govuk-caption-m\'>1 result</span></h2>',
},
],
[
{
classes: 'dpr-section-header-spacer-bottom',
colspan: 2,
text: '',
},
],
],
]
describe('SectionedDataTableBuilder', () => {
it('Sections added correctly', () => {
const mapped = new SectionedDataTableBuilder(specification)
.withNoHeaderOptions(['oranges', 'lemons'])
.buildTable(data)
expect(mapped.rows).toEqual([
...sectionHeaders[1],
headerResultRow,
resultRows[2],
...sectionHeaders[0],
headerResultRow,
resultRows[0],
resultRows[1],
...sectionHeaders[2],
headerResultRow,
resultRows[3],
])
expect(mapped.head).toBeNull()
expect(mapped.colCount).toEqual(2)
expect(mapped.rowCount).toEqual(4)
})
it('Section table summaries added correctly', () => {
const mapped = new SectionedDataTableBuilder(specification)
.withNoHeaderOptions(['oranges', 'lemons'])
.withSummaries({
'table-header': [
{
id: 'table-header',
template: 'table-header',
fields: [],
data: [
{
sectionOne: 'A',
sectionTwo: '01/02/03',
oranges: 'Fruit',
lemons: 'Lots',
},
{
sectionOne: 'A',
sectionTwo: '02/01/03',
oranges: 'Fruit',
lemons: 'Not so much',
},
],
},
],
'table-footer': [
{
id: 'table-footer',
template: 'table-footer',
fields: [],
data: [
{
sectionOne: 'A',
sectionTwo: '02/01/03',
oranges: 'Strawberry',
lemons: 'Lime',
},
{
sectionOne: 'B',
sectionTwo: '02/01/03',
oranges: 'Apples',
lemons: 'Oh no',
},
],
},
],
})
.buildTable(data)
expect(mapped.rows).toEqual([
...sectionHeaders[1],
headerResultRow,
[
{
classes: 'dpr-report-summary-cell dpr-report-summary-cell-table-header',
fieldName: 'oranges',
format: 'numeric',
text: 'Fruit',
},
{
classes: 'dpr-report-summary-cell dpr-report-summary-cell-table-header',
fieldName: 'lemons',
format: 'string',
text: 'Not so much',
},
],
resultRows[2],
[
{
classes: 'dpr-report-summary-cell dpr-report-summary-cell-table-footer',
fieldName: 'oranges',
format: 'numeric',
text: 'Strawberry',
},
{
classes: 'dpr-report-summary-cell dpr-report-summary-cell-table-footer',
fieldName: 'lemons',
format: 'string',
text: 'Lime',
},
],
...sectionHeaders[0],
headerResultRow,
[
{
classes: 'dpr-report-summary-cell dpr-report-summary-cell-table-header',
fieldName: 'oranges',
format: 'numeric',
text: 'Fruit',
},
{
classes: 'dpr-report-summary-cell dpr-report-summary-cell-table-header',
fieldName: 'lemons',
format: 'string',
text: 'Lots',
},
],
resultRows[0],
resultRows[1],
...sectionHeaders[2],
headerResultRow,
resultRows[3],
[
{
classes: 'dpr-report-summary-cell dpr-report-summary-cell-table-footer',
fieldName: 'oranges',
format: 'numeric',
text: 'Apples',
},
{
classes: 'dpr-report-summary-cell dpr-report-summary-cell-table-footer',
fieldName: 'lemons',
format: 'string',
text: 'Oh no',
},
],
])
expect(mapped.head).toBeNull()
expect(mapped.colCount).toEqual(2)
expect(mapped.rowCount).toEqual(4)
})
it('Section summaries added correctly', () => {
const mapped = new SectionedDataTableBuilder(specification)
.withNoHeaderOptions(['oranges', 'lemons'])
.withSummaries({
'section-header': [
{
id: 'section-header',
template: 'section-header',
fields: [
{
name: 'squishy',
display: 'Squishy?',
type: 'boolean',
},
],
data: [
{
sectionOne: 'A',
sectionTwo: '01/02/03',
squishy: 'Yes',
},
{
sectionOne: 'A',
sectionTwo: '02/01/03',
squishy: 'No',
},
],
},
],
'section-footer': [
{
id: 'section-footer',
template: 'section-footer',
fields: [
{
name: 'preference',
display: 'Preference',
type: 'string',
},
{
name: 'eaten',
display: 'Eaten',
type: 'long',
},
],
data: [
{
sectionOne: 'A',
sectionTwo: '02/01/03',
preference: 'Oranges',
eaten: '2005',
},
{
sectionOne: 'B',
sectionTwo: '02/01/03',
preference: 'NO',
eaten: '0',
},
{
sectionOne: 'B',
sectionTwo: '02/01/03',
preference: 'No, really',
eaten: '0',
},
],
},
],
})
.buildTable(data)
expect(mapped.rows).toEqual([
...sectionHeaders[1],
[
{
classes: 'dpr-summary-cell',
colspan: 2,
html: "<div class='dpr-summary-container-group dpr-summary-container-group-section-header'><div class='dpr-summary-container'><table class='govuk-table'><thead class='govuk-table__head'><th scope='col' class='govuk-table__header'>Squishy?</th></thead><tbody class='govuk-table__body'><tr class='govuk-table__row'><td class='govuk-table__cell govuk-table__cell--string '>No</td></tr></tbody></table></div></div>",
},
],
headerResultRow,
resultRows[2],
[
{
classes: 'dpr-summary-cell',
colspan: 2,
html: "<div class='dpr-summary-container-group dpr-summary-container-group-section-footer'><div class='dpr-summary-container'><table class='govuk-table'><thead class='govuk-table__head'><th scope='col' class='govuk-table__header'>Preference</th><th scope='col' class='govuk-table__header'>Eaten</th></thead><tbody class='govuk-table__body'><tr class='govuk-table__row'><td class='govuk-table__cell govuk-table__cell--string '>Oranges</td><td class='govuk-table__cell govuk-table__cell--numeric '>2005</td></tr></tbody></table></div></div>",
},
],
...sectionHeaders[0],
[
{
classes: 'dpr-summary-cell',
colspan: 2,
html: "<div class='dpr-summary-container-group dpr-summary-container-group-section-header'><div class='dpr-summary-container'><table class='govuk-table'><thead class='govuk-table__head'><th scope='col' class='govuk-table__header'>Squishy?</th></thead><tbody class='govuk-table__body'><tr class='govuk-table__row'><td class='govuk-table__cell govuk-table__cell--string '>Yes</td></tr></tbody></table></div></div>",
},
],
headerResultRow,
resultRows[0],
resultRows[1],
...sectionHeaders[2],
headerResultRow,
resultRows[3],
[
{
classes: 'dpr-summary-cell',
colspan: 2,
html: "<div class='dpr-summary-container-group dpr-summary-container-group-section-footer'><div class='dpr-summary-container'><table class='govuk-table'><thead class='govuk-table__head'><th scope='col' class='govuk-table__header'>Preference</th><th scope='col' class='govuk-table__header'>Eaten</th></thead><tbody class='govuk-table__body'><tr class='govuk-table__row'><td class='govuk-table__cell govuk-table__cell--string '>No, really</td><td class='govuk-table__cell govuk-table__cell--numeric '>0</td></tr><tr class='govuk-table__row'><td class='govuk-table__cell govuk-table__cell--string '>NO</td><td class='govuk-table__cell govuk-table__cell--numeric '>0</td></tr></tbody></table></div></div>",
},
],
])
expect(mapped.head).toBeNull()
expect(mapped.colCount).toEqual(2)
expect(mapped.rowCount).toEqual(4)
})
it('Large volumes of sections added correctly', () => {
let bigData: Dict<string>[] = []
for (let d = 0; d < 10000; d += 1) {
bigData = bigData.concat(data)
}
const mapped = new SectionedDataTableBuilder(specification)
.withNoHeaderOptions(['oranges', 'lemons'])
.buildTable(bigData)
expect(mapped.head).toBeNull()
expect(mapped.colCount).toEqual(2)
expect(mapped.rowCount).toEqual(40000)
expect(mapped.rows.length).toEqual(40011)
})
})