@telus-uds/components-community.data-grid 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +21 -0
- package/CHANGELOG.md +17 -0
- package/__mocks__/styleMock.js +7 -0
- package/babel.config.cjs +4 -0
- package/jest.config.cjs +19 -0
- package/lib/DataGrid.js +368 -0
- package/lib/DataGridBody.js +20 -0
- package/lib/DataGridCell.js +184 -0
- package/lib/DataGridHead.js +20 -0
- package/lib/DataGridRow.js +103 -0
- package/lib/DataGridTable.js +70 -0
- package/lib/dictionary.js +18 -0
- package/lib/index.js +12 -0
- package/lib/utility.js +75 -0
- package/package.json +48 -0
- package/src/DataGrid.jsx +465 -0
- package/src/DataGridBody.jsx +17 -0
- package/src/DataGridCell.jsx +200 -0
- package/src/DataGridHead.jsx +15 -0
- package/src/DataGridRow.jsx +110 -0
- package/src/DataGridTable.jsx +68 -0
- package/src/dictionary.js +18 -0
- package/src/index.js +14 -0
- package/src/utility.jsx +76 -0
package/src/DataGrid.jsx
ADDED
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import {
|
|
4
|
+
Typography,
|
|
5
|
+
Checkbox,
|
|
6
|
+
useCopy,
|
|
7
|
+
useThemeTokens,
|
|
8
|
+
variantProp,
|
|
9
|
+
IconButton
|
|
10
|
+
} from '@telus-uds/components-base'
|
|
11
|
+
import { styledComponents } from '@telus-uds/components-web'
|
|
12
|
+
import dictionary from './dictionary'
|
|
13
|
+
import {
|
|
14
|
+
resetColumnsData,
|
|
15
|
+
resetRowData,
|
|
16
|
+
DATA_TYPE,
|
|
17
|
+
SORT_DIRECTION,
|
|
18
|
+
isAtLeastOneRowUnselected,
|
|
19
|
+
toggleAllCheckBoxes,
|
|
20
|
+
toggleExpandedRow,
|
|
21
|
+
toggleRowCheckbox,
|
|
22
|
+
ROW_TYPE,
|
|
23
|
+
CELL_TYPE
|
|
24
|
+
} from './utility'
|
|
25
|
+
import DataGridTable from './DataGridTable'
|
|
26
|
+
import DataGridHead from './DataGridHead'
|
|
27
|
+
import DataGridBody from './DataGridBody'
|
|
28
|
+
import DataGridRow from './DataGridRow'
|
|
29
|
+
import DataGridCell from './DataGridCell'
|
|
30
|
+
|
|
31
|
+
const { styled } = styledComponents
|
|
32
|
+
|
|
33
|
+
export const StyledIconContainer = styled.div`
|
|
34
|
+
display: flex;
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
align-items: center;
|
|
37
|
+
width: 60px;
|
|
38
|
+
justify-content: space-between;
|
|
39
|
+
`
|
|
40
|
+
|
|
41
|
+
const DataGrid = React.forwardRef(
|
|
42
|
+
(
|
|
43
|
+
{
|
|
44
|
+
rows = [],
|
|
45
|
+
columns = [],
|
|
46
|
+
groupedRows,
|
|
47
|
+
showCheckbox = false,
|
|
48
|
+
isSortable = false,
|
|
49
|
+
makeScrollBarAlwaysVisible = true,
|
|
50
|
+
tokens,
|
|
51
|
+
variant,
|
|
52
|
+
copy = 'en',
|
|
53
|
+
testID = 'data-grid'
|
|
54
|
+
},
|
|
55
|
+
ref
|
|
56
|
+
) => {
|
|
57
|
+
const themeTokens = useThemeTokens('DataGrid', tokens, variant)
|
|
58
|
+
const getCopy = useCopy({ dictionary, copy })
|
|
59
|
+
const { expandRowIcon, hideExpandedRowIcon, sortColumnIconDown, sortColumnIconUp } = themeTokens
|
|
60
|
+
const [data, setData] = React.useState(
|
|
61
|
+
groupedRows ? resetRowData([...groupedRows], true) : resetRowData([...rows])
|
|
62
|
+
)
|
|
63
|
+
const [columnsData, setColumnsData] = React.useState(resetColumnsData(columns))
|
|
64
|
+
const [checkAllState, setCheckAllState] = React.useState(false)
|
|
65
|
+
|
|
66
|
+
const shouldCheckHeaderBox = (allData) => {
|
|
67
|
+
let isARowUnselectedInAllGroups = null
|
|
68
|
+
if (groupedRows) {
|
|
69
|
+
isARowUnselectedInAllGroups = Object.keys(allData).some((el) => {
|
|
70
|
+
return isAtLeastOneRowUnselected(allData[el].data)
|
|
71
|
+
})
|
|
72
|
+
} else {
|
|
73
|
+
isARowUnselectedInAllGroups = isAtLeastOneRowUnselected(allData)
|
|
74
|
+
}
|
|
75
|
+
return !isARowUnselectedInAllGroups
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const shouldDisplayIconColumn = (allData, _showCheckbox) => {
|
|
79
|
+
let doesOneRowHaveExpandedContent = null
|
|
80
|
+
if (groupedRows) {
|
|
81
|
+
doesOneRowHaveExpandedContent = Object.keys(allData).some((el) => {
|
|
82
|
+
return allData[el].data.some((eachData) => eachData.hasExpandedRow === true)
|
|
83
|
+
})
|
|
84
|
+
} else {
|
|
85
|
+
doesOneRowHaveExpandedContent = allData.some((el) => el.hasExpandedRow === true)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return !!(_showCheckbox || doesOneRowHaveExpandedContent)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const toggleSelectAll = () => {
|
|
92
|
+
let _rows = null
|
|
93
|
+
if (groupedRows) {
|
|
94
|
+
_rows = {}
|
|
95
|
+
Object.keys(data).forEach((el) => {
|
|
96
|
+
_rows[el] = { ...data[el] }
|
|
97
|
+
_rows[el].data = toggleAllCheckBoxes(data[el].data, checkAllState)
|
|
98
|
+
})
|
|
99
|
+
} else {
|
|
100
|
+
_rows = toggleAllCheckBoxes(data, checkAllState)
|
|
101
|
+
}
|
|
102
|
+
setData(_rows)
|
|
103
|
+
setCheckAllState(!checkAllState)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const toggleCheckbox = (rowId) => {
|
|
107
|
+
let _data = null
|
|
108
|
+
if (groupedRows) {
|
|
109
|
+
const [group, _] = rowId.split('-')
|
|
110
|
+
const id = parseInt(_, 10)
|
|
111
|
+
_data = { ...data }
|
|
112
|
+
_data[group].data = toggleRowCheckbox(_data[group].data, id)
|
|
113
|
+
} else {
|
|
114
|
+
_data = toggleRowCheckbox(data, rowId)
|
|
115
|
+
}
|
|
116
|
+
setData(_data)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const toggleRowExpansion = (rowId) => {
|
|
120
|
+
let _row = null
|
|
121
|
+
if (groupedRows) {
|
|
122
|
+
const [group, _] = rowId.split('-')
|
|
123
|
+
const id = parseInt(_, 10)
|
|
124
|
+
_row = { ...data }
|
|
125
|
+
_row[group].data = toggleExpandedRow(_row[group].data, id)
|
|
126
|
+
} else {
|
|
127
|
+
_row = toggleExpandedRow(data, rowId)
|
|
128
|
+
}
|
|
129
|
+
setData(_row)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const HeaderCheckBoxDisplay = showCheckbox && (
|
|
133
|
+
<Checkbox
|
|
134
|
+
label={getCopy('all')}
|
|
135
|
+
checked={shouldCheckHeaderBox(data)}
|
|
136
|
+
onChange={toggleSelectAll}
|
|
137
|
+
/>
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
const buildCheckBoxDisplay = (_showCheckbox, row, rowId) =>
|
|
141
|
+
_showCheckbox && <Checkbox checked={row.isSelected} onChange={() => toggleCheckbox(rowId)} />
|
|
142
|
+
|
|
143
|
+
const buildDisplayCaret = (row, rowId) =>
|
|
144
|
+
row.hasExpandedRow && (
|
|
145
|
+
<IconButton
|
|
146
|
+
onPress={() => toggleRowExpansion(rowId)}
|
|
147
|
+
icon={row.isExpandedRowOpen === true ? hideExpandedRowIcon : expandRowIcon}
|
|
148
|
+
accessibilityRole={`button-${rowId}`}
|
|
149
|
+
accessibilityLabel={
|
|
150
|
+
row.isExpandedRowOpen === true ? getCopy('hideRow') : getCopy('expandRow')
|
|
151
|
+
}
|
|
152
|
+
/>
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
const buildExpandedRow = (row, rowId) =>
|
|
156
|
+
row.isExpandedRowOpen && (
|
|
157
|
+
<DataGridRow
|
|
158
|
+
type={ROW_TYPE.EXPANDEDROW}
|
|
159
|
+
tokens={themeTokens}
|
|
160
|
+
rowId={rowId}
|
|
161
|
+
isExpandedRowOpen={row.isExpandedRowOpen}
|
|
162
|
+
>
|
|
163
|
+
<DataGridCell
|
|
164
|
+
type={CELL_TYPE.EXPANDEDROWCELL}
|
|
165
|
+
tokens={themeTokens}
|
|
166
|
+
columnsLength={columns.length}
|
|
167
|
+
showCheckbox={showCheckbox}
|
|
168
|
+
isExpandedRowOpen={row.isExpandedRowOpen}
|
|
169
|
+
>
|
|
170
|
+
{row.expandedRowChildComponent.component}
|
|
171
|
+
</DataGridCell>
|
|
172
|
+
</DataGridRow>
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
const buildRowContent = (
|
|
176
|
+
row,
|
|
177
|
+
rowId,
|
|
178
|
+
_showCheckbox,
|
|
179
|
+
CheckBoxDisplay,
|
|
180
|
+
DisplayCaret,
|
|
181
|
+
ExpandedRow
|
|
182
|
+
) => (
|
|
183
|
+
<React.Fragment key={rowId}>
|
|
184
|
+
<DataGridRow
|
|
185
|
+
key={rowId}
|
|
186
|
+
tokens={themeTokens}
|
|
187
|
+
rowId={rowId}
|
|
188
|
+
isExpandedRowOpen={row.isExpandedRowOpen}
|
|
189
|
+
hasExpandedContent={row.hasExpandedRow}
|
|
190
|
+
onClick={toggleRowExpansion}
|
|
191
|
+
>
|
|
192
|
+
{shouldDisplayIconColumn(data, _showCheckbox) && (
|
|
193
|
+
<DataGridCell tokens={themeTokens} isExpandedRowOpen={row.isExpandedRowOpen}>
|
|
194
|
+
<StyledIconContainer>
|
|
195
|
+
{CheckBoxDisplay}
|
|
196
|
+
{DisplayCaret}
|
|
197
|
+
</StyledIconContainer>
|
|
198
|
+
</DataGridCell>
|
|
199
|
+
)}
|
|
200
|
+
{columnsData.map((column) => (
|
|
201
|
+
<DataGridCell
|
|
202
|
+
tokens={themeTokens}
|
|
203
|
+
isExpandedRowOpen={row.isExpandedRowOpen}
|
|
204
|
+
key={`${rowId}-${column.key}`}
|
|
205
|
+
>
|
|
206
|
+
<Typography
|
|
207
|
+
block
|
|
208
|
+
variant={{ size: 'small' }}
|
|
209
|
+
tokens={{ color: themeTokens.cellTextColor }}
|
|
210
|
+
>
|
|
211
|
+
{row[column.key]}
|
|
212
|
+
</Typography>
|
|
213
|
+
</DataGridCell>
|
|
214
|
+
))}
|
|
215
|
+
</DataGridRow>
|
|
216
|
+
{ExpandedRow}
|
|
217
|
+
</React.Fragment>
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
const sortColumn = (column) => {
|
|
221
|
+
const _col = { ...column }
|
|
222
|
+
if (!groupedRows) {
|
|
223
|
+
const _data = [...data].sort((a, b) => {
|
|
224
|
+
const first =
|
|
225
|
+
_col.dataType === DATA_TYPE.NUMBER ? parseFloat(a[_col.key]) : a[_col.key].toUpperCase()
|
|
226
|
+
const next =
|
|
227
|
+
_col.dataType === DATA_TYPE.NUMBER ? parseFloat(b[_col.key]) : b[_col.key].toUpperCase()
|
|
228
|
+
|
|
229
|
+
if (_col.sortDirection === SORT_DIRECTION.DESC) {
|
|
230
|
+
if (first < next) return -1
|
|
231
|
+
return next === first ? 0 : 1
|
|
232
|
+
}
|
|
233
|
+
if (next < first) return -1
|
|
234
|
+
return first === next ? 0 : 1
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
_col.sortDirection =
|
|
238
|
+
_col.sortDirection === SORT_DIRECTION.DESC ? SORT_DIRECTION.ASC : SORT_DIRECTION.DESC
|
|
239
|
+
_col.currentSort = true
|
|
240
|
+
|
|
241
|
+
const _columns = resetColumnsData(columnsData)
|
|
242
|
+
_columns[columnsData.findIndex((_el) => _el.key === column.key)] = _col
|
|
243
|
+
|
|
244
|
+
setColumnsData(_columns)
|
|
245
|
+
setData(_data)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
let HeaderContent = null
|
|
250
|
+
let BodyContent = null
|
|
251
|
+
if (groupedRows) {
|
|
252
|
+
BodyContent = Object.keys(data).map((group) => {
|
|
253
|
+
const DefaultSubHeading = (
|
|
254
|
+
<Typography variant={{ size: 'h3' }}>{data[group].name}</Typography>
|
|
255
|
+
)
|
|
256
|
+
const GroupHeaderRow = (
|
|
257
|
+
<DataGridRow type={ROW_TYPE.EXPANDEDROW} tokens={themeTokens} key={group}>
|
|
258
|
+
<DataGridCell
|
|
259
|
+
type={CELL_TYPE.SUBHEADING}
|
|
260
|
+
tokens={themeTokens}
|
|
261
|
+
columnsLength={columnsData.length}
|
|
262
|
+
>
|
|
263
|
+
{data[group].groupHeaderComponent || DefaultSubHeading}
|
|
264
|
+
</DataGridCell>
|
|
265
|
+
</DataGridRow>
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
const DataRow = data[group].data.map((row) => {
|
|
269
|
+
const rowId = `${group}-${row.id}`
|
|
270
|
+
const CheckBoxDisplay = buildCheckBoxDisplay(showCheckbox, row, rowId)
|
|
271
|
+
|
|
272
|
+
const DisplayCaret = buildDisplayCaret(row, rowId)
|
|
273
|
+
|
|
274
|
+
const ExpandedRow = buildExpandedRow(row, rowId)
|
|
275
|
+
|
|
276
|
+
return buildRowContent(
|
|
277
|
+
row,
|
|
278
|
+
rowId,
|
|
279
|
+
showCheckbox,
|
|
280
|
+
CheckBoxDisplay,
|
|
281
|
+
DisplayCaret,
|
|
282
|
+
ExpandedRow
|
|
283
|
+
)
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
return (
|
|
287
|
+
<React.Fragment key={group}>
|
|
288
|
+
{GroupHeaderRow}
|
|
289
|
+
{DataRow}
|
|
290
|
+
</React.Fragment>
|
|
291
|
+
)
|
|
292
|
+
})
|
|
293
|
+
} else {
|
|
294
|
+
BodyContent = data.map((row) => {
|
|
295
|
+
const rowId = row.id
|
|
296
|
+
|
|
297
|
+
const CheckBoxDisplay = buildCheckBoxDisplay(showCheckbox, row, rowId)
|
|
298
|
+
|
|
299
|
+
const DisplayCaret = buildDisplayCaret(row, rowId)
|
|
300
|
+
|
|
301
|
+
const ExpandedRow = buildExpandedRow(row, rowId)
|
|
302
|
+
|
|
303
|
+
return buildRowContent(row, rowId, showCheckbox, CheckBoxDisplay, DisplayCaret, ExpandedRow)
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
HeaderContent = (
|
|
308
|
+
<>
|
|
309
|
+
{shouldDisplayIconColumn(data, showCheckbox) && (
|
|
310
|
+
<DataGridCell type={CELL_TYPE.HEADING} tokens={themeTokens} isFirstCol={true}>
|
|
311
|
+
{HeaderCheckBoxDisplay}
|
|
312
|
+
</DataGridCell>
|
|
313
|
+
)}
|
|
314
|
+
|
|
315
|
+
{columnsData.map((column) => (
|
|
316
|
+
<DataGridCell
|
|
317
|
+
type={CELL_TYPE.HEADING}
|
|
318
|
+
key={column.key}
|
|
319
|
+
tokens={themeTokens}
|
|
320
|
+
isSortable={isSortable}
|
|
321
|
+
>
|
|
322
|
+
<Typography
|
|
323
|
+
block
|
|
324
|
+
variant={{ size: 'h4' }}
|
|
325
|
+
tokens={{ color: themeTokens.headerTextColor }}
|
|
326
|
+
>
|
|
327
|
+
{column.label}
|
|
328
|
+
</Typography>
|
|
329
|
+
{isSortable && column.isSortable && (
|
|
330
|
+
<IconButton
|
|
331
|
+
variant={{ size: 'small' }}
|
|
332
|
+
tokens={{
|
|
333
|
+
iconColor: column.currentSort
|
|
334
|
+
? themeTokens.sortColumnIconActiveColor
|
|
335
|
+
: themeTokens.sortColumnIconDefaultColor
|
|
336
|
+
}}
|
|
337
|
+
onPress={() => {
|
|
338
|
+
if (typeof column.fn === 'function') {
|
|
339
|
+
column.fn(column, data, setData, columnsData, setColumnsData)
|
|
340
|
+
} else sortColumn(column)
|
|
341
|
+
}}
|
|
342
|
+
icon={
|
|
343
|
+
column.sortDirection === SORT_DIRECTION.ASC
|
|
344
|
+
? sortColumnIconUp
|
|
345
|
+
: sortColumnIconDown
|
|
346
|
+
}
|
|
347
|
+
accessibilityRole="sort-button"
|
|
348
|
+
accessibilityLabel={getCopy('button')}
|
|
349
|
+
/>
|
|
350
|
+
)}
|
|
351
|
+
</DataGridCell>
|
|
352
|
+
))}
|
|
353
|
+
</>
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
return (
|
|
357
|
+
<DataGridTable
|
|
358
|
+
tokens={themeTokens}
|
|
359
|
+
makeScrollBarAlwaysVisible={makeScrollBarAlwaysVisible}
|
|
360
|
+
ref={ref}
|
|
361
|
+
testID={testID}
|
|
362
|
+
>
|
|
363
|
+
<>
|
|
364
|
+
<DataGridHead>
|
|
365
|
+
<DataGridRow type={ROW_TYPE.HEADING}>{HeaderContent}</DataGridRow>
|
|
366
|
+
</DataGridHead>
|
|
367
|
+
<DataGridBody>{BodyContent}</DataGridBody>
|
|
368
|
+
</>
|
|
369
|
+
</DataGridTable>
|
|
370
|
+
)
|
|
371
|
+
}
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
DataGrid.displayName = 'DataGrid'
|
|
375
|
+
|
|
376
|
+
DataGrid.propTypes = {
|
|
377
|
+
/**
|
|
378
|
+
* Use `columns` to pass the data for data-grid column header items.
|
|
379
|
+
*/
|
|
380
|
+
columns: PropTypes.arrayOf(
|
|
381
|
+
PropTypes.shape({
|
|
382
|
+
key: PropTypes.string.isRequired,
|
|
383
|
+
label: PropTypes.string.isRequired,
|
|
384
|
+
width: PropTypes.string,
|
|
385
|
+
dataType: PropTypes.oneOf(['string', 'number']),
|
|
386
|
+
isSortable: PropTypes.bool, // Set this to true to enable sorting for this column
|
|
387
|
+
fn: PropTypes.func
|
|
388
|
+
})
|
|
389
|
+
).isRequired,
|
|
390
|
+
/**
|
|
391
|
+
* Use `groupedRows` to pass the grouped data for display
|
|
392
|
+
*/
|
|
393
|
+
groupedRows: PropTypes.arrayOf(
|
|
394
|
+
PropTypes.shape({
|
|
395
|
+
key: PropTypes.string.isRequired,
|
|
396
|
+
name: PropTypes.string.isRequired,
|
|
397
|
+
groupHeaderComponent: PropTypes.node,
|
|
398
|
+
data: PropTypes.arrayOf(
|
|
399
|
+
PropTypes.shape({
|
|
400
|
+
id: PropTypes.number.isRequired,
|
|
401
|
+
expandedRow: PropTypes.shape({
|
|
402
|
+
rows: PropTypes.arrayOf(
|
|
403
|
+
PropTypes.shape({
|
|
404
|
+
id: PropTypes.number.isRequired
|
|
405
|
+
})
|
|
406
|
+
),
|
|
407
|
+
columns: PropTypes.arrayOf(
|
|
408
|
+
PropTypes.shape({
|
|
409
|
+
key: PropTypes.string.isRequired,
|
|
410
|
+
label: PropTypes.string.isRequired
|
|
411
|
+
})
|
|
412
|
+
)
|
|
413
|
+
}),
|
|
414
|
+
expandedRowChildComponent: PropTypes.shape({
|
|
415
|
+
component: PropTypes.node,
|
|
416
|
+
displayOnLoad: PropTypes.bool.isRequired
|
|
417
|
+
})
|
|
418
|
+
})
|
|
419
|
+
)
|
|
420
|
+
})
|
|
421
|
+
),
|
|
422
|
+
/**
|
|
423
|
+
* Use `rows` to pass ungrouped data for data-grid row cells items.
|
|
424
|
+
*/
|
|
425
|
+
rows: PropTypes.arrayOf(
|
|
426
|
+
PropTypes.shape({
|
|
427
|
+
id: PropTypes.number.isRequired,
|
|
428
|
+
expandedRowChildComponent: PropTypes.shape({
|
|
429
|
+
component: PropTypes.node,
|
|
430
|
+
displayOnLoad: PropTypes.bool.isRequired
|
|
431
|
+
})
|
|
432
|
+
})
|
|
433
|
+
),
|
|
434
|
+
/**
|
|
435
|
+
* Set this to true to enable columns to become sortable.
|
|
436
|
+
*/
|
|
437
|
+
isSortable: PropTypes.bool,
|
|
438
|
+
/**
|
|
439
|
+
* Set this to true to make scrollbar always visible when content is larger than the grid
|
|
440
|
+
*/
|
|
441
|
+
makeScrollBarAlwaysVisible: PropTypes.bool,
|
|
442
|
+
/**
|
|
443
|
+
* Set this to true to enable checkboxes in the first column.
|
|
444
|
+
*/
|
|
445
|
+
showCheckbox: PropTypes.bool,
|
|
446
|
+
/**
|
|
447
|
+
* Use tokens prop to override theme tokens
|
|
448
|
+
*/
|
|
449
|
+
tokens: PropTypes.object,
|
|
450
|
+
/**
|
|
451
|
+
* No available variants
|
|
452
|
+
*/
|
|
453
|
+
variant: variantProp.propType,
|
|
454
|
+
/**
|
|
455
|
+
* Use copy for localisations of keys.
|
|
456
|
+
*/
|
|
457
|
+
copy: PropTypes.oneOf(['en', 'fr']),
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Use in tests if the datagrid test cases need to find the element with the id.
|
|
461
|
+
*/
|
|
462
|
+
testID: PropTypes.string
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export default DataGrid
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
|
|
4
|
+
const DataGridBody = React.forwardRef(({ children }, ref) => {
|
|
5
|
+
return <tbody ref={ref}>{children}</tbody>
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
DataGridBody.displayName = 'DataGridBody'
|
|
9
|
+
|
|
10
|
+
DataGridBody.propTypes = {
|
|
11
|
+
/**
|
|
12
|
+
* Accepts any React or HTML node
|
|
13
|
+
*/
|
|
14
|
+
children: PropTypes.node.isRequired
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default DataGridBody
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import { styledComponents } from '@telus-uds/components-web'
|
|
4
|
+
import { CELL_TYPE } from './utility'
|
|
5
|
+
|
|
6
|
+
const { styled, css } = styledComponents
|
|
7
|
+
|
|
8
|
+
const StyledHeaderCell = styled.th`
|
|
9
|
+
${(props) => {
|
|
10
|
+
return css`
|
|
11
|
+
background-color: ${props.headerRowBackgroundColor};
|
|
12
|
+
border-top: solid ${props.headerBorderTopWidth}px ${props.headerBorderTopColor};
|
|
13
|
+
border-bottom: solid ${props.headerBorderBottomWidth}px ${props.headerBorderBottomColor};
|
|
14
|
+
padding: ${props.headerPadding}px;
|
|
15
|
+
text-align: ${props.headerTextAlign};
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
font-size: ${props.headerFontSize}px;
|
|
18
|
+
font-weight: bold;
|
|
19
|
+
line-height: ${props.headerLineHeight * props.headerFontSize}px;
|
|
20
|
+
color: ${props.headerTextColor};
|
|
21
|
+
width: ${props.firstColWidth};
|
|
22
|
+
font-family: ${props.headerFontFamily};
|
|
23
|
+
`
|
|
24
|
+
}}
|
|
25
|
+
`
|
|
26
|
+
const StyledHeaderCellDiv = styled.div`
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
`
|
|
30
|
+
|
|
31
|
+
const StyledGroupHeaderCell = styled.td`
|
|
32
|
+
${(props) => {
|
|
33
|
+
return css`
|
|
34
|
+
background-color: ${props.subHeadingBackgroundColor};
|
|
35
|
+
padding: ${props.cellPadding}px;
|
|
36
|
+
border-top: solid ${props.cellBorderBottomWidth}px ${props.cellBorderBottomColor};
|
|
37
|
+
text-align: ${props.cellTextAlign};
|
|
38
|
+
font-size: ${props.cellFontSize}px;
|
|
39
|
+
font-weight: normal;
|
|
40
|
+
line-height: ${props.cellLineHeight * props.cellFontSize}px;
|
|
41
|
+
color: ${props.cellTextColor};
|
|
42
|
+
font-family: ${props.cellFontFamily};
|
|
43
|
+
`
|
|
44
|
+
}}
|
|
45
|
+
`
|
|
46
|
+
|
|
47
|
+
const StyledGroupHeaderCellDiv = styled.div`
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
justify-content: start;
|
|
51
|
+
`
|
|
52
|
+
|
|
53
|
+
const StyledExpandedCell = styled.td`
|
|
54
|
+
${(props) => {
|
|
55
|
+
return css`
|
|
56
|
+
padding: ${props.cellPadding}px;
|
|
57
|
+
border-bottom: ${
|
|
58
|
+
props.isExpandedRowOpen
|
|
59
|
+
? `solid ${props.cellBorderBottomWidth}px ${props.headerBorderBottomColor};`
|
|
60
|
+
: `solid ${props.cellBorderBottomWidth}px ${props.cellBorderBottomColor};`
|
|
61
|
+
}
|
|
62
|
+
text-align: ${props.cellTextAlign};
|
|
63
|
+
font-size: ${props.cellFontSize}px;
|
|
64
|
+
font-weight: normal;
|
|
65
|
+
line-height: ${props.cellLineHeight * props.cellFontSize}px;
|
|
66
|
+
color: ${props.cellTextColor};
|
|
67
|
+
font-family: ${props.cellFontFamily};
|
|
68
|
+
`
|
|
69
|
+
}}
|
|
70
|
+
`
|
|
71
|
+
|
|
72
|
+
const StyledExpandedCellDiv = styled.div`
|
|
73
|
+
${(props) => {
|
|
74
|
+
return css`
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: ${props.expandedContentAlignment ? props.expandedContentAlignment : 'start'};
|
|
78
|
+
padding-left: ${props.showCheckbox
|
|
79
|
+
? `${props.expandedContentLeftPadding}px`
|
|
80
|
+
: `${props.expandedContentDefaultLeftPadding}px`};
|
|
81
|
+
`
|
|
82
|
+
}}
|
|
83
|
+
`
|
|
84
|
+
|
|
85
|
+
export const StyledCell = styled.td`
|
|
86
|
+
${(props) => {
|
|
87
|
+
if (props.isExpandedRowOpen) {
|
|
88
|
+
return css`
|
|
89
|
+
padding: ${props.cellPadding}px;
|
|
90
|
+
text-align: ${props.cellTextAlign};
|
|
91
|
+
font-size: ${props.cellFontSize}px;
|
|
92
|
+
font-weight: normal;
|
|
93
|
+
line-height: ${props.cellLineHeight * props.cellFontSize}px;
|
|
94
|
+
color: ${props.cellTextColor};
|
|
95
|
+
font-family: ${props.cellFontFamily};
|
|
96
|
+
border-top: solid ${props.headerBorderTopWidth}px ${props.headerBorderTopColor};
|
|
97
|
+
`
|
|
98
|
+
}
|
|
99
|
+
return css`
|
|
100
|
+
padding: ${props.cellPadding}px;
|
|
101
|
+
border-bottom: ${props.hideRowBottomBorder === 1
|
|
102
|
+
? `none`
|
|
103
|
+
: `solid ${props.cellBorderBottomWidth}px ${props.cellBorderBottomColor}`};
|
|
104
|
+
text-align: ${props.cellTextAlign};
|
|
105
|
+
font-size: ${props.cellFontSize}px;
|
|
106
|
+
font-weight: normal;
|
|
107
|
+
line-height: ${props.cellLineHeight * props.cellFontSize}px;
|
|
108
|
+
color: ${props.cellTextColor};
|
|
109
|
+
font-family: ${props.cellFontFamily};
|
|
110
|
+
`
|
|
111
|
+
}}
|
|
112
|
+
`
|
|
113
|
+
|
|
114
|
+
const DataGridCell = React.forwardRef(
|
|
115
|
+
(
|
|
116
|
+
{
|
|
117
|
+
children,
|
|
118
|
+
tokens,
|
|
119
|
+
type,
|
|
120
|
+
columnsLength,
|
|
121
|
+
showCheckbox = false,
|
|
122
|
+
isFirstCol = false,
|
|
123
|
+
isExpandedRowOpen = false
|
|
124
|
+
},
|
|
125
|
+
ref
|
|
126
|
+
) => {
|
|
127
|
+
if (type === CELL_TYPE.HEADING) {
|
|
128
|
+
return (
|
|
129
|
+
<StyledHeaderCell
|
|
130
|
+
{...tokens}
|
|
131
|
+
firstColWidth={isFirstCol ? tokens.firstColWidth : 'auto'}
|
|
132
|
+
ref={ref}
|
|
133
|
+
>
|
|
134
|
+
<StyledHeaderCellDiv>{children}</StyledHeaderCellDiv>
|
|
135
|
+
</StyledHeaderCell>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
if (type === CELL_TYPE.SUBHEADING) {
|
|
139
|
+
return (
|
|
140
|
+
<StyledGroupHeaderCell colSpan={columnsLength + 1} {...tokens} ref={ref}>
|
|
141
|
+
<StyledGroupHeaderCellDiv {...tokens}>{children}</StyledGroupHeaderCellDiv>
|
|
142
|
+
</StyledGroupHeaderCell>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
if (type === CELL_TYPE.EXPANDEDROWCELL) {
|
|
146
|
+
return (
|
|
147
|
+
<StyledExpandedCell
|
|
148
|
+
colSpan={columnsLength + 1}
|
|
149
|
+
{...tokens}
|
|
150
|
+
isExpandedRowOpen={isExpandedRowOpen}
|
|
151
|
+
ref={ref}
|
|
152
|
+
>
|
|
153
|
+
<StyledExpandedCellDiv showCheckbox={showCheckbox} {...tokens}>
|
|
154
|
+
{children}
|
|
155
|
+
</StyledExpandedCellDiv>
|
|
156
|
+
</StyledExpandedCell>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
return (
|
|
160
|
+
<StyledCell {...tokens} isExpandedRowOpen={isExpandedRowOpen} ref={ref}>
|
|
161
|
+
{children}
|
|
162
|
+
</StyledCell>
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
DataGridCell.displayName = 'DataGridCell'
|
|
168
|
+
|
|
169
|
+
DataGridCell.propTypes = {
|
|
170
|
+
/**
|
|
171
|
+
cell type
|
|
172
|
+
*/
|
|
173
|
+
type: PropTypes.oneOf(['heading', 'subHeading', 'expandedRowCell']),
|
|
174
|
+
/**
|
|
175
|
+
* Accepts any React or HTML node
|
|
176
|
+
*/
|
|
177
|
+
children: PropTypes.node,
|
|
178
|
+
/**
|
|
179
|
+
* Tokens passed to the component
|
|
180
|
+
*/
|
|
181
|
+
tokens: PropTypes.object,
|
|
182
|
+
/**
|
|
183
|
+
* columnsLength passed to the component for certain scenarios in which colSpan is required
|
|
184
|
+
*/
|
|
185
|
+
columnsLength: PropTypes.number,
|
|
186
|
+
/**
|
|
187
|
+
* showCheckbox passed to the component for certain scenarios in which checkbox visibility impacts the display design
|
|
188
|
+
*/
|
|
189
|
+
showCheckbox: PropTypes.bool,
|
|
190
|
+
/**
|
|
191
|
+
* isFirstCol passed to the component to determine if cell is first within the row
|
|
192
|
+
*/
|
|
193
|
+
isFirstCol: PropTypes.bool,
|
|
194
|
+
/**
|
|
195
|
+
* isExpandedRowOpen passed to the component to determine if cell is displaying additional data
|
|
196
|
+
*/
|
|
197
|
+
isExpandedRowOpen: PropTypes.bool
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export default DataGridCell
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
|
|
4
|
+
const DataGridHead = React.forwardRef(({ children }, ref) => <thead ref={ref}>{children}</thead>)
|
|
5
|
+
|
|
6
|
+
DataGridHead.displayName = 'DataGridHead'
|
|
7
|
+
|
|
8
|
+
DataGridHead.propTypes = {
|
|
9
|
+
/**
|
|
10
|
+
* Accepts any React or HTML node
|
|
11
|
+
*/
|
|
12
|
+
children: PropTypes.node
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default DataGridHead
|