Skip to content

Commit

Permalink
Feat/table (#34)
Browse files Browse the repository at this point in the history
* test table

* implement custom table

* add test

* remove import react

* remove import
  • Loading branch information
mehdi-torabiv authored Jul 15, 2024
1 parent f966c24 commit 88a28be
Show file tree
Hide file tree
Showing 12 changed files with 244 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,7 @@ module.exports = {
"import/order": 'warn',
"import/extensions": 'warn',
'@typescript-eslint/no-exp': 'off',
"react/no-array": 'off',
"react/function-component-definition": "off"
},
};
2 changes: 1 addition & 1 deletion lint-staged.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export default {
"**/*.{js,jsx,ts,tsx}": ["eslint --fix"],
// "**/*.{js,jsx,ts,tsx}": ["eslint --fix"],
"**/*.{md,json}": ["prettier --write"],
"**/*.{css,scss}": ["prettier --write"],
};
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
"@fontsource/roboto": "^5.0.13",
"@mui/icons-material": "^5.16.0",
"@mui/material": "^5.16.0",
"@react-icons/all-files": "^4.1.0",
"@tanstack/react-query": "^5.50.1",
"@tanstack/react-query-devtools": "^5.50.1",
"axios": "^1.7.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.52.1",
"react-icons": "^5.2.1",
"react-router-dom": "^6.24.1",
"zod": "^3.23.8",
"zustand": "^4.5.4"
Expand Down
37 changes: 37 additions & 0 deletions src/components/shared/AccessControlButton.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// AccessControlButton.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import { vi } from 'vitest';
import AccessControlButton from './AccessControlButton';

describe('AccessControlButton', () => {
it('renders with "Grant Access" when hasAccess is false', () => {
const handleToggleAccess = vi.fn();
render(<AccessControlButton hasAccess={false} onToggleAccess={handleToggleAccess} />);

const button = screen.getByRole('button', { name: /grant access/i });
expect(button).toBeInTheDocument();
expect(button).toHaveTextContent('Grant Access');
expect(button).toHaveClass('MuiButton-containedPrimary');
});

it('renders with "Revoke Access" when hasAccess is true', () => {
const handleToggleAccess = vi.fn();
render(<AccessControlButton hasAccess={true} onToggleAccess={handleToggleAccess} />);

const button = screen.getByRole('button', { name: /revoke access/i });
expect(button).toBeInTheDocument();
expect(button).toHaveTextContent('Revoke Access');
expect(button).toHaveClass('MuiButton-outlinedError');
});

it('calls onToggleAccess when button is clicked', () => {
const handleToggleAccess = vi.fn();
render(<AccessControlButton hasAccess={false} onToggleAccess={handleToggleAccess} />);

const button = screen.getByRole('button', { name: /grant access/i });
fireEvent.click(button);

expect(handleToggleAccess).toHaveBeenCalledTimes(1);
});
});
21 changes: 21 additions & 0 deletions src/components/shared/AccessControlButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Button } from '@mui/material';

interface AccessControlButtonProps {
hasAccess: boolean;
onToggleAccess: () => void;
}

const AccessControlButton: React.FC<AccessControlButtonProps> = ({ hasAccess, onToggleAccess }) => {
return (
<Button
variant={hasAccess ? 'outlined' : 'contained'}
size='small'
color={hasAccess ? 'error' : 'primary'}
onClick={onToggleAccess}
>
{hasAccess ? 'Revoke Access' : 'Grant Access'}
</Button>
);
};

export default AccessControlButton;
40 changes: 40 additions & 0 deletions src/components/shared/CustomTable.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// CustomTable.test.tsx
import { render, screen } from '@testing-library/react';
import { vi } from 'vitest';
import CustomTable, { Platform, AccessData } from './CustomTable';

// Mock AccessControlButton component
vi.mock('./AccessControlButton', () => ({
__esModule: true,
default: ({ hasAccess, onToggleAccess }: { hasAccess: boolean, onToggleAccess: () => void }) => (
<button onClick={onToggleAccess}>
{hasAccess ? 'Revoke Access' : 'Grant Access'}
</button>
),
}));

describe('CustomTable', () => {
const platforms: Platform[] = [
{ name: 'Platform1', icon: <div>Icon1</div> },
{ name: 'Platform2', icon: <div>Icon2</div> },
];

const data: AccessData[] = [
{ application: 'App1', Platform1: true, Platform2: false },
{ application: 'App2', Platform1: false, Platform2: true },
];

it('renders the table with correct headers and data', () => {
render(<CustomTable xcolumns={platforms} ycolumns={data} data={data} />);

expect(screen.getByText('Applications \\ Identifiers')).toBeInTheDocument();
expect(screen.getByText('Platform1')).toBeInTheDocument();
expect(screen.getByText('Platform2')).toBeInTheDocument();

expect(screen.getByText('App1')).toBeInTheDocument();
expect(screen.getByText('App2')).toBeInTheDocument();

expect(screen.getAllByText('Revoke Access').length).toBe(2);
expect(screen.getAllByText('Grant Access').length).toBe(2);
});
});
82 changes: 82 additions & 0 deletions src/components/shared/CustomTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { TableContainer, Table, TableHead, TableRow, TableCell, TableBody, Avatar, Typography, Card } from '@mui/material';
import AccessControlButton from './AccessControlButton';

export interface Platform {
name: string;
icon: React.ReactNode;
}

export interface AccessData {
application: string;
[platform: string]: boolean | string;
}

export interface Column<T> {
field: keyof T;
headerName?: string;
headerComponent?: React.ReactNode;
renderCell?: (value: any, row: T) => React.ReactNode;
}

export interface CustomTableProps<T> {
xcolumns: Platform[];
ycolumns: T[];
data: T[];
}

const CustomTable: React.FC<CustomTableProps<AccessData>> = ({ xcolumns, ycolumns, data }) => {
const handleToggleAccess = (rowIndex: number, platform: string) => {
// Implement the logic to toggle access
console.log(`Toggle access for row ${rowIndex}, platform ${platform}`);
};

return (
<TableContainer>
<Table>
<TableHead>
<TableRow component={Card}>
<TableCell sx={{ padding: 1 }} align="center">
<Typography fontWeight="bold">
Applications \ Identifiers
</Typography>
</TableCell>
{xcolumns.map((platform, index) => (
<TableCell key={index} align="center" sx={{ padding: 1 }}>
<div className="flex flex-row space-x-1.5 items-center justify-center">
<Avatar>
{platform.icon}
</Avatar>
<Typography>{platform.name}</Typography>
</div>
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{ycolumns.map((application, rowIndex) => (
<TableRow component={Card} key={rowIndex}>
<TableCell align="center" sx={{ padding: 1 }}>
<div className='flex flex-col items-center justify-center text-center mx-auto space-y-2'>
<Avatar />
<Typography>
{application.application}
</Typography>
</div>
</TableCell>
{xcolumns.map((platform, colIndex) => (
<TableCell key={colIndex} align="center">
<AccessControlButton
hasAccess={data[rowIndex][platform.name] as boolean}
onToggleAccess={() => handleToggleAccess(rowIndex, platform.name)}
/>
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
};

export default CustomTable;
13 changes: 10 additions & 3 deletions src/libs/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import SpaceDashboardIcon from '@mui/icons-material/SpaceDashboard';
import FingerprintIcon from '@mui/icons-material/Fingerprint';
import BlockIcon from '@mui/icons-material/Block';
import { MenuItem } from '../interfaces';
import { SiAdguard } from 'react-icons/si';
import { SvgIconComponent } from '@mui/icons-material';
import { IconType } from 'react-icons';

export interface MenuItem {
title: string;
path: string;
icon: SvgIconComponent | IconType;
}

export const DRAWER_WIDTH = 240;

Expand All @@ -19,6 +26,6 @@ export const SIDEBAR_MENU: MenuItem[] = [
{
title: 'Permissions',
path: '/permissions',
icon: BlockIcon,
icon: SiAdguard,
},
];
9 changes: 8 additions & 1 deletion src/pages/Identifiers/Identifiers.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@

export function Identifiers() {
return <div>Identifiers</div>;
return (
<div>
dsajk
</div>
);
}

export default Identifiers;
23 changes: 22 additions & 1 deletion src/pages/Permissions/Permissions.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
import CustomTable, { AccessData, Platform } from '../../components/shared/CustomTable';
import { FaDiscord, FaTelegram, FaGoogle } from 'react-icons/fa';

const xcolumns: Platform[] = [
{ name: 'Discord', icon: <FaDiscord /> },
{ name: 'Telegram', icon: <FaTelegram /> },
{ name: 'Google', icon: <FaGoogle /> },
];

const ycolumns: AccessData[] = [
{ application: 'TogetherCrew', Discord: true, Telegram: false, Google: true },
{ application: 'MeetWith', Discord: true, Telegram: true, Google: false },
{ application: 'Wallet', Discord: false, Telegram: true, Google: true },
];

const data: AccessData[] = ycolumns;

export function Permissions() {
return <div>Permissions</div>;
return (
<div>
<CustomTable xcolumns={xcolumns} ycolumns={ycolumns} data={data} />
</div>
);
}
1 change: 1 addition & 0 deletions src/setupTests.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '@testing-library/jest-dom';
import * as matchers from '@testing-library/jest-dom/matchers';
import { expect } from 'vitest';

Expand Down

0 comments on commit 88a28be

Please sign in to comment.