Skip to content

Commit

Permalink
feat(topbar): new version of application drawer (#336)
Browse files Browse the repository at this point in the history
  • Loading branch information
NiklasRamstrom authored Oct 25, 2024
1 parent 2c49fbd commit 9b80daa
Show file tree
Hide file tree
Showing 8 changed files with 763 additions and 339 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { makeStyles, shorthands, tokens } from "@fluentui/react-components";

const triggerSpacingWithGroup = tokens.spacingHorizontalS;

export const useApplicationDrawrStyles = makeStyles({
export const useApplicationDrawerStyles = makeStyles({
drawer: {
...shorthands.borderRight(0),
},
Expand Down
302 changes: 302 additions & 0 deletions components/topbar/src/application-drawer-v1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
import { ArrowRightRegular, Dismiss20Regular } from "@fluentui/react-icons";
import React, { useState } from "react";
import { ApplicationArea } from "./top-bar.types";
import { useTranslation } from "./translation-context";
import {
Body1,
Body1Strong,
Button,
Caption1Stronger,
Divider,
Drawer,
DrawerBody,
DrawerHeader,
Link,
mergeClasses,
tokens,
} from "@fluentui/react-components";
import { useApplicationDrawerStyles } from "./application-drawer-v1.styles";
import {
ApplicationAreaFlaworedIcon,
ApplicationAreaIcon,
applicationAreaLabel,
findCurrent,
} from "./application-utils";
import {
ApplicationDrawerContent,
ApplicationDrawerProps,
SingleApplicationDrawerContent,
} from "./application-drawer.types";
import { useApplicationStyles } from "./application.styles";

const DrawerTrigger = (
{ setIsOpen, applicationArea, currentSelection }: {
setIsOpen: (isOpen: boolean) => void;
applicationArea: ApplicationArea;
currentSelection: SingleApplicationDrawerContent | undefined;
}
) => {
const styles = useApplicationDrawerStyles();
const [hover, setHover] = useState(false);

return (
<div
className={styles.drawerTriggerRoot}
data-testid="application-drawer-label"
>
<Button
className={styles.drawerTriggerButton}
data-testid="application-drawer-trigger"
appearance="subtle"
onClick={() => setIsOpen(true)}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
{<ApplicationAreaIcon applicationArea={applicationArea} />}
{currentSelection?.triggerGroupShortName
? (
<>
<Body1Strong className={styles.drawerTriggerTextWithGroup}>
{currentSelection?.triggerGroupShortName}
</Body1Strong>
<div className={styles.triggerDividerWithGroup}></div>
</>
)
: <Divider vertical style={{ padding: "0 0 0 12px" }}></Divider>}
{currentSelection
? (
<div
className={mergeClasses(
styles.drawerTriggerApplication,
currentSelection?.triggerGroupShortName
&& styles.drawerTriggerApplicationWithGroup
)}
>
<div
className={mergeClasses(
styles.drawerTriggerApplicationIcon,
hover && styles.drawerTriggerApplicationIconHovered
)}
>
{currentSelection.icon}
</div>
<Body1Strong
className={styles.drawerTriggerApplicationText}
>
{currentSelection.triggerLabel ?? currentSelection.label}
</Body1Strong>
</div>
)
: null}
</Button>
</div>
);
};

const ApplicationGroupTitle = ({ application }: {
application: ApplicationDrawerContent;
}): JSX.Element => {
const styles = useApplicationDrawerStyles();
const appStyles = useApplicationStyles();

return (
<div className={styles.applicationGroupTitle}>
<div
className={mergeClasses(
styles.applicationGroupTitleIcon,
appStyles.filledIcon
)}
>
{application.icon}
</div>
<Caption1Stronger className={styles.applicationGroupTitleText}>
{application.label.toLocaleUpperCase()}
</Caption1Stronger>
</div>
);
};

const iconConverter = (
icon: JSX.Element,
isCurrent: boolean,
applicationArea: ApplicationArea
) => {
return isCurrent
? (
<ApplicationAreaFlaworedIcon
applicationArea={applicationArea}
icon={icon}
/>
)
: (
icon
);
};

const SingleApplication = ({
application,
currentSelectionId,
onChange,
applicationArea,
}: {
application: SingleApplicationDrawerContent;
currentSelectionId: string;
onChange: (id: string) => void;
applicationArea: ApplicationArea;
}): JSX.Element => {
const styles = useApplicationDrawerStyles();

return (
<Button
data-testid={`application-drawer-item-${application.id}`}
className={mergeClasses(
styles.contentButton,
application.id === currentSelectionId && styles.contentButtonChecked
)}
appearance="subtle"
icon={iconConverter(
application.icon,
application.id === currentSelectionId,
applicationArea
)}
onClick={() => onChange(application.id)}
>
<Body1 className={styles.applicationButton}>
{application.label}
</Body1>
</Button>
);
};

const ApplicationWithChildren = ({
application,
currentSelectionId,
onChange,
applicationArea,
}: {
application: ApplicationDrawerContent;
currentSelectionId: string;
onChange: (id: string) => void;
applicationArea: ApplicationArea;
}): JSX.Element => {
const styles = useApplicationDrawerStyles();

return (
<>
<ApplicationGroupTitle application={application} />
<div className={styles.contentChildren}>
{application.children?.map((child) => {
return (
<SingleApplication
key={child.id}
application={child}
applicationArea={applicationArea}
currentSelectionId={currentSelectionId}
onChange={onChange}
/>
);
})}
</div>
</>
);
};

export const ApplicationDrawerV1 = ({
link,
title,
applicationId,
content,
onChange,
applicationArea,
}: ApplicationDrawerProps & { applicationArea: ApplicationArea }) => {
const { t } = useTranslation();
const [isOpen, setIsOpen] = useState(false);
const styles = useApplicationDrawerStyles();
const currentSelection = findCurrent(applicationId, content);

const onClickItem = (id: string) => {
if (id !== currentSelection?.id) {
onChange(id);
}
setIsOpen(false);
};

return (
<>
<DrawerTrigger
applicationArea={applicationArea}
currentSelection={currentSelection}
setIsOpen={setIsOpen}
/>

<Drawer
className={styles.drawer}
open={isOpen}
onOpenChange={(_, { open }) => setIsOpen(open)}
>
<DrawerHeader
className={styles.header}
onClick={() => setIsOpen(false)}
>
<div className={styles.headerTitle}>
<ApplicationAreaIcon applicationArea={applicationArea} />
<Body1Strong>
{applicationAreaLabel(t, applicationArea)}
</Body1Strong>
</div>
<Button
data-testid={"application-drawer-dismiss"}
size="small"
appearance="subtle"
aria-label="Close"
icon={<Dismiss20Regular color={tokens.colorNeutralForeground3} />}
onClick={() => setIsOpen(false)}
/>
</DrawerHeader>

<DrawerBody className={styles.body}>
{link && (
<div className={styles.linkWrapper}>
<Link
data-testid={"application-drawer-link"}
className={styles.link}
href={link.url}
>
{link.text}
<ArrowRightRegular />
</Link>
</div>
)}
<div className={styles.content}>
{title}
<Divider />
{content?.map((c) => {
return (
<div className={styles.contentGroup} key={c.id}>
{c.children
? (
<ApplicationWithChildren
application={c}
currentSelectionId={currentSelection?.id ?? ""}
onChange={onClickItem}
applicationArea={applicationArea ?? ""}
/>
)
: (
<SingleApplication
application={c}
currentSelectionId={currentSelection?.id ?? ""}
onChange={onClickItem}
applicationArea={applicationArea}
/>
)}
<Divider className={styles.contentDivider} />
</div>
);
})}
</div>
</DrawerBody>
</Drawer>
</>
);
};
Loading

0 comments on commit 9b80daa

Please sign in to comment.