Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions packages/common/src/core/__tests__/slickGrid.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,29 @@ describe('SlickGrid core file', () => {
expect(grid.getGridPosition()).toBeTruthy();
});

it('should handle ancestor scrolling after the grid container is moved', () => {
const columns = [{ id: 'firstName', field: 'firstName', name: 'First Name' }] as Column[];
const oldScrollContainer = document.createElement('div');
const newScrollContainer = document.createElement('div');
document.body.append(oldScrollContainer, newScrollContainer);
oldScrollContainer.appendChild(container);

grid = new SlickGrid<any, Column>(container, [{ id: 0, firstName: 'Avery' }], columns, defaultOptions);
grid.init();
grid.setActiveCell(0, 0);
const positionChangedSpy = vi.spyOn(grid.onActiveCellPositionChanged, 'notify');

oldScrollContainer.dispatchEvent(new Event('scroll', { bubbles: false }));
expect(positionChangedSpy).toHaveBeenCalledTimes(1);

positionChangedSpy.mockClear();
newScrollContainer.appendChild(container);
oldScrollContainer.dispatchEvent(new Event('scroll', { bubbles: false }));
newScrollContainer.dispatchEvent(new Event('scroll', { bubbles: false }));

expect(positionChangedSpy).toHaveBeenCalledTimes(1);
});

it('should be able to instantiate SlickGrid with an external PubSub Service', () => {
const columns = [{ id: 'firstName', field: 'firstName', name: 'First Name' }] as Column[];
grid = new SlickGrid<any, Column>('#myGrid', [], columns, defaultOptions, pubSubServiceStub);
Expand Down Expand Up @@ -7112,6 +7135,26 @@ describe('SlickGrid core file', () => {
expect(secondItemAgeCell.classList.contains('highlight')).toBeFalsy();
});

it('should merge keyed CSS style overlays before creating a cell', () => {
grid = new SlickGrid<any, Column>(container, items, columns, { ...defaultOptions, enableCellNavigation: true });
grid.addCellCssStyles('primary', { 0: { age: 'primary-highlight' } });
grid.addCellCssStyles('secondary', { 0: { age: 'secondary-highlight' } });

const initialRow = document.createElement('div');
(grid as any).appendCellHtml(initialRow, 0, 1, 1, 1, null, items[0]);
expect(initialRow.firstElementChild?.classList.contains('primary-highlight')).toBe(true);
expect(initialRow.firstElementChild?.classList.contains('secondary-highlight')).toBe(true);

grid.setCellCssStyles('primary', { 0: { age: 'replacement-highlight' } });
grid.removeCellCssStyles('secondary');

const updatedRow = document.createElement('div');
(grid as any).appendCellHtml(updatedRow, 0, 1, 1, 1, null, items[0]);
expect(updatedRow.firstElementChild?.classList.contains('primary-highlight')).toBe(false);
expect(updatedRow.firstElementChild?.classList.contains('secondary-highlight')).toBe(false);
expect(updatedRow.firstElementChild?.classList.contains('replacement-highlight')).toBe(true);
});

it('should removeCellCssStyles of all matching keys by predicate', () => {
grid = new SlickGrid<any, Column>(container, items, columns, { ...defaultOptions, enableCellNavigation: true });
const onCellStyleSpy = vi.spyOn(grid.onCellCssStylesChanged, 'notify');
Expand Down
53 changes: 35 additions & 18 deletions packages/common/src/core/slickGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,6 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
protected _viewport!: HTMLDivElement[];
protected _canvas!: HTMLDivElement[];
protected _style?: HTMLStyleElement;
protected _boundAncestors: HTMLElement[] = [];
protected stylesheet?: { cssRules: Array<{ selectorText: string }>; rules: Array<{ selectorText: string }> } | null;
protected columnCssRulesL?: Array<{ selectorText: string }>;
protected columnCssRulesR?: Array<{ selectorText: string }>;
Expand Down Expand Up @@ -462,6 +461,7 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e

protected plugins: SlickPlugin[] = [];
protected cellCssClasses: CssStyleHash = {};
protected cellCssClassesByCell: CssStyleHash = {};

protected columnsById: Record<string, number> = {};
protected visibleColumnsById: Record<string, number> = {};
Expand Down Expand Up @@ -1574,16 +1574,18 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
return this.absoluteColumnMinWidth;
}

// TODO: this is static. we need to handle page mutation.
protected bindAncestorScrollEvents(): void {
let elem: HTMLElement | null = this.hasFrozenRows && !this._options.frozenBottom ? this._canvasBottomL : this._canvasTopL;
while ((elem = elem!.parentNode as HTMLElement) !== document.body && elem) {
// bind to scroll containers only
if (elem === this._viewportTopL || elem.scrollWidth !== elem.clientWidth || elem.scrollHeight !== elem.clientHeight) {
this._boundAncestors.push(elem);
this._bindingEventService.bind(elem, 'scroll', this.handleActiveCellPositionChange.bind(this));
}
}
this._bindingEventService.bind(
document,
'scroll',
(event) => {
const target = event.target;
if (this._viewport.includes(target as HTMLDivElement) || (target instanceof Node && target.contains(this._container))) {
this.handleActiveCellPositionChange();
}
},
true
);
}

/**
Expand Down Expand Up @@ -3131,8 +3133,6 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
this.sortableSideLeftInstance.destroy();
}

this._boundAncestors.length = 0; // reset array

this._focusSink?.remove();
this._focusSink2?.remove();

Expand Down Expand Up @@ -4414,12 +4414,10 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
cellCss += ' active';
}

// TODO: merge them together in the setter
Object.keys(this.cellCssClasses).forEach((key) => {
if (this.cellCssClasses[key][row]?.[m.id]) {
cellCss += ` ${this.cellCssClasses[key][row][m.id]}`;
}
});
const cellCssClasses = this.cellCssClassesByCell[row]?.[m.id];
if (cellCssClasses) {
cellCss += ` ${cellCssClasses}`;
}

let value: any = null;
let formatterResult: FormatterResultWithHtml | FormatterResultWithText | HTMLElement | DocumentFragment | string = '';
Expand Down Expand Up @@ -6089,6 +6087,22 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
}
}

/** Merges the keyed CSS overlays once on update, rather than for every rendered cell. */
protected updateCellCssClassesByCell(): void {
this.cellCssClassesByCell = {};

Object.values(this.cellCssClasses).forEach((hash) => {
Object.entries(hash).forEach(([row, cellClasses]) => {
const mergedRowClasses = (this.cellCssClassesByCell[row] ??= {});
Object.entries(cellClasses).forEach(([columnId, cssClasses]) => {
if (cssClasses) {
mergedRowClasses[columnId] = mergedRowClasses[columnId] ? `${mergedRowClasses[columnId]} ${cssClasses}` : cssClasses;
}
});
});
});
}

/**
* Adds an "overlay" of CSS classes to cell DOM elements. SlickGrid can have many such overlays associated with different keys and they are frequently used by plugins. For example, SlickGrid uses this method internally to decorate selected cells with selectedCellCssClass (see options).
* @param {String} key A unique key you can use in calls to setCellCssStyles and removeCellCssStyles. If a hash with that key has already been set, an exception will be thrown.
Expand All @@ -6105,6 +6119,7 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
}

this.cellCssClasses[key] = hash;
this.updateCellCssClassesByCell();
this.updateCellCssStylesOnRenderedRows(hash, null);
this.triggerEvent(this.onCellCssStylesChanged, { key, hash, grid: this });
}
Expand All @@ -6117,6 +6132,7 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
if (this.cellCssClasses[key]) {
this.updateCellCssStylesOnRenderedRows(null, this.cellCssClasses[key]);
delete this.cellCssClasses[key];
this.updateCellCssClassesByCell();
this.triggerEvent(this.onCellCssStylesChanged, { key, hash: null, grid: this });
}
}
Expand All @@ -6143,6 +6159,7 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
setCellCssStyles(key: string, hash: CssStyleHash): void {
const prevHash = this.cellCssClasses[key];
this.cellCssClasses[key] = hash;
this.updateCellCssClassesByCell();
this.updateCellCssStylesOnRenderedRows(hash, prevHash);
this.triggerEvent(this.onCellCssStylesChanged, { key, hash, grid: this });
}
Expand Down
Loading