Maintainer: Danijel Beljan
Q Table is one tool of the Q toolbox to create tables. Test it in the demo.
Table of contents
Installation
git clone git@github.com:nzzdev/Q-table.git
cd ./Q-table
nvm use
npm install
npm run build
Configuration
No configuration is needed for this tool.
Development
Start the Q dev server:
npx @nzz/q-cli server
Run the Q tool:
node index.js
Testing
The testing framework used in this repository is Code.
Run the tests:
npm run test
Implementing a new test
When changing or implementing...
- A
route
, it needs to be tested in thee2e-tests.js
file - Something on the frontend, it needs to be tested in the
dom-tests.js
file
Deployment
We provide automatically built docker images. There are three options for deployment:
- Use the provided images
- Build your own docker images
- Deploy the service using another technology
Use the provided docker images
- Deploy
nzzonline/q-table
to a docker environment
Functionality
The tool structure follows the general structure of each Q tool. Further information can be found in Q server documentation - Developing tools.
Implementing a new feature
When implementing a new feature, a new file needs to be created under heplers
containing the name of the feature. The file needs to export a function which will process the data and return in every case an object
, when the function won't be used, the object
needs to be empty. The context
object will then contain the processed data.
Features
Spacing
- Q-Table uses CSS Table-Layout to adjust columns, rows and cells
- An algorythm discribes how large the width of each column and cell should be and to use the whole desktop width
- We want to give the responsability fully to the browser and don't want to calculate the layout by our own
- We want define rules based on the type of the column (Number, Text, Minibar) while numbers are right and text is left aligned
Rules for spacing
There's a CSS rule which contains that if the table doesn't use Card-Layout, the cell is not q-talble-minibar-header
and the following cell is not q-table__cell--numeric
then the padding-left
will be 20px
.
Further spacing rules will be explained in the other features.
Collapsable table This is a feature to shorten large tables in the article and make them collapsable.
Implementation details
- When rendering the rows, the renderingInfoScript
getShowMoreButtonScript()
will check how many rows the table contains - This is the matrix used to display/hide the rows
- If there are rows hidden, a new element will be created with the class
q-table_show-more-button
assigned - Clicking on the button will either call the function
showRowsFunction()
orhideRowsFunction()
- When calling the
hideRowsFunction
, the table will be collapsed again and with the functionscrollIntoView()
scrolled back to the top of the table
Footnotes
Footnotes are a feature to display annotations in the table and the sources in the footer of the table. It uses the metaData
feature of Q-Editor. Footnotes with the same text will be merged together.
Implementation details serverside
- The function
getFootnotes()
will filter and sort all footnotes fromitem.data.metaData
. Additional the data will be transitioned, that there will be listed only unique footnotes, with an array of coordinates, where the footnote will be placed. The function will always return an object, when not used the object will be empty. - Those footnotes will then be passed to the function
getTableData()
- Once the
tableData
is adjusted ingetTableData()
, there's a check if footnotes are set - If there are footnotes, they will be passed to the function
appendFootnoteAnnotationsToTableData()
along withtableData
andoptions
- In
appendFootnoteAnnotationsToTableData()
thefootnoteClass
will be calculated and determined if extra spacing is used or not - Before appending the footnotes to the
tableData
, the footnotes array will be flattened and prepared to ease to complexity. - This is the matrix how we apply spacing classes to the cell
- Because the title in the Card-Layout is already set by the
::before
pseudo element, it's not possible to apply the annotation with this selector as well. Therefore we have to add the unicode to the datasetdata-label
when the Card-Layout is set so we map the footnote annoation value to theunicode
. Important: The mapping of the value is from 1 to 9. - After applying the annotations to the
tableData
, the functionappendFootnoteAnnotationsToTableData()
should return an object like this:
[
[
{
type: "numeric",
value: "Rank",
classes: [],
footnote: {
value: 1,
unicode: "¹",
class: null,
},
},
],
[
{
type: "numeric",
value: "3",
classes: ["q-table-footnote-col-cardlayout-single"],
footnote: {
value: 2,
unicode: "²",
class: null,
},
},
],
];
Implementation details frontend
- The
value
of the cell will be displayed inside aspan
-element with the classq-table-footnote-annotation
- The
span
-element has the datasetdata-annotation
and the valuecell.footnote.value
applied to it - With the
::after
pseudo element, the datasetdata-annotation
will then be applied after the value - For the sources of the annotations the
footnotes
array applied to thecontext
will be looped and displayed in the footer - If the option
colorColumn
is selected, the footnote will be dispalyed in a seperate element and the color of the footnote will be set toopacity: 0.65
Options
hideTableHeader
This options allows to hide the header of each column. By default it's false
Implementation details
- If the options is used, the table-header won't be rendered
showTableSearch
This option allows to show or hide the table search feature. The option is only available, when there are more than 16 rows in the table. Default value is false
.
Implementation details
- By default the table is collapsed and the
q-table_show-more-button
is visible at the bottom of the table (see Collapsable table). - When the user starts typing, all rows will be made visible and the
q-table_show-more-button
disappears. - The actual search function triggers, as soon as the user types the second character.
- The filter searches through text-based columns only.
- When the user deletes his input, the table collapses and the
q-table_show-more-button
will be visible again.
Implementation details
- If the option is used, the input element for the table search won't be rendered.
cardLayout
Card-Layout is an option to display large tables well-arranged on mobile. There are 2 options to use it:
- Only show the Card-Layout in the mobile view
- Show the Card-Layout in every view
Implementation details
- The Card-Layout renderingInfoScript contains an
EventListener
on the eventresize
which calculates the current size of the graphic - When the graphic is smaller than
400px
, the renderingInfoScriptapplyCardLayoutClassFunction
adds the classq-table--card-layout
to thedataObject
and replaces all its styles - The column headers will then be hidden and each cell will display the
::before
:pseudo element containing its column header (e.g.Header1
)
minibar Minibars are a visual feature to display the difference between numbers in the table. Minibars are only useable on numeric columns. We decided not to support minibars in the Card-Layout because of [visual issues](https://3.basecamp.com/3500782/buckets/4515275/todos/1037397026).
Implementation details serverside
- The
option-availability
route will check if there are at least 3 columns and check if at least one if them isnumeric
- Once the option will be shown, the
dynamic-schema
route will read allnumeric
columns and display them in the option - Important: The function
getMinibarContext()
will always return an object, when minibars aren't used the object is empty - The function
getMinibarContext()
uses a copy ofitem.data
since the data will be altered - If minibars will be used, the function
getMinibarData()
with the parametersdata
andminibarOptions
will be called - The function then calls
getMinibarNumbersWithType()
will set the type of the minibar and prepare the data to be calculated - The table can have 3 types:
positive
when there positive numbers only,negative
when there are negative numbers only ormixed
when the numbers are positive and negative numbers in the column - The cell can have 3 types:
positive
, when the cell contains a positive number,negative
when the cell contains a negative number orempty
when there's no content in the cell - The function
getMinibarValue()
then calculates the length of the minibar based on thetype
of the table and max-value of the selected column - Then the color of the minibars will be adjusted. By default the sophie-colors
s-viz-color-diverging-2-2
forpositive
ands-viz-color-diverging-2-1
fornegative
colors will be used. If the user has the rolepoweruser
, he can set the colors by himself. One way by theclassName
of the sophie-colors or thecolorCode
which are simple hex codes of colors - In the end the
getMinibarContext()
function will return an object like this:
{
values: [
{type:"empty", value:0},
{type:"negative", value:7.142857142857143},
{type:"positive", value:14.285714285714286},
],
type:"mixed",
barColor: {
positive:{className:"s-viz-color-diverging-2-2", colorCode:""},
negative:{className:"s-viz-color-diverging-2-1", colorCode:""}
}
}
Implementation details frontend
- There are 3 different files for displaying minibars:
minibar-positive.html
,minibar-negative.html
andminibar-mixed.html
- Positive will display the
value
first and then theminibar
. Both as atd
-element - Negative will display the
minibar
first and then thevalue
. Both as atd
-element - Mixed will display the
value
and theminibar
inside thetd
as adiv
-element but in the same order as listed above - When resizing the graphic, the
EventLister
on the eventresize
implemented in thegetCardLayoutScript()
will be triggered - When the graphic is smaller than
400px
, the renderingInfoScriptrenderMinibarsFunction()
will be called inside theEventListener
listed above - The function
renderMinibarsFunction()
then loads the elements of the columnselectedColumn
andminibarColumn
which then will passed to the functionhandleMinibarsMinWidthFunctionName()
- The function
handleMinibarsMinWidthFunctionName()
then either removes or addsq-table-minibar-cell-mobile
orq-table-minibar--mixed-mobile
to the cell according to thetype
of the table which can be read out on the datasetdata-minibar
on each cell
colorColumn
This feature allows to select a column and colorize it. There are two types:
numerical
categorical
Numerical
When selecting the numerical option, the numbers inside the column will be split in buckets. Depending on the bucketType
, the buckets will be calculated differently.
This option will be displayed with columns containing numerical values only, otherwise an error will be displayed.
Label
The label allows to select between three options:
noLabel
(default)average
median
This value will then be displayed on the legend.
BucketType
There are four different buckets and each of it comes with it's own properties:
- Ckmeans,
numberBuckets
- Quantile,
numberBuckets
- Equal,
numberBuckets
- Custom, ,
numberBuckets
andcustomBuckets
Scale
The scale can be chosen from between two types:
sequential
diverging
Custom Color
When having the expert-role added, the user is able to adjust the colors of the buckets.
When selecting the diverging
-option, the options from which border it will be spilt are dynamically listed too.
Categorical
When selecting the categorical option, the values of the column will be mapped to categories. The categories will then be colored according to the color-scale.
Legend
The way the legend will be displayed is depending on the colorColumnType
. When using numerical-option, the range of the values will be calculated by buckets, which can be changed on the options. The lowest and highest value will be displayed on the left and right end of the legend.
Depending on the selected bucketing method, the legend will be displayed differently. The array passed to render the legend looks as following:
buckets = [
{
from, // lowest bucket border value
to, // highest bucket border value
color, // color depending on the selected color schema
},
]
If there's the case that one of the bucket has just a single value in it, the single bucket will be displayed below with a seperate icon. If there is an entry without a value, there will be an extra icon too, for displaying 'no data'.
The categorical legend will simply map the values to their colors.
Implementation details serverside
- Just like the feature minibars, the
option-availability
route will check if there are at least 3 columns to display this option\ - If the option is selected, through the
dynamic-schema
route, depending on whichcolorColumnType
is selected, will display specific options - Important: The function
getColorColumnContext()
will always return an object, when minibars aren't used the object is empty - The function
getColorColumnContext()
uses three parameters:colorColumn
(option),data
(data from table) andwidth
(content-width) - If the option is selected, the function
getColorColumnContext()
will always return this object
{
categoricalOptions, // object
colorColumnType, // string
colors, // array
legendData, // object
numericalOptions, // object
selectedColumn, // number
}
- Depending on the selected
colorColumnType
, eithernumerical
orcategorical
, some of the properties will be added - Important: The properties
categoricalOptions
andnumericalOptions
will always be an object, even if not selected - If selecting the
colorColumnType
numerical
, the following properties will be added
{
methodBox, // object
formattedValue, // array
}
Implementation details frontend
- Either
numerical
orcategorical
, both table-cells will be colored with the reference to thecolors
array - The legend will be displayed 100% on
desktop
andmobile
, onfullwidth
it will be displayed640px
(size of the text in article) - When resizing the graphic, the
EventLister
on the eventresize
implemented in therenderColorColumnNumericalLegend()
will be triggered - The legend will then be resized according to the
width
Display options
Display options can be set before embedding the graphic in the article.
hideTitle
Allows to hide the title
License
Copyright (c) Neue Zürcher Zeitung. All rights reserved.
This software is published under the MIT license.