diff --git a/site/.eslintrc.yaml b/site/.eslintrc.yaml index 5f8386529919a..d6c6be9eb6b95 100644 --- a/site/.eslintrc.yaml +++ b/site/.eslintrc.yaml @@ -92,8 +92,6 @@ rules: message: "Use path imports to avoid pulling in unused modules. See: https://material-ui.com/guides/minimizing-bundle-size/" - - name: "@material-ui/core/Tooltip" - message: "Use the custom Tooltip on componens/Tooltip" no-storage/no-browser-storage: error no-unused-vars: "off" "object-curly-spacing": "off" diff --git a/site/components/Button/CopyButton.tsx b/site/components/Button/CopyButton.tsx new file mode 100644 index 0000000000000..b10a6088da285 --- /dev/null +++ b/site/components/Button/CopyButton.tsx @@ -0,0 +1,68 @@ +import { makeStyles } from "@material-ui/core/styles" +import Button from "@material-ui/core/Button" +import Tooltip from "@material-ui/core/Tooltip" +import Check from "@material-ui/icons/Check" +import React, { useState } from "react" +import { FileCopy } from "../Icons" + +interface CopyButtonProps { + text: string + className?: string +} + +/** + * Copy button used inside the CodeBlock component internally + */ +export const CopyButton: React.FC = ({ className = "", text }) => { + const styles = useStyles() + const [isCopied, setIsCopied] = useState(false) + + const copyToClipboard = async (): Promise => { + try { + await window.navigator.clipboard.writeText(text) + setIsCopied(true) + + window.setTimeout(() => { + setIsCopied(false) + }, 1000) + } catch (err) { + const wrappedErr = new Error("copyToClipboard: failed to copy text to clipboard") + if (err instanceof Error) { + wrappedErr.stack = err.stack + } + console.error(wrappedErr) + } + } + + return ( + +
+ +
+
+ ) +} + +const useStyles = makeStyles((theme) => ({ + copyButtonWrapper: { + display: "flex", + marginLeft: theme.spacing(1), + }, + copyButton: { + borderRadius: 7, + background: theme.palette.codeBlock.button.main, + color: theme.palette.codeBlock.button.contrastText, + padding: theme.spacing(0.85), + minWidth: 32, + + "&:hover": { + background: theme.palette.codeBlock.button.hover, + }, + }, + fileCopyIcon: { + width: 20, + height: 20, + }, +})) diff --git a/site/components/Button/index.ts b/site/components/Button/index.ts index 318c0c373932d..a0845e5d310f1 100644 --- a/site/components/Button/index.ts +++ b/site/components/Button/index.ts @@ -1,2 +1,3 @@ export * from "./SplitButton" export * from "./LoadingButton" +export * from "./CopyButton" diff --git a/site/components/CodeBlock/index.stories.tsx b/site/components/CodeBlock/index.stories.tsx index b31d450e21246..86e14ffca2856 100644 --- a/site/components/CodeBlock/index.stories.tsx +++ b/site/components/CodeBlock/index.stories.tsx @@ -12,7 +12,7 @@ export default { title: "CodeBlock", component: CodeBlock, argTypes: { - lines: { control: "object", defaultValue: sampleLines }, + lines: { control: "text", defaultValue: sampleLines }, }, } diff --git a/site/components/CodeBlock/index.tsx b/site/components/CodeBlock/index.tsx index 6b8b1afd336a1..e32468f705afc 100644 --- a/site/components/CodeBlock/index.tsx +++ b/site/components/CodeBlock/index.tsx @@ -1,5 +1,6 @@ import { makeStyles } from "@material-ui/core/styles" import React from "react" +import { MONOSPACE_FONT_FAMILY } from "../../theme/constants" export interface CodeBlockProps { lines: string[] @@ -18,8 +19,6 @@ export const CodeBlock: React.FC = ({ lines }) => { ) } -const MONOSPACE_FONT_FAMILY = - "'Fira Code', 'Lucida Console', 'Lucida Sans Typewriter', 'Liberation Mono', 'Monaco', 'Courier New', Courier, monospace" const useStyles = makeStyles((theme) => ({ root: { diff --git a/site/components/CodeExample/CodeExample.stories.tsx b/site/components/CodeExample/CodeExample.stories.tsx new file mode 100644 index 0000000000000..4eafb626a83d2 --- /dev/null +++ b/site/components/CodeExample/CodeExample.stories.tsx @@ -0,0 +1,20 @@ +import { Story } from "@storybook/react" +import React from "react" +import { CodeExample, CodeExampleProps } from "./CodeExample" + +const sampleCode = `echo "Hello, world"` + +export default { + title: "CodeExample", + component: CodeExample, + argTypes: { + code: { control: "string", defaultValue: sampleCode }, + }, +} + +const Template: Story = (args: CodeExampleProps) => + +export const Example = Template.bind({}) +Example.args = { + code: sampleCode, +} diff --git a/site/components/CodeExample/CodeExample.test.tsx b/site/components/CodeExample/CodeExample.test.tsx new file mode 100644 index 0000000000000..7618f29d38f34 --- /dev/null +++ b/site/components/CodeExample/CodeExample.test.tsx @@ -0,0 +1,15 @@ +import { screen } from "@testing-library/react" +import { render } from "../../test_helpers" +import React from "react" +import { CodeExample } from "./CodeExample" + +describe("CodeExample", () => { + it("renders code", async () => { + // When + render() + + // Then + // Both lines should be rendered + await screen.findByText("echo hello") + }) +}) diff --git a/site/components/CodeExample/CodeExample.tsx b/site/components/CodeExample/CodeExample.tsx new file mode 100644 index 0000000000000..c3145af8f349b --- /dev/null +++ b/site/components/CodeExample/CodeExample.tsx @@ -0,0 +1,38 @@ +import { makeStyles } from "@material-ui/core/styles" +import React from "react" +import { MONOSPACE_FONT_FAMILY } from "../../theme/constants" + +import { CopyButton } from "../Button" + +export interface CodeExampleProps { + code: string +} + +/** + * Component to show single-line code examples, with a copy button + */ +export const CodeExample: React.FC = ({ code }) => { + const styles = useStyles() + + return ( +
+ {code} + +
+ ) +} + +const useStyles = makeStyles((theme) => ({ + root: { + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + background: theme.palette.background.default, + color: theme.palette.codeBlock.contrastText, + fontFamily: MONOSPACE_FONT_FAMILY, + fontSize: 13, + padding: theme.spacing(2), + borderRadius: theme.shape.borderRadius, + }, +})) diff --git a/site/components/CodeExample/index.ts b/site/components/CodeExample/index.ts new file mode 100644 index 0000000000000..a2c38996d0f11 --- /dev/null +++ b/site/components/CodeExample/index.ts @@ -0,0 +1 @@ +export * from "./CodeExample" diff --git a/site/components/Icons/FileCopy.tsx b/site/components/Icons/FileCopy.tsx new file mode 100644 index 0000000000000..d8f33f101ef5f --- /dev/null +++ b/site/components/Icons/FileCopy.tsx @@ -0,0 +1,11 @@ +import SvgIcon from "@material-ui/core/SvgIcon" +import React from "react" + +export const FileCopy: typeof SvgIcon = (props) => ( + + + +) diff --git a/site/components/Icons/index.ts b/site/components/Icons/index.ts index 17f48ebaa4a98..bb6950f2f1da9 100644 --- a/site/components/Icons/index.ts +++ b/site/components/Icons/index.ts @@ -1,4 +1,5 @@ export { CoderIcon } from "./CoderIcon" +export * from "./FileCopy" export { Logo } from "./Logo" export * from "./Logout" export { WorkspacesIcon } from "./WorkspacesIcon" diff --git a/site/pages/projects/index.tsx b/site/pages/projects/index.tsx index bf67deb52502b..105216e069d39 100644 --- a/site/pages/projects/index.tsx +++ b/site/pages/projects/index.tsx @@ -1,7 +1,6 @@ import React from "react" import { makeStyles } from "@material-ui/core/styles" import Paper from "@material-ui/core/Paper" -import { useRouter } from "next/router" import Link from "next/link" import { EmptyState } from "../../components" import { ErrorSummary } from "../../components/ErrorSummary" @@ -14,10 +13,10 @@ import { FullScreenLoader } from "../../components/Loader/FullScreenLoader" import { Organization, Project } from "./../../api" import useSWR from "swr" +import { CodeExample } from "../../components/CodeExample/CodeExample" const ProjectsPage: React.FC = () => { const styles = useStyles() - const router = useRouter() const { me, signOut } = useUser(true) const { data: projects, error } = useSWR("/api/v2/projects") const { data: orgs, error: orgsError } = useSWR("/api/v2/users/me/organizations") @@ -34,15 +33,6 @@ const ProjectsPage: React.FC = () => { return } - const createProject = () => { - void router.push("/projects/create") - } - - const action = { - text: "Create Project", - onClick: createProject, - } - // Create a dictionary of organization ID -> organization Name // Needed to properly construct links to dive into a project const orgDictionary = orgs.reduce((acc: Record, curr: Organization) => { @@ -62,17 +52,15 @@ const ProjectsPage: React.FC = () => { }, ] - const emptyState = ( - + const description = ( +
+
Run the following command to get started:
+ +
) + const emptyState = + const tableProps = { title: "All Projects", columns: columns, @@ -85,7 +73,7 @@ const ProjectsPage: React.FC = () => { return (
-
+
@@ -94,11 +82,14 @@ const ProjectsPage: React.FC = () => { ) } -const useStyles = makeStyles(() => ({ +const useStyles = makeStyles((theme) => ({ root: { display: "flex", flexDirection: "column", }, + descriptionLabel: { + marginBottom: theme.spacing(1), + }, })) export default ProjectsPage diff --git a/site/theme/palettes.ts b/site/theme/palettes.ts index 82132d93f5b6b..312d1144ed32d 100644 --- a/site/theme/palettes.ts +++ b/site/theme/palettes.ts @@ -9,6 +9,14 @@ declare module "@material-ui/core/styles/createPalette" { contrastText: string // Background color for codeblocks main: string + button: { + // Background for buttons inside a codeblock + main: string + // Hover background color for buttons inside a codeblock + hover: string + // Text color for buttons inside a codeblock + contrastText: string + } } navbar: { main: string @@ -26,6 +34,11 @@ declare module "@material-ui/core/styles/createPalette" { codeBlock: { contrastText: string main: string + button: { + main: string + hover: string + contrastText: string + } } navbar: { main: string @@ -71,6 +84,11 @@ export const lightPalette: CustomPalette = { codeBlock: { main: "#F3F3F3", contrastText: "rgba(0, 0, 0, 0.9)", + button: { + main: "#E6ECE6", + hover: "#DAEBDA", + contrastText: "#000", + }, }, primary: { main: "#519A54", @@ -135,6 +153,11 @@ export const darkPalette: CustomPalette = { codeBlock: { main: "rgb(24, 26, 27)", contrastText: "rgba(255, 255, 255, 0.8)", + button: { + main: "rgba(255, 255, 255, 0.1)", + hover: "rgba(255, 255, 255, 0.25)", + contrastText: "#FFF", + }, }, hero: { main: "#141414", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy