Skip to content

Commit 5170f92

Browse files
committed
feat: add table story
1 parent 04bbe4e commit 5170f92

File tree

1 file changed

+241
-0
lines changed

1 file changed

+241
-0
lines changed

src/stories/Table.stories.tsx

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
import { Meta, StoryObj } from '@storybook/react'
2+
import {
3+
Button,
4+
ButtonComponentType,
5+
ButtonStyleType,
6+
ButtonVariantType,
7+
ComponentSizeType,
8+
FiltersTypeEnum,
9+
PaginationEnum,
10+
SearchBar,
11+
SelectAllDialogStatus,
12+
Table,
13+
TableCellComponentProps,
14+
TableProps,
15+
TableViewWrapperProps,
16+
TableSignalEnum,
17+
} from '@devtron-labs/devtron-fe-common-lib'
18+
import { ReactComponent as ICPlay } from '@Icons/ic-play-outline.svg'
19+
import { ReactComponent as ICPause } from '@Icons/ic-pause.svg'
20+
import { ReactComponent as ICWarning } from '@Icons/ic-warning-y6.svg'
21+
import { useEffect, useState } from 'react'
22+
23+
const CellComponent = ({ field, value, signals, row }: TableCellComponentProps) => {
24+
const [isRowActive, setIsRowActive] = useState(false)
25+
26+
const handleButtonClick = () => {
27+
alert(`Row ${value} clicked`)
28+
}
29+
30+
useEffect(() => {
31+
const rowChangeCallback = ({
32+
detail: {
33+
activeRowData: { id },
34+
},
35+
}) => {
36+
setIsRowActive(id === row.id)
37+
}
38+
39+
const rowEnterPressedCallback = ({
40+
detail: {
41+
activeRowData: { id },
42+
},
43+
}) => {
44+
if (id === row.id && field === 'name') {
45+
handleButtonClick()
46+
}
47+
}
48+
49+
signals.addEventListener(TableSignalEnum.ACTIVE_ROW_CHANGED, rowChangeCallback)
50+
51+
signals.addEventListener(TableSignalEnum.ENTER_PRESSED, rowEnterPressedCallback)
52+
53+
return () => {
54+
signals.removeEventListener(TableSignalEnum.ACTIVE_ROW_CHANGED, rowChangeCallback)
55+
56+
signals.removeEventListener(TableSignalEnum.ENTER_PRESSED, rowEnterPressedCallback)
57+
}
58+
}, [])
59+
60+
if (field === 'name') {
61+
return (
62+
<div className="flexbox dc__align-items-center">
63+
<Button
64+
variant={ButtonVariantType.text}
65+
text={value as string}
66+
dataTestId={`${field}-${row.id}`}
67+
style={isRowActive ? ButtonStyleType.default : ButtonStyleType.neutral}
68+
onClick={handleButtonClick}
69+
/>
70+
</div>
71+
)
72+
}
73+
74+
return (
75+
<div className="flexbox dc__gap-6 dc__align-items-center">
76+
<ICWarning className="dc__no-shrink icon-dim-18" />
77+
78+
<span>{value}</span>
79+
</div>
80+
)
81+
}
82+
83+
const COLUMNS: TableProps['columns'] = [
84+
{
85+
field: 'name',
86+
size: { fixed: 300 },
87+
label: 'Name',
88+
comparator: (a: string, b: string) => a.localeCompare(b),
89+
isSortable: true,
90+
CellComponent,
91+
},
92+
{
93+
field: 'value',
94+
size: {
95+
range: {
96+
startWidth: 180,
97+
minWidth: 100,
98+
maxWidth: 600,
99+
},
100+
},
101+
label: 'Value',
102+
},
103+
{
104+
field: 'message',
105+
size: {
106+
fixed: 200,
107+
},
108+
label: 'Message',
109+
CellComponent,
110+
},
111+
]
112+
113+
type RowDataType = {
114+
name: string
115+
value: string
116+
message: string
117+
}
118+
119+
const ROWS: TableProps['rows'] = [
120+
{ id: '1', data: { name: 'Alice', value: '123', message: 'Something new' } },
121+
{ id: '2', data: { name: 'Bob', value: '456', message: 'Another message' } },
122+
{ id: '3', data: { name: 'Charlie', value: '789', message: 'Yet another one' } },
123+
{ id: '4', data: { name: 'Diana', value: '101', message: 'Message here' } },
124+
{ id: '5', data: { name: 'Eve', value: '202', message: 'Something else' } },
125+
{ id: '6', data: { name: 'Frank', value: '303', message: 'New message' } },
126+
{ id: '7', data: { name: 'Grace', value: '404', message: 'Important note' } },
127+
{ id: '8', data: { name: 'Hank', value: '505', message: 'Final message' } },
128+
{ id: '9', data: { name: 'Ivy', value: '606', message: 'Additional info' } },
129+
{ id: '10', data: { name: 'Jack', value: '707', message: 'Critical update' } },
130+
{ id: '11', data: { name: 'Karen', value: '808', message: 'New feature' } },
131+
{ id: '12', data: { name: 'Leo', value: '909', message: 'Bug fix' } },
132+
{ id: '13', data: { name: 'Mona', value: '1010', message: 'Performance improvement' } },
133+
{ id: '14', data: { name: 'Nina', value: '1111', message: 'Security patch' } },
134+
{ id: '15', data: { name: 'Oscar', value: '1212', message: 'UI enhancement' } },
135+
{ id: '16', data: { name: 'Paul', value: '1313', message: 'Backend update' } },
136+
{ id: '17', data: { name: 'Quinn', value: '1414', message: 'Database migration' } },
137+
{ id: '18', data: { name: 'Rachel', value: '1515', message: 'API change' } },
138+
{ id: '19', data: { name: 'Steve', value: '1616', message: 'Documentation update' } },
139+
{ id: '20', data: { name: 'Tina', value: '1717', message: 'New integration' } },
140+
{ id: '21', data: { name: 'Uma', value: '1818', message: 'Deprecated feature' } },
141+
{ id: '22', data: { name: 'Victor', value: '1919', message: 'Hotfix applied' } },
142+
{ id: '23', data: { name: 'Wendy', value: '2020', message: 'Code refactor' } },
143+
{ id: '24', data: { name: 'Xander', value: '2121', message: 'New dependency' } },
144+
{ id: '25', data: { name: 'Yara', value: '2222', message: 'Improved logging' } },
145+
{ id: '26', data: { name: 'Zane', value: '2323', message: 'Monitoring added' } },
146+
{ id: '27', data: { name: 'Amy', value: '2424', message: 'Analytics update' } },
147+
{ id: '28', data: { name: 'Brian', value: '2525', message: 'Localization added' } },
148+
{ id: '29', data: { name: 'Cathy', value: '2626', message: 'Accessibility fix' } },
149+
{ id: '30', data: { name: 'David', value: '2727', message: 'New dashboard' } },
150+
{ id: '31', data: { name: 'Ella', value: '2828', message: 'Improved UX' } },
151+
{ id: '32', data: { name: 'Fred', value: '2929', message: 'Updated icons' } },
152+
{ id: '33', data: { name: 'Gina', value: '3030', message: 'Enhanced security' } },
153+
{ id: '34', data: { name: 'Harry', value: '3131', message: 'New theme' } },
154+
{ id: '35', data: { name: 'Iris', value: '3232', message: 'Updated dependencies' } },
155+
{ id: '36', data: { name: 'Jake', value: '3333', message: 'Improved performance' } },
156+
{ id: '37', data: { name: 'Kara', value: '3434', message: 'New API endpoint' } },
157+
{ id: '38', data: { name: 'Liam', value: '3535', message: 'Updated README' } },
158+
]
159+
160+
const meta = {
161+
component: Table,
162+
} satisfies Meta<TableProps>
163+
164+
export default meta
165+
166+
type Story = StoryObj<typeof meta>
167+
168+
const BulkActionsComponent = () => (
169+
<>
170+
<Button
171+
icon={<ICPause />}
172+
dataTestId="rb-bulk-action__action-widget--cordon"
173+
component={ButtonComponentType.button}
174+
style={ButtonStyleType.negativeGrey}
175+
variant={ButtonVariantType.borderLess}
176+
ariaLabel="Pause"
177+
size={ComponentSizeType.small}
178+
onClick={() => alert('Pause clicked')}
179+
showAriaLabelInTippy
180+
/>
181+
182+
<Button
183+
icon={<ICPlay />}
184+
dataTestId="rb-bulk-action__action-widget--uncordon"
185+
component={ButtonComponentType.button}
186+
style={ButtonStyleType.neutral}
187+
variant={ButtonVariantType.borderLess}
188+
ariaLabel="Play"
189+
size={ComponentSizeType.small}
190+
onClick={() => alert('Play clicked!')}
191+
showAriaLabelInTippy
192+
/>
193+
</>
194+
)
195+
196+
const ViewWrapper = ({ children, handleSearch, searchKey }: TableViewWrapperProps) => (
197+
<div
198+
style={{ height: '800px' }}
199+
className="w-100 flexbox-col flex-grow-1 bg__primary dc__overflow-hidden dc__gap-16 py-12"
200+
>
201+
<div className="flexbox w-100 dc__align-start px-20">
202+
<SearchBar
203+
handleSearchChange={handleSearch}
204+
initialSearchText={searchKey}
205+
size={ComponentSizeType.medium}
206+
containerClassName="w-300"
207+
/>
208+
</div>
209+
210+
{children}
211+
</div>
212+
)
213+
214+
export const TableTemplate: Story = {
215+
args: {
216+
columns: COLUMNS,
217+
rows: ROWS,
218+
filtersVariant: FiltersTypeEnum.STATE,
219+
id: 'table-story',
220+
paginationVariant: PaginationEnum.PAGINATED,
221+
emptyStateConfig: {
222+
noRowsConfig: {
223+
title: 'No rows to display',
224+
description: 'There are no rows to display.',
225+
},
226+
},
227+
filter: (row, filterData) => {
228+
const lowerCasedSearchKey = filterData.searchKey.toLowerCase()
229+
return (row.data as RowDataType).name.toLowerCase().includes(lowerCasedSearchKey)
230+
},
231+
bulkSelectionConfig: {
232+
BulkActionsComponent,
233+
getSelectAllDialogStatus: () => SelectAllDialogStatus.CLOSED,
234+
onBulkSelectionChanged: () => {},
235+
},
236+
stylesConfig: {
237+
showSeparatorBetweenRows: true,
238+
},
239+
ViewWrapper,
240+
} as TableProps,
241+
}

0 commit comments

Comments
 (0)