Skip to content

Commit 815922b

Browse files
authored
fix: rollback portion of CSP safe code causing regression, fixes #914 (#915)
- this PR is mostly reverting commit 113b613 which caused a regression as shown in issue #914, we'll have to investigate on how to reapply the change in the future without regressing
1 parent 4793068 commit 815922b

File tree

2 files changed

+174
-33
lines changed

2 files changed

+174
-33
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
describe('Example 1 - Basic Grid', () => {
2+
const GRID_ROW_HEIGHT = 25;
3+
const fullTitles = ['Title', 'Duration', '% Complete', 'Start', 'Finish', 'Effort Driven'];
4+
5+
beforeEach(() => {
6+
// create a console.log spy for later use
7+
cy.window().then((win) => {
8+
cy.spy(win.console, "log");
9+
});
10+
});
11+
12+
it('should display Example 1 with Basic Basic', () => {
13+
cy.visit(`${Cypress.config('baseUrl')}/examples/example1-simple.html`);
14+
cy.get('h2').should('contain', 'Demonstrates:');
15+
cy.contains('basic grid with minimal configuration');
16+
});
17+
18+
it('should have exact Column Titles in grid', () => {
19+
cy.get('#myGrid')
20+
.find('.slick-header-columns')
21+
.children()
22+
.each(($child, index) => expect($child.text()).to.eq(fullTitles[index]));
23+
});
24+
25+
it('should resize all columns and make them wider', () => {
26+
// resize Finish column
27+
cy.get('.slick-header-columns')
28+
.children('.slick-header-column:nth(4)')
29+
.should('contain', 'Finish');
30+
31+
cy.get('.slick-resizable-handle:nth(4)')
32+
.trigger('mousedown', { which: 1, force: true })
33+
.trigger('mousemove', 'bottomRight');
34+
35+
cy.get('.slick-header-column:nth(5)')
36+
.trigger('mousemove', 'bottomRight')
37+
.trigger('mouseup', 'bottomRight', { which: 1, force: true });
38+
39+
// resize Start column
40+
cy.get('.slick-header-columns')
41+
.children('.slick-header-column:nth(3)')
42+
.should('contain', 'Start');
43+
44+
cy.get('.slick-resizable-handle:nth(3)')
45+
.trigger('mousedown', { which: 1, force: true })
46+
.trigger('mousemove', 'bottomRight');
47+
48+
cy.get('.slick-header-column:nth(5)')
49+
.trigger('mousemove', 'bottomRight')
50+
.trigger('mouseup', 'bottomRight', { which: 1, force: true });
51+
52+
// resize %Complete column
53+
cy.get('.slick-header-columns')
54+
.children('.slick-header-column:nth(2)')
55+
.should('contain', '% Complete');
56+
57+
cy.get('.slick-resizable-handle:nth(2)')
58+
.trigger('mousedown', { which: 1, force: true })
59+
.trigger('mousemove', 'bottomRight');
60+
61+
cy.get('.slick-header-column:nth(4)')
62+
.trigger('mousemove', 'bottomRight')
63+
.trigger('mouseup', 'bottomRight', { which: 1, force: true });
64+
65+
// resize Duration column
66+
cy.get('.slick-header-columns')
67+
.children('.slick-header-column:nth(1)')
68+
.should('contain', 'Duration');
69+
70+
cy.get('.slick-resizable-handle:nth(1)')
71+
.trigger('mousedown', { which: 1, force: true })
72+
.trigger('mousemove', 'bottomRight');
73+
74+
cy.get('.slick-header-column:nth(3)')
75+
.trigger('mousemove', 'bottomRight')
76+
.trigger('mouseup', 'bottomRight', { which: 1, force: true });
77+
78+
// resize Title column
79+
cy.get('.slick-header-columns')
80+
.children('.slick-header-column:nth(0)')
81+
.should('contain', 'Title');
82+
83+
cy.get('.slick-resizable-handle:nth(0)')
84+
.trigger('mousedown', { which: 1, force: true })
85+
.trigger('mousemove', 'bottomRight');
86+
87+
cy.get('.slick-header-column:nth(2)')
88+
.trigger('mousemove', 'bottomRight')
89+
.trigger('mouseup', 'bottomRight', { which: 1, force: true });
90+
});
91+
92+
it('should scroll horizontally completely to the right and expect all cell to be rendered', () => {
93+
// scroll to right
94+
cy.get('.slick-viewport-top.slick-viewport-left')
95+
.scrollTo('100%', '0%', { duration: 1000 });
96+
97+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell.l1`).should('contain', '5 days');
98+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell.l2`).contains(/[0-9]*/);
99+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell.l3`).should('contain', '01/01/2009');
100+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell.l4`).should('contain', '01/05/2009');
101+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell.l2`).contains(/[true|false]*/);
102+
103+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 15}px;"] > .slick-cell.l1`).should('contain', '5 days');
104+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 15}px;"] > .slick-cell.l2`).contains(/[0-9]*/);
105+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 15}px;"] > .slick-cell.l3`).should('contain', '01/01/2009');
106+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 15}px;"] > .slick-cell.l4`).should('contain', '01/05/2009');
107+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 15}px;"] > .slick-cell.l2`).contains(/[true|false]*/);
108+
});
109+
110+
it('should scroll vertically to the middle of the grid and expect all cell to be rendered', () => {
111+
// scroll to right
112+
cy.get('.slick-viewport-top.slick-viewport-left')
113+
.scrollTo('100%', '40%', { duration: 1000 });
114+
115+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 195}px;"] > .slick-cell.l1`).should('contain', '5 days');
116+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 195}px;"] > .slick-cell.l2`).contains(/[0-9]*/);
117+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 195}px;"] > .slick-cell.l3`).should('contain', '01/01/2009');
118+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 195}px;"] > .slick-cell.l4`).should('contain', '01/05/2009');
119+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 195}px;"] > .slick-cell.l2`).contains(/[true|false]*/);
120+
121+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 210}px;"] > .slick-cell.l1`).should('contain', '5 days');
122+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 210}px;"] > .slick-cell.l2`).contains(/[0-9]*/);
123+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 210}px;"] > .slick-cell.l3`).should('contain', '01/01/2009');
124+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 210}px;"] > .slick-cell.l4`).should('contain', '01/05/2009');
125+
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 210}px;"] > .slick-cell.l2`).contains(/[true|false]*/);
126+
});
127+
});

src/slick.grid.ts

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3809,7 +3809,7 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
38093809
return item[columnDef.field as keyof TData];
38103810
}
38113811

3812-
protected appendRowHtml(divArrayL: HTMLElement[], divArrayR: HTMLElement[], row: number, range: CellViewportRange, dataLength: number) {
3812+
protected appendRowHtml(stringArrayL: string[], stringArrayR: string[], row: number, range: CellViewportRange, dataLength: number) {
38133813
const d = this.getDataItem(row);
38143814
const dataLoading = row < dataLength && !d;
38153815
let rowCss = 'slick-row' +
@@ -3830,17 +3830,12 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
38303830

38313831
const frozenRowOffset = this.getFrozenRowOffset(row);
38323832

3833-
const rowDiv = document.createElement('div');
3834-
let rowDivR: HTMLElement | undefined;
3833+
const rowHtml = `<div class="ui-widget-content ${rowCss}" data-top="${(this.getRowTop(row) - frozenRowOffset)}px">`; //style="top:${(this.getRowTop(row) - frozenRowOffset)}px"
3834+
3835+
stringArrayL.push(rowHtml);
38353836

3836-
rowDiv.className = 'ui-widget-content ' + rowCss;
3837-
rowDiv.style.top = `${(this.getRowTop(row) - frozenRowOffset)}px`;
3838-
divArrayL.push(rowDiv);
38393837
if (this.hasFrozenColumns()) {
3840-
// it has to be a deep copy otherwise we will have issues with pass by reference in js since
3841-
// attempting to add the same element to 2 different arrays will just move 1 item to the other array
3842-
rowDivR = rowDiv.cloneNode(true) as HTMLElement;
3843-
divArrayR.push(rowDivR);
3838+
stringArrayR.push(rowHtml);
38443839
}
38453840

38463841
let colspan: number | string;
@@ -3866,23 +3861,28 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
38663861
}
38673862

38683863
if (this.hasFrozenColumns() && (i > this._options.frozenColumn!)) {
3869-
//could add it as a check in the if the !
3870-
this.appendCellHtml(rowDivR!, row, i, (colspan as number), d);
3864+
this.appendCellHtml(stringArrayR, row, i, (colspan as number), d);
38713865
} else {
3872-
this.appendCellHtml(rowDiv, row, i, (colspan as number), d);
3866+
this.appendCellHtml(stringArrayL, row, i, (colspan as number), d);
38733867
}
38743868
} else if (m.alwaysRenderColumn || (this.hasFrozenColumns() && i <= this._options.frozenColumn!)) {
3875-
this.appendCellHtml(rowDiv, row, i, (colspan as number), d);
3869+
this.appendCellHtml(stringArrayL, row, i, (colspan as number), d);
38763870
}
38773871

38783872
if ((colspan as number) > 1) {
38793873
i += ((colspan as number) - 1);
38803874
}
38813875
}
3876+
3877+
stringArrayL.push('</div>');
3878+
3879+
if (this.hasFrozenColumns()) {
3880+
stringArrayR.push('</div>');
3881+
}
38823882
}
38833883

3884-
protected appendCellHtml(divRow: HTMLElement, row: number, cell: number, colspan: number, item: TData) {
3885-
// divRow: the html element to append items too
3884+
protected appendCellHtml(stringArray: string[], row: number, cell: number, colspan: number, item: TData) {
3885+
// stringArray: stringBuilder containing the HTML parts
38863886
// row, cell: row and column index
38873887
// colspan: HTML colspan
38883888
// item: grid data for row
@@ -3923,26 +3923,27 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
39233923
if ((formatterResult as FormatterResultObject)?.addClasses) {
39243924
addlCssClasses += (addlCssClasses ? ' ' : '') + (formatterResult as FormatterResultObject).addClasses;
39253925
}
3926+
const toolTip = (formatterResult as FormatterResultObject)?.toolTip ? `title="${(formatterResult as FormatterResultObject).toolTip}"` : '';
39263927

3927-
const toolTipText = (formatterResult as FormatterResultObject)?.toolTip ? `${(formatterResult as FormatterResultObject).toolTip}` : '';
3928-
const cellDiv = document.createElement('div');
3929-
cellDiv.className = cellCss + (addlCssClasses ? ' ' + addlCssClasses : '');
3930-
cellDiv.setAttribute('title', toolTipText);
3928+
let customAttrStr = '';
39313929
if (m.hasOwnProperty('cellAttrs') && m.cellAttrs instanceof Object) {
39323930
for (const key in m.cellAttrs) {
39333931
if (m.cellAttrs.hasOwnProperty(key)) {
3934-
cellDiv.setAttribute(key, m.cellAttrs[key]);
3932+
customAttrStr += ` ${key}="${m.cellAttrs[key]}" `;
39353933
}
39363934
}
39373935
}
39383936

3937+
stringArray.push(`<div class="${cellCss + (addlCssClasses ? ' ' + addlCssClasses : '')}" ${toolTip + customAttrStr}>`);
3938+
39393939
// if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet)
39403940
if (item) {
39413941
const cellResult = (Object.prototype.toString.call(formatterResult) !== '[object Object]' ? formatterResult : (formatterResult as FormatterResultWithHtml).html || (formatterResult as FormatterResultWithText).text);
3942-
this.applyHtmlCode(cellDiv, cellResult as string | HTMLElement);
3942+
const formattedCellResult = (cellResult instanceof HTMLElement) ? cellResult.outerHTML : cellResult;
3943+
stringArray.push(formattedCellResult as string);
39433944
}
39443945

3945-
divRow.appendChild(cellDiv);
3946+
stringArray.push('</div>');
39463947

39473948
this.rowsCache[row].cellRenderQueue.push(cell);
39483949
this.rowsCache[row].cellColSpans[cell] = colspan;
@@ -4554,7 +4555,7 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
45544555

45554556
protected cleanUpAndRenderCells(range: CellViewportRange) {
45564557
let cacheEntry;
4557-
const divRow: HTMLElement = document.createElement('div');
4558+
const stringArray: string[] = [];
45584559
const processedRows: number[] = [];
45594560
let cellsAdded: number;
45604561
let totalCellsAdded = 0;
@@ -4605,7 +4606,7 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
46054606

46064607
const colspanNb = colspan as number; // at this point colspan is for sure a number
46074608
if (this.columnPosRight[Math.min(ii - 1, i + colspanNb - 1)] > range.leftPx) {
4608-
this.appendCellHtml(divRow, row, i, colspanNb, d);
4609+
this.appendCellHtml(stringArray, row, i, colspanNb, d);
46094610
cellsAdded++;
46104611
}
46114612

@@ -4618,12 +4619,12 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
46184619
processedRows.push(row);
46194620
}
46204621
}
4621-
if (!divRow.children.length) {
4622+
if (!stringArray.length) {
46224623
return;
46234624
}
46244625

46254626
const x = document.createElement('div');
4626-
x.appendChild(divRow);
4627+
x.innerHTML = this.sanitizeHtmlString(stringArray.join(''));
46274628

46284629
let processedRow: number | null | undefined;
46294630
let node: HTMLElement;
@@ -4648,8 +4649,8 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
46484649
}
46494650

46504651
protected renderRows(range: { top: number; bottom: number; leftPx: number; rightPx: number; }) {
4651-
const divArrayL: HTMLElement[] = [];
4652-
const divArrayR: HTMLElement[] = [];
4652+
const stringArrayL: string[] = [];
4653+
const stringArrayR: string[] = [];
46534654
const rows: number[] = [];
46544655
let needToReselectCell = false;
46554656
const dataLength = this.getDataLength();
@@ -4679,7 +4680,7 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
46794680
cellRenderQueue: []
46804681
};
46814682

4682-
this.appendRowHtml(divArrayL, divArrayR, i, range, dataLength);
4683+
this.appendRowHtml(stringArrayL, stringArrayR, i, range, dataLength);
46834684
if (this.activeCellNode && this.activeRow === i) {
46844685
needToReselectCell = true;
46854686
}
@@ -4690,8 +4691,12 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
46904691

46914692
const x = document.createElement('div');
46924693
const xRight = document.createElement('div');
4693-
divArrayL.forEach(elm => x.appendChild(elm as HTMLElement));
4694-
divArrayR.forEach(elm => xRight.appendChild(elm as HTMLElement));
4694+
x.innerHTML = this.sanitizeHtmlString(stringArrayL.join(''));
4695+
xRight.innerHTML = this.sanitizeHtmlString(stringArrayR.join(''));
4696+
const elements1 = x.querySelectorAll('[data-top]') as NodeListOf<HTMLElement>;
4697+
const elements2 = xRight.querySelectorAll('[data-top]') as NodeListOf<HTMLElement>;
4698+
this.applyTopStyling(elements1);
4699+
this.applyTopStyling(elements2);
46954700

46964701
for (let i = 0, ii = rows.length; i < ii; i++) {
46974702
if ((this.hasFrozenRows) && (rows[i] >= this.actualFrozenRow)) {
@@ -4726,6 +4731,15 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
47264731
}
47274732
}
47284733

4734+
protected applyTopStyling(elements: NodeListOf<HTMLElement>) {
4735+
elements?.forEach((element: HTMLElement) => {
4736+
const top = element.dataset.top;
4737+
if (top !== undefined) {
4738+
element.style.top = top;
4739+
}
4740+
});
4741+
}
4742+
47294743
protected startPostProcessing() {
47304744
if (!this._options.enableAsyncPostRender) {
47314745
return;
@@ -6781,4 +6795,4 @@ if (IIFE_ONLY && window.Slick) {
67816795
Utils.extend(Slick, {
67826796
Grid: SlickGrid,
67836797
});
6784-
}
6798+
}

0 commit comments

Comments
 (0)