Skip to content

Commit 56f8f0d

Browse files
authored
feat(topbar): update application selector to align with MySystems, MyAxis, MyPartners and MyBusiness (#123)
1 parent 26ba4ea commit 56f8f0d

File tree

7 files changed

+180
-74
lines changed

7 files changed

+180
-74
lines changed

components/topbar/src/application-menu.tsx

Lines changed: 92 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { ApplicationMenuProps } from "./application-menu.types";
1616
import { useStyles } from "./application.styles";
1717
import { defaultMySystemsAppData, isMySystemsAppId } from "./applications";
1818
import { TranslationFn, useTranslation } from "./translation-context";
19+
import { ApplicationArea } from "./top-bar.types";
1920

2021
const ApplicationIcon = bundleIcon(BoxFilled, BoxRegular);
2122

@@ -26,19 +27,22 @@ function appLabel(t: TranslationFn, id: string): string {
2627
return id;
2728
}
2829

29-
function appIcon(id: string): JSX.Element {
30+
function appIcon(id: string, filled = false): JSX.Element {
3031
if (isMySystemsAppId(id)) {
31-
return defaultMySystemsAppData[id].icon;
32+
return filled
33+
? defaultMySystemsAppData[id].filledIcon
34+
: defaultMySystemsAppData[id].icon;
3235
}
33-
return <ApplicationIcon />;
36+
return <ApplicationIcon filled={filled} />;
3437
}
3538

3639
export const ApplicationMenu = ({
3740
customContent,
3841
options,
3942
value,
4043
onChange,
41-
}: ApplicationMenuProps) => {
44+
applicationArea,
45+
}: ApplicationMenuProps & { applicationArea?: ApplicationArea }) => {
4246
const { t } = useTranslation();
4347
const styles = useStyles();
4448

@@ -48,68 +52,95 @@ export const ApplicationMenu = ({
4852
const onlyCustomContent = !options?.length && !!customContent;
4953

5054
return (
51-
<Menu>
52-
<MenuTrigger>
53-
<MenuButton
54-
appearance="subtle"
55+
<>
56+
<div
57+
className={mergeClasses(
58+
styles.menuRectangle,
59+
applicationArea === "mySystems"
60+
&& styles.mySystemsMenuRectangle,
61+
applicationArea === "myAxis" && styles.myAxisMenuRectangle,
62+
applicationArea === "myBusiness" && styles.myBusinessMenuRectangle,
63+
applicationArea === "myPartners" && styles.myPartnersMenuRectangle
64+
)}
65+
>
66+
<div
5567
className={mergeClasses(
56-
isStandalone && styles.standalone,
57-
styles.singleLine
68+
styles.currentIcon,
69+
applicationArea !== undefined && styles.applicationAreaIcon
5870
)}
59-
icon={currentSelection
60-
? currentSelection.icon ?? appIcon(currentSelection.id)
61-
: undefined}
62-
data-testid="application-menu-trigger"
63-
menuIcon={isStandalone ? null : undefined}
6471
>
65-
<span className={styles.applicationLabel}>
66-
{currentSelection
67-
? currentSelection.label ?? appLabel(t, currentSelection.id)
68-
// FIXME: use translateable placeholder
69-
: ""}
70-
{currentSelection?.beta && <BetaBadge />}
71-
</span>
72-
</MenuButton>
73-
</MenuTrigger>
74-
<MenuPopover>
75-
<MenuList>
76-
{customContent !== undefined && (
77-
<>
78-
{customContent}
79-
{!onlyCustomContent && <MenuDivider />}
80-
</>
81-
)}
82-
{options?.map(({ id, icon, label, beta }) => (
83-
<MenuItem
84-
data-testid={`application-menu-item-${id}`}
85-
icon={
86-
<div
87-
className={mergeClasses(id === value && styles.selectedApp)}
88-
>
89-
{icon ?? appIcon(id)}
90-
</div>
91-
}
92-
key={id}
93-
// eslint-disable-next-line react/jsx-no-bind
94-
onClick={() => {
95-
if (currentSelection?.id !== id) {
96-
onChange?.(id);
72+
{currentSelection
73+
? currentSelection.icon
74+
?? appIcon(currentSelection.id, applicationArea !== undefined)
75+
: undefined}
76+
</div>
77+
</div>
78+
<Menu
79+
positioning={{
80+
offset: applicationArea !== undefined
81+
? { mainAxis: 2, crossAxis: 36 }
82+
: undefined,
83+
}}
84+
>
85+
<MenuTrigger>
86+
<MenuButton
87+
appearance="subtle"
88+
className={mergeClasses(
89+
isStandalone && styles.standalone,
90+
styles.singleLine
91+
)}
92+
data-testid="application-menu-trigger"
93+
menuIcon={isStandalone ? null : undefined}
94+
>
95+
<span className={styles.applicationLabel}>
96+
{currentSelection
97+
? currentSelection.label ?? appLabel(t, currentSelection.id)
98+
// FIXME: use translateable placeholder
99+
: ""}
100+
{currentSelection?.beta && <BetaBadge />}
101+
</span>
102+
</MenuButton>
103+
</MenuTrigger>
104+
<MenuPopover className={styles.menuPopover}>
105+
<MenuList>
106+
{customContent !== undefined && (
107+
<>
108+
{customContent}
109+
{!onlyCustomContent && <MenuDivider />}
110+
</>
111+
)}
112+
{options?.map(({ id, icon, label, beta }) => (
113+
<MenuItem
114+
data-testid={`application-menu-item-${id}`}
115+
icon={
116+
<div
117+
className={mergeClasses(id === value && styles.selectedApp)}
118+
>
119+
{icon ?? appIcon(id)}
120+
</div>
97121
}
98-
}}
99-
>
100-
<Text
101-
className={mergeClasses(
102-
id === value && styles.selectedAppLabel
103-
)}
122+
key={id}
123+
// eslint-disable-next-line react/jsx-no-bind
124+
onClick={() => {
125+
if (currentSelection?.id !== id) {
126+
onChange?.(id);
127+
}
128+
}}
104129
>
105-
{label ?? appLabel(t, id)}
106-
</Text>
107-
{beta && <BetaBadge />}
108-
</MenuItem>
109-
))}
110-
</MenuList>
111-
</MenuPopover>
112-
</Menu>
130+
<Text
131+
className={mergeClasses(
132+
id === value && styles.selectedAppLabel
133+
)}
134+
>
135+
{label ?? appLabel(t, id)}
136+
</Text>
137+
{beta && <BetaBadge />}
138+
</MenuItem>
139+
))}
140+
</MenuList>
141+
</MenuPopover>
142+
</Menu>
143+
</>
113144
);
114145
};
115146

components/topbar/src/application.styles.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
makeStyles,
3+
shorthands,
34
tokens,
45
typographyStyles,
56
} from "@fluentui/react-components";
@@ -16,13 +17,15 @@ export const useStyles = makeStyles({
1617
pointerEvents: "none",
1718
},
1819
singleLine: {
20+
boxSizing: "border-box",
1921
whiteSpace: "nowrap",
2022
overflowX: "hidden",
2123
},
2224
applicationLabel: {
2325
overflowX: "hidden",
2426
whiteSpace: "nowrap",
2527
textOverflow: "ellipsis",
28+
marginLeft: tokens.spacingHorizontalXXXL,
2629
},
2730
beta: {
2831
marginLeft: "8px",
@@ -39,6 +42,42 @@ export const useStyles = makeStyles({
3942
display: "inline",
4043
},
4144
},
45+
menuPopover: {
46+
...shorthands.borderRadius("1px", "4px", "4px", "4px"),
47+
},
48+
currentIcon: {
49+
fontSize: "20px",
50+
},
51+
applicationAreaIcon: {
52+
fontSize: "24px",
53+
},
54+
menuRectangle: {
55+
display: "flex",
56+
pointerEvents: "none",
57+
position: "absolute",
58+
width: "34px",
59+
height: "32px",
60+
alignItems: "center",
61+
justifyContent: "center",
62+
flexShrink: 0,
63+
...shorthands.borderRadius("6px", "6px", "1px", "6px"),
64+
},
65+
mySystemsMenuRectangle: {
66+
color: "#CCEBF8",
67+
backgroundColor: "#008DC6",
68+
},
69+
myAxisMenuRectangle: {
70+
color: "#FFF5D6",
71+
backgroundColor: "#DFA001",
72+
},
73+
myPartnersMenuRectangle: {
74+
color: "#E8F4D9",
75+
backgroundColor: "#7FB239",
76+
},
77+
myBusinessMenuRectangle: {
78+
color: "#F7CEE8",
79+
backgroundColor: "#C10B7E",
80+
},
4281
selectedAppLabel: {
4382
...typographyStyles.body1Strong,
4483
color: tokens.colorNeutralForeground1,

components/topbar/src/applications.tsx

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,36 @@ export interface MySystemsAppData {
5656

5757
export const defaultMySystemsAppData: Record<
5858
MySystemsAppId,
59-
MySystemsAppData
59+
MySystemsAppData & { filledIcon: JSX.Element }
6060
> = {
61-
dm: { icon: <SystemManagementIcon />, labelKey: "app_dm" },
62-
iam: { icon: <UserManagementIcon />, labelKey: "app_iam" },
63-
lm: { icon: <LicenseManagementIcon />, labelKey: "app_lm" },
64-
portal: { icon: <MySystemsIcon />, labelKey: "app_portal" },
61+
dm: {
62+
icon: <SystemManagementIcon />,
63+
filledIcon: <SystemManagementIcon filled />,
64+
labelKey: "app_dm",
65+
},
66+
iam: {
67+
icon: <UserManagementIcon />,
68+
filledIcon: <UserManagementIcon filled />,
69+
labelKey: "app_iam",
70+
},
71+
lm: {
72+
icon: <LicenseManagementIcon />,
73+
filledIcon: <LicenseManagementIcon filled />,
74+
labelKey: "app_lm",
75+
},
76+
portal: {
77+
icon: <MySystemsIcon />,
78+
filledIcon: <MySystemsIcon filled />,
79+
labelKey: "app_portal",
80+
},
6581
shm: {
6682
icon: <SystemHealthMonitoringIcon />,
83+
filledIcon: <SystemHealthMonitoringIcon filled />,
6784
labelKey: "app_shm",
6885
},
69-
video: { icon: <VideoOperationIcon />, labelKey: "app_video" },
86+
video: {
87+
icon: <VideoOperationIcon />,
88+
filledIcon: <VideoOperationIcon filled />,
89+
labelKey: "app_video",
90+
},
7091
};

components/topbar/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ export type {
2323
ThemeSubmenuProps,
2424
} from "./profile-menu.types";
2525
export { TopBar } from "./top-bar";
26-
export type { TopBarProps } from "./top-bar.types";
26+
export type { ApplicationArea, TopBarProps } from "./top-bar.types";

components/topbar/src/top-bar.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ import { TranslationProvider } from "./translation-provider";
99

1010
export const TopBar = forwardRef(
1111
(props: TopBarProps, ref: ForwardedRef<HTMLDivElement>) => {
12-
const { appMenu, orgMenu, profileMenu, customContent, leftCustomContent } =
13-
props;
12+
const {
13+
appMenu,
14+
orgMenu,
15+
profileMenu,
16+
customContent,
17+
leftCustomContent,
18+
applicationArea,
19+
} = props;
1420
const styles = useTopBarStyles();
1521

1622
const rootStyle = mergeClasses(topBarClassNames.root, styles.root);
@@ -32,7 +38,9 @@ export const TopBar = forwardRef(
3238
<TranslationProvider locale={locale}>
3339
<div className={rootStyle} ref={ref}>
3440
<div className={leftSectionStyle}>
35-
{appMenu !== undefined && <ApplicationMenu {...appMenu} />}
41+
{appMenu !== undefined && (
42+
<ApplicationMenu {...appMenu} applicationArea={applicationArea} />
43+
)}
3644
{leftCustomContent !== undefined && (
3745
<>
3846
{appMenu !== undefined && (

components/topbar/src/top-bar.types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@ import { ApplicationMenuProps } from "./application-menu.types";
22
import { OrganizationMenuProps } from "./organization-menu.types";
33
import { ProfileMenuProps } from "./profile-menu.types";
44

5+
export type ApplicationArea =
6+
| "mySystems"
7+
| "myAxis"
8+
| "myBusiness"
9+
| "myPartners";
10+
511
export type TopBarProps = {
612
readonly appMenu?: ApplicationMenuProps;
713
readonly children?: ReadonlyArray<never>;
814
readonly customContent?: JSX.Element;
915
readonly leftCustomContent?: JSX.Element;
1016
readonly orgMenu?: OrganizationMenuProps;
1117
readonly profileMenu?: ProfileMenuProps;
18+
readonly applicationArea?: ApplicationArea;
1219
};

examples/src/components/top-bar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import {
2121
} from "@fluentui/react-components";
2222
import {
2323
AddRegular,
24-
BoxFilled,
25-
BoxRegular,
24+
AnimalCat20Filled,
25+
AnimalCat20Regular,
2626
bundleIcon,
2727
FoodApple24Regular,
2828
MailFilled,
@@ -34,7 +34,7 @@ import {
3434
import React, { useCallback, useState } from "react";
3535
import { useAppContext } from "../context/ApplicationStateProvider";
3636

37-
const ApplicationIcon = bundleIcon(BoxFilled, BoxRegular);
37+
const ApplicationIcon = bundleIcon(AnimalCat20Filled, AnimalCat20Regular);
3838
const MailIcon = bundleIcon(MailFilled, MailRegular);
3939

4040
const useStyles = makeStyles({

0 commit comments

Comments
 (0)