Generic view header (#115)

* implements static template for table view header

* initial table view header layout

* renamed

* added actions

* format

* review

Co-authored-by: Sarah Rambacher <srambach@redhat.com>
This commit is contained in:
Erik Jan de Wit 2020-09-25 19:29:03 +02:00 committed by GitHub
parent e7b108a623
commit 19b458577b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 205 additions and 64 deletions

View file

@ -10,6 +10,7 @@ import { HttpClientContext } from "../http-service/HttpClientContext";
import { KeycloakContext } from "../auth/KeycloakContext";
import { ClientRepresentation } from "./models/client-model";
import { RealmContext } from "../components/realm-context/RealmContext";
import { ViewHeader } from "../components/view-header/ViewHeader";
export const ClientsSection = () => {
const { t } = useTranslation("clients");
@ -28,40 +29,46 @@ export const ClientsSection = () => {
};
return (
<PageSection variant="light">
<DataLoader loader={loader}>
{(clients) => (
<TableToolbar
count={clients!.length}
first={first}
max={max}
onNextClick={setFirst}
onPreviousClick={setFirst}
onPerPageSelect={(f, m) => {
setFirst(f);
setMax(m);
}}
toolbarItem={
<>
<Button onClick={() => history.push("/add-client")}>
{t("createClient")}
</Button>
<Button
onClick={() => history.push("/import-client")}
variant="link"
>
{t("importClient")}
</Button>
</>
}
>
<ClientList
clients={clients}
baseUrl={keycloak!.authServerUrl()!}
/>
</TableToolbar>
)}
</DataLoader>
</PageSection>
<>
<ViewHeader
titleKey="clients:clientList"
subKey="clients:clientsExplain"
/>
<PageSection variant="light">
<DataLoader loader={loader}>
{(clients) => (
<TableToolbar
count={clients!.length}
first={first}
max={max}
onNextClick={setFirst}
onPreviousClick={setFirst}
onPerPageSelect={(first, max) => {
setFirst(first);
setMax(max);
}}
toolbarItem={
<>
<Button onClick={() => history.push("/add-client")}>
{t("createClient")}
</Button>
<Button
onClick={() => history.push("/import-client")}
variant="link"
>
{t("importClient")}
</Button>
</>
}
>
<ClientList
clients={clients}
baseUrl={keycloak!.authServerUrl()!}
/>
</TableToolbar>
)}
</DataLoader>
</PageSection>
</>
);
};

View file

@ -1,10 +1,7 @@
import React, { useState, useContext } from "react";
import { useHistory } from "react-router-dom";
import {
Text,
PageSection,
TextContent,
Divider,
Wizard,
AlertVariant,
WizardFooter,
@ -20,6 +17,7 @@ import { CapabilityConfig } from "./CapabilityConfig";
import { ClientRepresentation } from "../models/client-model";
import { useAlerts } from "../../components/alert/Alerts";
import { RealmContext } from "../../components/realm-context/RealmContext";
import { ViewHeader } from "../../components/view-header/ViewHeader";
export const NewClientForm = () => {
const { t } = useTranslation("clients");
@ -96,12 +94,10 @@ export const NewClientForm = () => {
return (
<>
<Alerts />
<PageSection variant="light">
<TextContent>
<Text component="h1">{title}</Text>
</TextContent>
</PageSection>
<Divider />
<ViewHeader
titleKey="clients:createClient"
subKey="clients:clientsExplain"
/>
<PageSection variant="light">
<Wizard
onClose={() => history.push("/clients")}

View file

@ -1,9 +1,6 @@
import React, { useContext } from "react";
import {
PageSection,
Text,
TextContent,
Divider,
Form,
FormGroup,
TextInput,
@ -20,6 +17,7 @@ import { HttpClientContext } from "../../http-service/HttpClientContext";
import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload";
import { useAlerts } from "../../components/alert/Alerts";
import { RealmContext } from "../../components/realm-context/RealmContext";
import { ViewHeader } from "../../components/view-header/ViewHeader";
export const ImportForm = () => {
const { t } = useTranslation("clients");
@ -55,13 +53,10 @@ export const ImportForm = () => {
return (
<>
<Alerts />
<PageSection variant="light">
<TextContent>
<Text component="h1">{t("importClient")}</Text>
{t("clientsExplain")}
</TextContent>
</PageSection>
<Divider />
<ViewHeader
titleKey="clients:importClient"
subKey="clients:clientsExplain"
/>
<PageSection variant="light">
<Form isHorizontal onSubmit={handleSubmit(save)}>
<JsonFileUpload id="realm-file" onChange={handleFileChange} />

View file

@ -14,6 +14,8 @@
"clearFile": "Clear this file",
"on": "On",
"off":"Off",
"enabled": "Enabled",
"disabled": "Disabled",
"signOut": "Sign out",
"manageAccount": "Manage account",

View file

@ -0,0 +1,108 @@
import React, { ReactElement, useContext, useState } from "react";
import {
Text,
PageSection,
TextContent,
Divider,
Level,
LevelItem,
Switch,
Toolbar,
ToolbarContent,
ToolbarItem,
Badge,
Select,
} from "@patternfly/react-core";
import { HelpContext } from "../help-enabler/HelpHeader";
import { useTranslation } from "react-i18next";
export type ViewHeaderProps = {
titleKey: string;
badge?: string;
subKey: string;
selectItems?: ReactElement[];
isEnabled?: boolean;
onSelect?: (value: string) => void;
onToggle?: (value: boolean) => void;
};
export const ViewHeader = ({
titleKey,
badge,
subKey,
selectItems,
isEnabled,
onSelect,
onToggle,
}: ViewHeaderProps) => {
const { t } = useTranslation();
const { enabled } = useContext(HelpContext);
const [open, setOpen] = useState(false);
const [checked, setChecked] = useState(isEnabled);
return (
<>
<PageSection variant="light">
<Level hasGutter>
<LevelItem>
<Level>
<LevelItem>
<TextContent className="pf-u-mr-sm">
<Text component="h1">{t(titleKey)}</Text>
</TextContent>
</LevelItem>
{badge && (
<LevelItem>
<Badge>{badge}</Badge>
</LevelItem>
)}
</Level>
</LevelItem>
<LevelItem></LevelItem>
{selectItems && (
<LevelItem>
<Toolbar>
<ToolbarContent>
<ToolbarItem>
<Switch
id={`${titleKey}-switch`}
label={t("common:enabled")}
labelOff={t("common:disabled")}
className="pf-u-mr-lg"
isChecked={checked}
onChange={(value) => {
if (onToggle) {
onToggle(value);
}
setChecked(value);
}}
/>
</ToolbarItem>
<ToolbarItem>
<Select
isOpen={open}
onToggle={() => setOpen(!open)}
onSelect={(_, value) => {
if (onSelect) {
onSelect(value as string);
}
setOpen(false);
}}
>
{selectItems}
</Select>
</ToolbarItem>
</ToolbarContent>
</Toolbar>
</LevelItem>
)}
</Level>
{enabled && (
<TextContent>
<Text>{t(subKey)}</Text>
</TextContent>
)}
</PageSection>
<Divider />
</>
);
};

View file

@ -1,16 +1,13 @@
import React, { useState, FormEvent, useContext } from "react";
import React, { useContext } from "react";
import { useTranslation } from "react-i18next";
import {
Text,
PageSection,
TextContent,
FormGroup,
Form,
TextInput,
Switch,
ActionGroup,
Button,
Divider,
AlertVariant,
} from "@patternfly/react-core";
@ -19,6 +16,7 @@ import { RealmRepresentation } from "../models/Realm";
import { HttpClientContext } from "../../http-service/HttpClientContext";
import { useAlerts } from "../../components/alert/Alerts";
import { useForm, Controller } from "react-hook-form";
import { ViewHeader } from "../../components/view-header/ViewHeader";
export const NewRealmForm = () => {
const { t } = useTranslation("realm");
@ -50,12 +48,7 @@ export const NewRealmForm = () => {
return (
<>
<Alerts />
<PageSection variant="light">
<TextContent>
<Text component="h1">Create Realm</Text>
</TextContent>
</PageSection>
<Divider />
<ViewHeader titleKey="realm:createRealm" subKey="realm:realmExplain" />
<PageSection variant="light">
<Form isHorizontal onSubmit={handleSubmit(save)}>
<JsonFileUpload id="kc-realm-filename" onChange={handleFileChange} />

View file

@ -5,6 +5,7 @@
"enabled":"Enabled",
"create":"Create",
"createRealm": "Create realm",
"realmExplain": "A realm manages a set of users, credentials, roles, and groups. A user belongs to and logs into a realm. Realms are isolated from one another and can only manage and authenticate the users that they control.",
"noRealmRoles": "No realm roles",
"emptyStateText": "There aren't any realm roles in this realm. Create a realm role to get started."
}

View file

@ -0,0 +1,39 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { Page, SelectOption } from "@patternfly/react-core";
import {
ViewHeader,
ViewHeaderProps,
} from "../components/view-header/ViewHeader";
export default {
title: "View Header",
component: ViewHeader,
} as Meta;
const Template: Story<ViewHeaderProps> = (args) => (
<Page>
<ViewHeader {...args} />
</Page>
);
export const Extended = Template.bind({});
Extended.args = {
titleKey: "This is the title",
badge: "badge",
subKey: "This is the description.",
selectItems: [
<SelectOption key="first" value="first-item">
First item
</SelectOption>,
<SelectOption key="second" value="second-item">
Second item
</SelectOption>,
],
};
export const Simple = Template.bind({});
Simple.args = {
titleKey: "Title simple",
subKey: "Some lengthy description about what this is about.",
};