@patternfly/react-table 6.5.0-prerelease.61 → 6.5.0-prerelease.62

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/components/package.json +1 -1
  3. package/deprecated/package.json +1 -1
  4. package/dist/dynamic/components/Table/package.json +1 -1
  5. package/dist/dynamic/components/Table/utils/decorators/package.json +1 -1
  6. package/dist/dynamic/components/Table/utils/package.json +1 -1
  7. package/dist/dynamic/deprecated/components/Table/base/package.json +1 -1
  8. package/dist/dynamic/deprecated/components/Table/package.json +1 -1
  9. package/dist/dynamic/deprecated/components/package.json +1 -1
  10. package/dist/esm/components/Table/InnerScrollContainer.d.ts +3 -1
  11. package/dist/esm/components/Table/InnerScrollContainer.d.ts.map +1 -1
  12. package/dist/esm/components/Table/InnerScrollContainer.js +5 -3
  13. package/dist/esm/components/Table/InnerScrollContainer.js.map +1 -1
  14. package/dist/esm/components/Table/Table.d.ts +5 -1
  15. package/dist/esm/components/Table/Table.d.ts.map +1 -1
  16. package/dist/esm/components/Table/Table.js +3 -3
  17. package/dist/esm/components/Table/Table.js.map +1 -1
  18. package/dist/js/components/Table/InnerScrollContainer.d.ts +3 -1
  19. package/dist/js/components/Table/InnerScrollContainer.d.ts.map +1 -1
  20. package/dist/js/components/Table/InnerScrollContainer.js +5 -4
  21. package/dist/js/components/Table/InnerScrollContainer.js.map +1 -1
  22. package/dist/js/components/Table/Table.d.ts +5 -1
  23. package/dist/js/components/Table/Table.d.ts.map +1 -1
  24. package/dist/js/components/Table/Table.js +3 -3
  25. package/dist/js/components/Table/Table.js.map +1 -1
  26. package/dist/umd/assets/{output-Df8I4QNx.css → output-Br-aNBLk.css} +6267 -6267
  27. package/dist/umd/react-table.min.js +3 -3
  28. package/package.json +2 -2
  29. package/src/components/Table/InnerScrollContainer.tsx +9 -2
  30. package/src/components/Table/Table.tsx +9 -1
  31. package/src/components/Table/__tests__/Table.test.tsx +36 -0
  32. package/src/components/Table/examples/Table.md +11 -2
  33. package/src/components/Table/examples/TableStickyHeaderDynamic.tsx +140 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-table",
3
- "version": "6.5.0-prerelease.61",
3
+ "version": "6.5.0-prerelease.62",
4
4
  "description": "This library provides a set of React table components for use with the PatternFly 4",
5
5
  "main": "dist/js/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -51,5 +51,5 @@
51
51
  "react": "^17 || ^18 || ^19",
52
52
  "react-dom": "^17 || ^18 || ^19"
53
53
  },
54
- "gitHead": "6558bdd10eb90061e973dcc37ec971eb2b1a354d"
54
+ "gitHead": "a42646491bffc8f27c860285ba61630931f755dc"
55
55
  }
@@ -1,3 +1,4 @@
1
+ import { forwardRef } from 'react';
1
2
  import { css } from '@patternfly/react-styles';
2
3
  import styles from '@patternfly/react-styles/css/components/Table/table-scrollable';
3
4
 
@@ -6,16 +7,22 @@ export interface InnerScrollContainerProps extends React.HTMLProps<HTMLDivElemen
6
7
  children?: React.ReactNode;
7
8
  /** Additional classes added to the container */
8
9
  className?: string;
10
+ /** @hide Forwarded ref */
11
+ innerRef?: React.Ref<HTMLDivElement>;
9
12
  }
10
13
 
11
- export const InnerScrollContainer: React.FunctionComponent<InnerScrollContainerProps> = ({
14
+ const InnerScrollContainerBase: React.FunctionComponent<InnerScrollContainerProps> = ({
12
15
  children,
13
16
  className,
17
+ innerRef,
14
18
  ...props
15
19
  }: InnerScrollContainerProps) => (
16
- <div className={css(className, styles.scrollInnerWrapper)} {...props}>
20
+ <div ref={innerRef} className={css(className, styles.scrollInnerWrapper)} {...props}>
17
21
  {children}
18
22
  </div>
19
23
  );
20
24
 
25
+ export const InnerScrollContainer = forwardRef((props: InnerScrollContainerProps, ref: React.Ref<HTMLDivElement>) => (
26
+ <InnerScrollContainerBase innerRef={ref} {...props} />
27
+ ));
21
28
  InnerScrollContainer.displayName = 'InnerScrollContainer';
@@ -52,8 +52,12 @@ export interface TableProps extends React.HTMLProps<HTMLTableElement>, OUIAProps
52
52
  isPlain?: boolean;
53
53
  /** @beta Flag indicating if the table should not have plain styling when in the glass theme */
54
54
  isNoPlainOnGlass?: boolean;
55
- /** If set to true, the table header sticks to the top of its container */
55
+ /** If set to true, the table header sticks to the top of its container. This property applies both the sticky position and styling. */
56
56
  isStickyHeader?: boolean;
57
+ /** @beta Flag indicating the table header should have sticky positioning to the top of the parent InnerScrollContainer. */
58
+ isStickyHeaderBase?: boolean;
59
+ /** @beta Flag indicating the table header should have stuck styling, when the header is not at the top of the scroll container. */
60
+ isStickyHeaderStuck?: boolean;
57
61
  /** @hide Forwarded ref */
58
62
  innerRef?: React.RefObject<any>;
59
63
  /** Flag indicating table is a tree table */
@@ -98,6 +102,8 @@ const TableBase: React.FunctionComponent<TableProps> = ({
98
102
  variant,
99
103
  borders = true,
100
104
  isStickyHeader = false,
105
+ isStickyHeaderBase = false,
106
+ isStickyHeaderStuck = false,
101
107
  isPlain = false,
102
108
  isNoPlainOnGlass = false,
103
109
  gridBreakPoint = TableGridBreakpoint.gridMd,
@@ -225,6 +231,8 @@ const TableBase: React.FunctionComponent<TableProps> = ({
225
231
  styles.modifiers[variant],
226
232
  !borders && styles.modifiers.noBorderRows,
227
233
  isStickyHeader && styles.modifiers.stickyHeader,
234
+ isStickyHeaderBase && styles.modifiers.stickyHeaderBase,
235
+ isStickyHeaderStuck && styles.modifiers.stickyHeaderStuck,
228
236
  isTreeTable && stylesTreeView.modifiers.treeView,
229
237
  isStriped && styles.modifiers.striped,
230
238
  isExpandable && styles.modifiers.expandable,
@@ -186,3 +186,39 @@ test(`Does not render with class ${styles.modifiers.noPlainOnGlass} when isNoPla
186
186
 
187
187
  expect(screen.getByRole('grid', { name: 'Test table' })).not.toHaveClass(styles.modifiers.noPlainOnGlass);
188
188
  });
189
+
190
+ test(`Renders with class ${styles.modifiers.stickyHeaderBase} when isStickyHeaderBase is true`, () => {
191
+ render(<Table isStickyHeaderBase aria-label="Test table" />);
192
+
193
+ expect(screen.getByRole('grid', { name: 'Test table' })).toHaveClass(styles.modifiers.stickyHeaderBase);
194
+ });
195
+
196
+ test(`Does not render with class ${styles.modifiers.stickyHeaderBase} when isStickyHeaderBase is false`, () => {
197
+ render(<Table isStickyHeaderBase={false} aria-label="Test table" />);
198
+
199
+ expect(screen.getByRole('grid', { name: 'Test table' })).not.toHaveClass(styles.modifiers.stickyHeaderBase);
200
+ });
201
+
202
+ test(`Does not render with class ${styles.modifiers.stickyHeaderBase} when isStickyHeaderBase is undefined`, () => {
203
+ render(<Table aria-label="Test table" />);
204
+
205
+ expect(screen.getByRole('grid', { name: 'Test table' })).not.toHaveClass(styles.modifiers.stickyHeaderBase);
206
+ });
207
+
208
+ test(`Renders with class ${styles.modifiers.stickyHeaderStuck} when isStickyHeaderStuck is true`, () => {
209
+ render(<Table isStickyHeaderStuck aria-label="Test table" />);
210
+
211
+ expect(screen.getByRole('grid', { name: 'Test table' })).toHaveClass(styles.modifiers.stickyHeaderStuck);
212
+ });
213
+
214
+ test(`Does not render with class ${styles.modifiers.stickyHeaderStuck} when isStickyHeaderStuck is false`, () => {
215
+ render(<Table isStickyHeaderStuck={false} aria-label="Test table" />);
216
+
217
+ expect(screen.getByRole('grid', { name: 'Test table' })).not.toHaveClass(styles.modifiers.stickyHeaderStuck);
218
+ });
219
+
220
+ test(`Does not render with class ${styles.modifiers.stickyHeaderStuck} when isStickyHeaderStuck is undefined`, () => {
221
+ render(<Table aria-label="Test table" />);
222
+
223
+ expect(screen.getByRole('grid', { name: 'Test table' })).not.toHaveClass(styles.modifiers.stickyHeaderStuck);
224
+ });
@@ -41,7 +41,7 @@ The `Table` component takes an explicit and declarative approach, and its implem
41
41
 
42
42
  The documentation for the deprecated table implementation can be found under the [React deprecated](/components/table/react-deprecated) tab. It is configuration based and takes a less declarative and more implicit approach to laying out the table structure, such as the rows and cells within it.
43
43
 
44
- import { Fragment, isValidElement, useCallback, useEffect, useRef, useState } from 'react';
44
+ import { Fragment, isValidElement, useCallback, useEffect, useRef, useState, useLayoutEffect } from 'react';
45
45
  import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
46
46
  import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon';
47
47
  import CodeIcon from '@patternfly/react-icons/dist/esm/icons/code-icon';
@@ -327,7 +327,6 @@ To enable a tree table:
327
327
  - `checkAriaLabel` - (optional) accessible label for the checkbox
328
328
  - `showDetailsAriaLabel` - (optional) accessible label for the show row details button in the responsive view
329
329
  4. The first `Td` in each row will pass the following to the `treeRow` prop:
330
-
331
330
  - `onCollapse` - Callback when user expands/collapses a row to reveal/hide the row's children.
332
331
  - `onCheckChange` - (optional) Callback when user changes the checkbox on a row.
333
332
  - `onToggleRowDetails` - (optional) Callback when user shows/hides the row details in responsive view.
@@ -419,6 +418,16 @@ To prevent the default text wrapping behavior and allow horizontal scrolling, al
419
418
 
420
419
  ```
421
420
 
421
+ ### Dynamic sticky header
422
+
423
+ A sticky header may alternatively be implemented with two properties: `isStickyHeaderBase` and `isStickyHeaderStuck` - which allows separate control of the sticky position and sticky styling. `isStickyHeaderBase` should always be applied to make the header position sticky, and `isStickyHeaderStuck` may be applied dynamically to enable the sticky styling, such as when the sticky header is not at the top of the scroll parent as shown in the example.
424
+
425
+ `isStickyHeader` acts as if both properties are present and true when applied, and is useful when dynamic sticky styling is not necessary.
426
+
427
+ ```ts file="TableStickyHeaderDynamic.tsx"
428
+
429
+ ```
430
+
422
431
  ### Sticky columns and header
423
432
 
424
433
  To maintain proper sticky behavior across sticky columns and header, `Table` must be wrapped with `OuterScrollContainer` and `InnerScrollContainer`.
@@ -0,0 +1,140 @@
1
+ import { useLayoutEffect, useRef, useState } from 'react';
2
+ import { Table, Thead, Tr, Th, Tbody, Td, InnerScrollContainer } from '@patternfly/react-table';
3
+ import BlueprintIcon from '@patternfly/react-icons/dist/esm/icons/blueprint-icon';
4
+
5
+ interface Fact {
6
+ name: string;
7
+ state: string;
8
+ detail1: string;
9
+ detail2: string;
10
+ detail3: string;
11
+ detail4: string;
12
+ detail5: string;
13
+ detail6: string;
14
+ detail7: string;
15
+ }
16
+
17
+ const useIsStuckFromScrollParent = ({
18
+ shouldTrack,
19
+ scrollParentRef
20
+ }: {
21
+ /** Indicates whether to track the scroll top position of the scroll parent element */
22
+ shouldTrack: boolean;
23
+ /** Reference to the scroll parent element */
24
+ scrollParentRef: React.RefObject<any>;
25
+ }): boolean => {
26
+ const [isStuck, setIsStuck] = useState(false);
27
+
28
+ useLayoutEffect(() => {
29
+ if (!shouldTrack) {
30
+ setIsStuck(false);
31
+ return;
32
+ }
33
+
34
+ const scrollElement = scrollParentRef.current;
35
+ if (!scrollElement) {
36
+ setIsStuck(false);
37
+ return;
38
+ }
39
+
40
+ const syncFromScroll = () => {
41
+ setIsStuck(scrollElement.scrollTop > 0);
42
+ };
43
+ syncFromScroll();
44
+ scrollElement.addEventListener('scroll', syncFromScroll, { passive: true });
45
+ return () => scrollElement.removeEventListener('scroll', syncFromScroll);
46
+ }, [shouldTrack, scrollParentRef]);
47
+
48
+ return isStuck;
49
+ };
50
+
51
+ export const TableStickyHeaderDynamic: React.FunctionComponent = () => {
52
+ const scrollContainerRef = useRef<HTMLDivElement>(null);
53
+ const isStuck = useIsStuckFromScrollParent({ shouldTrack: true, scrollParentRef: scrollContainerRef });
54
+
55
+ // In real usage, this data would come from some external source like an API via props.
56
+ const facts: Fact[] = Array.from({ length: 9 }, (_, index) => ({
57
+ name: `Fact ${index + 1}`,
58
+ state: `State ${index + 1}`,
59
+ detail1: `Test cell ${index + 1}-3`,
60
+ detail2: `Test cell ${index + 1}-4`,
61
+ detail3: `Test cell ${index + 1}-5`,
62
+ detail4: `Test cell ${index + 1}-6`,
63
+ detail5: `Test cell ${index + 1}-7`,
64
+ detail6: `Test cell ${index + 1}-8`,
65
+ detail7: `Test cell ${index + 1}-9`
66
+ }));
67
+
68
+ const columnNames = {
69
+ name: 'Fact',
70
+ state: 'State',
71
+ header3: 'Header 3',
72
+ header4: 'Header 4',
73
+ header5: 'Header 5',
74
+ header6: 'Header 6',
75
+ header7: 'Header 7',
76
+ header8: 'Header 8',
77
+ header9: 'Header 9'
78
+ };
79
+
80
+ return (
81
+ <div style={{ height: '400px' }}>
82
+ <InnerScrollContainer ref={scrollContainerRef}>
83
+ <Table
84
+ aria-label="Dynamic sticky header table"
85
+ gridBreakPoint=""
86
+ isStickyHeaderBase
87
+ isStickyHeaderStuck={isStuck}
88
+ >
89
+ <Thead>
90
+ <Tr>
91
+ <Th modifier="truncate">{columnNames.name}</Th>
92
+ <Th modifier="truncate">{columnNames.state}</Th>
93
+ <Th modifier="truncate">{columnNames.header3}</Th>
94
+ <Th modifier="truncate">{columnNames.header4}</Th>
95
+ <Th modifier="truncate">{columnNames.header5}</Th>
96
+ <Th modifier="truncate">{columnNames.header6}</Th>
97
+ <Th modifier="truncate">{columnNames.header7}</Th>
98
+ <Th modifier="truncate">{columnNames.header8}</Th>
99
+ <Th modifier="truncate">{columnNames.header9}</Th>
100
+ </Tr>
101
+ </Thead>
102
+ <Tbody>
103
+ {facts.map((fact) => (
104
+ <Tr key={fact.name}>
105
+ <Td modifier="nowrap" dataLabel={columnNames.name}>
106
+ {fact.name}
107
+ </Td>
108
+ <Td modifier="nowrap" dataLabel={columnNames.state}>
109
+ <BlueprintIcon />
110
+ {` ${fact.state}`}
111
+ </Td>
112
+ <Td modifier="nowrap" dataLabel={columnNames.header3}>
113
+ {fact.detail1}
114
+ </Td>
115
+ <Td modifier="nowrap" dataLabel={columnNames.header4}>
116
+ {fact.detail2}
117
+ </Td>
118
+ <Td modifier="nowrap" dataLabel={columnNames.header5}>
119
+ {fact.detail3}
120
+ </Td>
121
+ <Td modifier="nowrap" dataLabel={columnNames.header6}>
122
+ {fact.detail4}
123
+ </Td>
124
+ <Td modifier="nowrap" dataLabel={columnNames.header7}>
125
+ {fact.detail5}
126
+ </Td>
127
+ <Td modifier="nowrap" dataLabel={columnNames.header8}>
128
+ {fact.detail6}
129
+ </Td>
130
+ <Td modifier="nowrap" dataLabel={columnNames.header9}>
131
+ {fact.detail7}
132
+ </Td>
133
+ </Tr>
134
+ ))}
135
+ </Tbody>
136
+ </Table>
137
+ </InnerScrollContainer>
138
+ </div>
139
+ );
140
+ };