@indico-data/design-system 2.48.0 → 2.50.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.
Files changed (41) hide show
  1. package/lib/components/table/Table.stories.d.ts +1 -0
  2. package/lib/components/table/components/HorizontalStickyHeader.d.ts +10 -0
  3. package/lib/components/table/components/__tests__/HorizontalStickyHeader.test.d.ts +1 -0
  4. package/lib/components/table/components/helpers.d.ts +6 -0
  5. package/lib/components/table/hooks/usePinnedColumnsManager.d.ts +8 -0
  6. package/lib/components/table/sampleData.d.ts +4 -0
  7. package/lib/components/table/types.d.ts +11 -1
  8. package/lib/components/table/utils/processColumns.d.ts +2 -0
  9. package/lib/index.css +188 -89
  10. package/lib/index.d.ts +12 -1
  11. package/lib/index.esm.css +188 -89
  12. package/lib/index.esm.js +238 -2
  13. package/lib/index.esm.js.map +1 -1
  14. package/lib/index.js +238 -2
  15. package/lib/index.js.map +1 -1
  16. package/lib/stylesAndAnimations/utilityClasses/UtilityClassesData.d.ts +7 -0
  17. package/lib/stylesAndAnimations/utilityClasses/UtilityClassesTable.d.ts +1 -0
  18. package/lib/stylesAndAnimations/utilityClasses/UtilityClassesTable.stories.d.ts +6 -0
  19. package/lib/utils/getPreviousHeadersWidth.d.ts +1 -0
  20. package/package.json +1 -1
  21. package/src/components/table/Table.mdx +134 -0
  22. package/src/components/table/Table.stories.tsx +71 -2
  23. package/src/components/table/Table.tsx +16 -1
  24. package/src/components/table/components/HorizontalStickyHeader.tsx +57 -0
  25. package/src/components/table/components/__tests__/HorizontalStickyHeader.test.tsx +104 -0
  26. package/src/components/table/components/helpers.ts +90 -0
  27. package/src/components/table/hooks/usePinnedColumnsManager.ts +146 -0
  28. package/src/components/table/{sampleData.ts → sampleData.tsx} +156 -1
  29. package/src/components/table/styles/Table.scss +32 -15
  30. package/src/components/table/styles/_variables.scss +2 -0
  31. package/src/components/table/types.ts +13 -1
  32. package/src/components/table/utils/processColumns.tsx +35 -0
  33. package/src/setup/setupTests.ts +8 -0
  34. package/src/storybookDocs/Permafrost.mdx +22 -11
  35. package/src/styles/_borders.scss +2 -1
  36. package/src/stylesAndAnimations/borders/BorderColor.tsx +14 -6
  37. package/src/stylesAndAnimations/utilityClasses/UtilityClasses.mdx +24 -0
  38. package/src/stylesAndAnimations/utilityClasses/UtilityClassesData.ts +230 -0
  39. package/src/stylesAndAnimations/utilityClasses/UtilityClassesTable.stories.tsx +13 -0
  40. package/src/stylesAndAnimations/utilityClasses/UtilityClassesTable.tsx +146 -0
  41. package/src/utils/getPreviousHeadersWidth.ts +12 -0
@@ -0,0 +1,7 @@
1
+ export type UtilityClassData = {
2
+ className: string;
3
+ css: string;
4
+ category: string;
5
+ resolvedCss?: string;
6
+ };
7
+ export declare const utilityClassesData: UtilityClassData[];
@@ -0,0 +1 @@
1
+ export declare const UtilityClassesTable: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { UtilityClassesTable } from './UtilityClassesTable';
3
+ declare const meta: Meta<typeof UtilityClassesTable>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof UtilityClassesTable>;
6
+ export declare const Default: Story;
@@ -0,0 +1 @@
1
+ export declare const getPreviousHeadersWidth: (position: number) => number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indico-data/design-system",
3
- "version": "2.48.0",
3
+ "version": "2.50.0",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "main": "lib/index.js",
@@ -21,6 +21,9 @@ The `conditionalRowStyles` prop allows you to apply custom styles to specific ro
21
21
 
22
22
  - **`highlighted`**: This class is used to visually emphasize specific rows. When applied, the row will inherit styles defined in the design system, making the row stand out with distinct border and background colors.
23
23
 
24
+ ### Issues with storybook
25
+ You may notice the stories may behave a little strangely on storybook, this is not the case in the application. One such bug would be the checkboxes pinning on the default example when toggling props. Another will be the select all checkbox not being locked on the second pinned column example. We have not investigated as this is likely a quirk with the storybook environment.
26
+
24
27
  ### Example Usage
25
28
 
26
29
  Here's an example of how to use the `conditionalRowStyles` prop to apply the `checked` and `highlighted` classes:
@@ -44,3 +47,134 @@ const conditionalRowStyles = [
44
47
  conditionalRowStyles={conditionalRowStyles}
45
48
  />;
46
49
  ```
50
+
51
+ ## Pinned Columns
52
+
53
+ The `canPinColumns` prop allows you to pin columns to the left hand side of the table. This is useful for displaying important columns that should be visible at all times.
54
+
55
+ ## Known Bugs
56
+ A limitation of the library has caused us to compromise on the column widths. We had to choose between allowing the pinning of Fixed Width columns, or allowing the pinning of Auto Widths columns. As it stands right now, we went with Fixed Width columns. As a result, you will experience pixel drift on the third+ columns if you do not use fixed width. An example on how to use the fixed width is the following.
57
+ ```jsx
58
+ columns={[
59
+ {
60
+ isPinned: false,
61
+ name: 'Name',
62
+ selector: () => {},
63
+ width: '150px'
64
+ },
65
+ ]}
66
+ ```
67
+
68
+ ### Note
69
+
70
+ It will require both `canPinColumns` to be set to `true` and the columns that you wish to have the pin icon contain `isPinned: true` or `isPinned: false` to function. If `isPinned` is undefined, no icon will appear.
71
+ <Canvas of={TableStories.PinnedColumns} />
72
+ ```jsx
73
+ <Table
74
+ canPinColumns
75
+ columns={[
76
+ {
77
+ isPinned: false,
78
+ name: 'Name',
79
+ selector: () => {},
80
+ width: '150px'
81
+ },
82
+ {
83
+ isPinned: true,
84
+ name: 'Class',
85
+ selector: () => {},
86
+ width: '150px'
87
+ },
88
+ {
89
+ isPinned: true,
90
+ name: 'Age',
91
+ selector: () => {},
92
+ width: '150px'
93
+ },
94
+ {
95
+ name: 'Weapon',
96
+ selector: () => {}
97
+ },
98
+ {
99
+ name: 'Backstory',
100
+ selector: () => {}
101
+ },
102
+ {
103
+ name: 'Favorite Meal',
104
+ selector: () => {}
105
+ },
106
+ {
107
+ name: 'Homeland',
108
+ selector: () => {}
109
+ },
110
+ {
111
+ name: 'Alignment',
112
+ selector: () => {}
113
+ },
114
+ {
115
+ isPinned: false,
116
+ name: 'Special Ability',
117
+ selector: () => {}
118
+ }
119
+ ]}
120
+ data={[
121
+ {
122
+ age: 120,
123
+ alignment: 'Neutral Good',
124
+ backstory: 'Raised by wolves in the deep forests.',
125
+ class: 'Ranger',
126
+ favoriteMeal: 'Venison stew',
127
+ homeland: 'Silverleaf Forest',
128
+ name: 'Thalion',
129
+ specialAbility: 'Beast Speech',
130
+ test: 'test',
131
+ weapon: 'Longbow'
132
+ },
133
+ {
134
+ age: 35,
135
+ alignment: 'Lawful Good',
136
+ backstory: 'A former soldier seeking redemption.',
137
+ class: 'Fighter',
138
+ favoriteMeal: 'Roasted boar',
139
+ homeland: 'Kingdom of Valorhaven',
140
+ name: 'Brom',
141
+ specialAbility: 'Battle Master',
142
+ test: 'test',
143
+ weapon: 'Greatsword'
144
+ },
145
+ {
146
+ age: 60,
147
+ alignment: 'Lawful Good',
148
+ backstory: 'A devoted follower of the goddess of life.',
149
+ class: 'Cleric',
150
+ favoriteMeal: 'Vegetable soup',
151
+ homeland: 'Temple of Dawn',
152
+ name: 'Elysia',
153
+ specialAbility: 'Divine Healing',
154
+ test: 'test',
155
+ weapon: 'Mace'
156
+ },
157
+ //... more rows
158
+ ]}
159
+ dense
160
+ direction="ltr"
161
+ fixedHeader
162
+ noDataComponent={null}
163
+ onPinnedColumnsChange={() => {}}
164
+ pagination
165
+ paginationPerPage={10}
166
+ paginationRowsPerPageOptions={[
167
+ 5,
168
+ 10,
169
+ 15,
170
+ 20
171
+ ]}
172
+ responsive
173
+ selectableRows
174
+ subHeaderAlign="center"
175
+ subHeaderComponent={null}
176
+ subHeaderWrap
177
+ title="Character List"
178
+ totalEntriesText="Showing 12 of 12 entries."
179
+ />
180
+ ```
@@ -3,6 +3,7 @@ import { Table } from './Table';
3
3
  import { columns, sampleData, SampleDataRow } from './sampleData';
4
4
  import { registerFontAwesomeIcons } from '@/setup/setupIcons';
5
5
  import { indiconDefinitions } from '@/components/icons/indicons';
6
+ import { useState } from 'react';
6
7
 
7
8
  registerFontAwesomeIcons(...Object.values(indiconDefinitions));
8
9
 
@@ -12,7 +13,8 @@ const meta: Meta = {
12
13
  argTypes: {
13
14
  columns: {
14
15
  control: false,
15
- description: 'The columns to display in the table',
16
+ description:
17
+ 'The columns to display in the table. All columns require a unique id property. For pinned columns, please see the pinned example below.',
16
18
  table: {
17
19
  category: 'Data',
18
20
  type: { summary: 'array' },
@@ -251,6 +253,21 @@ const meta: Meta = {
251
253
  category: 'Styling',
252
254
  },
253
255
  },
256
+ canPinColumns: {
257
+ control: 'boolean',
258
+ description:
259
+ 'Allows the pinning of columns to the left hand side. This is required when using the column pin API',
260
+ table: {
261
+ category: 'Add-Ons',
262
+ },
263
+ },
264
+ onPinnedColumnsChange: {
265
+ control: false,
266
+ description: 'Callback that receives the IDs of the pinned columns when they change.',
267
+ table: {
268
+ category: 'Add-Ons',
269
+ },
270
+ },
254
271
  // hidden props
255
272
  onRowDoubleClicked: {
256
273
  table: {
@@ -521,6 +538,41 @@ type Story = StoryObj<typeof Table<SampleDataRow>>;
521
538
 
522
539
  export const Default: Story = {
523
540
  args: {
541
+ canPinColumns: false,
542
+ pagination: true,
543
+ selectableRows: true,
544
+ isDisabled: false,
545
+ isLoading: false,
546
+ direction: 'ltr',
547
+ striped: false,
548
+ subHeaderAlign: 'center',
549
+ subHeaderWrap: true,
550
+ paginationRowsPerPageOptions: [5, 10, 15, 20],
551
+ responsive: true,
552
+ title: 'Character List',
553
+ dense: true,
554
+ noHeader: false,
555
+ noTableHead: false,
556
+ noDataComponent: null,
557
+ expandableRows: false,
558
+ clearSelectedRows: false,
559
+ subHeader: false,
560
+ subHeaderComponent: null,
561
+ fixedHeader: true,
562
+ paginationPerPage: 10,
563
+ isFullHeight: false,
564
+ totalEntriesText: 'Showing 12 of 12 entries.',
565
+ columns: columns,
566
+ data: sampleData,
567
+ },
568
+ render: ({ ...args }) => {
569
+ return <Table {...args} columns={columns} />;
570
+ },
571
+ };
572
+
573
+ export const PinnedColumns: Story = {
574
+ args: {
575
+ canPinColumns: true,
524
576
  pagination: true,
525
577
  selectableRows: true,
526
578
  isDisabled: false,
@@ -540,11 +592,28 @@ export const Default: Story = {
540
592
  clearSelectedRows: false,
541
593
  subHeader: false,
542
594
  subHeaderComponent: null,
595
+ fixedHeader: true,
543
596
  paginationPerPage: 10,
544
597
  isFullHeight: false,
545
598
  totalEntriesText: 'Showing 12 of 12 entries.',
546
599
  columns: columns,
547
600
  data: sampleData,
548
601
  },
549
- render: ({ ...args }) => <Table {...args} />,
602
+ render: ({ ...args }) => {
603
+ const initialPinnedColumnIds = ['name', 'class'];
604
+ const [pinnedColumnIds, setPinnedColumnIds] = useState<string[]>(initialPinnedColumnIds);
605
+
606
+ const columnsWithPinning = columns.map((column) => ({
607
+ ...column,
608
+ isPinned: pinnedColumnIds.includes(column.id as string),
609
+ }));
610
+
611
+ return (
612
+ <Table
613
+ {...args}
614
+ columns={columnsWithPinning}
615
+ onPinnedColumnsChange={(ids) => setPinnedColumnIds(ids)}
616
+ />
617
+ );
618
+ },
550
619
  };
@@ -3,10 +3,10 @@ import DataTable, {
3
3
  Direction as RDTDirection,
4
4
  Alignment as RDTAlignment,
5
5
  } from 'react-data-table-component';
6
-
7
6
  import { LoadingComponent } from './LoadingComponent';
8
7
  import { TableProps } from './types';
9
8
  import { TablePagination } from './components/TablePagination';
9
+ import { usePinnedColumnsManager } from './hooks/usePinnedColumnsManager';
10
10
 
11
11
  export const Table = <T,>(props: TableProps<T>) => {
12
12
  const {
@@ -22,9 +22,20 @@ export const Table = <T,>(props: TableProps<T>) => {
22
22
  className,
23
23
  paginationTotalRows,
24
24
  totalEntriesText,
25
+ data,
26
+ columns: initialColumns,
27
+ canPinColumns = false,
28
+ onPinnedColumnsChange,
25
29
  ...rest
26
30
  } = props;
27
31
 
32
+ // Turns on/off column pinning.
33
+ const { columnsWithPinning } = usePinnedColumnsManager(
34
+ initialColumns,
35
+ canPinColumns,
36
+ onPinnedColumnsChange,
37
+ );
38
+
28
39
  const combinedClassName = classNames(className, {
29
40
  'table--striped': striped,
30
41
  'table-body': true,
@@ -37,6 +48,8 @@ export const Table = <T,>(props: TableProps<T>) => {
37
48
  return (
38
49
  <div className={tableWrapperClassName} data-testid="table">
39
50
  <DataTable
51
+ data={data}
52
+ columns={columnsWithPinning}
40
53
  responsive={responsive}
41
54
  direction={direction as RDTDirection}
42
55
  subHeaderAlign={subHeaderAlign as RDTAlignment}
@@ -52,6 +65,8 @@ export const Table = <T,>(props: TableProps<T>) => {
52
65
  <TablePagination {...props} totalEntriesText={totalEntriesText} />
53
66
  )}
54
67
  paginationTotalRows={paginationTotalRows}
68
+ highlightOnHover
69
+ pointerOnHover
55
70
  {...rest}
56
71
  />
57
72
  </div>
@@ -0,0 +1,57 @@
1
+ import { useEffect } from 'react';
2
+ import { Button } from '../../button/Button';
3
+ import {
4
+ getPreviousHeadersWidth,
5
+ applyStickyStylesToTableHeader,
6
+ clearStickyStyles,
7
+ } from './helpers';
8
+ interface HorizontalStickyHeaderProps {
9
+ children: React.ReactNode;
10
+ position: number;
11
+ onPinColumn?: (columnId: string) => void;
12
+ isPinned?: boolean;
13
+ forceUpdate?: number;
14
+ pinnedColumnIds: string[];
15
+ }
16
+
17
+ const HorizontalStickyHeader = ({
18
+ children,
19
+ position,
20
+ onPinColumn,
21
+ isPinned = false,
22
+ pinnedColumnIds,
23
+ }: HorizontalStickyHeaderProps) => {
24
+ useEffect(() => {
25
+ const calculateWidth = async () => {
26
+ await new Promise((resolve) => setTimeout(resolve, 0));
27
+ const header = document.querySelector(`[data-column-id="sticky-column-${position}"]`);
28
+ if (header) {
29
+ if (isPinned) {
30
+ const width = getPreviousHeadersWidth(position, pinnedColumnIds);
31
+ await applyStickyStylesToTableHeader(position, width);
32
+ } else {
33
+ clearStickyStyles(header as HTMLElement);
34
+ }
35
+ }
36
+ };
37
+
38
+ calculateWidth();
39
+ }, [position, isPinned, pinnedColumnIds]);
40
+
41
+ return (
42
+ <div className="table__header-cell" data-testid={`sticky-column-${position}`}>
43
+ <Button
44
+ data-testid={`sticky-header-pin-button-${position}`}
45
+ variant="link"
46
+ size="sm"
47
+ iconLeft="pin"
48
+ onClick={onPinColumn}
49
+ ariaLabel={isPinned ? 'Unpin column' : 'Pin column'}
50
+ className={`table__column--${isPinned ? 'is-pinned' : 'is-not-pinned'} table__column__pin-action`}
51
+ />
52
+ <div className="table__header-content">{children}</div>
53
+ </div>
54
+ );
55
+ };
56
+
57
+ export default HorizontalStickyHeader;
@@ -0,0 +1,104 @@
1
+ import { render, screen, fireEvent } from '@testing-library/react';
2
+ import HorizontalStickyHeader from '../HorizontalStickyHeader';
3
+ import { sampleData } from '../../sampleData';
4
+ import { Table } from '../../Table';
5
+ import { columns } from '../../sampleData';
6
+ import userEvent from '@testing-library/user-event';
7
+
8
+ // Add ResizeObserver mock before tests
9
+ class ResizeObserverMock {
10
+ observe() {}
11
+ unobserve() {}
12
+ disconnect() {}
13
+ }
14
+
15
+ // Add the mock to the global object
16
+ global.ResizeObserver = ResizeObserverMock;
17
+
18
+ describe('HorizontalStickyHeader', () => {
19
+ // Store original implementation
20
+ const originalGetBoundingClientRect = Element.prototype.getBoundingClientRect;
21
+
22
+ afterEach(() => {
23
+ // Restore original implementation after each test
24
+ Element.prototype.getBoundingClientRect = originalGetBoundingClientRect;
25
+ });
26
+
27
+ it('correctly handles column pinning states based on column configuration', () => {
28
+ render(
29
+ <Table
30
+ responsive
31
+ columns={columns}
32
+ canPinColumns
33
+ data={sampleData}
34
+ totalEntriesText="100 entries"
35
+ />,
36
+ );
37
+
38
+ columns.forEach((column, index) => {
39
+ if (column.isPinned === undefined) {
40
+ // For columns where isPinned is not defined, the pin button should not exist
41
+ const pinButton = screen.queryByTestId(`sticky-header-pin-button-${index}`);
42
+ expect(pinButton).not.toBeInTheDocument();
43
+ } else {
44
+ // For columns where isPinned is defined, check for the correct class
45
+ const pinButton = screen.getByTestId(`sticky-header-pin-button-${index}`);
46
+ expect(pinButton).toHaveClass(
47
+ column.isPinned ? 'table__column--is-pinned' : 'table__column--is-not-pinned',
48
+ );
49
+ }
50
+ });
51
+ });
52
+
53
+ it('fires the onPinColumn callback when the pin button is clicked', () => {
54
+ const onPinColumn = jest.fn();
55
+ render(
56
+ <HorizontalStickyHeader position={0} pinnedColumnIds={[]} onPinColumn={onPinColumn}>
57
+ Test Content
58
+ </HorizontalStickyHeader>,
59
+ );
60
+
61
+ const pinButton = screen.getByTestId('sticky-header-pin-button-0');
62
+ fireEvent.click(pinButton);
63
+ expect(onPinColumn).toHaveBeenCalled();
64
+ });
65
+
66
+ it('correctly handles column pinning states based on column configuration', () => {
67
+ render(
68
+ <Table
69
+ responsive
70
+ columns={columns}
71
+ canPinColumns
72
+ data={sampleData}
73
+ totalEntriesText="100 entries"
74
+ />,
75
+ );
76
+
77
+ columns.forEach((column, index) => {
78
+ if (column.isPinned === undefined) {
79
+ // For columns where isPinned is not defined, the pin button should not exist
80
+ const pinButton = screen.queryByTestId(`sticky-header-pin-button-${index}`);
81
+ expect(pinButton).not.toBeInTheDocument();
82
+ } else {
83
+ // For columns where isPinned is defined, check for the correct class
84
+ const pinButton = screen.getByTestId(`sticky-header-pin-button-${index}`);
85
+ expect(pinButton).toHaveClass(
86
+ column.isPinned ? 'table__column--is-pinned' : 'table__column--is-not-pinned',
87
+ );
88
+ }
89
+ });
90
+ });
91
+
92
+ it('fires the onPinColumn callback when the pin button is clicked', () => {
93
+ const onPinColumn = jest.fn();
94
+ render(
95
+ <HorizontalStickyHeader position={0} pinnedColumnIds={[]} onPinColumn={onPinColumn}>
96
+ Test Content
97
+ </HorizontalStickyHeader>,
98
+ );
99
+
100
+ const pinButton = screen.getByTestId('sticky-header-pin-button-0');
101
+ fireEvent.click(pinButton);
102
+ expect(onPinColumn).toHaveBeenCalled();
103
+ });
104
+ });
@@ -0,0 +1,90 @@
1
+ import { CSSObject } from 'styled-components';
2
+
3
+ // Gets the width of the previous pinned columns
4
+ export const getPreviousHeadersWidth = (position: number, pinnedColumnIds: string[]): number => {
5
+ let totalWidth = 0;
6
+
7
+ // Add checkbox column width if it's pinned
8
+ if (pinnedColumnIds.includes('checkbox-column')) {
9
+ const checkboxCell = document.querySelector('.rdt_TableCol:not([data-column-id])');
10
+ if (checkboxCell) {
11
+ totalWidth += (checkboxCell as HTMLElement).offsetWidth;
12
+ }
13
+ }
14
+
15
+ // Add widths of other pinned columns before this position
16
+ const previousHeaders = Array.from({ length: position }, (_, i) => {
17
+ const header = document.querySelector(`[data-column-id="sticky-column-${i}"]`);
18
+ if (header && pinnedColumnIds.includes(`sticky-column-${i}`)) {
19
+ return header as HTMLElement;
20
+ }
21
+ return null;
22
+ }).filter((header): header is HTMLElement => header !== null);
23
+
24
+ // Calculate base width from previous columns
25
+ totalWidth = previousHeaders.reduce((acc, header) => {
26
+ return acc + header.offsetWidth;
27
+ }, totalWidth);
28
+
29
+ // Leave this for if we ever try to fix the auto width columns
30
+ // There is a bug where borders cause the offset to be wrong, this keeps it in sync.
31
+ // Add offset that increases by 1 every two columns after index 1
32
+ // if (position >= 2) {
33
+ // const additionalOffset = Math.floor((position - 2) / 2) + 1;
34
+ // totalWidth += additionalOffset;
35
+ // }
36
+
37
+ return totalWidth;
38
+ };
39
+
40
+ // Applies sticky styles to the column header
41
+ export const applyStickyStylesToTableHeader = async (position: number, left: number) => {
42
+ const header = document.querySelector(`[data-column-id="sticky-column-${position}"]`);
43
+ if (header) {
44
+ (header as HTMLElement).style.position = 'sticky';
45
+ (header as HTMLElement).style.left = `${left}px`;
46
+ (header as HTMLElement).style.zIndex = '3';
47
+ (header as HTMLElement).style.backgroundColor =
48
+ 'var(--pf-table-pinned-column-background-color)';
49
+ }
50
+ };
51
+
52
+ // Sorts the pinned columns so that any column that is pinned comes before any column that is not.
53
+ export const sortPinnedColumns = <T>(columns: T[], pinnedColumnIds: string[]): T[] => {
54
+ return [...columns].sort((a, b) => {
55
+ const aIsPinned = pinnedColumnIds.includes((a as any).id);
56
+ const bIsPinned = pinnedColumnIds.includes((b as any).id);
57
+
58
+ if (aIsPinned && !bIsPinned) return -1; // a comes first
59
+ if (!aIsPinned && bIsPinned) return 1; // b comes first
60
+ return 0; // maintain relative order for columns with same pinned state
61
+ });
62
+ };
63
+
64
+ // Gets the styles for the pinned columns
65
+ export const getPinnedColumnStyles = (
66
+ isPinned: boolean,
67
+ index: number,
68
+ pinnedColumnIds: string[],
69
+ ): CSSObject => {
70
+ return isPinned
71
+ ? {
72
+ position: 'sticky',
73
+ left: `${getPreviousHeadersWidth(index, pinnedColumnIds)}px`,
74
+ zIndex: 3,
75
+ backgroundColor: 'var(--pf-table-pinned-column-background-color)',
76
+ }
77
+ : {
78
+ position: undefined,
79
+ left: undefined,
80
+ zIndex: undefined,
81
+ backgroundColor: undefined,
82
+ };
83
+ };
84
+
85
+ export const clearStickyStyles = (header: HTMLElement) => {
86
+ header.style.position = '';
87
+ header.style.left = '';
88
+ header.style.zIndex = '';
89
+ header.style.backgroundColor = '';
90
+ };