@sproutsocial/seeds-react-list 0.0.2 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +13 -0
- package/dist/esm/index.js +1053 -38
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +57 -5
- package/dist/index.d.ts +57 -5
- package/dist/index.js +1059 -41
- package/dist/index.js.map +1 -1
- package/package.json +8 -4
- package/src/DataItemSkeleton.tsx +43 -0
- package/src/DataItemSkeletonTypes.ts +8 -0
- package/src/DataList.stories.tsx +659 -0
- package/src/DataList.tsx +88 -0
- package/src/DataListTypes.ts +31 -0
- package/src/DynamicVirtualizedExample.tsx +85 -0
- package/src/List.stories.tsx +13 -5
- package/src/List.tsx +1 -10
- package/src/ListItem.tsx +22 -19
- package/src/ListItemButton.tsx +17 -5
- package/src/ListItemTypes.ts +2 -0
- package/src/ListTypes.ts +2 -0
- package/src/VirtualizedDataList.stories.tsx +353 -0
- package/src/VirtualizedDataList.tsx +122 -0
- package/src/VirtualizedExamples.tsx +55 -0
- package/src/__tests__/DataList.test.tsx +218 -0
- package/src/__tests__/List.test.tsx +10 -10
- package/src/index.ts +13 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, screen } from "@sproutsocial/seeds-react-testing-library";
|
|
3
|
+
import DataList from "../DataList";
|
|
4
|
+
import DataItemSkeleton from "../DataItemSkeleton";
|
|
5
|
+
|
|
6
|
+
interface TestItem {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const testData: TestItem[] = [
|
|
12
|
+
{ id: "1", name: "First item" },
|
|
13
|
+
{ id: "2", name: "Second item" },
|
|
14
|
+
{ id: "3", name: "Third item" },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
describe("DataList", () => {
|
|
18
|
+
it("should render properly", async () => {
|
|
19
|
+
const { container, runA11yCheck } = render(
|
|
20
|
+
<DataList
|
|
21
|
+
data={testData}
|
|
22
|
+
renderItem={(item) => <div key={item.id}>{item.name}</div>}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
expect(container).toBeTruthy();
|
|
26
|
+
await runA11yCheck();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should render all items from the data array", () => {
|
|
30
|
+
render(
|
|
31
|
+
<DataList
|
|
32
|
+
data={testData}
|
|
33
|
+
renderItem={(item) => <div key={item.id}>{item.name}</div>}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
expect(screen.getByText("First item")).toBeInTheDocument();
|
|
37
|
+
expect(screen.getByText("Second item")).toBeInTheDocument();
|
|
38
|
+
expect(screen.getByText("Third item")).toBeInTheDocument();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should render as ul by default", () => {
|
|
42
|
+
const { container } = render(
|
|
43
|
+
<DataList
|
|
44
|
+
data={testData}
|
|
45
|
+
renderItem={(item) => <div key={item.id}>{item.name}</div>}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
const listElement = container.querySelector("ul");
|
|
49
|
+
expect(listElement).toBeInTheDocument();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should render as ol when as='ol'", () => {
|
|
53
|
+
const { container } = render(
|
|
54
|
+
<DataList
|
|
55
|
+
as="ol"
|
|
56
|
+
data={testData}
|
|
57
|
+
renderItem={(item) => <div key={item.id}>{item.name}</div>}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
const listElement = container.querySelector("ol");
|
|
61
|
+
expect(listElement).toBeInTheDocument();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should wrap each rendered item in an li element", () => {
|
|
65
|
+
const { container } = render(
|
|
66
|
+
<DataList
|
|
67
|
+
data={testData}
|
|
68
|
+
renderItem={(item) => <div key={item.id}>{item.name}</div>}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
const listItems = container.querySelectorAll("li");
|
|
72
|
+
expect(listItems).toHaveLength(3);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should call renderItem for each data item", () => {
|
|
76
|
+
const renderItem = jest.fn((item: TestItem) => (
|
|
77
|
+
<div key={item.id}>{item.name}</div>
|
|
78
|
+
));
|
|
79
|
+
render(<DataList data={testData} renderItem={renderItem} />);
|
|
80
|
+
expect(renderItem).toHaveBeenCalledTimes(3);
|
|
81
|
+
expect(renderItem).toHaveBeenCalledWith(testData[0], 0);
|
|
82
|
+
expect(renderItem).toHaveBeenCalledWith(testData[1], 1);
|
|
83
|
+
expect(renderItem).toHaveBeenCalledWith(testData[2], 2);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should render loading skeletons when isLoading is true", () => {
|
|
87
|
+
const { container } = render(
|
|
88
|
+
<DataList
|
|
89
|
+
data={testData}
|
|
90
|
+
renderItem={(item) => <div key={item.id}>{item.name}</div>}
|
|
91
|
+
isLoading={true}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
94
|
+
// Default loading renders 5 skeleton items
|
|
95
|
+
const listItems = container.querySelectorAll("li");
|
|
96
|
+
expect(listItems).toHaveLength(5);
|
|
97
|
+
// Data items should not be rendered when loading
|
|
98
|
+
expect(screen.queryByText("First item")).not.toBeInTheDocument();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should use custom LoadingComponent when provided", () => {
|
|
102
|
+
const CustomLoader = () => <div>Custom Loading</div>;
|
|
103
|
+
render(
|
|
104
|
+
<DataList
|
|
105
|
+
data={testData}
|
|
106
|
+
renderItem={(item) => <div key={item.id}>{item.name}</div>}
|
|
107
|
+
isLoading={true}
|
|
108
|
+
LoadingComponent={CustomLoader}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
const loaders = screen.getAllByText("Custom Loading");
|
|
112
|
+
expect(loaders).toHaveLength(5);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should pass listItemProps to each list item", () => {
|
|
116
|
+
const { container } = render(
|
|
117
|
+
<DataList
|
|
118
|
+
data={testData}
|
|
119
|
+
renderItem={(item) => <div key={item.id}>{item.name}</div>}
|
|
120
|
+
listItemProps={{ isBordered: true }}
|
|
121
|
+
/>
|
|
122
|
+
);
|
|
123
|
+
const listItems = container.querySelectorAll("li");
|
|
124
|
+
listItems.forEach((item) => {
|
|
125
|
+
expect(item).toBeInTheDocument();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("should render Header when provided", () => {
|
|
130
|
+
const Header = <div data-testid="header">Custom Header</div>;
|
|
131
|
+
render(
|
|
132
|
+
<DataList
|
|
133
|
+
data={testData}
|
|
134
|
+
renderItem={(item) => <div key={item.id}>{item.name}</div>}
|
|
135
|
+
Header={Header}
|
|
136
|
+
/>
|
|
137
|
+
);
|
|
138
|
+
expect(screen.getByTestId("header")).toBeInTheDocument();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("should render Header with sticky positioning", () => {
|
|
142
|
+
const Header = <div data-testid="header">Custom Header</div>;
|
|
143
|
+
const { container } = render(
|
|
144
|
+
<DataList
|
|
145
|
+
data={testData}
|
|
146
|
+
renderItem={(item) => <div key={item.id}>{item.name}</div>}
|
|
147
|
+
Header={Header}
|
|
148
|
+
/>
|
|
149
|
+
);
|
|
150
|
+
const headerElement = container.querySelector("#header");
|
|
151
|
+
expect(headerElement).toBeInTheDocument();
|
|
152
|
+
expect(headerElement).toHaveStyle({ position: "sticky" });
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("should render empty list when data array is empty", () => {
|
|
156
|
+
const { container } = render(
|
|
157
|
+
<DataList
|
|
158
|
+
data={[]}
|
|
159
|
+
renderItem={(item: TestItem) => <div key={item.id}>{item.name}</div>}
|
|
160
|
+
/>
|
|
161
|
+
);
|
|
162
|
+
const listElement = container.querySelector("ul");
|
|
163
|
+
expect(listElement).toBeInTheDocument();
|
|
164
|
+
const listItems = container.querySelectorAll("li");
|
|
165
|
+
expect(listItems).toHaveLength(0);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should render Header during loading state", () => {
|
|
169
|
+
const Header = <div data-testid="header">Custom Header</div>;
|
|
170
|
+
render(
|
|
171
|
+
<DataList
|
|
172
|
+
data={testData}
|
|
173
|
+
renderItem={(item) => <div key={item.id}>{item.name}</div>}
|
|
174
|
+
isLoading={true}
|
|
175
|
+
Header={Header}
|
|
176
|
+
/>
|
|
177
|
+
);
|
|
178
|
+
expect(screen.getByTestId("header")).toBeInTheDocument();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("should handle complex data items", () => {
|
|
182
|
+
interface ComplexItem {
|
|
183
|
+
id: string;
|
|
184
|
+
user: { name: string; email: string };
|
|
185
|
+
metadata: { count: number };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const complexData: ComplexItem[] = [
|
|
189
|
+
{
|
|
190
|
+
id: "1",
|
|
191
|
+
user: { name: "John", email: "john@example.com" },
|
|
192
|
+
metadata: { count: 5 },
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: "2",
|
|
196
|
+
user: { name: "Jane", email: "jane@example.com" },
|
|
197
|
+
metadata: { count: 10 },
|
|
198
|
+
},
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
render(
|
|
202
|
+
<DataList
|
|
203
|
+
data={complexData}
|
|
204
|
+
renderItem={(item) => (
|
|
205
|
+
<div key={item.id}>
|
|
206
|
+
<span>{item.user.name}</span>
|
|
207
|
+
<span>{item.user.email}</span>
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
210
|
+
/>
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
expect(screen.getByText("John")).toBeInTheDocument();
|
|
214
|
+
expect(screen.getByText("john@example.com")).toBeInTheDocument();
|
|
215
|
+
expect(screen.getByText("Jane")).toBeInTheDocument();
|
|
216
|
+
expect(screen.getByText("jane@example.com")).toBeInTheDocument();
|
|
217
|
+
});
|
|
218
|
+
});
|
|
@@ -6,8 +6,8 @@ describe("List", () => {
|
|
|
6
6
|
it("should render properly", async () => {
|
|
7
7
|
const { container, runA11yCheck } = render(
|
|
8
8
|
<List as="ul">
|
|
9
|
-
<
|
|
10
|
-
<
|
|
9
|
+
<li>Item 1</li>
|
|
10
|
+
<li>Item 2</li>
|
|
11
11
|
</List>
|
|
12
12
|
);
|
|
13
13
|
expect(container).toBeTruthy();
|
|
@@ -17,7 +17,7 @@ describe("List", () => {
|
|
|
17
17
|
it("should render as ul when as='ul'", () => {
|
|
18
18
|
const { container } = render(
|
|
19
19
|
<List as="ul">
|
|
20
|
-
<
|
|
20
|
+
<li>Item 1</li>
|
|
21
21
|
</List>
|
|
22
22
|
);
|
|
23
23
|
const listElement = container.querySelector("ul");
|
|
@@ -27,7 +27,7 @@ describe("List", () => {
|
|
|
27
27
|
it("should render as ol when as='ol'", () => {
|
|
28
28
|
const { container } = render(
|
|
29
29
|
<List as="ol">
|
|
30
|
-
<
|
|
30
|
+
<li>Item 1</li>
|
|
31
31
|
</List>
|
|
32
32
|
);
|
|
33
33
|
const listElement = container.querySelector("ol");
|
|
@@ -37,9 +37,9 @@ describe("List", () => {
|
|
|
37
37
|
it("should wrap each child in an li element", () => {
|
|
38
38
|
const { container } = render(
|
|
39
39
|
<List as="ul">
|
|
40
|
-
<
|
|
41
|
-
<
|
|
42
|
-
<
|
|
40
|
+
<li>Item 1</li>
|
|
41
|
+
<li>Item 2</li>
|
|
42
|
+
<li>Item 3</li>
|
|
43
43
|
</List>
|
|
44
44
|
);
|
|
45
45
|
const listItems = container.querySelectorAll("li");
|
|
@@ -52,9 +52,9 @@ describe("List", () => {
|
|
|
52
52
|
it("should add mt={300} to all items except the first", () => {
|
|
53
53
|
const { container } = render(
|
|
54
54
|
<List as="ul">
|
|
55
|
-
<
|
|
56
|
-
<
|
|
57
|
-
<
|
|
55
|
+
<li>Item 1</li>
|
|
56
|
+
<li>Item 2</li>
|
|
57
|
+
<li>Item 3</li>
|
|
58
58
|
</List>
|
|
59
59
|
);
|
|
60
60
|
const listItems = container.querySelectorAll("li");
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import List from "./List";
|
|
2
2
|
import ListItem from "./ListItem";
|
|
3
3
|
import ListItemContent from "./ListItemContent";
|
|
4
|
+
import DataList from "./DataList";
|
|
5
|
+
import DataItemSkeleton from "./DataItemSkeleton";
|
|
6
|
+
import Experimental_VirtualizedDataList from "./VirtualizedDataList";
|
|
4
7
|
|
|
5
|
-
export {
|
|
8
|
+
export {
|
|
9
|
+
List,
|
|
10
|
+
ListItem,
|
|
11
|
+
ListItemContent,
|
|
12
|
+
DataList,
|
|
13
|
+
DataItemSkeleton,
|
|
14
|
+
Experimental_VirtualizedDataList,
|
|
15
|
+
};
|
|
6
16
|
export * from "./ListTypes";
|
|
7
17
|
export * from "./ListItemTypes";
|
|
18
|
+
export * from "./DataListTypes";
|
|
19
|
+
export * from "./DataItemSkeletonTypes";
|