Skip to content

Commit ef9558b

Browse files
authored
Merge pull request #50 from askides/feat/alias
feat: add support for the 'as' property
2 parents b063368 + 9f0d88f commit ef9558b

4 files changed

Lines changed: 192 additions & 93 deletions

File tree

libs/react-plock/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,11 @@
3030
},
3131
"peerDependencies": {
3232
"react": "^18.2.0"
33+
},
34+
"devDependencies": {
35+
"@testing-library/dom": "^10.4.0",
36+
"@testing-library/react": "^16.1.0",
37+
"@types/react": "^19.0.1",
38+
"@types/react-dom": "^19.0.2"
3339
}
3440
}

libs/react-plock/src/index.spec.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { describe, it, expect } from 'vitest';
22
import { createChunks, createDataColumns } from '.';
3+
import { render } from '@testing-library/react';
4+
import { Masonry } from '.';
35

46
describe('Plock', () => {
57
it('should create chunks', () => {
@@ -76,3 +78,61 @@ describe('Plock', () => {
7678
]);
7779
});
7880
});
81+
82+
describe('Integration Tests', () => {
83+
it('should render with different HTML elements using "as" prop', () => {
84+
const items = [1, 2, 3];
85+
const config = { columns: 3, gap: 10 };
86+
87+
// Test with 'section' element
88+
const { container: sectionContainer } = render(
89+
<Masonry
90+
items={items}
91+
render={(item) => <div key={item}>{item}</div>}
92+
config={config}
93+
as="section"
94+
/>
95+
);
96+
97+
// Test with 'article' element
98+
const { container: articleContainer } = render(
99+
<Masonry
100+
items={items}
101+
render={(item) => <div key={item}>{item}</div>}
102+
config={config}
103+
as="article"
104+
/>
105+
);
106+
107+
expect(sectionContainer.querySelector('section')).toBeTruthy();
108+
expect(articleContainer.querySelector('article')).toBeTruthy();
109+
});
110+
111+
it('should render with custom React component using "as" prop', () => {
112+
const CustomComponent = ({
113+
children,
114+
className,
115+
}: {
116+
children: React.ReactNode;
117+
className?: string;
118+
}) => <div className={`custom-wrapper ${className || ''}`}>{children}</div>;
119+
120+
const items = [1, 2, 3];
121+
const config = { columns: 3, gap: 10 };
122+
123+
const { container } = render(
124+
<Masonry
125+
items={items}
126+
render={(item) => <div key={item}>{item}</div>}
127+
config={config}
128+
as={CustomComponent}
129+
className="test-class"
130+
/>
131+
);
132+
133+
const customElement = container.querySelector('.custom-wrapper');
134+
135+
expect(customElement).toBeTruthy();
136+
expect(customElement?.classList.contains('test-class')).toBeTruthy();
137+
});
138+
});

libs/react-plock/src/index.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useState } from "react";
1+
import React, { useEffect, useState } from 'react';
22

33
export function useMediaValues(
44
medias: number[] | undefined,
@@ -36,27 +36,28 @@ export function useMediaValues(
3636

3737
// Apply Listeners
3838
for (const mediaQuery of mediaQueries) {
39-
mediaQuery.addEventListener("change", onSizeChange);
39+
mediaQuery.addEventListener('change', onSizeChange);
4040
}
4141

4242
return () => {
4343
for (const mediaQuery of mediaQueries) {
44-
mediaQuery.removeEventListener("change", onSizeChange);
44+
mediaQuery.removeEventListener('change', onSizeChange);
4545
}
4646
};
4747
}, [values.columns, values.gap]);
4848

4949
return values;
5050
}
5151

52-
export type MasonryProps<T> = React.ComponentPropsWithoutRef<"div"> & {
52+
export type MasonryProps<T> = React.ComponentPropsWithoutRef<'div'> & {
5353
items: T[];
5454
render: (item: T, idx: number) => React.ReactNode;
5555
config: {
5656
columns: number | number[];
5757
gap: number | number[];
5858
media?: number[];
5959
};
60+
as?: React.ElementType;
6061
};
6162

6263
export function createSafeArray(data: number | number[]) {
@@ -67,6 +68,7 @@ export function Masonry<T>({
6768
items = [],
6869
render,
6970
config,
71+
as: Component = 'div',
7072
...rest
7173
}: MasonryProps<T>) {
7274
const { columns, gap } = useMediaValues(
@@ -81,11 +83,11 @@ export function Masonry<T>({
8183
const dataColumns = createDataColumns<T>(chunks, columns);
8284

8385
return (
84-
<div
86+
<Component
8587
{...rest}
8688
style={{
87-
display: "grid",
88-
alignItems: "start",
89+
display: 'grid',
90+
alignItems: 'start',
8991
gridColumnGap: gap,
9092
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
9193
}}
@@ -95,7 +97,7 @@ export function Masonry<T>({
9597
{column.map((item, idx) => render(item, idx))}
9698
</MasonryRow>
9799
))}
98-
</div>
100+
</Component>
99101
);
100102
}
101103

@@ -109,9 +111,9 @@ export function MasonryRow({
109111
return (
110112
<div
111113
style={{
112-
display: "grid",
114+
display: 'grid',
113115
rowGap: gap,
114-
gridTemplateColumns: "minmax(0, 1fr)",
116+
gridTemplateColumns: 'minmax(0, 1fr)',
115117
}}
116118
>
117119
{children}

0 commit comments

Comments
 (0)