feat(site): add an organization switcher to the user menu (#13269)

This commit is contained in:
Kayla Washburn-Love
2024-05-15 13:14:34 -06:00
committed by GitHub
parent 1f5788feff
commit fc6f18aa96
6 changed files with 67 additions and 2 deletions

View File

@ -249,3 +249,10 @@ export const updateAppearanceSettings = (
},
};
};
export const myOrganizations = () => {
return {
queryKey: ["organizations", "me"],
queryFn: () => API.getOrganizations(),
};
};

View File

@ -67,7 +67,7 @@ export const RequireAuth: FC = () => {
};
type RequireKeys<T, R extends keyof T> = Omit<T, R> & {
[K in keyof Pick<T, R>]: NonNullable<T[K]>;
[K in keyof Pick<T, R>]-?: NonNullable<T[K]>;
};
// We can do some TS magic here but I would rather to be explicit on what

View File

@ -1,6 +1,7 @@
import type { Meta, StoryObj } from "@storybook/react";
import { chromaticWithTablet } from "testHelpers/chromatic";
import { MockUser, MockUser2 } from "testHelpers/entities";
import { withDashboardProvider } from "testHelpers/storybook";
import { NavbarView } from "./NavbarView";
const meta: Meta<typeof NavbarView> = {
@ -10,6 +11,7 @@ const meta: Meta<typeof NavbarView> = {
args: {
user: MockUser,
},
decorators: [withDashboardProvider],
};
export default meta;

View File

@ -1,6 +1,7 @@
import type { Meta, StoryObj } from "@storybook/react";
import { expect, screen, userEvent, within, waitFor } from "@storybook/test";
import { MockBuildInfo, MockUser } from "testHelpers/entities";
import { withDashboardProvider } from "testHelpers/storybook";
import { UserDropdown } from "./UserDropdown";
const meta: Meta<typeof UserDropdown> = {
@ -16,6 +17,7 @@ const meta: Meta<typeof UserDropdown> = {
{ icon: "/icon/aws.svg", name: "Amazon Web Services", target: "" },
],
},
decorators: [withDashboardProvider],
};
export default meta;

View File

@ -1,6 +1,8 @@
import { css, type Interpolation, type Theme, useTheme } from "@emotion/react";
import Badge from "@mui/material/Badge";
import type { FC } from "react";
import { useQuery } from "react-query";
import { myOrganizations } from "api/queries/users";
import type * as TypesGen from "api/typesGenerated";
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
import {
@ -9,6 +11,7 @@ import {
PopoverTrigger,
} from "components/Popover/Popover";
import { UserAvatar } from "components/UserAvatar/UserAvatar";
import { useDashboard } from "modules/dashboard/useDashboard";
import { BUTTON_SM_HEIGHT, navHeight } from "theme/constants";
import { UserDropdownContent } from "./UserDropdownContent";
@ -26,6 +29,11 @@ export const UserDropdown: FC<UserDropdownProps> = ({
onSignOut,
}) => {
const theme = useTheme();
const organizationsQuery = useQuery({
...myOrganizations(),
enabled: Boolean(localStorage.getItem("enableMultiOrganizationUi")),
});
const { organizationId, setOrganizationId } = useDashboard();
return (
<Popover>
@ -63,6 +71,9 @@ export const UserDropdown: FC<UserDropdownProps> = ({
user={user}
buildInfo={buildInfo}
supportLinks={supportLinks}
organizations={organizationsQuery.data}
organizationId={organizationId}
setOrganizationId={setOrganizationId}
onSignOut={onSignOut}
/>
</PopoverContent>

View File

@ -84,14 +84,20 @@ const styles = {
export interface UserDropdownContentProps {
user: TypesGen.User;
organizations?: TypesGen.Organization[];
organizationId?: string;
setOrganizationId?: (id: string) => void;
buildInfo?: TypesGen.BuildInfoResponse;
supportLinks?: readonly TypesGen.LinkConfig[];
onSignOut: () => void;
}
export const UserDropdownContent: FC<UserDropdownContentProps> = ({
buildInfo,
user,
organizations,
organizationId,
setOrganizationId,
buildInfo,
supportLinks,
onSignOut,
}) => {
@ -128,6 +134,43 @@ export const UserDropdownContent: FC<UserDropdownContentProps> = ({
<Divider css={{ marginBottom: 8 }} />
{organizations && (
<>
<div>
<div
css={{
padding: "8px 20px 6px",
textTransform: "uppercase",
letterSpacing: 1.1,
lineHeight: 1.1,
fontSize: "0.8em",
}}
>
My teams
</div>
{organizations.map((org) => (
<MenuItem
key={org.id}
css={styles.menuItem}
onClick={() => {
setOrganizationId?.(org.id);
popover.setIsOpen(false);
}}
>
{/* <LogoutIcon css={styles.menuItemIcon} /> */}
<Stack direction="row" spacing={1} css={styles.menuItemText}>
{org.name}
{organizationId === org.id && (
<span css={{ fontSize: 12, color: "gray" }}>Current</span>
)}
</Stack>
</MenuItem>
))}
</div>
<Divider css={{ marginTop: 8, marginBottom: 8 }} />
</>
)}
<Link to="/settings/account" css={styles.link}>
<MenuItem css={styles.menuItem} onClick={onPopoverClose}>
<AccountIcon css={styles.menuItemIcon} />