diff --git a/site/src/modules/management/OrganizationSidebarView.stories.tsx b/site/src/modules/management/OrganizationSidebarView.stories.tsx index 4f1b17a27c181..6533a5e004ef5 100644 --- a/site/src/modules/management/OrganizationSidebarView.stories.tsx +++ b/site/src/modules/management/OrganizationSidebarView.stories.tsx @@ -1,5 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, userEvent, waitFor, within } from "@storybook/test"; +import type { AuthorizationResponse } from "api/typesGenerated"; import { MockNoPermissions, MockOrganization, @@ -7,7 +8,10 @@ import { MockPermissions, } from "testHelpers/entities"; import { withDashboardProvider } from "testHelpers/storybook"; -import { OrganizationSidebarView } from "./OrganizationSidebarView"; +import { + OrganizationSidebarView, + type OrganizationWithPermissions, +} from "./OrganizationSidebarView"; const meta: Meta = { title: "modules/management/OrganizationSidebarView", @@ -286,3 +290,114 @@ export const OrgsDisabled: Story = { showOrganizations: false, }, }; + +const commonPerms: AuthorizationResponse = { + editOrganization: true, + editMembers: true, + editGroups: true, + auditOrganization: true, +}; + +const activeOrganization: OrganizationWithPermissions = { + ...MockOrganization, + display_name: "Omega org", + name: "omega", + id: "1", + permissions: { + ...commonPerms, + }, +}; + +export const OrgsSortedAlphabetically: Story = { + args: { + activeOrganization, + permissions: { + ...MockPermissions, + createOrganization: true, + }, + organizations: [ + { + ...MockOrganization, + display_name: "Zeta Org", + id: "2", + name: "zeta", + permissions: commonPerms, + }, + { + ...MockOrganization, + display_name: "alpha Org", + id: "3", + name: "alpha", + permissions: commonPerms, + }, + activeOrganization, + ], + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getByRole("button", { name: /Omega org/i })); + + // dropdown is not in #storybook-root so must query full document + const globalScreen = within(document.body); + + await waitFor(() => { + expect(globalScreen.queryByText("alpha Org")).toBeInTheDocument(); + expect(globalScreen.queryByText("Zeta Org")).toBeInTheDocument(); + }); + + const orgElements = globalScreen.getAllByRole("option"); + // filter out Create btn + const filteredElems = orgElements.slice(0, 3); + + const orgNames = filteredElems.map( + // handling fuzzy matching + (el) => el.textContent?.replace(/^[A-Z]/, "").trim() || "", + ); + + // active name first + expect(orgNames).toEqual(["Omega org", "alpha Org", "Zeta Org"]); + }, +}; + +export const SearchForOrg: Story = { + args: { + activeOrganization, + permissions: MockPermissions, + organizations: [ + { + ...MockOrganization, + display_name: "Zeta Org", + id: "2", + name: "zeta", + permissions: commonPerms, + }, + { + ...MockOrganization, + display_name: "alpha Org", + id: "3", + name: "fish", + permissions: commonPerms, + }, + activeOrganization, + ], + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getByRole("button", { name: /Omega org/i })); + + // dropdown is not in #storybook-root so must query full document + const globalScreen = within(document.body); + const searchInput = + await globalScreen.getByPlaceholderText("Find organization"); + + await userEvent.type(searchInput, "ALPHA"); + + const filteredResult = await globalScreen.findByText("alpha Org"); + expect(filteredResult).toBeInTheDocument(); + + // Omega org remains visible as the default org + await waitFor(() => { + expect(globalScreen.queryByText("Zeta Org")).not.toBeInTheDocument(); + }); + }, +}; diff --git a/site/src/modules/management/OrganizationSidebarView.tsx b/site/src/modules/management/OrganizationSidebarView.tsx index 8d913edf87df3..b618c4f72bd3d 100644 --- a/site/src/modules/management/OrganizationSidebarView.tsx +++ b/site/src/modules/management/OrganizationSidebarView.tsx @@ -3,9 +3,12 @@ import { Avatar } from "components/Avatar/Avatar"; import { Button } from "components/Button/Button"; import { Command, + CommandEmpty, CommandGroup, + CommandInput, CommandItem, CommandList, + CommandSeparator, } from "components/Command/Command"; import { Loader } from "components/Loader/Loader"; import { @@ -88,11 +91,15 @@ const OrganizationsSettingsNavigation: FC< return ; } - // Sort organizations to put active organization first - const sortedOrganizations = [ - activeOrganization, - ...organizations.filter((org) => org.id !== activeOrganization.id), - ]; + const sortedOrganizations = [...organizations].sort((a, b) => { + // active org first + if (a.id === activeOrganization.id) return -1; + if (b.id === activeOrganization.id) return 1; + + return a.display_name + .toLowerCase() + .localeCompare(b.display_name.toLowerCase()); + }); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const navigate = useNavigate(); @@ -123,14 +130,16 @@ const OrganizationsSettingsNavigation: FC< + + No organization found. {sortedOrganizations.length > 1 && (
{sortedOrganizations.map((organization) => ( { setIsPopoverOpen(false); navigate(urlForSubpage(organization.name)); @@ -158,11 +167,11 @@ const OrganizationsSettingsNavigation: FC< ))}
)} - {permissions.createOrganization && ( - <> - {organizations.length > 1 && ( -
- )} +
+ {permissions.createOrganization && ( + <> + {organizations.length > 1 && } + { @@ -174,9 +183,9 @@ const OrganizationsSettingsNavigation: FC< > Create Organization - - )} - + + + )}