Skip to content

Commit

Permalink
refactor: cleanup code & add missing unsubscribe
Browse files Browse the repository at this point in the history
- also make dropSide a better menu option
  • Loading branch information
ghiscoding committed Nov 6, 2021
1 parent 8bd3f4f commit 160d05e
Show file tree
Hide file tree
Showing 14 changed files with 61 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ export class Example1 {
// { command: '', divider: true, positionOrder: 72 },
// { command: 'hello', title: 'Hello', positionOrder: 69, action: (e, args) => alert('Hello World'), cssClass: 'red', tooltip: 'Hello World', iconCssClass: 'mdi mdi-close' },
// ],
alignDropSide: 'right',
// menuUsabilityOverride: () => false,
onBeforeMenuShow: () => {
console.log('onGridMenuBeforeMenuShow');
Expand Down Expand Up @@ -143,8 +142,8 @@ export class Example1 {
if (this.sgb2?.extensionService) {
const gridMenuInstance = this.sgb2.extensionService.getSlickgridAddonInstance(ExtensionName.gridMenu);
// open the external button Grid Menu, you can also optionally pass Grid Menu options as 2nd argument
// for example we want to align our external button on the left without affecting the menu within which will stay aligned on the right
gridMenuInstance.showGridMenu(e, { alignDropSide: 'left' });
// for example we want to align our external button on the right without affecting the menu within the grid which will stay aligned on the left
gridMenuInstance.showGridMenu(e, { dropSide: 'right' });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,6 @@ export class Example13 {
// frozenColumn: 2,
// frozenRow: 2,
headerButton: {
// when floating to left, you might want to inverse the icon orders
inverseOrder: true,
onCommand: (_e, args) => this.handleOnCommand(_e, args, 2)
}
};
Expand Down Expand Up @@ -193,6 +191,11 @@ export class Example13 {
]
};

// when floating to left, you might want to inverse the icon orders
if (gridNo === 2) {
this.columnDefinitions2[0].header?.buttons?.reverse();
}

// Set a button on the second column to demonstrate hover.
this[`columnDefinitions${gridNo}`][1].name = 'Hover me!';
this[`columnDefinitions${gridNo}`][1].header = {
Expand Down
24 changes: 22 additions & 2 deletions packages/common/src/controls/__tests__/slickGridMenu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ describe('GridMenuControl', () => {
expect(gridMenuElm.style.height).toBe('300px');
});

it('should open the Grid Menu via "showGridMenu" method from an external button which has span inside it and expect the Grid Menu still work', () => {
it('should open the Grid Menu via "showGridMenu" method from an external button which has span inside it and expect the Grid Menu still work, with drop aligned on left when defined', () => {
jest.spyOn(gridStub, 'getColumnIndex').mockReturnValue(undefined).mockReturnValue(1);
const repositionSpy = jest.spyOn(control, 'repositionMenu');

Expand All @@ -363,10 +363,30 @@ describe('GridMenuControl', () => {
spanBtnElm.textContent = 'Grid Menu';
Object.defineProperty(spanEvent, 'target', { writable: true, configurable: true, value: spanBtnElm });
Object.defineProperty(spanBtnElm, 'parentElement', { writable: true, configurable: true, value: buttonElm });
control.showGridMenu(spanEvent, { alignDropSide: 'left' });
control.showGridMenu(spanEvent, { dropSide: 'left' });
const gridMenuElm = document.querySelector('.slick-grid-menu') as HTMLDivElement;

expect(gridMenuElm.style.display).toBe('block');
expect(gridMenuElm.classList.contains('dropleft')).toBeTrue();
expect(repositionSpy).toHaveBeenCalledTimes(2);
});

it('should open the Grid Menu via "showGridMenu" method from an external button which has span inside it and expect the Grid Menu still work, with drop aligned on right when defined', () => {
jest.spyOn(gridStub, 'getColumnIndex').mockReturnValue(undefined).mockReturnValue(1);
const repositionSpy = jest.spyOn(control, 'repositionMenu');

control.init();
const spanEvent = new MouseEvent('click', { bubbles: true, cancelable: true, composed: false })
const spanBtnElm = document.createElement('span');
const buttonElm = document.createElement('button');
spanBtnElm.textContent = 'Grid Menu';
Object.defineProperty(spanEvent, 'target', { writable: true, configurable: true, value: spanBtnElm });
Object.defineProperty(spanBtnElm, 'parentElement', { writable: true, configurable: true, value: buttonElm });
control.showGridMenu(spanEvent, { dropSide: 'right' });
const gridMenuElm = document.querySelector('.slick-grid-menu') as HTMLDivElement;

expect(gridMenuElm.style.display).toBe('block');
expect(gridMenuElm.classList.contains('dropright')).toBeTrue();
expect(repositionSpy).toHaveBeenCalledTimes(2);
});

Expand Down
12 changes: 9 additions & 3 deletions packages/common/src/controls/slickGridMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class SlickGridMenu extends MenuBaseClass<GridMenu> {
onColumnsChanged = new Slick.Event();

protected _defaults = {
alignDropSide: 'right',
dropSide: 'left',
showButton: true,
hideForceFitButton: false,
hideSyncResizeButton: false,
Expand Down Expand Up @@ -342,11 +342,17 @@ export class SlickGridMenu extends MenuBaseClass<GridMenu> {
const nextPositionTop = menuIconOffset?.top ?? 0;
const nextPositionLeft = menuIconOffset?.right ?? 0;
const menuMarginBottom = ((addonOptions?.marginBottom !== undefined) ? addonOptions.marginBottom : this._defaults.marginBottom) || 0;
const calculatedLeftPosition = addonOptions?.alignDropSide === 'left' ? nextPositionLeft - buttonWidth : nextPositionLeft - currentMenuWidth;
const calculatedLeftPosition = addonOptions?.dropSide === 'right' ? nextPositionLeft - buttonWidth : nextPositionLeft - currentMenuWidth;

this._menuElm.style.top = `${nextPositionTop + buttonElm.offsetHeight}px`; // top position has to include button height so the menu is placed just below it
this._menuElm.style.left = `${calculatedLeftPosition}px`;
this._menuElm.classList.add(addonOptions?.alignDropSide === 'left' ? 'dropleft' : 'dropright');
if (addonOptions.dropSide === 'left') {
this._menuElm.classList.remove('dropright');
this._menuElm.classList.add('dropleft');
} else {
this._menuElm.classList.remove('dropleft');
this._menuElm.classList.add('dropright');
}
this._menuElm.appendChild(this._listElm);

if (contentMinWidth! > 0) {
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/global-grid-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export const GlobalGridOptions: GridOption = {
forceFitColumns: false,
frozenHeaderWidthCalcDifferential: 1,
gridMenu: {
alignDropSide: 'right',
dropSide: 'left',
commandLabels: {
clearAllFiltersCommandKey: 'CLEAR_ALL_FILTERS',
clearAllSortingCommandKey: 'CLEAR_ALL_SORTING',
Expand Down
12 changes: 6 additions & 6 deletions packages/common/src/interfaces/cellMenuOption.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ import {
} from './index';

export interface CellMenuOption {
/** Defaults to "bottom", user can optionally force the Cell Menu drop to be aligned to the top or bottom. */
alignDropDirection?: 'top' | 'bottom';

/** Defaults to "right", user can optionally force the Cell Menu drop to be aligned to the left or right. */
alignDropSide?: 'left' | 'right';

/** Defaults to true, Auto-align dropup or dropdown menu to the left or right depending on grid viewport available space */
autoAdjustDrop?: boolean;

Expand All @@ -32,6 +26,12 @@ export interface CellMenuOption {
/** Same as "commandTitle", except that it's a translation key which can be used on page load and/or when switching locale */
commandTitleKey?: string;

/** Defaults to "bottom", user can optionally force the Cell Menu drop to be aligned to the top or bottom. */
dropDirection?: 'top' | 'bottom';

/** Defaults to "right", user can optionally force the Cell Menu drop to be aligned to the left or right. */
dropSide?: 'left' | 'right';

/** Defaults to false, Hide the Close button on top right */
hideCloseButton?: boolean;

Expand Down
12 changes: 6 additions & 6 deletions packages/common/src/interfaces/contextMenuOption.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ import {
} from './index';

export interface ContextMenuOption {
/** Defaults to "bottom", user can optionally force the Cell Menu drop to be aligned to the top or bottom. */
alignDropDirection?: 'top' | 'bottom';

/** Defaults to "right", user can optionally force the Cell Menu drop to be aligned to the left or right. */
alignDropSide?: 'left' | 'right';

/** Defaults to true, Auto-align dropup or dropdown menu to the left or right depending on grid viewport available space */
autoAdjustDrop?: boolean;

Expand All @@ -35,6 +29,12 @@ export interface ContextMenuOption {
/** Same as "commandTitle", except that it's a translation key which can be used on page load and/or when switching locale */
commandTitleKey?: string;

/** Defaults to "bottom", user can optionally force the Cell Menu drop to be aligned to the top or bottom. */
dropDirection?: 'top' | 'bottom';

/** Defaults to "right", user can optionally force the Cell Menu drop to be aligned to the left or right. */
dropSide?: 'left' | 'right';

/** Defaults to false, hide the "Clear Grouping" command in the menu (Grid Option "enableGrouping: true" has to be enabled) */
hideClearAllGrouping?: boolean;

Expand Down
6 changes: 3 additions & 3 deletions packages/common/src/interfaces/gridMenuOption.interface.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { Column, GridMenuCallbackArgs, GridMenuCommandItemCallbackArgs, GridMenuLabel, GridOption, MenuCallbackArgs, MenuCommandItem, } from './index';

export interface GridMenuOption {
/** Defaults to "right", which side to align the grid menu dropdown? */
alignDropSide?: 'left' | 'right';

/**
* All the commands text labels
* NOTE: some of the text have other properties outside of this option (like 'customTitle', 'forceFitTitle', ...) and that is because they were created prior to this refactoring of labels
Expand Down Expand Up @@ -37,6 +34,9 @@ export interface GridMenuOption {
/** Same as "columnTitle", except that it's a translation key which can be used on page load and/or when switching locale */
columnTitleKey?: string;

/** Defaults to "left", which side to align the grid menu dropdown? */
dropSide?: 'left' | 'right';

/** Defaults to "Force fit columns" which is 1 of the last 2 checkbox title shown at the end of the picker list */
forceFitTitle?: string;

Expand Down
8 changes: 1 addition & 7 deletions packages/common/src/interfaces/headerButton.interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SlickHeaderButtons } from '../plugins';
import { SlickHeaderButtons } from '../plugins/slickHeaderButtons';
import { HeaderButtonOnCommandArgs } from './headerButtonOnCommandArgs.interface';
import { SlickEventData } from './slickEventData.interface';

Expand All @@ -17,10 +17,4 @@ export interface HeaderButton extends HeaderButtonOption {
export interface HeaderButtonOption {
/** an extra CSS class to add to the menu button */
buttonCssClass?: string;

/**
* defaults to false, since the default is right floating, we create the buttons in reverse order
* but if we align to left via CSS we might want to inverse the order
*/
inverseOrder?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ describe('CellMenu Plugin', () => {
it('should "autoAlignSide" and expect menu to aligned left with a calculate offset when showing menu', () => {
jest.spyOn(gridStub, 'getGridPosition').mockReturnValue({ top: 10, bottom: 5, left: 15, right: 22, width: 225 } as ElementPosition);
plugin.dispose();
plugin.init({ autoAdjustDrop: true, autoAlignSide: true, alignDropDirection: 'top', alignDropSide: 'left' });
plugin.init({ autoAdjustDrop: true, autoAlignSide: true, dropDirection: 'top', dropSide: 'left' });

const actionBtnElm = document.createElement('button');
slickCellElm.appendChild(actionBtnElm);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ describe('ContextMenu Plugin', () => {
it('should "autoAlignSide" and expect menu to aligned left with a calculate offset when showing menu', () => {
jest.spyOn(gridStub, 'getGridPosition').mockReturnValue({ top: 10, bottom: 5, left: 15, right: 22, width: 225 } as ElementPosition);
plugin.dispose();
plugin.init({ autoAdjustDrop: true, autoAlignSide: true, alignDropDirection: 'top', alignDropSide: 'left' });
plugin.init({ autoAdjustDrop: true, autoAlignSide: true, dropDirection: 'top', dropSide: 'left' });

const actionBtnElm = document.createElement('button');
slickCellElm.appendChild(actionBtnElm);
Expand Down
18 changes: 0 additions & 18 deletions packages/common/src/plugins/__tests__/slickHeaderButtons.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,23 +346,5 @@ describe('HeaderButton Plugin', () => {
`<li class="slick-header-button mdi mdi-lightbulb-on" data-command="show-negative-numbers" title="Highlight negative numbers."></li>
<li class="slick-header-button mdi mdi-lightbulb-outline" data-command="show-positive-numbers"></li>`));
});

it('should populate 2x Header Buttons but in an inversed order "inverseOrder" flag is enabled and the "disabled" property is set on the 2nd button', () => {
const headerDiv = document.createElement('div');
headerDiv.className = 'slick-header-column';

plugin.dispose();
plugin.init({ inverseOrder: true });
delete columnsMock[0].header.buttons[1].showOnHover;
columnsMock[0].header.buttons[1].disabled = true;

const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() };
gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub);

// add Header Buttons which are visible (2x buttons)
expect(removeExtraSpaces(headerDiv.innerHTML)).toBe(removeExtraSpaces(
`<li class="slick-header-button mdi mdi-lightbulb-outline" data-command="show-positive-numbers"></li>
<li class="slick-header-button slick-header-button-disabled mdi mdi-lightbulb-on" data-command="show-negative-numbers" title="Highlight negative numbers."></li>`));
});
});
});
8 changes: 4 additions & 4 deletions packages/common/src/plugins/menuFromCellBaseClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,14 +257,14 @@ export class MenuFromCellBaseClass<M extends CellMenu | ContextMenu> extends Men

// if autoAdjustDrop is enable, we first need to see what position the drop will be located (defaults to bottom)
// without necessary toggling it's position just yet, we just want to know the future position for calculation
if ((this._addonOptions as CellMenu | ContextMenu).autoAdjustDrop || (this._addonOptions as CellMenu | ContextMenu).alignDropDirection) {
if ((this._addonOptions as CellMenu | ContextMenu).autoAdjustDrop || (this._addonOptions as CellMenu | ContextMenu).dropDirection) {
// since we reposition menu below slick cell, we need to take it in consideration and do our calculation from that element
const spaceBottom = calculateAvailableSpace(parentElm).bottom;
const spaceTop = calculateAvailableSpace(parentElm).top;
const spaceBottomRemaining = spaceBottom + dropOffset - rowHeight;
const spaceTopRemaining = spaceTop - dropOffset + rowHeight;
const dropPosition = ((spaceBottomRemaining < menuHeight) && (spaceTopRemaining > spaceBottomRemaining)) ? 'top' : 'bottom';
if (dropPosition === 'top' || (this._addonOptions as CellMenu | ContextMenu).alignDropDirection === 'top') {
if (dropPosition === 'top' || (this._addonOptions as CellMenu | ContextMenu).dropDirection === 'top') {
this._menuElm.classList.remove('dropdown');
this._menuElm.classList.add('dropup');
menuOffsetTop = menuOffsetTop - menuHeight - dropOffset;
Expand All @@ -281,10 +281,10 @@ export class MenuFromCellBaseClass<M extends CellMenu | ContextMenu> extends Men
// when auto-align is set, it will calculate whether it has enough space in the viewport to show the drop menu on the right (default)
// if there isn't enough space on the right, it will automatically align the drop menu to the left (defaults to the right)
// to simulate an align left, we actually need to know the width of the drop menu
if ((this._addonOptions as CellMenu | ContextMenu).autoAlignSide || this._addonOptions.alignDropSide === 'left') {
if ((this._addonOptions as CellMenu | ContextMenu).autoAlignSide || this._addonOptions.dropSide === 'left') {
const gridPos = this.grid.getGridPosition();
const dropSide = ((menuOffsetLeft + (+menuWidth)) >= gridPos.width) ? 'left' : 'right';
if (dropSide === 'left' || this._addonOptions.alignDropSide === 'left') {
if (dropSide === 'left' || this._addonOptions.dropSide === 'left') {
this._menuElm.classList.remove('dropright');
this._menuElm.classList.add('dropleft');
if (this._camelPluginName === 'cellMenu') {
Expand Down
5 changes: 0 additions & 5 deletions packages/common/src/plugins/slickHeaderButtons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,6 @@ export class SlickHeaderButtons extends MenuBaseClass<HeaderButton> {
const column = args.column;

if (column.header?.buttons && Array.isArray(column.header.buttons)) {
// inverse the button (typically used when icons are floating left)
if (this._addonOptions?.inverseOrder) {
column.header.buttons.reverse();
}

let i = column.header.buttons.length;
while (i--) {
const buttonItem = column.header.buttons[i];
Expand Down

0 comments on commit 160d05e

Please sign in to comment.