From 7c3dfd315d425d25d3beeb68eb39587977aa9580 Mon Sep 17 00:00:00 2001 From: "daeyeon.kim" Date: Thu, 14 Sep 2023 21:55:50 +0900 Subject: [PATCH 1/6] fix: check/uncheck event emitted when multi check/uncheck via shift click --- packages/toast-ui.grid/src/dispatch/data.ts | 19 ++++++++++++++++--- packages/toast-ui.grid/types/event/index.d.ts | 1 + 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/toast-ui.grid/src/dispatch/data.ts b/packages/toast-ui.grid/src/dispatch/data.ts index 4c1022a62..d9c2361a1 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, + getRowKeyByIndexWithPageRange, } 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,9 +418,10 @@ export function setCheckboxBetween( startRowKey: RowKey, endRowKey?: RowKey ) { - const { data } = store; + const { data, id } = store; const { clickedCheckboxRowkey } = data; const targetRowKey = endRowKey || clickedCheckboxRowkey; + const eventBus = getEventBus(id); data.clickedCheckboxRowkey = startRowKey; @@ -432,8 +436,17 @@ export function setCheckboxBetween( const range = getIndexRangeOfCheckbox(store, startRowKey, targetRowKey); + const rowKeys: RowKey[] = []; + for (let i = range[0]; i < range[1]; i += 1) { + rowKeys.push(getRowKeyByIndexWithPageRange(data, i)); + } + + const gridEvent = new GridEvent({ rowKeys }); + setRowsAttributeInRange(store, 'checked', value, range); setCheckedAllRows(store); + + eventBus.trigger(value ? 'check' : 'uncheck', gridEvent); } export function checkAll(store: Store, allPage?: boolean) { 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; From 6b3c6beda4d8fd5e139530c0020383412b02fb18 Mon Sep 17 00:00:00 2001 From: "daeyeon.kim" Date: Fri, 15 Sep 2023 15:31:26 +0900 Subject: [PATCH 2/6] fix: exclude rowKeys whose checkbox state has not changed from check event --- packages/toast-ui.grid/src/dispatch/data.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/toast-ui.grid/src/dispatch/data.ts b/packages/toast-ui.grid/src/dispatch/data.ts index d9c2361a1..c74f349d5 100644 --- a/packages/toast-ui.grid/src/dispatch/data.ts +++ b/packages/toast-ui.grid/src/dispatch/data.ts @@ -418,8 +418,8 @@ export function setCheckboxBetween( startRowKey: RowKey, endRowKey?: RowKey ) { - const { data, id } = store; - const { clickedCheckboxRowkey } = data; + const { data, column, id } = store; + const { clickedCheckboxRowkey, filteredRawData } = data; const targetRowKey = endRowKey || clickedCheckboxRowkey; const eventBus = getEventBus(id); @@ -434,11 +434,23 @@ export function setCheckboxBetween( return; } - const range = getIndexRangeOfCheckbox(store, startRowKey, targetRowKey); + const prevCheckedCheckboxRowIndex = findIndexByRowKey( + data, + column, + id, + clickedCheckboxRowkey, + isFiltered(data) + ); + let range = getIndexRangeOfCheckbox(store, startRowKey, targetRowKey); + + range = + range[0] === prevCheckedCheckboxRowIndex ? [range[0] + 1, range[1]] : [range[0], range[1] - 1]; const rowKeys: RowKey[] = []; for (let i = range[0]; i < range[1]; i += 1) { - rowKeys.push(getRowKeyByIndexWithPageRange(data, i)); + if (filteredRawData[i]._attributes.checked !== value) { + rowKeys.push(getRowKeyByIndexWithPageRange(data, i)); + } } const gridEvent = new GridEvent({ rowKeys }); From ad21106738d91d1b56bf7a3de75354f6b016f716 Mon Sep 17 00:00:00 2001 From: "daeyeon.kim" Date: Fri, 15 Sep 2023 16:39:57 +0900 Subject: [PATCH 3/6] fix:check event has wrong rowKeys --- packages/toast-ui.grid/src/dispatch/data.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/toast-ui.grid/src/dispatch/data.ts b/packages/toast-ui.grid/src/dispatch/data.ts index c74f349d5..f47a4db0c 100644 --- a/packages/toast-ui.grid/src/dispatch/data.ts +++ b/packages/toast-ui.grid/src/dispatch/data.ts @@ -443,8 +443,11 @@ export function setCheckboxBetween( ); let range = getIndexRangeOfCheckbox(store, startRowKey, targetRowKey); - range = - range[0] === prevCheckedCheckboxRowIndex ? [range[0] + 1, range[1]] : [range[0], range[1] - 1]; + if (range[0] === prevCheckedCheckboxRowIndex) { + range = [range[0] + 1, range[1]]; + } else if (range[1] === prevCheckedCheckboxRowIndex) { + range = [range[0], range[1] - 1]; + } const rowKeys: RowKey[] = []; for (let i = range[0]; i < range[1]; i += 1) { From daec617b5dcf4f834c94a6eaf08f0a1856fea3e2 Mon Sep 17 00:00:00 2001 From: "daeyeon.kim" Date: Fri, 15 Sep 2023 17:13:26 +0900 Subject: [PATCH 4/6] chore: separate the logic of getting a rowKey into a function --- packages/toast-ui.grid/src/dispatch/data.ts | 35 +++++---------------- packages/toast-ui.grid/src/query/data.ts | 31 ++++++++++++++++++ 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/packages/toast-ui.grid/src/dispatch/data.ts b/packages/toast-ui.grid/src/dispatch/data.ts index f47a4db0c..45139b51e 100644 --- a/packages/toast-ui.grid/src/dispatch/data.ts +++ b/packages/toast-ui.grid/src/dispatch/data.ts @@ -54,7 +54,7 @@ import { isScrollPagination, isFiltered, getCreatedRowInfos, - getRowKeyByIndexWithPageRange, + getCheckStateChangedRowkeysInRange, } from '../query/data'; import { updateSummaryValueByCell, @@ -418,13 +418,11 @@ export function setCheckboxBetween( startRowKey: RowKey, endRowKey?: RowKey ) { - const { data, column, id } = store; - const { clickedCheckboxRowkey, filteredRawData } = data; + const { data, id } = store; + const { clickedCheckboxRowkey } = data; const targetRowKey = endRowKey || clickedCheckboxRowkey; const eventBus = getEventBus(id); - data.clickedCheckboxRowkey = startRowKey; - if (isNil(targetRowKey)) { if (value) { check(store, startRowKey); @@ -434,33 +432,16 @@ export function setCheckboxBetween( return; } - const prevCheckedCheckboxRowIndex = findIndexByRowKey( - data, - column, - id, - clickedCheckboxRowkey, - isFiltered(data) - ); - let range = getIndexRangeOfCheckbox(store, startRowKey, targetRowKey); - - if (range[0] === prevCheckedCheckboxRowIndex) { - range = [range[0] + 1, range[1]]; - } else if (range[1] === prevCheckedCheckboxRowIndex) { - range = [range[0], range[1] - 1]; - } - - const rowKeys: RowKey[] = []; - for (let i = range[0]; i < range[1]; i += 1) { - if (filteredRawData[i]._attributes.checked !== value) { - rowKeys.push(getRowKeyByIndexWithPageRange(data, i)); - } - } + const range = getIndexRangeOfCheckbox(store, startRowKey, targetRowKey); + const checkStateChangedRowkeys = getCheckStateChangedRowkeysInRange(store, value, range); - const gridEvent = new GridEvent({ rowKeys }); + data.clickedCheckboxRowkey = startRowKey; setRowsAttributeInRange(store, 'checked', value, range); setCheckedAllRows(store); + const gridEvent = new GridEvent({ rowKeys: checkStateChangedRowkeys }); + eventBus.trigger(value ? 'check' : 'uncheck', gridEvent); } diff --git a/packages/toast-ui.grid/src/query/data.ts b/packages/toast-ui.grid/src/query/data.ts index 917cb7c24..c9eb02230 100644 --- a/packages/toast-ui.grid/src/query/data.ts +++ b/packages/toast-ui.grid/src/query/data.ts @@ -366,3 +366,34 @@ 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, column, id } = store; + const { clickedCheckboxRowkey, filteredRawData } = data; + const prevCheckedCheckboxRowIndex = findIndexByRowKey( + data, + column, + id, + clickedCheckboxRowkey, + isFiltered(data) + ); + + if (range[0] === prevCheckedCheckboxRowIndex) { + range = [range[0] + 1, range[1]]; + } else if (range[1] === prevCheckedCheckboxRowIndex) { + range = [range[0], range[1] - 1]; + } + + 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; +} From 34f9c8fd6fea2c910a5d3f722e4be214a8992ec1 Mon Sep 17 00:00:00 2001 From: "daeyeon.kim" Date: Fri, 15 Sep 2023 17:18:13 +0900 Subject: [PATCH 5/6] test: add tests --- .../cypress/integration/eventBus.spec.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) 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] }); + } + }); }); }); From dbe4d41f920cf0c5d9d9b978f1d3aaf86c41464f Mon Sep 17 00:00:00 2001 From: "daeyeon.kim" Date: Mon, 18 Sep 2023 09:59:30 +0900 Subject: [PATCH 6/6] fix: wrong range of rowkeys --- packages/toast-ui.grid/src/query/data.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/packages/toast-ui.grid/src/query/data.ts b/packages/toast-ui.grid/src/query/data.ts index c9eb02230..7d079999e 100644 --- a/packages/toast-ui.grid/src/query/data.ts +++ b/packages/toast-ui.grid/src/query/data.ts @@ -372,21 +372,8 @@ export function getCheckStateChangedRowkeysInRange( checkState: boolean, range: [number, number] ) { - const { data, column, id } = store; - const { clickedCheckboxRowkey, filteredRawData } = data; - const prevCheckedCheckboxRowIndex = findIndexByRowKey( - data, - column, - id, - clickedCheckboxRowkey, - isFiltered(data) - ); - - if (range[0] === prevCheckedCheckboxRowIndex) { - range = [range[0] + 1, range[1]]; - } else if (range[1] === prevCheckedCheckboxRowIndex) { - range = [range[0], range[1] - 1]; - } + const { data } = store; + const { filteredRawData } = data; const rowKeys: RowKey[] = []; for (let i = range[0]; i < range[1]; i += 1) {