Skip to content

Commit

Permalink
chore: add more unit tests & code refactoring AutoTooltip plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
ghiscoding committed Jun 22, 2021
1 parent 4c59ba1 commit 43011cb
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@ export class Example4 {
container: '.demo-container',
},
enableAutoTooltip: true,
autoTooltipOptions: {
enableForHeaderCells: true
},
enableAutoSizeColumns: true,
enableAutoResize: true,
enableCellNavigation: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ export class Example10 {
const presetHighestDay = moment().add(20, 'days').format('YYYY-MM-DD');

this.gridOptions = {
enableAutoTooltip: true,
autoTooltipOptions: {
enableForHeaderCells: true
},
enableTranslate: true,
translater: this.translateService, // pass the TranslateService instance to the grid
enableAutoResize: false,
Expand Down
4 changes: 2 additions & 2 deletions packages/common/src/interfaces/autoTooltipOption.interface.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
export interface AutoTooltipOption {
/** are tooltip enabled for all cells? */
enableForCells: boolean;
enableForCells?: boolean;

/** are tooltip enabled for column headers */
enableForHeaderCells: boolean;
enableForHeaderCells?: boolean;

/** what is the maximum tooltip length in pixels (only type the number) */
maxToolTipLength?: number;
Expand Down
103 changes: 76 additions & 27 deletions packages/common/src/plugins/__tests__/autoTooltips.plugin.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { AutoTooltipOption, Column, GridOption, SlickGrid, SlickNamespace } from '../../interfaces/index';
import { AutoTooltipOption, Column, SlickGrid, SlickNamespace } from '../../interfaces/index';
import { SharedService } from '../../services/shared.service';
import { AutoTooltipsPlugin } from '../autoTooltips.plugin';

declare const Slick: SlickNamespace;

let pluginOptions: AutoTooltipOption = {
enableForCells: true,
enableForHeaderCells: true,
Expand All @@ -19,11 +20,6 @@ const gridStub = {
onMouseEnter: new Slick.Event(),
} as unknown as SlickGrid;

const mockAddon = jest.fn().mockImplementation(() => ({
init: jest.fn(),
destroy: jest.fn()
}));

const mockColumns = [ // The column definitions
{ name: 'Short', field: 'short', width: 100 },
{ name: 'Medium', field: 'medium', width: 100 },
Expand All @@ -34,8 +30,6 @@ const mockColumns = [ // The column definitions
] as Column[];

describe('AutoTooltip Plugin', () => {
jest.mock('slickgrid/plugins/slick.autotooltips', () => mockAddon);
Slick.AutoTooltips = mockAddon;
let plugin: AutoTooltipsPlugin;

beforeEach(() => {
Expand All @@ -58,13 +52,53 @@ describe('AutoTooltip Plugin', () => {
});
});

describe('onMouseEnter event', () => {
describe('plugins - autotooltips - header', () => {
beforeEach(() => {
jest.spyOn(SharedService.prototype, 'slickGrid', 'get').mockReturnValue(gridStub);
});

afterEach(() => {
plugin.destroy();
plugin.dispose();
});

it('should expect title is empty when header column has enough width', () => {
const mockNodeElm = document.createElement('div');
mockNodeElm.title = '';
mockNodeElm.textContent = 'some text';
jest.spyOn(gridStub, 'getCellFromEvent').mockReturnValue({ row: 1, cell: 2 });
jest.spyOn(gridStub, 'getCellNode').mockReturnValue(mockNodeElm);
Object.defineProperty(mockNodeElm, 'clientWidth', { writable: true, configurable: true, value: 150 });
Object.defineProperty(mockNodeElm, 'scrollWidth', { writable: true, configurable: true, value: 100 });

gridStub.onMouseEnter.notify({ grid: gridStub }, new Slick.EventData());

expect(mockNodeElm.title).toBe('');
});

it('title is present when header column is cut off', () => {
const mockNodeElm = document.createElement('div');
mockNodeElm.title = '';
mockNodeElm.textContent = 'some text';
jest.spyOn(gridStub, 'getCellFromEvent').mockReturnValue({ row: 1, cell: 2 });
jest.spyOn(gridStub, 'getCellNode').mockReturnValue(mockNodeElm);
Object.defineProperty(mockNodeElm, 'clientWidth', { writable: true, configurable: true, value: 150 });
Object.defineProperty(mockNodeElm, 'scrollWidth', { writable: true, configurable: true, value: 100 });

const eventData = new Slick.EventData();
gridStub.onMouseEnter.notify({ grid: gridStub }, new Slick.EventData());
gridStub.onHeaderMouseEnter.notify({ column: mockColumns[4], grid: gridStub }, eventData);

expect(mockNodeElm.title).toBe('');
});
});

describe('plugins - autotooltips - max tooltip', () => {
beforeEach(() => {
jest.spyOn(SharedService.prototype, 'slickGrid', 'get').mockReturnValue(gridStub);
});

afterEach(() => {
plugin.dispose();
});

it('title is empty when cell text has enough room', () => {
Expand Down Expand Up @@ -94,12 +128,6 @@ describe('AutoTooltip Plugin', () => {

expect(mockNodeElm.title).toBe('my super very lon...');
});
});

describe('onHeaderMouseEnter event', () => {
beforeEach(() => {
jest.spyOn(SharedService.prototype, 'slickGrid', 'get').mockReturnValue(gridStub);
});

it('title is empty when header column has enough width', () => {
const mockNodeElm = document.createElement('div');
Expand All @@ -126,25 +154,46 @@ describe('AutoTooltip Plugin', () => {
const mockNodeElm = document.createElement('div');
const mockHeaderElm = document.createElement('div');
const mockHeaderColElm = document.createElement('div');
mockHeaderColElm.className = 'slick-header-column';
mockNodeElm.className = 'slick-header-column';
mockHeaderColElm.className = 'slick-column-name';
mockHeaderElm.title = '';
mockHeaderElm.textContent = 'short text';
mockNodeElm.appendChild(mockHeaderElm);
mockHeaderElm.appendChild(mockHeaderColElm);
jest.spyOn(gridStub, 'getCellFromEvent').mockReturnValue({ row: 1, cell: 2 });
jest.spyOn(gridStub, 'getCellNode').mockReturnValue(mockNodeElm);
Object.defineProperty(mockNodeElm, 'clientWidth', { writable: true, configurable: true, value: 140 });
Object.defineProperty(mockNodeElm, 'scrollWidth', { writable: true, configurable: true, value: 175 });
Object.defineProperty(mockHeaderColElm, 'clientWidth', { writable: true, configurable: true, value: 50 });
Object.defineProperty(mockHeaderColElm, 'scrollWidth', { writable: true, configurable: true, value: 175 });
Object.defineProperty(mockNodeElm, 'clientWidth', { writable: true, configurable: true, value: 144 });
Object.defineProperty(mockHeaderColElm, 'clientWidth', { writable: true, configurable: true, value: 130 });

const eventData = new Slick.EventData();
Object.defineProperty(eventData, 'target', { writable: true, configurable: true, value: mockNodeElm });
gridStub.onMouseEnter.notify({ grid: gridStub }, new Slick.EventData());
gridStub.onHeaderMouseEnter.notify({ column: mockColumns[2], grid: gridStub }, eventData);
Object.defineProperty(eventData, 'target', { writable: true, configurable: true, value: mockHeaderColElm });
gridStub.onMouseEnter.notify({ grid: gridStub }, eventData);
gridStub.onHeaderMouseEnter.notify({ column: mockColumns[4], grid: gridStub }, eventData);

expect(mockNodeElm.title).toBe('short text');
expect(mockHeaderElm.title).toBe('');
expect(mockNodeElm.title).toBe('Long header creates tooltip');
});

it('title is not overridden when header column has pre-defined tooltip', () => {
const mockNodeElm = document.createElement('div');
const mockHeaderElm = document.createElement('div');
const mockHeaderColElm = document.createElement('div');
mockNodeElm.className = 'slick-header-column';
mockHeaderColElm.className = 'slick-column-name';
mockHeaderElm.title = '';
mockHeaderElm.textContent = 'short text';
mockNodeElm.appendChild(mockHeaderElm);
mockHeaderElm.appendChild(mockHeaderColElm);
jest.spyOn(gridStub, 'getCellFromEvent').mockReturnValue({ row: 1, cell: 2 });
jest.spyOn(gridStub, 'getCellNode').mockReturnValue(mockNodeElm);
Object.defineProperty(mockNodeElm, 'clientWidth', { writable: true, configurable: true, value: 144 });
Object.defineProperty(mockHeaderColElm, 'clientWidth', { writable: true, configurable: true, value: 130 });

const eventData = new Slick.EventData();
Object.defineProperty(eventData, 'target', { writable: true, configurable: true, value: mockHeaderColElm });
gridStub.onMouseEnter.notify({ grid: gridStub }, eventData);
gridStub.onHeaderMouseEnter.notify({ column: mockColumns[5], grid: gridStub }, eventData);

expect(mockNodeElm.title).toBe('');
});

it('title is present and truncated when cell text is cut off and too long', () => {
Expand All @@ -165,7 +214,7 @@ describe('AutoTooltip Plugin', () => {

const eventData = new Slick.EventData();
Object.defineProperty(eventData, 'target', { writable: true, configurable: true, value: mockNodeElm });
gridStub.onMouseEnter.notify({ grid: gridStub }, new Slick.EventData());
gridStub.onMouseEnter.notify({ grid: gridStub }, eventData);
gridStub.onHeaderMouseEnter.notify({ column: mockColumns[4], grid: gridStub }, eventData);

expect(mockNodeElm.title).toBe('Long header creat...');
Expand Down
24 changes: 18 additions & 6 deletions packages/common/src/plugins/autoTooltips.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import {
// using external SlickGrid JS libraries
declare const Slick: SlickNamespace;

/**
* AutoTooltips plugin to show/hide tooltips when columns are too narrow to fit content.
* @constructor
* @param {boolean} [options.enableForCells=true] - Enable tooltip for grid cells
* @param {boolean} [options.enableForHeaderCells=false] - Enable tooltip for header cells
* @param {number} [options.maxToolTipLength=null] - The maximum length for a tooltip
*/
export class AutoTooltipsPlugin {
private _eventHandler!: SlickEventHandler;
private _grid!: SlickGrid;
Expand Down Expand Up @@ -38,7 +45,7 @@ export class AutoTooltipsPlugin {

/** Initialize plugin. */
init(grid: SlickGrid) {
this._options = { ...this._defaults, ...this._options };
this._options = { ...this._defaults, ...this.options };
this._grid = grid;
if (this._options.enableForCells) {
const onMouseEnterHandler = this._grid.onMouseEnter;
Expand All @@ -50,8 +57,8 @@ export class AutoTooltipsPlugin {
}
}

/** Destroy (dispose) the SlickGrid 3rd party plugin */
destroy() {
/** Dispose (destroy) the SlickGrid 3rd party plugin */
dispose() {
this._eventHandler?.unsubscribeAll();
}

Expand Down Expand Up @@ -90,9 +97,14 @@ export class AutoTooltipsPlugin {
*/
private handleHeaderMouseEnter(event: Event, args: { column: Column; }) {
const column = args.column;
let node = (event.target as HTMLDivElement).querySelector<HTMLDivElement>('.slick-header-column');
if (node && !column?.toolTip) {
node.title = (node.clientWidth < node.scrollWidth) ? column.name ?? '' : '';
let node: HTMLDivElement | null;
const targetElm = (event.target as HTMLDivElement);

if (targetElm) {
node = targetElm.closest<HTMLDivElement>('.slick-header-column');
if (node && !(column?.toolTip)) {
node.title = (targetElm.clientWidth < node.clientWidth) ? column?.name ?? '' : '';
}
}
node = null;
}
Expand Down
3 changes: 3 additions & 0 deletions packages/common/src/styles/slick-grid.scss
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@

.slick-column-name {
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}

.slick-resizable-handle {
Expand Down

0 comments on commit 43011cb

Please sign in to comment.