Skip to content

Commit ad4f0f9

Browse files
authored
Merge pull request #90 from internxt/feat/grid-generic-component
[PB-3527]: feat/add generic Grid component
2 parents b91c0e4 + 72003ed commit ad4f0f9

6 files changed

Lines changed: 144 additions & 0 deletions

File tree

src/components/grid/Grid.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { ReactNode } from 'react';
2+
3+
export interface GridProps {
4+
children?: ReactNode;
5+
className?: string;
6+
id?: string;
7+
dataCy?: string;
8+
}
9+
10+
/**
11+
* Grid component
12+
*
13+
* A responsive grid container that automatically adjusts columns from 2 (mobile) up to 6 (extra large screens).
14+
*
15+
* @param {GridProps} props - The properties of the component.
16+
*
17+
* @property {ReactNode} [children]
18+
* - The child components or elements to be rendered inside the grid container.
19+
*
20+
* @property {string} [className]
21+
* - Optional custom CSS classes for additional styling or layout adjustments overriding default tailwind classes.
22+
*
23+
* @property {string} [id]
24+
* - Optional ID for the grid container element.
25+
*
26+
* @property {string} [dataCy]
27+
* - Custom data attribute used for e2e Cypress test targeting.
28+
*
29+
* @returns {JSX.Element}
30+
* - A JSX element containing the children formatted inside a grid.
31+
*/
32+
const Grid = ({ children, className = '', id, dataCy }: Readonly<GridProps>): JSX.Element => {
33+
return (
34+
<div
35+
id={id}
36+
data-cy={dataCy}
37+
className={`grid min-w-full auto-rows-min grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 ${className}`}
38+
>
39+
{children}
40+
</div>
41+
);
42+
};
43+
44+
export default Grid;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react';
2+
import { render } from '@testing-library/react';
3+
import { describe, it, expect } from 'vitest';
4+
import '@testing-library/jest-dom';
5+
import { Grid } from '../';
6+
7+
describe('Grid component', () => {
8+
it('should match snapshot', () => {
9+
const { container } = render(<Grid>Snapshot Test</Grid>);
10+
expect(container).toMatchSnapshot();
11+
});
12+
13+
it('should render children correctly', () => {
14+
const { getByText } = render(
15+
<Grid>
16+
<div>Item 1</div>
17+
<div>Item 2</div>
18+
</Grid>
19+
);
20+
21+
expect(getByText('Item 1')).toBeInTheDocument();
22+
expect(getByText('Item 2')).toBeInTheDocument();
23+
});
24+
25+
it('should apply the default grid classes', () => {
26+
const { container } = render(<Grid>Test</Grid>);
27+
const gridDiv = container.firstChild as HTMLElement;
28+
29+
expect(gridDiv).toHaveClass('grid');
30+
expect(gridDiv).toHaveClass('min-w-full');
31+
expect(gridDiv).toHaveClass('auto-rows-min');
32+
expect(gridDiv).toHaveClass('grid-cols-2');
33+
});
34+
35+
it('should append custom className to the wrapper', () => {
36+
const { container } = render(<Grid className="custom-class">Test</Grid>);
37+
const gridDiv = container.firstChild as HTMLElement;
38+
39+
expect(gridDiv).toHaveClass('custom-class');
40+
expect(gridDiv).toHaveClass('grid');
41+
});
42+
43+
it('should assign id and data-cy correctly', () => {
44+
const { container } = render(<Grid id="grid-id" dataCy="grid-data-cy">Test</Grid>);
45+
const gridDiv = container.firstChild as HTMLElement;
46+
47+
expect(gridDiv).toHaveAttribute('id', 'grid-id');
48+
expect(gridDiv).toHaveAttribute('data-cy', 'grid-data-cy');
49+
});
50+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`Grid component > should match snapshot 1`] = `
4+
<div>
5+
<div
6+
class="grid min-w-full auto-rows-min grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 "
7+
>
8+
Snapshot Test
9+
</div>
10+
</div>
11+
`;

src/components/grid/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default as Grid } from './Grid';
2+
export * from './Grid';

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ export * from './textArea';
2929
export * from './tooltip';
3030
export * from './sidenav';
3131
export * from './mail';
32+
export * from './grid';
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Meta, StoryObj } from '@storybook/react-vite';
2+
import { Grid } from '@/components/grid';
3+
4+
const meta: Meta<typeof Grid> = {
5+
title: 'Components/Grid',
6+
component: Grid,
7+
parameters: {
8+
layout: 'padded',
9+
},
10+
tags: ['autodocs'],
11+
argTypes: {
12+
className: {
13+
control: 'text',
14+
description: 'Custom CSS classes for the Grid container',
15+
},
16+
},
17+
};
18+
19+
export default meta;
20+
21+
type Story = StoryObj<typeof Grid>;
22+
23+
export const Default: Story = {
24+
render: (args) => (
25+
<Grid {...args}>
26+
{Array.from({ length: 12 }).map((_, index) => (
27+
<div
28+
key={index}
29+
className="flex items-center justify-center rounded-lg bg-gray-10 p-6 text-sm font-medium text-gray-80 shadow-sm"
30+
>
31+
Grid Item {index + 1}
32+
</div>
33+
))}
34+
</Grid>
35+
),
36+
};

0 commit comments

Comments
 (0)