Skip to content

Commit fa4ee2f

Browse files
authored
Add "Show All" button to load all items at once (#762)
* feat(loadMore): add "Show All" button to load all items at once Introduce an optional "Show All" button to the LoadMore component, allowing users to load all remaining items in one action. This feature enhances user experience by providing more flexibility in how items are loaded. The button is fully customizable, supporting additional props and labels. Update documentation and stories to reflect the new functionality and provide examples of usage. * bump version * formatting fix * misc fixes * refactor * better typing
1 parent a9ea6ec commit fa4ee2f

File tree

8 files changed

+240
-41
lines changed

8 files changed

+240
-41
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@nordcloud/gnui",
33
"description": "Nordcloud Design System - a collection of reusable React components used in Nordcloud's SaaS products",
4-
"version": "11.2.3",
4+
"version": "11.2.4",
55
"license": "MIT",
66
"repository": {
77
"type": "git",

src/components/loadMore/LoadMore.mdx

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ The `LoadMore` component provides a "Show More" button that allows users to load
1111
## Features
1212

1313
- "Show More" button to load additional items when clicked
14+
- Optional "Show All" button to load all remaining items at once
1415
- Optional count indicator showing current items and total (e.g., "1-5 of 15")
1516
- "Show Less" button appears when all items are loaded (if onLoadLess prop is provided)
1617
- Supports a loading state to disable the button during data fetching
1718
- Automatically hides the "Show More" button when all items are loaded
1819
- Option to hide the count indicator with centered button layout
20+
- Fully customizable buttons (labels, styles, and other props)
1921

2022
## Basic Usage
2123

@@ -36,6 +38,13 @@ const MyList = () => {
3638
setIsLoading(false);
3739
};
3840

41+
const handleShowAll = () => {
42+
setIsLoading(true);
43+
// Fetch all remaining items, then:
44+
setItems([...items, ...remainingItems]);
45+
setIsLoading(false);
46+
};
47+
3948
const handleLoadLess = () => {
4049
// Reduce items to initial batch size
4150
setItems(items.slice(0, batchSize));
@@ -53,8 +62,11 @@ const MyList = () => {
5362
currentCount={items.length}
5463
total={totalItems}
5564
onLoadMore={handleLoadMore}
65+
onShowAll={handleShowAll}
5666
onLoadLess={handleLoadLess}
5767
isLoading={isLoading}
68+
showMoreLabel="Load Next Page"
69+
showAllButtonProps={{ status: "accent" }}
5870
/>
5971
</div>
6072
);
@@ -79,6 +91,14 @@ Shows both the count indicator and "Show More" button:
7991
<Story id="components-loadmore--with-count" />
8092
</Canvas>
8193

94+
### With Show All
95+
96+
Shows the "Show All" button next to the "Show More" button.
97+
98+
<Canvas>
99+
<Story id="components-loadmore--with-show-all" />
100+
</Canvas>
101+
82102
### Mid-List Example
83103

84104
Shows the state when some items are loaded:
@@ -113,12 +133,22 @@ A complete example showing the component in use with a list:
113133

114134
### With Show Less
115135

116-
Shows how the "Show Less" button appears when all items are loaded and onLoadLess is provided:
136+
Shows how the "Show Less" button appears when all items are loaded and
137+
onLoadLess is provided:
117138

118139
<Canvas>
119140
<Story id="components-loadmore--with-show-less" />
120141
</Canvas>
121142

143+
### With List and Show All
144+
145+
A complete example showing the component in use with a list and the "Show All"
146+
button.
147+
148+
<Canvas>
149+
<Story id="components-loadmore--with-list-and-show-all" />
150+
</Canvas>
151+
122152
## Props
123153

124154
<ArgsTable of={LoadMore} />
@@ -127,7 +157,8 @@ Shows how the "Show Less" button appears when all items are loaded and onLoadLes
127157

128158
The component follows accessibility best practices:
129159

130-
- The "Show More" button is properly labeled for screen readers
160+
- The "Show More" and "Show All" buttons are properly labeled for screen
161+
readers
131162
- The button is disabled during loading states to prevent multiple clicks
132163
- The count indicator (when shown) provides context about the loaded items
133164

@@ -145,21 +176,46 @@ The component follows accessibility best practices:
145176
<LoadMore currentCount={5} total={15} onLoadMore={handleLoadMore} />
146177
```
147178

148-
3. **With Show Less**
179+
3. **With Show All**
180+
181+
```jsx
182+
<LoadMore currentCount={5} total={15} onLoadMore={handleLoadMore} onShowAll={handleShowAll} />
183+
```
184+
185+
4. **With Custom Buttons and Labels**
186+
187+
```jsx
188+
<LoadMore
189+
currentCount={5}
190+
total={15}
191+
onLoadMore={handleLoadMore}
192+
onShowAll={handleShowAll}
193+
showMoreLabel="Gimme more"
194+
showMoreButtonProps={{ severity: "low" }}
195+
showAllButtonProps={{ disabled: true }}
196+
/>
197+
```
198+
199+
5. **With Show Less**
149200

150201
```jsx
151202
<LoadMore currentCount={15} total={15} onLoadMore={handleLoadMore} onLoadLess={handleLoadLess} />
152203
```
153204

154-
4. **With Loading State**
205+
6. **With Loading State**
155206
```jsx
156207
<LoadMore currentCount={5} total={15} onLoadMore={handleLoadMore} isLoading={true} />
157208
```
158209

159210
## Design Considerations
160211

161-
- The count indicator uses the same styling as other pagination components for consistency
212+
- The count indicator uses the same styling as other pagination components for
213+
consistency
162214
- When `hideCount` is true, the button is automatically centered
163-
- The button uses the link color and underline hover effect for a lightweight appearance
215+
- The button uses the link color and underline hover effect for a lightweight
216+
appearance
164217
- Loading state is visually indicated while maintaining the button's position
165-
- "Show Less" button appears in place of "Show More" when all items are loaded and onLoadLess is provided
218+
- "Show Less" button appears in place of "Show More" when all items are loaded
219+
and onLoadLess is provided
220+
- The "Show All" button appears next to the "Show More" button when the
221+
`onShowAll` prop is provided.

src/components/loadMore/LoadMore.stories.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,28 @@ export const Loading: Story = {
7777
name: "loading",
7878
};
7979

80+
export const WithShowAll: Story = {
81+
render: () => (
82+
<LoadMore
83+
currentCount={5}
84+
total={15}
85+
onLoadMore={() => console.log("Load more clicked")}
86+
onShowAll={() => console.log("Show all clicked")}
87+
/>
88+
),
89+
name: "with-show-all",
90+
};
91+
8092
export const WithList: Story = {
8193
render: () => <LoadMoreList />,
8294
name: "with-list",
8395
};
8496

97+
export const WithListAndShowAll: Story = {
98+
render: () => <LoadMoreList withShowAll />,
99+
name: "with-list-and-show-all",
100+
};
101+
85102
export const WithShowLess: Story = {
86103
render: () => (
87104
<LoadMoreList

src/components/loadMore/LoadMore.tsx

Lines changed: 83 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,60 @@ const Container = styled(StyledPaginationBox)<{ hideCount?: boolean }>`
1212
min-height: 36px;
1313
`;
1414

15-
/**
16-
* LoadMore displays a "Show More" button to load additional items when clicked,
17-
* optionally with a count of loaded items.
18-
*/
15+
const ButtonsContainer = styled.div`
16+
display: flex;
17+
gap: ${theme.spacing.spacing02};
18+
`;
19+
20+
function extractButtonProps(props?: React.ComponentProps<typeof Button>) {
21+
if (!props) {
22+
return { disabled: false, rest: {} };
23+
}
24+
25+
const {
26+
disabled,
27+
children: ignoredChildren,
28+
onClick: ignoredOnClick,
29+
initialState: ignoredInitialState,
30+
...rest
31+
} = props;
32+
33+
return { disabled: !!disabled, rest };
34+
}
35+
1936
export function LoadMore({
2037
currentCount,
2138
total,
2239
onLoadMore,
2340
onLoadLess,
41+
onShowAll,
2442
isLoading = false,
2543
hideCount = false,
2644
className,
45+
showMoreLabel = "Show more",
46+
showAllLabel = "Show all",
47+
showLessLabel = "Show less",
48+
showMoreButtonProps,
49+
showAllButtonProps,
50+
showLessButtonProps,
2751
}: LoadMoreProps) {
2852
const hasMoreItems = currentCount < total;
2953
const showLessButton = onLoadLess && currentCount === total && total > 0;
3054

55+
const { disabled: isMoreDisabled, rest: restShowMoreProps } =
56+
extractButtonProps(showMoreButtonProps);
57+
const { disabled: isAllDisabled, rest: restShowAllProps } =
58+
extractButtonProps(showAllButtonProps);
59+
const { disabled: isLessDisabled, rest: restShowLessProps } =
60+
extractButtonProps(showLessButtonProps);
61+
62+
const baseButtonProps = {
63+
severity: "medium" as const,
64+
size: "sm" as const,
65+
style: { margin: 0 },
66+
initialState: isLoading ? "loading" : undefined,
67+
};
68+
3169
return (
3270
<Container className={className} small={false} hideCount={hideCount}>
3371
{!hideCount && (
@@ -39,34 +77,48 @@ export function LoadMore({
3977
/>
4078
)}
4179

42-
{hasMoreItems && (
43-
<Button
44-
severity="medium"
45-
size="sm"
46-
initialState={isLoading ? "loading" : undefined}
47-
aria-label="Show more items"
48-
disabled={isLoading}
49-
style={{ margin: 0 }}
50-
icon="chevronDown"
51-
onClick={onLoadMore}
52-
>
53-
{isLoading ? "Loading..." : "Show more"}
54-
</Button>
55-
)}
80+
{(hasMoreItems || showLessButton) && (
81+
<ButtonsContainer>
82+
{hasMoreItems && (
83+
<Button
84+
{...baseButtonProps}
85+
status="warning"
86+
icon="chevronDown"
87+
aria-label="Show more items"
88+
disabled={isLoading || isMoreDisabled}
89+
onClick={onLoadMore}
90+
{...restShowMoreProps}
91+
>
92+
{showMoreLabel}
93+
</Button>
94+
)}
95+
96+
{hasMoreItems && onShowAll && (
97+
<Button
98+
{...baseButtonProps}
99+
icon="chevronDown"
100+
aria-label="Show all items"
101+
disabled={isLoading || isAllDisabled}
102+
onClick={onShowAll}
103+
{...restShowAllProps}
104+
>
105+
{showAllLabel}
106+
</Button>
107+
)}
56108

57-
{showLessButton && (
58-
<Button
59-
severity="medium"
60-
size="sm"
61-
aria-label="Show less items"
62-
initialState={isLoading ? "loading" : undefined}
63-
disabled={isLoading}
64-
style={{ margin: 0 }}
65-
icon="chevronUp"
66-
onClick={onLoadLess}
67-
>
68-
{isLoading ? "Loading..." : "Show less"}
69-
</Button>
109+
{showLessButton && (
110+
<Button
111+
{...baseButtonProps}
112+
icon="chevronUp"
113+
aria-label="Show less items"
114+
disabled={isLoading || isLessDisabled}
115+
onClick={onLoadLess}
116+
{...restShowLessProps}
117+
>
118+
{showLessLabel}
119+
</Button>
120+
)}
121+
</ButtonsContainer>
70122
)}
71123
</Container>
72124
);

src/components/loadMore/LoadMoreList.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export function LoadMoreList({
3838
totalItems = 15,
3939
batchSize = 5,
4040
loadingDelay = 1000,
41+
withShowAll = false,
4142
}: LoadMoreListProps) {
4243
const [items, setItems] = React.useState<string[]>(initialItems);
4344
const [isLoading, setIsLoading] = React.useState(false);
@@ -63,6 +64,24 @@ export function LoadMoreList({
6364
}, loadingDelay);
6465
};
6566

67+
const handleShowAll = () => {
68+
setIsLoading(true);
69+
70+
setTimeout(() => {
71+
const itemsLeft = totalItems - items.length;
72+
73+
if (itemsLeft > 0) {
74+
const allNextItems = Array.from(
75+
{ length: itemsLeft },
76+
(_, index) => `Item ${items.length + index + 1}`
77+
);
78+
setItems([...items, ...allNextItems]);
79+
}
80+
81+
setIsLoading(false);
82+
}, loadingDelay);
83+
};
84+
6685
const handleLoadLess = () => {
6786
setItems(items.slice(0, batchSize));
6887
};
@@ -80,6 +99,7 @@ export function LoadMoreList({
8099
total={totalItems}
81100
isLoading={isLoading}
82101
onLoadMore={handleLoadMore}
102+
onShowAll={withShowAll ? handleShowAll : undefined}
83103
onLoadLess={items.length > batchSize ? handleLoadLess : undefined}
84104
/>
85105
</Container>

src/components/loadMore/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export { LoadMore } from "./LoadMore";
22
export { LoadMoreList } from "./LoadMoreList";
3-
export * from "./types";
3+
export type * from "./types";

0 commit comments

Comments
 (0)