Skip to content

Commit 30be43b

Browse files
authored
Feat/column pinning (#400)
* add row cell core components * add make rows to netdata table * add column pinning * create global controls and move them to netdata table root component * move pagination inside netdata table * make sticky header * possible fix for vertical scroll * add fixed height * remove sticky * fix color background * create sharablestate so hover can be aligned betwenn tables * remove current hovered * added main table * remove unsused imports * add hovered state * fix performance issues with filtering * remove margin my styledTableControls * remove arrow function from mouse leave and enter
1 parent 30ed42d commit 30be43b

16 files changed

Lines changed: 544 additions & 186 deletions

src/components/tableV2/components/comparisonFilter.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Box from "src/components/templates/box"
55
import Flex from "src/components/templates/flex"
66

77
import { TextInput } from "src/components/input"
8+
import { debounce } from "throttle-debounce"
89

910
const Comparisons = [
1011
{ value: "all", label: "All" },
@@ -33,11 +34,11 @@ const ComparisonFilter = ({ column }) => {
3334
<Box
3435
as={TextInput}
3536
width={{ max: 50 }}
36-
value={filterValue ? filterValue[1] : null}
37-
onChange={e => {
37+
defaultValue={filterValue ? filterValue[1] : null}
38+
onChange={debounce(300, e => {
3839
e.persist()
3940
setFilterValue(old => [old?.[0], e.target.value])
40-
}}
41+
})}
4142
pattern="[0-9]*(.[0-9]+)?"
4243
inputMode="decimal"
4344
/>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React, { createContext, useReducer, useCallback, useMemo } from "react"
2+
3+
const reducer = (state, action) => {
4+
switch (action.type) {
5+
case "UPDATE_STATE":
6+
return {
7+
...state,
8+
...action.payload,
9+
}
10+
default:
11+
throw new Error()
12+
}
13+
}
14+
15+
export const SharedTableContext = createContext({
16+
state: {},
17+
udateState: state => null,
18+
})
19+
20+
export const SharedTableProvider = ({ children }) => {
21+
const [state, dispatch] = useReducer(reducer, {})
22+
23+
const updateState = useCallback(state => {
24+
dispatch({
25+
type: "UPDATE_STATE",
26+
payload: { ...state },
27+
})
28+
}, [])
29+
30+
const contextValue = useMemo(() => ({ state, updateState }), [state, updateState])
31+
32+
return <SharedTableContext.Provider value={contextValue}>{children}</SharedTableContext.Provider>
33+
}

src/components/tableV2/core/base-table.js

Lines changed: 85 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import React, { forwardRef, useCallback } from "react"
22
import styled from "styled-components"
33
import { getColor } from "src/theme/utils"
4-
import SearchInput from "src/components/search"
54
import { Icon } from "src/components/icon"
65
import Flex from "src/components/templates/flex"
76
import Box from "src/components/templates/box"
87
import { Text } from "src/components/typography"
98
import { IconButton } from "src/components/button"
109
import Tooltip from "src/components/drops/tooltip"
1110
import useToggle from "src/hooks/use-toggle"
12-
import { debounce } from "throttle-debounce"
11+
12+
//TODO heights in Table.Cell and Table.HeadCell needs to change and not be direct.
13+
// the problem is when we are applying column pin the second table has different sizes
14+
// than the first one. This is happening when we have a head with a filter and the all
15+
// the cells are being addapted to that size.
1316

1417
const StyledRow = styled.tr`
1518
font-size: 14px;
1619
color: ${getColor("text")};
20+
background: ${getColor("mainBackground")};
1721
&:nth-child(2n) {
1822
background: ${getColor("tableRowBg")};
1923
}
@@ -38,85 +42,37 @@ const StyledSortIcon = styled(Icon)`
3842
margin: auto;
3943
`
4044
const StyledPagination = styled(Flex)`
41-
position: sticky;
42-
bottom: -16px;
4345
height: 45px;
4446
background: ${getColor("mainBackground")};
4547
border-top: 1px solid ${getColor("borderSecondary")};
4648
`
47-
const StyledTableControls = styled(Flex)`
48-
position: sticky;
49-
width: 100%;
50-
top: -16px;
51-
z-index: 10;
52-
background: ${getColor("mainBackground")};
53-
padding: 16px 0;
54-
margin: -16px 0 0;
55-
`
56-
const Table = forwardRef(
57-
(
58-
{
59-
hasBulkActions,
60-
handleSearch,
61-
children,
62-
searchPlaceholder = "Search",
63-
Pagination,
64-
bulkActions,
65-
dataGa,
66-
...props
67-
},
68-
ref
69-
) => {
70-
return (
71-
<Flex width={{ base: "100%", min: "fit-content" }} column>
72-
{(hasBulkActions || handleSearch) && (
73-
<StyledTableControls>
74-
{handleSearch && (
75-
<Box width={{ max: 50 }}>
76-
<SearchInput
77-
data-testid="table-global-search-filter"
78-
data-ga={`${dataGa}::search::table-filter`}
79-
onChange={debounce(300, e => {
80-
e.persist()
81-
handleSearch(e.target.value)
82-
})}
83-
placeholder={searchPlaceholder}
84-
iconRight={<Icon name="magnify" color="textLite" />}
85-
/>
86-
</Box>
87-
)}
88-
<Flex gap={1} data-testid="bulk-actions" width="100%" justifyContent="end">
89-
{bulkActions && bulkActions()}
90-
</Flex>
91-
</StyledTableControls>
92-
)}
93-
<Box sx={{ borderCollapse: "separate" }} ref={ref} as="table" {...props}>
94-
{children}
95-
</Box>
96-
{Pagination}
97-
</Flex>
98-
)
99-
}
100-
)
10149

102-
Table.Head = forwardRef(({ hasBulkActions, children, ...props }, ref) => {
50+
const Table = forwardRef(({ children, ...props }, ref) => {
10351
return (
104-
<Box
105-
ref={ref}
106-
sx={{
107-
whiteSpace: "nowrap",
108-
position: "sticky",
109-
top: hasBulkActions ? "50px" : "0",
110-
zIndex: "10",
111-
}}
112-
as="thead"
113-
{...props}
114-
>
115-
{children}
116-
</Box>
52+
<Flex width={{ base: "100%", min: "fit-content" }} height="100%" column>
53+
<Box
54+
sx={{ borderCollapse: "separate", position: "relative" }}
55+
ref={ref}
56+
as="table"
57+
{...props}
58+
>
59+
{children}
60+
</Box>
61+
</Flex>
11762
)
11863
})
11964

65+
Table.Head = forwardRef(({ children, ...props }, ref) => (
66+
<Box
67+
ref={ref}
68+
sx={{ whiteSpace: "nowrap", zIndex: 1, position: "sticky", top: 0 }}
69+
as="thead"
70+
{...props}
71+
>
72+
{children}
73+
</Box>
74+
))
75+
12076
Table.HeadRow = forwardRef(({ children, ...props }, ref) => (
12177
<StyledHeaderRow ref={ref} {...props}>
12278
{children}
@@ -128,7 +84,14 @@ Table.HeadCell = forwardRef(
12884
<StyledHeaderCell
12985
width={{ max: maxWidth, base: width, min: minWidth }}
13086
ref={ref}
131-
sx={{ textAlign: align, fontSize: "14px", ...styles }}
87+
sx={{
88+
textAlign: align,
89+
fontSize: "14px",
90+
height: "90px",
91+
position: "sticky",
92+
top: 0,
93+
...styles,
94+
}}
13295
{...props}
13396
as="th"
13497
>
@@ -180,7 +143,14 @@ Table.SortingHeadCell = forwardRef(
180143
as="th"
181144
ref={ref}
182145
{...props}
183-
sx={{ textAlign: align, fontSize: "14px", ...styles }}
146+
sx={{
147+
textAlign: align,
148+
fontSize: "14px",
149+
height: "90px",
150+
position: "sticky",
151+
top: 0,
152+
...styles,
153+
}}
184154
data-testid={dataTestid}
185155
>
186156
<Box
@@ -218,7 +188,7 @@ Table.Cell = forwardRef(
218188
<Box
219189
width={{ max: maxWidth, base: width, min: minWidth }}
220190
padding={[3]}
221-
sx={{ textAlign: align }}
191+
sx={{ textAlign: align, height: "80px" }}
222192
as="td"
223193
ref={ref}
224194
{...props}
@@ -230,32 +200,48 @@ Table.Cell = forwardRef(
230200
}
231201
)
232202

233-
Table.Row = forwardRef(({ children, onClick, disableClickRow, ...props }, ref) => {
234-
const isRowDisabledForClick = disableClickRow && disableClickRow()
235-
const handleClick = e => {
236-
if (isRowDisabledForClick) return
237-
e.persist()
238-
e.stopPropagation()
239-
onClick?.()
240-
}
203+
Table.Row = forwardRef(
204+
(
205+
{ children, onClick, disableClickRow, onMouseEnter, onMouseLeave, isHovering, ...props },
206+
ref
207+
) => {
208+
const isRowDisabledForClick = disableClickRow && disableClickRow()
209+
const handleClick = e => {
210+
if (isRowDisabledForClick) return
211+
e.persist()
212+
e.stopPropagation()
213+
onClick?.()
214+
}
241215

242-
const isRowClickable = !isRowDisabledForClick && onClick !== undefined
243-
const cursor = isRowClickable ? "pointer" : "intial"
216+
const handleMouseEnter = event => {
217+
onMouseEnter?.(event)
218+
}
244219

245-
return (
246-
<Box
247-
as={StyledRow}
248-
_hover={isRowClickable && { background: "borderSecondary" }}
249-
cursor={cursor}
250-
isClickable={!!onClick}
251-
onClick={handleClick}
252-
ref={ref}
253-
{...props}
254-
>
255-
{children}
256-
</Box>
257-
)
258-
})
220+
const handleMousLeave = event => {
221+
onMouseLeave?.(event)
222+
}
223+
224+
const isRowClickable = !isRowDisabledForClick && onClick !== undefined
225+
const cursor = isRowClickable ? "pointer" : "intial"
226+
227+
return (
228+
<Box
229+
onMouseEnter={handleMouseEnter}
230+
onMouseLeave={handleMousLeave}
231+
as={StyledRow}
232+
_hover={isRowClickable && { background: "borderSecondary" }}
233+
cursor={cursor}
234+
isClickable={!!onClick}
235+
onClick={handleClick}
236+
ref={ref}
237+
{...props}
238+
data-hover={isHovering ? "" : undefined}
239+
>
240+
{children}
241+
</Box>
242+
)
243+
}
244+
)
259245

260246
export const Pagination = ({
261247
pageIndex,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React, { memo } from "react"
2+
3+
import Table from "./base-table"
4+
5+
const DataCell = ({ cell, testPrefix, flexRender }) => {
6+
return (
7+
<Table.Cell
8+
width={cell.column.getSize()}
9+
minWidth={cell.column.columnDef.minSize}
10+
maxWidth={cell.column.columnDef.maxSize}
11+
data-testid={`netdata-table-cell-${cell.column.columnDef.id}${testPrefix}`}
12+
key={cell.id}
13+
{...cell.column.columnDef.meta}
14+
>
15+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
16+
</Table.Cell>
17+
)
18+
}
19+
20+
export default memo(DataCell)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React from "react"
2+
3+
import Table from "./base-table"
4+
import makeHeadCell from "./headCell"
5+
import Rows from "./rows"
6+
7+
const FullTable = ({
8+
tableRef,
9+
getRowHandler,
10+
testPrefix,
11+
dataGa,
12+
headers,
13+
enableSorting,
14+
testPrefixCallback,
15+
onClickRow,
16+
table,
17+
disableClickRow,
18+
flexRender,
19+
onHoverRow,
20+
}) => {
21+
return (
22+
<Table
23+
ref={tableRef}
24+
data-testid={`netdata-table${testPrefix}`}
25+
testPrefix={testPrefix}
26+
dataGa={dataGa}
27+
>
28+
<Table.Head data-testid={`netdata-table-head${testPrefix}`}>
29+
<Table.HeadRow data-testid={`netdata-table-headRow${testPrefix}`}>
30+
{makeHeadCell({ headers, enableSorting, testPrefix })}
31+
</Table.HeadRow>
32+
</Table.Head>
33+
<Table.Body data-testid={`netdata-table-body${testPrefix}`}>
34+
<Rows
35+
testPrefix={testPrefix}
36+
testPrefixCallback={testPrefixCallback}
37+
onClickRow={onClickRow}
38+
table={table}
39+
disableClickRow={disableClickRow}
40+
flexRender={flexRender}
41+
getRowHandler={getRowHandler}
42+
onHoverRow={onHoverRow}
43+
/>
44+
</Table.Body>
45+
</Table>
46+
)
47+
}
48+
49+
export default FullTable

0 commit comments

Comments
 (0)