make pagination work on the client list (#39)

* fix: make pagination work

* fix formatting

* Xmove toolbar to seperate component
This commit is contained in:
Erik Jan de Wit 2020-08-24 20:11:17 +02:00 committed by GitHub
parent 64f96c7d1b
commit f11f2bffdf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 178 additions and 116 deletions

View file

@ -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 = () => {
<PageSection variant="light">
<DataLoader loader={loader}>
{(clients) => (
<TableToolbar
count={clients!.length}
first={first}
max={max}
onNextClick={(f) => setFirst(f)}
onPreviousClick={(f) => setFirst(f)}
onPerPageSelect={(f, m) => {
setFirst(f);
setMax(m);
}}
toolbarItem={
<>
<Button>Create client</Button>
<Button variant="link">Import client</Button>
</>
}
>
<ClientList
clients={clients}
baseUrl={keycloak!.authServerUrl()!}
/>
</TableToolbar>
)}
</DataLoader>
</PageSection>

View file

@ -13,7 +13,8 @@ import {
PageHeader,
PageHeaderTools,
PageHeaderToolsItem,
PageHeaderToolsGroup} from '@patternfly/react-core';
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 (
<DropdownItem key="manage account" onClick={() => keycloak?.account()}>{t('Manage account')}</DropdownItem>
<DropdownItem key="manage account" onClick={() => keycloak?.account()}>
{t('Manage account')}
</DropdownItem>
);
}
};
const SignOutDropdownItem = () => {
const keycloak = useContext(KeycloakContext);
const { t } = useTranslation();
return (
<DropdownItem key="sign out" onClick={() => keycloak?.logout()}>{t('Sign out')}</DropdownItem>
<DropdownItem key="sign out" onClick={() => keycloak?.logout()}>
{t('Sign out')}
</DropdownItem>
);
}
};
const ServerInfoDropdownItem = () => {
const { t } = useTranslation();
return (
<DropdownItem key="server info">{t('Server info')}</DropdownItem>
)
}
return <DropdownItem key="server info">{t('Server info')}</DropdownItem>;
};
const HelpDropdownItem = () => {
const { t } = useTranslation();
const help = t('Help');
return (
<DropdownItem><HelpIcon />{` ${help}`}</DropdownItem>
)
}
<DropdownItem>
<HelpIcon />
{` ${help}`}
</DropdownItem>
);
};
const kebabDropdownItems = [
<ManageAccountDropdownItem key='kebab Manage Account'/>,
<ServerInfoDropdownItem key='kebab Server Info'/>,
<HelpDropdownItem key='kebab Help'/>,
<ManageAccountDropdownItem key="kebab Manage Account" />,
<ServerInfoDropdownItem key="kebab Server Info" />,
<HelpDropdownItem key="kebab Help" />,
<DropdownSeparator key="kebab sign out seperator" />,
<SignOutDropdownItem key='kebab Sign out'/>
<SignOutDropdownItem key="kebab Sign out" />,
];
const userDropdownItems = [
<ManageAccountDropdownItem key='Manage Account'/>,
<ServerInfoDropdownItem key='Server info'/>,
<ManageAccountDropdownItem key="Manage Account" />,
<ServerInfoDropdownItem key="Server info" />,
<DropdownSeparator key="sign out seperator" />,
<SignOutDropdownItem key='Sign out'/>
<SignOutDropdownItem key="Sign out" />,
];
const headerTools = () => {
@ -79,7 +85,7 @@ const headerTools = () => {
<PageHeaderToolsGroup
visibility={{
default: 'hidden',
md: 'visible'
md: 'visible',
}} /** the settings and help icon buttons are only visible on desktop sizes and replaced by a kebab dropdown for other sizes */
>
<PageHeaderToolsItem>
@ -92,24 +98,24 @@ const headerTools = () => {
<PageHeaderToolsGroup>
<PageHeaderToolsItem
visibility={{
md: 'hidden'
md: 'hidden',
}} /** this kebab dropdown replaces the icon buttons and is hidden for desktop sizes */
>
<KebabDropdown/>
<KebabDropdown />
</PageHeaderToolsItem>
<PageHeaderToolsItem
visibility={{
default: 'hidden',
md: 'visible'
md: 'visible',
}} /** this user dropdown is hidden on mobile sizes */
>
<UserDropdown/>
<UserDropdown />
</PageHeaderToolsItem>
</PageHeaderToolsGroup>
<Avatar src="/img_avatar.svg" alt="Avatar image" />
</PageHeaderTools>
);
}
};
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={<DropdownToggle onToggle={onDropdownToggle}>{keycloak?.loggedInUser}</DropdownToggle>}
toggle={
<DropdownToggle onToggle={onDropdownToggle}>
{keycloak?.loggedInUser}
</DropdownToggle>
}
dropdownItems={userDropdownItems}
/>
);
}
};

View file

@ -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') => (
<Pagination
isCompact
toggleTemplate={({ firstIndex, lastIndex }: ToggleTemplateProps) => (
<b>
{firstIndex} - {lastIndex}
</b>
)}
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,24 +57,6 @@ export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
return { cells: columns.map((col) => c[col]) };
});
return (
<>
<Toolbar>
<ToolbarContent>
<ToolbarItem>
<InputGroup>
<TextInput type="text" aria-label="search for client criteria" />
<Button variant="control" aria-label="search for client">
<SearchIcon />
</Button>
</InputGroup>
</ToolbarItem>
<ToolbarItem>
<Button>Create client</Button>
<Button variant="link">Import client</Button>
</ToolbarItem>
<ToolbarItem variant="pagination">{pagination()}</ToolbarItem>
</ToolbarContent>
</Toolbar>
<Table
variant={TableVariant.compact}
cells={[
@ -115,9 +71,5 @@ export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
<TableHeader />
<TableBody />
</Table>
<Toolbar>
<ToolbarItem>{pagination('bottom')}</ToolbarItem>
</Toolbar>
</>
);
};

View file

@ -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') => (
<Pagination
isCompact
toggleTemplate={({ firstIndex, lastIndex }: ToggleTemplateProps) => (
<b>
{firstIndex} - {lastIndex}
</b>
)}
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 (
<>
<Toolbar>
<ToolbarContent>
<ToolbarItem>
<InputGroup>
<TextInput type="text" aria-label="search for client criteria" />
<Button variant="control" aria-label="search for client">
<SearchIcon />
</Button>
</InputGroup>
</ToolbarItem>
{ toolbarItem && <ToolbarItem>
{ toolbarItem }
</ToolbarItem>}
<ToolbarItem variant="pagination">{pagination()}</ToolbarItem>
</ToolbarContent>
</Toolbar>
{children}
<Toolbar>
<ToolbarItem>{pagination('bottom')}</ToolbarItem>
</Toolbar>
</>
);
};

View file

@ -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 (
<>
<PageSection variant="light">

View file

@ -114,7 +114,7 @@ export class HttpClient {
);
}
return url + searchParams.toString();
return url + '?' + searchParams.toString();
}
private makeConfig(config: RequestInit = {}): Promise<RequestInit> {