make pagination work on the client list (#39)
* fix: make pagination work * fix formatting * Xmove toolbar to seperate component
This commit is contained in:
parent
64f96c7d1b
commit
f11f2bffdf
6 changed files with 178 additions and 116 deletions
27
src/App.tsx
27
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 = () => {
|
|||
<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>
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
78
src/components/table-toolbar/TableToolbar.tsx
Normal file
78
src/components/table-toolbar/TableToolbar.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -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">
|
||||
|
|
|
@ -114,7 +114,7 @@ export class HttpClient {
|
|||
);
|
||||
}
|
||||
|
||||
return url + searchParams.toString();
|
||||
return url + '?' + searchParams.toString();
|
||||
}
|
||||
|
||||
private makeConfig(config: RequestInit = {}): Promise<RequestInit> {
|
||||
|
|
Loading…
Reference in a new issue