Introduced a predefined mapper dialog (#169)

* added optional dialog variant

* added predefined scope dialog

* added mapper dialog for predefined and custom

* format

* changed to use dataList instead of table

* fixed test
This commit is contained in:
Erik Jan de Wit 2020-10-21 14:18:41 +02:00 committed by GitHub
parent 49284a0f11
commit a9dc031fff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 19464 additions and 11 deletions

View file

@ -0,0 +1,167 @@
import React, { ReactElement, useState } from "react";
import {
Button,
ButtonVariant,
DataList,
DataListCell,
DataListItem,
DataListItemCells,
DataListItemRow,
Modal,
Text,
TextContent,
} from "@patternfly/react-core";
import {
Table,
TableBody,
TableHeader,
TableVariant,
} from "@patternfly/react-table";
import { useTranslation } from "react-i18next";
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
import {
ProtocolMapperRepresentation,
ProtocolMapperTypeRepresentation,
} from "../../context/server-info/server-info";
export type AddMapperDialogProps = {
protocol: string;
buildIn: boolean;
onConfirm: (
value: ProtocolMapperTypeRepresentation | ProtocolMapperRepresentation[]
) => {};
};
type AddMapperDialogModalProps = AddMapperDialogProps & {
open: boolean;
toggleDialog: () => void;
};
export const useAddMapperDialog = (
props: AddMapperDialogProps
): [() => void, () => ReactElement] => {
const [show, setShow] = useState(false);
function toggleDialog() {
setShow((show) => !show);
}
const Dialog = () => (
<AddMapperDialog {...props} open={show} toggleDialog={toggleDialog} />
);
return [toggleDialog, Dialog];
};
export const AddMapperDialog = ({
protocol,
buildIn,
open,
toggleDialog,
onConfirm,
}: AddMapperDialogModalProps) => {
const serverInfo = useServerInfo();
const protocolMappers = serverInfo.protocolMapperTypes[protocol];
const buildInMappers = serverInfo.builtinProtocolMappers[protocol];
const { t } = useTranslation("client-scopes");
const [rows, setRows] = useState(
buildInMappers.map((mapper) => {
const mapperType = protocolMappers.filter(
(type) => type.id === mapper.protocolMapper
)[0];
return {
item: mapper,
selected: false,
cells: [mapper.name, mapperType.helpText],
};
})
);
return (
<Modal
title={t("chooseAMapperType")}
isOpen={open}
onClose={toggleDialog}
actions={
buildIn
? [
<Button
id="modal-confirm"
key="confirm"
onClick={() => {
onConfirm(
rows.filter((row) => row.selected).map((row) => row.item)
);
toggleDialog();
}}
>
{t("common:add")}
</Button>,
<Button
id="modal-cancel"
key="cancel"
variant={ButtonVariant.secondary}
onClick={() => {
toggleDialog();
}}
>
{t("common:cancel")}
</Button>,
]
: []
}
>
<TextContent>
<Text>{t("predefinedMappingDescription")}</Text>
</TextContent>
{!buildIn && (
<DataList
onSelectDataListItem={(id) => {
const mapper = protocolMappers.find((mapper) => mapper.id === id);
onConfirm(mapper!);
toggleDialog();
}}
aria-label={t("chooseAMapperType")}
isCompact
>
{protocolMappers.map((mapper) => (
<DataListItem
aria-labelledby={mapper.name}
key={mapper.id}
id={mapper.id}
>
<DataListItemRow>
<DataListItemCells
dataListCells={[
<DataListCell key={`name-${mapper.id}`}>
<>{mapper.name}</>
</DataListCell>,
<DataListCell key={`helpText-${mapper.id}`}>
<>{mapper.helpText}</>
</DataListCell>,
]}
/>
</DataListItemRow>
</DataListItem>
))}
</DataList>
)}
{buildIn && (
<Table
variant={TableVariant.compact}
cells={[t("name"), t("description")]}
onSelect={(_, isSelected, rowIndex) => {
rows[rowIndex].selected = isSelected;
setRows([...rows]);
}}
canSelectAll={false}
rows={rows}
aria-label={t("chooseAMapperType")}
>
<TableHeader />
<TableBody />
</Table>
)}
</Modal>
);
};

View file

@ -0,0 +1,75 @@
import React from "react";
import { mount } from "enzyme";
import { Button } from "@patternfly/react-core";
import serverInfo from "../../../context/server-info/__tests__/mock.json";
import { ServerInfoContext } from "../../../context/server-info/ServerInfoProvider";
import { AddMapperDialogProps, useAddMapperDialog } from "../MapperDialog";
describe("<MapperDialog/>", () => {
const Test = (args: AddMapperDialogProps) => {
const [toggle, Dialog] = useAddMapperDialog(args);
return (
<ServerInfoContext.Provider value={serverInfo}>
<Dialog />
<Button id="open" onClick={toggle}>
Show
</Button>
</ServerInfoContext.Provider>
);
};
it("should return empty array when selecting nothing", () => {
const onConfirm = jest.fn();
const container = mount(
<Test buildIn={true} protocol="openid-connect" onConfirm={onConfirm} />
);
container.find("button#open").simulate("click");
expect(container).toMatchSnapshot();
container.find("button#modal-confirm").simulate("click");
expect(onConfirm).toBeCalledWith([]);
});
it("should return array with selected build in protocol mapping", () => {
const onConfirm = jest.fn();
const protocol = "openid-connect";
const container = mount(
<Test buildIn={true} protocol={protocol} onConfirm={onConfirm} />
);
container.find("button#open").simulate("click");
container
.find("input[name='checkrow0']")
.simulate("change", { target: { value: true } });
container
.find("input[name='checkrow1']")
.simulate("change", { target: { value: true } });
container.find("button#modal-confirm").simulate("click");
expect(onConfirm).toBeCalledWith([
serverInfo.builtinProtocolMappers[protocol][0],
serverInfo.builtinProtocolMappers[protocol][1],
]);
});
it("should return selected protocol mapping type on click", () => {
const onConfirm = jest.fn();
const protocol = "openid-connect";
const container = mount(
<Test buildIn={false} protocol={protocol} onConfirm={onConfirm} />
);
container.find("button#open").simulate("click");
expect(container).toMatchSnapshot();
container
.find("div.pf-c-data-list__item-content")
.first()
.simulate("click");
expect(onConfirm).toBeCalledWith(
serverInfo.protocolMapperTypes[protocol][0]
);
});
});

File diff suppressed because it is too large Load diff

View file

@ -30,6 +30,9 @@
"mappingDeletedError": "Could not delete mapping: '{{error}}'",
"displayOnConsentScreen": "Display on consent screen",
"consentScreenText": "Consent screen text",
"guiOrder": "Display Order"
"guiOrder": "Display Order",
"chooseAMapperType": "Choose a mapper type",
"predefinedMappingDescription": "Choose one of the predefined mappings from this table",
"mappingTable": "Table with predefined mapping"
}
}

View file

@ -3,6 +3,7 @@
"fullName": "{{givenName}} {{familyName}}",
"unknownUser": "Anonymous",
"add": "Add",
"create": "Create",
"save": "Save",
"cancel": "Cancel",

View file

@ -38,6 +38,7 @@ export type ConfirmDialogProps = {
cancelButtonLabel?: string;
continueButtonLabel?: string;
continueButtonVariant?: ButtonVariant;
variant?: ModalVariant;
onConfirm: () => void;
onCancel?: () => void;
children?: ReactNode;
@ -53,6 +54,7 @@ export const ConfirmDialogModal = ({
onCancel,
children,
open = true,
variant = ModalVariant.default,
toggleDialog,
}: ConfirmDialogModalProps) => {
const { t } = useTranslation();
@ -61,7 +63,7 @@ export const ConfirmDialogModal = ({
title={t(titleKey)}
isOpen={open}
onClose={toggleDialog}
variant={ModalVariant.small}
variant={variant}
actions={[
<Button
id="modal-confirm"

View file

@ -48,7 +48,7 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
ouiaSafe={true}
showClose={true}
title="Delete app02?"
variant="small"
variant="default"
>
<Portal
containerInfo={
@ -63,8 +63,8 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
aria-describedby="pf-modal-part-3"
aria-labelledby="pf-modal-part-2"
aria-modal="true"
class="pf-c-modal-box pf-m-sm"
data-ouia-component-id="OUIA-Generated-Modal-small-2"
class="pf-c-modal-box"
data-ouia-component-id="OUIA-Generated-Modal-default-2"
data-ouia-component-type="PF4/ModalContent"
data-ouia-safe="true"
id="pf-modal-part-1"
@ -170,11 +170,11 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
isOpen={true}
labelId="pf-modal-part-2"
onClose={[Function]}
ouiaId="OUIA-Generated-Modal-small-2"
ouiaId="OUIA-Generated-Modal-default-2"
ouiaSafe={true}
showClose={true}
title="Delete app02?"
variant="small"
variant="default"
>
<Backdrop>
<div
@ -198,20 +198,20 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
aria-label=""
aria-labelledby="pf-modal-part-2"
className=""
data-ouia-component-id="OUIA-Generated-Modal-small-2"
data-ouia-component-id="OUIA-Generated-Modal-default-2"
data-ouia-component-type="PF4/ModalContent"
data-ouia-safe={true}
id="pf-modal-part-1"
style={Object {}}
variant="small"
variant="default"
>
<div
aria-describedby="pf-modal-part-3"
aria-label={null}
aria-labelledby="pf-modal-part-2"
aria-modal="true"
className="pf-c-modal-box pf-m-sm"
data-ouia-component-id="OUIA-Generated-Modal-small-2"
className="pf-c-modal-box"
data-ouia-component-id="OUIA-Generated-Modal-default-2"
data-ouia-component-type="PF4/ModalContent"
data-ouia-safe={true}
id="pf-modal-part-1"

View file

@ -0,0 +1,38 @@
import React from "react";
import { Button } from "@patternfly/react-core";
import { Meta, Story } from "@storybook/react";
import serverInfo from "../context/server-info/__tests__/mock.json";
import { ServerInfoContext } from "../context/server-info/ServerInfoProvider";
import {
AddMapperDialog,
AddMapperDialogProps,
useAddMapperDialog,
} from "../client-scopes/add/MapperDialog";
export default {
title: "Add mapper dialog",
component: AddMapperDialog,
} as Meta;
const Template: Story<AddMapperDialogProps> = (args) => {
const [toggle, Dialog] = useAddMapperDialog(args);
return (
<ServerInfoContext.Provider value={serverInfo}>
<Dialog />
<Button onClick={toggle}>Show</Button>
</ServerInfoContext.Provider>
);
};
export const BuildInDialog = Template.bind({});
BuildInDialog.args = {
protocol: "openid-connect",
buildIn: true,
};
export const ProtocolMapperDialog = Template.bind({});
ProtocolMapperDialog.args = {
protocol: "openid-connect",
buildIn: false,
};