• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stacklok / codegate-ui / 13261729647

11 Feb 2025 11:19AM UTC coverage: 68.226% (-0.5%) from 68.676%
13261729647

Pull #276

github

web-flow
Merge 5f42a30de into 7529f7310
Pull Request #276: feat: implement "Revert" button for workspace name

401 of 671 branches covered (59.76%)

Branch coverage included in aggregate %.

57 of 76 new or added lines in 11 files covered. (75.0%)

3 existing lines in 1 file now uncovered.

868 of 1189 relevant lines covered (73.0%)

73.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.78
/src/features/workspace/components/workspaces-selection.tsx
1
import { useListWorkspaces } from "@/features/workspace/hooks/use-list-workspaces";
2
import {
3
  Button,
4
  DialogTrigger,
5
  Input,
6
  LinkButton,
7
  ListBox,
8
  ListBoxItem,
9
  Popover,
10
  SearchField,
11
  Separator,
12
} from "@stacklok/ui-kit";
13
import { useQueryClient } from "@tanstack/react-query";
14
import { useState } from "react";
15
import { useMutationActivateWorkspace } from "../hooks/use-mutation-activate-workspace";
16
import clsx from "clsx";
17
import { useActiveWorkspaceName } from "../hooks/use-active-workspace-name";
18
import { hrefs } from "@/lib/hrefs";
19
import { twMerge } from "tailwind-merge";
20
import ChevronDown from "@untitled-ui/icons-react/build/cjs/ChevronDown";
21
import { SearchMd, Settings01 } from "@untitled-ui/icons-react";
22

23
export function WorkspacesSelection() {
24
  const queryClient = useQueryClient();
9✔
25

26
  const { data: workspacesResponse } = useListWorkspaces();
9✔
27
  const { mutateAsync: activateWorkspace } = useMutationActivateWorkspace();
9✔
28

29
  const { data: activeWorkspaceName } = useActiveWorkspaceName();
9✔
30

31
  const [isOpen, setIsOpen] = useState(false);
9✔
32
  const [searchWorkspace, setSearchWorkspace] = useState("");
9✔
33
  const workspaces = workspacesResponse?.workspaces ?? [];
9✔
34
  const filteredWorkspaces = workspaces.filter((workspace) =>
9✔
35
    workspace.name.toLowerCase().includes(searchWorkspace.toLowerCase()),
20✔
36
  );
37

38
  const handleWorkspaceClick = (name: string) => {
9✔
39
    activateWorkspace({ body: { name } }).then(() => {
×
40
      // eslint-disable-next-line no-restricted-syntax
41
      queryClient.invalidateQueries({ refetchType: "all" }); // Global setting, refetch **everything**
×
42
      setIsOpen(false);
×
43
    });
44
  };
45

46
  return (
47
    <DialogTrigger isOpen={isOpen} onOpenChange={(test) => setIsOpen(test)}>
1✔
48
      <Button variant="tertiary" className="flex cursor-pointer">
49
        Active workspace{" "}
50
        <span className="font-bold">{activeWorkspaceName ?? "default"}</span>
13✔
51
        <ChevronDown />
52
      </Button>
53

54
      <Popover className="w-[32rem] p-3" placement="bottom left">
55
        <div>
56
          <div>
57
            <SearchField
58
              onChange={setSearchWorkspace}
59
              autoFocus
60
              aria-label="search"
61
            >
62
              <Input icon={<SearchMd />} />
63
            </SearchField>
64
          </div>
65

66
          <ListBox
67
            aria-label="Workspaces"
68
            items={filteredWorkspaces}
69
            selectedKeys={activeWorkspaceName ? [activeWorkspaceName] : []}
9✔
70
            onAction={(v) => {
UNCOV
71
              handleWorkspaceClick(v?.toString());
×
72
            }}
73
            className="-mx-1 my-2 max-h-80 overflow-auto"
74
            renderEmptyState={() => (
75
              <p className="text-center">No workspaces found</p>
76
            )}
77
          >
78
            {(item) => (
79
              <ListBoxItem
80
                id={item.name}
81
                key={item.name}
82
                textValue={item.name}
83
                data-is-selected={item.name === activeWorkspaceName}
84
                className={clsx(
85
                  "grid grid-cols-[auto_1.5rem] group/selector cursor-pointer py-2 m-1 text-base hover:bg-gray-200 rounded-sm",
86
                  {
87
                    "!bg-gray-900 hover:bg-gray-900 !text-gray-25 hover:!text-gray-25":
88
                      item.is_active,
89
                  },
90
                )}
91
              >
92
                <span className="block truncate">{item.name}</span>
93

94
                <LinkButton
95
                  href={hrefs.workspaces.edit(item.name)}
UNCOV
96
                  onPress={() => setIsOpen(false)}
×
97
                  isIcon
98
                  variant="tertiary"
99
                  className={twMerge(
100
                    "ml-auto size-6 group-hover/selector:opacity-100 opacity-0 transition-opacity",
101
                    item.is_active
4✔
102
                      ? "hover:bg-gray-800 pressed:bg-gray-700"
103
                      : "hover:bg-gray-50 hover:text-primary",
104
                  )}
105
                >
106
                  <Settings01
107
                    className={twMerge(
108
                      item.is_active ? "text-gray-25" : "text-secondary",
4✔
109
                    )}
110
                  />
111
                </LinkButton>
112
              </ListBoxItem>
113
            )}
114
          </ListBox>
115
          <Separator className="" />
116
          <LinkButton
117
            href="/workspaces"
UNCOV
118
            onPress={() => setIsOpen(false)}
×
119
            variant="tertiary"
120
            className="text-secondary h-10 pl-2 gap-2 flex mt-2 justify-start"
121
          >
122
            <Settings01 />
123
            Manage Workspaces
124
          </LinkButton>
125
        </div>
126
      </Popover>
127
    </DialogTrigger>
128
  );
129
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc