mirror of
https://github.com/zhigang1992/react.git
synced 2026-04-29 04:35:32 +08:00
refactor(table): redesign interfaces to improve the experience in TypeScript (#569)
* refactor(table): redesign interfaces to improve the experience in TypeScript * docs: upgrade to new type exports * style: fix lint warnings
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Table should be no erros when width is too large 1`] = `
|
||||
"<table class=\\"\\"><colgroup></colgroup><thead><tr><th><div class=\\"thead-box\\">property</div></th><th><div class=\\"thead-box\\">description</div></th><th><div class=\\"thead-box\\">default</div></th></tr></thead><style>
|
||||
"<table class=\\"\\"><colgroup></colgroup><thead><tr><th class=\\"\\"><div class=\\"thead-box\\">property</div></th><th class=\\"\\"><div class=\\"thead-box\\">description</div></th><th class=\\"\\"><div class=\\"thead-box\\">default</div></th></tr></thead><style>
|
||||
thead {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
@@ -46,7 +46,7 @@ exports[`Table should be no erros when width is too large 1`] = `
|
||||
min-height: calc(2.5 * var(--table-font-size));
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style><tbody><tr class=\\"hover\\"><td><div class=\\"cell\\">type</div></td><td><div class=\\"cell\\">Content type</div></td><td><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover\\"><td><div class=\\"cell\\">Component</div></td><td><div class=\\"cell\\">DOM element to use</div></td><td><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover\\"><td><div class=\\"cell\\">bold</div></td><td><div class=\\"cell\\">Bold style</div></td><td><div class=\\"cell\\">true</div></td></tr><style>
|
||||
</style><tbody><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">type</div></td><td class=\\"\\"><div class=\\"cell\\">Content type</div></td><td class=\\"\\"><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">Component</div></td><td class=\\"\\"><div class=\\"cell\\">DOM element to use</div></td><td class=\\"\\"><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">bold</div></td><td class=\\"\\"><div class=\\"cell\\">Bold style</div></td><td class=\\"\\"><div class=\\"cell\\">true</div></td></tr><style>
|
||||
tr {
|
||||
transition: background-color 0.25s ease;
|
||||
font-size: inherit;
|
||||
@@ -86,7 +86,7 @@ exports[`Table should be no erros when width is too large 1`] = `
|
||||
`;
|
||||
|
||||
exports[`Table should render children for table head 1`] = `
|
||||
"<table class=\\"\\"><colgroup></colgroup><thead><tr><th><div class=\\"thead-box\\"><code>property</code></div></th></tr></thead><style>
|
||||
"<table class=\\"\\"><colgroup></colgroup><thead><tr><th class=\\"\\"><div class=\\"thead-box\\"><code>property</code></div></th></tr></thead><style>
|
||||
thead {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
@@ -131,7 +131,7 @@ exports[`Table should render children for table head 1`] = `
|
||||
min-height: calc(2.5 * var(--table-font-size));
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style><tbody><tr class=\\"hover\\"><td><div class=\\"cell\\">type</div></td></tr><tr class=\\"hover\\"><td><div class=\\"cell\\">Component</div></td></tr><tr class=\\"hover\\"><td><div class=\\"cell\\">bold</div></td></tr><style>
|
||||
</style><tbody><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">type</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">Component</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">bold</div></td></tr><style>
|
||||
tr {
|
||||
transition: background-color 0.25s ease;
|
||||
font-size: inherit;
|
||||
@@ -171,7 +171,7 @@ exports[`Table should render children for table head 1`] = `
|
||||
`;
|
||||
|
||||
exports[`Table should render correctly 1`] = `
|
||||
"<table class=\\"\\"><colgroup></colgroup><thead><tr><th><div class=\\"thead-box\\">property</div></th><th><div class=\\"thead-box\\">description</div></th><th><div class=\\"thead-box\\">default</div></th></tr></thead><style>
|
||||
"<table class=\\"\\"><colgroup></colgroup><thead><tr><th class=\\"\\"><div class=\\"thead-box\\">property</div></th><th class=\\"\\"><div class=\\"thead-box\\">description</div></th><th class=\\"\\"><div class=\\"thead-box\\">default</div></th></tr></thead><style>
|
||||
thead {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
@@ -216,7 +216,7 @@ exports[`Table should render correctly 1`] = `
|
||||
min-height: calc(2.5 * var(--table-font-size));
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style><tbody><tr class=\\"hover\\"><td><div class=\\"cell\\">type</div></td><td><div class=\\"cell\\">Content type</div></td><td><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover\\"><td><div class=\\"cell\\">Component</div></td><td><div class=\\"cell\\">DOM element to use</div></td><td><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover\\"><td><div class=\\"cell\\">bold</div></td><td><div class=\\"cell\\">Bold style</div></td><td><div class=\\"cell\\">true</div></td></tr><style>
|
||||
</style><tbody><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">type</div></td><td class=\\"\\"><div class=\\"cell\\">Content type</div></td><td class=\\"\\"><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">Component</div></td><td class=\\"\\"><div class=\\"cell\\">DOM element to use</div></td><td class=\\"\\"><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">bold</div></td><td class=\\"\\"><div class=\\"cell\\">Bold style</div></td><td class=\\"\\"><div class=\\"cell\\">true</div></td></tr><style>
|
||||
tr {
|
||||
transition: background-color 0.25s ease;
|
||||
font-size: inherit;
|
||||
@@ -256,7 +256,7 @@ exports[`Table should render correctly 1`] = `
|
||||
`;
|
||||
|
||||
exports[`Table should set width automatically 1`] = `
|
||||
"<table class=\\"\\"><colgroup><col width=\\"25\\"><col width=\\"25\\"><col width=\\"50\\"></colgroup><thead><tr><th><div class=\\"thead-box\\">property</div></th><th><div class=\\"thead-box\\">description</div></th><th><div class=\\"thead-box\\">default</div></th></tr></thead><style>
|
||||
"<table class=\\"\\"><colgroup><col width=\\"25\\"><col width=\\"25\\"><col width=\\"50\\"></colgroup><thead><tr><th class=\\"\\"><div class=\\"thead-box\\">property</div></th><th class=\\"\\"><div class=\\"thead-box\\">description</div></th><th class=\\"\\"><div class=\\"thead-box\\">default</div></th></tr></thead><style>
|
||||
thead {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
@@ -301,7 +301,92 @@ exports[`Table should set width automatically 1`] = `
|
||||
min-height: calc(2.5 * var(--table-font-size));
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style><tbody><tr class=\\"hover\\"><td><div class=\\"cell\\">type</div></td><td><div class=\\"cell\\">Content type</div></td><td><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover\\"><td><div class=\\"cell\\">Component</div></td><td><div class=\\"cell\\">DOM element to use</div></td><td><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover\\"><td><div class=\\"cell\\">bold</div></td><td><div class=\\"cell\\">Bold style</div></td><td><div class=\\"cell\\">true</div></td></tr><style>
|
||||
</style><tbody><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">type</div></td><td class=\\"\\"><div class=\\"cell\\">Content type</div></td><td class=\\"\\"><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">Component</div></td><td class=\\"\\"><div class=\\"cell\\">DOM element to use</div></td><td class=\\"\\"><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">bold</div></td><td class=\\"\\"><div class=\\"cell\\">Bold style</div></td><td class=\\"\\"><div class=\\"cell\\">true</div></td></tr><style>
|
||||
tr {
|
||||
transition: background-color 0.25s ease;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
tr.hover:hover {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
tr :global(td) {
|
||||
padding: 0 0.5em;
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
color: #444;
|
||||
font-size: calc(0.875 * var(--table-font-size));
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
tr :global(.cell) {
|
||||
min-height: calc(3.125 * var(--table-font-size));
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
</style></tbody><style>
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
--table-font-size: calc(1 * 16px);
|
||||
font-size: var(--table-font-size);
|
||||
width: 100%;
|
||||
height: auto;
|
||||
padding: 0 0 0 0;
|
||||
margin: 0 0 0 0;
|
||||
}
|
||||
</style></table>"
|
||||
`;
|
||||
|
||||
exports[`Table should work correctly with multiple identical props 1`] = `
|
||||
"<table class=\\"\\"><colgroup></colgroup><thead><tr><th class=\\"\\"><div class=\\"thead-box\\">property3</div></th><th class=\\"\\"><div class=\\"thead-box\\">description2</div></th></tr></thead><style>
|
||||
thead {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 0 0.5em;
|
||||
font-size: calc(0.75 * var(--table-font-size));
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
letter-spacing: 0;
|
||||
vertical-align: middle;
|
||||
min-height: calc(2.5 * var(--table-font-size));
|
||||
color: #666;
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
border-top: 1px solid #eaeaea;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
th:nth-child(1) {
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
border-left: 1px solid #eaeaea;
|
||||
border-top: 1px solid #eaeaea;
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
th:last-child {
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
border-right: 1px solid #eaeaea;
|
||||
border-top: 1px solid #eaeaea;
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.thead-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
-webkit-box-align: center;
|
||||
min-height: calc(2.5 * var(--table-font-size));
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style><tbody><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">type</div></td><td class=\\"\\"><div class=\\"cell\\">Content type</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">Component</div></td><td class=\\"\\"><div class=\\"cell\\">DOM element to use</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">bold</div></td><td class=\\"\\"><div class=\\"cell\\">Bold style</div></td></tr><style>
|
||||
tr {
|
||||
transition: background-color 0.25s ease;
|
||||
font-size: inherit;
|
||||
@@ -341,7 +426,7 @@ exports[`Table should set width automatically 1`] = `
|
||||
`;
|
||||
|
||||
exports[`Table should work with other components 1`] = `
|
||||
"<table class=\\"\\"><colgroup></colgroup><thead><tr><th><div class=\\"thead-box\\">property</div></th><th><div class=\\"thead-box\\">description</div></th><th><div class=\\"thead-box\\">default</div></th></tr></thead><style>
|
||||
"<table class=\\"\\"><colgroup></colgroup><thead><tr><th class=\\"\\"><div class=\\"thead-box\\">property</div></th><th class=\\"\\"><div class=\\"thead-box\\">description</div></th><th class=\\"\\"><div class=\\"thead-box\\">default</div></th></tr></thead><style>
|
||||
thead {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
@@ -386,7 +471,7 @@ exports[`Table should work with other components 1`] = `
|
||||
min-height: calc(2.5 * var(--table-font-size));
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style><tbody><tr class=\\"hover\\"><td><div class=\\"cell\\">type</div></td><td><div class=\\"cell\\">Content type</div></td><td><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover\\"><td><div class=\\"cell\\">Component</div></td><td><div class=\\"cell\\">DOM element to use</div></td><td><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover\\"><td><div class=\\"cell\\">bold</div></td><td><div class=\\"cell\\">Bold style</div></td><td><div class=\\"cell\\">true</div></td></tr><tr class=\\"hover\\"><td><div class=\\"cell\\">bold</div></td><td><div class=\\"cell\\"><code>boolean</code></div></td><td><div class=\\"cell\\">true</div></td></tr><style>
|
||||
</style><tbody><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">type</div></td><td class=\\"\\"><div class=\\"cell\\">Content type</div></td><td class=\\"\\"><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">Component</div></td><td class=\\"\\"><div class=\\"cell\\">DOM element to use</div></td><td class=\\"\\"><div class=\\"cell\\">-</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">bold</div></td><td class=\\"\\"><div class=\\"cell\\">Bold style</div></td><td class=\\"\\"><div class=\\"cell\\">true</div></td></tr><tr class=\\"hover \\"><td class=\\"\\"><div class=\\"cell\\">bold</div></td><td class=\\"\\"><div class=\\"cell\\"><code>boolean</code></div></td><td class=\\"\\"><div class=\\"cell\\">true</div></td></tr><style>
|
||||
tr {
|
||||
transition: background-color 0.25s ease;
|
||||
font-size: inherit;
|
||||
@@ -426,7 +511,7 @@ exports[`Table should work with other components 1`] = `
|
||||
`;
|
||||
|
||||
exports[`Table should work without hover effect 1`] = `
|
||||
"<table class=\\"\\"><colgroup></colgroup><thead><tr><th><div class=\\"thead-box\\">property</div></th><th><div class=\\"thead-box\\">description</div></th><th><div class=\\"thead-box\\">default</div></th></tr></thead><style>
|
||||
"<table class=\\"\\"><colgroup></colgroup><thead><tr><th class=\\"\\"><div class=\\"thead-box\\">property</div></th><th class=\\"\\"><div class=\\"thead-box\\">description</div></th><th class=\\"\\"><div class=\\"thead-box\\">default</div></th></tr></thead><style>
|
||||
thead {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
@@ -471,7 +556,7 @@ exports[`Table should work without hover effect 1`] = `
|
||||
min-height: calc(2.5 * var(--table-font-size));
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style><tbody><tr class=\\"\\"><td><div class=\\"cell\\">type</div></td><td><div class=\\"cell\\">Content type</div></td><td><div class=\\"cell\\">-</div></td></tr><tr class=\\"\\"><td><div class=\\"cell\\">Component</div></td><td><div class=\\"cell\\">DOM element to use</div></td><td><div class=\\"cell\\">-</div></td></tr><tr class=\\"\\"><td><div class=\\"cell\\">bold</div></td><td><div class=\\"cell\\">Bold style</div></td><td><div class=\\"cell\\">true</div></td></tr><style>
|
||||
</style><tbody><tr class=\\" \\"><td class=\\"\\"><div class=\\"cell\\">type</div></td><td class=\\"\\"><div class=\\"cell\\">Content type</div></td><td class=\\"\\"><div class=\\"cell\\">-</div></td></tr><tr class=\\" \\"><td class=\\"\\"><div class=\\"cell\\">Component</div></td><td class=\\"\\"><div class=\\"cell\\">DOM element to use</div></td><td class=\\"\\"><div class=\\"cell\\">-</div></td></tr><tr class=\\" \\"><td class=\\"\\"><div class=\\"cell\\">bold</div></td><td class=\\"\\"><div class=\\"cell\\">Bold style</div></td><td class=\\"\\"><div class=\\"cell\\">true</div></td></tr><style>
|
||||
tr {
|
||||
transition: background-color 0.25s ease;
|
||||
font-size: inherit;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import { mount } from 'enzyme'
|
||||
import { Table, Code } from 'components'
|
||||
import { TableCellActions } from '../table-cell'
|
||||
import { nativeEvent, updateWrapper } from 'tests/utils'
|
||||
import { act } from 'react-dom/test-utils'
|
||||
import { TableColumnRender } from 'components/table/table-types'
|
||||
|
||||
const data = [
|
||||
{ property: 'type', description: 'Content type', default: '-' },
|
||||
@@ -24,6 +24,20 @@ describe('Table', () => {
|
||||
expect(() => wrapper.unmount()).not.toThrow()
|
||||
})
|
||||
|
||||
it('should work correctly with multiple identical props', () => {
|
||||
const wrapper = mount(
|
||||
<Table data={data}>
|
||||
<Table.Column prop="property" label="property" />
|
||||
<Table.Column prop="description" label="description" />
|
||||
<Table.Column prop="property" label="property2" />
|
||||
<Table.Column prop="property" label="property3" />
|
||||
<Table.Column prop="description" label="description2" />
|
||||
</Table>,
|
||||
)
|
||||
expect(wrapper.html()).toMatchSnapshot()
|
||||
expect(() => wrapper.unmount()).not.toThrow()
|
||||
})
|
||||
|
||||
it('should re-render when data changed', async () => {
|
||||
const wrapper = mount(
|
||||
<Table data={data}>
|
||||
@@ -99,47 +113,39 @@ describe('Table', () => {
|
||||
expect(() => wrapper.unmount()).not.toThrow()
|
||||
})
|
||||
|
||||
it('should be possible to remove the row', () => {
|
||||
const operation = (actions: TableCellActions) => {
|
||||
return <button onClick={() => actions.remove()}>Remove</button>
|
||||
it('should be render specified elements', async () => {
|
||||
type Item = {
|
||||
property: string
|
||||
description: string
|
||||
operation: string
|
||||
}
|
||||
const data = [{ property: 'bold', description: 'boolean', operation }]
|
||||
const wrapper = mount(
|
||||
<Table data={data}>
|
||||
<Table.Column prop="property" label="property" />
|
||||
<Table.Column prop="description" label="description" />
|
||||
<Table.Column prop="operation" label="operation" />
|
||||
</Table>,
|
||||
)
|
||||
expect(wrapper.find('tbody').find('tr').length).toBe(1)
|
||||
wrapper.find('tbody').find('button').simulate('click')
|
||||
expect(wrapper.find('tbody').find('tr').length).toBe(0)
|
||||
expect(() => wrapper.unmount()).not.toThrow()
|
||||
})
|
||||
|
||||
it('should be possible to update the row', () => {
|
||||
const operation = (actions: TableCellActions) => {
|
||||
const renderAction: TableColumnRender<Item> = (value, rowData, index) => {
|
||||
return (
|
||||
<button
|
||||
onClick={() =>
|
||||
actions.update({ property: 'test', description: 'test', operation })
|
||||
}>
|
||||
Update
|
||||
</button>
|
||||
<div>
|
||||
<button id="test-btn">Remove</button>
|
||||
<div id="value">{value}</div>
|
||||
<div id="row-data">{rowData.description}</div>
|
||||
<div id="row-index">{index}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
const operation = Math.random().toString(16).slice(-10)
|
||||
const data = [{ property: 'bold', description: 'boolean', operation }]
|
||||
const wrapper = mount(
|
||||
<Table data={data}>
|
||||
<Table.Column prop="property" label="property" />
|
||||
<Table.Column prop="description" label="description" />
|
||||
<Table.Column prop="operation" label="operation" />
|
||||
<Table<Item> data={data}>
|
||||
<Table.Column<Item> prop="property" label="property" />
|
||||
<Table.Column<Item> prop="description" label="description" />
|
||||
<Table.Column<Item> prop="operation" label="operation" render={renderAction} />
|
||||
</Table>,
|
||||
)
|
||||
expect(wrapper.find('tbody').find('tr').length).toBe(1)
|
||||
wrapper.find('tbody').find('button').simulate('click')
|
||||
expect(wrapper.find('tbody').find('tr').find('td').first().text()).toContain('test')
|
||||
expect(() => wrapper.unmount()).not.toThrow()
|
||||
const buttons = wrapper.find('tbody').find('#test-btn')
|
||||
expect(buttons.length).not.toEqual(0)
|
||||
const value = wrapper.find('tbody').find('#value').html()
|
||||
expect(value).toMatch(operation)
|
||||
const rowData = wrapper.find('tbody').find('#row-data').html()
|
||||
expect(rowData).toMatch(`${data[0].description}`)
|
||||
const rowIndex = wrapper.find('tbody').find('#row-index').html()
|
||||
expect(rowIndex).toMatch(`0`)
|
||||
})
|
||||
|
||||
it('should render emptyText when data missing', () => {
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
import Table from './table'
|
||||
import TableColumn from './table-column'
|
||||
|
||||
export type TableComponentType = typeof Table & {
|
||||
Column: typeof TableColumn
|
||||
}
|
||||
;(Table as TableComponentType).Column = TableColumn
|
||||
|
||||
export type {
|
||||
TableProps,
|
||||
TableOnRow,
|
||||
TableOnChange,
|
||||
TableOnCell,
|
||||
TableDataSource,
|
||||
} from './table'
|
||||
export type { TableProps } from './table'
|
||||
export type { TableColumnProps } from './table-column'
|
||||
export type {
|
||||
TableOperation,
|
||||
TableCellActions,
|
||||
TableCellActionRemove,
|
||||
TableCellActionUpdate,
|
||||
TableCellData,
|
||||
} from './table-cell'
|
||||
export default Table as TableComponentType
|
||||
TableOnCellClick,
|
||||
TableAbstractColumn,
|
||||
TableOnChange,
|
||||
TableOnRowClick,
|
||||
TableRowClassNameHandler,
|
||||
TableDataItemBase,
|
||||
TableColumnRender,
|
||||
} from './table-types'
|
||||
export default Table
|
||||
|
||||
@@ -2,45 +2,54 @@ import React from 'react'
|
||||
import useTheme from '../use-theme'
|
||||
import TableCell from './table-cell'
|
||||
import { useTableContext } from './table-context'
|
||||
import {
|
||||
TableDataItemBase,
|
||||
TableOnCellClick,
|
||||
TableOnRowClick,
|
||||
TableRowClassNameHandler,
|
||||
} from './table-types'
|
||||
|
||||
interface Props {
|
||||
interface Props<TableDataItem extends TableDataItemBase> {
|
||||
hover: boolean
|
||||
emptyText: string
|
||||
onRow: (row: any, index: number) => void
|
||||
onCell: (cell: any, index: number, colunm: number) => void
|
||||
data: Array<any>
|
||||
onRow?: TableOnRowClick<TableDataItem>
|
||||
onCell?: TableOnCellClick<TableDataItem>
|
||||
data: Array<TableDataItem>
|
||||
className?: string
|
||||
rowClassName: TableRowClassNameHandler<TableDataItem>
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
className: '',
|
||||
}
|
||||
|
||||
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
|
||||
export type TableBodyProps = Props & NativeAttrs
|
||||
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props<any>>
|
||||
export type TableBodyProps<TableDataItem> = Props<TableDataItem> & NativeAttrs
|
||||
|
||||
const TableBody: React.FC<TableBodyProps> = ({
|
||||
const TableBody = <TableDataItem extends TableDataItemBase>({
|
||||
data,
|
||||
hover,
|
||||
emptyText,
|
||||
onRow,
|
||||
onCell,
|
||||
}: TableBodyProps & typeof defaultProps) => {
|
||||
rowClassName,
|
||||
}: TableBodyProps<TableDataItem> & typeof defaultProps) => {
|
||||
const theme = useTheme()
|
||||
const { columns } = useTableContext()
|
||||
const rowClickHandler = (row: any, index: number) => {
|
||||
onRow(row, index)
|
||||
const { columns } = useTableContext<TableDataItem>()
|
||||
const rowClickHandler = (row: TableDataItem, index: number) => {
|
||||
onRow && onRow(row, index)
|
||||
}
|
||||
|
||||
return (
|
||||
<tbody>
|
||||
{data.map((row, index) => {
|
||||
const className = rowClassName(row, index)
|
||||
return (
|
||||
<tr
|
||||
key={`tbody-row-${index}`}
|
||||
className={hover ? 'hover' : ''}
|
||||
className={`${hover ? 'hover' : ''} ${className}`}
|
||||
onClick={() => rowClickHandler(row, index)}>
|
||||
<TableCell
|
||||
<TableCell<TableDataItem>
|
||||
columns={columns}
|
||||
row={row}
|
||||
rowIndex={index}
|
||||
|
||||
@@ -1,65 +1,47 @@
|
||||
import React from 'react'
|
||||
import { TableColumnItem, useTableContext } from './table-context'
|
||||
import { TableDataItemBase, TableAbstractColumn, TableOnCellClick } from './table-types'
|
||||
|
||||
interface Props {
|
||||
columns: Array<TableColumnItem>
|
||||
row: any
|
||||
interface Props<TableDataItem extends TableDataItemBase> {
|
||||
columns: Array<TableAbstractColumn<TableDataItem>>
|
||||
row: TableDataItem
|
||||
rowIndex: number
|
||||
emptyText: string
|
||||
onCellClick: (cell: any, rowIndex: number, colunmIndex: number) => void
|
||||
onCellClick?: TableOnCellClick<TableDataItem>
|
||||
}
|
||||
|
||||
export type TableCellData = {
|
||||
export type TableCellData<TableDataItem> = {
|
||||
row: number
|
||||
column: number
|
||||
rowValue: any
|
||||
rowValue: TableDataItem
|
||||
}
|
||||
|
||||
export type TableCellActionRemove = () => void
|
||||
export type TableCellActionUpdate = (data: any) => void
|
||||
export type TableCellActions = {
|
||||
update: TableCellActionUpdate
|
||||
remove: TableCellActionRemove
|
||||
}
|
||||
export type TableOperation = (fn: TableCellActions, rowData: any) => any
|
||||
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props<any>>
|
||||
export type TableCellProps<TableDataItem extends TableDataItemBase> =
|
||||
Props<TableDataItem> & NativeAttrs
|
||||
|
||||
const TableCell: React.FC<Props> = ({
|
||||
const TableCell = <TableDataItem extends TableDataItemBase>({
|
||||
columns,
|
||||
row,
|
||||
rowIndex,
|
||||
emptyText,
|
||||
onCellClick,
|
||||
}) => {
|
||||
const { removeRow, updateRow } = useTableContext()
|
||||
const actions: TableCellActions = {
|
||||
update: data => {
|
||||
updateRow && updateRow(rowIndex, data)
|
||||
},
|
||||
remove: () => {
|
||||
removeRow && removeRow(rowIndex)
|
||||
},
|
||||
}
|
||||
}: TableCellProps<TableDataItem>) => {
|
||||
/* eslint-disable react/jsx-no-useless-fragment */
|
||||
return (
|
||||
<>
|
||||
{columns.map((column, index) => {
|
||||
const data: TableCellData = {
|
||||
row: rowIndex,
|
||||
column: index,
|
||||
rowValue: row,
|
||||
}
|
||||
const rowLabel = row[column.value]
|
||||
const cellValue = !rowLabel
|
||||
? emptyText
|
||||
: typeof rowLabel === 'function'
|
||||
? rowLabel(actions, data)
|
||||
: rowLabel
|
||||
const currentRowValue = row[column.prop]
|
||||
const cellValue = currentRowValue || emptyText
|
||||
const shouldBeRenderElement = column.renderHandler(currentRowValue, row, rowIndex)
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`row-td-${index}-${column.value}`}
|
||||
onClick={() => onCellClick(cellValue, rowIndex, index)}>
|
||||
<div className="cell">{cellValue}</div>
|
||||
key={`row-td-${index}-${column.prop}`}
|
||||
onClick={() => onCellClick && onCellClick(currentRowValue, rowIndex, index)}
|
||||
className={column.className}>
|
||||
<div className="cell">
|
||||
{shouldBeRenderElement ? shouldBeRenderElement : cellValue}
|
||||
</div>
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
@@ -68,4 +50,4 @@ const TableCell: React.FC<Props> = ({
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
||||
export default React.memo(TableCell)
|
||||
export default TableCell
|
||||
|
||||
@@ -1,36 +1,52 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTableContext } from './table-context'
|
||||
import useWarning from '../utils/use-warning'
|
||||
import { TableColumnRender, TableDataItemBase } from './table-types'
|
||||
|
||||
interface Props {
|
||||
prop: string
|
||||
label?: string
|
||||
width?: number
|
||||
const defaultProps = {
|
||||
className: '',
|
||||
render: () => {},
|
||||
}
|
||||
|
||||
export type TableColumnProps = Props
|
||||
export type TableColumnProps<TableDataItem> = {
|
||||
prop: keyof TableDataItem
|
||||
label?: string
|
||||
width?: number
|
||||
className?: string
|
||||
render?: TableColumnRender<TableDataItem>
|
||||
}
|
||||
|
||||
const TableColumn: React.FC<React.PropsWithChildren<TableColumnProps>> = ({
|
||||
children,
|
||||
prop,
|
||||
label,
|
||||
width,
|
||||
}) => {
|
||||
const { updateColumn } = useTableContext()
|
||||
if (!prop || prop.trim() === '') {
|
||||
const TableColumn = <TableDataItem extends TableDataItemBase>(
|
||||
columnProps: React.PropsWithChildren<TableColumnProps<TableDataItem>>,
|
||||
) => {
|
||||
const {
|
||||
children,
|
||||
prop,
|
||||
label,
|
||||
width,
|
||||
className,
|
||||
render: renderHandler,
|
||||
} = columnProps as React.PropsWithChildren<TableColumnProps<TableDataItem>> &
|
||||
typeof defaultProps
|
||||
const { updateColumn } = useTableContext<TableDataItem>()
|
||||
const safeProp = `${prop}`.trim()
|
||||
if (!safeProp) {
|
||||
useWarning('The props "prop" is required.', 'Table.Column')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
updateColumn &&
|
||||
updateColumn({
|
||||
label: children || label,
|
||||
value: `${prop}`.trim(),
|
||||
width,
|
||||
})
|
||||
}, [children, label, prop, width])
|
||||
updateColumn({
|
||||
label: children || label,
|
||||
prop: safeProp,
|
||||
width,
|
||||
className,
|
||||
renderHandler,
|
||||
})
|
||||
}, [children, label, prop, width, className, renderHandler])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
TableColumn.defaultProps = defaultProps
|
||||
TableColumn.displayName = 'GiestTableColumn'
|
||||
export default TableColumn
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
import React from 'react'
|
||||
import { TableAbstractColumn } from './table-types'
|
||||
|
||||
export type TableColumnItem = {
|
||||
value: string
|
||||
label: React.ReactNode | string
|
||||
width?: number
|
||||
}
|
||||
|
||||
export interface TableConfig {
|
||||
columns: Array<TableColumnItem>
|
||||
updateColumn?: (column: TableColumnItem) => void
|
||||
removeRow?: (rowIndex: number) => void
|
||||
updateRow?: (rowIndex: number, newData: any) => void
|
||||
export interface TableConfig<T> {
|
||||
columns: Array<TableAbstractColumn<T>>
|
||||
updateColumn: (column: TableAbstractColumn<T>) => void
|
||||
}
|
||||
|
||||
const defaultContext = {
|
||||
columns: [],
|
||||
updateColumn: () => {},
|
||||
}
|
||||
|
||||
export const TableContext = React.createContext<TableConfig>(defaultContext)
|
||||
export const TableContext = React.createContext<TableConfig<any>>(defaultContext)
|
||||
|
||||
export const useTableContext = (): TableConfig =>
|
||||
React.useContext<TableConfig>(TableContext)
|
||||
export const useTableContext = <T>(): TableConfig<T> =>
|
||||
React.useContext<TableConfig<T>>(TableContext)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import useTheme from '../use-theme'
|
||||
import { TableColumnItem } from './table-context'
|
||||
import { TableAbstractColumn, TableDataItemBase } from './table-types'
|
||||
|
||||
interface Props {
|
||||
interface Props<TableDataItem extends TableDataItemBase> {
|
||||
width: number
|
||||
columns: Array<TableColumnItem>
|
||||
columns: Array<TableAbstractColumn<TableDataItem>>
|
||||
className?: string
|
||||
}
|
||||
|
||||
@@ -12,10 +12,13 @@ const defaultProps = {
|
||||
className: '',
|
||||
}
|
||||
|
||||
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
|
||||
export type TableHeadProps = Props & NativeAttrs
|
||||
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props<any>>
|
||||
export type TableHeadProps<TableDataItem> = Props<TableDataItem> & NativeAttrs
|
||||
|
||||
const makeColgroup = (width: number, columns: Array<TableColumnItem>) => {
|
||||
const makeColgroup = <TableDataItem,>(
|
||||
width: number,
|
||||
columns: Array<TableAbstractColumn<TableDataItem>>,
|
||||
) => {
|
||||
const unsetWidthCount = columns.filter(c => !c.width).length
|
||||
const customWidthTotal = columns.reduce((pre, current) => {
|
||||
return current.width ? pre + current.width : pre
|
||||
@@ -31,11 +34,11 @@ const makeColgroup = (width: number, columns: Array<TableColumnItem>) => {
|
||||
)
|
||||
}
|
||||
|
||||
const TableHead: React.FC<TableHeadProps> = ({
|
||||
columns,
|
||||
width,
|
||||
}: TableHeadProps & typeof defaultProps) => {
|
||||
const TableHead = <TableDataItem extends TableDataItemBase>(
|
||||
props: TableHeadProps<TableDataItem>,
|
||||
) => {
|
||||
const theme = useTheme()
|
||||
const { columns, width } = props as TableHeadProps<TableDataItem> & typeof defaultProps
|
||||
const isScalableWidth = useMemo(() => columns.find(item => !!item.width), [columns])
|
||||
const colgroup = useMemo(() => {
|
||||
if (!isScalableWidth) return <colgroup />
|
||||
@@ -48,7 +51,7 @@ const TableHead: React.FC<TableHeadProps> = ({
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map((column, index) => (
|
||||
<th key={`table-th-${column.value}-${index}`}>
|
||||
<th key={`table-th-${column.prop}-${index}`} className={column.className}>
|
||||
<div className="thead-box">{column.label}</div>
|
||||
</th>
|
||||
))}
|
||||
|
||||
32
components/table/table-types.ts
Normal file
32
components/table/table-types.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from 'react'
|
||||
|
||||
export type TableDataItemBase = Record<string, any>
|
||||
|
||||
export type TableColumnRender<Item extends TableDataItemBase> = (
|
||||
value: Item[keyof Item],
|
||||
rowData: Item,
|
||||
rowIndex: number,
|
||||
) => JSX.Element | void
|
||||
|
||||
export type TableAbstractColumn<TableDataItem> = {
|
||||
prop: keyof TableDataItem
|
||||
label: React.ReactNode | string
|
||||
className: string
|
||||
width?: number
|
||||
renderHandler: TableColumnRender<TableDataItem>
|
||||
}
|
||||
|
||||
export type TableOnRowClick<TableDataItem> = (
|
||||
rowData: TableDataItem,
|
||||
rowIndex: number,
|
||||
) => void
|
||||
export type TableOnCellClick<TableDataItem> = (
|
||||
cellValue: TableDataItem[keyof TableDataItem],
|
||||
rowIndex: number,
|
||||
colunmIndex: number,
|
||||
) => void
|
||||
export type TableOnChange<TableDataItem> = (data: Array<TableDataItem>) => void
|
||||
export type TableRowClassNameHandler<TableDataItem> = (
|
||||
rowData: TableDataItem,
|
||||
rowIndex: number,
|
||||
) => string
|
||||
@@ -3,107 +3,102 @@ import TableHead from './table-head'
|
||||
import TableBody from './table-body'
|
||||
import useRealShape from '../utils/use-real-shape'
|
||||
import useResize from '../utils/use-resize'
|
||||
import { TableContext, TableColumnItem, TableConfig } from './table-context'
|
||||
import useCurrentState from '../utils/use-current-state'
|
||||
import { TableOperation } from './table-cell'
|
||||
import useScaleable, { withScaleable } from '../use-scaleable'
|
||||
import { TableContext, TableConfig } from './table-context'
|
||||
import {
|
||||
TableAbstractColumn,
|
||||
TableDataItemBase,
|
||||
TableOnCellClick,
|
||||
TableOnChange,
|
||||
TableOnRowClick,
|
||||
TableRowClassNameHandler,
|
||||
} from './table-types'
|
||||
import useScaleable, { ScaleableProps, withScaleable } from '../use-scaleable'
|
||||
import TableColumn from './table-column'
|
||||
|
||||
export type TableOnRow = (row: any, index: number) => void
|
||||
export type TableOnCell = (cell: any, index: number, colunm: number) => void
|
||||
export type TableOnChange = (data: any) => void
|
||||
export type TableDataSource<T extends { [key: string]: any }> = T & {
|
||||
operation?: TableOperation
|
||||
}
|
||||
|
||||
interface Props {
|
||||
data?: Array<TableDataSource<any>>
|
||||
interface Props<TableDataItem extends TableDataItemBase> {
|
||||
data?: Array<TableDataItem>
|
||||
initialData?: Array<TableDataItem>
|
||||
emptyText?: string
|
||||
hover?: boolean
|
||||
onRow?: TableOnRow
|
||||
onCell?: TableOnCell
|
||||
onChange?: TableOnChange
|
||||
onRow?: TableOnRowClick<TableDataItem>
|
||||
onCell?: TableOnCellClick<TableDataItem>
|
||||
onChange?: TableOnChange<TableDataItem>
|
||||
className?: string
|
||||
rowClassName?: TableRowClassNameHandler<TableDataItem>
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
hover: true,
|
||||
initialData: [],
|
||||
emptyText: '',
|
||||
onRow: (() => {}) as TableOnRow,
|
||||
onCell: (() => {}) as TableOnCell,
|
||||
onChange: (() => {}) as TableOnChange,
|
||||
className: '',
|
||||
rowClassName: () => '',
|
||||
}
|
||||
|
||||
type NativeAttrs = Omit<React.TableHTMLAttributes<any>, keyof Props>
|
||||
export type TableProps = Props & NativeAttrs
|
||||
type NativeAttrs = Omit<React.TableHTMLAttributes<any>, keyof Props<any>>
|
||||
export type TableProps<TableDataItem extends TableDataItemBase> = Props<TableDataItem> &
|
||||
NativeAttrs
|
||||
|
||||
const TableComponent: React.FC<React.PropsWithChildren<TableProps>> = ({
|
||||
children,
|
||||
data,
|
||||
hover,
|
||||
emptyText,
|
||||
onRow,
|
||||
onCell,
|
||||
onChange,
|
||||
className,
|
||||
...props
|
||||
}: React.PropsWithChildren<TableProps> & typeof defaultProps) => {
|
||||
function TableComponent<TableDataItem extends TableDataItemBase>(
|
||||
tableProps: React.PropsWithChildren<TableProps<TableDataItem>>,
|
||||
) {
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
const {
|
||||
children,
|
||||
data: customData,
|
||||
initialData,
|
||||
hover,
|
||||
emptyText,
|
||||
onRow,
|
||||
onCell,
|
||||
onChange,
|
||||
className,
|
||||
rowClassName,
|
||||
...props
|
||||
} = tableProps as React.PropsWithChildren<TableProps<TableDataItem>> &
|
||||
typeof defaultProps
|
||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||
const { SCALES } = useScaleable()
|
||||
const ref = useRef<HTMLTableElement>(null)
|
||||
const [{ width }, updateShape] = useRealShape<HTMLTableElement>(ref)
|
||||
const [columns, setColumns] = useState<Array<TableColumnItem>>([])
|
||||
const [selfData, setSelfData, dataRef] = useCurrentState<Array<TableDataSource<any>>>(
|
||||
[],
|
||||
)
|
||||
const updateColumn = (column: TableColumnItem) => {
|
||||
const [columns, setColumns] = useState<Array<TableAbstractColumn<TableDataItem>>>([])
|
||||
const [data, setData] = useState<Array<TableDataItem>>(initialData)
|
||||
const updateColumn = (column: TableAbstractColumn<TableDataItem>) => {
|
||||
setColumns(last => {
|
||||
const hasColumn = last.find(item => item.value === column.value)
|
||||
const hasColumn = last.find(item => item.prop === column.prop)
|
||||
if (!hasColumn) return [...last, column]
|
||||
return last.map(item => {
|
||||
if (item.value !== column.value) return item
|
||||
if (item.prop !== column.prop) return item
|
||||
return column
|
||||
})
|
||||
})
|
||||
}
|
||||
const removeRow = (rowIndex: number) => {
|
||||
const next = dataRef.current.filter((_, index) => index !== rowIndex)
|
||||
onChange(next)
|
||||
setSelfData([...next])
|
||||
}
|
||||
const updateRow = (rowIndex: number, newData: any) => {
|
||||
const next = dataRef.current.map((data, index) =>
|
||||
index === rowIndex ? { ...data, ...newData } : data,
|
||||
)
|
||||
onChange(next)
|
||||
setSelfData([...next])
|
||||
}
|
||||
|
||||
const initialValue = useMemo<TableConfig>(
|
||||
const contextValue = useMemo<TableConfig<TableDataItem>>(
|
||||
() => ({
|
||||
columns,
|
||||
updateColumn,
|
||||
removeRow,
|
||||
updateRow,
|
||||
}),
|
||||
[columns],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!data) return
|
||||
setSelfData(data)
|
||||
}, [data])
|
||||
if (typeof customData === 'undefined') return
|
||||
setData(customData)
|
||||
}, [customData])
|
||||
useResize(() => updateShape())
|
||||
|
||||
return (
|
||||
<TableContext.Provider value={initialValue}>
|
||||
<TableContext.Provider value={contextValue}>
|
||||
<table ref={ref} className={className} {...props}>
|
||||
<TableHead columns={columns} width={width} />
|
||||
<TableBody
|
||||
data={selfData}
|
||||
<TableBody<TableDataItem>
|
||||
data={data}
|
||||
hover={hover}
|
||||
emptyText={emptyText}
|
||||
onRow={onRow}
|
||||
onCell={onCell}
|
||||
rowClassName={rowClassName}
|
||||
/>
|
||||
{children}
|
||||
|
||||
@@ -126,5 +121,7 @@ const TableComponent: React.FC<React.PropsWithChildren<TableProps>> = ({
|
||||
|
||||
TableComponent.defaultProps = defaultProps
|
||||
TableComponent.displayName = 'GeistTable'
|
||||
const Table = withScaleable(TableComponent)
|
||||
export default Table
|
||||
TableComponent.Column = TableColumn
|
||||
let Table = withScaleable(TableComponent) as any
|
||||
Table.Column = TableColumn
|
||||
export default Table as typeof TableComponent & ScaleableProps
|
||||
|
||||
Reference in New Issue
Block a user