`,
`
`,
@@ -391,7 +392,8 @@ export class Editor {
cell = {data: value}
}
// Set the cell content
- this.dt.data.data[this.data.rowIndex][this.data.columnIndex] = cell
+ const row = this.dt.data.data[this.data.rowIndex]
+ row.cells[this.data.columnIndex] = cell
this.closeModal()
const rowIndex = this.data.rowIndex
const columnIndex = this.data.columnIndex
@@ -412,7 +414,7 @@ export class Editor {
const rowIndex = parseInt(tr.dataset.index, 10)
const row = this.dt.data.data[rowIndex]
this.data = {
- row,
+ row: row.cells,
rowIndex
}
this.editing = true
@@ -466,7 +468,7 @@ export class Editor {
html: [
`
`,
``,
- ``,
+ ``,
"
"
].join("")
}), form.lastElementChild)
@@ -503,10 +505,12 @@ export class Editor {
*/
saveRow(data: string[], row: cellType[]) {
// Store the old data for the emitter
- const oldData = row.map((cell: cellType) => cell.text ?? String(cell.data))
+ const oldData = row.map((cell: cellType) => cellToText(cell))
+ const updatedRow = this.dt.data.data[this.data.rowIndex]
+
if (data) {
let valueCounter = 0
- this.dt.data.data[this.data.rowIndex] = row.map((oldItem, colIndex) => {
+ updatedRow.cells = row.map((oldItem, colIndex) => {
if (this.options.excludeColumns.includes(colIndex) || this.dt.columns.settings[colIndex].hidden) {
return oldItem
}
@@ -548,8 +552,7 @@ export class Editor {
})
}
- const updatedRow = this.dt.data.data[this.data.rowIndex]
- const newData = updatedRow.map(cell => cell.text ?? String(cell.data))
+ const newData = updatedRow.cells.map(cell => cellToText(cell))
this.data = {}
this.dt.update(true)
diff --git a/src/export/csv.ts b/src/export/csv.ts
index 448e0313..7a92f502 100644
--- a/src/export/csv.ts
+++ b/src/export/csv.ts
@@ -1,9 +1,12 @@
import {
+ cellToText,
isObject
} from "../helpers"
import {DataTable} from "../datatable"
import {
+ cellDataType,
cellType,
+ dataRowType,
headerCellType
} from "../types"
@@ -41,32 +44,39 @@ export const exportCSV = function(dt: DataTable, userOptions: csvUserOptions = {
...userOptions
}
const columnShown = (index: number) => !options.skipColumn.includes(index) && !dt.columns.settings[index]?.hidden
- let rows : (string | number | boolean | object | undefined | null)[][] = []
const headers = dt.data.headings.filter((_heading: headerCellType, index: number) => columnShown(index)).map((header: headerCellType) => header.text ?? header.data)
- // Include headings
- rows[0] = headers
// Selection or whole table
+ let selectedRows: dataRowType[]
if (options.selection) {
// Page number
if (Array.isArray(options.selection)) {
// Array of page numbers
+ selectedRows = []
for (let i = 0; i < options.selection.length; i++) {
- rows = rows.concat(dt.pages[options.selection[i] - 1].map((row: {row: cellType[], index: number}) => row.row.filter((_cell: cellType, index: number) => columnShown(index)).map((cell: cellType) => cell.text ?? cell.data)))
+ selectedRows = selectedRows.concat(dt.pages[options.selection[i] - 1].map(row => row.row))
}
} else {
- rows = rows.concat(dt.pages[options.selection - 1].map((row: {row: cellType[], index: number}) => row.row.filter((_cell: cellType, index: number) => columnShown(index)).map((cell: cellType) => cell.text ?? cell.data)))
+ selectedRows = dt.pages[options.selection - 1].map(row => row.row)
}
} else {
- rows = rows.concat(dt.data.data.map((row: cellType[]) => row.filter((_cell: cellType, index: number) => columnShown(index)).map((cell: cellType) => cell.text ?? cell.data)))
+ selectedRows = dt.data.data
}
+ let rows : cellDataType[][] = []
+ // Include headings
+ rows[0] = headers
+ rows = rows.concat(selectedRows.map((row: dataRowType) => {
+ const shownCells = row.cells.filter((_cell: cellType, index: number) => columnShown(index))
+ return shownCells.map((cell: cellType) => cellToText(cell))
+ }))
+
// Only proceed if we have data
if (rows.length) {
let str = ""
rows.forEach(row => {
- row.forEach((cell: (string | number | boolean | object | undefined | null)) => {
+ row.forEach((cell: cellDataType) => {
if (typeof cell === "string") {
cell = cell.trim()
cell = cell.replace(/\s{2,}/g, " ")
diff --git a/src/export/json.ts b/src/export/json.ts
index b9746bd6..55bb1ae5 100644
--- a/src/export/json.ts
+++ b/src/export/json.ts
@@ -1,11 +1,13 @@
import {
+ cellToText,
isObject
} from "../helpers"
import {DataTable} from "../datatable"
import {
+ cellDataType,
cellType,
- headerCellType,
- elementNodeType
+ dataRowType,
+ headerCellType
} from "../types"
/**
* Export table to JSON
@@ -44,30 +46,36 @@ export const exportJSON = function(dt: DataTable, userOptions: jsonUserOptions =
const columnShown = (index: number) => !options.skipColumn.includes(index) && !dt.columns.settings[index]?.hidden
- let rows : (string | number | boolean | object | undefined | null)[][] = []
// Selection or whole table
+ let selectedRows: dataRowType[]
if (options.selection) {
// Page number
if (Array.isArray(options.selection)) {
// Array of page numbers
+ selectedRows = []
for (let i = 0; i < options.selection.length; i++) {
- rows = rows.concat(dt.pages[options.selection[i] - 1].map((row: {row: cellType[], index: number}) => row.row.filter((_cell: cellType, index: number) => columnShown(index)).map((cell: cellType) => cell.data)))
+ selectedRows = selectedRows.concat(dt.pages[options.selection[i] - 1].map(row => row.row))
}
} else {
- rows = rows.concat(dt.pages[options.selection - 1].map((row: {row: cellType[], index: number}) => row.row.filter((_cell: cellType, index: number) => columnShown(index)).map((cell: cellType) => cell.data)))
+ selectedRows = dt.pages[options.selection - 1].map(row => row.row)
}
} else {
- rows = rows.concat(dt.data.data.map((row: cellType[]) => row.filter((_cell: cellType, index: number) => columnShown(index)).map((cell: cellType) => cell.data)))
+ selectedRows = dt.data.data
}
+ const rows: cellDataType[][] = selectedRows.map((row: dataRowType) => {
+ const shownCells = row.cells.filter((_cell: cellType, index: number) => columnShown(index))
+ return shownCells.map((cell: cellType) => cellToText(cell))
+ })
+
const headers = dt.data.headings.filter((_heading: headerCellType, index: number) => columnShown(index)).map((header: headerCellType) => header.text ?? String(header.data))
// Only proceed if we have data
if (rows.length) {
- const arr: (void | { [key: string]: (string | number | boolean | undefined | null | elementNodeType[])})[] = []
- rows.forEach((row: (string | number | boolean | object | undefined | null)[], x: number) => {
+ const arr: (void | { [key: string]: cellDataType})[] = []
+ rows.forEach((row: cellDataType[], x: number) => {
arr[x] = arr[x] || {}
- row.forEach((cell: (string | number | boolean | object | undefined | null), i: number) => {
+ row.forEach((cell: cellDataType, i: number) => {
arr[x][headers[i]] = cell
})
})
diff --git a/src/export/sql.ts b/src/export/sql.ts
index ebb45cb5..7eea28b9 100644
--- a/src/export/sql.ts
+++ b/src/export/sql.ts
@@ -1,9 +1,12 @@
import {
+ cellToText,
isObject
} from "../helpers"
import {DataTable} from "../datatable"
import {
+ cellDataType,
cellType,
+ dataRowType,
headerCellType
} from "../types"
/**
@@ -37,23 +40,29 @@ export const exportSQL = function(dt: DataTable, userOptions : sqlUserOptions =
...userOptions
}
const columnShown = (index: number) => !options.skipColumn.includes(index) && !dt.columns.settings[index]?.hidden
- let rows : (string | number | boolean | object | undefined | null)[][] = []
+
// Selection or whole table
+ let selectedRows: dataRowType[] = []
if (options.selection) {
// Page number
if (Array.isArray(options.selection)) {
// Array of page numbers
for (let i = 0; i < options.selection.length; i++) {
- rows = rows.concat(dt.pages[options.selection[i] - 1].map((row: {row: cellType[], index: number}) => row.row.filter((_cell: cellType, index: number) => columnShown(index)).map((cell: cellType) => cell.text ?? cell.data)))
+ selectedRows = selectedRows.concat(dt.pages[options.selection[i] - 1].map(row => row.row))
}
} else {
- rows = rows.concat(dt.pages[options.selection - 1].map((row: {row: cellType[], index: number}) => row.row.filter((_cell: cellType, index: number) => columnShown(index)).map((cell: cellType) => cell.text ?? cell.data)))
+ selectedRows = dt.pages[options.selection - 1].map(row => row.row)
}
} else {
- rows = rows.concat(dt.data.data.map((row: cellType[]) => row.filter((_cell: cellType, index: number) => columnShown(index)).map((cell: cellType) => cell.text ?? cell.data)))
+ selectedRows = dt.data.data
}
+ const rows: cellDataType[][] = selectedRows.map((row: dataRowType) => {
+ const shownCells = row.cells.filter((_cell: cellType, index: number) => columnShown(index))
+ return shownCells.map((cell: cellType) => cellToText(cell))
+ })
+
const headers = dt.data.headings.filter((_heading: headerCellType, index: number) => columnShown(index)).map((header: headerCellType) => header.text ?? String(header.data))
// Only proceed if we have data
if (rows.length) {
@@ -73,9 +82,9 @@ export const exportSQL = function(dt: DataTable, userOptions : sqlUserOptions =
// Iterate rows and convert cell data to column values
- rows.forEach((row: (string | number | boolean | object | undefined | null)[]) => {
+ rows.forEach((row: cellDataType[]) => {
str += "("
- row.forEach((cell: (string | number | boolean | object | undefined | null)) => {
+ row.forEach((cell: cellDataType) => {
if (typeof cell === "string") {
str += `"${cell}",`
} else {
diff --git a/src/export/txt.ts b/src/export/txt.ts
index aa09c21f..7aae2083 100644
--- a/src/export/txt.ts
+++ b/src/export/txt.ts
@@ -1,9 +1,12 @@
import {
+ cellToText,
isObject
} from "../helpers"
import {DataTable} from "../datatable"
import {
+ cellDataType,
cellType,
+ dataRowType,
headerCellType
} from "../types"
/**
@@ -41,32 +44,39 @@ export const exportTXT = function(dt: DataTable, userOptions : txtUserOptions =
const columnShown = (index: number) => !options.skipColumn.includes(index) && !dt.columns.settings[index]?.hidden
- let rows : (string | number | boolean | object | undefined | null)[][] = []
const headers = dt.data.headings.filter((_heading: headerCellType, index: number) => columnShown(index)).map((header: headerCellType) => header.text ?? header.data)
- // Include headings
- rows[0] = headers
// Selection or whole table
+ let selectedRows: dataRowType[]
if (options.selection) {
// Page number
if (Array.isArray(options.selection)) {
// Array of page numbers
+ selectedRows = []
for (let i = 0; i < options.selection.length; i++) {
- rows = rows.concat(dt.pages[options.selection[i] - 1].map((row: {row: cellType[], index: number}) => row.row.filter((_cell: cellType, index: number) => columnShown(index)).map((cell: cellType) => cell.data)))
+ selectedRows = selectedRows.concat(dt.pages[options.selection[i] - 1].map(row => row.row))
}
} else {
- rows = rows.concat(dt.pages[options.selection - 1].map((row: {row: cellType[], index: number}) => row.row.filter((_cell: cellType, index: number) => columnShown(index)).map((cell: cellType) => cell.data)))
+ selectedRows = dt.pages[options.selection - 1].map(row => row.row)
}
} else {
- rows = rows.concat(dt.data.data.map((row: cellType[]) => row.filter((_cell: cellType, index: number) => columnShown(index)).map((cell: cellType) => cell.data)))
+ selectedRows = dt.data.data
}
+ let rows : cellDataType[][] = []
+ // Include headings
+ rows[0] = headers
+ rows = rows.concat(selectedRows.map((row: dataRowType) => {
+ const shownCells = row.cells.filter((_cell: cellType, index: number) => columnShown(index))
+ return shownCells.map((cell: cellType) => cellToText(cell))
+ }))
+
// Only proceed if we have data
if (rows.length) {
let str = ""
rows.forEach(row => {
- row.forEach((cell: (string | number | boolean | object | undefined | null)) => {
+ row.forEach((cell: cellDataType) => {
if (typeof cell === "string") {
cell = cell.trim()
cell = cell.replace(/\s{2,}/g, " ")
diff --git a/src/helpers.ts b/src/helpers.ts
index e96592d7..6a88b344 100644
--- a/src/helpers.ts
+++ b/src/helpers.ts
@@ -1,4 +1,11 @@
-import {elementNodeType, columnSettingsType, textNodeType} from "./types"
+import {
+ cellDataType,
+ cellType,
+ columnSettingsType,
+ inputCellType,
+ nodeType,
+ textNodeType
+} from "./types"
/**
* Check is item is object
@@ -35,16 +42,28 @@ export const createElement = (nodeName: string, attrs?: { [key: string]: string}
return dom
}
-export const objToText = (obj: (elementNodeType| textNodeType)) => {
+export const objToText = (obj: nodeType) => {
if (["#text", "#comment"].includes(obj.nodeName)) {
return (obj as textNodeType).data
}
if (obj.childNodes) {
- return obj.childNodes.map((childNode: (elementNodeType | textNodeType)) => objToText(childNode)).join("")
+ return obj.childNodes.map((childNode: nodeType) => objToText(childNode)).join("")
}
return ""
}
+export const cellToText = (obj: inputCellType | cellDataType | null | undefined): string => {
+ if (obj === null || obj === undefined) {
+ return ""
+ } else if (obj.hasOwnProperty("text") || obj.hasOwnProperty("data")) {
+ const cell = obj as cellType
+ return cell.text ?? cellToText(cell.data)
+ } else if (obj.hasOwnProperty("nodeName")) {
+ return objToText(obj as nodeType)
+ }
+ return String(obj)
+}
+
export const escapeText = function(text: string) {
return text
@@ -80,3 +99,18 @@ export const columnToVisibleIndex = function(columnIndex: number, columns: colum
}
return visibleIndex
}
+
+/**
+ * Converts a [NamedNodeMap](https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap) into a normal object.
+ *
+ * @param map The `NamedNodeMap` to convert
+ */
+export const namedNodeMapToObject = function(map: NamedNodeMap) {
+ const obj = {}
+ if (map) {
+ for (const attr of map) {
+ obj[attr.name] = attr.value
+ }
+ }
+ return obj
+}
diff --git a/src/read_data.ts b/src/read_data.ts
index 7c001ca8..c3e1b4df 100644
--- a/src/read_data.ts
+++ b/src/read_data.ts
@@ -1,7 +1,17 @@
-import {stringToObj, nodeToObj} from "diff-dom"
+import {nodeToObj, stringToObj} from "diff-dom"
import {parseDate} from "./date"
-import {objToText} from "./helpers"
-import {cellType, DataOption, headerCellType, inputCellType, inputHeaderCellType, nodeType, columnSettingsType} from "./types"
+import {namedNodeMapToObject, objToText} from "./helpers"
+import {
+ cellType,
+ columnSettingsType,
+ DataOption,
+ dataRowType,
+ headerCellType,
+ inputCellType,
+ inputHeaderCellType,
+ inputRowType,
+ nodeType
+} from "./types"
export const readDataCell = (cell: inputCellType, columnSettings : columnSettingsType) : cellType => {
if (cell?.constructor === Object && Object.prototype.hasOwnProperty.call(cell, "data") && !Object.keys(cell).find(key => !(["text", "order", "data", "attributes"].includes(key)))) {
@@ -100,12 +110,7 @@ const readDOMDataCell = (cell: HTMLElement, columnSettings : columnSettingsType)
}
// Save cell attributes to reference when rendering
- cellData.attributes = {}
- if (cell.attributes) {
- for (const attr of cell.attributes) {
- cellData.attributes[attr.name] = attr.value
- }
- }
+ cellData.attributes = namedNodeMapToObject(cell.attributes)
return cellData
}
@@ -168,8 +173,8 @@ export const readDOMHeaderCell = (cell: HTMLElement) : headerCellType => {
export const readTableData = (dataOption: DataOption, dom: (HTMLTableElement | undefined)=undefined, columnSettings, defaultType, defaultFormat) => {
const data = {
- data: [],
- headings: []
+ data: [] as dataRowType[],
+ headings: [] as headerCellType[]
}
if (dataOption.headings) {
data.headings = dataOption.headings.map((heading: inputHeaderCellType) => readHeaderCell(heading))
@@ -203,7 +208,9 @@ export const readTableData = (dataOption: DataOption, dom: (HTMLTableElement | u
return heading
})
} else if (dataOption.data?.length) {
- data.headings = dataOption.data[0].map((_cell: inputCellType) => readHeaderCell(""))
+ const firstRow = dataOption.data[0]
+ const firstRowCells = Array.isArray(firstRow) ? firstRow : firstRow.cells
+ data.headings = firstRowCells.map((_cell: inputCellType) => readHeaderCell(""))
} else if (dom?.tBodies.length) {
data.headings = Array.from(dom.tBodies[0].rows[0].cells).map((_cell: HTMLElement) => readHeaderCell(""))
}
@@ -219,25 +226,42 @@ export const readTableData = (dataOption: DataOption, dom: (HTMLTableElement | u
}
}
if (dataOption.data) {
- data.data = dataOption.data.map((row: inputCellType[]) => row.map((cell: inputCellType, index: number) => readDataCell(cell, columnSettings[index])))
+ data.data = dataOption.data.map((row: inputRowType | inputCellType[]) => {
+ let attributes: { [key: string]: string }
+ let cells: inputCellType[]
+ if (Array.isArray(row)) {
+ attributes = {}
+ cells = row
+ } else {
+ attributes = row.attributes
+ cells = row.cells
+ }
+ return {
+ attributes,
+ cells: cells.map((cell: inputCellType, index: number) => readDataCell(cell, columnSettings[index]))
+ } as dataRowType
+ })
} else if (dom?.tBodies?.length) {
data.data = Array.from(dom.tBodies[0].rows).map(
- row => Array.from(row.cells).map(
- (cell, index) => {
- const cellData = cell.dataset.content ?
- readDataCell(cell.dataset.content, columnSettings[index]) :
- readDOMDataCell(cell, columnSettings[index])
- if (cell.dataset.order) {
- cellData.order = isNaN(parseFloat(cell.dataset.order)) ? cell.dataset.order : parseFloat(cell.dataset.order)
- }
- return cellData
+ row => ({
+ attributes: namedNodeMapToObject(row.attributes),
+ cells: Array.from(row.cells).map(
+ (cell, index) => {
+ const cellData = cell.dataset.content ?
+ readDataCell(cell.dataset.content, columnSettings[index]) :
+ readDOMDataCell(cell, columnSettings[index])
+ if (cell.dataset.order) {
+ cellData.order = isNaN(parseFloat(cell.dataset.order)) ? cell.dataset.order : parseFloat(cell.dataset.order)
+ }
+ return cellData
- }
- )
+ }
+ )
+ } as dataRowType)
)
}
- if (data.data.length && data.data[0].length !== data.headings.length) {
+ if (data.data.length && data.data[0].cells.length !== data.headings.length) {
throw new Error(
"Data heading length mismatch."
)
diff --git a/src/rows.ts b/src/rows.ts
index 59f30760..881d7d50 100644
--- a/src/rows.ts
+++ b/src/rows.ts
@@ -1,6 +1,7 @@
import {readDataCell} from "./read_data"
import {DataTable} from "./datatable"
-import {cellType, inputCellType} from "./types"
+import {cellType, dataRowType, inputCellType} from "./types"
+import {cellToText} from "./helpers"
/**
* Rows API
*/
@@ -35,16 +36,18 @@ export class Rows {
* Add new row
*/
add(data: cellType[]) {
- const row = data.map((cell: cellType, index: number) => {
- const columnSettings = this.dt.columns.settings[index]
- return readDataCell(cell, columnSettings)
- })
- this.dt.data.data.push(row)
+ if (!Array.isArray(data) || data.length < 1) {
+ return
+ }
- // We may have added data to an empty table
- if ( this.dt.data.data.length ) {
- this.dt.hasRows = true
+ const row: dataRowType = {
+ cells: data.map((cell: cellType, index: number) => {
+ const columnSettings = this.dt.columns.settings[index]
+ return readDataCell(cell, columnSettings)
+ })
}
+ this.dt.data.data.push(row)
+ this.dt.hasRows = true
this.dt.update(true)
}
@@ -53,7 +56,7 @@ export class Rows {
*/
remove(select: number | number[]) {
if (Array.isArray(select)) {
- this.dt.data.data = this.dt.data.data.filter((_row: cellType[], index: number) => !select.includes(index))
+ this.dt.data.data = this.dt.data.data.filter((_row: dataRowType, index: number) => !select.includes(index))
// We may have emptied the table
if ( !this.dt.data.data.length ) {
this.dt.hasRows = false
@@ -72,7 +75,11 @@ export class Rows {
// returns row index of first case-insensitive string match
// inside the td innerText at specific column index
return this.dt.data.data.findIndex(
- (row: cellType[]) => (row[columnIndex].text ?? String(row[columnIndex].data)).toLowerCase().includes(String(value).toLowerCase())
+ (row: dataRowType) => {
+ const cell = row.cells[columnIndex]
+ const cellText = cellToText(cell)
+ return cellText.toLowerCase().includes(String(value).toLowerCase())
+ }
)
}
@@ -93,7 +100,7 @@ export class Rows {
// get the row from data
const row = this.dt.data.data[index]
// return innerHTML of each td
- const cols = row.map((cell: cellType) => cell.data)
+ const cols = row.cells.map((cell: cellType) => cell.data)
// return everything
return {
index,
@@ -106,10 +113,12 @@ export class Rows {
* Update a row with new data
*/
updateRow(select: number, data: inputCellType[]) {
- const row = data.map((cell: inputCellType, index: number) => {
- const columnSettings = this.dt.columns.settings[index]
- return readDataCell(cell, columnSettings)
- })
+ const row: dataRowType = {
+ cells: data.map((cell: inputCellType, index: number) => {
+ const columnSettings = this.dt.columns.settings[index]
+ return readDataCell(cell, columnSettings)
+ })
+ }
this.dt.data.data.splice(select, 1, row)
this.dt.update(true)
}
diff --git a/src/types.ts b/src/types.ts
index eaeb19f4..d5d46f0a 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -20,8 +20,10 @@ type nodeType = elementNodeType | textNodeType
// Definitions for table cells and other table relevant data
+type cellDataType = string | number | boolean | nodeType | object;
+
interface cellType {
- data: string | number | boolean | elementNodeType[] | object;
+ data: cellDataType;
text?: string;
order?: string | number;
attributes?: { [key: string]: string };
@@ -29,8 +31,13 @@ interface cellType {
type inputCellType = cellType | string | number | boolean;
+interface inputRowType {
+ attributes?: { [key: string]: string };
+ cells: inputCellType[];
+}
+
interface headerCellType {
- data: string | number | boolean | elementNodeType[] | object;
+ data: cellDataType;
type?: ("html" | "string");
text?: string;
attributes?: { [key: string]: string };
@@ -38,21 +45,25 @@ interface headerCellType {
type inputHeaderCellType = headerCellType | string | number | boolean;
+interface dataRowType {
+ attributes?: { [key: string]: string };
+ cells: cellType[];
+}
-interface DataOption{
+interface DataOption {
headings?: string[];
- data?: inputCellType[][] ;
+ data?: (inputRowType | inputCellType[])[];
}
-interface TableDataType{
+interface TableDataType {
headings: headerCellType[];
- data: cellType[][] ;
+ data: dataRowType[];
}
type renderType = ((cellData: (string | number | boolean | object | elementNodeType[]), td: object, rowIndex: number, cellIndex: number) => elementNodeType | string | void);
interface rowType {
- row: cellType[];
+ row: dataRowType;
index: number;
}
@@ -210,10 +221,11 @@ interface ClassConfiguration {
type pagerRenderType = ((data: [onFirstPage: boolean, onLastPage: boolean, currentPage: number, totalPages: number], pager: elementNodeType) => elementNodeType | void);
-type rowRenderType = ((row: object, tr: object, index: number) => elementNodeType | void);
+type rowRenderType = ((row: dataRowType, tr: elementNodeType, index: number) => elementNodeType | void);
+
+type renderTypeEnum = "main" | "print" | "header" | "message"
-type tableRenderType = ((data: object, table: elementNodeType, type: string) => elementNodeType | void);
-// Type can be 'main', 'print', 'header' or 'message'
+type tableRenderType = ((data: object, table: elementNodeType, type: renderTypeEnum) => elementNodeType | void);
interface DataTableConfiguration {
@@ -472,15 +484,18 @@ interface columnsStateType {
export {
+ cellDataType,
cellType,
columnsStateType,
DataOption,
DataTableConfiguration,
DataTableOptions,
+ dataRowType,
filterStateType,
headerCellType,
inputCellType,
inputHeaderCellType,
+ inputRowType,
elementNodeType,
nodeType,
renderOptions,
diff --git a/src/virtual_dom.ts b/src/virtual_dom.ts
index 2fff27f3..37f3ae67 100644
--- a/src/virtual_dom.ts
+++ b/src/virtual_dom.ts
@@ -1,6 +1,7 @@
import {stringToObj} from "diff-dom"
import {cellType, columnsStateType, columnSettingsType, DataTableOptions, headerCellType, elementNodeType, textNodeType, renderOptions, rowType} from "./types"
+import {cellToText} from "./helpers"
export const headingsToVirtualHeaderRowDOM = (
@@ -124,9 +125,12 @@ export const dataToVirtualDOM = (tableAttributes: { [key: string]: string}, head
const tr: elementNodeType = {
nodeName: "TR",
attributes: {
- "data-index": String(index)
+ ...row.attributes,
+ ...{
+ "data-index": String(index)
+ }
},
- childNodes: row.map(
+ childNodes: row.cells.map(
(cell: cellType, cIndex: number) => {
const column = columnSettings[cIndex] || ({
type,
@@ -145,7 +149,7 @@ export const dataToVirtualDOM = (tableAttributes: { [key: string]: string}, head
[
{
nodeName: "#text",
- data: cell.text ?? String(cell.data)
+ data: cellToText(cell)
}
]
} as elementNodeType
diff --git a/test/cases/cell-attributes-dom.html b/test/cases/cell-attributes-dom.html
new file mode 100644
index 00000000..efc6c2f2
--- /dev/null
+++ b/test/cases/cell-attributes-dom.html
@@ -0,0 +1,48 @@
+
+
+
+
+
cell-attributes-dom
+
+
+
+
+
+ Name |
+ Ext. |
+ City |
+ Start Date |
+ Completion |
+
+
+
+
+ Unity Pugh |
+ 9958 |
+ Curicó |
+ 2005/02/11 |
+ 37% |
+
+
+ Theodore Duran |
+ 8971 |
+ Dhanbad |
+ 1999/04/07 |
+ 97% |
+
+
+ Kylie Bishop |
+ 3147 |
+ Norman |
+ 2005/09/08 |
+ 63% |
+
+
+
+
+
+
+
+
diff --git a/test/cases/cell-attributes-js.html b/test/cases/cell-attributes-js.html
new file mode 100644
index 00000000..1b8db0ed
--- /dev/null
+++ b/test/cases/cell-attributes-js.html
@@ -0,0 +1,71 @@
+
+
+
+
+
cell-attributes-js
+
+
+
+
+
+
+
+
diff --git a/test/test.mjs b/test/test.mjs
index cdaf53e8..4c42c0b0 100644
--- a/test/test.mjs
+++ b/test/test.mjs
@@ -20,8 +20,10 @@ if (process.env.CI) { // eslint-disable-line no-process-env
options.headless()
}
const driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).setChromeOptions(options).build()
-const manage = driver.manage()
-manage.window().maximize()
+driver.manage().window().setRect({width: 1920,
+ height: 1080,
+ x: 0,
+ y: 0})
const baseUrl = `http://localhost:${port}/`
let demoUrls
server.listen(port)
@@ -60,7 +62,7 @@ const clickAllSortableHeaders = function(driver, counter=0) {
describe("Demos work", function() {
this.timeout(5000)
forEach(demoUrls).it("loads %s without JS errors", url => driver.get(url).then(
- () => manage.logs().get("browser")
+ () => driver.manage().logs().get("browser")
).then(
log => assert.deepEqual(log, [])
))
@@ -68,7 +70,7 @@ describe("Demos work", function() {
forEach(demoUrls).it("can click on all sort headers of %s without JS errors", url => driver.get(url).then(
() => clickAllSortableHeaders(driver)
).then(
- () => manage.logs().get("browser")
+ () => driver.manage().logs().get("browser")
).then(
log => assert.deepEqual(log, [])
))
@@ -120,6 +122,26 @@ describe("Integration tests pass", function() {
const captionText = await caption.getText()
assert.equal(captionText, "This is a table caption.")
})
+
+ /**
+ * Assert that the rendered table has all the attributes defined.
+ */
+ const assertCellAttrs = async function(tableId) {
+ await driver.findElement(webdriver.By.xpath(`//table[@id='${tableId}' and contains(@class, 'my-table') and @style='white-space: nowrap;']`))
+ await driver.findElement(webdriver.By.xpath(`//table[@id='${tableId}']/thead/tr/th[@class='red']`))
+ await driver.findElement(webdriver.By.xpath(`//table[@id='${tableId}']/tbody/tr[@class='yellow']`))
+ await driver.findElement(webdriver.By.xpath(`//table[@id='${tableId}']/tbody/tr[@class='yellow']/td[@class='red']`))
+ }
+
+ it("preserves cell attributes (DOM)", async () => {
+ await driver.get(`${baseUrl}tests/cell-attributes-dom.html`)
+ await assertCellAttrs("cell-attributes-dom-table")
+ })
+
+ it("preserves cell attributes (JS)", async () => {
+ await driver.get(`${baseUrl}tests/cell-attributes-js.html`)
+ await assertCellAttrs("cell-attributes-js-table")
+ })
})
after(() => {