Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): expose all SlickEvent via internal PubSub Service #1311

Merged
merged 1 commit into from
Jan 6, 2024
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
15 changes: 13 additions & 2 deletions examples/vite-demo-vanilla-bundle/src/examples/example19.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,28 @@ export default class Example19 {
this.sgb = new Slicker.GridBundle(document.querySelector(`.grid19`) as HTMLDivElement, this.columnDefinitions, { ...ExampleGridOptions, ...this.gridOptions }, this.dataset);
document.body.classList.add('salesforce-theme');

// bind any of the grid events
// bind any of the grid events, e.g. onSelectedRangesChanged to show selection range on screen
const cellSelectionModel = this.sgb.slickGrid!.getSelectionModel();
this._eventHandler.subscribe(cellSelectionModel!.onSelectedRangesChanged, (_e, args) => {
const targetRange = document.querySelector('#selectionRange') as HTMLSpanElement;
targetRange.textContent = '';

for (const slickRange of args) {
targetRange.textContent += JSON.stringify(slickRange);
}
});

// or subscribe to any events via internal PubSub (from `this.sgb.instances?.eventPubSubService` or `this.sgb.slickGrid?.getPubSubService()`)
// Note: SlickEvent use the structure:: { eventData: SlickEventData; args: any; }
// while other regular PubSub events use the structure:: args: any;
// this.sgb.instances?.eventPubSubService?.subscribe('onSelectedRangesChanged', (e) => console.log(e));
// this.sgb.slickGrid?.getPubSubService()?.subscribe('onSelectedRangesChanged', (e) => {
// const targetRange = document.querySelector('#selectionRange') as HTMLSpanElement;
// targetRange.textContent = '';
// for (const slickRange of e.args) {
// targetRange.textContent += JSON.stringify(slickRange);
// }
// });

const hash = {
0: {},
1: {
Expand Down
19 changes: 19 additions & 0 deletions packages/common/src/core/__tests__/slickGrid.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { BasePubSubService } from '@slickgrid-universal/event-pub-sub';
import { InputEditor, LongTextEditor } from '../../editors';
import { SlickCellSelectionModel, SlickRowSelectionModel } from '../../extensions';
import { Column, FormatterResultWithHtml, FormatterResultWithText, GridOption } from '../../interfaces';
Expand All @@ -7,6 +8,13 @@ import { SlickGrid } from '../slickGrid';

jest.useFakeTimers();

const pubSubServiceStub = {
publish: jest.fn(),
subscribe: jest.fn(),
unsubscribe: jest.fn(),
unsubscribeAll: jest.fn(),
} as BasePubSubService;

const DEFAULT_COLUMN_HEIGHT = 25;
const DEFAULT_GRID_HEIGHT = 600;
const DEFAULT_GRID_WIDTH = 800;
Expand Down Expand Up @@ -56,6 +64,17 @@ describe('SlickGrid core file', () => {
expect(grid.getContainerNode()).toEqual(container);
});

it('should be able to instantiate SlickGrid with an external PubSub Service', () => {
const columns = [{ id: 'firstName', field: 'firstName', name: 'First Name' }] as Column[];
const options = { enableCellNavigation: true, devMode: { ownerNodeIndex: 0 } } as GridOption;
grid = new SlickGrid<any, Column>('#myGrid', [], columns, options, pubSubServiceStub);
grid.init();

expect(grid).toBeTruthy();
expect(grid.getData()).toEqual([]);
expect(grid.getPubSubService()).toEqual(pubSubServiceStub);
});

it('should be able to instantiate SlickGrid and get columns', () => {
const columns = [{ id: 'firstName', field: 'firstName', name: 'First Name', headerCssClass: 'header-class', headerCellAttrs: { 'some-attr': 3 } }] as Column[];
const options = { enableCellNavigation: true, devMode: { ownerNodeIndex: 0 } } as GridOption;
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/core/slickCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ export class Utils {
* @param {BasePubSub} [pubSubService]
* @param {*} scope
*/
public static addSlickEventPubSubWhenDefined<T = any>(pubSub?: BasePubSub, scope?: T) {
public static addSlickEventPubSubWhenDefined<T = any>(pubSub: BasePubSub, scope: T) {
if (pubSub) {
for (const prop in scope) {
if (scope[prop] instanceof SlickEvent && typeof (scope[prop] as SlickEvent).setPubSubService === 'function') {
Expand Down
24 changes: 14 additions & 10 deletions packages/common/src/core/slickGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
protected sortableSideLeftInstance?: Sortable;
protected sortableSideRightInstance?: Sortable;
protected logMessageMaxCount = 30;
protected _pubSubService?: BasePubSub;

/**
* Creates a new instance of the grid.
Expand All @@ -471,6 +472,15 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
* @param {Object} [externalPubSub] - optional External PubSub Service to use by SlickEvent
**/
constructor(protected readonly container: HTMLElement | string, protected data: CustomDataView<TData> | TData[], protected columns: C[], options: Partial<O>, protected readonly externalPubSub?: BasePubSub) {
this._container = typeof this.container === 'string'
? document.querySelector(this.container) as HTMLDivElement
: this.container;

if (!this._container) {
throw new Error(`SlickGrid requires a valid container, ${this.container} does not exist in the DOM.`);
}

this._pubSubService = externalPubSub;
this.onActiveCellChanged = new SlickEvent<OnActiveCellChangedEventArgs>('onActiveCellChanged', externalPubSub);
this.onActiveCellPositionChanged = new SlickEvent<SlickGridEventData>('onActiveCellPositionChanged', externalPubSub);
this.onAddNewRow = new SlickEvent<OnAddNewRowEventArgs>('onAddNewRow', externalPubSub);
Expand Down Expand Up @@ -584,16 +594,6 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
}

protected initialize(options: Partial<O>) {
if (typeof this.container === 'string') {
this._container = document.querySelector(this.container) as HTMLDivElement;
} else {
this._container = this.container;
}

if (!this._container) {
throw new Error(`SlickGrid requires a valid container, ${this.container} does not exist in the DOM.`);
}

// calculate these only once and share between grid instances
if (options?.mixinDefaults) {
// use provided options and then assign defaults
Expand Down Expand Up @@ -968,6 +968,10 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
return undefined;
}

getPubSubService(): BasePubSub | undefined {
return this._pubSubService;
}

/**
* Unregisters a current selection model and registers a new one. See the definition of SelectionModel for more information.
* @param {Object} selectionModel A SelectionModel.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@ import { SlickCellSelectionModel } from '../slickCellSelectionModel';
import { SlickCellExternalCopyManager } from '../slickCellExternalCopyManager';
import { InputEditor } from '../../editors/inputEditor';
import { SlickEvent, SlickEventData, SlickGrid, SlickRange } from '../../core/index';
import { BasePubSubService } from '@slickgrid-universal/event-pub-sub';

jest.mock('flatpickr', () => { });

const pubSubServiceStub = {
publish: jest.fn(),
subscribe: jest.fn(),
unsubscribe: jest.fn(),
unsubscribeAll: jest.fn(),
} as BasePubSubService;

const mockGetSelectionModel = {
getSelectedRanges: jest.fn(),
};
Expand All @@ -22,6 +30,7 @@ const gridStub = {
getData: jest.fn(),
getDataItem: jest.fn(),
getDataLength: jest.fn(),
getPubSubService: () => pubSubServiceStub,
getEditorLock: () => ({
isActive: () => false,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'jest-extended';
import type { GridOption } from '../../interfaces/index';
import { SlickCellRangeSelector } from '../slickCellRangeSelector';
import { SlickEvent, SlickGrid } from '../../core/index';
import { BasePubSubService } from '@slickgrid-universal/event-pub-sub';

const GRID_UID = 'slickgrid_12345';
jest.mock('flatpickr', () => { });
Expand All @@ -19,6 +20,13 @@ const mockGridOptions = {
rowHeight: 30,
} as GridOption;

const pubSubServiceStub = {
publish: jest.fn(),
subscribe: jest.fn(),
unsubscribe: jest.fn(),
unsubscribeAll: jest.fn(),
} as BasePubSubService;

const getEditorLockMock = {
commitCurrentEdit: jest.fn(),
isActive: jest.fn(),
Expand All @@ -35,6 +43,7 @@ const gridStub = {
getCellFromPoint: jest.fn(),
getCellNodeBox: jest.fn(),
getDisplayedScrollbarDimensions: jest.fn(),
getPubSubService: () => pubSubServiceStub,
getEditorLock: () => getEditorLockMock,
getOptions: () => mockGridOptions,
getUID: () => GRID_UID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { GridOption } from '../../interfaces/index';
import { SlickCellRangeSelector } from '../slickCellRangeSelector';
import { SlickCellSelectionModel } from '../slickCellSelectionModel';
import { SlickEvent, SlickGrid, SlickRange } from '../../core/index';
import { BasePubSubService } from '@slickgrid-universal/event-pub-sub';

const GRID_UID = 'slickgrid_12345';
const NB_ITEMS = 200;
Expand Down Expand Up @@ -44,7 +45,14 @@ const mockColumns = [
{ id: 'firstName', field: 'firstName' },
{ id: 'lastName', field: 'lastName' },
{ id: 'age', field: 'age' },
]
];

const pubSubServiceStub = {
publish: jest.fn(),
subscribe: jest.fn(),
unsubscribe: jest.fn(),
unsubscribeAll: jest.fn(),
} as BasePubSubService;

const gridStub = {
canCellBeSelected: jest.fn(),
Expand All @@ -54,6 +62,7 @@ const gridStub = {
getCellFromEvent: jest.fn(),
getCellFromPoint: jest.fn(),
getCellNodeBox: jest.fn(),
getPubSubService: () => pubSubServiceStub,
getColumns: () => mockColumns,
getData: () => dataViewStub,
getDataLength: jest.fn(),
Expand Down Expand Up @@ -556,8 +565,8 @@ describe('CellSelectionModel Plugin', () => {

plugin.init(gridStub);
const output = plugin.rangesAreEqual(
[{ fromCell: 1, fromRow: 2, toCell: 3, toRow: 4 }],
[{ fromCell: 1, fromRow: 2, toCell: 3, toRow: 4 }]
[{ fromCell: 1, fromRow: 2, toCell: 3, toRow: 4 } as SlickRange],
[{ fromCell: 1, fromRow: 2, toCell: 3, toRow: 4 } as SlickRange]
);

expect(output).toBeTrue();
Expand All @@ -568,8 +577,8 @@ describe('CellSelectionModel Plugin', () => {

plugin.init(gridStub);
const output = plugin.rangesAreEqual(
[{ fromCell: 1, fromRow: 2, toCell: 3, toRow: 4 }],
[{ fromCell: 2, fromRow: 3, toCell: 3, toRow: 4 }]
[{ fromCell: 1, fromRow: 2, toCell: 3, toRow: 4 } as SlickRange],
[{ fromCell: 2, fromRow: 3, toCell: 3, toRow: 4 } as SlickRange]
);

expect(output).toBeFalse();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SlickEvent, SlickGrid, SlickRange } from '../../core/index';
import type { Column, GridOption } from '../../interfaces/index';
import { SlickCellRangeSelector } from '../slickCellRangeSelector';
import { SlickRowSelectionModel } from '../slickRowSelectionModel';
import { BasePubSubService } from '@slickgrid-universal/event-pub-sub';

const GRID_UID = 'slickgrid_12345';
jest.mock('flatpickr', () => { });
Expand All @@ -26,6 +27,13 @@ const mockGridOptions = {
multiSelect: true,
} as GridOption;

const pubSubServiceStub = {
publish: jest.fn(),
subscribe: jest.fn(),
unsubscribe: jest.fn(),
unsubscribeAll: jest.fn(),
} as BasePubSubService;

const getEditorLockMock = {
commitCurrentEdit: jest.fn(),
isActive: jest.fn(),
Expand All @@ -41,6 +49,7 @@ const gridStub = {
getCellNodeBox: jest.fn(),
getColumns: jest.fn(),
getDataLength: jest.fn(),
getPubSubService: () => pubSubServiceStub,
getEditorLock: () => getEditorLockMock,
getOptions: () => mockGridOptions,
getUID: () => GRID_UID,
Expand Down
16 changes: 11 additions & 5 deletions packages/common/src/extensions/slickCellExternalCopyManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createDomElement, stripTags } from '@slickgrid-universal/utils';

import type { Column, ExcelCopyBufferOption, ExternalCopyClipCommand, OnEventArgs } from '../interfaces/index';
import { SlickEvent, SlickEventData, SlickEventHandler, type SlickGrid, SlickRange, SlickDataView } from '../core/index';
import { SlickEvent, SlickEventData, SlickEventHandler, type SlickGrid, SlickRange, SlickDataView, Utils as SlickUtils } from '../core/index';

// using external SlickGrid JS libraries
const CLEAR_COPY_SELECTION_DELAY = 2000;
Expand All @@ -17,10 +17,10 @@ const CLIPBOARD_PASTE_DELAY = 100;
*/
export class SlickCellExternalCopyManager {
pluginName: 'CellExternalCopyManager' = 'CellExternalCopyManager' as const;
onCopyCells = new SlickEvent<{ ranges: SlickRange[]; }>();
onCopyCancelled = new SlickEvent<{ ranges: SlickRange[]; }>();
onPasteCells = new SlickEvent<{ ranges: SlickRange[]; }>();
onBeforePasteCell = new SlickEvent<{ cell: number; row: number; item: any; columnDef: Column; value: any; }>();
onCopyCells = new SlickEvent<{ ranges: SlickRange[]; }>('onCopyCells');
onCopyCancelled = new SlickEvent<{ ranges: SlickRange[]; }>('onCopyCancelled');
onPasteCells = new SlickEvent<{ ranges: SlickRange[]; }>('onPasteCells');
onBeforePasteCell = new SlickEvent<{ cell: number; row: number; item: any; columnDef: Column; value: any; }>('onBeforePasteCell');

protected _addonOptions!: ExcelCopyBufferOption;
protected _bodyElement = document.body;
Expand Down Expand Up @@ -54,6 +54,12 @@ export class SlickCellExternalCopyManager {
this._onCopyInit = this._addonOptions.onCopyInit || undefined;
this._onCopySuccess = this._addonOptions.onCopySuccess || undefined;

// add PubSub instance to all SlickEvent
const pubSub = grid.getPubSubService();
if (pubSub) {
SlickUtils.addSlickEventPubSubWhenDefined(pubSub, this);
}

this._eventHandler.subscribe(this._grid.onKeyDown, this.handleKeyDown.bind(this));

// we need a cell selection model
Expand Down
14 changes: 10 additions & 4 deletions packages/common/src/extensions/slickCellRangeSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import type {
OnScrollEventArgs,
} from '../interfaces/index';
import { SlickCellRangeDecorator } from './index';
import { SlickEvent, SlickEventData, SlickEventHandler, type SlickGrid, SlickRange } from '../core/index';
import { SlickEvent, SlickEventData, SlickEventHandler, type SlickGrid, SlickRange, Utils as SlickUtils } from '../core/index';

export class SlickCellRangeSelector {
pluginName: 'CellRangeSelector' = 'CellRangeSelector' as const;
onBeforeCellRangeSelected = new SlickEvent<{ row: number; cell: number; }>();
onCellRangeSelecting = new SlickEvent<{ range: SlickRange; }>();
onCellRangeSelected = new SlickEvent<{ range: SlickRange; }>();
onBeforeCellRangeSelected = new SlickEvent<{ row: number; cell: number; }>('onBeforeCellRangeSelected');
onCellRangeSelecting = new SlickEvent<{ range: SlickRange; }>('onCellRangeSelecting');
onCellRangeSelected = new SlickEvent<{ range: SlickRange; }>('onCellRangeSelected');

protected _activeCanvas!: HTMLElement;
protected _options!: CellRangeSelectorOption;
Expand Down Expand Up @@ -88,6 +88,12 @@ export class SlickCellRangeSelector {
this._gridOptions = grid.getOptions();
this._gridUid = grid.getUID();

// add PubSub instance to all SlickEvent
const pubSub = grid.getPubSubService();
if (pubSub) {
SlickUtils.addSlickEventPubSubWhenDefined(pubSub, this);
}

this._eventHandler
.subscribe(this._grid.onDrag, this.handleDrag.bind(this))
.subscribe(this._grid.onDragInit, this.handleDragInit.bind(this))
Expand Down
9 changes: 8 additions & 1 deletion packages/common/src/extensions/slickCellSelectionModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface CellSelectionModelOption {
}

export class SlickCellSelectionModel implements SelectionModel {
onSelectedRangesChanged = new SlickEvent<SlickRange[]>();
onSelectedRangesChanged = new SlickEvent<SlickRange[]>('onSelectedRangesChanged');
pluginName: 'CellSelectionModel' = 'CellSelectionModel' as const;

protected _addonOptions?: CellSelectionModelOption;
Expand Down Expand Up @@ -55,6 +55,13 @@ export class SlickCellSelectionModel implements SelectionModel {
this._dataView = grid?.getData() ?? {} as SlickDataView;
}
this._addonOptions = { ...this._defaults, ...this._addonOptions } as CellSelectionModelOption;

// add PubSub instance to all SlickEvent
const pubSub = grid.getPubSubService();
if (pubSub) {
this.onSelectedRangesChanged.setPubSubService(pubSub);
}

this._eventHandler
.subscribe(this._grid.onActiveCellChanged, this.handleActiveCellChange.bind(this))
.subscribe(this._grid.onKeyDown, this.handleKeyDown.bind(this))
Expand Down
5 changes: 4 additions & 1 deletion packages/common/src/extensions/slickColumnPicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { SlickEvent, SlickEventHandler, type SlickGrid } from '../core/index';
* @constructor
*/
export class SlickColumnPicker {
onColumnsChanged = new SlickEvent<OnColumnsChangedArgs>();
onColumnsChanged = new SlickEvent<OnColumnsChangedArgs>('onColumnsChanged');

protected _areVisibleColumnDifferent = false;
protected _bindEventService: BindingEventService;
Expand Down Expand Up @@ -91,6 +91,9 @@ export class SlickColumnPicker {
this._gridUid = this.grid.getUID() ?? '';
this.gridOptions.columnPicker = { ...this._defaults, ...this.gridOptions.columnPicker };

// add PubSub instance to all SlickEvent
this.onColumnsChanged.setPubSubService(this.pubSubService);

// localization support for the picker
this.addonOptions.columnTitle = this.extensionUtility.getPickerTitleOutputString('columnTitle', 'columnPicker');
this.addonOptions.forceFitTitle = this.extensionUtility.getPickerTitleOutputString('forceFitTitle', 'columnPicker');
Expand Down
5 changes: 4 additions & 1 deletion packages/common/src/extensions/slickDraggableGrouping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class SlickDraggableGrouping {
) {
this._bindingEventService = new BindingEventService();
this._eventHandler = new SlickEventHandler();
this.onGroupChanged = new SlickEvent<{ caller?: string; groupColumns: Grouping[]; }>();
this.onGroupChanged = new SlickEvent<{ caller?: string; groupColumns: Grouping[]; }>('onGroupChanged');
}

get addonOptions(): DraggableGroupingOption {
Expand Down Expand Up @@ -134,6 +134,9 @@ export class SlickDraggableGrouping {
this._dropzoneElm = grid.getPreHeaderPanel();
this._dropzoneElm.classList.add('slick-dropzone');

// add PubSub instance to all SlickEvent
this.onGroupChanged.setPubSubService(this.pubSubService);

// add optional group "Toggle All" with its button & text when provided
if (!this._addonOptions.hideToggleAllButton) {
this._groupToggler = createDomElement('div', {
Expand Down
Loading