diff --git a/components/ConfigExamples.tsx b/components/ConfigExamples.tsx new file mode 100644 index 0000000..8ea9b34 --- /dev/null +++ b/components/ConfigExamples.tsx @@ -0,0 +1,142 @@ +import React from "react"; +import styled, { css } from "styled-components"; + +import Highlight, { defaultProps } from "prism-react-renderer"; +import { PrismStyles } from "./styles/prism-styles"; + +const Container = styled.div` + font-family: "Menlo", "Monaco", monospace; + font-size: 16px; + + width: 100%; + background-color: #242424; + border-radius: 4px; +`; + +const CODE_EXAMPLES = [ + { + title: "Simple", + code: `{ + // You probably don't need to set anything in the configuration, + // we infer a lot of information from the repo. One value that's worth + // setting is your default sandbox ids to fork for a PR. It's easier to test + // on a sandbox that includes some test cases already. + // This is also optional, we default to 'vanilla' if it isn't set. + "sandboxes": ["new", "vanilla"] +}` + }, + { + title: "Monorepo", + code: `{ + // If you have a monorepo we infer your packages from your Yarn workspaces + // or lerna configuration by default. If you want to explicitly + // set what to build, you can fill the 'packages' field with paths to your + // packages + "packages": ["packages/react", "packages/react-dom"], + "sandboxes": ["new", "vanilla"] +}` + }, + { + title: "Custom Install/Build", + code: `{ + // You can also set custom install or build commands. These commands + // are appended after \`yarn run\` or \`npm run\`. + // This will call \`yarn run custom-install\` or \`npm run custom-install\`: + "installCommand": "custom-install", + // You can also provide \`false\` as a value if you want to skip the step: + "buildCommand": false +}` + }, + { + title: "GitHub Examples", + code: `{ + // You can directly link to sandboxes in your GitHub repository. If you have + // an example in \`/examples/todomvc\` in your repository, you can refer to this + // example in the config. The advantage of this is that we will always take the + // version of the example that's in your PR. If you have a PR that updates + // the example, it will be reflected in the generated sandbox. + "sandboxes": ["/examples/todomvc"] +}` + } +]; + +const Buttons = styled.div` + display: flex; + width: 100%; +`; + +const Button = styled.button<{ selected: boolean }>` + transition: 0.3s ease all; + font-family: "Inter"; + display: flex; + width: 100%; + text-align: center; + justify-content: center; + + background-color: transparent; + outline: 0; + border: 0; + font-size: 13px; + color: #ccc; + padding: 0.75rem 0; + font-weight: 600; + border-bottom: 2px solid rgba(0, 0, 0, 0.25); + cursor: pointer; + + ${props => + props.selected && + css` + color: white; + border-color: #64d2ff; + `} + + &:hover { + color: white; + } +`; + +export const ConfigExamples = () => { + const [exampleIndex, setExampleIndex] = React.useState(0); + + return ( + <> + + + {CODE_EXAMPLES.map((example, i) => ( + + ))} + +
+ + + {({ className, style, tokens, getLineProps, getTokenProps }) => ( +
+                {tokens.map((line, i) => (
+                  
+ {line.map((token, key) => ( + + ))} +
+ ))} +
+ )} +
+
+
+ + ); +}; diff --git a/components/LogsContainer.tsx b/components/LogsContainer.tsx index 7dc3ed9..438cb14 100644 --- a/components/LogsContainer.tsx +++ b/components/LogsContainer.tsx @@ -79,24 +79,15 @@ export const LogsContainer = ({ status, duration, log }: Props) => { - {log - ? log.split(/(^\+.*\n)/m).map((line, i) => - line.startsWith("+") ? ( - - {line} - - ) : ( - - {line} - - ) - ) - : status === "queued" - ? "Waiting to be built..." - : "Loading..."} + {log ? ( + + {log} + + ) : status === "queued" ? ( + "Waiting to be built..." + ) : ( + "Loading..." + )}
diff --git a/components/SetupPage.tsx b/components/SetupPage.tsx new file mode 100644 index 0000000..e884c81 --- /dev/null +++ b/components/SetupPage.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { SkeletonStatusPage } from "../components/SkeletonStatusPage"; +import { LEARN_MORE_DOCUMENTATION_URL } from "../utils/constants"; +import { Title, Description } from "../components/_elements"; +import { ConfigExamples } from "../components/ConfigExamples"; +import { colors } from "../theme/colors"; + +export const SetupPage = () => ( + + You've installed CodeSandbox CI! + + The last step is to create a Pull Request with the CI configuration file + in your repository at `.codesandbox/ci.json`{" "} + + (example) + + . + +
+
+ Example Configurations +
+ +
+ + And that’s it! Now check your Pull Request on GitHub to see your built + library. For more information, go to{" "} + + our documentation. + + +
+); diff --git a/components/StatusPage.tsx b/components/StatusPage.tsx index da6acc9..bb0dc50 100644 --- a/components/StatusPage.tsx +++ b/components/StatusPage.tsx @@ -14,8 +14,13 @@ import { Layout } from "./Layout"; import { SkeletonStatusPage } from "./SkeletonStatusPage"; import { useGlobalState } from "../utils/state"; import { colors } from "../theme/colors"; -import { LEARN_MORE_DOCUMENT_URL } from "../utils/constants"; +import { + LEARN_MORE_DOCUMENT_URL, + INSTALL_GITHUB_URL +} from "../utils/constants"; import { BUILD_LINK, buildLink, PR_LINK, prLink } from "../utils/url"; +import { SetupPage } from "./SetupPage"; +import { Button } from "./_elements"; // Initialize the desired locales. JavascriptTimeAgo.locale(en); @@ -68,6 +73,7 @@ export interface StatusPageProps { selectedBuildId: number; builds?: IBuild[]; notFound?: boolean; + showSetup?: boolean; error?: boolean; } @@ -79,6 +85,7 @@ const StatusPage = ({ selectedBuildId, builds, notFound, + showSetup, error }: StatusPageProps) => { const [statePrs, setPrs] = useGlobalState("prs"); @@ -89,14 +96,28 @@ const StatusPage = ({ setPrs(prs); }, [username, repo, prs, setPrs]); + if (showSetup) { + return ; + } + + if (notFound) { + return ( + + + We could not find the repository you were looking for, have you + installed the GitHub App? + + + + + ); + } + if (notFound || error) { return ( - {notFound - ? `We could not find the repository you were looking for, have you - installed the GitHub App?` - : `We just got an error, please retry in a couple minutes!`} + We just got an error, please retry in a couple minutes! ); @@ -213,7 +234,12 @@ StatusPage.getInitialProps = async ({ query, res }): Promise< - { title?: string } & (StatusPageProps | { notFound: true } | { error: true }) + { title?: string } & ( + | StatusPageProps + | { notFound: true } + | { showSetup: true } + | { error: true } + ) > => { try { const { username, repo } = query; @@ -227,6 +253,13 @@ StatusPage.getInitialProps = async ({ } const { prs } = await getPrs(username, repo); + + if (prs.length === 0) { + // No PRs have been registered yet + + return { showSetup: true, title: "CodeSandbox CI Installed" }; + } + let prNumber = query.prNumber; if (!prNumber) { prNumber = prs[0].number; diff --git a/components/_elements.ts b/components/_elements.ts new file mode 100644 index 0000000..974e1f0 --- /dev/null +++ b/components/_elements.ts @@ -0,0 +1,60 @@ +import styled from "styled-components"; + +export const Title = styled.h1` + font-size: 19px; + font-weight: 300; + margin-bottom: 0; +`; + +export const Description = styled.p` + font-size: 16px; + font-weight: 300; + margin-bottom: 0; + max-width: 600px; + text-align: center; + line-height: 1.6; +`; + +export const SubTitle = styled.h2` + font-size: 16px; + font-weight: 300; + color: #ccc; +`; + +export const ButtonContainer = styled.div` + width: 20rem; + margin-top: 1.5rem; + text-align: center; +`; + +export const Link = styled.a` + transition: 0.3s ease color; + display: block; + margin-top: 1.5rem; + font-size: 1rem; + font-weight: 300; + color: #ccc !important; + + &:hover { + color: white !important; + } +`; + +export const Button = styled.a` + transition: 0.3s ease background-color; + + background-color: #0a84ff; + border-radius: 4px; + border: 0; + padding: 0.5rem 2rem; + font-family: "Inter", sans-serif; + font-weight: 600; + font-size: 1rem; + cursor: pointer; + + color: white; + + &:hover { + background-color: #0971f1; + } +`; diff --git a/components/styles/prism-styles.ts b/components/styles/prism-styles.ts new file mode 100644 index 0000000..0eeac7d --- /dev/null +++ b/components/styles/prism-styles.ts @@ -0,0 +1,126 @@ +import { createGlobalStyle } from "styled-components"; + +export const PrismStyles = createGlobalStyle` +/** + * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML + * Based on https://github.com/chriskempson/tomorrow-theme + * @author Rose Pritchard + */ + +code[class*="language-"], +pre[class*="language-"] { + color: #ccc; + background: none; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: transparent; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.block-comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #999; +} + +.token.punctuation { + color: #ccc; +} + +.token.tag, +.token.attr-name, +.token.namespace, +.token.deleted { + color: #e2777a; +} + +.token.function-name { + color: #6196cc; +} + +.token.boolean, +.token.number, +.token.function { + color: #f08d49; +} + +.token.property, +.token.class-name, +.token.constant, +.token.symbol { + color: #f8c555; +} + +.token.selector, +.token.important, +.token.atrule, +.token.keyword, +.token.builtin { + color: #cc99cd; +} + +.token.string, +.token.char, +.token.attr-value, +.token.regex, +.token.variable { + color: #7ec699; +} + +.token.operator, +.token.entity, +.token.url { + color: #67cdcc; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + +.token.inserted { + color: green; +} +`; diff --git a/package.json b/package.json index f829357..347724f 100644 --- a/package.json +++ b/package.json @@ -18,13 +18,14 @@ "lru-cache": "^5.1.1", "next": "^9.0.4", "nprogress": "^0.2.0", + "prism-react-renderer": "^1.0.2", "react": "^16.9.0", "react-dom": "^16.9.0", "react-hooks-global-state": "^0.14.0", "react-is": "^16.9.0", "react-show": "^3.0.4", "react-time-ago": "^5.0.4", - "styled-components": "^5.0.0-beta.8" + "styled-components": "^5.0.0-rc.1" }, "devDependencies": { "@types/amplitude-js": "^4.4.5", diff --git a/pages/_document.tsx b/pages/_document.tsx index 49963fe..9aff6ec 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -8,20 +8,29 @@ interface Props { } export default class MyDocument extends Document { - static getInitialProps({ renderPage }) { - // Step 1: Create an instance of ServerStyleSheet + static async getInitialProps(ctx) { const sheet = new ServerStyleSheet(); + const originalRenderPage = ctx.renderPage; - // Step 2: Retrieve styles from components in the page - const page = renderPage(App => props => - sheet.collectStyles() - ); - - // Step 3: Extract the styles as