diff --git a/README.md b/README.md index 09a53242..6c88c90b 100644 --- a/README.md +++ b/README.md @@ -50,3 +50,16 @@ Run the production build command: ```bash npm run preview ``` + +## Custom SVG icons + +In order to generate custom SVG icons based on the Figma design, download the icon from Figma and place it +in the `icons/` folder. + +Then run: + +```bash +npm run generate-icons +``` + + diff --git a/icons/Continue.svg b/icons/Continue.svg new file mode 100644 index 00000000..b3d638d9 --- /dev/null +++ b/icons/Continue.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/Copilot.svg b/icons/Copilot.svg new file mode 100644 index 00000000..d5509498 --- /dev/null +++ b/icons/Copilot.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/Discord.svg b/icons/Discord.svg new file mode 100644 index 00000000..8cd6bf6d --- /dev/null +++ b/icons/Discord.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/Github.svg b/icons/Github.svg new file mode 100644 index 00000000..d7723086 --- /dev/null +++ b/icons/Github.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/Youtube.svg b/icons/Youtube.svg new file mode 100644 index 00000000..d345df73 --- /dev/null +++ b/icons/Youtube.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/package.json b/package.json index 10b241a4..c2d3aab6 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "preview": "vite preview", "test": "vitest", "test:coverage": "vitest run --coverage", - "type-check": "tsc --noEmit -p ./tsconfig.app.json" + "type-check": "tsc --noEmit -p ./tsconfig.app.json", + "generate-icons": "npx @svgr/cli --typescript --no-dimensions --jsx-runtime automatic --out-dir ./src/components/icons/ -- icons" }, "dependencies": { "@hey-api/client-fetch": "^0.6.0", diff --git a/src/App.test.tsx b/src/App.test.tsx index 41abf8f9..faf71670 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -20,41 +20,79 @@ describe("App", () => { render(); expect(screen.getByText(/toggle sidebar/i)).toBeVisible(); expect(screen.getByText("Certificates")).toBeVisible(); - expect(screen.getByText("Setup")).toBeVisible(); + expect(screen.getByText("Help")).toBeVisible(); expect(screen.getByRole("banner", { name: "App header" })).toBeVisible(); expect( - screen.getByRole("heading", { name: /codeGate dashboard/i }) + screen.getByRole("heading", { name: /codeGate dashboard/i }), ).toBeVisible(); + + await userEvent.click(screen.getByText("Certificates")); + expect( - screen.getByRole("link", { + screen.getByRole("menuitem", { name: /certificate security/i, - }) + }), ).toBeVisible(); expect( - screen.getByRole("link", { - name: /set up in continue/i, - }) + screen.getByRole("menuitem", { + name: /download/i, + }), ).toBeVisible(); + await userEvent.click(screen.getByText("Certificates")); + await userEvent.click(screen.getByText("Help")); + expect( - screen.getByRole("link", { - name: /set up in copilot/i, - }) + screen.getByRole("menuitem", { + name: /set up in continue/i, + }), ).toBeVisible(); + expect( - screen.getByRole("link", { - name: /download/i, - }) + screen.getByRole("menuitem", { + name: /set up in copilot/i, + }), ).toBeVisible(); + expect( - screen.getByRole("link", { + screen.getByRole("menuitem", { name: /documentation/i, - }) + }), ).toBeVisible(); + + const discordMenuItem = screen.getByRole("menuitem", { + name: /discord/i, + }); + expect(discordMenuItem).toBeVisible(); + expect(discordMenuItem).toHaveAttribute( + "href", + "https://discord.gg/stacklok", + ); + + const githubMenuItem = screen.getByRole("menuitem", { + name: /github/i, + }); + expect(githubMenuItem).toBeVisible(); + expect(githubMenuItem).toHaveAttribute( + "href", + "https://github.com/stacklok/codegate", + ); + + const youtubeMenuItem = screen.getByRole("menuitem", { + name: /youtube/i, + }); + expect(youtubeMenuItem).toBeVisible(); + expect(youtubeMenuItem).toHaveAttribute( + "href", + "https://www.youtube.com/@Stacklok", + ); + + await userEvent.click(screen.getByText("Help")); + await waitFor(() => expect( - screen.getByRole("link", { name: /codeGate dashboard/i }) - ).toBeVisible() + screen.getByRole("link", { name: /codeGate dashboard/i }), + ).toBeVisible(), ); }); @@ -63,8 +101,8 @@ describe("App", () => { await waitFor(() => expect( - screen.getByRole("link", { name: "CodeGate Dashboard" }) - ).toBeVisible() + screen.getByRole("link", { name: "CodeGate Dashboard" }), + ).toBeVisible(), ); const workspaceSelectionButton = screen.getByRole("button", { @@ -78,8 +116,8 @@ describe("App", () => { expect( screen.getByRole("option", { name: /anotherworkspae/i, - }) - ).toBeVisible() + }), + ).toBeVisible(), ); }); }); diff --git a/src/App.tsx b/src/App.tsx index fc50449a..14201770 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,29 +4,24 @@ import { usePromptsData } from "./hooks/usePromptsData"; import { Sidebar } from "./components/Sidebar"; import { useSse } from "./hooks/useSse"; import Page from "./Page"; -import { useHref, useNavigate } from "react-router-dom"; -import { RouterProvider } from "@stacklok/ui-kit"; function App() { const { data: prompts, isLoading } = usePromptsData(); - const navigate = useNavigate(); useSse(); return ( - -
- - - -
-
+
+ + + +
+
-
- -
+
+
- +
); } diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 2f28839c..5cb1a929 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,8 +1,10 @@ import { Link } from "react-router-dom"; import { SidebarTrigger } from "./ui/sidebar"; import { HoverPopover } from "./HoverPopover"; -import { Separator, ButtonDarkMode } from "@stacklok/ui-kit"; +import { Separator, ButtonDarkMode, MenuItem } from "@stacklok/ui-kit"; import { WorkspacesSelection } from "@/features/workspace/components/workspaces-selection"; +import { BookOpenText, Download, ShieldCheck } from "lucide-react"; +import { Continue, Copilot, Discord, Github, Youtube } from "./icons"; export function Header({ hasError }: { hasError?: boolean }) { return ( @@ -30,47 +32,57 @@ export function Header({ hasError }: { hasError?: boolean }) {
- }> + About certificate security + + } href="/certificates"> + Download certificates + + + + + }> + Set up in Continue + + } href="/help/copilot-setup"> + Set up in Copilot + + + } > - Download - - + + + + } > - Certificate Security - - + Discord + - - } > - Set up in Continue - - + + } > - Set up in Copilot - + YouTube + -
- -
-
diff --git a/src/components/HoverPopover.tsx b/src/components/HoverPopover.tsx index 036b6ce2..5197963c 100644 --- a/src/components/HoverPopover.tsx +++ b/src/components/HoverPopover.tsx @@ -1,25 +1,31 @@ -import { ReactNode } from "react"; -import { twMerge } from "tailwind-merge"; +import { Button, DropdownMenu, MenuTrigger, Popover } from "@stacklok/ui-kit"; +import { OverlayTriggerStateContext } from "react-aria-components"; +import { ReactNode, useContext } from "react"; +import { ChevronDown, ChevronUp } from "lucide-react"; + +function PopoverIcon() { + const { isOpen = false } = useContext(OverlayTriggerStateContext) ?? {}; + + return isOpen ? : ; +} export function HoverPopover({ children, title, - className }: { title: ReactNode; children: ReactNode; - className?: string + className?: string; }) { return ( -
-
+ +
-
-
- {children} -
-
-
+ + + + {children} + + ); } diff --git a/src/components/icons/Continue.tsx b/src/components/icons/Continue.tsx new file mode 100644 index 00000000..00acb879 --- /dev/null +++ b/src/components/icons/Continue.tsx @@ -0,0 +1,15 @@ +import type { SVGProps } from "react"; +const SvgContinue = (props: SVGProps) => ( + + + +); +export default SvgContinue; diff --git a/src/components/icons/Copilot.tsx b/src/components/icons/Copilot.tsx new file mode 100644 index 00000000..6f20d1eb --- /dev/null +++ b/src/components/icons/Copilot.tsx @@ -0,0 +1,15 @@ +import type { SVGProps } from "react"; +const SvgCopilot = (props: SVGProps) => ( + + + +); +export default SvgCopilot; diff --git a/src/components/icons/Discord.tsx b/src/components/icons/Discord.tsx new file mode 100644 index 00000000..8062dbd1 --- /dev/null +++ b/src/components/icons/Discord.tsx @@ -0,0 +1,15 @@ +import type { SVGProps } from "react"; +const SvgDiscord = (props: SVGProps) => ( + + + +); +export default SvgDiscord; diff --git a/src/components/icons/Github.tsx b/src/components/icons/Github.tsx new file mode 100644 index 00000000..e6f6d288 --- /dev/null +++ b/src/components/icons/Github.tsx @@ -0,0 +1,17 @@ +import type { SVGProps } from "react"; +const SvgGithub = (props: SVGProps) => ( + + + +); +export default SvgGithub; diff --git a/src/components/icons/Youtube.tsx b/src/components/icons/Youtube.tsx new file mode 100644 index 00000000..b3aeda65 --- /dev/null +++ b/src/components/icons/Youtube.tsx @@ -0,0 +1,17 @@ +import type { SVGProps } from "react"; +const SvgYoutube = (props: SVGProps) => ( + + + +); +export default SvgYoutube; diff --git a/src/components/icons/index.ts b/src/components/icons/index.ts new file mode 100644 index 00000000..b1107dfe --- /dev/null +++ b/src/components/icons/index.ts @@ -0,0 +1,5 @@ +export { default as Continue } from "./Continue"; +export { default as Copilot } from "./Copilot"; +export { default as Discord } from "./Discord"; +export { default as Github } from "./Github"; +export { default as Youtube } from "./Youtube";