diff --git a/packages/toast-ui.grid/cypress/integration/eventBus.spec.ts b/packages/toast-ui.grid/cypress/integration/eventBus.spec.ts index 4b4408571..86a529b46 100644 --- a/packages/toast-ui.grid/cypress/integration/eventBus.spec.ts +++ b/packages/toast-ui.grid/cypress/integration/eventBus.spec.ts @@ -335,6 +335,55 @@ describe('rowHeader: checkbox', () => { cy.wrap(uncheckCallback).should('be.calledOnce'); }); + + it(`checkBetween / uncheckBetween by ${type}`, () => { + cy.createGrid({ + data: [...data, ...data], + columns, + draggable: true, + bodyHeight: 150, + width: 500, + rowHeaders: ['rowNum', 'checkbox'], + }); + + const checkCallback = cy.stub(); + const uncheckCallback = cy.stub(); + + cy.getByCls('cell-row-header').get('input').eq(1).as('firstCheckbox'); + cy.getByCls('cell-row-header').get('input').eq(2).as('secondCheckbox'); + cy.getByCls('cell-row-header').get('input').eq(-1).as('lastCheckbox'); + + cy.gridInstance().invoke('on', 'check', checkCallback); + cy.gridInstance().invoke('on', 'uncheck', uncheckCallback); + + if (type === 'UI') { + // In Cypress 4.9.0, there is no way to test Shift-click + // cy.get('@secondCheckbox').click(); + // cy.get('@firstCheckbox').click(); + // cy.get('@lastCheckbox').click({ + // shiftKey: true, // not available in Cypress 4.9.0 + // }); + } else { + cy.gridInstance().invoke('check', 1); + cy.gridInstance().invoke('checkBetween', 0, 3); + + cy.wrap(checkCallback).should('be.calledWithMatch', { rowKeys: [0, 2, 3] }); + } + + if (type === 'UI') { + // In Cypress 4.9.0, there is no way to test Shift-click + // cy.get('@secondCheckbox').click(); + // cy.get('@firstCheckbox').click(); + // cy.get('@lastCheckbox').click({ + // shiftKey: true, // not available in Cypress 4.9.0 + // }); + } else { + cy.gridInstance().invoke('uncheck', 1); + cy.gridInstance().invoke('uncheckBetween', 0, 3); + + cy.wrap(uncheckCallback).should('be.calledWithMatch', { rowKeys: [0, 2, 3] }); + } + }); }); }); diff --git a/packages/toast-ui.grid/src/dispatch/data.ts b/packages/toast-ui.grid/src/dispatch/data.ts index 4c1022a62..45139b51e 100644 --- a/packages/toast-ui.grid/src/dispatch/data.ts +++ b/packages/toast-ui.grid/src/dispatch/data.ts @@ -54,6 +54,7 @@ import { isScrollPagination, isFiltered, getCreatedRowInfos, + getCheckStateChangedRowkeysInRange, } from '../query/data'; import { updateSummaryValueByCell, @@ -378,7 +379,8 @@ export function check(store: Store, rowKey: RowKey) { /** * Occurs when a checkbox in row header is checked * @event Grid#check - * @property {number | string} rowKey - rowKey of the checked row + * @property {number | string} [rowKey] - rowKey of the checked row(when single check via click) + * @property {Array} [rowKeys] - rowKeys of the checked rows(when multiple check via shift-click) * @property {Grid} instance - Current grid instance */ eventBus.trigger('check', gridEvent); @@ -403,7 +405,8 @@ export function uncheck(store: Store, rowKey: RowKey) { /** * Occurs when a checkbox in row header is unchecked * @event Grid#uncheck - * @property {number | string} rowKey - rowKey of the unchecked row + * @property {number | string} [rowKey] - rowKey of the unchecked row(when single check via click) + * @property {Array} [rowKeys] - rowKeys of the unchecked rows(when multiple unchecked via shift-click) * @property {Grid} instance - Current grid instance */ eventBus.trigger('uncheck', gridEvent); @@ -415,11 +418,10 @@ export function setCheckboxBetween( startRowKey: RowKey, endRowKey?: RowKey ) { - const { data } = store; + const { data, id } = store; const { clickedCheckboxRowkey } = data; const targetRowKey = endRowKey || clickedCheckboxRowkey; - - data.clickedCheckboxRowkey = startRowKey; + const eventBus = getEventBus(id); if (isNil(targetRowKey)) { if (value) { @@ -431,9 +433,16 @@ export function setCheckboxBetween( } const range = getIndexRangeOfCheckbox(store, startRowKey, targetRowKey); + const checkStateChangedRowkeys = getCheckStateChangedRowkeysInRange(store, value, range); + + data.clickedCheckboxRowkey = startRowKey; setRowsAttributeInRange(store, 'checked', value, range); setCheckedAllRows(store); + + const gridEvent = new GridEvent({ rowKeys: checkStateChangedRowkeys }); + + eventBus.trigger(value ? 'check' : 'uncheck', gridEvent); } export function checkAll(store: Store, allPage?: boolean) { diff --git a/packages/toast-ui.grid/src/query/data.ts b/packages/toast-ui.grid/src/query/data.ts index 917cb7c24..7d079999e 100644 --- a/packages/toast-ui.grid/src/query/data.ts +++ b/packages/toast-ui.grid/src/query/data.ts @@ -366,3 +366,21 @@ export function changeRawDataToOriginDataForTree(rawData: Row[]) { .filter((row) => isNil(row._attributes?.tree?.parentRowKey)) .map((row) => changeRowToOriginRowForTree(row)); } + +export function getCheckStateChangedRowkeysInRange( + store: Store, + checkState: boolean, + range: [number, number] +) { + const { data } = store; + const { filteredRawData } = data; + + const rowKeys: RowKey[] = []; + for (let i = range[0]; i < range[1]; i += 1) { + if (filteredRawData[i]._attributes.checked !== checkState) { + rowKeys.push(getRowKeyByIndexWithPageRange(data, i)); + } + } + + return rowKeys; +} diff --git a/packages/toast-ui.grid/types/event/index.d.ts b/packages/toast-ui.grid/types/event/index.d.ts index 553fe2225..490aa6f86 100644 --- a/packages/toast-ui.grid/types/event/index.d.ts +++ b/packages/toast-ui.grid/types/event/index.d.ts @@ -21,6 +21,7 @@ export interface GridEventProps { nextValue?: CellValue; event?: MouseEvent; rowKey?: RowKey | null; + rowKeys?: RowKey[] | null; columnName?: string | null; prevRowKey?: RowKey | null; prevColumnName?: string | null;