Skip to content

Commit 0f1d09c

Browse files
add pagination and download from modal for cards
1 parent 3f530ff commit 0f1d09c

File tree

15 files changed

+550
-57
lines changed

15 files changed

+550
-57
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
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,6 +1,6 @@
11
{
22
"name": "@undp-data/undp-visualization-library",
3-
"version": "1.0.34",
3+
"version": "1.0.35",
44
"main": "./dist/index.cjs",
55
"module": "./dist/index.js",
66
"browser": "./dist/index.umd.js",

src/Components/Actions/CopyTextButton.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@ interface Props {
88
successMessage?: string;
99
buttonText?: string;
1010
buttonSmall?: boolean;
11-
mode: 'dark' | 'light';
11+
mode?: 'dark' | 'light';
1212
}
1313

1414
export function CopyTextButton(props: Props) {
15-
const { text, successMessage, buttonText, buttonSmall, mode } = props;
15+
const {
16+
text,
17+
successMessage = 'Text copied',
18+
buttonText,
19+
buttonSmall = false,
20+
mode = 'light',
21+
} = props;
1622
const [showMessage, setShowMessage] = useState(false);
1723

1824
const handleShowMessage = () => {
@@ -44,7 +50,7 @@ export function CopyTextButton(props: Props) {
4450
<p
4551
className='undp-viz-typography'
4652
style={{
47-
color: UNDPColorModule[mode || 'light'].grays['gray-700'],
53+
color: UNDPColorModule[mode].grays['gray-700'],
4854
textTransform: 'uppercase',
4955
marginBottom: 0,
5056
fontSize: '0.875rem',
@@ -56,7 +62,7 @@ export function CopyTextButton(props: Props) {
5662
</button>
5763
{showMessage && (
5864
<AutoCloseMessage
59-
message={successMessage || 'Text copied'}
65+
message={successMessage}
6066
duration={2000}
6167
mode={mode}
6268
/>

src/Components/Actions/CsvDownloadButton.tsx

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,31 @@ interface Props {
2222
export function CsvDownloadButton(props: Props) {
2323
const {
2424
buttonContent,
25-
buttonType,
26-
buttonArrow,
25+
buttonType = 'quaternary',
26+
buttonArrow = false,
2727
csvData,
28-
fileName,
28+
fileName = 'data',
2929
headers,
30-
separator,
31-
buttonSmall,
32-
mode,
30+
separator = ',',
31+
buttonSmall = false,
32+
mode = 'light',
3333
} = props;
3434
return (
3535
<CSVLink
3636
headers={headers}
3737
enclosingCharacter=''
38-
separator={separator || ','}
38+
separator={separator}
3939
data={csvData}
40-
filename={`${fileName || 'data'}.csv`}
40+
filename={`${fileName}.csv`}
4141
asyncOnClick
4242
target='_blank'
4343
style={{ backgroundImage: 'none', textDecoration: 'none' }}
4444
aria-label='Click to download the data as csv'
4545
>
4646
<div
47-
className={`undp-viz-download-button undp-viz-button button-${
48-
buttonType || 'quaternary'
49-
}${mode === 'dark' ? ' dark' : ''}${
50-
buttonArrow ? ' button-arrow' : ''
51-
}`}
47+
className={`undp-viz-download-button undp-viz-button button-${buttonType}${
48+
mode === 'dark' ? ' dark' : ''
49+
}${buttonArrow ? ' button-arrow' : ''}`}
5250
style={{
5351
textDecoration: 'none',
5452
padding: buttonSmall ? '0.5rem' : '1rem 1.5rem',

src/Components/Actions/ExcelDownloadButton.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,31 @@ interface Props {
2323
export function ExcelDownloadButton(props: Props) {
2424
const {
2525
buttonContent,
26-
buttonType,
26+
buttonType = 'quaternary',
2727
buttonArrow,
2828
csvData,
29-
fileName,
29+
fileName = 'data',
3030
headers,
3131
xlsxHeader,
3232
wscols,
3333
buttonSmall,
34-
mode,
34+
mode = 'light',
3535
} = props;
3636
return (
3737
<button
3838
type='button'
39-
className={`undp-viz-download-button undp-viz-button button-${
40-
buttonType || 'quaternary'
41-
}${mode === 'dark' ? ' dark' : ''}${buttonArrow ? ' button-arrow' : ''}`}
39+
className={`undp-viz-download-button undp-viz-button button-${buttonType}${
40+
mode === 'dark' ? ' dark' : ''
41+
}${buttonArrow ? ' button-arrow' : ''}`}
4242
style={{
4343
padding: buttonSmall ? '0.5rem' : '1rem 1.5rem',
4444
}}
4545
onClick={() =>
46-
excelDownload(csvData, fileName || 'data', headers, xlsxHeader, wscols)
46+
excelDownload(csvData, fileName, headers, xlsxHeader, wscols)
4747
}
4848
aria-label='Click to download the data as xlsx'
4949
>
50-
{buttonContent || <FileDown mode={mode || 'light'} />}
50+
{buttonContent || <FileDown mode={mode} />}
5151
</button>
5252
);
5353
}

src/Components/Actions/ImageDownloadButton.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,19 @@ interface Props {
1414
export function ImageDownloadButton(props: Props) {
1515
const {
1616
nodeID,
17-
filename,
17+
filename = 'image',
1818
buttonContent,
19-
buttonType,
19+
buttonType = 'quaternary',
2020
buttonArrow,
2121
buttonSmall,
22-
mode,
22+
mode = 'light',
2323
} = props;
2424
return (
2525
<button
2626
type='button'
27-
className={`undp-viz-download-button undp-viz-button button-${
28-
buttonType || 'quaternary'
29-
}${mode === 'dark' ? ' dark' : ''}${buttonArrow ? ' button-arrow' : ''}`}
27+
className={`undp-viz-download-button undp-viz-button button-${buttonType}${
28+
mode === 'dark' ? ' dark' : ''
29+
}${buttonArrow ? ' button-arrow' : ''}`}
3030
style={{
3131
padding: buttonSmall ? '0.5rem' : '1rem 1.5rem',
3232
}}
@@ -35,18 +35,18 @@ export function ImageDownloadButton(props: Props) {
3535
if (document.getElementById(nodeID)) {
3636
imageDownload(
3737
document.getElementById(nodeID) as HTMLElement,
38-
filename || 'image',
38+
filename,
3939
);
4040
} else {
4141
console.error('Cannot find the html element');
4242
}
4343
} else {
44-
imageDownload(nodeID as HTMLElement, filename || 'image');
44+
imageDownload(nodeID as HTMLElement, filename);
4545
}
4646
}}
4747
aria-label='Click to download the graph as image'
4848
>
49-
{buttonContent || <ImageDown mode={mode || 'light'} />}
49+
{buttonContent || <ImageDown mode={mode} />}
5050
</button>
5151
);
5252
}

src/Components/Actions/SVGDownloadButton.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,19 @@ interface Props {
1414
export function SVGDownloadButton(props: Props) {
1515
const {
1616
nodeID,
17-
filename,
17+
filename = 'image',
1818
buttonContent,
19-
buttonType,
19+
buttonType = 'quaternary',
2020
buttonArrow,
2121
buttonSmall,
22-
mode,
22+
mode = 'dark',
2323
} = props;
2424
return (
2525
<button
2626
type='button'
27-
className={`undp-viz-download-button undp-viz-button button-${
28-
buttonType || 'quaternary'
29-
}${mode === 'dark' ? ' dark' : ''}${buttonArrow ? ' button-arrow' : ''}`}
27+
className={`undp-viz-download-button undp-viz-button button-${buttonType}${
28+
mode === 'dark' ? ' dark' : ''
29+
}${buttonArrow ? ' button-arrow' : ''}`}
3030
style={{
3131
padding: buttonSmall ? '0.5rem' : '1rem 1.5rem',
3232
}}
@@ -35,18 +35,18 @@ export function SVGDownloadButton(props: Props) {
3535
if (document.getElementById(nodeID)) {
3636
svgDownload(
3737
document.getElementById(nodeID) as HTMLElement,
38-
filename || 'image',
38+
filename,
3939
);
4040
} else {
4141
console.error('Cannot find the html element');
4242
}
4343
} else {
44-
svgDownload(nodeID as HTMLElement, filename || 'image');
44+
svgDownload(nodeID as HTMLElement, filename);
4545
}
4646
}}
4747
aria-label='Click to download the graph as svg'
4848
>
49-
{buttonContent || <ImageDown mode={mode || 'light'} />}
49+
{buttonContent || <ImageDown mode={mode} />}
5050
</button>
5151
);
5252
}

src/Components/Dashboard/GraphEl.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2846,6 +2846,8 @@ function GraphEl(props: Props) {
28462846
padding: settings?.padding,
28472847
cardBackgroundStyle: settings?.cardBackgroundStyle,
28482848
detailsOnClick: settings?.detailsOnClick,
2849+
allowDataDownloadOnDetail: settings?.allowDataDownloadOnDetail,
2850+
noOfItemsInAPage: settings?.noOfItemsInAPage,
28492851
};
28502852
default:
28512853
return {};
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { useEffect, useState } from 'react';
2+
import {
3+
PaginationUnit,
4+
PaginationContent,
5+
PaginationEllipsis,
6+
PaginationItem,
7+
PaginationLink,
8+
PaginationNext,
9+
PaginationPrevious,
10+
} from './pagination';
11+
12+
interface PaginationProps {
13+
defaultPage?: number;
14+
total: number;
15+
pageSize: number;
16+
onChange: (page: number) => void;
17+
mode?: 'dark' | 'light';
18+
pageNo: number;
19+
}
20+
21+
const getPageNumbers = (currentPageNo: number, totalPages: number) => {
22+
const pages: (number | 'ellipsis')[] = [];
23+
24+
if (totalPages <= 7) {
25+
return Array.from({ length: totalPages }, (_, i) => i + 1);
26+
}
27+
28+
// Always show first page
29+
pages.push(1);
30+
31+
if (currentPageNo <= 3) {
32+
pages.push(2, 3, 4, 'ellipsis');
33+
} else if (currentPageNo >= totalPages - 2) {
34+
pages.push('ellipsis', totalPages - 3, totalPages - 2, totalPages - 1);
35+
} else {
36+
pages.push(
37+
'ellipsis',
38+
currentPageNo - 1,
39+
currentPageNo,
40+
currentPageNo + 1,
41+
'ellipsis',
42+
);
43+
}
44+
45+
// Always show last page
46+
pages.push(totalPages);
47+
48+
return pages;
49+
};
50+
51+
function Pagination(props: PaginationProps) {
52+
const {
53+
defaultPage = 1,
54+
total,
55+
pageSize,
56+
pageNo,
57+
onChange,
58+
mode = 'light',
59+
} = props;
60+
const totalPages = Math.ceil(total / pageSize);
61+
const [currentPage, setCurrentPage] = useState(pageNo || defaultPage);
62+
const [pageNumbers, setPageNumbers] = useState<(number | 'ellipsis')[]>(
63+
getPageNumbers(defaultPage, totalPages),
64+
);
65+
66+
useEffect(() => {
67+
setCurrentPage(pageNo);
68+
});
69+
useEffect(() => {
70+
setPageNumbers(getPageNumbers(currentPage, totalPages));
71+
}, [currentPage, totalPages]);
72+
return (
73+
<PaginationUnit style={{ userSelect: 'none' }}>
74+
<PaginationContent>
75+
<PaginationItem>
76+
<PaginationPrevious
77+
onClick={() => {
78+
if (currentPage > 1) {
79+
onChange(currentPage - 1);
80+
setCurrentPage(currentPage - 1);
81+
}
82+
}}
83+
mode={mode}
84+
style={{
85+
opacity: currentPage <= 1 ? 0.25 : 1,
86+
cursor: currentPage <= 1 ? 'not-allowed' : 'pointer',
87+
pointerEvents: currentPage <= 1 ? 'none' : 'auto',
88+
padding: '0.5rem',
89+
borderRadius: '999px',
90+
backgroundColor: mode === 'light' ? '#232E3D' : '#FFF',
91+
}}
92+
/>
93+
</PaginationItem>
94+
95+
{pageNumbers.map((page, index) => (
96+
<PaginationItem key={index}>
97+
{page === 'ellipsis' ? (
98+
<PaginationEllipsis mode={mode} />
99+
) : (
100+
<PaginationLink
101+
onClick={() => {
102+
setCurrentPage(page);
103+
onChange(page);
104+
}}
105+
className={`${
106+
mode === 'dark'
107+
? 'undp-pagination-link dark-mode'
108+
: 'undp-pagination-link'
109+
}${page === currentPage ? ' selected' : ''}`}
110+
>
111+
{page}
112+
</PaginationLink>
113+
)}
114+
</PaginationItem>
115+
))}
116+
117+
<PaginationItem>
118+
<PaginationNext
119+
onClick={() => {
120+
if (currentPage < totalPages) {
121+
onChange(currentPage + 1);
122+
setCurrentPage(currentPage + 1);
123+
}
124+
}}
125+
mode={mode}
126+
style={{
127+
opacity: currentPage >= totalPages ? 0.25 : 1,
128+
cursor: currentPage >= totalPages ? 'not-allowed' : 'pointer',
129+
pointerEvents: currentPage >= totalPages ? 'none' : 'auto',
130+
padding: '0.5rem',
131+
borderRadius: '999px',
132+
backgroundColor: mode === 'light' ? '#232E3D' : '#FFF',
133+
}}
134+
/>
135+
</PaginationItem>
136+
</PaginationContent>
137+
</PaginationUnit>
138+
);
139+
}
140+
141+
export { Pagination };

0 commit comments

Comments
 (0)