diff --git a/components/table/__docs__/demo/virtual-rowspan/index.tsx b/components/table/__docs__/demo/virtual-rowspan/index.tsx index dff6b8a5f5..6b1f56545f 100644 --- a/components/table/__docs__/demo/virtual-rowspan/index.tsx +++ b/components/table/__docs__/demo/virtual-rowspan/index.tsx @@ -3,8 +3,6 @@ import ReactDOM from 'react-dom'; import { Table, Button } from '@alifd/next'; import type { ColumnProps, TableProps } from '@alifd/next/types/table'; -const noop = () => {}; - const dataSource = (j: number) => { const result = []; for (let i = 0; i < j; i++) { diff --git a/components/table/__docs__/theme/index.tsx b/components/table/__docs__/theme/index.tsx index d18be0f568..8768a31cb4 100644 --- a/components/table/__docs__/theme/index.tsx +++ b/components/table/__docs__/theme/index.tsx @@ -7,7 +7,7 @@ import { Demo, type DemoFunctionDefineForObject, DemoGroup, initDemo } from '../ import ConfigProvider from '../../../config-provider'; import zhCN from '../../../locale/zh-cn'; import enUS from '../../../locale/en-us'; -import Table, { ColumnProps, type TableProps } from '../../index'; +import Table, { type ColumnProps, type TableProps } from '../../index'; const i18nMap = { 'en-us': { @@ -111,7 +111,6 @@ class FunctionDemo extends React.Component<{ lang: 'zh-cn' | 'en-us' }> { const functions = convert(demoFunction); const { lang } = this.props; const i18n = i18nMap[lang]; - const { size } = functions; const rowSelection: TableProps['rowSelection'] = { mode: functions.rowSelection as 'single' | 'multiple', selectedRowKeys: [4], @@ -416,7 +415,6 @@ class TableFunctionDemo extends React.Component<{ lang: 'zh-cn' | 'en-us' }> { const functions = convert(demoFunction); const { lang } = this.props; const i18n = i18nMap[lang]; - const { size } = functions; const rowSelection: TableProps['rowSelection'] = functions.rowSelection === 'false' ? null diff --git a/components/table/__tests__/index-spec.tsx b/components/table/__tests__/index-spec.tsx index cf94c97379..0021122619 100644 --- a/components/table/__tests__/index-spec.tsx +++ b/components/table/__tests__/index-spec.tsx @@ -1,537 +1,384 @@ import React from 'react'; -import Enzyme, { mount } from 'enzyme'; -import Adapter from 'enzyme-adapter-react-16'; -import assert from 'power-assert'; -import Promise from 'promise-polyfill'; -import sinon from 'sinon'; +import PropTypes from 'prop-types'; import Loading from '../../loading'; import Icon from '../../icon'; -import Checkbox from '../../checkbox'; -import Table from '../index'; - -/* eslint-disable */ -Enzyme.configure({ adapter: new Adapter() }); +import Table, { type TableProps } from '../index'; +import '../style'; + +const dataSource = [ + { id: '1', name: 'test' }, + { id: '2', name: 'test2' }, +]; + +const table = ( + + + +
+); + +const stickyLock = ( + + + + +); describe('Table', () => { - let dataSource = [ - { id: '1', name: 'test' }, - { id: '2', name: 'test2' }, - ], - table, - wrapper, - timeout, - stickyLock, - stickyLockWrapper, - timeoutStickyLock; - - beforeEach(() => { - table = ( - - - -
- ); - - stickyLock = ( - - - - - ); - - wrapper = mount(table); - stickyLockWrapper = mount(stickyLock); - timeout = (props, callback) => { - return new Promise(resolve => { - wrapper.setProps(props); - setTimeout(function () { - resolve(); - }, 10); - }).then(callback); - }; - - timeoutStickyLock = (props, callback) => { - return new Promise(resolve => { - stickyLockWrapper.setProps(props); - setTimeout(function () { - resolve(); - }, 10); - }).then(callback); - }; - }); - - afterEach(() => { - table = null; - stickyLockWrapper = null; - }); - it('should mount table', () => { - assert(wrapper.find('.next-table-body .next-table-row').length === 2); + cy.mount(table); + cy.get('.next-table-body .next-table-row').should('have.length', 2); }); - it('should render checkboxMode', done => { - timeout( - { - rowSelection: { - getProps: record => { - if (record.id === '1') { - return { - disabled: true, - }; - } - }, + it('should render checkboxMode', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + rowSelection: { + getProps: record => { + if (record.id === '1') { + return { + disabled: true, + }; + } }, }, - () => { - assert(wrapper.find(Checkbox).length === 3); - assert(wrapper.find('.next-checkbox-wrapper.disabled').length === 1); - wrapper.find('.next-checkbox').at(2).find('input').simulate('click'); - wrapper.find('.next-checkbox').at(2).find('input').simulate('change'); - done(); - } - ); + }); + cy.get('.next-checkbox').should('have.length', 3); + cy.get('.next-checkbox-wrapper.disabled').should('have.length', 1); }); - it('should support rowSelect control', done => { - timeout( - { - rowSelection: { - selectedRowKeys: ['1'], - }, + it('should support rowSelect control', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + rowSelection: { + selectedRowKeys: ['1'], }, - () => { - assert(wrapper.find('.next-checkbox-wrapper.checked').length === 1); - done(); - } - ); + }); + cy.get('.next-checkbox-wrapper.checked').should('have.length', 1); }); - it('should render when dataSource is [] & children is null', done => { - timeout( - { - dataSource: [], - children: [], - }, - () => { - assert(wrapper); - done(); - } - ); + it('should render when dataSource is [] & children is null', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + dataSource: [], + children: [], + }); + cy.get('.next-table-empty').should('exist'); + cy.rerender('Demo', { + dataSource: [], + children: null, + }); + cy.get('.next-table-empty').should('exist'); }); - it('should render when dataSource is made of string', done => { - timeout( - { - dataSource: ['string1', 'string2'], - children: [ record} />], - }, - () => { - assert(wrapper); - done(); - } - ); + it('should render when dataSource is made of string', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + // @ts-expect-error 参考 types 里 RecordItem 的描述 + dataSource: ['string1', 'string2'], + children: [ record} />], + }); + cy.get('.next-table-body .next-table-row').should('have.length', 2); }); - it('should render RadioMode', done => { - timeout( - { - rowSelection: { - mode: 'single', - }, + it('should render RadioMode', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + rowSelection: { + mode: 'single', }, - () => { - assert(wrapper.find('.next-radio').length === 2); - done(); - } - ); - }); - - it('should support columnProps/titleProps/titleAddons of rowSelection', done => { - timeout( - { - rowSelection: { - columnProps: () => { - return { - lock: 'right', - width: 90, - align: 'center', - }; - }, - titleAddons: () => { - return
请选择
; - }, - titleProps: () => { - return { - disabled: true, - children: '>', - }; - }, + }); + cy.get('.next-radio').should('have.length', 2); + }); + + it('should support columnProps/titleProps/titleAddons of rowSelection', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + rowSelection: { + columnProps: () => { + return { + lock: 'right', + width: 90, + align: 'center', + }; + }, + titleAddons: () => { + return
请选择
; + }, + titleProps: () => { + return { + disabled: true, + children: '>', + }; }, }, - () => { - assert(wrapper.find('#table-titleAddons').at(0).text() === '请选择'); - assert(wrapper.find('colgroup').at(2).props().children[0].props.style.width === 90); - assert(wrapper.find('th .next-checkbox-wrapper').at(1).hasClass('disabled')); - assert( - wrapper.find('th .next-checkbox-wrapper .next-checkbox-label').at(0).text() === - '>' - ); - done(); - } - ); - }); - - it('should support events', done => { - const onRowClick = sinon.spy(); - const onRowMouseEnter = sinon.spy(); - const onRowMouseLeave = sinon.spy(); - timeout( - { - onRowClick, - onRowMouseEnter, - onRowMouseLeave, - }, - () => { - const row = wrapper.find('.next-table-body .next-table-row').first(); - row.simulate('click'); - assert(onRowClick.called); - row.simulate('mouseenter'); - assert(onRowMouseEnter.called); - row.simulate('mouseleave'); - assert(onRowMouseLeave.called); - done(); - } - ); + }); + cy.get('#table-titleAddons').eq(0).should('have.text', '请选择'); + cy.get('colgroup col').eq(2).should('have.css', 'width', '90px'); + cy.get('th .next-checkbox-wrapper').eq(0).should('have.class', 'disabled'); + cy.get('th .next-checkbox-wrapper .next-checkbox-label').eq(0).should('have.text', '>'); + }); + + it('should support events', () => { + const onRowClick = cy.spy(); + const onRowMouseEnter = cy.spy(); + const onRowMouseLeave = cy.spy(); + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + onRowClick, + onRowMouseEnter, + onRowMouseLeave, + }); + cy.get('.next-table-body .next-table-row').first().trigger('click'); + cy.wrap(onRowClick).should('be.called'); + // React 的 mouseEnter 事件是通过监听 mouseover 实现的 + cy.get('.next-table-body .next-table-row').first().trigger('mouseover'); + cy.wrap(onRowMouseEnter).should('be.called'); + // React 的 mouseLeave 事件是通过监听 mouseout 实现的 + cy.get('.next-table-body .next-table-row').first().trigger('mouseout'); + cy.wrap(onRowMouseLeave).should('be.called'); }); - it('should support sort', done => { - const onSort = (dataIndex, order) => { - assert(dataIndex === 'id'); - assert(order === 'desc'); - }; + it('should support sort', () => { + const onSort = cy.spy(); - timeout( - { - children: [ - , - , - ], - onSort, - }, - () => { - const sortNode = wrapper.find('.next-table-header .next-table-sort'); - sortNode.simulate('click'); - done(); - } - ); + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + children: [ + , + , + ], + onSort, + }); + cy.get('.next-table-header .next-table-sort').first().click(); + cy.wrap(onSort).should('be.calledWith', 'id', 'desc'); }); - it('should support tableLayout&tableWidth', done => { - timeout( - { - children: [ - , - , - ], - tableLayout: 'fixed', - tableWidth: 1200, - }, - () => { - const tablewrapper = wrapper.find('.next-table'); - const table = wrapper.find('.next-table table'); - - assert(tablewrapper.hasClass('next-table-layout-fixed')); - assert(table.at(0).props().style.width === 1200); - done(); - } - ); + it('should support tableLayout&tableWidth', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + children: [ + , + , + ], + tableLayout: 'fixed', + tableWidth: 1200, + }); + cy.get('.next-table').should('have.class', 'next-table-layout-fixed'); + cy.get('.next-table table').should('have.css', 'width', '1200px'); }); - it('should support sortIcons', done => { - const onSort = (dataIndex, order) => { - assert(dataIndex === 'id'); - assert(order === 'desc'); - }; + it('should support sortIcons', () => { + const onSort = cy.spy(); - timeout( - { - children: [ - , - , - ], - onSort, - sortIcons: { - desc: ( - - ), - asc: ( - - ), - }, + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + children: [ + , + , + ], + onSort, + sortIcons: { + desc: , + asc: , }, - () => { - const sortNode = wrapper.find('.next-table-header .next-table-sort'); - assert(sortNode.find('.next-icon-arrow-down')); - assert(sortNode.find('.next-icon-arrow-up')); - sortNode.simulate('click'); - done(); - } - ); + }); + cy.get('.next-table-header .next-table-sort .next-icon-arrow-down').should('exist'); + cy.get('.next-table-header .next-table-sort .next-icon-arrow-up').should('exist'); + cy.get('.next-table-header .next-table-sort').first().click(); + cy.wrap(onSort).should('be.calledWith', 'id', 'desc'); }); - it('should support getRowProps for setting className', done => { - const getRowProps = record => { + it('should support getRowProps for setting className', () => { + const getRowProps: TableProps['getRowProps'] = record => { if (record.id === '1') { return { className: 'rowClassName' }; } }; - timeout( - { - getRowProps, - }, - () => { - const row = wrapper.find('.next-table-body .next-table-row').first(); - assert(row.hasClass('rowClassName')); - done(); - } - ); + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + getRowProps, + }); + cy.get('.next-table-body .next-table-row').first().should('have.class', 'rowClassName'); }); - it('should support rowProps for setting className', done => { - const rowProps = record => { + it('should support rowProps for setting className', () => { + const rowProps: TableProps['rowProps'] = record => { if (record.id === '1') { return { className: 'rowClassName' }; } }; - timeout( - { - rowProps, - }, - () => { - const row = wrapper.find('.next-table-body .next-table-row').first(); - assert(row.hasClass('rowClassName')); - done(); - } - ); + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + rowProps, + }); + cy.get('.next-table-body .next-table-row').first().should('have.class', 'rowClassName'); }); - it('should support fixedHeader, isZebra, hasBorder, loading', done => { - timeout( - { - fixedHeader: true, - }, - () => { - assert(wrapper.find('div.next-table-fixed').length === 1); - } - ) - .then(() => { - return timeout( - { - isZebra: true, - }, - () => { - assert(wrapper.find('div.zebra').length === 1); - } - ); - }) - .then(() => { - return timeout( - { - hasBorder: false, - }, - () => { - assert(wrapper.find('div.only-bottom-border').length === 1); - } - ); - }) - .then(() => { - return timeout( - { - loading: true, - }, - () => { - wrapper.debug(); - assert(wrapper.find(Loading).length === 1); - } - ); - }) - .then(() => { - const loadingIndicator =
Loading...
; - const CustomLoading = ({ className }) => ( - - ); - - return timeout( - { - loading: true, - loadingComponent: CustomLoading, - }, - () => { - wrapper.debug(); - assert(wrapper.find(CustomLoading).length === 1); - assert(wrapper.find('div.test-custom-loading').length === 1); - done(); - } - ); - }); + it('should support fixedHeader, isZebra, hasBorder, loading', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + fixedHeader: true, + }); + cy.get('.next-table-fixed').should('exist'); + cy.rerender('Demo', { + isZebra: true, + }); + cy.get('.zebra').should('exist'); + cy.rerender('Demo', { + hasBorder: false, + }); + cy.get('.only-bottom-border').should('exist'); + cy.rerender('Demo', { + loading: true, + }); + cy.get('.next-loading').should('exist'); + const loadingIndicator =
Loading...
; + const CustomLoading: TableProps['loadingComponent'] = ({ className }) => ( + + ); + CustomLoading.propTypes = { + className: PropTypes.string, + }; + cy.rerender('Demo', { + loading: true, + loadingComponent: CustomLoading, + }); + cy.get('.test-custom-loading').should('exist'); }); - it('should support expandedRowRender getExpandedColProps with expandedIndexSimulate', done => { - const arr = []; - timeout( - { - children: [ - , - { - arr.push(index); - }} - width={200} - />, - ], - expandedRowRender: (record, index) => record.name + index, - getRowProps: (record, index) => { - assert(record.id == index + 1); - return { className: `next-myclass-${index}` }; - }, - getExpandedColProps: (record, index) => { - assert(record.id == index + 1); - }, - expandedIndexSimulate: true, + it('should support expandedRowRender getExpandedColProps with expandedIndexSimulate', () => { + cy.mount(table).as('Demo'); + const cellHandler = cy.spy(); + cy.rerender('Demo', { + children: [ + , + { + cellHandler(index); + }} + width={200} + />, + ], + expandedRowRender: (record: { name: string }, index) => record.name + index, + getRowProps: (record: { id: string }, index) => { + expect(Number(record.id)).equal(index + 1); + return { className: `next-myclass-${index}` }; }, - () => { - assert(arr.toString() === '0,1'); - - let expandedCtrl0 = wrapper - .find('.next-table-body .next-table-expanded-ctrl') - .at(0); - expandedCtrl0.simulate('click'); - let expandedRow0 = wrapper.find('.next-table-body .next-table-expanded-row').at(0); - - assert(expandedRow0.text().replace(/\s$|^\s/g, '') === 'test' + '0'); - - let expandedCtrl1 = wrapper - .find('.next-table-body .next-table-expanded-ctrl') - .at(1); - expandedCtrl1.simulate('click'); - let expandedRow1 = wrapper.find('.next-table-body .next-table-expanded-row').at(1); - - assert(expandedRow1.text().replace(/\s$|^\s/g, '') === 'test2' + '1'); - } - ).then(() => { - return timeout( - { - expandedRowIndent: [2, 1], - }, - () => { - let expandedRowTdFirst = wrapper - .find('.next-table-body .next-table-expanded-row td') - .first(), - expandedRowTdSecond = wrapper - .find('.next-table-body .next-table-expanded-row td') - .at(1); - assert(expandedRowTdFirst.text().replace(/\s$|^\s/g, '') === ''); - assert(expandedRowTdSecond.text().replace(/\s$|^\s/g, '') === ''); - done(); - } - ); + getExpandedColProps: (record: { id: string }, index) => { + expect(Number(record.id)).equal(index + 1); + }, + expandedIndexSimulate: true, + expandedRowIndent: [1, 1], }); + cy.wrap(cellHandler).should('be.calledTwice'); + cy.get('.next-table-body .next-table-expanded-ctrl').eq(0).click(); + cy.get('.next-table-body .next-table-expanded-row') + .eq(0) + .invoke('text') + .then(text => { + expect(text.trim()).equal('test0'); + }); + cy.get('.next-table-body .next-table-expanded-ctrl').eq(1).click(); + cy.get('.next-table-body .next-table-expanded-row') + .eq(1) + .invoke('text') + .then(text => { + expect(text.trim()).equal('test21'); + }); + cy.get('.next-table-body .next-table-expanded-row td') + .eq(0) + .invoke('text') + .then(text => { + expect(text.trim()).equal(''); + }); + cy.get('.next-table-body .next-table-expanded-row') + .eq(0) + .find('td') + .last() + .invoke('text') + .then(text => { + expect(text.trim()).equal(''); + }); }); - it('should support expandedRowEvents', done => { - timeout( - { - expandedRowRender: record => record.name, - onRowOpen: rowKeys => { - assert(rowKeys[0] === '1'); - done(); - }, + it('should support expandedRowEvents', () => { + const onRowOpen = cy.spy(); + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + expandedRowRender: (record: { name: string }, index) => record.name + index, + onRowOpen: rowKeys => { + onRowOpen(rowKeys[0]); }, - () => { - let expandedRow = wrapper.find('.next-table-body .next-table-expanded-row').first(); - assert(expandedRow.length === 0); - let expandedCtrl = wrapper - .find('.next-table-body .next-table-expanded-ctrl') - .first(); - expandedCtrl.simulate('click'); - } - ); + }); + cy.get('.next-table-body .next-table-expanded-row').should('not.exist'); + cy.get('.next-table-body .next-table-expanded-ctrl').first().click(); + cy.wrap(onRowOpen).should('be.calledWith', '1'); }); - it('should support rowExpandable', done => { - timeout( - { - dataSource: [ - { id: '1', name: 'test', expandable: false }, - { id: '2', name: 'test2', expandable: true }, - { id: '3', name: 'test3', expandable: true }, - ], - expandedRowRender: record => record.name, - rowExpandable: record => record.expandable, - }, - () => { - let expandedTotal = wrapper.find('.next-table-row'); - let expandedIcon = wrapper.find( - '.next-table-prerow .next-table-cell-wrapper .next-icon' - ); - - assert(expandedTotal.length - expandedIcon.length === 1); - done(); - } - ); + it('should support rowExpandable', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + dataSource: [ + { id: '1', name: 'test', expandable: false }, + { id: '2', name: 'test2', expandable: true }, + { id: '3', name: 'test3', expandable: true }, + ], + expandedRowRender: (record: { name: string }) => record.name, + rowExpandable: (record: { expandable: boolean }) => record.expandable, + }); + cy.get('.next-table-row').should('have.length', 3); + cy.get('.next-table-prerow .next-table-cell-wrapper .next-icon').should('have.length', 2); }); - it('should support multiple header', done => { - timeout( - { - children: [ - - - - , - - - - , - ], - }, - () => { - let header = wrapper.find('.next-table-header tr'); - assert(header.length === 2); - assert(header.first().text().replace(/\s/g, '') === 'group1group2'); - done(); - } - ); + it('should support multiple header', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + children: [ + + + + , + + + + , + ], + }); + cy.get('.next-table-header tr').should('have.length', 2); + cy.get('.next-table-header tr') + .first() + .invoke('text') + .then(text => { + expect(text.trim()).equal('group1group2'); + }); }); it('should support filter', () => { - let id; - const onFilter = (...args) => { + const onFilterSpy = cy.spy(); + const onFilter: TableProps['onFilter'] = (...args) => { console.log('on filter', args); - id = args[0].id.selectedKeys[0]; + onFilterSpy(args[0].id.selectedKeys[0]); }, filters = [ { - label: 'Nano 包含1', + label: 'Nano 包含 1', value: 1, }, { - label: 'Nano 包含3', + label: 'Nano 包含 3', value: 3, }, { - label: 'Nano 包含2', + label: 'Nano 包含 2', value: 2, children: [ { - label: 'Nano 包含12', + label: 'Nano 包含 12', value: 22, }, { - label: 'Nano 包含23', + label: 'Nano 包含 23', value: 23, }, ], @@ -540,100 +387,92 @@ describe('Table', () => { label: '其他', children: [ { - label: 'Nano 包含4', + label: 'Nano 包含 4', value: 4, }, { - label: 'Nano 包含5', + label: 'Nano 包含 5', value: 5, }, ], }, ]; - wrapper.setProps({ + cy.mount(table).as('Demo'); + cy.rerender('Demo', { onFilter, - children: [], - }); - - assert(wrapper.find('next-table-filter-active').length === 0); - - wrapper.find('.next-icon-filter').simulate('click'); - wrapper.find('.next-btn').at(0).simulate('click'); - - assert.deepEqual(id, undefined); - wrapper.find('.next-icon-filter').simulate('click'); - wrapper.find('.next-menu-item').at(1).simulate('click'); - wrapper.find('.next-btn').at(0).simulate('click'); - assert.deepEqual(id, '3'); - - assert(wrapper.find('next-table-filter-active')); - wrapper.setProps({ + children: [], + }).as('Demo2'); + + cy.get('next-table-filter-active').should('not.exist'); + + cy.get('.next-icon-filter').click(); + cy.get('.next-btn').first().click(); + cy.wrap(onFilterSpy).should('be.calledWith', undefined); + cy.get('.next-icon-filter').click(); + cy.get('.next-menu-item').eq(1).click(); + cy.get('.next-btn').first().click(); + cy.wrap(onFilterSpy).should('be.calledWith', '3'); + cy.get('.next-table-filter-active').should('exist'); + cy.rerender('Demo2', { filterParams: { id: { selectedKeys: '1', }, }, }); - wrapper.find('.next-icon-filter').simulate('click'); - wrapper.find('.next-btn').at(0).simulate('click'); - assert.deepEqual(id, '1'); - wrapper.find('.next-icon-filter').simulate('click'); - assert.deepEqual( - wrapper.find('.next-menu-item').at(0).props().className.indexOf('next-selected') > -1, - true - ); + cy.get('.next-icon-filter').click(); + cy.get('.next-btn').eq(0).click(); + cy.wrap(onFilterSpy).should('be.calledWith', '1'); + cy.get('.next-icon-filter').click(); + cy.get('.next-menu-item').eq(0).should('have.class', 'next-selected'); }); it('should support lock', () => { - wrapper.setProps({ + cy.mount(table).as('Demo'); + cy.rerender('Demo', { children: [ - , - , + , + , ], - }); - wrapper.debug(); - assert(wrapper.find('div.next-table-lock-left').length === 1); - assert(wrapper.find('div.next-table-lock-right').length === 1); - assert(wrapper.find('div.next-table-empty').length === 0); - //Fix #21 - wrapper.setProps({ + }).as('Demo2'); + cy.get('.next-table-lock-left').should('exist'); + cy.get('.next-table-lock-right').should('exist'); + cy.get('.next-table-empty').should('not.exist'); + cy.rerender('Demo2', { dataSource: [], }); - assert(wrapper.find('div.next-table-empty').length !== 0); + cy.get('.next-table-empty').should('exist'); }); it('should support async virtual', () => { - wrapper.setProps({ + cy.mount(table).as('Demo'); + cy.rerender('Demo', { dataSource: [], useVirtual: true, children: [ - , - , + , + , ], + }).as('Demo2'); + cy.rerender('Demo2', { + dataSource: new Array(40).fill((i: number) => { + return { + id: `${i}`, + name: `test${i}`, + }; + }), }); - assert(wrapper.find('div.next-table-empty').length !== 0); - - const dataSource = new Array(40).fill(i => { - return { - id: i + '', - name: `test${i}`, - }; - }); - wrapper.setProps({ - useVirtual: true, - dataSource, - }); - - assert(wrapper.find('div.next-table-empty').length === 0); - assert(wrapper.find('tr.next-table-row').length < 40); + cy.get('.next-table-empty').should('not.exist'); + cy.get('.next-table-row').its('length').should('be.lt', 40); }); it('should support virtual + list table', () => { - timeout({ + cy.mount(table).as('Demo'); + cy.rerender('Demo', { children: [ - header} />, - , - footer} />, + header} />, + , + footer} />, ], useVirtual: true, dataSource: [ @@ -652,253 +491,163 @@ describe('Table', () => { name: 'test2', }, ], - }).then(() => { - assert(wrapper.find('tr.next-table-group-header').length === 2); - assert(wrapper.find('tr.next-table-group-footer').length === 2); - done(); }); + cy.get('.next-table-group-header').should('have.length', 2); + cy.get('.next-table-group-footer').should('have.length', 2); }); - it('should support lock row mouseEnter mouseLeave', done => { - wrapper.setProps({ + it('should support lock row mouseEnter mouseLeave', () => { + const onRowClick = cy.spy(); + const onRowMouseEnter = cy.spy(); + const onRowMouseLeave = cy.spy(); + cy.mount(table).as('Demo'); + cy.rerender('Demo', { children: [ - , - , + , + , ], + onRowClick, + onRowMouseEnter, + onRowMouseLeave, }); - const onRowClick = sinon.spy(); - const onRowMouseEnter = sinon.spy(); - const onRowMouseLeave = sinon.spy(); - timeout( - { - onRowClick, - onRowMouseEnter, - onRowMouseLeave, - }, - () => { - const row = wrapper.find('.next-table-body .next-table-row').first(); - row.simulate('click'); - assert(onRowClick.called); - row.simulate('mouseenter'); - assert(onRowMouseEnter.called); - row.simulate('mouseleave'); - assert(onRowMouseLeave.called); - done(); - } - ); - }); - - it('should support treeMode', done => { - timeout( - { - dataSource: [ - { - id: '1', - name: 'test', - children: [ - { - id: '12', - name: '12test', - }, - ], - }, - { - id: '2', - name: 'test2', - }, - ], - isTree: true, - }, - () => { - assert(wrapper.find('.next-table-row.hidden').length === 1); - let treeNode = wrapper.find( - '.next-table-row .next-icon-arrow-right.next-table-tree-arrow' - ); - treeNode.simulate('click'); - assert(wrapper.find('.next-table-row.hidden').length === 0); - assert( - wrapper - .find('.next-table-row') - .at(1) - .find('.next-table-cell-wrapper') - .first() - .props().style.paddingLeft === 24 - ); - done(); - } - ); + cy.get('.next-table-body .next-table-row').first().click(); + cy.wrap(onRowClick).should('be.called'); + cy.get('.next-table-body .next-table-row').first().trigger('mouseover'); + cy.wrap(onRowMouseEnter).should('be.called'); + cy.get('.next-table-body .next-table-row').first().trigger('mouseout'); + cy.wrap(onRowMouseLeave).should('be.called'); }); - it('header should support colspan', done => { - wrapper.setProps({}); - - timeout( - { - children: [, ], - }, - () => { - assert(wrapper.find('.next-table-header th').length === 2); - } - ).then(() => { - timeout( + it('should support treeMode', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + dataSource: [ { + id: '1', + name: 'test', children: [ - , - , + { + id: '12', + name: '12test', + }, ], }, - () => { - assert(wrapper.find('.next-table-header th').length === 1); - done(); - } - ); - }); - }); - - it('should support colspan & rowspan', done => { - wrapper.setProps({}); - timeout( - { - dataSource: [ - { id: '1', name: 'test' }, - { id: '2', name: 'test2' }, - { id: '3', name: 'test3' }, - ], - cellProps: (rowIndex, colIndex) => { - if (rowIndex == 0 && colIndex == 1) { - return { - rowSpan: 3, - }; - } - }, - }, - () => { - assert(/test/.test(wrapper.find('.next-table-row').at(0).find('td').at(1).text())); - assert(wrapper.find('.next-table-row').at(1).find('td').length === 1); - assert(wrapper.find('.next-table-row').at(2).find('td').length === 1); - } - ).then(() => { - timeout( { - dataSource: [ - { id: '1', name: 'test' }, - { id: '2', name: 'test2' }, - { id: '3', name: 'test3' }, - ], - cellProps: (rowIndex, colIndex) => { - if (rowIndex == 0 && colIndex == 0) { - return { - colSpan: 2, - }; - } - }, + id: '2', + name: 'test2', }, - () => { - done(); - assert(/1/.test(wrapper.find('.next-table-row').at(0).find('td').at(0).text())); - } - ); + ], + isTree: true, + }); + cy.get('.next-table-row.hidden').should('have.length', 1); + cy.get('.next-table-row .next-icon-arrow-right.next-table-tree-arrow').click(); + cy.get('.next-table-row.hidden').should('have.length', 0); + cy.get('.next-table-row') + .eq(1) + .find('.next-table-cell-wrapper') + .first() + .should('have.css', 'padding-left', '24px'); + }); + + it('header should support colspan', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + children: [ + , + , + ], + }); + cy.get('.next-table-header th').should('have.length', 2); + cy.rerender('Demo', { + children: [ + , + , + ], }); + cy.get('.next-table-header th').should('have.length', 1); }); - it('should support colspan & rowspan', done => { - wrapper.setProps({}); - timeout( - { - dataSource: [ - { id: '1', name: 'test' }, - { id: '2', name: 'test2' }, - { id: '3', name: 'test3' }, - ], - getCellProps: (rowIndex, colIndex) => { - if (rowIndex == 0 && colIndex == 1) { - return { - rowSpan: 3, - }; - } - }, + it('should support colspan & rowspan', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + dataSource: [ + { id: '1', name: 'test' }, + { id: '2', name: 'test2' }, + { id: '3', name: 'test3' }, + ], + cellProps: (rowIndex, colIndex) => { + if (rowIndex === 0 && colIndex === 1) { + return { + rowSpan: 3, + }; + } }, - () => { - assert(/test/.test(wrapper.find('.next-table-row').at(0).find('td').at(1).text())); - assert(wrapper.find('.next-table-row').at(1).find('td').length === 1); - assert(wrapper.find('.next-table-row').at(2).find('td').length === 1); - } - ).then(() => { - timeout( - { - dataSource: [ - { id: '1', name: 'test' }, - { id: '2', name: 'test2' }, - { id: '3', name: 'test3' }, - ], - getCellProps: (rowIndex, colIndex) => { - if (rowIndex == 0 && colIndex == 0) { - return { - colSpan: 2, - }; - } - }, - }, - () => { - done(); - assert(/1/.test(wrapper.find('.next-table-row').at(0).find('td').at(0).text())); + }); + cy.get('.next-table-row').eq(0).find('td').eq(1).should('have.text', 'test'); + cy.get('.next-table-row').eq(1).find('td').should('have.length', 1); + cy.get('.next-table-row').eq(2).find('td').should('have.length', 1); + cy.rerender('Demo', { + dataSource: [ + { id: '1', name: 'test' }, + { id: '2', name: 'test2' }, + { id: '3', name: 'test3' }, + ], + cellProps: (rowIndex, colIndex) => { + if (rowIndex === 0 && colIndex === 0) { + return { + colSpan: 2, + }; } - ); + }, }); + cy.get('.next-table-row').eq(0).find('td').eq(0).should('have.text', '1'); }); - it('should support getRowProps', done => { - timeout( - { - dataSource: [ - { id: '1', name: 'test' }, - { id: '2', name: 'test2' }, - { id: '3', name: 'test3' }, - ], - getRowProps: (record, index) => { - if (index == 0) { - return { - 'data-props': 'rowprops', - }; - } - }, + it('should support getRowProps', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + dataSource: [ + { id: '1', name: 'test' }, + { id: '2', name: 'test2' }, + { id: '3', name: 'test3' }, + ], + getRowProps: (record, index) => { + if (index === 0) { + return { + 'data-props': 'rowprops', + }; + } }, - () => { - assert(wrapper.find('.next-table-row').at(0).prop('data-props') === 'rowprops'); - done(); - } - ); + }); + cy.get('.next-table-row').eq(0).should('have.attr', 'data-props', 'rowprops'); }); - it('should support rowProps', done => { - timeout( - { - dataSource: [ - { id: '1', name: 'test' }, - { id: '2', name: 'test2' }, - { id: '3', name: 'test3' }, - ], - rowProps: (record, index) => { - if (index == 0) { - return { - 'data-props': 'rowprops', - }; - } - }, + it('should support rowProps', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + dataSource: [ + { id: '1', name: 'test' }, + { id: '2', name: 'test2' }, + { id: '3', name: 'test3' }, + ], + rowProps: (record, index) => { + if (index === 0) { + return { + 'data-props': 'rowprops', + }; + } }, - () => { - assert(wrapper.find('.next-table-row').at(0).prop('data-props') === 'rowprops'); - done(); - } - ); + }); + cy.get('.next-table-row').eq(0).should('have.attr', 'data-props', 'rowprops'); }); - it('should support list Header', done => { - timeout({ + it('should support list Header', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { children: [ - header} />, - , - footer} />, + header} />, + , + footer} />, ], dataSource: [ { @@ -916,264 +665,220 @@ describe('Table', () => { name: 'test2', }, ], - }).then(() => { - assert(wrapper.find('tr.next-table-group-header').length === 2); - assert(wrapper.find('tr.next-table-group-footer').length === 2); - done(); }); + cy.get('.next-table-group-header').should('have.length', 2); + cy.get('.next-table-group-footer').should('have.length', 2); }); it('should render null when ColGroup, GroupHeader, Col', () => { - const colgroup = mount(); - const col = mount(); - const groupHeader = mount(); - }); - - it('should support stickyHeader', done => { - timeout( - { - stickyHeader: true, - }, - () => { - assert(wrapper.find('div.next-table-affix').length === 1); - done(); - } - ); + cy.mount(); + cy.get('div').should('have.length', 1); + cy.mount(); + cy.get('div').should('have.length', 1); + cy.mount(); + cy.get('div').should('have.length', 1); + }); + + it('should support stickyHeader', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + stickyHeader: true, + }); + cy.get('.next-table-affix').should('exist'); }); - it('should support resize', done => { - timeout( - { - children: [ - , - , - ], - onResizeChange: (dataIndex, value) => { - console.log(dataIndex, value); - }, + it('should support resize', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + children: [ + , + , + ], + onResizeChange: (dataIndex, value) => { + console.log(dataIndex, value); }, - () => { - wrapper.find('.next-table-resize-handler').simulate('mousedown', { pageX: 0 }); - assert(document.body.style.cursor === 'ew-resize'); - document.dispatchEvent(new Event('mousemove', { pageX: 0 })); - document.dispatchEvent(new Event('mouseup')); - - setTimeout(() => { - assert(document.body.style.cursor === ''); - done(); - }, 100); - } - ); + }); + cy.get('.next-table-resize-handler').first().trigger('mousedown', { pageX: 0 }); + cy.get('body').should('have.css', 'cursor', 'ew-resize'); + cy.get('body').trigger('mousemove', { pageX: 0 }); + cy.get('body').trigger('mouseup'); + cy.get('body').should('have.css', 'cursor', 'auto'); }); - it('should support rtl', done => { - timeout( - { - children: [ - , - , - ], - rtl: true, - }, - () => { - assert(wrapper.find('.next-table[dir="rtl"]').length === 3); - done(); - } - ); + it('should support rtl', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + rtl: true, + children: [ + , + , + ], + }); + cy.get('.next-table[dir="rtl"]').should('have.length', 3); }); - it('should support rtl resize', done => { - timeout( - { - children: [ - , - , - ], - rtl: true, - onResizeChange: (dataIndex, value) => { - console.log(dataIndex, value); - }, + it('should support rtl resize', () => { + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + rtl: true, + children: [ + , + , + ], + onResizeChange: (dataIndex, value) => { + console.log(dataIndex, value); }, - () => { - wrapper.find('.next-table-resize-handler').simulate('mousedown', { pageX: 0 }); - assert(document.body.style.cursor === 'ew-resize'); - document.dispatchEvent(new Event('mousemove', { pageX: 0 })); - document.dispatchEvent(new Event('mouseup')); - - setTimeout(() => { - assert(document.body.style.cursor === ''); - done(); - }, 100); - } - ); + }); + cy.get('.next-table-resize-handler').first().trigger('mousedown', { pageX: 0 }); + cy.get('body').should('have.css', 'cursor', 'ew-resize'); + cy.get('body').trigger('mousemove', { pageX: 0 }); + cy.get('body').trigger('mouseup'); + cy.get('body').should('have.css', 'cursor', 'auto'); }); it('should support dataSource [] => [{},{}] => []', () => { - wrapper.setProps({ + cy.mount(table).as('Demo'); + cy.rerender('Demo', { children: [ - , - , + , + , ], - }); - wrapper.debug(); - assert(wrapper.find('div.next-table-lock-left').length === 1); - assert(wrapper.find('div.next-table-lock-right').length === 1); - assert(wrapper.find('div.next-table-empty').length === 0); - - wrapper.setProps({ + }).as('Demo2'); + cy.get('.next-table-lock-left').should('have.length', 1); + cy.get('.next-table-lock-right').should('have.length', 1); + cy.get('.next-table-empty').should('have.length', 0); + cy.rerender('Demo2', { dataSource: [], - }); - assert(wrapper.find('div.next-table-empty').length !== 0); - - wrapper.setProps({ + }).as('Demo3'); + cy.get('.next-table-empty').should('have.length', 1); + cy.rerender('Demo3', { dataSource: [ { id: '1', name: 'test' }, { id: '2', name: 'test2' }, ], - }); - - assert(wrapper.find('div.next-table-lock-left').length === 1); - assert(wrapper.find('div.next-table-lock-right').length === 1); - assert(wrapper.find('div.next-table-empty').length === 0); + }).as('Demo4'); + cy.get('.next-table-lock-left').should('have.length', 1); + cy.get('.next-table-lock-right').should('have.length', 1); + cy.get('.next-table-empty').should('have.length', 0); }); it('should support lock scroll x', () => { const ds = new Array(30).fill(1).map((val, i) => { - return { id: i, name: 'test' + i }; + return { id: i, name: `test${i}` }; }); - wrapper.setProps({ + cy.mount(table).as('Demo'); + cy.rerender('Demo', { children: [ - , - , - , + , + , + , ], - fixedHeader: true, dataSource: ds, + fixedHeader: true, }); - - assert(wrapper.find('div.next-table-lock-left').length === 1); - assert(wrapper.find('div.next-table-lock-right').length === 1); - - wrapper - .find('div.next-table-lock .next-table-inner .next-table-body') - .at(1) - .props() - .onLockScroll({ - target: { - scrollLeft: 30, - scrollTop: 20, - }, - }); - - wrapper - .find('div.next-table-lock-right .next-table-body') - .at(1) - .props() - .onLockScroll({ - target: { - scrollLeft: 30, - scrollTop: 20, - }, + cy.get('.next-table-lock-left').should('have.length', 1); + cy.get('.next-table-lock-right').should('have.length', 1); + cy.get('div.next-table-lock-left .next-table-body').then($ele => { + $ele.scrollTop(100); + }); + cy.get('.next-table-body').eq(0).should('have.prop', 'scrollTop', 100); + cy.get('div.next-table-lock-right .next-table-body').should('have.prop', 'scrollTop', 100); + cy.get('div.next-table-lock-right .next-table-body').then($ele => { + $ele.scrollTop(200); + }); + cy.get('.next-table-body').eq(0).should('have.prop', 'scrollTop', 200); + cy.get('div.next-table-lock-left .next-table-body').should('have.prop', 'scrollTop', 200); + cy.get('.next-table-body') + .eq(0) + .then($ele => { + $ele.scrollTop(300); }); + cy.get('div.next-table-lock-left .next-table-body').should('have.prop', 'scrollTop', 300); + cy.get('div.next-table-lock-right .next-table-body').should('have.prop', 'scrollTop', 300); }); - it('should support StickyLock', done => { + it('should support StickyLock', () => { const ds = new Array(30).fill(1).map((val, i) => { - return { id: i, name: 'test' + i }; + return { id: i, name: `test${i}` }; }); - stickyLockWrapper.setProps({ + cy.mount(stickyLock).as('Demo'); + cy.rerender('Demo', { children: [ - , - , - , + , + , + , ], dataSource: ds, }); - - wrapper.setProps({ - children: [ - , - , - , - , - ], - dataSource: ds, - tableWidth: 1000, - }); - - assert(stickyLockWrapper.find('div.next-table-lock-left').length === 0); - assert(stickyLockWrapper.find('div.next-table-lock-right').length === 0); - assert( - stickyLockWrapper - .find('div.next-table-lock tr td.next-table-fix-left.next-table-fix-left-last') - .at(0) - .instance().style.position === 'sticky' + cy.get('.next-table-lock-left').should('not.exist'); + cy.get('.next-table-lock-right').should('not.exist'); + cy.get('div.next-table-lock tr td.next-table-fix-left.next-table-fix-left-last') + .eq(0) + .should('have.css', 'position', 'sticky'); + cy.get('div.next-table-lock tr td.next-table-fix-left.next-table-fix-left-last').should( + 'have.length', + ds.length ); - wrapper.update(); - stickyLockWrapper.update(); - - setTimeout(() => { - // assert(wrapper.find('div.next-table-lock.next-table-scrolling-to-right').length === 1); - // assert(stickyLockWrapper.find('div.next-table-lock.next-table-scrolling-to-right').length === 1); - assert( - stickyLockWrapper.find( - 'div.next-table-lock tr td.next-table-fix-left.next-table-fix-left-last' - ).length === ds.length - ); - done(); - }, 500); }); it('should support align alignHeader', () => { - wrapper.setProps({ + cy.mount(table).as('Demo'); + cy.rerender('Demo', { children: [ , + , + , - , - , ], }); - - assert(wrapper.find('thead tr th').at(0).props().style.textAlign === 'left'); - assert(wrapper.find('thead tr th').at(1).props().style.textAlign === 'left'); - assert(wrapper.find('thead tr th').at(2).props().style.textAlign === 'right'); - - assert(wrapper.find('tbody tr').at(0).find('td').at(0).props().style.textAlign === 'right'); - assert(wrapper.find('tbody tr').at(0).find('td').at(1).props().style.textAlign === 'left'); - assert( - wrapper.find('tbody tr').at(0).find('td').at(2).props().style.textAlign === undefined - ); + cy.get('thead tr th').eq(0).should('have.css', 'text-align', 'left'); + cy.get('thead tr th').eq(1).should('have.css', 'text-align', 'left'); + cy.get('thead tr th').eq(2).should('have.css', 'text-align', 'right'); + cy.get('tbody tr').eq(0).find('td').eq(0).should('have.css', 'text-align', 'right'); + cy.get('tbody tr').eq(0).find('td').eq(1).should('have.css', 'text-align', 'left'); + cy.get('tbody tr').eq(0).find('td').eq(2).should('have.css', 'text-align', 'start'); }); it('should support align alignHeader rtl', () => { - wrapper.setProps({ + cy.mount(table).as('Demo'); + cy.rerender('Demo', { + rtl: true, children: [ , + , + , - , - , ], - rtl: true, }); - - assert(wrapper.find('thead tr th').at(0).props().style.textAlign === 'right'); - assert(wrapper.find('thead tr th').at(1).props().style.textAlign === 'right'); - assert(wrapper.find('thead tr th').at(2).props().style.textAlign === 'left'); - - assert(wrapper.find('tbody tr').at(0).find('td').at(0).props().style.textAlign === 'left'); - assert(wrapper.find('tbody tr').at(0).find('td').at(1).props().style.textAlign === 'right'); - assert( - wrapper.find('tbody tr').at(0).find('td').at(2).props().style.textAlign === undefined - ); + cy.get('thead tr th').eq(0).should('have.css', 'text-align', 'right'); + cy.get('thead tr th').eq(1).should('have.css', 'text-align', 'right'); + cy.get('thead tr th').eq(2).should('have.css', 'text-align', 'left'); + cy.get('tbody tr').eq(0).find('td').eq(0).should('have.css', 'text-align', 'left'); + cy.get('tbody tr').eq(0).find('td').eq(1).should('have.css', 'text-align', 'right'); + cy.get('tbody tr').eq(0).find('td').eq(2).should('have.css', 'text-align', 'start'); }); }); diff --git a/components/table/__tests__/issue-spec.tsx b/components/table/__tests__/issue-spec.tsx index 4cc540fba3..c37fdeb217 100644 --- a/components/table/__tests__/issue-spec.tsx +++ b/components/table/__tests__/issue-spec.tsx @@ -1,19 +1,11 @@ import React, { useState } from 'react'; -import ReactDOM from 'react-dom'; -import ReactTestUtils from 'react-dom/test-utils'; -import Enzyme, { mount } from 'enzyme'; -import Adapter from 'enzyme-adapter-react-16'; -import assert from 'power-assert'; -import Promise from 'promise-polyfill'; -import Table from '../index'; +import Table, { type ColumnProps, type TableProps } from '../index'; import Button from '../../button/index'; import ConfigProvider from '../../config-provider'; import Input from '../../input'; import '../style'; -/* eslint-disable */ -Enzyme.configure({ adapter: new Adapter() }); -const delay = duration => new Promise(resolve => setTimeout(resolve, duration)); -const generateDataSource = j => { + +const generateDataSource = (j: number) => { const result = []; for (let i = 0; i < j; i++) { result.push({ @@ -27,89 +19,52 @@ const generateDataSource = j => { } return result; }; +const dataSource = [ + { id: '1', name: 'test' }, + { id: '2', name: 'test2' }, +]; + +const table = ( + + + +
+); describe('Issue', () => { - let dataSource = [ - { id: '1', name: 'test' }, - { id: '2', name: 'test2' }, - ], - table, - timeout, - wrapper; - beforeEach(() => { - table = ( - - - -
- ); - - wrapper = mount(table); - timeout = (props, callback) => { - return new Promise(resolve => { - wrapper.setProps(props); - setTimeout(function () { - resolve(); - }, 10); - }).then(callback); - }; + cy.mount(table).as('Demo'); }); - afterEach(() => { - table = null; - }); - - it('should support not display empty when table is Loading', done => { - wrapper.setProps({}); - timeout( - { - loading: true, - dataSource: [], - }, - () => { - assert(wrapper.find('.next-table-empty').text() === ' '); - } - ).then(() => { - timeout( - { - loading: false, - }, - () => { - assert(wrapper.find('.next-table-empty').text() === '没有数据'); - done(); - } - ); + it('should support not display empty when table is Loading', () => { + cy.rerender('Demo', { + loading: true, + dataSource: [], + }).as('Demo2'); + cy.get('.next-table-empty').should('have.text', ' '); + cy.rerender('Demo2', { + loading: false, }); + cy.get('.next-table-empty').should('have.text', '没有数据'); }); - it('should support rowSelection without children and columns', done => { - const container = document.createElement('div'); - document.body.appendChild(container); - class App extends React.Component { - render() { - return ( - {} }} - expandedRowRender={record => record.title} - /> - ); - } - } - - ReactDOM.render(, container, function () { - setTimeout(() => { - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - done(); - }, 10); - }); + it('should support rowSelection without children and columns', () => { + const onRowSelect = cy.spy(); + cy.mount( +
{ + onRowSelect(selectedRowKeys); + }, + }} + /> + ); + cy.get('.next-table-row .next-checkbox-input').eq(0).click(); + cy.wrap(onRowSelect).should('be.calledWith', ['1']); }); - it('should support columns with lock', done => { - const container = document.createElement('div'); - document.body.appendChild(container); + it('should support columns with lock', () => { const columns = [ { title: 'Title6', @@ -154,33 +109,25 @@ describe('Issue', () => { ); } } - - ReactDOM.render(, container, function () { - assert( - container.querySelectorAll( - '#normal-table .next-table-lock-left .next-table-body tbody tr' - ).length === 2 - ); - assert( - container.querySelectorAll('#sticky-table .next-table-fix-left')[0].style - .position === 'sticky' - ); - setTimeout(() => { - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - done(); - }, 10); - }); + cy.mount(); + cy.get('#normal-table .next-table-lock-left .next-table-body tbody tr').should( + 'have.length', + 2 + ); + cy.get('#sticky-table .next-table-fix-left').should('have.css', 'position', 'sticky'); }); - it('should fix onChange reRender bug', done => { - const container = document.createElement('div'); - document.body.appendChild(container); + it('should fix onChange reRender bug', () => { + // const container = document.createElement('div'); + // document.body.appendChild(container); class App extends React.Component { state = { selected: [], }; - onSelectionChange = (ids, records) => { + onSelectionChange: NonNullable['onChange'] = ( + ids, + records + ) => { this.setState({ selected: records, }); @@ -198,213 +145,129 @@ describe('Issue', () => { } } - ReactDOM.render(, container, function () { - const input = container.querySelector('.next-table-header .next-checkbox input'); - input.click(); - setTimeout(() => { - assert( - container.querySelectorAll('.next-table-body .next-checkbox-wrapper.checked') - .length === 2 - ); - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - done(); - }, 10); - }); + cy.mount(); + cy.get('.next-table-header .next-checkbox input').eq(0).click(); + cy.get('.next-table-body .next-checkbox-wrapper.checked').should('have.length', 2); }); it('should support null child', () => { - wrapper.setProps({ - children: [null, ], + cy.rerender('Demo', { + children: [null, ], }); - assert(wrapper.find('.next-table-body tr').length === 2); + cy.get('.next-table-body tr').should('have.length', 2); }); - it('should support rowSelection & tree', done => { - timeout( - { - isTree: true, - rowSelection: { - onChange: () => {}, - }, - dataSource: [ - { - id: '1', - name: 'test', - children: [ - { - id: '12', - name: '12test', - }, - ], - }, - { - id: '2', - name: 'test2', - }, - ], + it('should support rowSelection & tree', () => { + cy.rerender('Demo', { + isTree: true, + rowSelection: { + onChange: () => {}, }, - () => { - assert(wrapper.find('.next-table-row').find('.hidden').length === 1); - done(); - } - ); + dataSource: [ + { + id: '1', + name: 'test', + children: [ + { + id: '12', + name: '12test', + }, + ], + }, + { + id: '2', + name: 'test2', + }, + ], + }); + cy.get('.next-table-row.hidden').should('have.length', 1); }); it('should support rowSelection click', () => { - const div = document.createElement('div'); - document.body.appendChild(div); - const rowSelection = { - onChange: () => {}, - }; - - ReactDOM.render( -
- -
, - div - ); - - div.querySelectorAll('.next-checkbox-wrapper')[1].click(); - assert(div.querySelectorAll('.next-checkbox-wrapper.checked').length === 1); - div.querySelectorAll('.next-checkbox-wrapper')[0].click(); - assert(div.querySelectorAll('.next-checkbox-wrapper.checked').length === 3); - - ReactDOM.unmountComponentAtNode(div); - document.body.removeChild(div); + cy.rerender('Demo', { + rowSelection: { + onChange: () => {}, + }, + children: , + }); + cy.get('.next-checkbox-wrapper').eq(1).click(); + cy.get('.next-checkbox-wrapper.checked').should('have.length', 1); + cy.get('.next-checkbox-wrapper').eq(0).click(); + cy.get('.next-checkbox-wrapper.checked').should('have.length', 3); }); it('should ignore lock as `true` string', () => { - wrapper.setProps({ + cy.rerender('Demo', { rowSelection: { onChange: () => {}, }, children: , }); + cy.get('.next-table-lock-left').should('not.exist'); }); - // it('should support optimization', (done) => { - // class App extends React.Component { - // state = { - // extra: 'abc' - // } - // cellRender = (value) => { - // return value + this.state.extra; - // } - // render() { - // return - // - //
- // } - // componentDidMount() { - // setTimeout(() => { - // this.setState({ - // extra: 'bcd' - // }); - // }, 10); - // } - // } - // const wrapper = mount(); - // setTimeout(() => { - // assert(/bcd/.test(wrapper.find('.next-table-body td').at(0).text())); - // done(); - // }, 100); - // }); - - it('should ignore lock when colWidth < tableWidth', done => { - class App extends React.Component { - state = { - cols: [ - , - , - , - ], - }; - cellRender = value => { - return value; + it('should ignore lock when colWidth < tableWidth', () => { + const cellRender = (value: unknown) => { + return value; + }; + class App extends React.Component<{ more: boolean }> { + static defaultProps = { + more: true, }; render() { return (
- {this.state.cols}
+ + + {this.props.more + ? [ + , + , + ] + : null} +
); } - componentDidMount() { - setTimeout(() => { - this.setState({ - cols: ( - - ), - }); - }, 100); - } } - const div = document.createElement('div'); - document.body.appendChild(div); - ReactDOM.render(, div); - assert(div.querySelectorAll('.next-table-lock-left')[0].children.length !== 0); - assert(div.querySelectorAll('.next-table-lock-right')[0].children.length === 0); - assert( - div.querySelectorAll('div.next-table-lock.next-table-scrolling-to-right').length === 1 - ); - - setTimeout(() => { - assert(div.querySelectorAll('.next-table-lock-left')[0].children.length === 0); - assert(div.querySelectorAll('.next-table-lock-right')[0].children.length === 0); - ReactDOM.unmountComponentAtNode(div); - document.body.removeChild(div); - done(); - }, 200); + cy.mount().as('Demo'); + cy.get('.next-table-lock-left').children().should('have.length.gt', 0); + cy.get('.next-table-lock-right').children().should('have.length', 0); + cy.rerender('Demo', { more: false }); + cy.get('.next-table-lock-left').children().should('have.length', 0); + cy.get('.next-table-lock-right').children().should('have.length', 0); }); it('should has border when set hasHeader as false', () => { - const div = document.createElement('div'); - document.body.appendChild(div); - ReactDOM.render( - - -
, - div - ); - //Hack firefox,IE10,IE11 render error; - div.querySelectorAll('.next-table table')[0].style.borderCollapse = 'separate'; - assert( - parseInt( - window.getComputedStyle(div.querySelectorAll('.next-table')[0]).borderTopWidth, - 10 - ) === 1 - ); - ReactDOM.unmountComponentAtNode(div); - document.body.removeChild(div); + cy.rerender('Demo', { + hasHeader: false, + }); + cy.get('.next-table').should('have.css', 'border-top-width', '1px'); }); it('should support style config for Table.Column', () => { - const div = document.createElement('div'); - document.body.appendChild(div); - ReactDOM.render( + cy.mount( -
, - div + ); - assert(div.querySelectorAll('.next-table table td')[0].style.textAlign === ''); - assert(div.querySelectorAll('.next-table table th')[0].style.textAlign === 'left'); - ReactDOM.unmountComponentAtNode(div); - document.body.removeChild(div); + cy.get('.next-table td').eq(0).should('have.css', 'text-align', 'start'); + cy.get('.next-table th').eq(0).should('have.css', 'text-align', 'left'); }); it('should support pass null to sort and any others', () => { - const div = document.createElement('div'); - document.body.appendChild(div); - ReactDOM.render( + cy.mount( { expandedRowKeys={null} > -
, - div + ); - ReactDOM.unmountComponentAtNode(div); - document.body.removeChild(div); + cy.get('.next-table').should('exist'); }); it('should support virtual list', () => { @@ -433,15 +294,8 @@ describe('Issue', () => { ); } } - - const div = document.createElement('div'); - document.body.appendChild(div); - ReactDOM.render(, div); - - assert(div.querySelectorAll('.next-table-body tbody tr').length < 100); - - ReactDOM.unmountComponentAtNode(div); - document.body.removeChild(div); + cy.mount(); + cy.get('.next-table-body tbody tr').should('have.length.lt', 100); }); it('should support defaultOpenRowKeys', () => { @@ -461,15 +315,8 @@ describe('Issue', () => { } } - const div = document.createElement('div'); - document.body.appendChild(div); - ReactDOM.render(, div); - - let expandedTotal = div.querySelectorAll('tbody tr'); - assert(expandedTotal.length === 5); - - ReactDOM.unmountComponentAtNode(div); - document.body.removeChild(div); + cy.mount(); + cy.get('tbody tr').should('have.length', 5); }); it('sort should be singleton', () => { @@ -484,18 +331,10 @@ describe('Issue', () => { ); } } - - const div = document.createElement('div'); - document.body.appendChild(div); - ReactDOM.render(, div); - - const sortBtn = div.querySelectorAll('.next-table-header .next-table-sort'); - sortBtn[0].click(); - sortBtn[1].click(); - - assert(div.getElementsByClassName('current').length === 1); - ReactDOM.unmountComponentAtNode(div); - document.body.removeChild(div); + cy.mount(); + cy.get('.next-table-header .next-table-sort').eq(0).click(); + cy.get('.next-table-header .next-table-sort').eq(1).click(); + cy.get('.current').should('have.length', 1); }); it('should sortDirections work', () => { @@ -515,24 +354,16 @@ describe('Issue', () => { ); } } - - const div = document.createElement('div'); - document.body.appendChild(div); - ReactDOM.render(, div); - - const sortBtn = div.querySelectorAll('.next-table-header .next-table-sort'); - sortBtn[0].click(); - assert(div.querySelectorAll('a.current .next-icon-descending')); - sortBtn[0].click(); - assert(div.querySelectorAll('a.current .next-icon-ascending')); - sortBtn[0].click(); - assert(div.querySelectorAll('a.current').length === 0); - - ReactDOM.unmountComponentAtNode(div); - document.body.removeChild(div); + cy.mount(); + cy.get('.next-table-header .next-table-sort').eq(0).click(); + cy.get('a.current .next-icon-descending').should('exist'); + cy.get('.next-table-header .next-table-sort').eq(0).click(); + cy.get('a.current .next-icon-ascending').should('exist'); + cy.get('.next-table-header .next-table-sort').eq(0).click(); + cy.get('a.current').should('not.exist'); }); - it('sort should have only one empty when datasorce=[] && enough width', () => { + it('there should be only one empty block with lock config when datasource=[] && enough width', () => { class App extends React.Component { render() { return ( @@ -545,16 +376,11 @@ describe('Issue', () => { } } - const div = document.createElement('div'); - document.body.appendChild(div); - ReactDOM.render(, div); - - assert(div.querySelectorAll('div.next-table-empty').length === 1); - ReactDOM.unmountComponentAtNode(div); - document.body.removeChild(div); + cy.mount(); + cy.get('.next-table-empty').should('exist'); }); - it('fix #466, stickHeader + lock with enough space', () => { + it('fix #466, stickyHeader + lock with enough space', () => { class App extends React.Component { render() { return ( @@ -566,18 +392,13 @@ describe('Issue', () => { } } - const div = document.createElement('div'); - document.body.appendChild(div); - ReactDOM.render(, div); - - assert(div.querySelectorAll('div.next-table-empty').length === 1); - ReactDOM.unmountComponentAtNode(div); - document.body.removeChild(div); + cy.mount(); + cy.get('.next-table-empty').should('have.length', 1); }); - it('should support crossline hover', done => { - const container = document.createElement('div'); - document.body.appendChild(container); + it('should support crossline hover', () => { + // const container = document.createElement('div'); + // document.body.appendChild(container); class App extends React.Component { render() { return ( @@ -599,48 +420,19 @@ describe('Issue', () => { } } - ReactDOM.render(, container, function () { - const cell = container.querySelector( - 'td[data-next-table-col="1"][data-next-table-row="1"]' - ); - const mouseover = new MouseEvent('mouseover', { - view: window, - bubbles: true, - cancelable: true, - }); - - cell.dispatchEvent(mouseover); - - assert(container.querySelectorAll('td.next-table-cell.hovered').length === 2); - - assert(container.querySelectorAll('tr.next-table-row.hovered').length === 1); - - const mouseout = new MouseEvent('mouseout', { - view: window, - bubbles: true, - cancelable: true, - }); - - cell.dispatchEvent(mouseout); - - assert(container.querySelectorAll('td.next-table-cell.hovered').length === 0); - - // target is in inner - const renderA = container.querySelector('#name-0'); - renderA.dispatchEvent(mouseover); - - assert(container.querySelectorAll('td.next-table-cell.hovered').length === 2); - - assert(container.querySelectorAll('tr.next-table-row.hovered').length === 1); - - renderA.dispatchEvent(mouseout); - - assert(container.querySelectorAll('td.next-table-cell.hovered').length === 0); - - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - done(); - }); + cy.mount(); + cy.get('td[data-next-table-col="1"][data-next-table-row="1"]') + .as('cell') + .trigger('mouseover'); + cy.get('.next-table-cell.hovered').should('have.length', 2); + cy.get('.next-table-row.hovered').should('have.length', 1); + cy.get('@cell').trigger('mouseout'); + cy.get('.next-table-cell.hovered').should('have.length', 0); + cy.get('#name-0').as('renderA').trigger('mouseover'); + cy.get('.next-table-cell.hovered').should('have.length', 2); + cy.get('.next-table-row.hovered').should('have.length', 1); + cy.get('@renderA').trigger('mouseout'); + cy.get('.next-table-cell.hovered').should('have.length', 0); }); it('should support useFirstLevelDataWhenNoChildren', () => { @@ -670,7 +462,7 @@ describe('Issue', () => { }} /> { + cell={(product: Array<{ title: string }>) => { return product[0].title; }} title="Product Details" @@ -683,20 +475,14 @@ describe('Issue', () => { ); } } - - const div = document.createElement('div'); - document.body.appendChild(div); - ReactDOM.render(, div); - - assert(div.querySelectorAll('.next-table-group-header + tr').length === 1); - ReactDOM.unmountComponentAtNode(div); - document.body.removeChild(div); + cy.mount(); + cy.get('.next-table-group-header + tr').should('have.length', 1); }); // fix https://github.com/alibaba-fusion/next/issues/4396 - it('should support Header and Body follow the TableGroupHeader when it locks the columns', async () => { - const container = document.createElement('div'); - document.body.appendChild(container); + it('should support Header and Body follow the TableGroupHeader when it locks the columns', () => { + // const container = document.createElement('div'); + // document.body.appendChild(container); const dataSource = () => { const result = []; @@ -716,12 +502,12 @@ describe('Issue', () => { { title: 'Title2', dataIndex: 'id', - lock: 'left', + lock: 'left' as const, width: 140, }, { title: 'Title3', - lock: 'left', + lock: 'left' as const, dataIndex: 'time', width: 200, }, @@ -738,40 +524,33 @@ describe('Issue', () => { width: 500, }, ]; - - ReactDOM.render( + cy.mount( { return
title
; }} /> -
, - container + ); - const tableHeader = container.querySelector('.next-table-header'); - const tableBody = container.querySelector('.next-table-body'); - assert(tableHeader); - assert(tableBody); - // wait for initial scroll align - await delay(200); - tableHeader.scrollLeft = 100; - ReactTestUtils.Simulate.scroll(tableHeader); - await delay(200); - assert(tableHeader.scrollLeft === 100); - assert(tableBody.scrollLeft === 100); - - tableBody.scrollLeft = 0; - ReactTestUtils.Simulate.scroll(tableBody); - await delay(200); - assert(tableHeader.scrollLeft === 0); - assert(tableBody.scrollLeft === 0); + cy.get('.next-table-header').as('tableHeader').should('exist'); + cy.get('.next-table-body').as('tableBody').should('exist'); + // table 内部有定时锁阻止同一时间多次设置 scroll,需要等待一段时间 + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + cy.get('@tableHeader').then(tableHeader => { + tableHeader.scrollLeft(100); + }); + cy.get('@tableBody').should('have.prop', 'scrollLeft', 100); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + cy.get('@tableBody').then(tableHeader => { + tableHeader.scrollLeft(0); + }); + cy.get('@tableHeader').should('have.prop', 'scrollLeft', 0); }); - it('should support multiple header lock', done => { - const container = document.createElement('div'); - document.body.appendChild(container); - + it('should support multiple header lock', () => { const dataSource = () => { const result = []; for (let i = 0; i < 5; i++) { @@ -783,7 +562,7 @@ describe('Issue', () => { } return result; }; - const render = (value, index, record) => { + const render: ColumnProps['cell'] = (value, index, record) => { return Remove({record.id}); }; @@ -829,24 +608,13 @@ describe('Issue', () => { }, ]; - ReactDOM.render( - , - container, - function () { - setTimeout(() => { - assert(parseInt(container.querySelector('#target-line').style.left) - 340 < 1); - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - done(); - }, 10); - } - ); + cy.mount(); + cy.get('#target-line').then($ele => { + expect(parseInt($ele.css('left'))).to.be.closeTo(340, 1); + }); }); - it('should set right offset, fix #2276', done => { - const container = document.createElement('div'); - document.body.appendChild(container); - + it('should set right offset, fix #2276', () => { const dataSource = () => { const result = []; for (let i = 0; i < 5; i++) { @@ -858,7 +626,7 @@ describe('Issue', () => { } return result; }; - const render = (value, index, record) => { + const render: ColumnProps['cell'] = (value, index, record) => { return Remove({record.id}); }; @@ -933,33 +701,16 @@ describe('Issue', () => { }, ]; - ReactDOM.render( - , - container, - function () { - setTimeout(() => { - assert( - parseInt( - container.querySelectorAll( - '.next-table-cell.next-table-fix-right.next-table-fix-right-first' - )[3].style.right - ) - - 200 < - 1 - ); - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - done(); - }, 10); - } - ); + cy.mount(); + cy.get('.next-table-cell.next-table-fix-right.next-table-fix-right-first') + .eq(3) + .then($ele => { + expect(parseInt($ele.css('right'))).to.be.closeTo(200, 1); + }); }); - it('should work with expanded virtual table, fix #2646', done => { - const container = document.createElement('div'); - document.body.appendChild(container); - - const dataSource = n => { + it('should work with expanded virtual table, fix #2646', () => { + const dataSource = (n: number) => { const result = []; for (let i = 0; i < n; i++) { result.push({ @@ -970,7 +721,7 @@ describe('Issue', () => { } return result; }; - const render = (value, index, record) => { + const render: ColumnProps['cell'] = (value, index, record) => { return Remove({record.id}); }; @@ -978,7 +729,7 @@ describe('Issue', () => { state = { scrollToRow: 20, }; - onBodyScroll = start => { + onBodyScroll: TableProps['onBodyScroll'] = start => { this.setState({ scrollToRow: start, }); @@ -1004,35 +755,19 @@ describe('Issue', () => { } } - ReactDOM.render(, container, function () { - setTimeout(() => { - const trCount = container.querySelectorAll( - '.next-table .next-table-body table tr.next-table-row' - ).length; - assert(trCount > 10); - assert(trCount < 100); - - const ctrl = container.querySelectorAll( - '.next-table .next-table-body table tr.next-table-row .next-table-expanded-ctrl' - )[0]; - ctrl.click(); - - assert( - container.querySelectorAll( - '.next-table .next-table-body table tr.next-table-expanded-row' - ) - ); - - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - done(); - }, 10); - }); + cy.mount(); + cy.get('.next-table .next-table-body table tr.next-table-row') + .its('length') + .should('be.greaterThan', 10); + cy.get('.next-table .next-table-body table tr.next-table-row') + .its('length') + .should('be.lessThan', 100); + cy.get('.next-table .next-table-body table tr.next-table-row .next-table-expanded-ctrl') + .eq(0) + .click({ force: true }); + cy.get('.next-table .next-table-body table tr.next-table-expanded-row').should('exist'); }); - it("should set expanded row's width after stickylock table toggle loading, close #3000", done => { - const container = document.createElement('div'); - document.body.appendChild(container); - + it("should set expanded row's width after stickyLock table toggle loading, close #3000", () => { const dataSource = () => { const result = []; for (let i = 0; i < 5; i++) { @@ -1045,19 +780,16 @@ describe('Issue', () => { } return result; }, - expandedRowRender = record => record.title, - render = (value, index, record) => { + expandedRowRender: TableProps['expandedRowRender'] = record => record.title, + render: ColumnProps['cell'] = (value, index, record) => { return Remove({record.id}); }; class App extends React.Component { - constructor(props) { - super(props); - this.state = { - dataSource: dataSource(), - loading: false, - }; - } + state = { + dataSource: dataSource(), + loading: false, + }; toggleLoading = () => { this.setState({ @@ -1092,34 +824,19 @@ describe('Issue', () => { } } - ReactDOM.render(, container, function () { - setTimeout(() => { - const expandedRows = container.querySelectorAll( - '.next-table-expanded-row .next-table-cell-wrapper' - ); - expandedRows.forEach(row => { - assert(row.style.width === '499px'); - }); - - const btn = container.querySelector('#sticky-expanded-row-width'); - btn.click(); - setTimeout(() => { - btn.click(); - - expandedRows.forEach(row => { - assert(row.style.width === '499px'); - }); - - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - done(); - }, 100); - }, 100); + cy.mount(); + cy.get('.next-table-expanded-row .next-table-cell-wrapper').each($row => { + expect(parseInt($row.css('width'))).to.be.closeTo(499, 1); + }); + cy.get('#sticky-expanded-row-width').click(); + cy.get('#sticky-expanded-row-width').click(); + cy.get('.next-table-expanded-row .next-table-cell-wrapper').each($row => { + expect(parseInt($row.css('width'))).to.be.closeTo(499, 1); }); }); - it('Different sorts have different className of table header , close #3386', done => { - const container = document.createElement('div'); - document.body.appendChild(container); + it('Different sorts have different className of table header , close #3386', () => { + // const container = document.createElement('div'); + // document.body.appendChild(container); class App extends React.Component { render() { return ( @@ -1131,34 +848,14 @@ describe('Issue', () => { } } - ReactDOM.render(, container, function () { - const input = container.querySelector('.next-table-header .next-table-sort'); - input.click(); - setTimeout(() => { - assert( - container.querySelectorAll( - `.next-table-header-node.next-table-header-sort-desc` - ).length === 1 - ); - input.click(); - setTimeout(() => { - assert( - container.querySelectorAll( - `.next-table-header-node.next-table-header-sort-asc` - ).length === 1 - ); - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - done(); - }, 10); - }, 10); - }); + cy.mount(); + cy.get('.next-table-header .next-table-sort').click(); + cy.get('.next-table-header-node.next-table-header-sort-desc').should('have.length', 1); + cy.get('.next-table-header .next-table-sort').click(); + cy.get('.next-table-header-node.next-table-header-sort-asc').should('have.length', 1); }); it('should not modify columns props passed from outside, close #4062', () => { - const container = document.createElement('div'); - document.body.appendChild(container); - const columns = [ { title: '商品编码', @@ -1176,20 +873,20 @@ describe('Issue', () => { barcode: 'Bar16858180524079952', itemCode: 'code16858180524079952', itemId: 128581419, - itemName: '测试商品16858180524065799', + itemName: '测试商品 16858180524065799', ownerId: 624144, - ownerName: '快消-商家测试帐号86', + ownerName: '快消 - 商家测试帐号 86', }, { barcode: 'Bar16858755068847002', itemCode: 'code16858755068847002', itemId: 128581770, - itemName: '测试商品16858755068835325', + itemName: '测试商品 16858755068835325', ownerId: 624144, - ownerName: '快消-商家测试帐号86', + ownerName: '快消 - 商家测试帐号 86', }, ]; - const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [selectedRowKeys, setSelectedRowKeys] = useState>([]); return ( { ); } - ReactDOM.render(, container); - - assert(columns.length === 2); + cy.mount(); + expect(columns.length).to.equal(2); }); it('should support ConfigProvider prefix, close #4073', () => { - const container = document.createElement('div'); - document.body.appendChild(container); - const dataSource = () => { const result = []; for (let i = 0; i < 5; i++) { @@ -1236,73 +929,49 @@ describe('Issue', () => { } return result; }; - - ReactDOM.render( + cy.mount(
- , - container + ); - - assert(container.querySelectorAll('.my-table').length >= 1); + cy.get('.my-table').should('exist'); }); it('should not crash when dataSource is undefined, close #4073', () => { - const container = document.createElement('div'); - document.body.appendChild(container); - - ReactDOM.render( + cy.mount( -
, - container + ); - assert(container.querySelector('.next-table-empty')); + cy.get('.next-table-empty').should('exist'); }); - it('should not crash when columns is undefined, close #4070', done => { - wrapper.setProps({}); - timeout( - { - columns: undefined, - }, - () => { - assert(wrapper.find('.next-table-empty')); - done(); - } - ); + it('should not crash when columns is undefined, close #4070', () => { + cy.rerender('Demo', { children: undefined }); + cy.get('.next-table').should('exist'); }); it('should can use Input at column.title, close #4370', () => { - const container = document.createElement('div'); - document.body.appendChild(container); - - ReactDOM.render( + cy.mount( } lock htmlTitle="Unique Id" dataIndex="id" /> -
, - container + ); - const input = container.querySelector('input'); - assert(input); - ReactTestUtils.Simulate.change(input, { target: { value: 'aa' } }); - assert(input.value === 'aa'); + cy.get('input').type('aa'); + cy.get('input').should('have.value', 'aa'); }); it('should support locking columns when the data source is empty and in the same grouping, close #4282', () => { - const container = document.createElement('div'); - document.body.appendChild(container); - - ReactDOM.render( + cy.mount( @@ -1315,23 +984,21 @@ describe('Issue', () => { - , - container +
); - - const title1Cell = container.querySelector('th.next-table-fix-left[rowspan="1"]'); - const title1CellLeft = title1Cell.getBoundingClientRect().left; - const title1CellWidth = title1Cell.getBoundingClientRect().width; - const title2CellLeft = container - .querySelector('th.next-table-fix-left-last[rowspan="1"]') - .getBoundingClientRect().left; - assert(title1CellLeft + title1CellWidth === title2CellLeft); + cy.get('th.next-table-fix-left[rowspan="1"]').eq(0).as('title1Cell'); + cy.get('th.next-table-fix-left-last[rowspan="1"]').as('title2Cell'); + cy.get('@title1Cell').then($title1Cell => { + const title1CellLeft = $title1Cell.get(0).getBoundingClientRect().left; + const title1CellWidth = $title1Cell.get(0).getBoundingClientRect().width; + cy.get('@title2Cell').then($title2Cell => { + const title2CellLeft = $title2Cell.get(0).getBoundingClientRect().left; + expect(title1CellLeft + title1CellWidth).to.equal(title2CellLeft); + }); + }); }); it('should support when dataSource item id 0, close #3740', () => { - const container = document.createElement('div'); - document.body.appendChild(container); - function CheckTable() { const dataSource = [ { id: 0, name: 'test' }, @@ -1351,44 +1018,25 @@ describe('Issue', () => { ); } - ReactDOM.render(, container); - const getRowCell = function (row, index = 1) { - return row.querySelectorAll('.next-table-cell')[index]; + cy.mount(); + const getRowCell = function (row: Cypress.Chainable>, index = 1) { + return row.find('.next-table-cell').eq(index); }; - const rows = container.querySelectorAll('tr.next-table-row'); - assert(container.querySelectorAll('.next-checkbox-wrapper.checked').length === 1); - assert(container.querySelectorAll('.next-table-body .next-table-row').length === 3); - assert( - getRowCell(rows[0]).textContent === '0' && getRowCell(rows[0], 2).textContent === 'test' - ); - assert( - getRowCell(rows[1]).textContent === '1' && - getRowCell(rows[1], 2).textContent === 'test1' - ); - assert( - getRowCell(rows[2]).textContent === '2' && - getRowCell(rows[2], 2).textContent === 'test2' - ); - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); + cy.get('tr.next-table-row').as('rows'); + cy.get('.next-checkbox-wrapper.checked').should('have.length', 1); + cy.get('.next-table-body .next-table-row').should('have.length', 3); + getRowCell(cy.get('@rows').eq(0)).should('have.text', '0'); + getRowCell(cy.get('@rows').eq(0), 2).should('have.text', 'test'); + getRowCell(cy.get('@rows').eq(1)).should('have.text', '1'); + getRowCell(cy.get('@rows').eq(1), 2).should('have.text', 'test1'); + getRowCell(cy.get('@rows').eq(2)).should('have.text', '2'); + getRowCell(cy.get('@rows').eq(2), 2).should('have.text', 'test2'); }); }); describe('TableScroll', () => { - let mountNode; - - beforeEach(() => { - mountNode = document.createElement('div'); - document.body.appendChild(mountNode); - }); - - afterEach(() => { - ReactDOM.unmountComponentAtNode(mountNode); - document.body.removeChild(mountNode); - }); - it('scroll position error, close #4484', () => { - const dataSource = j => { + const dataSource = (j: number) => { const result = []; for (let i = 0; i < j; i++) { result.push({ @@ -1400,17 +1048,14 @@ describe('TableScroll', () => { } return result; }; - const relockColumn = (value, index, record) => { + const reLockColumn: ColumnProps['cell'] = (value, index, record) => { return Remove({record.id}); }; class Demo extends React.Component { - constructor(props) { - super(props); - } state = { scrollToRow: 20, }; - onBodyScroll = start => { + onBodyScroll: TableProps['onBodyScroll'] = start => { this.setState({ scrollToRow: start, }); @@ -1433,27 +1078,29 @@ describe('TableScroll', () => { - + - + ); } } - ReactDOM.render(, mountNode); - const scrollNode = mountNode.querySelector('.next-table-body'); - const rowHeight = scrollNode.querySelector('.next-table-cell').clientHeight; - scrollNode.scrollTop = 200; - ReactTestUtils.Simulate.click(mountNode.querySelector('.next-btn')); - assert(rowHeight * 100 === scrollNode.scrollTop); + cy.mount(); + cy.get('.next-table-body') + .eq(0) + .then($scrollNode => { + const rowHeight = $scrollNode + .get(0) + .querySelector('.next-table-cell')!.clientHeight; + cy.get('.next-table-body').eq(0).scrollTo(0, 200); + cy.get('.next-btn').click(); + cy.get('.next-table-body').should('have.prop', 'scrollTop', rowHeight * 100); + }); }); // fix https://github.com/alibaba-fusion/next/issues/4394 - it('should support onBodyScroll under the condition that useVirtual, dataSource is returned asynchronously, close #4394', async () => { - const container = document.createElement('div'); - document.body.appendChild(container); - - const dataSource = j => { + it('should support onBodyScroll under the condition that useVirtual, dataSource is returned asynchronously, close #4394', () => { + const dataSource = (j: number) => { const result = []; for (let i = 0; i < j; i++) { result.push({ @@ -1477,7 +1124,7 @@ describe('TableScroll', () => { }); }, 50); }; - onBodyScroll = start => { + onBodyScroll: TableProps['onBodyScroll'] = start => { this.setState({ scrollToRow: start, }); @@ -1512,40 +1159,42 @@ describe('TableScroll', () => { } } - ReactDOM.render(, container); - - await delay(200); - const button = container.querySelector('tr.next-table-row.first button'); - assert(button); - button.click(); - await delay(200); - - const getBodyTop = () => { - const { top, height } = container.querySelector('thead').getBoundingClientRect(); - return top + height; - }; - const skipRow = container.querySelectorAll('tr.next-table-row')[10]; - assert(skipRow.children[0].textContent === '180'); - await delay(200); - const skipRowTop = skipRow.getBoundingClientRect().top; - assert(skipRowTop >= getBodyTop()); - const tbody = container.querySelector('.next-table-body'); - tbody.scrollTop += 10; - ReactTestUtils.Simulate.scroll(tbody); - await delay(200); - const scrollRow = container.querySelectorAll('tr.next-table-row')[10]; - assert(scrollRow.children[0].textContent === '180'); - const scrollRowTop = scrollRow.getBoundingClientRect().top; - assert(scrollRowTop >= getBodyTop() - 10); + cy.mount(); + cy.get('tr.next-table-row.first button').click(); + cy.get('tr.next-table-row') + .eq(10) + .as('skipRow') + .children() + .eq(0) + .should('have.text', '180'); + cy.get('@skipRow').then($skipRow => { + const skipRowTop = $skipRow.get(0).getBoundingClientRect().top; + cy.get('thead').then($thead => { + const theadTop = $thead.get(0).getBoundingClientRect().top; + const theadHeight = $thead.get(0).getBoundingClientRect().height; + expect(skipRowTop).to.gte(theadTop + theadHeight); + }); + }); + cy.get('.next-table-body').then($tbody => { + $tbody.get(0).scrollTop += 10; + }); + cy.get('@skipRow').then($skipRow => { + const skipRowTop = $skipRow.get(0).getBoundingClientRect().top; + cy.get('thead').then($thead => { + const theadTop = $thead.get(0).getBoundingClientRect().top; + const theadHeight = $thead.get(0).getBoundingClientRect().height; + expect(skipRowTop).to.gte(theadTop + theadHeight - 10); + }); + }); }); // fix https://github.com/alibaba-fusion/next/issues/4264 // fix https://github.com/alibaba-fusion/next/issues/4716 - it('should support for merging cells in locked columns, close #4264, #4716', async () => { - const container = document.createElement('div'); - document.body.appendChild(container); + it('should support for merging cells in locked columns, close #4264, #4716', () => { + // const container = document.createElement('div'); + // document.body.appendChild(container); - const dataSource = j => { + const dataSource = (j: number) => { const result = []; for (let i = 0; i < j; i++) { result.push({ @@ -1558,7 +1207,7 @@ describe('TableScroll', () => { return result; }; - const mergeCell = (rowIndex, colIndex) => { + const mergeCell: TableProps['cellProps'] = (rowIndex, colIndex) => { if (colIndex === 0 && rowIndex === 0) { return { rowSpan: 2, @@ -1567,7 +1216,7 @@ describe('TableScroll', () => { } }; - ReactDOM.render( + cy.mount(
@@ -1576,31 +1225,29 @@ describe('TableScroll', () => { -
, - container - ); - - const titleHeaderNode = container.querySelectorAll('thead .next-table-header-node')[1]; - assert(titleHeaderNode); - const idHeaderNode = container.querySelectorAll('thead .next-table-header-node')[0]; - assert(idHeaderNode); - assert( - titleHeaderNode.getBoundingClientRect().left === - idHeaderNode.getBoundingClientRect().right - ); - - const tableNode = container.querySelector('.next-table-body'); - tableNode.scrollLeft = 900; - ReactTestUtils.Simulate.scroll(tableNode); - await delay(200); - const timeNode = container.querySelectorAll('thead .next-table-header-node')[2]; - assert(timeNode); - assert( - timeNode.getBoundingClientRect().left === titleHeaderNode.getBoundingClientRect().right + ); + cy.get('thead .next-table-header-node').eq(0).as('idHeaderNode').should('exist'); + cy.get('thead .next-table-header-node').eq(1).as('titleHeaderNode').should('exist'); + cy.get('thead .next-table-header-node').eq(2).as('timeHeaderNode').should('exist'); + cy.get('@idHeaderNode').then($id => { + const idRight = $id.get(0).getBoundingClientRect().right; + cy.get('@titleHeaderNode').then($title => { + const titleLeft = $title.get(0).getBoundingClientRect().left; + expect(titleLeft).to.equal(idRight); + }); + }); + cy.get('.next-table-body').scrollTo(900, 0); + cy.get('@timeHeaderNode').then($time => { + const timeLeft = $time.get(0).getBoundingClientRect().left; + cy.get('@titleHeaderNode').then($title => { + const titleRight = $title.get(0).getBoundingClientRect().right; + expect(timeLeft).to.equal(titleRight); + }); + }); }); - it('set keepForwardRenderRows to support large rowSpan when useVirtual, close #4395', async () => { - const datas = j => { + it('set keepForwardRenderRows to support large rowSpan when useVirtual, close #4395', () => { + const datas = (j: number) => { const result = []; for (let i = 0; i < j; i++) { result.push({ @@ -1612,13 +1259,14 @@ describe('TableScroll', () => { } return result; }; - class App extends React.Component { + type AppProps = { scrollToRow?: number; keepForwardRenderRows?: number }; + class App extends React.Component { state = { scrollToRow: 0, dataSource: datas(200), }; - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: AppProps) { if ( 'scrollToRow' in this.props && this.props.scrollToRow !== prevProps.scrollToRow @@ -1629,7 +1277,7 @@ describe('TableScroll', () => { } } - onBodyScroll = start => { + onBodyScroll: TableProps['onBodyScroll'] = start => { this.setState({ scrollToRow: start, }); @@ -1645,7 +1293,7 @@ describe('TableScroll', () => { keepForwardRenderRows={this.props.keepForwardRenderRows} scrollToRow={this.state.scrollToRow} onBodyScroll={this.onBodyScroll} - cellProps={(rowIndex, colIndex) => { + cellProps={(rowIndex: number, colIndex) => { if ([0, 17, 34].includes(rowIndex) && colIndex === 0) { return { rowSpan: 17, @@ -1660,18 +1308,10 @@ describe('TableScroll', () => { ); } } - - const container = document.createElement('div'); - document.body.appendChild(container); - const wrapper = mount(, { attachTo: container }); - await delay(100); - - wrapper.setProps({ scrollToRow: 15 }); - assert(!container.querySelector('[data-next-table-row="0"]')); - - wrapper.setProps({ keepForwardRenderRows: 17 }); - assert(container.querySelector('[data-next-table-row="0"]')); - - wrapper.unmount(); + cy.mount().as('Demo'); + cy.rerender('Demo', { scrollToRow: 15 }).as('Demo2'); + cy.get('[data-next-table-row="0"]').should('not.exist'); + cy.rerender('Demo2', { keepForwardRenderRows: 17 }); + cy.get('[data-next-table-row="0"]').should('exist'); }); }); diff --git a/components/table/base-pre.tsx b/components/table/base-pre.tsx index 072e652e5b..505701e25d 100644 --- a/components/table/base-pre.tsx +++ b/components/table/base-pre.tsx @@ -1,5 +1,5 @@ // 这个文件看起来没有被使用到 -import React, { Ref, type LegacyRef } from 'react'; +import React, { type Ref } from 'react'; import Loading from '../loading'; import BodyComponent from './base/body'; import HeaderComponent from './base/header'; diff --git a/components/table/base.tsx b/components/table/base.tsx index 9fc85a23a4..a4dd71bd47 100644 --- a/components/table/base.tsx +++ b/components/table/base.tsx @@ -17,7 +17,7 @@ import FilterComponent from './base/filter'; import SortComponent from './base/sort'; import Column from './column'; import ColumnGroup from './column-group'; -import { +import type { CellLike, RowLike, BaseTableContext, diff --git a/components/table/base/filter.tsx b/components/table/base/filter.tsx index aebd6b8166..95ffbe1a85 100644 --- a/components/table/base/filter.tsx +++ b/components/table/base/filter.tsx @@ -168,11 +168,11 @@ class Filter extends React.Component { const { visible, selectedKeys } = this.state; const { subMenuSelectable, ...others } = filterMenuProps || {}; - function renderMenuItem(item: { value: Key; label: ReactNode }) { + function renderMenuItem(item: { value?: Key; label: ReactNode }) { return {item.label}; } - function renderSubMenu(parent: { value: Key; label: ReactNode }, children: FilterItem[]) { + function renderSubMenu(parent: { value?: Key; label: ReactNode }, children: FilterItem[]) { return ( { return null; } } - if ((attrs.colSpan && attrs.colSpan > 1) || (attrs.rowSpan && attrs.rowSpan > 1)) { + if ( + (attrs.colSpan && (attrs.colSpan as number) > 1) || + (attrs.rowSpan && attrs.rowSpan > 1) + ) { this._getNotRenderCellIndex( colIndex!, rowIndex!, - attrs.colSpan || 1, + (attrs.colSpan as number) || 1, attrs.rowSpan || 1 ); } @@ -155,7 +158,7 @@ export default class Row extends React.Component { last: lockType !== 'left' && (colIndex === columns.length - 1 || - colIndex! + attrs.colSpan! === columns.length), // 考虑合并单元格的情况 + colIndex! + (attrs.colSpan as number) === columns.length), // 考虑合并单元格的情况 [child.className!]: child.className, [cellClass!]: cellClass, }); diff --git a/components/table/selection.tsx b/components/table/selection.tsx index 92aa7fb617..390278a81a 100644 --- a/components/table/selection.tsx +++ b/components/table/selection.tsx @@ -214,10 +214,10 @@ export default function selection(BaseComponent: typeof Base) { attrs = getProps(record, index) || {}; } // 反选和全选的时候不要丢弃禁用项的选中状态 - if (checked && (!attrs.disabled || selectedRowKeys.indexOf(id) > -1)) { + if (checked && (!attrs!.disabled || selectedRowKeys.indexOf(id) > -1)) { ret.push(id); records.push(record); - } else if (attrs.disabled && selectedRowKeys.indexOf(id) > -1) { + } else if (attrs!.disabled && selectedRowKeys.indexOf(id) > -1) { ret.push(id); records.push(record); } else { diff --git a/components/table/types.ts b/components/table/types.ts index f48ece9328..8ff5557f7d 100644 --- a/components/table/types.ts +++ b/components/table/types.ts @@ -10,9 +10,13 @@ import type { RadioProps } from '../radio'; interface HTMLAttributesWeak extends Omit, 'title' | 'children'> {} -export type FilterItem = { value: React.Key; label: React.ReactNode; children?: FilterItem[] }; +export type FilterItem = { value?: React.Key; label: React.ReactNode; children?: FilterItem[] }; + /** - * @api + * @api RecordItem + * @remarks RecordItem 也可能是单纯 string 的情况,但考虑到这样业务的适配成本比较高,因此这里不加入 + * - + * Record may be string, but it's hard to adapt the business, so it's not added */ export type RecordItem = Record & { children?: RecordItem[] }; @@ -347,7 +351,7 @@ export interface ColumnProps extends HTMLAttributesWeak, CommonProps { * 是否支持锁列,可选值为`left`,`right`, `true` * @en Whether to support locking, the value can be `left`, `right`, `true` */ - lock?: boolean | 'left' | 'right'; + lock?: boolean | string; /** * 是否支持列宽调整,当该值设为 true,table 的布局方式会修改为 fixed. @@ -367,7 +371,7 @@ export interface ColumnProps extends HTMLAttributesWeak, CommonProps { * header cell 横跨的格数,设置为 0 表示不出现此 th * @en The number of cells that the header cell spans, set to 0 to not appear this th */ - colSpan?: number; + colSpan?: number | string; /** * 设置该列单元格的 word-break 样式,对于 id 类、中文类适合用 all,对于英文句子适合用 word @@ -664,7 +668,10 @@ export interface RowSelection { * 获取 selection 的默认属性 * @en Get the default properties of the selection */ - getProps?: (record: RecordItem, index: number) => CheckboxProps | RadioProps; + getProps?: ( + record: RecordItem, + index: number + ) => CheckboxProps | RadioProps | void | undefined | null; /** * 选择改变的时候触发的事件,**注意:** 其中 records 只会包含当前 dataSource 的数据,很可能会小于 selectedRowKeys 的长度。 * @en The event triggered when the selection changes. **Note:** records will only contain the data in the dataSource, and it may be less than the length of selectedRowKeys. @@ -891,13 +898,13 @@ export interface TableProps * Assume you want to control the menu item with key 'one' in the filter menu of the dataIndex column * `` */ - filterParams?: { [propName: string]: { selectedKeys?: string[]; visible?: boolean } }; + filterParams?: { [propName: string]: { selectedKeys?: string[]; visible?: boolean } } | null; /** * 当前排序的字段,使用此属性可以控制表格的字段的排序,格式为\{dataIndex: 'asc'\} * @en the current sorted field, using this property can control the sorting of the table's fields, the format is \{dataIndex: 'asc'\} */ - sort?: { [key: string]: SortOrder }; + sort?: { [key: string]: SortOrder } | null; /** * 自定义排序按钮,例如上下排布的:`{desc: , asc: }` @@ -928,7 +935,7 @@ export interface TableProps * 展开的行,传入后展开状态只受此属性控制 * @en expanded row, after passing, the expanded state is only controlled by this property. */ - openRowKeys?: Array; + openRowKeys?: Array | null; /** * 默认展开的行 * @en the default expanded row @@ -950,9 +957,11 @@ export interface TableProps getExpandedColProps?: ( record: RecordItem, index: number - ) => React.DetailedHTMLProps, HTMLSpanElement> & { - disabled?: boolean; - }; + ) => + | (React.DetailedHTMLProps, HTMLSpanElement> & { + disabled?: boolean; + }) + | void; /** * 在额外渲染行或者树展开或者收起的时候触发的事件 diff --git a/components/table/util.ts b/components/table/util.ts index c6ebab2b4e..77df9b32be 100644 --- a/components/table/util.ts +++ b/components/table/util.ts @@ -111,7 +111,7 @@ export const setStickyStyle = ( let nodesLen = 0; const arrLen = (Array.isArray(node && node.children) && node!.children!.length) || 0; if (arrLen > 0) { - nodesLen = node!.children!.reduce((ret, item, idx) => { + nodesLen = node!.children!.reduce((ret, item) => { // @ts-expect-error 这里实现感觉有些问题,应该传入的是 item return ret + getLeafNodes(item.children); }, 0); diff --git a/components/table/virtual.tsx b/components/table/virtual.tsx index 4eafe59abd..0a0f711781 100644 --- a/components/table/virtual.tsx +++ b/components/table/virtual.tsx @@ -291,7 +291,7 @@ export default function virtual(BaseComponent: typeof Base) { components = { ...components }; const { start, end } = this.getVisibleRange(this.state.scrollToRow!); let count = -1; - dataSource!.forEach((current, index, record) => { + dataSource!.forEach((current, index) => { if (!current.__hidden) { count += 1; if (count >= Math.max(start - keepForwardRenderRows!, 0) && count < end) {