Skip to content

Commit

Permalink
Improvement for hook configuration UI
Browse files Browse the repository at this point in the history
  • Loading branch information
carmenlau committed Jan 5, 2023
2 parents 4ca2600 + f299f8a commit cb6d9f8
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 87 deletions.
2 changes: 2 additions & 0 deletions portal/src/CommandBarContainer.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@
}

.content {
flex: 1 0 0;
min-height: 0;
overflow: auto;
}
25 changes: 17 additions & 8 deletions portal/src/CommandBarContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,31 @@ export interface CommandBarContainerProps {
primaryItems?: ICommandBarItemProps[];
secondaryItems?: ICommandBarItemProps[];
children?: React.ReactNode;
hideCommandBar?: boolean;
}

const CommandBarContainer: React.VFC<CommandBarContainerProps> =
function CommandBarContainer(props) {
const { className, isLoading, primaryItems, secondaryItems, messageBar } =
props;
const {
className,
isLoading,
primaryItems,
secondaryItems,
messageBar,
hideCommandBar,
} = props;

return (
<>
<div className={styles.header}>
<CommandBar
className={styles.commandBar}
styles={commandBarStyles}
items={primaryItems ?? []}
farItems={secondaryItems}
/>
{hideCommandBar === true ? null : (
<CommandBar
className={styles.commandBar}
styles={commandBarStyles}
items={primaryItems ?? []}
farItems={secondaryItems}
/>
)}
{messageBar}
<ProgressIndicator
styles={progressIndicatorStyles}
Expand Down
24 changes: 9 additions & 15 deletions portal/src/FormContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,21 +166,15 @@ const FormContainer: React.VFC<FormContainerProps> = function FormContainer(
rules={errorRules}
fallbackErrorMessageID={fallbackErrorMessageID}
>
{hideCommandBar === true ? (
<div>
<FormErrorMessageBar>{messageBar}</FormErrorMessageBar>
<form onSubmit={onFormSubmit}>{props.children}</form>
</div>
) : (
<CommandBarContainer
isLoading={isUpdating}
primaryItems={items}
secondaryItems={farItems}
messageBar={<FormErrorMessageBar>{messageBar}</FormErrorMessageBar>}
>
<form onSubmit={onFormSubmit}>{props.children}</form>
</CommandBarContainer>
)}
<CommandBarContainer
hideCommandBar={hideCommandBar}
isLoading={isUpdating}
primaryItems={items}
secondaryItems={farItems}
messageBar={<FormErrorMessageBar>{messageBar}</FormErrorMessageBar>}
>
<form onSubmit={onFormSubmit}>{props.children}</form>
</CommandBarContainer>
<Dialog
hidden={!isResetDialogVisible}
dialogContentProps={resetDialogContentProps}
Expand Down
13 changes: 8 additions & 5 deletions portal/src/graphql/portal/HookConfigurationScreen.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
@apply tablet:col-span-full;
}

.codeEditorContainer {
grid-column: 1 / span 10;
@apply tablet:col-span-full;

@apply flex flex-col;
@apply gap-y-5;
}

.columnFull {
grid-column: 1 / span 8;

Expand Down Expand Up @@ -73,11 +81,6 @@
min-width: 200px;
}

.codeEditorContainer {
@apply flex flex-col;
@apply gap-y-5;
}

.codeEditor {
height: 50vh;
}
Expand Down
183 changes: 125 additions & 58 deletions portal/src/graphql/portal/HookConfigurationScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {
Label,
FontIcon,
Text,
Dialog,
useTheme,
DialogFooter,
} from "@fluentui/react";
import produce from "immer";
import ShowError from "../../ShowError";
Expand Down Expand Up @@ -58,6 +60,13 @@ import PrimaryButton from "../../PrimaryButton";
import ActionButton from "../../ActionButton";
import CodeEditor from "../../CodeEditor";
import DefaultButton from "../../DefaultButton";
import { useSystemConfig } from "../../context/SystemConfigContext";

const CODE_EDITOR_OPTIONS = {
minimap: {
enabled: false,
},
};

const BLOCKING_EVENT_NAME_TO_TYPE_NAME: Record<string, string | undefined> = {
"user.pre_create": "EventUserPreCreate",
Expand Down Expand Up @@ -318,8 +327,10 @@ interface BlockingHandlerItemEditProps {
const BlockingHandlerItemEdit: React.VFC<BlockingHandlerItemEditProps> =
function BlockingHandlerItemEdit(props) {
const { index, value, onChange, onEdit } = props;
const [newEventName, setNewEventName] = useState<string | null>(null);

const { renderToString } = useContext(Context);
const { themes } = useSystemConfig();

const theme = useTheme();

Expand All @@ -340,11 +351,36 @@ const BlockingHandlerItemEdit: React.VFC<BlockingHandlerItemEditProps> =
const eventFieldProps = useErrorMessageString(eventField);
const urlFieldProps = useErrorMessage(urlField);

const onDismissDialog = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
setNewEventName(null);
}, []);
const onConfirmChangeEvent = useCallback(
(e) => {
e.preventDefault();
e.stopPropagation();
if (newEventName != null) {
onChange({ ...value, event: newEventName });
setNewEventName(null);
}
},
[onChange, value, newEventName]
);
const onBlockingEventChange = useCallback(
(_, event?: IDropdownOption) => {
onChange({ ...value, event: String(event?.key ?? "") });
// Show the dialog to confirm overwriting the script if
// the kind is denohook.
if (value.kind === "denohook") {
const key = event?.key ?? null;
if (typeof key === "string") {
setNewEventName(key);
}
} else {
onChange({ ...value, event: String(event?.key ?? "") });
}
},
[onChange, value]
[value, onChange]
);
const onURLChange = useCallback(
(_, url?: string) => {
Expand Down Expand Up @@ -405,65 +441,95 @@ const BlockingHandlerItemEdit: React.VFC<BlockingHandlerItemEditProps> =
];
}, [renderToString]);

const dialogContentProps = useMemo(() => {
return {
title: renderToString("HookConfigurationScreen.change-event.title"),
subText: renderToString(
"HookConfigurationScreen.change-event.description"
),
};
}, [renderToString]);

return (
<div className={styles.hookContainer}>
<Dropdown
className={styles.blockingHookKind}
options={kindOptions}
selectedKey={value.kind}
onChange={onChangeHookKind}
ariaLabel={"HookConfigurationScreen.hook-kind.label"}
/>
<Dropdown
className={styles.blockingHookEvent}
options={eventOptions}
selectedKey={value.event}
onChange={onBlockingEventChange}
ariaLabel={"HookConfigurationScreen.blocking-events.label"}
{...eventFieldProps}
/>
{value.kind === "webhook" ? (
<div className={cn(styles.blockingHookConfig, styles.hookConfig)}>
<Label>
<FormattedMessage id="HookConfigurationScreen.action.endpoint.label" />
</Label>
<TextField
className={styles.hookConfigConfig}
value={value.url}
onChange={onURLChange}
placeholder="https://example.com/callback"
{...urlFieldProps}
/>
</div>
) : null}
{value.kind === "denohook" ? (
<div className={cn(styles.blockingHookConfig, styles.hookConfig)}>
<Label>
<FormattedMessage id="HookConfigurationScreen.action.script.label" />
</Label>
<ActionButton
className={styles.hookConfigConfig}
iconProps={EDIT_BUTTON_ICON_PROPS}
styles={EDIT_BUTTON_STYLES}
<>
<Dialog
hidden={newEventName == null}
onDismiss={onDismissDialog}
dialogContentProps={dialogContentProps}
>
<DialogFooter>
<PrimaryButton
theme={themes.destructive}
text={
<>
<FormattedMessage id="HookConfigurationScreen.edit-hook.label" />
{value.isDirty ? (
<FontIcon
iconName="LocationDot"
className={styles.dot}
style={{
color: theme.palette.themePrimary,
}}
/>
) : null}
</>
<FormattedMessage id="HookConfigurationScreen.change-event.label" />
}
onClick={onClickEdit}
onClick={onConfirmChangeEvent}
/>
</div>
) : null}
</div>
<DefaultButton
text={<FormattedMessage id="cancel" />}
onClick={onDismissDialog}
/>
</DialogFooter>
</Dialog>
<div className={styles.hookContainer}>
<Dropdown
className={styles.blockingHookKind}
options={kindOptions}
selectedKey={value.kind}
onChange={onChangeHookKind}
ariaLabel={"HookConfigurationScreen.hook-kind.label"}
/>
<Dropdown
className={styles.blockingHookEvent}
options={eventOptions}
selectedKey={value.event}
onChange={onBlockingEventChange}
ariaLabel={"HookConfigurationScreen.blocking-events.label"}
{...eventFieldProps}
/>
{value.kind === "webhook" ? (
<div className={cn(styles.blockingHookConfig, styles.hookConfig)}>
<Label>
<FormattedMessage id="HookConfigurationScreen.action.endpoint.label" />
</Label>
<TextField
className={styles.hookConfigConfig}
value={value.url}
onChange={onURLChange}
placeholder="https://example.com/callback"
{...urlFieldProps}
/>
</div>
) : null}
{value.kind === "denohook" ? (
<div className={cn(styles.blockingHookConfig, styles.hookConfig)}>
<Label>
<FormattedMessage id="HookConfigurationScreen.action.script.label" />
</Label>
<ActionButton
className={styles.hookConfigConfig}
iconProps={EDIT_BUTTON_ICON_PROPS}
styles={EDIT_BUTTON_STYLES}
text={
<>
<FormattedMessage id="HookConfigurationScreen.edit-hook.label" />
{value.isDirty ? (
<FontIcon
iconName="LocationDot"
className={styles.dot}
style={{
color: theme.palette.themePrimary,
}}
/>
) : null}
</>
}
onClick={onClickEdit}
/>
</div>
) : null}
</div>
</>
);
};

Expand Down Expand Up @@ -1062,7 +1128,7 @@ const HookConfigurationScreenContent: React.VFC<HookConfigurationScreenContentPr
<FormContainer form={form} hideCommandBar={codeEditorState != null}>
<ScreenContent>
{codeEditorState != null ? (
<div className={cn(styles.widget, styles.codeEditorContainer)}>
<div className={cn(styles.codeEditorContainer)}>
<WidgetTitle>
<FormattedMessage id="HookConfigurationScreen.edit-hook.label" />
</WidgetTitle>
Expand All @@ -1074,6 +1140,7 @@ const HookConfigurationScreenContent: React.VFC<HookConfigurationScreenContentPr
language="typescript"
value={code}
onChange={onChangeCode}
options={CODE_EDITOR_OPTIONS}
/>
<div className={styles.codeEditorFooter}>
<PrimaryButton
Expand Down
5 changes: 4 additions & 1 deletion portal/src/locale-data/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -789,8 +789,11 @@
"HookConfigurationScreen.hook-kind.label": "Kind",
"HookConfigurationScreen.hook-kind.webhook": "Webhook",
"HookConfigurationScreen.hook-kind.denohook": "TypeScript",
"HookConfigurationScreen.change-event.title": "Change Event",
"HookConfigurationScreen.change-event.description": "Changing the event will overwrite your script. Are you sure to change?",
"HookConfigurationScreen.change-event.label": "Change Event",
"HookConfigurationScreen.edit-hook.label": "Edit Script",
"HookConfigurationScreen.edit-hook.description": "Refer to the {DocLink, react, href{https://docs.authgear.com/integrate/events-hooks/denohooks} children{documentation}} to learn how to write the script.",
"HookConfigurationScreen.edit-hook.description": "Refer to the {DocLink, react, href{https://docs.authgear.com/integrate/events-hooks/denohooks} children{documentation}} to learn how to write the script. See the {DocLink, react, href{https://docs.authgear.com/integrate/events-hooks/event-list} children{Event List}} for the full list of events.",
"HookConfigurationScreen.blocking-events": "Blocking Events",
"HookConfigurationScreen.blocking-events.description": "Blocking Events allow your hooks to check and abort operations in different scenarios. {br, react} {DocLink, react, href{https://docs.authgear.com/integrate/events-hooks#blocking-events} children{Learn more about blocking events}}.",
"HookConfigurationScreen.blocking-handlers.label": "Blocking Hooks",
Expand Down

0 comments on commit cb6d9f8

Please sign in to comment.