diff --git a/cypress/integration/example-colspan.spec.js b/cypress/integration/example-colspan.spec.js
index 1421cbb00..b9a40ef43 100644
--- a/cypress/integration/example-colspan.spec.js
+++ b/cypress/integration/example-colspan.spec.js
@@ -10,6 +10,7 @@ describe('Example - Column Span & Header Grouping', { retries: 1 }, () => {
it('should display Example title', () => {
cy.visit(`${Cypress.config('baseExampleUrl')}/example-colspan.html`);
cy.get('h2').contains('Demonstrates');
+ cy.get('h2 + ul > li').first().contains('column span');
});
it('should have exact column titles', () => {
diff --git a/cypress/integration/example-csp-header.spec.js b/cypress/integration/example-csp-header.spec.js
new file mode 100644
index 000000000..e0a5d4700
--- /dev/null
+++ b/cypress/integration/example-csp-header.spec.js
@@ -0,0 +1,52 @@
+describe('Example CSP Header - with Column Span & Header Grouping', () => {
+ // NOTE: everywhere there's a * 2 is because we have a top+bottom (frozen rows) containers even after Unfreeze Columns/Rows
+ const GRID_ROW_HEIGHT = 25;
+ const fullTitles = ['Title', 'Duration', '% Complete', 'Start', 'Finish', 'Effort Driven'];
+ for (let i = 0; i < 30; i++) {
+ fullTitles.push(`Mock${i}`);
+ }
+
+ beforeEach(() => {
+ // create a console.log spy for later use
+ cy.window().then((win) => {
+ cy.spy(win.console, "log");
+ });
+ });
+
+ it('should display Example title', () => {
+ cy.visit(`${Cypress.config('baseExampleUrl')}/example-csp-header.html`);
+ cy.get('h2').contains('Demonstrates');
+ cy.get('h2 + ul > li').first().contains('column span');
+ });
+
+ it('should have exact column titles', () => {
+ cy.get('#myGrid')
+ .find('.slick-header-columns')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(fullTitles[index]));
+ });
+
+ it('should expect 1st row to be 1 column spanned to the entire width', () => {
+ cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(0)`).should('contain', 'Task 0');
+ cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(1)`).should('not.exist');
+ });
+
+ it('should expect 2nd row to be 4 columns and not be spanned', () => {
+ cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(0)`).should('contain', 'Task 1');
+ cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(1)`).should('contain', '5 days');
+ cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(2)`).should('contain', '01/05/2009');
+ cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(3)`).contains(/(true|false)/);
+ });
+
+ it('should expect 3rd row to be 1 column spanned to the entire width', () => {
+ cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(0)`).should('contain', 'Task 2');
+ cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(1)`).should('not.exist');
+ });
+
+ it('should expect 4th row to be 4 columns and not be spanned', () => {
+ cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(0)`).should('contain', 'Task 3');
+ cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(1)`).should('contain', '5 days');
+ cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(2)`).should('contain', '01/05/2009');
+ cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(3)`).contains(/(true|false)/);
+ });
+});
diff --git a/examples/example-csp-header.html b/examples/example-csp-header.html
new file mode 100644
index 000000000..021ba2dd6
--- /dev/null
+++ b/examples/example-csp-header.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+ ⌂
+ Demonstrates:
+
+
+ View Source:
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/example-csp-header.js b/examples/example-csp-header.js
new file mode 100644
index 000000000..5a8d07818
--- /dev/null
+++ b/examples/example-csp-header.js
@@ -0,0 +1,61 @@
+document.addEventListener("DOMContentLoaded", function () {
+ var grid;
+ var columns = [
+ { id: "title", name: "Title", field: "title" },
+ { id: "duration", name: "Duration", field: "duration" },
+ { id: "%", name: "% Complete", field: "percentComplete", selectable: false, width: 100 },
+ { id: "start", name: "Start", field: "start" },
+ { id: "finish", name: "Finish", field: "finish" },
+ { id: "effort-driven", name: "Effort Driven", field: "effortDriven", width: 100 }
+ ];
+
+ var options = {
+ enableCellNavigation: true,
+ enableColumnReorder: false,
+ sanitizer: (html) => window.DOMPurify.sanitize(html, { RETURN_TRUSTED_TYPE: true })
+ };
+ let gridElement = document.getElementById("myGrid");
+ gridElement.style.width = "600px";
+ gridElement.style.height = "500px";
+
+ let linkElement = document.getElementById("link");
+ //text-decoration: none; font-size: 22px
+ linkElement.style.textDecoration = "none";
+ linkElement.style.fontSize = "22px";
+
+ var data = [];
+ for (var i = 0; i < 500; i++) {
+ data[i] = {
+ title: "Task " + i,
+ duration: "5 days",
+ percentComplete: Math.round(Math.random() * 100),
+ start: "01/01/2009",
+ finish: "01/05/2009",
+ effortDriven: (i % 5 === 0)
+ };
+ }
+
+ data.getItemMetadata = function (row) {
+ if (row % 2 === 1) {
+ return {
+ "columns": {
+ "duration": {
+ "colspan": 3
+ }
+ }
+ };
+ } else {
+ return {
+ "columns": {
+ 0: {
+ "colspan": "*"
+ }
+ }
+ };
+ }
+ };
+
+ grid = new Slick.Grid("#myGrid", data, columns, options);
+
+ grid.setSelectionModel(new Slick.CellSelectionModel());
+});
\ No newline at end of file
diff --git a/examples/index.html b/examples/index.html
index 67ecaf522..7267edeae 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -79,10 +79,15 @@ Grouping
Other Features
Bootstrap, Dynamic Grids and Third Party component editors
diff --git a/slick.grid.js b/slick.grid.js
index 2da292f19..feab78e61 100644
--- a/slick.grid.js
+++ b/slick.grid.js
@@ -1287,7 +1287,8 @@ if (typeof Slick === "undefined") {
const headerRowTarget = hasFrozenColumns() ? ((i <= options.frozenColumn) ? _headerRowL : _headerRowR) : _headerRowL;
const header = utils.createDomElement('div', { id: `${uid + m.id}`, dataset: { id: m.id }, className: 'ui-state-default slick-header-column', title: m.toolTip || '' }, headerTarget);
- utils.createDomElement('span', { className: 'slick-column-name', innerHTML: sanitizeHtmlString(m.name) }, header);
+ const colNameElm = utils.createDomElement('span', { className: 'slick-column-name' }, header);
+ colNameElm.innerHTML = sanitizeHtmlString(m.name);
utils.width(header, m.width - headerColumnWidthDiff);
let classname = m.headerCssClass || null;
@@ -2743,7 +2744,8 @@ if (typeof Slick === "undefined") {
// headers have not yet been created, create a new node
let header = getHeader(columnDef);
headerColEl = utils.createDomElement('div', { id: dummyHeaderColElId, className: 'ui-state-default slick-header-column', }, header);
- utils.createDomElement('span', { className: 'slick-column-name', innerHTML: sanitizeHtmlString(columnDef.name) }, headerColEl);
+ const colNameElm = utils.createDomElement('span', { className: 'slick-column-name' }, headerColEl);
+ colNameElm.innerHTML = sanitizeHtmlString(columnDef.name);
clone.style.cssText = 'position: absolute; visibility: hidden;right: auto;text-overflow: initial;white-space: nowrap;';
if (columnDef.headerCssClass) {
headerColEl.classList.add(...columnDef.headerCssClass.split(' '));
@@ -4125,7 +4127,8 @@ if (typeof Slick === "undefined") {
return;
}
- var x = utils.createDomElement('div', { innerHTML: sanitizeHtmlString(stringArray.join('')) });
+ var x = document.createElement('div');
+ x.innerHTML = sanitizeHtmlString(stringArray.join(''));
var processedRow;
var node;
while ((processedRow = processedRows.pop()) != null) {
@@ -4185,8 +4188,10 @@ if (typeof Slick === "undefined") {
if (!rows.length) { return; }
- let x = utils.createDomElement('div', { innerHTML: sanitizeHtmlString(stringArrayL.join('')) });
- let xRight = utils.createDomElement('div', { innerHTML: sanitizeHtmlString(stringArrayR.join('')) });
+ const x = document.createElement('div');
+ const xRight = document.createElement('div');
+ x.innerHTML = sanitizeHtmlString(stringArrayL.join(''));
+ xRight.innerHTML = sanitizeHtmlString(stringArrayR.join(''));
for (var i = 0, ii = rows.length; i < ii; i++) {
if (( hasFrozenRows ) && ( rows[i] >= actualFrozenRow )) {