From f11f2bffdf42a836831a57f6505dbcb8265f7e92 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Mon, 24 Aug 2020 20:11:17 +0200 Subject: [PATCH] make pagination work on the client list (#39) * fix: make pagination work * fix formatting * Xmove toolbar to seperate component --- src/App.tsx | 35 +++++-- src/PageHeader.tsx | 94 ++++++++++--------- src/clients/ClientList.tsx | 78 +++------------ src/components/table-toolbar/TableToolbar.tsx | 78 +++++++++++++++ src/forms/realm/NewRealmForm.tsx | 7 +- src/http-service/http-client.ts | 2 +- 6 files changed, 178 insertions(+), 116 deletions(-) create mode 100644 src/components/table-toolbar/TableToolbar.tsx diff --git a/src/App.tsx b/src/App.tsx index 3149c4970a..829476df72 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,21 +1,24 @@ -import React, { useContext } from 'react'; +import React, { useContext, useState } from 'react'; +import { Page, PageSection, Button } from '@patternfly/react-core'; import { ClientList } from './clients/ClientList'; import { DataLoader } from './components/data-loader/DataLoader'; import { HttpClientContext } from './http-service/HttpClientContext'; import { Client } from './clients/client-model'; -import { Page, PageSection } from '@patternfly/react-core'; import { Header } from './PageHeader'; import { PageNav } from './PageNav'; import { KeycloakContext } from './auth/KeycloakContext'; +import { TableToolbar } from './components/table-toolbar/TableToolbar'; export const App = () => { + const [max, setMax] = useState(10); + const [first, setFirst] = useState(0); const httpClient = useContext(HttpClientContext); const keycloak = useContext(KeycloakContext); const loader = async () => { return await httpClient - ?.doGet('/realms/master/clients?first=0&max=20&search=true') + ?.doGet('/realms/master/clients', { params: { first, max } }) .then((r) => r.data as Client[]); }; return ( @@ -23,10 +26,28 @@ export const App = () => { {(clients) => ( - + setFirst(f)} + onPreviousClick={(f) => setFirst(f)} + onPerPageSelect={(f, m) => { + setFirst(f); + setMax(m); + }} + toolbarItem={ + <> + + + + } + > + + )} diff --git a/src/PageHeader.tsx b/src/PageHeader.tsx index 12de526c64..781f400773 100644 --- a/src/PageHeader.tsx +++ b/src/PageHeader.tsx @@ -1,19 +1,20 @@ import React, { useContext, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { - Avatar, - Button, - ButtonVariant, - Brand, - Dropdown, - DropdownItem, - DropdownSeparator, - DropdownToggle, - KebabToggle, - PageHeader, - PageHeaderTools, - PageHeaderToolsItem, - PageHeaderToolsGroup} from '@patternfly/react-core'; + Avatar, + Button, + ButtonVariant, + Brand, + Dropdown, + DropdownItem, + DropdownSeparator, + DropdownToggle, + KebabToggle, + PageHeader, + PageHeaderTools, + PageHeaderToolsItem, + PageHeaderToolsGroup, +} from '@patternfly/react-core'; import { HelpIcon } from '@patternfly/react-icons'; import { KeycloakContext } from './auth/KeycloakContext'; @@ -31,46 +32,51 @@ const ManageAccountDropdownItem = () => { const keycloak = useContext(KeycloakContext); const { t } = useTranslation(); return ( - keycloak?.account()}>{t('Manage account')} + keycloak?.account()}> + {t('Manage account')} + ); -} +}; const SignOutDropdownItem = () => { const keycloak = useContext(KeycloakContext); const { t } = useTranslation(); return ( - keycloak?.logout()}>{t('Sign out')} + keycloak?.logout()}> + {t('Sign out')} + ); -} +}; const ServerInfoDropdownItem = () => { const { t } = useTranslation(); - return ( - {t('Server info')} - ) -} + return {t('Server info')}; +}; const HelpDropdownItem = () => { const { t } = useTranslation(); const help = t('Help'); return ( - {` ${help}`} - ) -} + + + {` ${help}`} + + ); +}; const kebabDropdownItems = [ - , - , - , + , + , + , , - + , ]; const userDropdownItems = [ - , - , - , - + , + , + , + , ]; const headerTools = () => { @@ -79,7 +85,7 @@ const headerTools = () => { @@ -92,24 +98,24 @@ const headerTools = () => { ); -} +}; const KebabDropdown = () => { const [isDropdownOpen, setDropdownOpen] = useState(false); @@ -126,8 +132,8 @@ const KebabDropdown = () => { isOpen={isDropdownOpen} dropdownItems={kebabDropdownItems} /> - ) -} + ); +}; const UserDropdown = () => { const keycloak = useContext(KeycloakContext); @@ -142,8 +148,12 @@ const UserDropdown = () => { isPlain position="right" isOpen={isDropdownOpen} - toggle={{keycloak?.loggedInUser}} + toggle={ + + {keycloak?.loggedInUser} + + } dropdownItems={userDropdownItems} /> ); -} +}; diff --git a/src/clients/ClientList.tsx b/src/clients/ClientList.tsx index 1b63051a3f..e6e8f2056d 100644 --- a/src/clients/ClientList.tsx +++ b/src/clients/ClientList.tsx @@ -7,18 +7,7 @@ import { IFormatter, IFormatterValueType, } from '@patternfly/react-table'; -import { - ToolbarContent, - ToolbarItem, - Pagination, - Toolbar, - InputGroup, - TextInput, - Button, - Badge, - ToggleTemplateProps, -} from '@patternfly/react-core'; -import { SearchIcon } from '@patternfly/react-icons'; +import { Badge } from '@patternfly/react-core'; import { Client } from './client-model'; @@ -35,21 +24,6 @@ const columns: (keyof Client)[] = [ ]; export const ClientList = ({ baseUrl, clients }: ClientListProps) => { - const pagination = (variant: 'top' | 'bottom' = 'top') => ( - ( - - {firstIndex} - {lastIndex} - - )} - itemCount={100} - page={1} - perPage={10} - variant={variant} - /> - ); - const enabled = (): IFormatter => (data?: IFormatterValueType) => { const field = data!.toString(); const value = field.substring(0, field.indexOf('#')); @@ -83,41 +57,19 @@ export const ClientList = ({ baseUrl, clients }: ClientListProps) => { return { cells: columns.map((col) => c[col]) }; }); return ( - <> - - - - - - - - - - - - - {pagination()} - - - - - -
- - {pagination('bottom')} - - + + + +
); }; diff --git a/src/components/table-toolbar/TableToolbar.tsx b/src/components/table-toolbar/TableToolbar.tsx new file mode 100644 index 0000000000..8d92cc4e82 --- /dev/null +++ b/src/components/table-toolbar/TableToolbar.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { + ToggleTemplateProps, + Toolbar, + ToolbarContent, + ToolbarItem, + InputGroup, + TextInput, + Button, + Pagination, +} from '@patternfly/react-core'; +import { SearchIcon } from '@patternfly/react-icons'; + +type TableToolbarProps = { + count: number; + first: number; + max: number; + onNextClick: (page: number) => void; + onPreviousClick: (page: number) => void; + onPerPageSelect: (max: number, first: number) => void; + toolbarItem?: React.ReactNode; + children: React.ReactNode; +}; + +export const TableToolbar = ({ + count, + first, + max, + onNextClick, + onPreviousClick, + onPerPageSelect, + toolbarItem, + children, +}: TableToolbarProps) => { + const page = first / max; + const pagination = (variant: 'top' | 'bottom' = 'top') => ( + ( + + {firstIndex} - {lastIndex} + + )} + itemCount={count + page * max + (count <= max ? 1 : 0)} + page={page + 1} + perPage={max} + onNextClick={(_, p) => onNextClick((p - 1) * max)} + onPreviousClick={(_, p) => onPreviousClick((p - 1) * max)} + onPerPageSelect={(_, m, f) => onPerPageSelect(f, m)} + variant={variant} + /> + ); + + return ( + <> + + + + + + + + + { toolbarItem && + { toolbarItem } + } + {pagination()} + + + {children} + + {pagination('bottom')} + + + ); +}; diff --git a/src/forms/realm/NewRealmForm.tsx b/src/forms/realm/NewRealmForm.tsx index 766d41702b..cf1fec42c4 100644 --- a/src/forms/realm/NewRealmForm.tsx +++ b/src/forms/realm/NewRealmForm.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React from 'react'; import { Text, PageSection, @@ -11,13 +11,14 @@ import { ActionGroup, Button, Divider, -} from "@patternfly/react-core"; +} from '@patternfly/react-core'; //type NewRealmFormProps = { // realm: string; //}; -export const NewRealmForm = () => { //({ realm }: NewRealmFormProps) => { +export const NewRealmForm = () => { + //({ realm }: NewRealmFormProps) => { return ( <> diff --git a/src/http-service/http-client.ts b/src/http-service/http-client.ts index d2a85386e5..1dd148acab 100644 --- a/src/http-service/http-client.ts +++ b/src/http-service/http-client.ts @@ -114,7 +114,7 @@ export class HttpClient { ); } - return url + searchParams.toString(); + return url + '?' + searchParams.toString(); } private makeConfig(config: RequestInit = {}): Promise {