@indico-data/design-system 2.47.3 → 2.49.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 (54) hide show
  1. package/lib/components/index.d.ts +1 -0
  2. package/lib/components/pagination/Pagination.d.ts +2 -0
  3. package/lib/components/pagination/Pagination.stories.d.ts +6 -0
  4. package/lib/components/pagination/__tests__/Pagination.test.d.ts +1 -0
  5. package/lib/components/pagination/index.d.ts +1 -0
  6. package/lib/components/pagination/types.d.ts +6 -0
  7. package/lib/components/table/Table.stories.d.ts +1 -0
  8. package/lib/components/table/__tests__/Table.test.d.ts +1 -0
  9. package/lib/components/table/components/HorizontalStickyHeader.d.ts +10 -0
  10. package/lib/components/table/components/TablePagination.d.ts +9 -0
  11. package/lib/components/table/components/__tests__/HorizontalStickyHeader.test.d.ts +1 -0
  12. package/lib/components/table/components/__tests__/TablePagination.test.d.ts +1 -0
  13. package/lib/components/table/components/helpers.d.ts +6 -0
  14. package/lib/components/table/hooks/usePinnedColumnsManager.d.ts +8 -0
  15. package/lib/components/table/sampleData.d.ts +6 -0
  16. package/lib/components/table/types.d.ts +16 -5
  17. package/lib/components/table/utils/processColumns.d.ts +2 -0
  18. package/lib/index.css +78 -17
  19. package/lib/index.d.ts +16 -5
  20. package/lib/index.esm.css +78 -17
  21. package/lib/index.esm.js +305 -14
  22. package/lib/index.esm.js.map +1 -1
  23. package/lib/index.js +304 -13
  24. package/lib/index.js.map +1 -1
  25. package/lib/utils/getPreviousHeadersWidth.d.ts +1 -0
  26. package/package.json +1 -1
  27. package/src/components/index.ts +1 -0
  28. package/src/components/pagination/Pagination.mdx +31 -0
  29. package/src/components/pagination/Pagination.stories.tsx +80 -0
  30. package/src/components/pagination/Pagination.tsx +117 -0
  31. package/src/components/pagination/__tests__/Pagination.test.tsx +91 -0
  32. package/src/components/pagination/index.ts +1 -0
  33. package/src/components/pagination/styles/Pagination.scss +22 -0
  34. package/src/components/pagination/types.ts +6 -0
  35. package/src/components/table/Table.mdx +136 -0
  36. package/src/components/table/Table.stories.tsx +91 -30
  37. package/src/components/table/Table.tsx +25 -2
  38. package/src/components/table/__tests__/Table.test.tsx +10 -0
  39. package/src/components/table/components/HorizontalStickyHeader.tsx +57 -0
  40. package/src/components/table/components/TablePagination.tsx +44 -0
  41. package/src/components/table/components/__tests__/HorizontalStickyHeader.test.tsx +104 -0
  42. package/src/components/table/components/__tests__/TablePagination.test.tsx +17 -0
  43. package/src/components/table/components/helpers.ts +90 -0
  44. package/src/components/table/hooks/usePinnedColumnsManager.ts +146 -0
  45. package/src/components/table/sampleData.tsx +436 -0
  46. package/src/components/table/styles/Table.scss +72 -24
  47. package/src/components/table/styles/_variables.scss +3 -0
  48. package/src/components/table/types.ts +19 -7
  49. package/src/components/table/utils/processColumns.tsx +35 -0
  50. package/src/setup/setupIcons.ts +4 -0
  51. package/src/setup/setupTests.ts +8 -0
  52. package/src/styles/index.scss +1 -0
  53. package/src/utils/getPreviousHeadersWidth.ts +12 -0
  54. package/src/components/table/sampleData.ts +0 -171
@@ -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.47.3",
3
+ "version": "2.49.0",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "main": "lib/index.js",
@@ -20,3 +20,4 @@ export { SingleInputDatePicker } from './forms/date/inputDatePicker';
20
20
  export { InputDateRangePicker } from './forms/date/inputDateRangePicker';
21
21
  export { Modal } from './modal';
22
22
  export { Badge } from './badge';
23
+ export { Pagination } from './pagination';
@@ -0,0 +1,31 @@
1
+ import { Canvas, Meta, Controls, Story } from '@storybook/blocks';
2
+ import * as Pagination from './Pagination.stories';
3
+ import { Container, Row, Col } from '../grid';
4
+ import { fas } from '@fortawesome/free-solid-svg-icons';
5
+ import { registerFontAwesomeIcons } from '@/setup/setupIcons';
6
+ import { indiconDefinitions } from '@/components/icons/indicons';
7
+
8
+
9
+ <Meta title="Layout/Pagination" name="Pagination" of={Pagination} />
10
+
11
+ # Pagination
12
+
13
+ <Canvas of={Pagination.Default} />
14
+
15
+ ### The following props are available for the Pagination component:
16
+
17
+ <Controls of={Pagination.Default} />
18
+
19
+ ### Usage
20
+
21
+ The pagination component is used to navigate through a list of items. It is already baked into the Table component. To manage the pagination, there is an `onChange` callback that returns the next page, previous page, or the page entered in the input field. This will then be used by your implementation to update the page.
22
+
23
+ ```tsx
24
+
25
+ <Pagination totalPages={10} currentPage={1} onChange={(page) => console.log(page)} />
26
+ ```
27
+
28
+
29
+ ### Notes
30
+
31
+ The input field allows the user to enter an invalid number but it will warn them by highlighting the input in red. When the user clicks out, it will then revert to its original value.
@@ -0,0 +1,80 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { Pagination } from './Pagination';
3
+ import { Col, Container, Row } from '../grid';
4
+ import { useEffect, useState } from 'react';
5
+
6
+ const meta: Meta = {
7
+ title: 'Layout/Pagination',
8
+ component: Pagination,
9
+ argTypes: {
10
+ className: {
11
+ control: false,
12
+ description: 'The css class name for the pagination component',
13
+ table: {
14
+ category: 'Props',
15
+ type: {
16
+ summary: 'css class',
17
+ },
18
+ },
19
+ },
20
+ totalPages: {
21
+ control: 'number',
22
+ description: 'The total number of pages to be displayed',
23
+ table: {
24
+ category: 'Props',
25
+ type: {
26
+ summary: 'number',
27
+ },
28
+ },
29
+ },
30
+ currentPage: {
31
+ control: 'number',
32
+ description: 'The current page displayed in the input field.',
33
+ table: {
34
+ category: 'Props',
35
+ type: {
36
+ summary: 'number',
37
+ },
38
+ },
39
+ },
40
+ onChange: {
41
+ action: 'change',
42
+ description: 'The callback function that is called when the page changes.',
43
+ table: {
44
+ category: 'Callbacks',
45
+ type: {
46
+ summary: '(page: number) => void',
47
+ },
48
+ },
49
+ },
50
+ },
51
+ };
52
+
53
+ export default meta;
54
+
55
+ type Story = StoryObj<typeof Pagination>;
56
+
57
+ export const Default: Story = {
58
+ args: {
59
+ totalPages: 10,
60
+ currentPage: 1,
61
+ },
62
+
63
+ render: (args) => {
64
+ const [currentPage, setCurrentPage] = useState(args.currentPage);
65
+
66
+ useEffect(() => {
67
+ setCurrentPage(args.currentPage);
68
+ }, [args.currentPage]);
69
+
70
+ return (
71
+ <Container>
72
+ <Row>
73
+ <Col sm={4}>
74
+ <Pagination {...args} currentPage={currentPage} onChange={setCurrentPage} />
75
+ </Col>
76
+ </Row>
77
+ </Container>
78
+ );
79
+ },
80
+ };
@@ -0,0 +1,117 @@
1
+ import { useState, useEffect } from 'react';
2
+ import classNames from 'classnames';
3
+ import { PaginationProps } from './types';
4
+ import { Container, Row, Col } from '../grid';
5
+ import { Input } from '../forms/input';
6
+ import { Button } from '../button';
7
+
8
+ export const Pagination = ({
9
+ totalPages,
10
+ currentPage = 1,
11
+ onChange,
12
+ className,
13
+ ...rest
14
+ }: PaginationProps) => {
15
+ const [inputValue, setInputValue] = useState(currentPage.toString());
16
+ const totalPagesText = `of ${totalPages}`;
17
+ const classes = classNames('pagination', className);
18
+
19
+ useEffect(() => {
20
+ setInputValue(currentPage.toString());
21
+ }, [currentPage]);
22
+
23
+ const handleNextPage = () => {
24
+ if (currentPage < totalPages) {
25
+ onChange?.(currentPage + 1);
26
+ }
27
+ };
28
+
29
+ const handlePreviousPage = () => {
30
+ if (currentPage > 1) {
31
+ onChange?.(currentPage - 1);
32
+ }
33
+ };
34
+
35
+ const validateAndUpdatePage = (value: string) => {
36
+ // If empty or invalid, reset to current page
37
+ if (!value) {
38
+ setInputValue(currentPage.toString());
39
+ return;
40
+ }
41
+
42
+ const page = Number(value);
43
+ if (!isNaN(page) && page > 0 && page <= totalPages) {
44
+ onChange?.(page);
45
+ } else {
46
+ setInputValue(currentPage.toString());
47
+ }
48
+ };
49
+
50
+ const isNextButtonDisabled = currentPage === totalPages;
51
+ const isPreviousButtonDisabled = currentPage === 1;
52
+
53
+ const hasError = Number(inputValue) > totalPages || Number(inputValue) < 1;
54
+
55
+ return (
56
+ <div className={classes} {...rest}>
57
+ <Container>
58
+ <Row gutterWidth={12} align="center">
59
+ <Col xs="content">
60
+ <div className="pagination__previous">
61
+ <Button
62
+ data-testid="pagination-previous-button"
63
+ ariaLabel="Previous Page"
64
+ variant="link"
65
+ onClick={handlePreviousPage}
66
+ iconLeft="chevron-left"
67
+ isDisabled={isPreviousButtonDisabled}
68
+ />
69
+ </div>
70
+ </Col>
71
+ <Col xs="content">
72
+ <div className="pagination__current-page">
73
+ <Input
74
+ data-testid="pagination-current-page-input"
75
+ className={classNames('pagination__current-page-input', {
76
+ 'has-error': hasError,
77
+ })}
78
+ value={inputValue}
79
+ name="currentPage"
80
+ label="Current Page"
81
+ hasHiddenLabel
82
+ onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
83
+ if (e.key === 'Enter') {
84
+ validateAndUpdatePage(e.currentTarget.value);
85
+ }
86
+ }}
87
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
88
+ const value = e.currentTarget.value;
89
+ // Allow empty value or numbers
90
+ if (value === '' || /^\d*$/.test(value)) {
91
+ setInputValue(value);
92
+ }
93
+ }}
94
+ onBlur={(e) => validateAndUpdatePage(e.currentTarget.value)}
95
+ />
96
+ </div>
97
+ </Col>
98
+ <Col xs="content">
99
+ <p className="pagination__page-total">{totalPagesText}</p>
100
+ </Col>
101
+ <Col xs="content">
102
+ <div className="pagination__next">
103
+ <Button
104
+ data-testid="pagination-next-button"
105
+ ariaLabel="Next Page"
106
+ variant="link"
107
+ onClick={handleNextPage}
108
+ iconLeft="chevron-right"
109
+ isDisabled={isNextButtonDisabled}
110
+ />
111
+ </div>
112
+ </Col>
113
+ </Row>
114
+ </Container>
115
+ </div>
116
+ );
117
+ };
@@ -0,0 +1,91 @@
1
+ import { fireEvent, render, screen } from '@testing-library/react';
2
+ import { Pagination } from '../Pagination';
3
+
4
+ describe('Pagination', () => {
5
+ it('fires the onChange callback when the user clicks the next button', () => {
6
+ const onChange = jest.fn();
7
+ render(<Pagination totalPages={10} currentPage={1} onChange={onChange} />);
8
+ fireEvent.click(screen.getByTestId('pagination-next-button'));
9
+ expect(onChange).toHaveBeenCalledWith(2);
10
+ });
11
+
12
+ it('fires the onChange callback when the user clicks the previous button', () => {
13
+ const onChange = jest.fn();
14
+ render(<Pagination totalPages={10} currentPage={2} onChange={onChange} />);
15
+ fireEvent.click(screen.getByTestId('pagination-previous-button'));
16
+ expect(onChange).toHaveBeenCalledWith(1);
17
+ });
18
+
19
+ it('does not fire the onchange callback when the user clicks next and they are already on the last page', () => {
20
+ const onChange = jest.fn();
21
+ render(<Pagination totalPages={10} currentPage={10} onChange={onChange} />);
22
+ fireEvent.click(screen.getByTestId('pagination-next-button'));
23
+ expect(onChange).not.toHaveBeenCalled();
24
+ });
25
+
26
+ it('does not fire the onchange callback when the user clicks previous and they are already on the first page', () => {
27
+ const onChange = jest.fn();
28
+ render(<Pagination totalPages={10} currentPage={1} onChange={onChange} />);
29
+ fireEvent.click(screen.getByTestId('pagination-previous-button'));
30
+ expect(onChange).not.toHaveBeenCalled();
31
+ });
32
+
33
+ it('fires the onchange callback when the user enters a value in the input field', () => {
34
+ const onChange = jest.fn();
35
+ render(<Pagination totalPages={10} currentPage={1} onChange={onChange} />);
36
+ fireEvent.change(screen.getByTestId('pagination-current-page-input'), {
37
+ target: { value: '2' },
38
+ });
39
+ fireEvent.keyDown(screen.getByTestId('pagination-current-page-input'), {
40
+ key: 'Enter',
41
+ code: 'Enter',
42
+ });
43
+ expect(onChange).toHaveBeenCalledWith(2);
44
+ });
45
+
46
+ it('does not fire the onChange callback when the user enters an invalid value in the input field', () => {
47
+ const onChange = jest.fn();
48
+ render(<Pagination totalPages={10} currentPage={1} onChange={onChange} />);
49
+
50
+ // Test non-numeric input
51
+ fireEvent.change(screen.getByTestId('pagination-current-page-input'), {
52
+ target: { value: 'abc' },
53
+ });
54
+ expect(onChange).not.toHaveBeenCalled();
55
+
56
+ // Test empty input
57
+ fireEvent.change(screen.getByTestId('pagination-current-page-input'), {
58
+ target: { value: '' },
59
+ });
60
+ expect(onChange).not.toHaveBeenCalled();
61
+
62
+ // Test out of range input
63
+ fireEvent.change(screen.getByTestId('pagination-current-page-input'), {
64
+ target: { value: '0' },
65
+ });
66
+ expect(onChange).not.toHaveBeenCalled();
67
+
68
+ fireEvent.change(screen.getByTestId('pagination-current-page-input'), {
69
+ target: { value: '11' },
70
+ });
71
+ expect(onChange).not.toHaveBeenCalled();
72
+ });
73
+
74
+ it('disables the next button when the user is on the last page', () => {
75
+ render(<Pagination totalPages={10} currentPage={10} onChange={() => {}} />);
76
+ expect(screen.getByTestId('pagination-next-button')).toBeDisabled();
77
+ });
78
+
79
+ it('disables the previous button when the user is on the first page', () => {
80
+ render(<Pagination totalPages={10} currentPage={1} onChange={() => {}} />);
81
+ expect(screen.getByTestId('pagination-previous-button')).toBeDisabled();
82
+ });
83
+
84
+ it('adds the has-error class to the input field when the user enters an invalid value', () => {
85
+ render(<Pagination totalPages={10} currentPage={1} onChange={() => {}} />);
86
+ fireEvent.change(screen.getByTestId('pagination-current-page-input'), {
87
+ target: { value: '11' },
88
+ });
89
+ expect(screen.getByTestId('pagination-current-page-input')).toHaveClass('has-error');
90
+ });
91
+ });
@@ -0,0 +1 @@
1
+ export { Pagination } from './Pagination';
@@ -0,0 +1,22 @@
1
+ .pagination {
2
+ .form-control {
3
+ margin-bottom: 0;
4
+ }
5
+ }
6
+
7
+ .pagination__current-page {
8
+ max-width: 50px;
9
+ }
10
+
11
+ .pagination__current-page-input {
12
+ text-align: center;
13
+ font-weight: var(--pf-font-weight-heavy);
14
+ }
15
+
16
+ .pagination {
17
+ .pagination__current-page-input {
18
+ &.has-error {
19
+ border-color: var(--pf-error-color);
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,6 @@
1
+ export interface PaginationProps {
2
+ totalPages: number;
3
+ currentPage?: number;
4
+ className?: string;
5
+ onChange?: (value: number) => void;
6
+ }
@@ -1,6 +1,8 @@
1
1
  import { Canvas, Meta, Controls } from '@storybook/blocks';
2
2
  import * as TableStories from './Table.stories';
3
3
 
4
+
5
+
4
6
  <Meta title="Layout/Table" name="Table" />
5
7
 
6
8
  # Table
@@ -19,6 +21,9 @@ The `conditionalRowStyles` prop allows you to apply custom styles to specific ro
19
21
 
20
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.
21
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
+
22
27
  ### Example Usage
23
28
 
24
29
  Here's an example of how to use the `conditionalRowStyles` prop to apply the `checked` and `highlighted` classes:
@@ -42,3 +47,134 @@ const conditionalRowStyles = [
42
47
  conditionalRowStyles={conditionalRowStyles}
43
48
  />;
44
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
+ ```
@@ -1,6 +1,11 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
2
  import { Table } from './Table';
3
- import { SampleDataRow, sampleData } from './sampleData';
3
+ import { columns, sampleData, SampleDataRow } from './sampleData';
4
+ import { registerFontAwesomeIcons } from '@/setup/setupIcons';
5
+ import { indiconDefinitions } from '@/components/icons/indicons';
6
+ import { useState } from 'react';
7
+
8
+ registerFontAwesomeIcons(...Object.values(indiconDefinitions));
4
9
 
5
10
  const meta: Meta = {
6
11
  title: 'Layout/Table',
@@ -8,7 +13,8 @@ const meta: Meta = {
8
13
  argTypes: {
9
14
  columns: {
10
15
  control: false,
11
- 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.',
12
18
  table: {
13
19
  category: 'Data',
14
20
  type: { summary: 'array' },
@@ -239,6 +245,29 @@ const meta: Meta = {
239
245
  defaultValue: { summary: 'false' },
240
246
  },
241
247
  },
248
+ totalEntriesText: {
249
+ control: 'text',
250
+ description:
251
+ 'The text to display in the total entries section. This is hidden if 1. No pagination exists, 2. No string is passed.',
252
+ table: {
253
+ category: 'Styling',
254
+ },
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
+ },
242
271
  // hidden props
243
272
  onRowDoubleClicked: {
244
273
  table: {
@@ -495,6 +524,11 @@ const meta: Meta = {
495
524
  disable: true,
496
525
  },
497
526
  },
527
+ currentPage: {
528
+ table: {
529
+ disable: true,
530
+ },
531
+ },
498
532
  },
499
533
  };
500
534
 
@@ -504,6 +538,7 @@ type Story = StoryObj<typeof Table<SampleDataRow>>;
504
538
 
505
539
  export const Default: Story = {
506
540
  args: {
541
+ canPinColumns: false,
507
542
  pagination: true,
508
543
  selectableRows: true,
509
544
  isDisabled: false,
@@ -523,36 +558,62 @@ export const Default: Story = {
523
558
  clearSelectedRows: false,
524
559
  subHeader: false,
525
560
  subHeaderComponent: null,
561
+ fixedHeader: true,
526
562
  paginationPerPage: 10,
527
563
  isFullHeight: false,
528
- columns: [
529
- {
530
- name: 'Name',
531
- selector: (row) => row.name,
532
- },
533
- {
534
- name: 'Class',
535
- selector: (row) => row.class,
536
- },
537
- {
538
- name: 'Age',
539
- selector: (row) => row.age,
540
- sortable: true,
541
- },
542
- {
543
- name: 'Weapon',
544
- selector: (row) => row.weapon,
545
- },
546
- {
547
- name: 'Backstory',
548
- selector: (row) => row.backstory,
549
- },
550
- {
551
- name: 'Favorite Meal',
552
- selector: (row) => row.favoriteMeal,
553
- },
554
- ],
564
+ totalEntriesText: 'Showing 12 of 12 entries.',
565
+ columns: columns,
555
566
  data: sampleData,
556
567
  },
557
- render: ({ ...args }) => <Table {...args} />,
568
+ render: ({ ...args }) => {
569
+ return <Table {...args} columns={columns} />;
570
+ },
571
+ };
572
+
573
+ export const PinnedColumns: Story = {
574
+ args: {
575
+ canPinColumns: true,
576
+ pagination: true,
577
+ selectableRows: true,
578
+ isDisabled: false,
579
+ isLoading: false,
580
+ direction: 'ltr',
581
+ striped: false,
582
+ subHeaderAlign: 'center',
583
+ subHeaderWrap: true,
584
+ paginationRowsPerPageOptions: [5, 10, 15, 20],
585
+ responsive: true,
586
+ title: 'Character List',
587
+ dense: true,
588
+ noHeader: false,
589
+ noTableHead: false,
590
+ noDataComponent: null,
591
+ expandableRows: false,
592
+ clearSelectedRows: false,
593
+ subHeader: false,
594
+ subHeaderComponent: null,
595
+ fixedHeader: true,
596
+ paginationPerPage: 10,
597
+ isFullHeight: false,
598
+ totalEntriesText: 'Showing 12 of 12 entries.',
599
+ columns: columns,
600
+ data: sampleData,
601
+ },
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
+ },
558
619
  };