Skip to content

Commit

Permalink
feat: dynamic row span (#1535)
Browse files Browse the repository at this point in the history
* tests: add test for dynamic row span

* feat: dynamic rowspan for each columns

* feat: dynamic rowspan with filter

* feat: dynamic rowspan with row dnd

* refactor: apply dynamic rowspan when create store

chore: disable eslint warning 'no-console' on export

* feat: dynamic rowspan with sort

* chore: update rowspan for visible columns

* feat: dynamic rowspan with pagination

* feat: dynamic rowspan with tree

* chore: apply dynamic rowspan at the end of grid creation

* feat: dynamic rowspan with relation columns

* feat: dynamic rowspan with data modifying APIs

* chore: dynamic row span option to column option

* docs: update documents for dynamic row span

* chore: rename parameter of reset row span

* chore: fix merging errors

* chore: change default parameter of reset rowspan

* docs: update row span document based on code review

* chore: apply code review

* chore: apply code review

* chore: fix lint errors

chore: remove unnecessary type

* chore: apply code reviews

chore: call updateRowSpan synchronously
chore: change type definition
chore: add warning when use deprecated option
chore: unnecessary return statement

* chore: replace complex conditional expression to variable
  • Loading branch information
jajugoguma authored Dec 10, 2021
1 parent 4db3eef commit 84ef63e
Show file tree
Hide file tree
Showing 30 changed files with 732 additions and 105 deletions.
37 changes: 37 additions & 0 deletions packages/toast-ui.grid/cypress/integration/relation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '../../samples/relations';
import Grid from '@/grid';
import { deepCopyArray } from '@/helper/common';
import { OptColumn } from '@t/options';

function changeCellValues(rowKey: number) {
// changed fixed value to remove unnecessary paramter for values
Expand Down Expand Up @@ -318,3 +319,39 @@ describe(`throw error`, () => {
});
});
});

describe('Dynamic rowSpan', () => {
const rowSpanData = deepCopyArray(data);
rowSpanData[0].category1 = '02';
rowSpanData[0].category2 = '02_03';

it('should not apply dynamic rowSpan to child relation column on ordered relation columns', () => {
const orderedRelationColumnsWithRowSpan = orderedRelationColumns.map((column) => {
(column as OptColumn).rowSpan = true;
return column;
});
cy.createGrid({
data: rowSpanData,
columns: orderedRelationColumnsWithRowSpan,
rowSpan: 'all',
});

cy.getCell(0, 'category1').should('have.attr', 'rowSpan', 2);
cy.getCell(0, 'category2').should('not.have.attr', 'rowSpan');
});

it('should not apply dynamic rowSpan to child relation column on unordered relation columns', () => {
const unorderedRelationColumnsWithRowSpan = unorderedRelationColumns1.map((column) => {
(column as OptColumn).rowSpan = true;
return column;
});
cy.createGrid({
data: rowSpanData,
columns: unorderedRelationColumnsWithRowSpan,
rowSpan: 'all',
});

cy.getCell(0, 'category1').should('have.attr', 'rowSpan', 2);
cy.getCell(0, 'category2').should('not.have.attr', 'rowSpan');
});
});
248 changes: 247 additions & 1 deletion packages/toast-ui.grid/cypress/integration/rowSpan.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { RowKey, RowSpan } from '@t/store/data';
import { data as sample } from '../../samples/basic';
import { OptRow } from '@t/options';
import { OptColumn, OptGrid, OptRow } from '@t/options';
import { invokeFilter, dragAndDropRow } from '../helper/util';
import { deepCopyArray } from '@/helper/common';

function createDataWithRowSpanAttr(): OptRow[] {
const optRows: OptRow[] = sample.slice();
Expand Down Expand Up @@ -320,3 +322,247 @@ it('render rowSpan cell properly by calling setColumns API', () => {
cy.getCell(3, 'name').should('have.attr', 'rowSpan', '3');
cy.getCell(4, 'artist').should('have.attr', 'rowSpan', '3');
});

describe('Dynamic RowSpan', () => {
const dataForDynamicRowSpan = [
{ name: 'Han', age: 10, value: 1 },
{ name: 'Kim', age: 10, value: 1 },
{ name: 'Cho', age: 20, value: 1 },
{ name: 'Ryu', age: 15, value: 1 },
{ name: 'Lee', age: 15, value: 2 },
{ name: 'Park', age: 10, value: 2 },
];
const columnsForDynamicRowSpanToAll: OptColumn[] = [
{ name: 'name', rowSpan: true },
{ name: 'age', filter: 'number', sortingType: 'asc', sortable: true, rowSpan: true },
{ name: 'value', rowSpan: true },
];
const columnsForDynamicRowSpanToAge: OptColumn[] = [
{ name: 'name' },
{ name: 'age', filter: 'number', sortingType: 'asc', sortable: true, rowSpan: true },
{ name: 'value' },
];

function createGridWithRowSpan(
options?: Omit<OptGrid, 'el' | 'columns'>,
columnsOptions?: OptColumn[]
) {
const rowSpanColumns = columnsOptions ?? columnsForDynamicRowSpanToAge;

cy.createGrid({
data: dataForDynamicRowSpan,
columns: rowSpanColumns,
...options,
});
}

it("should render rowSpan cell properly for all columns (rowSpan: 'all')", () => {
createGridWithRowSpan({}, columnsForDynamicRowSpanToAll);

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(0, 'value').should('have.attr', 'rowSpan', '4');
cy.getCell(4, 'value').should('have.attr', 'rowSpan', '2');
});

it("should render rowSpan cell properly for specific columns (rowSpan: ['age'])", () => {
createGridWithRowSpan();

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
cy.getColumnCells('value').each(($el) => {
cy.wrap($el).should('not.have.attr', 'rowSpan');
});
});

describe('With filter', () => {
it('should render rowSpan cell properly with filter', () => {
createGridWithRowSpan();

invokeFilter('age', [{ code: 'eq', value: 10 }]);

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '3');
});

it('should render rowSpan cell properly with unfilter', () => {
createGridWithRowSpan();

invokeFilter('age', [{ code: 'eq', value: 10 }]);

cy.gridInstance().invoke('unfilter', 'age');

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
});
});

describe('With row D&D', () => {
it('should reset rowSpan when row drag started', () => {
createGridWithRowSpan({ draggable: true });

cy.getCell(0, '_draggable').trigger('mousedown');

cy.getColumnCells('age').each(($el) => {
cy.wrap($el).should('not.have.attr', 'rowSpan');
});
});

it('should render rowSpan cell properly after D&D', () => {
createGridWithRowSpan({ draggable: true });

dragAndDropRow(0, 250);

cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(5, 'age').should('have.attr', 'rowSpan', '2');
});
});

describe('With sort', () => {
it('should render rowSpan cell properly after sorting (asc)', () => {
createGridWithRowSpan();

cy.gridInstance().invoke('sort', 'age', true);

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '3');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
});

it('should render rowSpan cell properly after sorting (desc)', () => {
createGridWithRowSpan();

cy.gridInstance().invoke('sort', 'age', false);

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '3');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
});

it('should render rowSpan cell properly after unsorting', () => {
createGridWithRowSpan();

cy.gridInstance().invoke('sort', 'age', true);
cy.gridInstance().invoke('unsort', 'age');

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
});
});

describe('With pagination', () => {
it('should render rowSpan cell properly with pagination', () => {
createGridWithRowSpan({
pageOptions: {
useClient: true,
perPage: 5,
},
});

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
});

it('should not apply rowSpan to another page cell', () => {
createGridWithRowSpan({
pageOptions: {
useClient: true,
perPage: 4,
},
});

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(3, 'age').should('not.have.attr', 'rowSpan');
});
});

describe('With other data modifying APIs', () => {
it('should render rowSpan cell properly after showColumn()', () => {
const columnsWithHideAge = deepCopyArray(columnsForDynamicRowSpanToAge);
columnsWithHideAge[1].hidden = true;
createGridWithRowSpan({}, columnsWithHideAge);

cy.gridInstance().invoke('showColumn', 'age');

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
});

it('should render rowSpan cell properly after setValue()', () => {
createGridWithRowSpan();

cy.gridInstance().invoke('setValue', 2, 'age', 10);

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '3');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
});

it('should render rowSpan cell properly after setColumnValues()', () => {
createGridWithRowSpan();

cy.gridInstance().invoke('setColumnValues', 'age', 10);

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '6');
});

it('should render rowSpan cell properly after appendRow()', () => {
createGridWithRowSpan();

const appendedRow = { name: 'Choi', age: 10, value: 3 };

cy.gridInstance().invoke('appendRow', appendedRow);

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(5, 'age').should('have.attr', 'rowSpan', '2');
});

it('should render rowSpan cell properly after prependRow()', () => {
createGridWithRowSpan();

const prependedRow = { name: 'Choi', age: 10, value: 3 };

cy.gridInstance().invoke('prependRow', prependedRow);

cy.getCell(6, 'age').should('have.attr', 'rowSpan', '3');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
});

it('should render rowSpan cell properly after removeRow()', () => {
createGridWithRowSpan();

cy.gridInstance().invoke('removeRow', 0);

cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
});

it('should render rowSpan cell properly after setRow()', () => {
createGridWithRowSpan();

const setRow = { name: 'Cho', age: 10, value: 1 };

cy.gridInstance().invoke('setRow', 2, setRow);

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '3');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
});

it('should render rowSpan cell properly after appendRows()', () => {
createGridWithRowSpan();

const appendedRow = [{ name: 'Choi', age: 10, value: 3 }];

cy.gridInstance().invoke('appendRows', appendedRow);

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(5, 'age').should('have.attr', 'rowSpan', '2');
});

it('should render rowSpan cell properly after resetData()', () => {
createGridWithRowSpan();

cy.gridInstance().invoke('resetData', dataForDynamicRowSpan);

cy.getCell(0, 'age').should('have.attr', 'rowSpan', '2');
cy.getCell(3, 'age').should('have.attr', 'rowSpan', '2');
});
});
});
20 changes: 20 additions & 0 deletions packages/toast-ui.grid/cypress/integration/tree.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1589,3 +1589,23 @@ it('should update row number after calling appendTreeRow()', () => {
['test1'],
]);
});

it('should not apply dynamic rowSpan', () => {
const treeColumnsWithRowSpan = columns.map((column) => {
column.rowSpan = true;
return column;
});
cy.createGrid({
data,
columns: treeColumnsWithRowSpan,
treeColumnOptions: {
name: 'c1',
},
});

['c1', 'c2'].forEach((columnName) => {
cy.getColumnCells(columnName).each(($el) => {
cy.wrap($el).should('not.have.attr', 'rowSpan');
});
});
});
Loading

0 comments on commit 84ef63e

Please sign in to comment.