• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SAP / ui5-webcomponents-react / 5976576265

25 Aug 2023 01:36PM CUT coverage: 87.974% (+0.1%) from 87.834%
5976576265

Pull #5011

github

web-flow
Merge 1859b77b6 into 814dcc422
Pull Request #5011: feat(AnalyticalTable): add `headerLabel` column property

2734 of 3821 branches covered (0.0%)

11 of 11 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

5033 of 5721 relevant lines covered (87.97%)

19487.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.26
/packages/main/src/components/AnalyticalTable/index.tsx
1
'use client';
2

3
import { useVirtualizer } from '@tanstack/react-virtual';
4
import {
5
  debounce,
6
  enrichEventWithDetails,
7
  useI18nBundle,
8
  useIsomorphicId,
9
  useIsomorphicLayoutEffect,
10
  useIsRTL
11
} from '@ui5/webcomponents-react-base';
12
import { clsx } from 'clsx';
13
import type { ComponentType, CSSProperties, MutableRefObject, ReactNode, Ref } from 'react';
14
import React, { forwardRef, useCallback, useEffect, useMemo, useRef } from 'react';
15
import { createUseStyles } from 'react-jss';
16
import type { PluginHook } from 'react-table';
17
import {
18
  useColumnOrder,
19
  useExpanded,
20
  useFilters,
21
  useGlobalFilter,
22
  useGroupBy,
23
  useResizeColumns,
24
  useRowSelect,
25
  useSortBy,
26
  useTable
27
} from 'react-table';
28
import type {
29
  AnalyticalTableScrollMode,
30
  TextAlign,
31
  ValueState,
32
  VerticalAlign,
33
  TableScaleWidthMode,
34
  TableSelectionMode,
35
  TableSelectionBehavior,
36
  TableVisibleRowCountMode
37
} from '../../enums/index.js';
38
import {
39
  AnalyticalTableScaleWidthMode,
40
  AnalyticalTableSelectionBehavior,
41
  AnalyticalTableVisibleRowCountMode,
42
  GlobalStyleClasses,
43
  AnalyticalTableSelectionMode
44
} from '../../enums/index.js';
45
import {
46
  COLLAPSE_NODE,
47
  COLLAPSE_PRESS_SPACE,
48
  EXPAND_NODE,
49
  EXPAND_PRESS_SPACE,
50
  FILTERED,
51
  GROUPED,
52
  INVALID_TABLE,
53
  SELECT_PRESS_SPACE,
54
  UNSELECT_PRESS_SPACE
55
} from '../../i18n/i18n-defaults.js';
56
import type { CommonProps } from '../../interfaces/index.js';
57
import { FlexBox } from '../FlexBox/index.js';
58
import { Text } from '../Text/index.js';
59
import styles from './AnayticalTable.jss.js';
60
import { ColumnHeaderContainer } from './ColumnHeader/ColumnHeaderContainer.js';
61
import { DefaultColumn } from './defaults/Column/index.js';
62
import { DefaultLoadingComponent } from './defaults/LoadingComponent/index.js';
63
import { TablePlaceholder } from './defaults/LoadingComponent/TablePlaceholder.js';
64
import { DefaultNoDataComponent } from './defaults/NoDataComponent/index.js';
65
import { useA11y } from './hooks/useA11y.js';
66
import { useDragAndDrop } from './hooks/useDragAndDrop.js';
67
import { useDynamicColumnWidths } from './hooks/useDynamicColumnWidths.js';
68
import { useKeyboardNavigation } from './hooks/useKeyboardNavigation.js';
69
import { usePopIn } from './hooks/usePopIn.js';
70
import { useResizeColumnsConfig } from './hooks/useResizeColumnsConfig.js';
71
import { useRowHighlight } from './hooks/useRowHighlight.js';
72
import { useRowNavigationIndicators } from './hooks/useRowNavigationIndicator.js';
73
import { useRowSelectionColumn } from './hooks/useRowSelectionColumn.js';
74
import { useSelectionChangeCallback } from './hooks/useSelectionChangeCallback.js';
75
import { useSingleRowStateSelection } from './hooks/useSingleRowStateSelection.js';
76
import { useStyling } from './hooks/useStyling.js';
77
import { useTableScrollHandles } from './hooks/useTableScrollHandles.js';
78
import { useToggleRowExpand } from './hooks/useToggleRowExpand.js';
79
import { useVisibleColumnsWidth } from './hooks/useVisibleColumnsWidth.js';
80
import { VerticalScrollbar } from './scrollbars/VerticalScrollbar.js';
81
import { VirtualTableBody } from './TableBody/VirtualTableBody.js';
82
import { VirtualTableBodyContainer } from './TableBody/VirtualTableBodyContainer.js';
83
import { stateReducer } from './tableReducer/stateReducer.js';
84
import { TitleBar } from './TitleBar/index.js';
85
import { getRowHeight, getSubRowsByString, tagNamesWhichShouldNotSelectARow } from './util/index.js';
86
import { VerticalResizer } from './VerticalResizer.js';
87

88
export interface AnalyticalTableColumnDefinition {
89
  // base properties
90
  /**
91
   * This `string`/`function` is used to build the data model for your column.
92
   *
93
   * __Note__: You can also specify deeply nested values with accessors like `info.hobby` or even `address[0].street`
94
   * __Note__: If no `accessor` is set, or the `accessor` is a function, the `id` property has to be set.
95
   */
96
  accessor?: string | ((row: any, rowIndex: number) => any);
97
  /**
98
   * Defines the unique ID for the column. It is used by reference in things like sorting, grouping, filtering etc.
99
   *
100
   * __Note__: If no `accessor` is set, or the `accessor` is a function, the `id` property has to be set.
101
   */
102
  id?: string;
103
  /**
104
   * Can either be string or a React component that will be rendered as column header
105
   */
106
  Header?: string | ComponentType<any> | ((props?: any) => ReactNode);
107
  /**
108
   * Tooltip for the column header. If not set, the display text will be the same as the Header if it is a `string`.
109
   */
110
  headerTooltip?: string;
111
  /**
112
   * Custom cell renderer. If set, the table will call that component for every cell and pass all required information as props, e.g. the cell value as `props.cell.value`
113
   */
114
  Cell?: string | ComponentType<any> | ((props?: any) => ReactNode);
115
  /**
116
   * Cell width, if not set the table will distribute all columns without a width evenly.
117
   */
118
  width?: number;
119
  /**
120
   * Minimum width of the column, e.g. used for resizing.
121
   */
122
  minWidth?: number;
123
  /**
124
   * Maximum with of the column, e.g. used for resizing.
125
   */
126
  maxWidth?: number;
127

128
  // useFilters
129
  /**
130
   * Filter Component to be rendered in the Header.
131
   */
132
  Filter?: string | ComponentType<any> | ((props?: any) => ReactNode);
133
  /**
134
   * Disable filters for this column.
135
   */
136
  disableFilters?: boolean;
137
  /**
138
   * If set to true, this column will be filterable, regardless if it has a valid `accessor`.
139
   */
140
  defaultCanFilter?: boolean;
141
  /**
142
   * Either a string or a filter function.
143
   *
144
   * Supported String Values:
145
   * * `text`
146
   * * `exactText`
147
   * * `exactTextCase`
148
   * * `equals`
149
   */
150
  filter?: string | ((rows: any[], columnIds: string[], filterValue: string) => any);
151

152
  // useGlobalFilter
153
  /**
154
   * Disables global filtering for this column
155
   */
156
  disableGlobalFilter?: boolean;
157

158
  // useGroupBy
159
  /**
160
   * Component to render for aggregated cells.
161
   */
162
  Aggregated?: string | ComponentType<any> | ((props?: any) => ReactNode);
163
  /**
164
   * Aggregation function or string.<br />Supported String Values: <ul><li>`min`</li><li>`max`</li><li>`median`</li><li>`count`</li></ul>
165
   */
166
  aggregate?: string | ((leafValues, aggregatedValues) => any);
167
  /**
168
   * When attempting to group/aggregate non-primitive cell values (e.g. arrays of items) you will likely need to resolve a stable primitive value like a number or string to use in normal row aggregations. This property can be used to aggregate or simply access the value to be used in aggregations eg. count-ing the unique number of items in a cell's array value before sum-ing that count across the table.
169
   */
170
  aggregateValue?: string | ((values, row, column) => any);
171
  /**
172
   * Disable groupBy for this column.
173
   */
174
  disableGroupBy?: boolean;
175

176
  // useSortBy
177
  /**
178
   * If set to true, this column will be sortable, regardless if it has a valid `accessor`.
179
   */
180
  defaultCanSort?: boolean;
181
  /**
182
   * Disable sorting for this column.
183
   */
184
  disableSortBy?: boolean;
185
  /**
186
   * If set to `true`, the first sort direction for this column will be descending instead of ascending.
187
   */
188
  sortDescFirst?: boolean;
189
  /**
190
   * If set to `true`, the underlying sorting direction will be inverted, but the UI will not.
191
   */
192
  sortInverted?: boolean;
193
  /**
194
   * String or custom sort function.
195
   *
196
   * Supported String Values:
197
   * * `basic`
198
   * * `datetime`
199
   * * `alphanumeric`
200
   */
201
  sortType?: string | ((rowA, rowB, columnId: string, descending: boolean) => any);
202

203
  // useResizeColumns
204
  /**
205
   * Disable resizing for this column.
206
   */
207
  disableResizing?: boolean;
208

209
  // ui5 web components react properties
210
  /**
211
   * Horizontal alignment of the cell.
212
   */
213
  hAlign?: TextAlign;
214
  /**
215
   * Vertical alignment of the cell.
216
   */
217
  vAlign?: VerticalAlign;
218

219
  // usePopIn
220
  /**
221
   * Enables the pop-in behavior of the column. When the `responsiveMinWidth` is smaller than the width of the table, the content of each cell will move to the first cell in the row, improving usability on small or mobile devices.
222
   */
223
  responsivePopIn?: boolean;
224
  /**
225
   * Defines how the table should react when its width falls below the `responsiveMinWidth`.
226
   *
227
   * - If `responsivePopIn` is `true` the content of this column will be moved to the first column.
228
   * - If `responsivePopIn` is not set or `false` the column will be hidden.
229
   */
230
  responsiveMinWidth?: number;
231
  /**
232
   * Custom pop-in header renderer. If set, the table will call that component for every column that is "popped-in" and pass the table instance as prop.
233
   */
234
  PopInHeader?: string | ComponentType<any> | ((props?: any) => ReactNode);
235

236
  //use useDragAndDrop
237
  /**
238
   * Defines if the column is reorderable by dragging and dropping columns.
239
   *
240
   * Defaults to: `true`
241
   *
242
   * @deprecated please use `disableDragAndDrop` instead.
243
   */
244
  canReorder?: boolean;
245
  /**
246
   * Defines if the column is reorderable by dragging and dropping columns.
247
   */
248
  disableDragAndDrop?: boolean;
249
  /**
250
   * Defines whether this column should allow multi-sort.
251
   *
252
   * __Note:__ When sorting by a column that does not allow multiple sorting, only the current column is sorted and all other sorted columns are reset.
253
   */
254
  enableMultiSort?: boolean;
255

256
  // all other custom properties of [React Table v7](https://github.com/TanStack/table/blob/v7/docs/src/pages/docs/api/overview.md) column options
257
  [key: string]: any;
258
}
259

260
export interface DivWithCustomScrollProp extends HTMLDivElement {
261
  isExternalVerticalScroll?: boolean;
262
}
263

264
export interface AnalyticalTableDomRef extends Omit<HTMLDivElement, 'scrollTo'> {
265
  scrollToItem: (index: number, align?: AnalyticalTableScrollMode) => void;
266
  scrollTo: (scrollOffset: number) => void; // overrides native scrollTo function
267
}
268

269
export interface AnalyticalTablePropTypes extends Omit<CommonProps, 'title'> {
270
  /**
271
   * Defines the columns array where you can define the configuration for each column.
272
   *
273
   * __Must be memoized!__
274
   */
275
  columns: AnalyticalTableColumnDefinition[];
276
  /**
277
   * The data array that you want to display on the table.
278
   *
279
   * __Must be memoized!__
280
   */
281
  data: Record<string, any>[];
282
  /**
283
   * Component or text rendered in the header section of the `AnalyticalTable`.
284
   *
285
   * __Note:__ If not set, it will be hidden.
286
   */
287
  header?: ReactNode;
288
  /**
289
   * Extension section of the Table. If not set, no extension area will be rendered
290
   */
291
  extension?: ReactNode;
292
  /**
293
   * The minimum number of rows that are displayed. If the data contains fewer entries than `minRows`, it will be filled with empty rows.
294
   */
295
  minRows?: number;
296
  /**
297
   * Defines how the table will render visible rows.
298
   *
299
   * - __"Fixed":__ The table always has as many rows as defined in the `visibleRows` prop.
300
   * - __"Auto":__ The number of visible rows displayed depends on the height of the surrounding container.
301
   * - __"Interactive":__ Adds a resizer to the bottom of the table to dynamically add or remove visible rows. The initial number of rows is defined by the `visibleRows` prop.
302
   *
303
   * __Note:__ When `"Auto"` is enabled, we recommend to use a fixed height for the outer container.
304
   */
305
  visibleRowCountMode?:
306
    | AnalyticalTableVisibleRowCountMode
307
    | keyof typeof AnalyticalTableVisibleRowCountMode
308
    | TableVisibleRowCountMode;
309

310
  /**
311
   * The number of rows visible without going into overflow.
312
   *
313
   * __Note:__ If the data contains more entries than the `visibleRow` count, a vertical scrollbar is rendered and the table goes into overflow.
314
   */
315
  visibleRows?: number;
316
  /**
317
   * Defines whether the row height of popped-in columns should be considered when calculating the body height of the table.
318
   *
319
   * __Note:__ If set so `true` the table will change its height depending on whether columns are popped in or not.
320
   */
321
  adjustTableHeightOnPopIn?: boolean;
322
  /**
323
   * Indicates whether a loading indicator should be shown.
324
   *
325
   * __Note:__ If the data array is not empty and loading is set to `true` a `Loader` will be displayed underneath the header, otherwise a loading placeholder will be shown.
326
   * You can use your own placeholder by passing it to the `LoadingComponent` prop.
327
   */
328
  loading?: boolean;
329
  /**
330
   * Setting this prop to `true` will show an overlay on top of the AnalyticalTable content preventing users from interacting with it.
331
   */
332
  showOverlay?: boolean;
333
  /**
334
   * Defines the text shown if the data array is empty. If not set "No data" will be displayed.
335
   */
336
  noDataText?: string;
337
  /**
338
   * Defines the height of the rows and header row.
339
   *
340
   * __Note:__ This prop overrides the default height. If you want to use the default height after you have set a custom height, you will have to remove this prop and remount the component.
341
   */
342
  rowHeight?: number;
343
  /**
344
   * Defines the height of the header row.
345
   *
346
   * __Note:__ If this property is set, it overwrites the height set in `rowHeight` for the header row.
347
   *
348
   * __Note:__ This prop overrides the default height. If you want to use the default height after you have set a custom height, you will have to remove this prop and remount the component.
349
   */
350
  headerRowHeight?: number;
351
  /**
352
   * Defines whether the table should retain its column width, when a column has been manually resized and the container width has changed.
353
   */
354
  retainColumnWidth?: boolean;
355
  /**
356
   * Defines whether the table should display rows with alternating row colors.
357
   */
358
  alternateRowColor?: boolean;
359
  /**
360
   * Flag whether the table should add an extra column at the end of the rows for displaying a navigation highlight.
361
   */
362
  withNavigationHighlight?: boolean;
363
  /**
364
   * Flag whether the table should add an extra column at the start of the rows for displaying row highlights, based on the `highlightField` prop.
365
   */
366
  withRowHighlight?: boolean;
367
  /**
368
   * Accessor for showing the row highlights. Only taken into account when `withRowHighlight` is set.
369
   *
370
   * Default Value: `status`.
371
   *
372
   * The value of this prop can either be a `string` pointing to a `ValueState` in your dataset
373
   * or an accessor function which should return a `ValueState`.
374
   */
375
  highlightField?: string | ((row: Record<any, any>) => ValueState);
376
  /**
377
   * Defines whether columns are filterable.
378
   */
379
  filterable?: boolean;
380
  /**
381
   * Defines whether columns are sortable.
382
   */
383
  sortable?: boolean;
384
  /**
385
   * Defines whether columns are groupable.
386
   *
387
   * __Note:__ This prop has no effect when `isTreeTable` is true or `renderRowSubComponent` is set.
388
   */
389
  groupable?: boolean;
390
  /**
391
   * Group table rows by adding the column's `accessor` or `id` to the array.
392
   *
393
   * __Note:__ This prop has no effect when `isTreeTable` is true or `renderRowSubComponent` is set.
394
   */
395
  groupBy?: string[];
396
  /**
397
   * Defines the selection behavior of the table.
398
   *
399
   * - __"Row":__ A selection column is rendered along with the normal columns. The whole row is selectable.
400
   * - __"RowOnly":__ No selection column is rendered along with the normal columns. The whole row is selectable.
401
   * - __"RowSelector":__ The row is only selectable by clicking on the corresponding field in the selection column.
402
   */
403
  selectionBehavior?:
404
    | AnalyticalTableSelectionBehavior
405
    | keyof typeof AnalyticalTableSelectionBehavior
406
    | TableSelectionBehavior;
407
  /**
408
   * Defines the `SelectionMode` of the table.
409
   *
410
   * - __"None":__ The rows are not selectable.
411
   * - __"SingleSelect":__ You can select only one row at once. Clicking on another row will unselect the previously selected row.
412
   * - __"MultiSelect":__ You can select multiple rows.
413
   */
414
  selectionMode?: AnalyticalTableSelectionMode | keyof typeof AnalyticalTableSelectionMode | TableSelectionMode;
415

416
  /**
417
   * Defines the column growing behaviour. Possible Values:
418
   *
419
   * - **Default**: The available space of the table is distributed evenly for columns without fixed width. If the minimum width of all columns is reached, horizontal scrolling will be enabled.
420
   * - **Smart**: Every column gets the space it needs for displaying the full header text. If all header texts need more space than the available table width, horizontal scrolling will be enabled. If there is space left, columns with a long text will get more space until there is no more table space left.
421
   * - **Grow**: Every column gets the space it needs for displaying its full header text and full text content of all cells. If it requires more space than the table has, horizontal scrolling will be enabled. To prevent huge header text from polluting the table, a max-width of 700px is applied to each column. It can be overwritten by setting the respective column property. This mode adds a calculated `minWidth` to each column. If the internally calculated `minWidth` is larger than the `width` set in the column options, it can lead to an unwanted scrollbar. To prevent this, you can set the `minWidth` in the column options yourself.
422
   *
423
   * __Note:__ Custom cells with components instead of text as children are ignored by the `Smart` and `Grow` modes.
424
   * __Note:__ For performance reasons, the `Smart` and `Grow` modes base their calculation for table cell width on a subset of column cells. If the first 20 cells of a column are significantly smaller than the rest of the column cells, the content may still not be fully displayed for all cells.
425
   *
426
   */
427
  scaleWidthMode?: AnalyticalTableScaleWidthMode | keyof typeof AnalyticalTableScaleWidthMode | TableScaleWidthMode;
428
  /**
429
   * Defines the number of the CSS `scaleX(sx: number)` function. `sx` is representing the abscissa of the scaling vector.
430
   *
431
   * __Note:__ If `transform: scale()` is used, this prop is mandatory, otherwise it will lead to unwanted behavior and design.
432
   */
433
  scaleXFactor?: number;
434
  /**
435
   * Defines the columns order by their `accessor` or `id`.
436
   */
437
  columnOrder?: string[];
438
  /**
439
   * Defines whether infinite scroll is active.
440
   */
441
  infiniteScroll?: boolean;
442
  /**
443
   * The `infiniteScrollThreshold` defines at how many remaining rows the `onLoadMore` event should be fired.
444
   *
445
   * Example: Your initial dataset consists of 50 entries and you want to load more data when the user scrolled to the 40th row. Then you should set the `infiniteScrollThreshold` to 10.
446
   */
447
  infiniteScrollThreshold?: number;
448
  /**
449
   * Defines the value that should be filtered on across all rows.
450
   *
451
   * __Note:__ This prop is not supported for tree-tables. You can enable it by creating your own global-filter function. You can find out more about this in the [react-table v7 documentation](https://github.com/TanStack/table/blob/v7/docs/src/pages/docs/api/useGlobalFilter.md).
452
   */
453
  globalFilterValue?: string;
454
  /**
455
   * Additional options which will be passed to [v7 react-table´s useTable hook](https://github.com/TanStack/table/blob/v7/docs/src/pages/docs/api/useTable.md#table-options)
456
   */
457
  reactTableOptions?: Record<string, unknown>;
458
  /**
459
   * You can use this prob to add custom hooks to the table.
460
   */
461
  tableHooks?: PluginHook<any>[];
462
  /**
463
   * Defines the key for nested rows.
464
   *
465
   * __Note__: You can also specify deeply nested sub-rows with accessors like `values.subRows`.
466
   *
467
   * Default: "subRows"
468
   */
469
  subRowsKey?: string;
470
  /**
471
   * The key must consist of a valid `rowId` like `{ 2: true }` or `{ '0.2.0': true }` for nested rows.
472
   *
473
   * __Note:__ This prop updates the internal table state and must therefore be memoized!
474
   */
475
  selectedRowIds?: { [key: string]: boolean };
476
  /**
477
   * Defines whether the table should act as tree-table.
478
   */
479
  isTreeTable?: boolean;
480
  /**
481
   * The amount of columns to load both behind and ahead of the current window range.
482
   */
483
  overscanCountHorizontal?: number;
484
  /**
485
   * The amount of rows to load both behind and ahead of the current window range.
486
   */
487
  overscanCount?: number;
488
  /**
489
   * Defines the subcomponent that should be displayed below each row.
490
   *
491
   * __Note:__ When rendering active elements inside the subcomponent, make sure to add the `data-subcomponent-active-element' attribute, otherwise focus behavior won't be consistent.
492
   */
493
  renderRowSubComponent?: (row?: any) => ReactNode;
494
  /**
495
   * Defines whether a subcomponent should be rendered as expandable container or directly at the bottom of the row.
496
   */
497
  alwaysShowSubComponent?: boolean;
498
  /**
499
   * Defines where modals and other elements which should be mounted outside the DOM hierarchy are rendered into via `React.createPortal`.
500
   *
501
   * You can find out more about this [here](https://sap.github.io/ui5-webcomponents-react/?path=/docs/knowledge-base-working-with-portals--page).
502
   *
503
   * Defaults to: `document.body`
504
   */
505
  portalContainer?: Element;
506

507
  // events
508
  /**
509
   * This callback can be used to programmatically show an indicator for navigated rows. It has no effect if `withNavigationHighlight` is not set.
510
   *
511
   * __Must be memoized!__
512
   */
513
  markNavigatedRow?: (row?: Record<any, any>) => boolean;
514
  /**
515
   * Fired when the sorting of the rows changes.
516
   */
517
  onSort?: (e: CustomEvent<{ column: unknown; sortDirection: string }>) => void;
518
  /**
519
   * Fired when the grouping of the rows changes.
520
   */
521
  onGroup?: (e: CustomEvent<{ column: unknown; groupedColumns: string[] }>) => void;
522
  /**
523
   * Fired when a row is selected or unselected.
524
   */
525
  onRowSelect?: (
526
    e?: CustomEvent<{
527
      allRowsSelected: boolean;
528
      row?: Record<string, unknown>;
529
      isSelected?: boolean;
530
      selectedFlatRows: Record<string, unknown>[];
531
      selectedRowIds: Record<string | number, boolean>;
532
    }>
533
  ) => void;
534
  /**
535
   * Fired when a row is clicked
536
   */
537
  onRowClick?: (e?: CustomEvent<{ row?: unknown }>) => void;
538
  /**
539
   * Fired when a row is expanded or collapsed
540
   */
541
  onRowExpandChange?: (e?: CustomEvent<{ row: unknown; column: unknown }>) => void;
542
  /**
543
   * Fired when the columns order is changed.
544
   */
545
  onColumnsReorder?: (e?: CustomEvent<{ columnsNewOrder: string[]; column: unknown }>) => void;
546
  /**
547
   * Fired when the `infiniteScrollThreshold` is reached.
548
   *
549
   * @param {number} e.detail.rowCount - The number of rows
550
   * @param {number} e.detail.totalRowCount - The total number of rows, including sub-rows
551
   */
552
  onLoadMore?: (e?: CustomEvent<{ rowCount: number; totalRowCount: number }>) => void;
553
  /**
554
   * Fired when the body of the table is scrolled.
555
   */
556
  onTableScroll?: (e?: CustomEvent<{ rows: Record<string, any>[]; rowElements: HTMLCollection }>) => void;
557
  // default components
558
  /**
559
   * Component that will be rendered when the table is not loading and has no data.
560
   */
561
  NoDataComponent?: ComponentType<any>;
562
  /**
563
   * Component that will be rendered when the table is loading and has data.
564
   */
565
  LoadingComponent?: ComponentType<any>;
566

567
  /**
568
   * Exposes the internal table instance.
569
   * This object will contain all [instance properties (react-table v7)](https://github.com/TanStack/table/blob/v7/docs/src/pages/docs/api/useTable.md#instance-properties)
570
   * of the `useTable` hook and all instance properties from `useColumnOrder`, `useExpanded`, `useFilters`,
571
   * `useGlobalFilter`, `useGroupBy`,`useResizeColumns`, `useRowSelect` and `useSortBy` plugin hooks.
572
   *
573
   * **Note**: Use this prop with care, some properties might have an impact on the internal `AnalyticalTable` implementation.
574
   */
575
  tableInstance?: Ref<Record<string, any>>;
576
}
577

578
// When a sorted column is removed from the visible columns array (e.g. when "popped-in"), it doesn't clean up the sorted columns leading to an undefined `sortType`.
579
const sortTypesFallback = {
266✔
580
  undefined: () => undefined
581
};
582

583
const useStyles = createUseStyles(styles, { name: 'AnalyticalTable' });
266✔
584
/**
585
 * The `AnalyticalTable` provides a set of convenient functions for responsive table design, including virtualization of rows and columns, infinite scrolling and customizable columns that will, unless otherwise defined, distribute the available space equally among themselves.
586
 * It also provides several possibilities for working with the data, including sorting, filtering, grouping and aggregation.
587
 */
120✔
588
const AnalyticalTable = forwardRef<AnalyticalTableDomRef, AnalyticalTablePropTypes>((props, ref) => {
266✔
589
  const {
590
    alternateRowColor,
591
    alwaysShowSubComponent,
120✔
592
    adjustTableHeightOnPopIn,
593
    className,
594
    columnOrder,
595
    columns,
596
    data: rawData,
120✔
597
    extension,
598
    filterable,
599
    globalFilterValue,
600
    groupBy,
601
    groupable,
602
    header,
603
    headerRowHeight,
604
    highlightField,
605
    infiniteScroll,
606
    infiniteScrollThreshold,
607
    isTreeTable,
608
    loading,
609
    markNavigatedRow,
610
    minRows,
611
    noDataText,
612
    overscanCount,
613
    overscanCountHorizontal,
614
    portalContainer,
615
    retainColumnWidth,
616
    reactTableOptions,
617
    renderRowSubComponent,
618
    rowHeight,
619
    scaleWidthMode,
620
    selectedRowIds,
621
    selectionBehavior,
622
    selectionMode,
623
    showOverlay,
624
    sortable,
625
    style,
626
    subRowsKey,
627
    tableHooks,
628
    tableInstance,
629
    visibleRowCountMode,
630
    visibleRows,
631
    withNavigationHighlight,
632
    withRowHighlight,
633
    onColumnsReorder,
634
    onGroup,
635
    onLoadMore,
636
    onRowClick,
637
    onRowExpandChange,
638
    onRowSelect,
639
    onSort,
640
    onTableScroll,
641
    LoadingComponent,
642
    NoDataComponent,
643
    scaleXFactor,
644
    ...rest
645
  } = props;
32,057✔
646
  const uniqueId = useIsomorphicId();
32,054✔
647
  const i18nBundle = useI18nBundle('@ui5/webcomponents-react');
32,054✔
648
  const titleBarId = useRef(`titlebar-${uniqueId}`).current;
32,054✔
649
  const invalidTableTextId = useRef(`invalidTableText-${uniqueId}`).current;
32,054✔
650

651
  const classes = useStyles();
32,054✔
652

653
  const [analyticalTableRef, scrollToRef] = useTableScrollHandles(ref);
32,054✔
654
  const tableRef = useRef<DivWithCustomScrollProp>(null);
32,054✔
655

656
  const isRtl = useIsRTL(analyticalTableRef);
32,054✔
657

658
  const getSubRows = useCallback((row) => getSubRowsByString(subRowsKey, row) || [], [subRowsKey]);
314,495✔
659

660
  const invalidTableA11yText = i18nBundle.getText(INVALID_TABLE);
32,054✔
661
  const tableInstanceRef = useRef<Record<string, any>>(null);
32,054✔
662
  tableInstanceRef.current = useTable(
32,054✔
663
    {
664
      columns,
665
      data: rawData,
666
      defaultColumn: DefaultColumn,
×
667
      getSubRows,
668
      stateReducer,
669
      disableFilters: !filterable,
670
      disableSortBy: !sortable,
671
      disableGroupBy: isTreeTable || (!alwaysShowSubComponent && renderRowSubComponent) ? true : !groupable,
114,809✔
672
      selectSubRows: false,
673
      sortTypes: sortTypesFallback,
674
      webComponentsReactProperties: {
675
        translatableTexts: {
676
          expandA11yText: i18nBundle.getText(EXPAND_PRESS_SPACE),
677
          collapseA11yText: i18nBundle.getText(COLLAPSE_PRESS_SPACE),
678
          selectA11yText: i18nBundle.getText(SELECT_PRESS_SPACE),
679
          unselectA11yText: i18nBundle.getText(UNSELECT_PRESS_SPACE),
×
680
          expandNodeA11yText: i18nBundle.getText(EXPAND_NODE),
681
          collapseNodeA11yText: i18nBundle.getText(COLLAPSE_NODE),
682
          filteredA11yText: i18nBundle.getText(FILTERED),
683
          groupedA11yText: i18nBundle.getText(GROUPED)
684
        },
685
        tagNamesWhichShouldNotSelectARow,
686
        tableRef,
687
        selectionMode,
688
        selectionBehavior,
689
        classes,
690
        onRowSelect: onRowSelect,
691
        onRowClick,
692
        onRowExpandChange,
693
        isTreeTable,
694
        alternateRowColor,
695
        scaleWidthMode,
696
        loading,
697
        withRowHighlight,
698
        highlightField,
699
        withNavigationHighlight,
700
        markNavigatedRow,
701
        renderRowSubComponent,
702
        alwaysShowSubComponent,
703
        scrollToRef,
704
        showOverlay,
705
        uniqueId,
706
        scaleXFactor,
707
        subRowsKey
708
      },
709
      ...reactTableOptions
710
    },
711
    useFilters,
712
    useGlobalFilter,
713
    useColumnOrder,
714
    useGroupBy,
715
    useSortBy,
716
    useExpanded,
717
    useRowSelect,
718
    useResizeColumns,
719
    useResizeColumnsConfig,
720
    useRowSelectionColumn,
721
    useSingleRowStateSelection,
722
    useSelectionChangeCallback,
723
    useRowHighlight,
724
    useRowNavigationIndicators,
725
    useDynamicColumnWidths,
726
    useStyling,
727
    useToggleRowExpand,
728
    useA11y,
729
    usePopIn,
730
    useVisibleColumnsWidth,
731
    useKeyboardNavigation,
732
    ...tableHooks
733
  );
734

735
  const {
736
    getTableProps,
737
    headerGroups,
738
    rows,
739
    prepareRow,
740
    state: tableState,
741
    columns: tableInternalColumns,
742
    setColumnOrder,
743
    dispatch,
744
    totalColumnsWidth,
745
    visibleColumns,
746
    visibleColumnsWidth,
747
    setGroupBy,
748
    setGlobalFilter
749
  } = tableInstanceRef.current;
32,054✔
750

751
  if (tableInstance && {}.hasOwnProperty.call(tableInstance, 'current')) {
32,054✔
752
    (tableInstance as MutableRefObject<Record<string, any>>).current = tableInstanceRef.current;
1,765✔
753
  }
754
  if (typeof tableInstance === 'function') {
32,054!
755
    tableInstance(tableInstanceRef.current);
756
  }
757

758
  const titleBarRef = useRef(null);
32,054✔
759
  const extensionRef = useRef(null);
32,054!
760
  const headerRef = useRef(null);
32,054✔
761

762
  const extensionsHeight =
32,054!
763
    (titleBarRef.current?.offsetHeight ?? 0) +
62,032✔
764
    (extensionRef.current?.offsetHeight ?? 0) +
64,108✔
765
    (headerRef.current?.offsetHeight ?? 0);
40,814✔
766

767
  const internalRowHeight = getRowHeight(rowHeight, tableRef);
32,054✔
768
  const internalHeaderRowHeight = headerRowHeight ?? internalRowHeight;
32,054✔
769
  const popInRowHeight =
770
    tableState?.popInColumns?.length > 0
32,054✔
771
      ? internalRowHeight + tableState.popInColumns.length * (internalRowHeight + 16)
×
772
      : internalRowHeight;
×
773

×
774
  const internalVisibleRowCount = tableState.visibleRows ?? visibleRows;
32,054✔
775

776
  const updateTableClientWidth = useCallback(() => {
32,054!
777
    if (tableRef.current) {
5,620✔
778
      dispatch({ type: 'TABLE_RESIZE', payload: { tableClientWidth: tableRef.current.clientWidth } });
5,620!
779
    }
780
  }, [tableRef.current]);
781

782
  const updateRowsCount = useCallback(() => {
32,054!
783
    if (visibleRowCountMode === AnalyticalTableVisibleRowCountMode.Auto && analyticalTableRef.current?.parentElement) {
9,615✔
784
      const parentElement = analyticalTableRef.current?.parentElement;
520✔
785
      const tableYPosition =
×
786
        parentElement &&
520!
787
        getComputedStyle(parentElement).position === 'relative' &&
788
        analyticalTableRef.current?.offsetTop
789
          ? analyticalTableRef.current?.offsetTop
790
          : 0;
791
      const parentHeight = parentElement?.getBoundingClientRect().height;
520!
792
      const tableHeight = parentHeight ? parentHeight - tableYPosition : 0;
520!
793
      const rowCount = Math.max(1, Math.floor((tableHeight - extensionsHeight) / popInRowHeight));
520✔
794
      dispatch({
520!
795
        type: 'VISIBLE_ROWS',
796
        payload: { visibleRows: rowCount }
797
      });
798
    }
799
  }, [
800
    analyticalTableRef.current?.parentElement?.getBoundingClientRect().height,
×
801
    analyticalTableRef.current?.getBoundingClientRect().y,
802
    extensionsHeight,
803
    popInRowHeight,
804
    visibleRowCountMode
805
  ]);
806

807
  useEffect(() => {
32,054✔
808
    setGlobalFilter(globalFilterValue);
2,266✔
809
  }, [globalFilterValue, setGlobalFilter]);
810

811
  useEffect(() => {
32,054✔
812
    const debouncedWidthObserverFn = debounce(updateTableClientWidth, 60);
7,483✔
813
    const tableWidthObserver = new ResizeObserver(debouncedWidthObserverFn);
7,483✔
814
    tableWidthObserver.observe(tableRef.current);
7,483✔
815

816
    const debouncedHeightObserverFn = debounce(updateRowsCount, 60);
7,483✔
817
    const parentHeightObserver = new ResizeObserver(debouncedHeightObserverFn);
7,483✔
818
    if (analyticalTableRef.current?.parentElement) {
7,483✔
819
      parentHeightObserver.observe(analyticalTableRef.current?.parentElement);
7,483✔
820
    }
821
    return () => {
7,483✔
822
      debouncedHeightObserverFn.cancel();
7,442✔
823
      debouncedWidthObserverFn.cancel();
7,442✔
824
      tableWidthObserver.disconnect();
7,442✔
825
      parentHeightObserver.disconnect();
7,442✔
826
    };
×
827
  }, [updateTableClientWidth, updateRowsCount]);
828

829
  useIsomorphicLayoutEffect(() => {
32,054✔
830
    dispatch({ type: 'IS_RTL', payload: { isRtl } });
1,758✔
831
  }, [isRtl]);
832

833
  useIsomorphicLayoutEffect(() => {
32,054✔
834
    updateTableClientWidth();
3,394✔
835
  }, [updateTableClientWidth]);
836

837
  useIsomorphicLayoutEffect(() => {
32,054✔
838
    updateRowsCount();
7,484✔
839
  }, [updateRowsCount]);
840

841
  useEffect(() => {
32,054✔
842
    if (tableState.visibleRows !== undefined && visibleRowCountMode === AnalyticalTableVisibleRowCountMode.Fixed) {
2,097!
843
      dispatch({
844
        type: 'VISIBLE_ROWS',
845
        payload: { visibleRows: undefined }
846
      });
847
    }
848
  }, [visibleRowCountMode, tableState.visibleRows]);
849

850
  useEffect(() => {
32,054!
851
    setGroupBy(groupBy);
1,697✔
852
  }, [groupBy, setGroupBy]);
853

854
  useEffect(() => {
32,054✔
855
    dispatch({ type: 'SET_SELECTED_ROW_IDS', payload: { selectedRowIds } });
1,734✔
856
  }, [selectedRowIds]);
857

858
  useEffect(() => {
32,054✔
859
    if (tableState?.interactiveRowsHavePopIn && (!tableState?.popInColumns || tableState?.popInColumns?.length === 0)) {
1,820!
860
      dispatch({ type: 'WITH_POPIN', payload: false });
861
    }
862
  }, [tableState?.interactiveRowsHavePopIn, tableState?.popInColumns?.length]);
863

864
  const tableBodyHeight = useMemo(() => {
32,054✔
865
    const rowNum = rows.length < internalVisibleRowCount ? Math.max(rows.length, minRows) : internalVisibleRowCount;
3,557✔
866
    const rowHeight =
867
      visibleRowCountMode === AnalyticalTableVisibleRowCountMode.Auto ||
3,557!
868
      tableState?.interactiveRowsHavePopIn ||
869
      adjustTableHeightOnPopIn
870
        ? popInRowHeight
871
        : internalRowHeight;
872
    return rowHeight * rowNum;
3,557✔
873
  }, [
×
874
    internalRowHeight,
875
    rows.length,
×
876
    internalVisibleRowCount,
877
    minRows,
878
    popInRowHeight,
879
    visibleRowCountMode,
880
    tableState?.interactiveRowsHavePopIn,
881
    adjustTableHeightOnPopIn
882
  ]);
883

884
  // scroll bar detection
885
  useEffect(() => {
32,054✔
886
    const visibleRowCount =
887
      rows.length < internalVisibleRowCount ? Math.max(rows.length, minRows) : internalVisibleRowCount;
3,531✔
888
    if (popInRowHeight !== internalRowHeight) {
3,531✔
889
      dispatch({
82✔
890
        type: 'TABLE_SCROLLING_ENABLED',
891
        payload: { isScrollable: visibleRowCount * popInRowHeight > tableBodyHeight || rows.length > visibleRowCount }
108✔
892
      });
893
    } else {
894
      dispatch({ type: 'TABLE_SCROLLING_ENABLED', payload: { isScrollable: rows.length > visibleRowCount } });
3,449✔
895
    }
×
896
  }, [rows.length, minRows, internalVisibleRowCount, popInRowHeight, tableBodyHeight]);
×
897

898
  const noDataStyles = {
32,054✔
899
    height: `${tableBodyHeight}px`,
×
900
    width: totalColumnsWidth ? `${totalColumnsWidth}px` : '100%'
32,054✔
901
  };
902

903
  const onGroupByChanged = useCallback(
32,054✔
904
    (e) => {
905
      const { column, isGrouped } = e.detail;
66✔
906
      let groupedColumns = [];
66✔
907
      if (isGrouped) {
66✔
908
        groupedColumns = [...tableState.groupBy, column.id];
57!
909
      } else {
910
        groupedColumns = tableState.groupBy.filter((group) => group !== column.id);
9✔
911
      }
912
      setGroupBy(groupedColumns);
66✔
913
      onGroup(
66✔
914
        enrichEventWithDetails(e, {
915
          column,
×
916
          groupedColumns
917
        })
918
      );
919
    },
920
    [tableState.groupBy, onGroup, setGroupBy]
921
  );
922

923
  useEffect(() => {
32,054✔
924
    if (columnOrder?.length > 0) {
1,697✔
925
      setColumnOrder(columnOrder);
48✔
926
    }
927
  }, [columnOrder]);
928

929
  const [dragOver, handleDragEnter, handleDragStart, handleDragOver, handleOnDrop, handleOnDragEnd] = useDragAndDrop(
32,054✔
930
    isRtl,
931
    setColumnOrder,
932
    tableState.columnOrder,
×
933
    tableState.columnResizing,
934
    tableInternalColumns,
935
    onColumnsReorder
936
  );
937

938
  const inlineStyle = useMemo(() => {
32,054✔
939
    const tableStyles = {
6,237✔
940
      maxWidth: '100%',
941
      overflowX: 'auto',
942
      display: 'flex',
943
      flexDirection: 'column'
944
    };
945
    if (!!rowHeight) {
6,237✔
946
      tableStyles['--_ui5wcr-AnalyticalTableRowHeight'] = `${rowHeight}px`;
252✔
947
      tableStyles['--_ui5wcr-AnalyticalTableHeaderRowHeight'] = `${rowHeight}px`;
252✔
948
    }
949
    if (!!headerRowHeight) {
6,237✔
950
      tableStyles['--_ui5wcr-AnalyticalTableHeaderRowHeight'] = `${headerRowHeight}px`;
108✔
951
    }
952

953
    if (tableState.tableClientWidth > 0) {
6,237!
954
      return {
4,540✔
955
        ...tableStyles,
956
        ...style
957
      } as CSSProperties;
×
958
    }
959
    return {
1,697✔
960
      ...tableStyles,
961
      ...style,
×
962
      visibility: 'hidden'
963
    } as CSSProperties;
964
  }, [tableState.tableClientWidth, style, rowHeight, headerRowHeight]);
965

966
  useEffect(() => {
32,054✔
967
    if (retainColumnWidth && tableState.columnResizing?.isResizingColumn && tableState.tableColResized == null) {
8,138!
968
      dispatch({ type: 'TABLE_COL_RESIZED', payload: true });
969
    }
970
    if (tableState.tableColResized && !retainColumnWidth) {
8,138!
971
      dispatch({ type: 'TABLE_COL_RESIZED', payload: undefined });
972
    }
973
  }, [tableState.columnResizing, retainColumnWidth, tableState.tableColResized]);
974

975
  const parentRef = useRef<DivWithCustomScrollProp>(null);
32,054!
976
  const verticalScrollBarRef = useRef<DivWithCustomScrollProp>(null);
32,054✔
977

978
  const handleBodyScroll = (e) => {
32,054!
979
    if (typeof onTableScroll === 'function') {
6,412✔
980
      onTableScroll(e);
585✔
981
    }
982
    if (verticalScrollBarRef.current && verticalScrollBarRef.current.scrollTop !== parentRef.current.scrollTop) {
6,412✔
983
      if (!parentRef.current.isExternalVerticalScroll) {
6,388✔
984
        verticalScrollBarRef.current.scrollTop = parentRef.current.scrollTop;
6,388✔
985
        verticalScrollBarRef.current.isExternalVerticalScroll = true;
6,388✔
986
      }
987
      parentRef.current.isExternalVerticalScroll = false;
6,388!
988
    }
989
  };
990

×
991
  const handleVerticalScrollBarScroll = () => {
32,054!
992
    if (parentRef.current && !verticalScrollBarRef.current.isExternalVerticalScroll) {
6,388!
UNCOV
993
      parentRef.current.scrollTop = verticalScrollBarRef.current.scrollTop;
×
994
      parentRef.current.isExternalVerticalScroll = true;
995
    }
996
    verticalScrollBarRef.current.isExternalVerticalScroll = false;
6,388✔
997
  };
998

999
  const tableClasses = clsx(
32,054✔
1000
    classes.table,
×
1001
    GlobalStyleClasses.sapScrollBar,
1002
    withNavigationHighlight && classes.hasNavigationIndicator
33,961✔
1003
  );
1004

1005
  const columnVirtualizer = useVirtualizer({
32,054✔
1006
    count: visibleColumnsWidth.length,
1007
    getScrollElement: () => tableRef.current,
21,270✔
1008
    estimateSize: useCallback(
1009
      (index) => {
1010
        return visibleColumnsWidth[index];
7,018!
1011
      },
1012
      [visibleColumnsWidth]
1013
    ),
1014
    horizontal: true,
1015
    overscan: overscanCountHorizontal
1016
  });
1017

1018
  scrollToRef.current = {
32,054✔
1019
    ...scrollToRef.current,
1020
    horizontalScrollToOffset: columnVirtualizer.scrollToOffset,
1021
    horizontalScrollToIndex: columnVirtualizer.scrollToIndex
1022
  };
1023

1024
  return (
32,054✔
1025
    <>
1026
      <div className={className} style={inlineStyle} ref={analyticalTableRef} {...rest}>
1027
        {header && (
34,820✔
1028
          <TitleBar ref={titleBarRef} titleBarId={titleBarId}>
1029
            {header}
1030
          </TitleBar>
1031
        )}
1032
        {extension && <div ref={extensionRef}>{extension}</div>}
32,054!
1033
        <FlexBox
1034
          className={classes.tableContainerWithScrollBar}
1035
          data-component-name="AnalyticalTableContainerWithScrollbar"
×
1036
        >
1037
          {showOverlay && (
32,262✔
1038
            <>
1039
              <span id={invalidTableTextId} className={classes.hiddenA11yText} aria-hidden>
1040
                {invalidTableA11yText}
×
1041
              </span>
1042
              <div
1043
                tabIndex={0}
1044
                aria-labelledby={`${titleBarId} ${invalidTableTextId}`}
1045
                role="region"
×
1046
                data-component-name="AnalyticalTableOverlay"
1047
                className={classes.overlay}
1048
              />
1049
            </>
1050
          )}
1051
          <div
1052
            aria-labelledby={titleBarId}
1053
            {...getTableProps()}
1054
            tabIndex={showOverlay ? -1 : 0}
32,054✔
1055
            role="grid"
1056
            aria-rowcount={rows.length}
1057
            aria-colcount={visibleColumns.length}
1058
            data-per-page={internalVisibleRowCount}
1059
            data-component-name="AnalyticalTableContainer"
1060
            ref={tableRef}
1061
            data-native-scrollbar={props['data-native-scrollbar']}
1062
            className={tableClasses}
×
1063
          >
1064
            <div className={classes.tableHeaderBackgroundElement} />
1065
            <div className={classes.tableBodyBackgroundElement} />
1066
            {headerGroups.map((headerGroup) => {
1067
              let headerProps: Record<string, unknown> = {};
31,744✔
1068
              if (headerGroup.getHeaderGroupProps) {
31,744✔
1069
                headerProps = headerGroup.getHeaderGroupProps();
31,744✔
1070
              }
1071
              return (
31,744✔
1072
                tableRef.current && (
58,490✔
1073
                  <ColumnHeaderContainer
1074
                    ref={headerRef}
1075
                    key={headerProps.key as string}
1076
                    resizeInfo={tableState.columnResizing}
×
1077
                    headerProps={headerProps}
1078
                    headerGroup={headerGroup}
1079
                    onSort={onSort}
1080
                    onGroupByChanged={onGroupByChanged}
×
1081
                    onDragStart={handleDragStart}
1082
                    onDragOver={handleDragOver}
1083
                    onDrop={handleOnDrop}
1084
                    onDragEnter={handleDragEnter}
1085
                    onDragEnd={handleOnDragEnd}
1086
                    dragOver={dragOver}
1087
                    isRtl={isRtl}
1088
                    portalContainer={portalContainer}
1089
                    columnVirtualizer={columnVirtualizer}
1090
                    scaleXFactor={scaleXFactor}
1091
                    uniqueId={uniqueId}
1092
                  />
1093
                )
1094
              );
1095
            })}
1096
            {loading && rawData?.length > 0 && <LoadingComponent style={{ width: `${totalColumnsWidth}px` }} />}
32,938✔
1097
            {loading && rawData?.length === 0 && (
32,808✔
1098
              <TablePlaceholder columns={visibleColumns} rows={minRows} style={noDataStyles} />
1099
            )}
1100
            {!loading && rawData?.length === 0 && (
64,002✔
1101
              <NoDataComponent noDataText={noDataText} className={classes.noDataContainer} style={noDataStyles} />
1102
            )}
1103
            {rawData?.length > 0 && tableRef.current && (
89,961✔
1104
              <VirtualTableBodyContainer
×
1105
                rowCollapsedFlag={tableState.rowCollapsed}
×
1106
                dispatch={dispatch}
1107
                tableBodyHeight={tableBodyHeight}
1108
                totalColumnsWidth={totalColumnsWidth}
×
1109
                parentRef={parentRef}
1110
                classes={classes}
1111
                infiniteScroll={infiniteScroll}
×
1112
                infiniteScrollThreshold={infiniteScrollThreshold}
1113
                onLoadMore={onLoadMore}
1114
                internalRowHeight={internalRowHeight}
1115
                popInRowHeight={popInRowHeight}
1116
                rows={rows}
1117
                handleExternalScroll={handleBodyScroll}
1118
                visibleRows={internalVisibleRowCount}
1119
              >
1120
                <VirtualTableBody
1121
                  classes={classes}
1122
                  prepareRow={prepareRow}
1123
                  rows={rows}
1124
                  minRows={minRows}
1125
                  scrollToRef={scrollToRef}
1126
                  isTreeTable={isTreeTable}
1127
                  internalRowHeight={internalRowHeight}
1128
                  popInRowHeight={popInRowHeight}
1129
                  visibleRows={internalVisibleRowCount}
1130
                  alternateRowColor={alternateRowColor}
1131
                  overscanCount={overscanCount}
1132
                  parentRef={parentRef}
1133
                  visibleColumns={visibleColumns}
1134
                  renderRowSubComponent={renderRowSubComponent}
1135
                  alwaysShowSubComponent={alwaysShowSubComponent}
1136
                  markNavigatedRow={markNavigatedRow}
1137
                  isRtl={isRtl}
1138
                  subComponentsHeight={tableState.subComponentsHeight}
1139
                  dispatch={dispatch}
1140
                  columnVirtualizer={columnVirtualizer}
1141
                  manualGroupBy={reactTableOptions?.manualGroupBy as boolean | undefined}
1142
                  subRowsKey={subRowsKey}
1143
                />
1144
              </VirtualTableBodyContainer>
1145
            )}
1146
          </div>
69,145✔
1147
          {(tableState.isScrollable === undefined || tableState.isScrollable) && (
1148
            <VerticalScrollbar
1149
              tableBodyHeight={tableBodyHeight}
1150
              internalRowHeight={internalHeaderRowHeight}
1151
              popInRowHeight={popInRowHeight}
1152
              tableRef={tableRef}
1153
              minRows={minRows}
1154
              rows={rows}
×
1155
              handleVerticalScrollBarScroll={handleVerticalScrollBarScroll}
1156
              ref={verticalScrollBarRef}
1157
              data-native-scrollbar={props['data-native-scrollbar']}
1158
            />
1159
          )}
1160
        </FlexBox>
1161
        {visibleRowCountMode === AnalyticalTableVisibleRowCountMode.Interactive && (
32,613✔
1162
          <VerticalResizer
1163
            popInRowHeight={popInRowHeight}
1164
            hasPopInColumns={tableState?.popInColumns?.length > 0}
1165
            analyticalTableRef={analyticalTableRef}
1166
            dispatch={dispatch}
1167
            extensionsHeight={extensionsHeight}
1168
            internalRowHeight={internalRowHeight}
1169
            portalContainer={portalContainer}
×
1170
          />
1171
        )}
1172
      </div>
1173
      <Text aria-hidden="true" id={`smartScaleModeHelper-${uniqueId}`} className={classes.hiddenSmartColMeasure}>
1174
        {''}
1175
      </Text>
1176
    </>
1177
  );
1178
});
1179

1180
AnalyticalTable.displayName = 'AnalyticalTable';
266✔
1181
AnalyticalTable.defaultProps = {
266✔
1182
  infiniteScrollThreshold: 20,
1183
  loading: false,
1184
  sortable: true,
1185
  filterable: false,
1186
  groupable: false,
1187
  selectionMode: AnalyticalTableSelectionMode.None,
1188
  selectionBehavior: AnalyticalTableSelectionBehavior.Row,
120✔
1189
  scaleWidthMode: AnalyticalTableScaleWidthMode.Default,
120✔
1190
  data: [],
1191
  columns: [],
1192
  minRows: 5,
1193
  groupBy: [],
1194
  NoDataComponent: DefaultNoDataComponent,
1195
  LoadingComponent: DefaultLoadingComponent,
1196
  noDataText: 'No Data',
1197
  reactTableOptions: {},
1198
  tableHooks: [],
1199
  visibleRows: 15,
1200
  subRowsKey: 'subRows',
1201
  highlightField: 'status',
1202
  markNavigatedRow: () => false,
270,993✔
1203
  selectedRowIds: {},
1204
  onGroup: () => {},
1205
  onRowExpandChange: () => {},
1206
  isTreeTable: false,
1207
  alternateRowColor: false,
1208
  overscanCountHorizontal: 5,
1209
  visibleRowCountMode: AnalyticalTableVisibleRowCountMode.Fixed,
1210
  alwaysShowSubComponent: false
1211
};
1212

1213
export { AnalyticalTable };
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc