Skip to content

Commit 2f6a6b6

Browse files
Fix: Optional props
1 parent d9825ee commit 2f6a6b6

17 files changed

+180
-159
lines changed

README.md

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# React Multi Level Table
22

3+
<div align="center">
4+
<!-- Screenshot of the table component in action -->
5+
<img src="./src/assets/table-image.png" alt="Multi Level Table Component Example" width="800" height="400"/>
6+
</div>
7+
38
## Table of Contents
49

510
- [Overview](#overview)
@@ -144,33 +149,33 @@ The MultiLevelTable component accepts the following props:
144149
| selectable | boolean | No | false | Enable/disable row selection |
145150

146151
#### State Props
147-
| Prop | Type | Required | Description |
148-
|------|------|----------|-------------|
149-
| selectionState | SelectionState | Yes | Current selection state with selected rows and all selected flag |
150-
| searchTerm | string | Yes | Current search term for filtering data |
151-
| selectedFilterValues | Set<string \| number> | Yes | Currently selected filter values |
152-
| deletePopup | object | Yes | Delete confirmation popup state (isOpen, itemId, itemName) |
153-
| bulkDeletePopup | object | Yes | Bulk delete confirmation popup state (isOpen, selectedCount) |
154-
| openDropdowns | Set<string> | Yes | Set of currently open dropdown IDs |
155-
| expandedRows | Set<string \| number> | Yes | Set of currently expanded row IDs |
152+
| Prop | Type | Required | Default | Description |
153+
|------|------|----------|---------|-------------|
154+
| selectionState | SelectionState | No | { selectedRows: new Set(), isAllSelected: false } | Current selection state with selected rows and all selected flag |
155+
| searchTerm | string | No | '' | Current search term for filtering data |
156+
| selectedFilterValues | Set<string \| number> | No | new Set() | Currently selected filter values |
157+
| deletePopup | object | No | { isOpen: false, itemId: null, itemName: '' } | Delete confirmation popup state (isOpen, itemId, itemName) |
158+
| bulkDeletePopup | object | No | { isOpen: false, selectedCount: 0 } | Bulk delete confirmation popup state (isOpen, selectedCount) |
159+
| openDropdowns | Set<string> | No | new Set() | Set of currently open dropdown IDs |
160+
| expandedRows | Set<string \| number> | No | new Set() | Set of currently expanded row IDs |
156161

157162
#### Handler Props
158-
| Prop | Type | Description |
159-
|------|------|-------------|
160-
| onSearchChange | (searchTerm: string) => void | Updates the search term for filtering data |
161-
| onFilterChange | (values: Set<string \| number>) => void | Updates the selected filter values |
162-
| onDeleteClick | (itemId: string \| number, itemName: string) => void | Handles delete button click for a specific row |
163-
| onDeleteConfirm | () => void | Confirms the delete action for the selected row |
164-
| onDeleteCancel | () => void | Cancels the delete action and closes popup |
165-
| onBulkDeleteClick | () => void | Handles bulk delete button click for selected rows |
166-
| onBulkDeleteConfirm | () => void | Confirms the bulk delete action for selected rows |
167-
| onBulkDeleteCancel | () => void | Cancels the bulk delete action and closes popup |
168-
| onDropdownToggle | (buttonId: string, isOpen: boolean) => void | Toggles dropdown open/close state for action buttons |
169-
| onDropdownClose | (buttonId: string) => void | Closes a specific dropdown by ID |
170-
| onButtonClick | (button: ButtonConfig) => void | Handles click events for action buttons (export, filter, etc.) |
171-
| onSelectAll | () => void | Handles select all checkbox click to select/deselect all rows |
172-
| onRowSelect | (rowId: string \| number) => void | Handles individual row selection checkbox click |
173-
| onRowToggle | (rowId: string \| number) => void | Handles row expand/collapse toggle for nested rows |
163+
| Prop | Type | Required | Default | Description |
164+
|------|------|----------|---------|-------------|
165+
| onSearchChange | (searchTerm: string) => void | No | - | Updates the search term for filtering data |
166+
| onFilterChange | (values: Set<string \| number>) => void | No | - | Updates the selected filter values |
167+
| onDeleteClick | (itemId: string \| number, itemName: string) => void | No | - | Handles delete button click for a specific row |
168+
| onDeleteConfirm | () => void | No | - | Confirms the delete action for the selected row |
169+
| onDeleteCancel | () => void | No | - | Cancels the delete action and closes popup |
170+
| onBulkDeleteClick | () => void | No | - | Handles bulk delete button click for selected rows |
171+
| onBulkDeleteConfirm | () => void | No | - | Confirms the bulk delete action for selected rows |
172+
| onBulkDeleteCancel | () => void | No | - | Cancels the bulk delete action and closes popup |
173+
| onDropdownToggle | (buttonId: string, isOpen: boolean) => void | No | - | Toggles dropdown open/close state for action buttons |
174+
| onDropdownClose | (buttonId: string) => void | No | - | Closes a specific dropdown by ID |
175+
| onButtonClick | (button: ButtonConfig) => void | No | - | Handles click events for action buttons (export, filter, etc.) |
176+
| onSelectAll | () => void | No | - | Handles select all checkbox click to select/deselect all rows |
177+
| onRowSelect | (rowId: string \| number) => void | No | - | Handles individual row selection checkbox click |
178+
| onRowToggle | (rowId: string \| number) => void | No | - | Handles row expand/collapse toggle for nested rows |
174179

175180
#### Additional Props
176181
| Prop | Type | Default | Description |
@@ -356,6 +361,7 @@ interface PaginationProps {
356361
previousPage: () => void; // Go to previous page
357362
setPageSize: (pageSize: number) => void; // Change page size
358363
state: TableStateWithPagination<T>; // Current table state
364+
theme?: ThemeProps; // Optional theme for styling
359365
}
360366
```
361367

example/src/components/DetailRow.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,23 @@ import type { ThemeProps } from '../types/theme';
55
interface DetailRowProps {
66
label: string;
77
value: React.ReactNode;
8-
theme: ThemeProps;
8+
theme?: ThemeProps;
99
}
1010

1111
export const DetailRow: React.FC<DetailRowProps> = ({ label, value, theme }) => (
1212
<div className="detail-row">
1313
<span
1414
className="detail-row-label"
1515
style={{
16-
color: theme.colors?.textColor || '#666666',
16+
color: theme?.colors?.textColor || '#666666',
1717
}}
1818
>
1919
{label}
2020
</span>
2121
<div
2222
className="detail-row-value"
2323
style={{
24-
color: theme.colors?.textColor || '#333333',
24+
color: theme?.colors?.textColor || '#333333',
2525
}}
2626
>
2727
{value}

example/src/components/ExportDropdown.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { colors } from '../styles/style';
44
import '../styles/ExportDropdown.css';
55

66
interface ExportDropdownProps {
7-
onClose: () => void;
7+
onClose?: () => void;
88
handleExportCSV?: () => void;
99
theme?: {
1010
colors?: {
@@ -24,7 +24,7 @@ export const ExportDropdown: React.FC<ExportDropdownProps> = ({
2424
useEffect(() => {
2525
const handleClickOutside = (event: MouseEvent) => {
2626
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node))
27-
onClose();
27+
onClose?.();
2828
};
2929

3030
document.addEventListener('mousedown', handleClickOutside);
@@ -36,7 +36,7 @@ export const ExportDropdown: React.FC<ExportDropdownProps> = ({
3636
if (handleExportCSV)
3737
handleExportCSV();
3838

39-
onClose();
39+
onClose?.();
4040
};
4141

4242
return (

example/src/components/FilterDropdown.tsx

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,23 @@ import '../styles/FilterDropdown.css';
55

66
interface FilterDropdownProps {
77
// State props
8-
tempSelectedValues: Set<string | number>;
9-
selectedCategory: string | null;
10-
categories: Array<{
8+
tempSelectedValues?: Set<string | number>;
9+
selectedCategory?: string | null;
10+
categories?: Array<{
1111
key: string;
1212
title: string;
1313
count: number;
1414
}>;
15-
categoryFilterOptions: FilterOption[];
15+
categoryFilterOptions?: FilterOption[];
1616

1717
// Handler props
18-
onClose: () => void;
19-
onApply: (values: Set<string | number>) => void;
20-
onCancel: () => void;
21-
onReset: () => void;
22-
onCategoryChange: (categoryKey: string) => void;
23-
onSelectAll: () => void;
24-
onOptionChange: (value: string | number) => void;
18+
onClose?: () => void;
19+
onApply?: (values: Set<string | number>) => void;
20+
onCancel?: () => void;
21+
onReset?: () => void;
22+
onCategoryChange?: (categoryKey: string) => void;
23+
onSelectAll?: () => void;
24+
onOptionChange?: (value: string | number) => void;
2525

2626
// Configuration props
2727
title?: string;
@@ -48,10 +48,10 @@ interface FilterDropdownProps {
4848

4949
export const FilterDropdown: React.FC<FilterDropdownProps> = ({
5050
// State props
51-
tempSelectedValues,
52-
selectedCategory,
53-
categories,
54-
categoryFilterOptions,
51+
tempSelectedValues = new Set(),
52+
selectedCategory = null,
53+
categories = [],
54+
categoryFilterOptions = [],
5555

5656
// Handler props
5757
onClose,
@@ -80,7 +80,7 @@ export const FilterDropdown: React.FC<FilterDropdownProps> = ({
8080
useEffect(() => {
8181
const handleClickOutside = (event: MouseEvent) => {
8282
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node))
83-
onClose();
83+
onClose?.();
8484
};
8585

8686
document.addEventListener('mousedown', handleClickOutside);
@@ -91,17 +91,17 @@ export const FilterDropdown: React.FC<FilterDropdownProps> = ({
9191
}, [onClose]);
9292

9393
const handleApply = () => {
94-
onApply(tempSelectedValues);
95-
onClose();
94+
onApply?.(tempSelectedValues);
95+
onClose?.();
9696
};
9797

9898
const handleCancel = () => {
99-
onCancel();
100-
onClose();
99+
onCancel?.();
100+
onClose?.();
101101
};
102102

103103
const handleResetFilters = () => {
104-
onReset();
104+
onReset?.();
105105
};
106106

107107
return (
@@ -169,7 +169,7 @@ export const FilterDropdown: React.FC<FilterDropdownProps> = ({
169169
<div
170170
key={category.key}
171171
className={`category-item ${isSelected ? 'selected' : ''}`}
172-
onClick={() => onCategoryChange(category.key)}
172+
onClick={() => onCategoryChange?.(category.key)}
173173
style={{
174174
cursor: 'pointer',
175175
backgroundColor: isSelected ? '#F3F3FF' : 'transparent',
@@ -209,7 +209,7 @@ export const FilterDropdown: React.FC<FilterDropdownProps> = ({
209209
ref={(input) => {
210210
if (input) input.indeterminate = isIndeterminate;
211211
}}
212-
onChange={onSelectAll}
212+
onChange={() => onSelectAll?.()}
213213
style={{
214214
borderColor: theme?.table?.filter?.borderColor || '#E5E7EB',
215215
}}
@@ -238,7 +238,7 @@ export const FilterDropdown: React.FC<FilterDropdownProps> = ({
238238
<input
239239
type="checkbox"
240240
checked={isSelected}
241-
onChange={() => onOptionChange(option.value)}
241+
onChange={() => onOptionChange?.(option.value)}
242242
style={{
243243
borderColor: theme?.table?.filter?.borderColor || '#E5E7EB',
244244
}}

example/src/components/Popup.tsx

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@ import '../styles/Popup.css';
66

77
interface PopupButton {
88
text: string;
9-
onClick: () => void;
9+
onClick?: () => void;
1010
variant?: 'primary' | 'secondary';
1111
disabled?: boolean;
1212
}
1313

1414
interface PopupProps {
1515
isOpen: boolean;
16-
onClose: () => void;
17-
icon: React.ComponentType<{ width?: number; height?: number }>;
16+
onClose?: () => void;
17+
icon?: React.ComponentType<{ width?: number; height?: number }>;
1818
title?: string;
19-
text: string;
20-
buttons: PopupButton[];
19+
text?: string;
20+
buttons?: PopupButton[];
2121
theme?: ThemeProps;
2222
}
2323

@@ -27,20 +27,20 @@ export const Popup: React.FC<PopupProps> = ({
2727
icon: Icon,
2828
title,
2929
text,
30-
buttons,
30+
buttons = [],
3131
theme,
3232
}) => {
3333
const popupRef = useRef<HTMLDivElement>(null);
3434

3535
useEffect(() => {
3636
const handleClickOutside = (event: MouseEvent) => {
3737
if (popupRef.current && !popupRef.current.contains(event.target as Node))
38-
onClose();
38+
onClose?.();
3939
};
4040

4141
const handleEscapeKey = (event: KeyboardEvent) => {
4242
if (event.key === 'Escape')
43-
onClose();
43+
onClose?.();
4444
};
4545

4646
if (isOpen) {
@@ -57,7 +57,7 @@ export const Popup: React.FC<PopupProps> = ({
5757
}, [isOpen, onClose]);
5858

5959
const renderIcon = () => {
60-
return <Icon width={48} height={48} />;
60+
return Icon ? <Icon width={48} height={48} /> : null;
6161
};
6262

6363
if (!isOpen) return null;
@@ -84,9 +84,11 @@ export const Popup: React.FC<PopupProps> = ({
8484
</div>
8585

8686
<div className="popup-content">
87-
<p className="popup-text" style={{ color: theme?.colors?.textColor || colors.borderDark }}>
88-
{text}
89-
</p>
87+
{text && (
88+
<p className="popup-text" style={{ color: theme?.colors?.textColor || colors.borderDark }}>
89+
{text}
90+
</p>
91+
)}
9092
</div>
9193

9294
<div className="popup-footer">
@@ -99,11 +101,11 @@ export const Popup: React.FC<PopupProps> = ({
99101
style={{
100102
backgroundColor: button.variant === 'primary'
101103
? (theme?.colors?.primaryColor || colors.primary)
102-
: (theme?.colors?.background || colors.backgroundWhite),
104+
: 'transparent',
103105
color: button.variant === 'primary'
104106
? '#ffffff'
105107
: (theme?.colors?.textColor || colors.borderDark),
106-
borderColor: theme?.colors?.borderColor || colors.borderDark,
108+
borderColor: theme?.colors?.borderColor || colors.borderFilter,
107109
}}
108110
>
109111
{button.text}

0 commit comments

Comments
 (0)