diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index b0efeb7bc..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,20 +0,0 @@ -version: 2 -jobs: - build: - docker: - - image: circleci/node:8 - steps: - - checkout - - restore_cache: - keys: - - dependencies-{{ checksum "yarn.lock" }} - - run: - name: Install - command: yarn install --pure-lockfile - - save_cache: - paths: - - node_modules - key: dependencies-{{ checksum "yarn.lock" }} - - run: - name: Check Prettier, ESLint, Flow - command: yarn ci-check diff --git a/.eslintignore b/.eslintignore index 942541715..ee6604687 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,8 @@ node_modules/* +# Skip beta +beta/* + # Ignore markdown files and examples content/* diff --git a/.flowconfig b/.flowconfig index 836f6ec1e..baf4b0255 100644 --- a/.flowconfig +++ b/.flowconfig @@ -1,5 +1,6 @@ [ignore] +/beta/.* /content/.* /node_modules/.* /public/.* diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d3c569401..e34dda4af 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,12 +1,10 @@ - - " + + - name: Create Comment + uses: peter-evans/create-or-update-comment@v1.4.4 + if: success() && steps.fc.outputs.comment-id == 0 + with: + issue-number: ${{ steps.get-comment-body.outputs.pr-number }} + body: ${{ steps.get-comment-body.outputs.body }} + + - name: Update Comment + uses: peter-evans/create-or-update-comment@v1.4.4 + if: success() && steps.fc.outputs.comment-id != 0 + with: + issue-number: ${{ steps.get-comment-body.outputs.pr-number }} + body: ${{ steps.get-comment-body.outputs.body }} + comment-id: ${{ steps.fc.outputs.comment-id }} + edit-mode: replace \ No newline at end of file diff --git a/.github/workflows/beta_site_lint.yml b/.github/workflows/beta_site_lint.yml new file mode 100644 index 000000000..a9cacd7f0 --- /dev/null +++ b/.github/workflows/beta_site_lint.yml @@ -0,0 +1,30 @@ +name: Beta Site Lint / Heading ID check + +on: + push: + branches: + - main # change this if your default branch is named differently + pull_request: + types: [opened, synchronize, reopened] + +jobs: + lint: + runs-on: ubuntu-latest + + name: Lint on node 12.x and ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Use Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: 12.x + + - name: Install deps and build (with cache) + uses: bahmutov/npm-install@v1.7.10 + with: + working-directory: 'beta' + + + - name: Lint codebase + run: cd beta && yarn ci-check diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml new file mode 100644 index 000000000..90a961d4c --- /dev/null +++ b/.github/workflows/label.yml @@ -0,0 +1,22 @@ +# This workflow will triage pull requests and apply a label based on the +# paths that are modified in the pull request. +# +# To use this workflow, you will need to set up a .github/labeler.yml +# file with configuration. For more information, see: +# https://github.com/actions/labeler + +name: Labeler +on: [pull_request_target] + +jobs: + label: + + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + steps: + - uses: actions/labeler@v2 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml new file mode 100644 index 000000000..eed7b3d94 --- /dev/null +++ b/.github/workflows/nodejs.yml @@ -0,0 +1,27 @@ +name: Lint / Flow check + +on: + push: + branches: + - main # change this if your default branch is named differently + pull_request: + types: [opened, synchronize, reopened] + +jobs: + lint: + runs-on: ubuntu-latest + + name: Lint on node 12.x and ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Use Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: 12.x + + - name: Install deps and build (with cache) + uses: bahmutov/npm-install@v1.7.10 + + - name: Lint codebase + run: yarn ci-check diff --git a/.gitignore b/.gitignore index d1bde99ce..e81f1af62 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ .DS_STORE .idea node_modules -public +/public yarn-error.log \ No newline at end of file diff --git a/.nvmrc b/.nvmrc index 5debbed21..898e643b0 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -lts/carbon +12.22.0 diff --git a/README.md b/README.md index 0984a9dc2..33e9112f0 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This repo contains the source code and documentation powering [hy.reactjs.org](h ### Prerequisites 1. Git -1. Node: any 8.x version starting with 8.4.0 or greater +1. Node: any 12.x version starting with v12.0.0 or greater 1. Yarn: See [Yarn website for installation instructions](https://yarnpkg.com/lang/en/docs/install/) 1. A fork of the repo (for any contributions) 1. A clone of the [hy.reactjs.org repo](https://github.com/reactjs/hy.reactjs.org) on your local machine @@ -26,17 +26,26 @@ This repo contains the source code and documentation powering [hy.reactjs.org](h ### Guidelines +<<<<<<< HEAD The documentation is divided into several sections with a different tone and purpose. If you plan to write more than a few sentences, you might find it helpful to get familiar with the [contributing guidelines](https://github.com/reactjs/hy.reactjs.org/blob/master/CONTRIBUTING.md#guidelines-for-text) for the appropriate sections. ### Create a branch 1. `git checkout master` from any folder in your local `hy.reactjs.org` repository 1. `git pull origin master` to ensure you have the latest main code +======= +The documentation is divided into several sections with a different tone and purpose. If you plan to write more than a few sentences, you might find it helpful to get familiar with the [contributing guidelines](https://github.com/reactjs/reactjs.org/blob/main/CONTRIBUTING.md#guidelines-for-text) for the appropriate sections. + +### Create a branch + +1. `git checkout main` from any folder in your local `reactjs.org` repository +1. `git pull origin main` to ensure you have the latest main code +>>>>>>> 3aac8c59848046fb427aab4373a7aadd7069a24c 1. `git checkout -b the-name-of-my-branch` (replacing `the-name-of-my-branch` with a suitable name) to create a branch ### Make the change -1. Follow the "Running locally" instructions +1. Follow the ["Running locally"](#running-locally) instructions 1. Save the files and check in the browser 1. Changes to React components in `src` will hot-reload 1. Changes to markdown files in `content` will hot-reload @@ -53,11 +62,11 @@ The documentation is divided into several sections with a different tone and pur 1. `git push my-fork-name the-name-of-my-branch` 1. Go to the [hy.reactjs.org repo](https://github.com/reactjs/hy.reactjs.org) and you should see recently pushed branches. 1. Follow GitHub's instructions. -1. If possible, include screenshots of visual changes. A Netlify build will also be automatically created once you make your PR so other people can see your change. +1. If possible, include screenshots of visual changes. A preview build is triggered after your changes are pushed to GitHub. ## Translation -If you are interested in translating `reactjs.org`, please see the current translation efforts at [isreacttranslatedyet.com](https://www.isreacttranslatedyet.com/). +If you are interested in translating `reactjs.org`, please see the current translation efforts at [translations.reactjs.org](https://translations.reactjs.org/). If your language does not have a translation and you would like to create one, please follow the instructions at [reactjs.org Translations](https://github.com/reactjs/reactjs.org-translation#translating-reactjsorg). diff --git a/beta/.env.development b/beta/.env.development new file mode 100644 index 000000000..e69de29bb diff --git a/beta/.env.production b/beta/.env.production new file mode 100644 index 000000000..d25eb7dd4 --- /dev/null +++ b/beta/.env.production @@ -0,0 +1 @@ +NEXT_PUBLIC_GA_TRACKING_ID = 'UA-41298772-4' \ No newline at end of file diff --git a/beta/.eslintignore b/beta/.eslintignore new file mode 100644 index 000000000..4738cb697 --- /dev/null +++ b/beta/.eslintignore @@ -0,0 +1,3 @@ +scripts +plugins +next.config.js diff --git a/beta/.eslintrc b/beta/.eslintrc new file mode 100644 index 000000000..5c336583f --- /dev/null +++ b/beta/.eslintrc @@ -0,0 +1,13 @@ +{ + "root": true, + "extends": "next/core-web-vitals", + "rules": { + "no-unused-vars": "warn" + }, + "env": { + "node": true, + "commonjs": true, + "browser": true, + "es6": true + } +} diff --git a/beta/.gitignore b/beta/.gitignore new file mode 100644 index 000000000..35b7a4acf --- /dev/null +++ b/beta/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem +tsconfig.tsbuildinfo + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/beta/.husky/pre-commit b/beta/.husky/pre-commit new file mode 100755 index 000000000..bbf64071e --- /dev/null +++ b/beta/.husky/pre-commit @@ -0,0 +1,5 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +cd beta +yarn lint-staged \ No newline at end of file diff --git a/beta/.prettierignore b/beta/.prettierignore new file mode 100644 index 000000000..17908faf4 --- /dev/null +++ b/beta/.prettierignore @@ -0,0 +1,2 @@ +src/pages/docs/**/*.md +src/pages/blog/**/*.md diff --git a/beta/.prettierrc b/beta/.prettierrc new file mode 100644 index 000000000..19b54ad05 --- /dev/null +++ b/beta/.prettierrc @@ -0,0 +1,21 @@ +{ + "bracketSpacing": false, + "singleQuote": true, + "bracketSameLine": true, + "trailingComma": "es5", + "printWidth": 80, + "overrides": [ + { + "files": "*.css", + "options": { + "parser": "css" + } + }, + { + "files": "*.md", + "options": { + "parser": "mdx" + } + } + ] +} diff --git a/beta/CONTRIBUTING.md b/beta/CONTRIBUTING.md new file mode 100644 index 000000000..f893cab12 --- /dev/null +++ b/beta/CONTRIBUTING.md @@ -0,0 +1,135 @@ +# Contributing + +Thank you for your interest in contributing to the React Docs! + +## Code of Conduct + +Facebook has adopted a Code of Conduct that we expect project +participants to adhere to. Please [read the full text](https://code.facebook.com/codeofconduct) +so that you can understand what actions will and will not be tolerated. + +## Technical Writing Tips + +This is a [good summary](https://medium.com/@kvosswinkel/coding-like-a-journalist-ee52360a16bc) for things to keep in mind when writing technical docs. + +## Guidelines for Text + +**Different sections intentionally have different styles.** + +The documentation is divided into sections to cater to different learning styles and use cases. When editing an article, try to match the surrounding text in tone and style. When creating a new article, try to match the tone of the other articles in the same section. Learn about the motivation behind each section below. + +**[Learn React](https://beta.reactjs.org/learn)** is designed to introduce fundamental concepts in a step-by-step way. Each individual article in Learn React builds on the knowledge from the previous ones, so make sure not to add any "cyclical dependencies" between them. It is important that the reader can start with the first article and work their way to the last Learn React article without ever having to "look ahead" for a definition. This explains some ordering choices (e.g. that state is explained before events, or that "thinking in React" doesn't use refs). Learn React also serves as a reference manual for React concepts, so it is important to be very strict about their definitions and relationships between them. + +**[API Reference](https://reactjs.org/apis)** is organized by APIs rather than concepts. It is intended to be exhaustive. Any corner cases or recommendations that were skipped for brevity in Learn React should be mentioned in the reference documentation for the corresponding APIs. + +**Try to follow your own instructions.** + +When writing step-by-step instructions (e.g. how to install something), try to forget everything you know about the topic, and actually follow the instructions you wrote, a single step at time. Often you will discover that there is implicit knowledge that you forgot to mention, or that there are missing or out-of-order steps in the instructions. Bonus points for getting *somebody else* to follow the steps and watching what they struggle with. Often it would be something very simple that you have not anticipated. + +## Guidelines for Code Examples + +### Syntax + +#### Prefer JSX to `createElement`. + +Ignore this if you're specifically describing `createElement`. + +#### Use `const` where possible, otherwise `let`. Don't use `var`. + +Ignore this if you're specifically writing about ES5. + +#### Don't use ES6 features when equivalent ES5 features have no downsides. + +Remember that ES6 is still new to a lot of people. While we use it in many places (`const` / `let`, classes, arrow functions), if the equivalent ES5 code is just as straightforward and readable, consider using it. + +In particular, you should prefer named `function` declarations over `const myFunction = () => ...` arrows for top-level functions. However, you *should* use arrow functions where they provide a tangible improvement (such as preserving `this` context inside a component). Consider both sides of the tradeoff when deciding whether to use a new feature. + +#### Don't use features that aren't standardized yet. + +For example, **don't** write this: + +```js +class MyComponent extends React.Component { + state = {value: ''}; + handleChange = (e) => { + this.setState({value: e.target.value}); + }; +} +``` + +Instead, **do** write this: + +```js +class MyComponent extends React.Component { + constructor(props) { + super(props); + this.handleChange = this.handleChange.bind(this); + this.state = {value: ''}; + } + handleChange(e) { + this.setState({value: e.target.value}); + } +} +``` + +Ignore this rule if you're specifically describing an experimental proposal. Make sure to mention its experimental nature in the code and in the surrounding text. + +### Style + +- Use semicolons. +- No space between function names and parens (`method() {}` not `method () {}`). +- When in doubt, use the default style favored by [Prettier](https://prettier.io/playground/). + +### Highlighting + +Use `js` as the highlighting language in Markdown code blocks: + +```` +```js +// code +``` +```` + +Sometimes you'll see blocks with numbers. +They tell the website to highlight specific lines. + +You can highlight a single line: + +```` +```js {2} +function hello() { + // this line will get highlighted +} +``` +```` + +A range of lines: + +```` +```js {2-4} +function hello() { + // these lines + // will get + // highlighted +} +``` +```` + +Or even multiple ranges: + +```` +```js {2-4,6} +function hello() { + // these lines + // will get + // highlighted + console.log('hello'); + // also this one + console.log('there'); +} +``` +```` + +Be mindful that if you move some code in an example with highlighting, you also need to update the highlighting. + +Don't be afraid to often use highlighting! It is very valuable when you need to focus the reader's attention on a particular detail that's easy to miss. diff --git a/beta/README.md b/beta/README.md new file mode 100644 index 000000000..725a2c382 --- /dev/null +++ b/beta/README.md @@ -0,0 +1,73 @@ +# reactjs.org + +This repo contains the source code and documentation powering [reactjs.org](https://reactjs.org/). + + +## Getting started + +### Prerequisites + +1. Git +1. Node: any 12.x version starting with v12.0.0 or greater +1. Yarn: See [Yarn website for installation instructions](https://yarnpkg.com/lang/en/docs/install/) +1. A fork of the repo (for any contributions) +1. A clone of the [reactjs.org repo](https://github.com/reactjs/reactjs.org) on your local machine + +### Installation + +1. `cd reactjs.org` to go into the project root +1. `cd beta` to open the beta website +3. `yarn` to install the website's npm dependencies + +### Running locally + +1. Make sure you're in the `beta` folder +1. `yarn dev` to start the development server (powered by [Next.js](https://nextjs.org/)) +1. `open http://localhost:3000` to open the site in your favorite browser + +## Contributing + +### Guidelines + +The documentation is divided into several sections with a different tone and purpose. If you plan to write more than a few sentences, you might find it helpful to get familiar with the [contributing guidelines](https://github.com/reactjs/reactjs.org/blob/main/CONTRIBUTING.md#guidelines-for-text) for the appropriate sections. + +### Create a branch + +1. `git checkout main` from any folder in your local `reactjs.org` repository +1. `git pull origin main` to ensure you have the latest main code +1. `git checkout -b the-name-of-my-branch` (replacing `the-name-of-my-branch` with a suitable name) to create a branch + +### Make the change + +1. Follow the ["Running locally"](#running-locally) instructions +1. Save the files and check in the browser + 1. Changes to React components in `src` will hot-reload + 1. Changes to markdown files in `content` will hot-reload + 1. If working with plugins, you may need to remove the `.cache` directory and restart the server + +### Test the change + +1. If possible, test any visual changes in all latest versions of common browsers, on both desktop and mobile. +2. Run `yarn check-all` from the `beta` folder. (This will run Prettier, ESLint and validate types.) + +### Push it + +1. `git add -A && git commit -m "My message"` (replacing `My message` with a commit message, such as `Fix header logo on Android`) to stage and commit your changes +1. `git push my-fork-name the-name-of-my-branch` +1. Go to the [reactjs.org repo](https://github.com/reactjs/reactjs.org) and you should see recently pushed branches. +1. Follow GitHub's instructions. +1. If possible, include screenshots of visual changes. A preview build is triggered after your changes are pushed to GitHub. + +## Translation + +If you are interested in translating `reactjs.org`, please see the current translation efforts at [translations.reactjs.org](https://translations.reactjs.org/). + + +If your language does not have a translation and you would like to create one, please follow the instructions at [reactjs.org Translations](https://github.com/reactjs/reactjs.org-translation#translating-reactjsorg). + +## Troubleshooting + +- `yarn reset` to clear the local cache + +## License +Content submitted to [reactjs.org](https://reactjs.org/) is CC-BY-4.0 licensed, as found in the [LICENSE-DOCS.md](https://github.com/open-source-explorer/reactjs.org/blob/master/LICENSE-DOCS.md) file. diff --git a/beta/colors.js b/beta/colors.js new file mode 100644 index 000000000..bac74d41e --- /dev/null +++ b/beta/colors.js @@ -0,0 +1,89 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +module.exports = { + // Text colors + primary: '#23272F', // gray-90 + 'primary-dark': '#F6F7F9', // gray-5 + secondary: '#404756', // gray-70 + 'secondary-dark': '#EBECF0', // gray-10 + link: '#087EA4', // blue-50 + 'link-dark': '#149ECA', // blue-40 + syntax: '#EBECF0', // gray-10 + wash: '#FFFFFF', + 'wash-dark': '#23272F', // gray-90 + card: '#F6F7F9', // gray-05 + 'card-dark': '#343A46', // gray-80 + highlight: '#E6F7FF', // blue-10 + 'highlight-dark': 'rgba(88,175,223,.1)', + border: '#EBECF0', // gray-10 + 'border-dark': '#343A46', // gray-80 + 'secondary-button': '#EBECF0', // gray-10 + 'secondary-button-dark': '#404756', // gray-70 + + // Gray + 'gray-95': '#16181D', + 'gray-90': '#23272F', + 'gray-80': '#343A46', + 'gray-70': '#404756', + 'gray-60': '#4E5769', + 'gray-50': '#5E687E', // unused + 'gray-40': '#78839B', + 'gray-30': '#99A1B3', + 'gray-20': '#BCC1CD', + 'gray-10': '#EBECF0', + 'gray-5': '#F6F7F9', + + // Blue + 'blue-60': '#045975', + 'blue-50': '#087EA4', + 'blue-40': '#149ECA', // Brand Blue + 'blue-30': '#58C4DC', // unused + 'blue-20': '#ABE2ED', + 'blue-10': '#E6F7FF', // todo: doesn't match illustrations + 'blue-5': '#E6F6FA', + + // Yellow + 'yellow-60': '#B65700', + 'yellow-50': '#C76A15', + 'yellow-40': '#DB7D27', // unused + 'yellow-30': '#FABD62', // unused + 'yellow-20': '#FCDEB0', // unused + 'yellow-10': '#FDE7C7', + 'yellow-5': '#FEF5E7', + + // Purple + 'purple-60': '#2B3491', // unused + 'purple-50': '#575FB7', + 'purple-40': '#6B75DB', + 'purple-30': '#8891EC', + 'purple-20': '#C3C8F5', // unused + 'purple-10': '#E7E9FB', + 'purple-5': '#F3F4FD', + + // Green + 'green-60': '#2B6E62', + 'green-50': '#388F7F', + 'green-40': '#44AC99', + 'green-30': '#7FCCBF', + 'green-20': '#ABDED5', + 'green-10': '#E5F5F2', + 'green-5': '#F4FBF9', + + // RED + 'red-60': '#712D28', + 'red-50': '#A6423A', // unused + 'red-40': '#C1554D', + 'red-30': '#D07D77', + 'red-20': '#E5B7B3', // unused + 'red-10': '#F2DBD9', // unused + 'red-5': '#FAF1F0', + + // MISC + 'code-block': '#99a1b30f', // gray-30 @ 6% + 'gradient-blue': '#58C4DC', // Only used for the landing gradient for now. + github: { + highlight: '#fffbdd', + }, +}; diff --git a/beta/next-env.d.ts b/beta/next-env.d.ts new file mode 100644 index 000000000..4f11a03dc --- /dev/null +++ b/beta/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/beta/next.config.js b/beta/next.config.js new file mode 100644 index 000000000..7b3222720 --- /dev/null +++ b/beta/next.config.js @@ -0,0 +1,59 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +const path = require('path'); +const {remarkPlugins} = require('./plugins/markdownToHtml'); +const redirects = require('./src/redirects.json'); + +module.exports = { + pageExtensions: ['jsx', 'js', 'ts', 'tsx', 'mdx', 'md'], + experimental: { + plugins: true, + // TODO: this doesn't work because https://github.com/vercel/next.js/issues/30714 + concurrentFeatures: false, + scrollRestoration: true, + }, + async redirects() { + return redirects.redirects; + }, + rewrites() { + return [ + { + source: '/feed.xml', + destination: '/_next/static/feed.xml', + }, + ]; + }, + webpack: (config, {dev, isServer, ...options}) => { + if (process.env.ANALYZE) { + const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer'); + config.plugins.push( + new BundleAnalyzerPlugin({ + analyzerMode: 'static', + reportFilename: options.isServer + ? '../analyze/server.html' + : './analyze/client.html', + }) + ); + } + + // Add our custom markdown loader in order to support frontmatter + // and layout + config.module.rules.push({ + test: /.mdx?$/, // load both .md and .mdx files + use: [ + options.defaultLoaders.babel, + { + loader: '@mdx-js/loader', + options: { + remarkPlugins, + }, + }, + path.join(__dirname, './plugins/md-layout-loader'), + ], + }); + + return config; + }, +}; diff --git a/beta/package.json b/beta/package.json new file mode 100644 index 000000000..d2fe90cb0 --- /dev/null +++ b/beta/package.json @@ -0,0 +1,106 @@ +{ + "name": "react-website", + "version": "1.0.0", + "private": true, + "license": "CC", + "scripts": { + "analyze": "ANALYZE=true next build", + "dev": "next", + "build": "next build && node ./scripts/generateRSS.js && node ./scripts/generateRedirects.js", + "lint": "next lint", + "lint:fix": "next lint --fix", + "format:source": "prettier --config .prettierrc --write \"{plugins,src}/**/*.{js,ts,jsx,tsx,css}\"", + "nit:source": "prettier --config .prettierrc --list-different \"{plugins,src}/**/*.{js,ts,jsx,tsx,css}\"", + "prettier": "yarn format:source", + "prettier:diff": "yarn nit:source", + "lint-heading-ids": "node scripts/headingIdLinter.js", + "fix-headings": "node scripts/headingIdLinter.js --fix", + "ci-check": "npm-run-all prettier:diff --parallel lint tsc lint-heading-ids", + "tsc": "tsc --noEmit", + "start": "next start", + "postinstall": "patch-package && (is-ci || (cd .. && husky install beta/.husky))", + "check-all": "npm-run-all prettier lint:fix tsc" + }, + "dependencies": { + "@codesandbox/sandpack-react": "v0.19.8-experimental.0", + "@docsearch/css": "3.0.0-alpha.41", + "@docsearch/react": "3.0.0-alpha.41", + "@headlessui/react": "^1.3.0", + "@mdx-js/react": "^1.6.16", + "body-scroll-lock": "^3.1.3", + "classnames": "^2.2.6", + "date-fns": "^2.16.1", + "debounce": "^1.2.1", + "ga-lite": "^2.1.4", + "github-slugger": "^1.3.0", + "next": "^12.0.10", + "parse-numeric-range": "^1.2.0", + "react": "experimental", + "react-collapsed": "3.1.0", + "react-dom": "experimental", + "scroll-into-view-if-needed": "^2.2.25" + }, + "devDependencies": { + "@mdx-js/loader": "^1.6.16", + "@types/body-scroll-lock": "^2.6.1", + "@types/classnames": "^2.2.10", + "@types/github-slugger": "^1.3.0", + "@types/mdx-js__react": "^1.5.2", + "@types/node": "^14.6.4", + "@types/parse-numeric-range": "^0.0.1", + "@types/react": "^16.9.46", + "@types/react-dom": "^16.9.8", + "@typescript-eslint/eslint-plugin": "2.x", + "@typescript-eslint/parser": "2.x", + "asyncro": "^3.0.0", + "autoprefixer": "^10.4.2", + "babel-eslint": "10.x", + "eslint": "7.x", + "eslint-config-next": "12.0.3", + "eslint-config-react-app": "^5.2.1", + "eslint-plugin-flowtype": "4.x", + "eslint-plugin-import": "2.x", + "eslint-plugin-jsx-a11y": "6.x", + "eslint-plugin-react": "7.x", + "eslint-plugin-react-hooks": "2.x", + "fs-extra": "^9.0.1", + "globby": "^11.0.1", + "gray-matter": "^4.0.2", + "husky": "^7.0.4", + "is-ci": "^3.0.1", + "lint-staged": ">=10", + "mdast-util-to-string": "^1.1.0", + "npm-run-all": "^4.1.5", + "patch-package": "^6.2.2", + "postcss": "^8.4.5", + "postcss-flexbugs-fixes": "4.2.1", + "postcss-preset-env": "^6.7.0", + "prettier": "^2.5.1", + "reading-time": "^1.2.0", + "remark": "^12.0.1", + "remark-external-links": "^7.0.0", + "remark-html": "^12.0.0", + "remark-images": "^2.0.0", + "remark-slug": "^7.0.0", + "remark-unwrap-images": "^2.0.0", + "retext": "^7.0.1", + "retext-smartypants": "^4.0.0", + "rss": "^1.2.2", + "tailwindcss": "^3.0.22", + "typescript": "^4.0.2", + "unist-util-visit": "^2.0.3", + "webpack-bundle-analyzer": "^4.5.0" + }, + "engines": { + "node": ">=12.x" + }, + "nextBundleAnalysis": { + "budget": null, + "budgetPercentIncreaseRed": 10, + "showDetails": true + }, + "lint-staged": { + "*.{js,ts,jsx,tsx,css}": "yarn prettier", + "src/**/*.md": "yarn fix-headings" + } +} diff --git a/beta/patches/@codesandbox+sandpack-react+0.19.8-experimental.0.patch b/beta/patches/@codesandbox+sandpack-react+0.19.8-experimental.0.patch new file mode 100644 index 000000000..77ad867fc --- /dev/null +++ b/beta/patches/@codesandbox+sandpack-react+0.19.8-experimental.0.patch @@ -0,0 +1,37 @@ +diff --git a/node_modules/@codesandbox/sandpack-react/dist/esm/index.js b/node_modules/@codesandbox/sandpack-react/dist/esm/index.js +index e6e5787..712a70d 100644 +--- a/node_modules/@codesandbox/sandpack-react/dist/esm/index.js ++++ b/node_modules/@codesandbox/sandpack-react/dist/esm/index.js +@@ -566,17 +566,16 @@ var REACT_TEMPLATE = { + }, + "/index.js": { + code: `import React, { StrictMode } from "react"; +-import ReactDOM from "react-dom"; ++import { createRoot } from "react-dom/client"; + import "./styles.css"; + + import App from "./App"; + +-const rootElement = document.getElementById("root"); +-ReactDOM.render( ++const root = createRoot(document.getElementById("root")); ++root.render( + + +- , +- rootElement ++ + );` + }, + "/styles.css": { +@@ -611,8 +610,8 @@ h1 { + } + }, + dependencies: { +- react: "^17.0.0", +- "react-dom": "^17.0.0", ++ react: "^18.0.0", ++ "react-dom": "^18.0.0", + "react-scripts": "^4.0.0" + }, + entry: "/index.js", diff --git a/beta/plugins/markdownToHtml.js b/beta/plugins/markdownToHtml.js new file mode 100644 index 000000000..0d5fe7afb --- /dev/null +++ b/beta/plugins/markdownToHtml.js @@ -0,0 +1,30 @@ +const remark = require('remark'); +const externalLinks = require('remark-external-links'); // Add _target and rel to external links +const customHeaders = require('./remark-header-custom-ids'); // Custom header id's for i18n +const images = require('remark-images'); // Improved image syntax +const unrwapImages = require('remark-unwrap-images'); // Removes

wrapper around images +const smartyPants = require('./remark-smartypants'); // Cleans up typography +const html = require('remark-html'); + +module.exports = { + remarkPlugins: [ + externalLinks, + customHeaders, + images, + unrwapImages, + smartyPants, + ], + markdownToHtml, +}; + +async function markdownToHtml(markdown) { + const result = await remark() + .use(externalLinks) + .use(customHeaders) + .use(images) + .use(unrwapImages) + .use(smartyPants) + .use(html) + .process(markdown); + return result.toString(); +} diff --git a/beta/plugins/md-layout-loader.js b/beta/plugins/md-layout-loader.js new file mode 100644 index 000000000..253839257 --- /dev/null +++ b/beta/plugins/md-layout-loader.js @@ -0,0 +1,38 @@ +const fm = require('gray-matter'); +const path = require('path'); + +// Makes mdx in next.js suck less by injecting necessary exports so that +// the docs are still readable on github. +// +// Layout component for a .mdx or .md page can be specfied in the frontmatter. +// This plugin assumes that the layout file and named export are the same. This +// easily changed by modifying the string below. +// +// All metadata can be written in yaml front matter. It will be passed to the +// layout component as `meta` prop. +// +// (Shamelessly stolen from Expo.io docs) +// @see https://github.com/expo/expo/blob/master/docs/common/md-loader.js +module.exports = async function (src) { + const callback = this.async(); + const {content, data} = fm(src); + const pageParentDir = path + .dirname(path.relative('./src/pages', this.resourcePath)) + .split(path.sep) + .shift(); + const layoutMap = { + blog: 'Post', + learn: 'Learn', + apis: 'API', + }; + const layout = layoutMap[pageParentDir] || 'Home'; + const code = + `import withLayout from 'components/Layout/Layout${layout}'; + +export default withLayout(${JSON.stringify(data)}) + + +` + content; + + return callback(null, code); +}; diff --git a/beta/plugins/remark-header-custom-ids.js b/beta/plugins/remark-header-custom-ids.js new file mode 100644 index 000000000..327e266c4 --- /dev/null +++ b/beta/plugins/remark-header-custom-ids.js @@ -0,0 +1,64 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +/*! + * Based on 'gatsby-remark-autolink-headers' + * Original Author: Kyle Mathews + * Updated by Jared Palmer; + * Copyright (c) 2015 Gatsbyjs + */ + +const toString = require('mdast-util-to-string'); +const visit = require('unist-util-visit'); +const slugs = require('github-slugger')(); + +function patch(context, key, value) { + if (!context[key]) { + context[key] = value; + } + return context[key]; +} + +const svgIcon = ``; + +module.exports = ({ + icon = svgIcon, + className = `anchor`, + maintainCase = false, +} = {}) => { + slugs.reset(); + return function transformer(tree) { + visit(tree, 'heading', (node) => { + const children = node.children; + let tail = children[children.length - 1]; + // Generate slugs on the fly (even if not specified in markdown) + // so that it's possible to copy anchor links in newly written content. + let id = slugs.slug(toString(node), maintainCase); + // However, for committed docs, we'll extract slug from the headers. + if (tail && tail.type === 'text' && tail.value === '/}') { + tail = children[children.length - 2]; + if (tail && tail.type === 'emphasis') { + // Use custom ID instead. + id = toString(tail); + // Until we're on MDX 2, we need to "cut off" the comment syntax. + tail = children[children.length - 3]; + if (tail && tail.type === 'text' && tail.value.endsWith('{/')) { + // Remove the emphasis and trailing `/}` + children.splice(children.length - 2, 2); + // Remove the `{/` + tail.value = tail.value.replace(/[ \t]*\{\/$/, ''); + } + } + } + + const data = patch(node, 'data', {}); + + patch(data, 'id', id); + patch(data, 'htmlAttributes', {}); + patch(data, 'hProperties', {}); + patch(data.htmlAttributes, 'id', id); + patch(data.hProperties, 'id', id); + }); + }; +}; diff --git a/beta/plugins/remark-smartypants.js b/beta/plugins/remark-smartypants.js new file mode 100644 index 000000000..7dd1b0c4a --- /dev/null +++ b/beta/plugins/remark-smartypants.js @@ -0,0 +1,21 @@ +const visit = require('unist-util-visit'); +const retext = require('retext'); +const smartypants = require('retext-smartypants'); + +function check(parent) { + if (parent.tagName === 'script') return false; + if (parent.tagName === 'style') return false; + return true; +} + +module.exports = function (options) { + const processor = retext().use(smartypants, options); + + function transformer(tree) { + visit(tree, 'text', (node, index, parent) => { + if (check(parent)) node.value = String(processor.processSync(node.value)); + }); + } + + return transformer; +}; diff --git a/beta/postcss.config.js b/beta/postcss.config.js new file mode 100644 index 000000000..427294522 --- /dev/null +++ b/beta/postcss.config.js @@ -0,0 +1,20 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + 'postcss-flexbugs-fixes': {}, + 'postcss-preset-env': { + autoprefixer: { + flexbox: 'no-2009', + }, + stage: 3, + features: { + 'custom-properties': false, + }, + }, + }, +} diff --git a/beta/public/external.png b/beta/public/external.png new file mode 100644 index 000000000..423ec131b Binary files /dev/null and b/beta/public/external.png differ diff --git a/beta/public/favicon.ico b/beta/public/favicon.ico new file mode 100644 index 000000000..38fd8641c Binary files /dev/null and b/beta/public/favicon.ico differ diff --git a/beta/public/favicon_dark.ico b/beta/public/favicon_dark.ico new file mode 100644 index 000000000..9bcfdcd7b Binary files /dev/null and b/beta/public/favicon_dark.ico differ diff --git a/beta/public/fonts/Source-Code-Pro-Regular.woff2 b/beta/public/fonts/Source-Code-Pro-Regular.woff2 new file mode 100644 index 000000000..655cd9e81 Binary files /dev/null and b/beta/public/fonts/Source-Code-Pro-Regular.woff2 differ diff --git a/beta/public/html/single-file-example.html b/beta/public/html/single-file-example.html new file mode 100644 index 000000000..380a169f5 --- /dev/null +++ b/beta/public/html/single-file-example.html @@ -0,0 +1,36 @@ + + + + + Hello World + + + + + + + +

+ + + + diff --git a/beta/public/icons/logo-white.svg b/beta/public/icons/logo-white.svg new file mode 100644 index 000000000..7ed0e8b05 --- /dev/null +++ b/beta/public/icons/logo-white.svg @@ -0,0 +1,9 @@ + + React Logo + + + + + + + diff --git a/beta/public/icons/logo.svg b/beta/public/icons/logo.svg new file mode 100644 index 000000000..ea77a618d --- /dev/null +++ b/beta/public/icons/logo.svg @@ -0,0 +1,9 @@ + + React Logo + + + + + + + diff --git a/beta/public/images/blog/animal-sounds.jpg b/beta/public/images/blog/animal-sounds.jpg new file mode 100644 index 000000000..abe6d815e Binary files /dev/null and b/beta/public/images/blog/animal-sounds.jpg differ diff --git a/beta/public/images/blog/chatapp.png b/beta/public/images/blog/chatapp.png new file mode 100644 index 000000000..de8c09ff3 Binary files /dev/null and b/beta/public/images/blog/chatapp.png differ diff --git a/beta/public/images/blog/cra-better-output.png b/beta/public/images/blog/cra-better-output.png new file mode 100644 index 000000000..44ed320ff Binary files /dev/null and b/beta/public/images/blog/cra-better-output.png differ diff --git a/beta/public/images/blog/cra-dynamic-import.gif b/beta/public/images/blog/cra-dynamic-import.gif new file mode 100644 index 000000000..88db2896f Binary files /dev/null and b/beta/public/images/blog/cra-dynamic-import.gif differ diff --git a/beta/public/images/blog/cra-jest-search.gif b/beta/public/images/blog/cra-jest-search.gif new file mode 100644 index 000000000..ed6819d89 Binary files /dev/null and b/beta/public/images/blog/cra-jest-search.gif differ diff --git a/beta/public/images/blog/cra-pwa.png b/beta/public/images/blog/cra-pwa.png new file mode 100644 index 000000000..9862ae565 Binary files /dev/null and b/beta/public/images/blog/cra-pwa.png differ diff --git a/beta/public/images/blog/cra-runtime-error.gif b/beta/public/images/blog/cra-runtime-error.gif new file mode 100644 index 000000000..7b4405f62 Binary files /dev/null and b/beta/public/images/blog/cra-runtime-error.gif differ diff --git a/beta/public/images/blog/cra-update-exports.gif b/beta/public/images/blog/cra-update-exports.gif new file mode 100644 index 000000000..2820d63df Binary files /dev/null and b/beta/public/images/blog/cra-update-exports.gif differ diff --git a/beta/public/images/blog/create-apps-with-no-configuration/compiled-successfully.png b/beta/public/images/blog/create-apps-with-no-configuration/compiled-successfully.png new file mode 100644 index 000000000..223f5abf2 Binary files /dev/null and b/beta/public/images/blog/create-apps-with-no-configuration/compiled-successfully.png differ diff --git a/beta/public/images/blog/create-apps-with-no-configuration/compiled-with-warnings.png b/beta/public/images/blog/create-apps-with-no-configuration/compiled-with-warnings.png new file mode 100644 index 000000000..20aa25e3c Binary files /dev/null and b/beta/public/images/blog/create-apps-with-no-configuration/compiled-with-warnings.png differ diff --git a/beta/public/images/blog/create-apps-with-no-configuration/created-folder.png b/beta/public/images/blog/create-apps-with-no-configuration/created-folder.png new file mode 100644 index 000000000..44aff6dcd Binary files /dev/null and b/beta/public/images/blog/create-apps-with-no-configuration/created-folder.png differ diff --git a/beta/public/images/blog/create-apps-with-no-configuration/failed-to-compile.png b/beta/public/images/blog/create-apps-with-no-configuration/failed-to-compile.png new file mode 100644 index 000000000..a72b5fb2e Binary files /dev/null and b/beta/public/images/blog/create-apps-with-no-configuration/failed-to-compile.png differ diff --git a/beta/public/images/blog/create-apps-with-no-configuration/npm-run-build.png b/beta/public/images/blog/create-apps-with-no-configuration/npm-run-build.png new file mode 100644 index 000000000..a7b931e12 Binary files /dev/null and b/beta/public/images/blog/create-apps-with-no-configuration/npm-run-build.png differ diff --git a/beta/public/images/blog/devtools-component-filters.gif b/beta/public/images/blog/devtools-component-filters.gif new file mode 100644 index 000000000..287c66757 Binary files /dev/null and b/beta/public/images/blog/devtools-component-filters.gif differ diff --git a/beta/public/images/blog/devtools-full.gif b/beta/public/images/blog/devtools-full.gif new file mode 100644 index 000000000..fd7ed9493 Binary files /dev/null and b/beta/public/images/blog/devtools-full.gif differ diff --git a/beta/public/images/blog/devtools-highlight-updates.png b/beta/public/images/blog/devtools-highlight-updates.png new file mode 100644 index 000000000..780bcf2ef Binary files /dev/null and b/beta/public/images/blog/devtools-highlight-updates.png differ diff --git a/beta/public/images/blog/devtools-search.gif b/beta/public/images/blog/devtools-search.gif new file mode 100644 index 000000000..22d80051d Binary files /dev/null and b/beta/public/images/blog/devtools-search.gif differ diff --git a/beta/public/images/blog/devtools-side-pane.gif b/beta/public/images/blog/devtools-side-pane.gif new file mode 100644 index 000000000..381e3554e Binary files /dev/null and b/beta/public/images/blog/devtools-side-pane.gif differ diff --git a/beta/public/images/blog/devtools-tree-view.png b/beta/public/images/blog/devtools-tree-view.png new file mode 100644 index 000000000..854a39f8f Binary files /dev/null and b/beta/public/images/blog/devtools-tree-view.png differ diff --git a/beta/public/images/blog/devtools-v4-screenshot.png b/beta/public/images/blog/devtools-v4-screenshot.png new file mode 100644 index 000000000..83a67d548 Binary files /dev/null and b/beta/public/images/blog/devtools-v4-screenshot.png differ diff --git a/beta/public/images/blog/dog-tutorial.png b/beta/public/images/blog/dog-tutorial.png new file mode 100644 index 000000000..72f8b4341 Binary files /dev/null and b/beta/public/images/blog/dog-tutorial.png differ diff --git a/beta/public/images/blog/first-look.png b/beta/public/images/blog/first-look.png new file mode 100644 index 000000000..d36aa1f35 Binary files /dev/null and b/beta/public/images/blog/first-look.png differ diff --git a/beta/public/images/blog/flux-chart.png b/beta/public/images/blog/flux-chart.png new file mode 100644 index 000000000..fbfdf94ea Binary files /dev/null and b/beta/public/images/blog/flux-chart.png differ diff --git a/beta/public/images/blog/flux-diagram.png b/beta/public/images/blog/flux-diagram.png new file mode 100644 index 000000000..69cf64e0b Binary files /dev/null and b/beta/public/images/blog/flux-diagram.png differ diff --git a/beta/public/images/blog/genesis_skeleton.png b/beta/public/images/blog/genesis_skeleton.png new file mode 100644 index 000000000..4b7c51e29 Binary files /dev/null and b/beta/public/images/blog/genesis_skeleton.png differ diff --git a/beta/public/images/blog/gpu-cursor-move.gif b/beta/public/images/blog/gpu-cursor-move.gif new file mode 100644 index 000000000..b3a621f78 Binary files /dev/null and b/beta/public/images/blog/gpu-cursor-move.gif differ diff --git a/beta/public/images/blog/guess_filter.jpg b/beta/public/images/blog/guess_filter.jpg new file mode 100644 index 000000000..1983df1c8 Binary files /dev/null and b/beta/public/images/blog/guess_filter.jpg differ diff --git a/beta/public/images/blog/hacker-news-react-native.png b/beta/public/images/blog/hacker-news-react-native.png new file mode 100644 index 000000000..a65679aba Binary files /dev/null and b/beta/public/images/blog/hacker-news-react-native.png differ diff --git a/beta/public/images/blog/highlight-updates-example.gif b/beta/public/images/blog/highlight-updates-example.gif new file mode 100644 index 000000000..ab0c87d0c Binary files /dev/null and b/beta/public/images/blog/highlight-updates-example.gif differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/commit-selector.png b/beta/public/images/blog/introducing-the-react-profiler/commit-selector.png new file mode 100644 index 000000000..9c027444d Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/commit-selector.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/component-chart.png b/beta/public/images/blog/introducing-the-react-profiler/component-chart.png new file mode 100644 index 000000000..3a3615db2 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/component-chart.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/devtools-profiler-tab.png b/beta/public/images/blog/introducing-the-react-profiler/devtools-profiler-tab.png new file mode 100644 index 000000000..eabb3e500 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/devtools-profiler-tab.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/filtering-commits.gif b/beta/public/images/blog/introducing-the-react-profiler/filtering-commits.gif new file mode 100644 index 000000000..1d73258e3 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/filtering-commits.gif differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/flame-chart.png b/beta/public/images/blog/introducing-the-react-profiler/flame-chart.png new file mode 100644 index 000000000..80840b55c Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/flame-chart.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/interactions-for-commit.png b/beta/public/images/blog/introducing-the-react-profiler/interactions-for-commit.png new file mode 100644 index 000000000..8f1a14c61 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/interactions-for-commit.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/interactions.png b/beta/public/images/blog/introducing-the-react-profiler/interactions.png new file mode 100644 index 000000000..5683ac94a Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/interactions.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/navigate-between-interactions-and-commits.gif b/beta/public/images/blog/introducing-the-react-profiler/navigate-between-interactions-and-commits.gif new file mode 100644 index 000000000..e500459c3 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/navigate-between-interactions-and-commits.gif differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/no-interactions.png b/beta/public/images/blog/introducing-the-react-profiler/no-interactions.png new file mode 100644 index 000000000..d70199fc7 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/no-interactions.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/no-profiler-data-multi-root.png b/beta/public/images/blog/introducing-the-react-profiler/no-profiler-data-multi-root.png new file mode 100644 index 000000000..e56fd4128 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/no-profiler-data-multi-root.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/no-render-times-for-selected-component.png b/beta/public/images/blog/introducing-the-react-profiler/no-render-times-for-selected-component.png new file mode 100644 index 000000000..f77b42f85 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/no-render-times-for-selected-component.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/no-timing-data-for-commit.png b/beta/public/images/blog/introducing-the-react-profiler/no-timing-data-for-commit.png new file mode 100644 index 000000000..84a4dcac2 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/no-timing-data-for-commit.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/props-and-state.gif b/beta/public/images/blog/introducing-the-react-profiler/props-and-state.gif new file mode 100644 index 000000000..b0b05b127 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/props-and-state.gif differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/ranked-chart.png b/beta/public/images/blog/introducing-the-react-profiler/ranked-chart.png new file mode 100644 index 000000000..01246f5e5 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/ranked-chart.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/see-all-commits-for-a-fiber.gif b/beta/public/images/blog/introducing-the-react-profiler/see-all-commits-for-a-fiber.gif new file mode 100644 index 000000000..dac21e249 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/see-all-commits-for-a-fiber.gif differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/see-which-props-changed.gif b/beta/public/images/blog/introducing-the-react-profiler/see-which-props-changed.gif new file mode 100644 index 000000000..ae965be9b Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/see-which-props-changed.gif differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/select-a-root-to-view-profiling-data.gif b/beta/public/images/blog/introducing-the-react-profiler/select-a-root-to-view-profiling-data.gif new file mode 100644 index 000000000..b53f31025 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/select-a-root-to-view-profiling-data.gif differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/start-profiling.png b/beta/public/images/blog/introducing-the-react-profiler/start-profiling.png new file mode 100644 index 000000000..7256474c0 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/start-profiling.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/stop-profiling.png b/beta/public/images/blog/introducing-the-react-profiler/stop-profiling.png new file mode 100644 index 000000000..4ef27f333 Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/stop-profiling.png differ diff --git a/beta/public/images/blog/introducing-the-react-profiler/zoom-in-and-out.gif b/beta/public/images/blog/introducing-the-react-profiler/zoom-in-and-out.gif new file mode 100644 index 000000000..3a8be2c9b Binary files /dev/null and b/beta/public/images/blog/introducing-the-react-profiler/zoom-in-and-out.gif differ diff --git a/beta/public/images/blog/jsx-compiler.png b/beta/public/images/blog/jsx-compiler.png new file mode 100644 index 000000000..9b5e169c0 Binary files /dev/null and b/beta/public/images/blog/jsx-compiler.png differ diff --git a/beta/public/images/blog/kendoui.png b/beta/public/images/blog/kendoui.png new file mode 100644 index 000000000..8b18bf438 Binary files /dev/null and b/beta/public/images/blog/kendoui.png differ diff --git a/beta/public/images/blog/khan-academy-editor.png b/beta/public/images/blog/khan-academy-editor.png new file mode 100644 index 000000000..f0413939d Binary files /dev/null and b/beta/public/images/blog/khan-academy-editor.png differ diff --git a/beta/public/images/blog/landoflisp.png b/beta/public/images/blog/landoflisp.png new file mode 100644 index 000000000..42bad2d4b Binary files /dev/null and b/beta/public/images/blog/landoflisp.png differ diff --git a/beta/public/images/blog/lights-out.png b/beta/public/images/blog/lights-out.png new file mode 100644 index 000000000..78c545e7e Binary files /dev/null and b/beta/public/images/blog/lights-out.png differ diff --git a/beta/public/images/blog/makona-editor.png b/beta/public/images/blog/makona-editor.png new file mode 100644 index 000000000..4fc4ece99 Binary files /dev/null and b/beta/public/images/blog/makona-editor.png differ diff --git a/beta/public/images/blog/markdown_refactor.png b/beta/public/images/blog/markdown_refactor.png new file mode 100644 index 000000000..b81679541 Binary files /dev/null and b/beta/public/images/blog/markdown_refactor.png differ diff --git a/beta/public/images/blog/modus-create.gif b/beta/public/images/blog/modus-create.gif new file mode 100644 index 000000000..194252ad1 Binary files /dev/null and b/beta/public/images/blog/modus-create.gif differ diff --git a/beta/public/images/blog/monkeys.gif b/beta/public/images/blog/monkeys.gif new file mode 100644 index 000000000..3da6185ce Binary files /dev/null and b/beta/public/images/blog/monkeys.gif differ diff --git a/beta/public/images/blog/ngreact.png b/beta/public/images/blog/ngreact.png new file mode 100644 index 000000000..6ce7e3418 Binary files /dev/null and b/beta/public/images/blog/ngreact.png differ diff --git a/beta/public/images/blog/om-backbone.png b/beta/public/images/blog/om-backbone.png new file mode 100644 index 000000000..df411b280 Binary files /dev/null and b/beta/public/images/blog/om-backbone.png differ diff --git a/beta/public/images/blog/parse-react.jpg b/beta/public/images/blog/parse-react.jpg new file mode 100644 index 000000000..15e45d2ae Binary files /dev/null and b/beta/public/images/blog/parse-react.jpg differ diff --git a/beta/public/images/blog/polarr.jpg b/beta/public/images/blog/polarr.jpg new file mode 100644 index 000000000..1cfb3cc90 Binary files /dev/null and b/beta/public/images/blog/polarr.jpg differ diff --git a/beta/public/images/blog/propeller-logo.png b/beta/public/images/blog/propeller-logo.png new file mode 100644 index 000000000..81cfaa181 Binary files /dev/null and b/beta/public/images/blog/propeller-logo.png differ diff --git a/beta/public/images/blog/property-finder.png b/beta/public/images/blog/property-finder.png new file mode 100644 index 000000000..709448b4f Binary files /dev/null and b/beta/public/images/blog/property-finder.png differ diff --git a/beta/public/images/blog/quiztime.png b/beta/public/images/blog/quiztime.png new file mode 100644 index 000000000..7021036e6 Binary files /dev/null and b/beta/public/images/blog/quiztime.png differ diff --git a/beta/public/images/blog/react-50k-mock-full.jpg b/beta/public/images/blog/react-50k-mock-full.jpg new file mode 100644 index 000000000..1ebac0063 Binary files /dev/null and b/beta/public/images/blog/react-50k-mock-full.jpg differ diff --git a/beta/public/images/blog/react-50k-mock.jpg b/beta/public/images/blog/react-50k-mock.jpg new file mode 100644 index 000000000..bc2de61d4 Binary files /dev/null and b/beta/public/images/blog/react-50k-mock.jpg differ diff --git a/beta/public/images/blog/react-50k-tshirt.jpg b/beta/public/images/blog/react-50k-tshirt.jpg new file mode 100644 index 000000000..c96b44ca5 Binary files /dev/null and b/beta/public/images/blog/react-50k-tshirt.jpg differ diff --git a/beta/public/images/blog/react-browserify-gulp.jpg b/beta/public/images/blog/react-browserify-gulp.jpg new file mode 100644 index 000000000..d4389d6e9 Binary files /dev/null and b/beta/public/images/blog/react-browserify-gulp.jpg differ diff --git a/beta/public/images/blog/react-dev-tools.jpg b/beta/public/images/blog/react-dev-tools.jpg new file mode 100644 index 000000000..7eef44629 Binary files /dev/null and b/beta/public/images/blog/react-dev-tools.jpg differ diff --git a/beta/public/images/blog/react-diff-tree.png b/beta/public/images/blog/react-diff-tree.png new file mode 100644 index 000000000..9935e4ae1 Binary files /dev/null and b/beta/public/images/blog/react-diff-tree.png differ diff --git a/beta/public/images/blog/react-draggable.png b/beta/public/images/blog/react-draggable.png new file mode 100644 index 000000000..c0cb61f16 Binary files /dev/null and b/beta/public/images/blog/react-draggable.png differ diff --git a/beta/public/images/blog/react-hackathon.jpg b/beta/public/images/blog/react-hackathon.jpg new file mode 100644 index 000000000..2537d9865 Binary files /dev/null and b/beta/public/images/blog/react-hackathon.jpg differ diff --git a/beta/public/images/blog/react-page.png b/beta/public/images/blog/react-page.png new file mode 100644 index 000000000..3249fb370 Binary files /dev/null and b/beta/public/images/blog/react-page.png differ diff --git a/beta/public/images/blog/react-perf-chrome-timeline.png b/beta/public/images/blog/react-perf-chrome-timeline.png new file mode 100644 index 000000000..e9db8acfb Binary files /dev/null and b/beta/public/images/blog/react-perf-chrome-timeline.png differ diff --git a/beta/public/images/blog/react-php.png b/beta/public/images/blog/react-php.png new file mode 100644 index 000000000..cb7e69abc Binary files /dev/null and b/beta/public/images/blog/react-php.png differ diff --git a/beta/public/images/blog/react-svg-fbp.png b/beta/public/images/blog/react-svg-fbp.png new file mode 100644 index 000000000..ef89babad Binary files /dev/null and b/beta/public/images/blog/react-svg-fbp.png differ diff --git a/beta/public/images/blog/react-v16.13.0/hydration-warning-after.png b/beta/public/images/blog/react-v16.13.0/hydration-warning-after.png new file mode 100644 index 000000000..92b6c9919 Binary files /dev/null and b/beta/public/images/blog/react-v16.13.0/hydration-warning-after.png differ diff --git a/beta/public/images/blog/react-v16.13.0/hydration-warning-before.png b/beta/public/images/blog/react-v16.13.0/hydration-warning-before.png new file mode 100644 index 000000000..381ab8677 Binary files /dev/null and b/beta/public/images/blog/react-v16.13.0/hydration-warning-before.png differ diff --git a/beta/public/images/blog/react-v16.9.0/codemod.gif b/beta/public/images/blog/react-v16.9.0/codemod.gif new file mode 100644 index 000000000..955b30fdc Binary files /dev/null and b/beta/public/images/blog/react-v16.9.0/codemod.gif differ diff --git a/beta/public/images/blog/react-v16.9.0/cwm-warning.png b/beta/public/images/blog/react-v16.9.0/cwm-warning.png new file mode 100644 index 000000000..3ee69d748 Binary files /dev/null and b/beta/public/images/blog/react-v16.9.0/cwm-warning.png differ diff --git a/beta/public/images/blog/react-v17-rc/react_17_delegation.png b/beta/public/images/blog/react-v17-rc/react_17_delegation.png new file mode 100644 index 000000000..c8b23c0d6 Binary files /dev/null and b/beta/public/images/blog/react-v17-rc/react_17_delegation.png differ diff --git a/beta/public/images/blog/reactive-bookmarklet.png b/beta/public/images/blog/reactive-bookmarklet.png new file mode 100644 index 000000000..25351e717 Binary files /dev/null and b/beta/public/images/blog/reactive-bookmarklet.png differ diff --git a/beta/public/images/blog/reflux-flux.png b/beta/public/images/blog/reflux-flux.png new file mode 100644 index 000000000..dad38c7d1 Binary files /dev/null and b/beta/public/images/blog/reflux-flux.png differ diff --git a/beta/public/images/blog/relay-components/relay-architecture.png b/beta/public/images/blog/relay-components/relay-architecture.png new file mode 100644 index 000000000..0cfbff0d8 Binary files /dev/null and b/beta/public/images/blog/relay-components/relay-architecture.png differ diff --git a/beta/public/images/blog/relay-components/relay-containers-data-flow.png b/beta/public/images/blog/relay-components/relay-containers-data-flow.png new file mode 100644 index 000000000..2f062dd65 Binary files /dev/null and b/beta/public/images/blog/relay-components/relay-containers-data-flow.png differ diff --git a/beta/public/images/blog/relay-components/relay-containers.png b/beta/public/images/blog/relay-components/relay-containers.png new file mode 100644 index 000000000..be7dee719 Binary files /dev/null and b/beta/public/images/blog/relay-components/relay-containers.png differ diff --git a/beta/public/images/blog/relay-components/sample-newsfeed.png b/beta/public/images/blog/relay-components/sample-newsfeed.png new file mode 100644 index 000000000..0e7f5b5de Binary files /dev/null and b/beta/public/images/blog/relay-components/sample-newsfeed.png differ diff --git a/beta/public/images/blog/relay-visual-architecture-tour.png b/beta/public/images/blog/relay-visual-architecture-tour.png new file mode 100644 index 000000000..b35c4978f Binary files /dev/null and b/beta/public/images/blog/relay-visual-architecture-tour.png differ diff --git a/beta/public/images/blog/release-script-build-confirmation.png b/beta/public/images/blog/release-script-build-confirmation.png new file mode 100644 index 000000000..02026d172 Binary files /dev/null and b/beta/public/images/blog/release-script-build-confirmation.png differ diff --git a/beta/public/images/blog/release-script-build-overview.png b/beta/public/images/blog/release-script-build-overview.png new file mode 100644 index 000000000..d726be464 Binary files /dev/null and b/beta/public/images/blog/release-script-build-overview.png differ diff --git a/beta/public/images/blog/release-script-publish-confirmation.png b/beta/public/images/blog/release-script-publish-confirmation.png new file mode 100644 index 000000000..e05e64830 Binary files /dev/null and b/beta/public/images/blog/release-script-publish-confirmation.png differ diff --git a/beta/public/images/blog/resistance-calculator.png b/beta/public/images/blog/resistance-calculator.png new file mode 100644 index 000000000..16e8b3536 Binary files /dev/null and b/beta/public/images/blog/resistance-calculator.png differ diff --git a/beta/public/images/blog/skills-matter.png b/beta/public/images/blog/skills-matter.png new file mode 100644 index 000000000..4a4858c3d Binary files /dev/null and b/beta/public/images/blog/skills-matter.png differ diff --git a/beta/public/images/blog/snake.png b/beta/public/images/blog/snake.png new file mode 100644 index 000000000..96d72b38a Binary files /dev/null and b/beta/public/images/blog/snake.png differ diff --git a/beta/public/images/blog/steve_reverse.gif b/beta/public/images/blog/steve_reverse.gif new file mode 100644 index 000000000..a442fbbd9 Binary files /dev/null and b/beta/public/images/blog/steve_reverse.gif differ diff --git a/beta/public/images/blog/strict-mode-unsafe-lifecycles-warning.png b/beta/public/images/blog/strict-mode-unsafe-lifecycles-warning.png new file mode 100644 index 000000000..fbdeccde6 Binary files /dev/null and b/beta/public/images/blog/strict-mode-unsafe-lifecycles-warning.png differ diff --git a/beta/public/images/blog/sweet-jsx.png b/beta/public/images/blog/sweet-jsx.png new file mode 100644 index 000000000..f20aeede4 Binary files /dev/null and b/beta/public/images/blog/sweet-jsx.png differ diff --git a/beta/public/images/blog/tcomb-react-native.png b/beta/public/images/blog/tcomb-react-native.png new file mode 100644 index 000000000..98120c758 Binary files /dev/null and b/beta/public/images/blog/tcomb-react-native.png differ diff --git a/beta/public/images/blog/thinking-in-react-components.png b/beta/public/images/blog/thinking-in-react-components.png new file mode 100644 index 000000000..c71a86bcb Binary files /dev/null and b/beta/public/images/blog/thinking-in-react-components.png differ diff --git a/beta/public/images/blog/thinking-in-react-mock.png b/beta/public/images/blog/thinking-in-react-mock.png new file mode 100644 index 000000000..78bd00a4a Binary files /dev/null and b/beta/public/images/blog/thinking-in-react-mock.png differ diff --git a/beta/public/images/blog/todomvc.png b/beta/public/images/blog/todomvc.png new file mode 100644 index 000000000..ee78eb1cd Binary files /dev/null and b/beta/public/images/blog/todomvc.png differ diff --git a/beta/public/images/blog/turboreact.png b/beta/public/images/blog/turboreact.png new file mode 100644 index 000000000..e8ef8cd3a Binary files /dev/null and b/beta/public/images/blog/turboreact.png differ diff --git a/beta/public/images/blog/tutsplus.png b/beta/public/images/blog/tutsplus.png new file mode 100644 index 000000000..771653086 Binary files /dev/null and b/beta/public/images/blog/tutsplus.png differ diff --git a/beta/public/images/blog/unite.png b/beta/public/images/blog/unite.png new file mode 100644 index 000000000..ab45a5355 Binary files /dev/null and b/beta/public/images/blog/unite.png differ diff --git a/beta/public/images/blog/versioning-1.png b/beta/public/images/blog/versioning-1.png new file mode 100644 index 000000000..c13f98fd1 Binary files /dev/null and b/beta/public/images/blog/versioning-1.png differ diff --git a/beta/public/images/blog/versioning-2.png b/beta/public/images/blog/versioning-2.png new file mode 100644 index 000000000..39de2a010 Binary files /dev/null and b/beta/public/images/blog/versioning-2.png differ diff --git a/beta/public/images/blog/versioning-3.png b/beta/public/images/blog/versioning-3.png new file mode 100644 index 000000000..1824e89a9 Binary files /dev/null and b/beta/public/images/blog/versioning-3.png differ diff --git a/beta/public/images/blog/versioning-4.png b/beta/public/images/blog/versioning-4.png new file mode 100644 index 000000000..13ba32e39 Binary files /dev/null and b/beta/public/images/blog/versioning-4.png differ diff --git a/beta/public/images/blog/versioning-5.png b/beta/public/images/blog/versioning-5.png new file mode 100644 index 000000000..542a3926b Binary files /dev/null and b/beta/public/images/blog/versioning-5.png differ diff --git a/beta/public/images/blog/versioning-6.png b/beta/public/images/blog/versioning-6.png new file mode 100644 index 000000000..e82bc7136 Binary files /dev/null and b/beta/public/images/blog/versioning-6.png differ diff --git a/beta/public/images/blog/versioning-poll.png b/beta/public/images/blog/versioning-poll.png new file mode 100644 index 000000000..8b3e18d93 Binary files /dev/null and b/beta/public/images/blog/versioning-poll.png differ diff --git a/beta/public/images/blog/warn-legacy-context-in-strict-mode.png b/beta/public/images/blog/warn-legacy-context-in-strict-mode.png new file mode 100644 index 000000000..e061325ac Binary files /dev/null and b/beta/public/images/blog/warn-legacy-context-in-strict-mode.png differ diff --git a/beta/public/images/blog/weather.png b/beta/public/images/blog/weather.png new file mode 100644 index 000000000..90c5e6fea Binary files /dev/null and b/beta/public/images/blog/weather.png differ diff --git a/beta/public/images/blog/wolfenstein_react.png b/beta/public/images/blog/wolfenstein_react.png new file mode 100644 index 000000000..98241b864 Binary files /dev/null and b/beta/public/images/blog/wolfenstein_react.png differ diff --git a/beta/public/images/blog/xoxo2013.png b/beta/public/images/blog/xoxo2013.png new file mode 100644 index 000000000..d35989095 Binary files /dev/null and b/beta/public/images/blog/xoxo2013.png differ diff --git a/beta/public/images/blog/xreact.png b/beta/public/images/blog/xreact.png new file mode 100644 index 000000000..ba23489d5 Binary files /dev/null and b/beta/public/images/blog/xreact.png differ diff --git a/beta/public/images/docs/blur-popover-close.gif b/beta/public/images/docs/blur-popover-close.gif new file mode 100644 index 000000000..fefc6b8a4 Binary files /dev/null and b/beta/public/images/docs/blur-popover-close.gif differ diff --git a/beta/public/images/docs/cdn-cors-header.png b/beta/public/images/docs/cdn-cors-header.png new file mode 100644 index 000000000..31b047cbd Binary files /dev/null and b/beta/public/images/docs/cdn-cors-header.png differ diff --git a/beta/public/images/docs/cm-steps-simple.png b/beta/public/images/docs/cm-steps-simple.png new file mode 100644 index 000000000..e5683f9e6 Binary files /dev/null and b/beta/public/images/docs/cm-steps-simple.png differ diff --git a/beta/public/images/docs/codewinds-004.png b/beta/public/images/docs/codewinds-004.png new file mode 100644 index 000000000..6c4bc997f Binary files /dev/null and b/beta/public/images/docs/codewinds-004.png differ diff --git a/beta/public/images/docs/devtools-dev.png b/beta/public/images/docs/devtools-dev.png new file mode 100644 index 000000000..5347b4b8d Binary files /dev/null and b/beta/public/images/docs/devtools-dev.png differ diff --git a/beta/public/images/docs/devtools-prod.png b/beta/public/images/docs/devtools-prod.png new file mode 100644 index 000000000..4e200fb48 Binary files /dev/null and b/beta/public/images/docs/devtools-prod.png differ diff --git a/beta/public/images/docs/diagrams/passing_data_context_close.dark.png b/beta/public/images/docs/diagrams/passing_data_context_close.dark.png new file mode 100644 index 000000000..59045299d Binary files /dev/null and b/beta/public/images/docs/diagrams/passing_data_context_close.dark.png differ diff --git a/beta/public/images/docs/diagrams/passing_data_context_close.png b/beta/public/images/docs/diagrams/passing_data_context_close.png new file mode 100644 index 000000000..4591aacce Binary files /dev/null and b/beta/public/images/docs/diagrams/passing_data_context_close.png differ diff --git a/beta/public/images/docs/diagrams/passing_data_context_far.dark.png b/beta/public/images/docs/diagrams/passing_data_context_far.dark.png new file mode 100644 index 000000000..7e0d0bc58 Binary files /dev/null and b/beta/public/images/docs/diagrams/passing_data_context_far.dark.png differ diff --git a/beta/public/images/docs/diagrams/passing_data_context_far.png b/beta/public/images/docs/diagrams/passing_data_context_far.png new file mode 100644 index 000000000..7a4c85150 Binary files /dev/null and b/beta/public/images/docs/diagrams/passing_data_context_far.png differ diff --git a/beta/public/images/docs/diagrams/passing_data_lifting_state.dark.png b/beta/public/images/docs/diagrams/passing_data_lifting_state.dark.png new file mode 100644 index 000000000..dd7b48556 Binary files /dev/null and b/beta/public/images/docs/diagrams/passing_data_lifting_state.dark.png differ diff --git a/beta/public/images/docs/diagrams/passing_data_lifting_state.png b/beta/public/images/docs/diagrams/passing_data_lifting_state.png new file mode 100644 index 000000000..919b4e684 Binary files /dev/null and b/beta/public/images/docs/diagrams/passing_data_lifting_state.png differ diff --git a/beta/public/images/docs/diagrams/passing_data_prop_drilling.dark.png b/beta/public/images/docs/diagrams/passing_data_prop_drilling.dark.png new file mode 100644 index 000000000..1cbd5a2f8 Binary files /dev/null and b/beta/public/images/docs/diagrams/passing_data_prop_drilling.dark.png differ diff --git a/beta/public/images/docs/diagrams/passing_data_prop_drilling.png b/beta/public/images/docs/diagrams/passing_data_prop_drilling.png new file mode 100644 index 000000000..c22cb2c52 Binary files /dev/null and b/beta/public/images/docs/diagrams/passing_data_prop_drilling.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_add_component.dark.png b/beta/public/images/docs/diagrams/preserving_state_add_component.dark.png new file mode 100644 index 000000000..24155f936 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_add_component.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_add_component.png b/beta/public/images/docs/diagrams/preserving_state_add_component.png new file mode 100644 index 000000000..d98e7bd3d Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_add_component.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_position_p1.dark.png b/beta/public/images/docs/diagrams/preserving_state_diff_position_p1.dark.png new file mode 100644 index 000000000..b2c2efc67 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_position_p1.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_position_p1.png b/beta/public/images/docs/diagrams/preserving_state_diff_position_p1.png new file mode 100644 index 000000000..84d4ef52d Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_position_p1.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_position_p2.dark.png b/beta/public/images/docs/diagrams/preserving_state_diff_position_p2.dark.png new file mode 100644 index 000000000..9472414cf Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_position_p2.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_position_p2.png b/beta/public/images/docs/diagrams/preserving_state_diff_position_p2.png new file mode 100644 index 000000000..fad05b2e2 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_position_p2.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_position_p3.dark.png b/beta/public/images/docs/diagrams/preserving_state_diff_position_p3.dark.png new file mode 100644 index 000000000..2b520eb6a Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_position_p3.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_position_p3.png b/beta/public/images/docs/diagrams/preserving_state_diff_position_p3.png new file mode 100644 index 000000000..1fbbe510a Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_position_p3.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_pt1.dark.png b/beta/public/images/docs/diagrams/preserving_state_diff_pt1.dark.png new file mode 100644 index 000000000..0bb042c94 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_pt1.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_pt1.png b/beta/public/images/docs/diagrams/preserving_state_diff_pt1.png new file mode 100644 index 000000000..1cbc6874f Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_pt1.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_pt2.dark.png b/beta/public/images/docs/diagrams/preserving_state_diff_pt2.dark.png new file mode 100644 index 000000000..ae6bdf95b Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_pt2.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_pt2.png b/beta/public/images/docs/diagrams/preserving_state_diff_pt2.png new file mode 100644 index 000000000..3d52b278e Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_pt2.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_same_pt1.dark.png b/beta/public/images/docs/diagrams/preserving_state_diff_same_pt1.dark.png new file mode 100644 index 000000000..f0eb4a786 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_same_pt1.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_same_pt1.png b/beta/public/images/docs/diagrams/preserving_state_diff_same_pt1.png new file mode 100644 index 000000000..3d22270d8 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_same_pt1.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_same_pt2.dark.png b/beta/public/images/docs/diagrams/preserving_state_diff_same_pt2.dark.png new file mode 100644 index 000000000..07ef5a761 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_same_pt2.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_diff_same_pt2.png b/beta/public/images/docs/diagrams/preserving_state_diff_same_pt2.png new file mode 100644 index 000000000..1bbc87a3e Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_diff_same_pt2.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_dom_tree.dark.png b/beta/public/images/docs/diagrams/preserving_state_dom_tree.dark.png new file mode 100644 index 000000000..4f7c8afdc Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_dom_tree.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_dom_tree.png b/beta/public/images/docs/diagrams/preserving_state_dom_tree.png new file mode 100644 index 000000000..3b38a1bd3 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_dom_tree.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_increment.dark.png b/beta/public/images/docs/diagrams/preserving_state_increment.dark.png new file mode 100644 index 000000000..5bc133611 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_increment.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_increment.png b/beta/public/images/docs/diagrams/preserving_state_increment.png new file mode 100644 index 000000000..9b9ce9ca8 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_increment.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_remove_component.dark.png b/beta/public/images/docs/diagrams/preserving_state_remove_component.dark.png new file mode 100644 index 000000000..cb6a466ac Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_remove_component.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_remove_component.png b/beta/public/images/docs/diagrams/preserving_state_remove_component.png new file mode 100644 index 000000000..966c1744b Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_remove_component.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_same_component.dark.png b/beta/public/images/docs/diagrams/preserving_state_same_component.dark.png new file mode 100644 index 000000000..013fcaa80 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_same_component.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_same_component.png b/beta/public/images/docs/diagrams/preserving_state_same_component.png new file mode 100644 index 000000000..4cb1b32ed Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_same_component.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_tree.dark.png b/beta/public/images/docs/diagrams/preserving_state_tree.dark.png new file mode 100644 index 000000000..452343857 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_tree.dark.png differ diff --git a/beta/public/images/docs/diagrams/preserving_state_tree.png b/beta/public/images/docs/diagrams/preserving_state_tree.png new file mode 100644 index 000000000..a77e49ab2 Binary files /dev/null and b/beta/public/images/docs/diagrams/preserving_state_tree.png differ diff --git a/beta/public/images/docs/diagrams/responding_to_input_flow.dark.png b/beta/public/images/docs/diagrams/responding_to_input_flow.dark.png new file mode 100644 index 000000000..60e5c8f36 Binary files /dev/null and b/beta/public/images/docs/diagrams/responding_to_input_flow.dark.png differ diff --git a/beta/public/images/docs/diagrams/responding_to_input_flow.png b/beta/public/images/docs/diagrams/responding_to_input_flow.png new file mode 100644 index 000000000..cbbec9ac3 Binary files /dev/null and b/beta/public/images/docs/diagrams/responding_to_input_flow.png differ diff --git a/beta/public/images/docs/diagrams/sharing_data_child.dark.png b/beta/public/images/docs/diagrams/sharing_data_child.dark.png new file mode 100644 index 000000000..3efdc29fe Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_data_child.dark.png differ diff --git a/beta/public/images/docs/diagrams/sharing_data_child.png b/beta/public/images/docs/diagrams/sharing_data_child.png new file mode 100644 index 000000000..afc794bcd Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_data_child.png differ diff --git a/beta/public/images/docs/diagrams/sharing_data_child_clicked.dark.png b/beta/public/images/docs/diagrams/sharing_data_child_clicked.dark.png new file mode 100644 index 000000000..cf60c235f Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_data_child_clicked.dark.png differ diff --git a/beta/public/images/docs/diagrams/sharing_data_child_clicked.png b/beta/public/images/docs/diagrams/sharing_data_child_clicked.png new file mode 100644 index 000000000..c14fb4a8f Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_data_child_clicked.png differ diff --git a/beta/public/images/docs/diagrams/sharing_data_parent.dark.png b/beta/public/images/docs/diagrams/sharing_data_parent.dark.png new file mode 100644 index 000000000..d4f369835 Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_data_parent.dark.png differ diff --git a/beta/public/images/docs/diagrams/sharing_data_parent.png b/beta/public/images/docs/diagrams/sharing_data_parent.png new file mode 100644 index 000000000..8c7b70305 Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_data_parent.png differ diff --git a/beta/public/images/docs/diagrams/sharing_data_parent_clicked.dark.png b/beta/public/images/docs/diagrams/sharing_data_parent_clicked.dark.png new file mode 100644 index 000000000..ac4c765de Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_data_parent_clicked.dark.png differ diff --git a/beta/public/images/docs/diagrams/sharing_data_parent_clicked.png b/beta/public/images/docs/diagrams/sharing_data_parent_clicked.png new file mode 100644 index 000000000..ca16cb8dd Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_data_parent_clicked.png differ diff --git a/beta/public/images/docs/diagrams/sharing_state_child.dark.png b/beta/public/images/docs/diagrams/sharing_state_child.dark.png new file mode 100644 index 000000000..dfaf897b0 Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_state_child.dark.png differ diff --git a/beta/public/images/docs/diagrams/sharing_state_child.png b/beta/public/images/docs/diagrams/sharing_state_child.png new file mode 100644 index 000000000..c3a9f0ec9 Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_state_child.png differ diff --git a/beta/public/images/docs/diagrams/sharing_state_child_clicked.dark.png b/beta/public/images/docs/diagrams/sharing_state_child_clicked.dark.png new file mode 100644 index 000000000..bc6b3c89a Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_state_child_clicked.dark.png differ diff --git a/beta/public/images/docs/diagrams/sharing_state_child_clicked.png b/beta/public/images/docs/diagrams/sharing_state_child_clicked.png new file mode 100644 index 000000000..d4dddbd9b Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_state_child_clicked.png differ diff --git a/beta/public/images/docs/diagrams/sharing_state_parent.dark.png b/beta/public/images/docs/diagrams/sharing_state_parent.dark.png new file mode 100644 index 000000000..ba937de7e Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_state_parent.dark.png differ diff --git a/beta/public/images/docs/diagrams/sharing_state_parent.png b/beta/public/images/docs/diagrams/sharing_state_parent.png new file mode 100644 index 000000000..3aaaef724 Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_state_parent.png differ diff --git a/beta/public/images/docs/diagrams/sharing_state_parent_clicked.dark.png b/beta/public/images/docs/diagrams/sharing_state_parent_clicked.dark.png new file mode 100644 index 000000000..22ef522b4 Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_state_parent_clicked.dark.png differ diff --git a/beta/public/images/docs/diagrams/sharing_state_parent_clicked.png b/beta/public/images/docs/diagrams/sharing_state_parent_clicked.png new file mode 100644 index 000000000..406cd8314 Binary files /dev/null and b/beta/public/images/docs/diagrams/sharing_state_parent_clicked.png differ diff --git a/beta/public/images/docs/diagrams/writing_jsx_form.dark.png b/beta/public/images/docs/diagrams/writing_jsx_form.dark.png new file mode 100644 index 000000000..3f7184239 Binary files /dev/null and b/beta/public/images/docs/diagrams/writing_jsx_form.dark.png differ diff --git a/beta/public/images/docs/diagrams/writing_jsx_form.png b/beta/public/images/docs/diagrams/writing_jsx_form.png new file mode 100644 index 000000000..21baa97fc Binary files /dev/null and b/beta/public/images/docs/diagrams/writing_jsx_form.png differ diff --git a/beta/public/images/docs/diagrams/writing_jsx_html.dark.png b/beta/public/images/docs/diagrams/writing_jsx_html.dark.png new file mode 100644 index 000000000..c73d3fb42 Binary files /dev/null and b/beta/public/images/docs/diagrams/writing_jsx_html.dark.png differ diff --git a/beta/public/images/docs/diagrams/writing_jsx_html.png b/beta/public/images/docs/diagrams/writing_jsx_html.png new file mode 100644 index 000000000..6f30314a3 Binary files /dev/null and b/beta/public/images/docs/diagrams/writing_jsx_html.png differ diff --git a/beta/public/images/docs/diagrams/writing_jsx_js.dark.png b/beta/public/images/docs/diagrams/writing_jsx_js.dark.png new file mode 100644 index 000000000..aee00cb98 Binary files /dev/null and b/beta/public/images/docs/diagrams/writing_jsx_js.dark.png differ diff --git a/beta/public/images/docs/diagrams/writing_jsx_js.png b/beta/public/images/docs/diagrams/writing_jsx_js.png new file mode 100644 index 000000000..9c3d44216 Binary files /dev/null and b/beta/public/images/docs/diagrams/writing_jsx_js.png differ diff --git a/beta/public/images/docs/diagrams/writing_jsx_sidebar.dark.png b/beta/public/images/docs/diagrams/writing_jsx_sidebar.dark.png new file mode 100644 index 000000000..5fa45be1c Binary files /dev/null and b/beta/public/images/docs/diagrams/writing_jsx_sidebar.dark.png differ diff --git a/beta/public/images/docs/diagrams/writing_jsx_sidebar.png b/beta/public/images/docs/diagrams/writing_jsx_sidebar.png new file mode 100644 index 000000000..b1474562d Binary files /dev/null and b/beta/public/images/docs/diagrams/writing_jsx_sidebar.png differ diff --git a/beta/public/images/docs/error-boundaries-stack-trace-line-numbers.png b/beta/public/images/docs/error-boundaries-stack-trace-line-numbers.png new file mode 100644 index 000000000..db828905a Binary files /dev/null and b/beta/public/images/docs/error-boundaries-stack-trace-line-numbers.png differ diff --git a/beta/public/images/docs/error-boundaries-stack-trace.png b/beta/public/images/docs/error-boundaries-stack-trace.png new file mode 100644 index 000000000..f0d49d903 Binary files /dev/null and b/beta/public/images/docs/error-boundaries-stack-trace.png differ diff --git a/beta/public/images/docs/granular-dom-updates.gif b/beta/public/images/docs/granular-dom-updates.gif new file mode 100644 index 000000000..1651b0dae Binary files /dev/null and b/beta/public/images/docs/granular-dom-updates.gif differ diff --git a/beta/public/images/docs/illustrations/i_browser-paint.png b/beta/public/images/docs/illustrations/i_browser-paint.png new file mode 100644 index 000000000..b0d61fa8f Binary files /dev/null and b/beta/public/images/docs/illustrations/i_browser-paint.png differ diff --git a/beta/public/images/docs/illustrations/i_children-prop.png b/beta/public/images/docs/illustrations/i_children-prop.png new file mode 100644 index 000000000..296c626dc Binary files /dev/null and b/beta/public/images/docs/illustrations/i_children-prop.png differ diff --git a/beta/public/images/docs/illustrations/i_declarative-ui-programming.png b/beta/public/images/docs/illustrations/i_declarative-ui-programming.png new file mode 100644 index 000000000..206d73e51 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_declarative-ui-programming.png differ diff --git a/beta/public/images/docs/illustrations/i_html_js.svg b/beta/public/images/docs/illustrations/i_html_js.svg new file mode 100644 index 000000000..9b6f5bad8 --- /dev/null +++ b/beta/public/images/docs/illustrations/i_html_js.svg @@ -0,0 +1,95 @@ + + + + + + + + + onSubmit() { ... } + + + + login() { ... } + + + + onClick() { ... } + + JS + + + + + + + + <div> + + + + <form> + + + + <p> + + HTML + + diff --git a/beta/public/images/docs/illustrations/i_imperative-ui-programming.png b/beta/public/images/docs/illustrations/i_imperative-ui-programming.png new file mode 100644 index 000000000..1e3b5fdd8 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_imperative-ui-programming.png differ diff --git a/beta/public/images/docs/illustrations/i_import-export.svg b/beta/public/images/docs/illustrations/i_import-export.svg new file mode 100644 index 000000000..eb62e2b8c --- /dev/null +++ b/beta/public/images/docs/illustrations/i_import-export.svg @@ -0,0 +1,100 @@ + + + + + + + + + export default function Button() { ... } + + Component.js + + + one default export + + + multiple named exports + + + + export function Checkbox() { ... } + + + + export function Slider() { ... } + + Components.js + + + + + + export function Avatar() { ... } + + MixedComponents.js + + + export default function FriendsList() { ... } + + named export(s)and one default export + + diff --git a/beta/public/images/docs/illustrations/i_inputs1.png b/beta/public/images/docs/illustrations/i_inputs1.png new file mode 100644 index 000000000..35150b22d Binary files /dev/null and b/beta/public/images/docs/illustrations/i_inputs1.png differ diff --git a/beta/public/images/docs/illustrations/i_inputs2.png b/beta/public/images/docs/illustrations/i_inputs2.png new file mode 100644 index 000000000..f519af36e Binary files /dev/null and b/beta/public/images/docs/illustrations/i_inputs2.png differ diff --git a/beta/public/images/docs/illustrations/i_jsx.svg b/beta/public/images/docs/illustrations/i_jsx.svg new file mode 100644 index 000000000..629cad665 --- /dev/null +++ b/beta/public/images/docs/illustrations/i_jsx.svg @@ -0,0 +1,55 @@ + + + + + + + + + Form() { onClick() { ... } onSubmit() { ... } <form onSubmit> <input onClick /> <input onClick /> </form> } + Form.js + + + + + + Sidebar() { isLoggedIn() { <p>Welcome</p> } else { <Form /> }} + Sidebar.js + + + diff --git a/beta/public/images/docs/illustrations/i_puritea-recipe.png b/beta/public/images/docs/illustrations/i_puritea-recipe.png new file mode 100644 index 000000000..d3a348bd5 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_puritea-recipe.png differ diff --git a/beta/public/images/docs/illustrations/i_react-batching.png b/beta/public/images/docs/illustrations/i_react-batching.png new file mode 100644 index 000000000..e213ba3ad Binary files /dev/null and b/beta/public/images/docs/illustrations/i_react-batching.png differ diff --git a/beta/public/images/docs/illustrations/i_ref.png b/beta/public/images/docs/illustrations/i_ref.png new file mode 100644 index 000000000..c391e57e4 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_ref.png differ diff --git a/beta/public/images/docs/illustrations/i_render-and-commit1.png b/beta/public/images/docs/illustrations/i_render-and-commit1.png new file mode 100644 index 000000000..d62ba5806 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_render-and-commit1.png differ diff --git a/beta/public/images/docs/illustrations/i_render-and-commit2.png b/beta/public/images/docs/illustrations/i_render-and-commit2.png new file mode 100644 index 000000000..e01d0cce7 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_render-and-commit2.png differ diff --git a/beta/public/images/docs/illustrations/i_render-and-commit3.png b/beta/public/images/docs/illustrations/i_render-and-commit3.png new file mode 100644 index 000000000..bdf58c0e0 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_render-and-commit3.png differ diff --git a/beta/public/images/docs/illustrations/i_render1.png b/beta/public/images/docs/illustrations/i_render1.png new file mode 100644 index 000000000..3613c7691 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_render1.png differ diff --git a/beta/public/images/docs/illustrations/i_render2.png b/beta/public/images/docs/illustrations/i_render2.png new file mode 100644 index 000000000..ca53a7785 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_render2.png differ diff --git a/beta/public/images/docs/illustrations/i_render3.png b/beta/public/images/docs/illustrations/i_render3.png new file mode 100644 index 000000000..797860a7c Binary files /dev/null and b/beta/public/images/docs/illustrations/i_render3.png differ diff --git a/beta/public/images/docs/illustrations/i_rerender1.png b/beta/public/images/docs/illustrations/i_rerender1.png new file mode 100644 index 000000000..e94361e7b Binary files /dev/null and b/beta/public/images/docs/illustrations/i_rerender1.png differ diff --git a/beta/public/images/docs/illustrations/i_rerender2.png b/beta/public/images/docs/illustrations/i_rerender2.png new file mode 100644 index 000000000..6d85cae84 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_rerender2.png differ diff --git a/beta/public/images/docs/illustrations/i_rerender3.png b/beta/public/images/docs/illustrations/i_rerender3.png new file mode 100644 index 000000000..d589a9b93 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_rerender3.png differ diff --git a/beta/public/images/docs/illustrations/i_state-snapshot1.png b/beta/public/images/docs/illustrations/i_state-snapshot1.png new file mode 100644 index 000000000..69bbf2b72 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_state-snapshot1.png differ diff --git a/beta/public/images/docs/illustrations/i_state-snapshot2.png b/beta/public/images/docs/illustrations/i_state-snapshot2.png new file mode 100644 index 000000000..eea48a218 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_state-snapshot2.png differ diff --git a/beta/public/images/docs/illustrations/i_state-snapshot3.png b/beta/public/images/docs/illustrations/i_state-snapshot3.png new file mode 100644 index 000000000..a393528e2 Binary files /dev/null and b/beta/public/images/docs/illustrations/i_state-snapshot3.png differ diff --git a/beta/public/images/docs/implementation-notes-tree.png b/beta/public/images/docs/implementation-notes-tree.png new file mode 100644 index 000000000..923ef5d0d Binary files /dev/null and b/beta/public/images/docs/implementation-notes-tree.png differ diff --git a/beta/public/images/docs/javascript-jabber.png b/beta/public/images/docs/javascript-jabber.png new file mode 100644 index 000000000..57d63c41c Binary files /dev/null and b/beta/public/images/docs/javascript-jabber.png differ diff --git a/beta/public/images/docs/keyboard-focus.png b/beta/public/images/docs/keyboard-focus.png new file mode 100644 index 000000000..65a8dc300 Binary files /dev/null and b/beta/public/images/docs/keyboard-focus.png differ diff --git a/beta/public/images/docs/outerclick-with-keyboard.gif b/beta/public/images/docs/outerclick-with-keyboard.gif new file mode 100644 index 000000000..c82d299f8 Binary files /dev/null and b/beta/public/images/docs/outerclick-with-keyboard.gif differ diff --git a/beta/public/images/docs/outerclick-with-mouse.gif b/beta/public/images/docs/outerclick-with-mouse.gif new file mode 100644 index 000000000..e562e0324 Binary files /dev/null and b/beta/public/images/docs/outerclick-with-mouse.gif differ diff --git a/beta/public/images/docs/perf-dom.png b/beta/public/images/docs/perf-dom.png new file mode 100644 index 000000000..9a843c6ca Binary files /dev/null and b/beta/public/images/docs/perf-dom.png differ diff --git a/beta/public/images/docs/perf-exclusive.png b/beta/public/images/docs/perf-exclusive.png new file mode 100644 index 000000000..a8ad5003c Binary files /dev/null and b/beta/public/images/docs/perf-exclusive.png differ diff --git a/beta/public/images/docs/perf-inclusive.png b/beta/public/images/docs/perf-inclusive.png new file mode 100644 index 000000000..e46b370aa Binary files /dev/null and b/beta/public/images/docs/perf-inclusive.png differ diff --git a/beta/public/images/docs/perf-wasted.png b/beta/public/images/docs/perf-wasted.png new file mode 100644 index 000000000..c73131818 Binary files /dev/null and b/beta/public/images/docs/perf-wasted.png differ diff --git a/beta/public/images/docs/react-devtools-extension.png b/beta/public/images/docs/react-devtools-extension.png new file mode 100644 index 000000000..6ea2aad8d Binary files /dev/null and b/beta/public/images/docs/react-devtools-extension.png differ diff --git a/beta/public/images/docs/react-devtools-standalone.png b/beta/public/images/docs/react-devtools-standalone.png new file mode 100644 index 000000000..07da74137 Binary files /dev/null and b/beta/public/images/docs/react-devtools-standalone.png differ diff --git a/beta/public/images/docs/react-devtools-state.gif b/beta/public/images/docs/react-devtools-state.gif new file mode 100644 index 000000000..c9ff6cd35 Binary files /dev/null and b/beta/public/images/docs/react-devtools-state.gif differ diff --git a/beta/public/images/docs/s_thinking-in-react_ui.png b/beta/public/images/docs/s_thinking-in-react_ui.png new file mode 100644 index 000000000..21802352c Binary files /dev/null and b/beta/public/images/docs/s_thinking-in-react_ui.png differ diff --git a/beta/public/images/docs/s_thinking-in-react_ui_outline.png b/beta/public/images/docs/s_thinking-in-react_ui_outline.png new file mode 100644 index 000000000..d38f3e19f Binary files /dev/null and b/beta/public/images/docs/s_thinking-in-react_ui_outline.png differ diff --git a/beta/public/images/docs/should-component-update.png b/beta/public/images/docs/should-component-update.png new file mode 100644 index 000000000..590af60b8 Binary files /dev/null and b/beta/public/images/docs/should-component-update.png differ diff --git a/beta/public/images/docs/source/i_browser-paint.psd b/beta/public/images/docs/source/i_browser-paint.psd new file mode 100644 index 000000000..bb541e889 Binary files /dev/null and b/beta/public/images/docs/source/i_browser-paint.psd differ diff --git a/beta/public/images/docs/source/i_children-prop.psd b/beta/public/images/docs/source/i_children-prop.psd new file mode 100644 index 000000000..2577c0aa0 Binary files /dev/null and b/beta/public/images/docs/source/i_children-prop.psd differ diff --git a/beta/public/images/docs/source/i_declarative-ui-programming.psd b/beta/public/images/docs/source/i_declarative-ui-programming.psd new file mode 100644 index 000000000..3b1a9a4d1 Binary files /dev/null and b/beta/public/images/docs/source/i_declarative-ui-programming.psd differ diff --git a/beta/public/images/docs/source/i_imperative-ui-programming.psd b/beta/public/images/docs/source/i_imperative-ui-programming.psd new file mode 100644 index 000000000..c1511b022 Binary files /dev/null and b/beta/public/images/docs/source/i_imperative-ui-programming.psd differ diff --git a/beta/public/images/docs/source/i_keys-in-trees.psd b/beta/public/images/docs/source/i_keys-in-trees.psd new file mode 100644 index 000000000..bab04dc08 Binary files /dev/null and b/beta/public/images/docs/source/i_keys-in-trees.psd differ diff --git a/beta/public/images/docs/source/i_puritea-recipe.psd b/beta/public/images/docs/source/i_puritea-recipe.psd new file mode 100644 index 000000000..72a33ab30 Binary files /dev/null and b/beta/public/images/docs/source/i_puritea-recipe.psd differ diff --git a/beta/public/images/docs/source/i_react-batching.psd b/beta/public/images/docs/source/i_react-batching.psd new file mode 100644 index 000000000..88bcd617b Binary files /dev/null and b/beta/public/images/docs/source/i_react-batching.psd differ diff --git a/beta/public/images/docs/source/i_react-is-blind-to-ui-swap.psd b/beta/public/images/docs/source/i_react-is-blind-to-ui-swap.psd new file mode 100644 index 000000000..5e422ba63 Binary files /dev/null and b/beta/public/images/docs/source/i_react-is-blind-to-ui-swap.psd differ diff --git a/beta/public/images/docs/source/i_ref.psd b/beta/public/images/docs/source/i_ref.psd new file mode 100644 index 000000000..12349b431 Binary files /dev/null and b/beta/public/images/docs/source/i_ref.psd differ diff --git a/beta/public/images/docs/source/i_render-commit.psd b/beta/public/images/docs/source/i_render-commit.psd new file mode 100644 index 000000000..99f62802a Binary files /dev/null and b/beta/public/images/docs/source/i_render-commit.psd differ diff --git a/beta/public/images/docs/source/i_rerender.psd b/beta/public/images/docs/source/i_rerender.psd new file mode 100644 index 000000000..9dc83d5ed Binary files /dev/null and b/beta/public/images/docs/source/i_rerender.psd differ diff --git a/beta/public/images/docs/source/i_rerendering.psd b/beta/public/images/docs/source/i_rerendering.psd new file mode 100644 index 000000000..2ba1cba58 Binary files /dev/null and b/beta/public/images/docs/source/i_rerendering.psd differ diff --git a/beta/public/images/docs/source/i_state-snapshot.psd b/beta/public/images/docs/source/i_state-snapshot.psd new file mode 100644 index 000000000..b8d54ddcc Binary files /dev/null and b/beta/public/images/docs/source/i_state-snapshot.psd differ diff --git a/beta/public/images/docs/source/s_inputs.psd b/beta/public/images/docs/source/s_inputs.psd new file mode 100644 index 000000000..60354742a Binary files /dev/null and b/beta/public/images/docs/source/s_inputs.psd differ diff --git a/beta/public/images/docs/source/s_ui-snapshot.psd b/beta/public/images/docs/source/s_ui-snapshot.psd new file mode 100644 index 000000000..41c65f8fe Binary files /dev/null and b/beta/public/images/docs/source/s_ui-snapshot.psd differ diff --git a/beta/public/images/docs/thinking-in-react-tagtree.png b/beta/public/images/docs/thinking-in-react-tagtree.png new file mode 100644 index 000000000..3d4db2d23 Binary files /dev/null and b/beta/public/images/docs/thinking-in-react-tagtree.png differ diff --git a/beta/public/images/external.png b/beta/public/images/external.png new file mode 100644 index 000000000..748a27e44 Binary files /dev/null and b/beta/public/images/external.png differ diff --git a/beta/public/images/external_2x.png b/beta/public/images/external_2x.png new file mode 100644 index 000000000..66230854d Binary files /dev/null and b/beta/public/images/external_2x.png differ diff --git a/beta/public/images/g_arrow.png b/beta/public/images/g_arrow.png new file mode 100644 index 000000000..36258add5 Binary files /dev/null and b/beta/public/images/g_arrow.png differ diff --git a/beta/public/images/history.png b/beta/public/images/history.png new file mode 100644 index 000000000..4ca56a866 Binary files /dev/null and b/beta/public/images/history.png differ diff --git a/beta/public/images/noise.png b/beta/public/images/noise.png new file mode 100644 index 000000000..698f924f4 Binary files /dev/null and b/beta/public/images/noise.png differ diff --git a/beta/public/images/oss_logo.png b/beta/public/images/oss_logo.png new file mode 100644 index 000000000..3f376bee9 Binary files /dev/null and b/beta/public/images/oss_logo.png differ diff --git a/beta/public/images/search.png b/beta/public/images/search.png new file mode 100644 index 000000000..151377693 Binary files /dev/null and b/beta/public/images/search.png differ diff --git a/beta/public/images/team/acdlite.jpg b/beta/public/images/team/acdlite.jpg new file mode 100644 index 000000000..ab54b793b Binary files /dev/null and b/beta/public/images/team/acdlite.jpg differ diff --git a/beta/public/images/team/bvaughn.jpg b/beta/public/images/team/bvaughn.jpg new file mode 100644 index 000000000..227fe8d94 Binary files /dev/null and b/beta/public/images/team/bvaughn.jpg differ diff --git a/beta/public/images/team/gaearon.jpg b/beta/public/images/team/gaearon.jpg new file mode 100644 index 000000000..e152143b7 Binary files /dev/null and b/beta/public/images/team/gaearon.jpg differ diff --git a/beta/public/images/team/huxpro.jpg b/beta/public/images/team/huxpro.jpg new file mode 100644 index 000000000..f9a94452e Binary files /dev/null and b/beta/public/images/team/huxpro.jpg differ diff --git a/beta/public/images/team/lunaruan.jpg b/beta/public/images/team/lunaruan.jpg new file mode 100644 index 000000000..91daa3d17 Binary files /dev/null and b/beta/public/images/team/lunaruan.jpg differ diff --git a/beta/public/images/team/necolas.jpg b/beta/public/images/team/necolas.jpg new file mode 100644 index 000000000..b7caaac06 Binary files /dev/null and b/beta/public/images/team/necolas.jpg differ diff --git a/beta/public/images/team/rickhanlonii.jpg b/beta/public/images/team/rickhanlonii.jpg new file mode 100644 index 000000000..eb04614c5 Binary files /dev/null and b/beta/public/images/team/rickhanlonii.jpg differ diff --git a/beta/public/images/team/rnabors.jpg b/beta/public/images/team/rnabors.jpg new file mode 100644 index 000000000..4425c90db Binary files /dev/null and b/beta/public/images/team/rnabors.jpg differ diff --git a/beta/public/images/team/salazarm.jpeg b/beta/public/images/team/salazarm.jpeg new file mode 100644 index 000000000..0360f208d Binary files /dev/null and b/beta/public/images/team/salazarm.jpeg differ diff --git a/beta/public/images/team/sebmarkbage.jpg b/beta/public/images/team/sebmarkbage.jpg new file mode 100644 index 000000000..56a480ff4 Binary files /dev/null and b/beta/public/images/team/sebmarkbage.jpg differ diff --git a/beta/public/images/team/sethwebster.jpg b/beta/public/images/team/sethwebster.jpg new file mode 100644 index 000000000..c665a0b00 Binary files /dev/null and b/beta/public/images/team/sethwebster.jpg differ diff --git a/beta/public/images/team/threepointone.jpg b/beta/public/images/team/threepointone.jpg new file mode 100644 index 000000000..9ad860171 Binary files /dev/null and b/beta/public/images/team/threepointone.jpg differ diff --git a/beta/public/images/team/trueadm.jpg b/beta/public/images/team/trueadm.jpg new file mode 100644 index 000000000..33a6b838f Binary files /dev/null and b/beta/public/images/team/trueadm.jpg differ diff --git a/beta/public/images/team/yuzhi.jpg b/beta/public/images/team/yuzhi.jpg new file mode 100644 index 000000000..c3f4175d7 Binary files /dev/null and b/beta/public/images/team/yuzhi.jpg differ diff --git a/beta/public/images/tutorial/devtools.png b/beta/public/images/tutorial/devtools.png new file mode 100644 index 000000000..6c4765703 Binary files /dev/null and b/beta/public/images/tutorial/devtools.png differ diff --git a/beta/public/images/tutorial/tictac-empty.png b/beta/public/images/tutorial/tictac-empty.png new file mode 100644 index 000000000..fabe3f034 Binary files /dev/null and b/beta/public/images/tutorial/tictac-empty.png differ diff --git a/beta/public/images/tutorial/tictac-numbers.png b/beta/public/images/tutorial/tictac-numbers.png new file mode 100644 index 000000000..69d163f45 Binary files /dev/null and b/beta/public/images/tutorial/tictac-numbers.png differ diff --git a/beta/public/js/jsfiddle-integration-babel.js b/beta/public/js/jsfiddle-integration-babel.js new file mode 100644 index 000000000..006c79c8a --- /dev/null +++ b/beta/public/js/jsfiddle-integration-babel.js @@ -0,0 +1,16 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +// Do not delete or move this file. +// Many fiddles reference it so we have to keep it here. +(function() { + var tag = document.querySelector( + 'script[type="application/javascript;version=1.7"]' + ); + if (!tag || tag.textContent.indexOf('window.onload=function(){') !== -1) { + alert('Bad JSFiddle configuration, please fork the original React JSFiddle'); + } + tag.setAttribute('type', 'text/babel'); + tag.textContent = tag.textContent.replace(/^\/\/ { + const routes = []; + const blogPosts = await globby('src/pages/blog/**/*.md'); + + for (let postpath of blogPosts) { + const [year, month, day, title] = postpath + .replace('src/pages/blog/', '') + .split('/'); + + const rawStr = await fs.readFile(postpath, 'utf8'); + const {data, excerpt, content} = fm(rawStr, { + excerpt: function firstLine(file, options) { + file.excerpt = file.content.split('\n').slice(0, 2).join(' '); + }, + }); + const rendered = await markdownToHtml(excerpt.trimLeft().trim()); + + routes.unshift({ + path: postpath.replace('src/pages', ''), + date: [year, month, day].join('-'), + title: data.title, + author: data.author, + excerpt: rendered, + readingTime: readingTime(content).text, + }); + } + + const sorted = routes.sort((post1, post2) => + parseISO(post1.date) > parseISO(post2.date) ? -1 : 1 + ); + const blogManifest = { + routes: sorted, + }; + const blogRecentSidebar = { + routes: [ + { + title: 'Recent Posts', + path: '/blog', + heading: true, + routes: sorted.slice(0, 25), + }, + ], + }; + + await fs.writeFile( + path.resolve('./src/blogIndex.json'), + JSON.stringify(blogManifest, null, 2) + ); + await fs.writeFile( + path.resolve('./src/blogIndexRecent.json'), + JSON.stringify(blogRecentSidebar, null, 2) + ); + }) + .catch(console.error); diff --git a/beta/scripts/generateRSS.js b/beta/scripts/generateRSS.js new file mode 100644 index 000000000..a08c7e2ad --- /dev/null +++ b/beta/scripts/generateRSS.js @@ -0,0 +1,46 @@ +const RSS = require('rss'); +const fs = require('fs-extra'); +const authorsJson = require('../src/authors.json'); +const blogIndexJson = require('../src/blogIndex.json'); +const parse = require('date-fns/parse'); + +function removeFromLast(path, key) { + const i = path.lastIndexOf(key); + return i === -1 ? path : path.substring(0, i); +} + +const SITE_URL = 'https://reactjs.org'; + +function generate() { + const feed = new RSS({ + title: 'React.js Blog', + site_url: SITE_URL, + feed_url: SITE_URL + '/feed.xml', + }); + + blogIndexJson.routes.map((meta) => { + feed.item({ + title: meta.title, + guid: removeFromLast(meta.path, '.'), + url: SITE_URL + removeFromLast(meta.path, '.'), + date: parse(meta.date, 'yyyy-MM-dd', new Date()), + description: meta.description, + custom_elements: [].concat( + meta.author.map((author) => ({ + author: [{ name: authorsJson[author].name }], + })) + ), + }); + }); + + const rss = feed.xml({ indent: true }); + + fs.writeFileSync('./.next/static/feed.xml', rss); +} + +try { + generate(); +} catch (error) { + console.error('Error generating rss feed'); + throw error; +} diff --git a/beta/scripts/generateRedirects.js b/beta/scripts/generateRedirects.js new file mode 100644 index 000000000..b6e23b58a --- /dev/null +++ b/beta/scripts/generateRedirects.js @@ -0,0 +1,81 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +const resolve = require('path').resolve; +const {writeFile} = require('fs-extra'); +const readFileSync = require('fs').readFileSync; +const safeLoad = require('js-yaml').safeLoad; +const path = require('path'); +const versionsFile = resolve(__dirname, '../../content/versions.yml'); +const file = readFileSync(versionsFile, 'utf8'); +const versions = safeLoad(file); +const redirectsFilePath = path.join('vercel.json'); + +function writeRedirectsFile(redirects, redirectsFilePath) { + if (!redirects.length) { + return null; + } + + /** + * We will first read the old config to validate if the redirect already exists in the json + */ + const vercelConfigPath = resolve(__dirname, '../../vercel.json'); + const vercelConfigFile = readFileSync(vercelConfigPath); + const oldConfigContent = JSON.parse(vercelConfigFile); + /** + * Map data as vercel expects it to be + */ + + let vercelRedirects = {}; + + redirects.forEach((redirect) => { + const {fromPath, isPermanent, toPath} = redirect; + + vercelRedirects[fromPath] = { + destination: toPath, + permanent: !!isPermanent, + }; + }); + + /** + * Make sure we dont have the same redirect already + */ + oldConfigContent.redirects.forEach((data) => { + if(vercelRedirects[data.source]){ + delete vercelRedirects[data.source]; + } + }); + + /** + * Serialize the object to array of objects + */ + let newRedirects = []; + Object.keys(vercelRedirects).forEach((value) => + newRedirects.push({ + source: value, + destination: vercelRedirects[value].destination, + permanent: !!vercelRedirects[value].isPermanent, + }) + ); + + /** + * We already have a vercel.json so we spread the new contents along with old ones + */ + const newContents = { + ...oldConfigContent, + redirects: [...oldConfigContent.redirects, ...newRedirects], + }; + writeFile(redirectsFilePath, JSON.stringify(newContents, null, 2)); +} + +// versions.yml structure is [{path: string, url: string, ...}, ...] +writeRedirectsFile( + versions + .filter((version) => version.path && version.url) + .map((version) => ({ + fromPath: version.path, + toPath: version.url, + })), + redirectsFilePath +); diff --git a/beta/scripts/headingIDHelpers/generateHeadingIDs.js b/beta/scripts/headingIDHelpers/generateHeadingIDs.js new file mode 100644 index 000000000..0b1d627e0 --- /dev/null +++ b/beta/scripts/headingIDHelpers/generateHeadingIDs.js @@ -0,0 +1,110 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +// To do: Make this ESM. +// To do: properly check heading numbers (headings with the same text get +// numbered, this script doesn’t check that). + +const assert = require('assert'); +const fs = require('fs'); +const GithubSlugger = require('github-slugger'); +const walk = require('./walk'); + +let modules; + +function stripLinks(line) { + return line.replace(/\[([^\]]+)\]\([^)]+\)/, (match, p1) => p1); +} + +function addHeaderID(line, slugger) { + // check if we're a header at all + if (!line.startsWith('#')) { + return line; + } + + const match = + /^(#+\s+)(.+?)(\s*\{(?:\/\*|#)([^\}\*\/]+)(?:\*\/)?\}\s*)?$/.exec(line); + const before = match[1] + match[2]; + const proc = modules + .unified() + .use(modules.remarkParse) + .use(modules.remarkSlug); + const tree = proc.runSync(proc.parse(before)); + const head = tree.children[0]; + assert( + head && head.type === 'heading', + 'expected `' + + before + + '` to be a heading, is it using a normal space after `#`?' + ); + const autoId = head.data.id; + const existingId = match[4]; + const id = existingId || autoId; + // Ignore numbers: + const cleanExisting = existingId + ? existingId.replace(/-\d+$/, '') + : undefined; + const cleanAuto = autoId.replace(/-\d+$/, ''); + + if (cleanExisting && cleanExisting !== cleanAuto) { + console.log( + 'Note: heading `%s` has a different ID (`%s`) than what GH generates for it: `%s`:', + before, + existingId, + autoId + ); + } + + return match[1] + match[2] + ' {/*' + id + '*/}'; +} + +function addHeaderIDs(lines) { + // Sluggers should be per file + const slugger = new GithubSlugger(); + let inCode = false; + const results = []; + lines.forEach((line) => { + // Ignore code blocks + if (line.startsWith('```')) { + inCode = !inCode; + results.push(line); + return; + } + if (inCode) { + results.push(line); + return; + } + + results.push(addHeaderID(line, slugger)); + }); + return results; +} + +async function main(paths) { + paths = paths.length === 0 ? ['src/pages'] : paths; + + const [unifiedMod, remarkParseMod, remarkSlugMod] = await Promise.all([ + import('unified'), + import('remark-parse'), + import('remark-slug'), + ]); + const unified = unifiedMod.default; + const remarkParse = remarkParseMod.default; + const remarkSlug = remarkSlugMod.default; + modules = {unified, remarkParse, remarkSlug}; + const files = paths.map((path) => [...walk(path)]).flat(); + + files.forEach((file) => { + if (!(file.endsWith('.md') || file.endsWith('.mdx'))) { + return; + } + + const content = fs.readFileSync(file, 'utf8'); + const lines = content.split('\n'); + const updatedLines = addHeaderIDs(lines); + fs.writeFileSync(file, updatedLines.join('\n')); + }); +} + +module.exports = main; diff --git a/beta/scripts/headingIDHelpers/validateHeadingIDs.js b/beta/scripts/headingIDHelpers/validateHeadingIDs.js new file mode 100644 index 000000000..535613e7f --- /dev/null +++ b/beta/scripts/headingIDHelpers/validateHeadingIDs.js @@ -0,0 +1,68 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + */ +const fs = require('fs'); +const walk = require('./walk'); + +/** + * Validate if there is a custom heading id and exit if there isn't a heading + * @param {string} line + * @returns + */ +function validateHeaderId(line) { + if (!line.startsWith('#')) { + return; + } + + const match = /\{\/\*(.*?)\*\/}/.exec(line); + const id = match; + if (!id) { + console.error( + 'Run yarn fix-headings to generate headings.' + ); + process.exit(1); + } +} + +/** + * Loops through the lines to skip code blocks + * @param {Array} lines + */ +function validateHeaderIds(lines) { + let inCode = false; + const results = []; + lines.forEach((line) => { + // Ignore code blocks + if (line.startsWith('```')) { + inCode = !inCode; + + results.push(line); + return; + } + if (inCode) { + results.push(line); + return; + } + validateHeaderId(line); + }); +} +/** + * paths are basically array of path for which we have to validate heading IDs + * @param {Array} paths + */ +async function main(paths) { + paths = paths.length === 0 ? ['src/pages'] : paths; + const files = paths.map((path) => [...walk(path)]).flat(); + + files.forEach((file) => { + if (!(file.endsWith('.md') || file.endsWith('.mdx'))) { + return; + } + + const content = fs.readFileSync(file, 'utf8'); + const lines = content.split('\n'); + validateHeaderIds(lines); + }); +} + +module.exports = main; diff --git a/beta/scripts/headingIDHelpers/walk.js b/beta/scripts/headingIDHelpers/walk.js new file mode 100644 index 000000000..721274e09 --- /dev/null +++ b/beta/scripts/headingIDHelpers/walk.js @@ -0,0 +1,24 @@ +const fs = require('fs'); + +module.exports = function walk(dir) { + let results = []; + /** + * If the param is a directory we can return the file + */ + if(dir.includes('md')){ + return [dir]; + } + const list = fs.readdirSync(dir); + list.forEach(function (file) { + file = dir + '/' + file; + const stat = fs.statSync(file); + if (stat && stat.isDirectory()) { + /* Recurse into a subdirectory */ + results = results.concat(walk(file)); + } else { + /* Is a file */ + results.push(file); + } + }); + return results; +}; diff --git a/beta/scripts/headingIdLinter.js b/beta/scripts/headingIdLinter.js new file mode 100644 index 000000000..037e4945f --- /dev/null +++ b/beta/scripts/headingIdLinter.js @@ -0,0 +1,16 @@ +const validateHeaderIds = require('./headingIDHelpers/validateHeadingIDs'); +const generateHeadingIds = require('./headingIDHelpers/generateHeadingIDs'); + +/** + * yarn lint-heading-ids --> Checks all files and causes an error if heading ID is missing + * yarn lint-heading-ids --fix --> Fixes all markdown file's heading IDs + * yarn lint-heading-ids path/to/markdown.md --> Checks that particular file for missing heading ID (path can denote a directory or particular file) + * yarn lint-heading-ids --fix path/to/markdown.md --> Fixes that particular file's markdown IDs (path can denote a directory or particular file) +*/ + +const markdownPaths = process.argv.slice(2); +if (markdownPaths.includes('--fix')) { + generateHeadingIds(markdownPaths.filter((path) => path !== '--fix')); +} else { + validateHeaderIds(markdownPaths); +} diff --git a/beta/scripts/migrations/migrateBlogPosts.js b/beta/scripts/migrations/migrateBlogPosts.js new file mode 100644 index 000000000..8b93c23ab --- /dev/null +++ b/beta/scripts/migrations/migrateBlogPosts.js @@ -0,0 +1,50 @@ +const fs = require('fs-extra'); +const path = require('path'); +const fm = require('gray-matter'); +const globby = require('globby'); +const parse = require('date-fns/parse'); + +/** + * This script takes the gatsby blog posts directory and migrates it. + * + * In gatsby, blog posts were put in markdown files title YYYY-MM-DD-post-title.md. + * This script looks at that directory and then moves posts into folders paths + * that match the end URL structure of /blog/YYYY/MM/DD/postitle.md + * + * This allows us to use MDX in blog posts. + */ + +// I dropped them into src/pages/oldblog +// @todo remove after migration +// I am not proud of this. Also, the blog posts needed to be cleaned up for MDX, don't run this again. +Promise.resolve() + .then(async () => { + const blogManifest = {}; + const blogPosts = await globby('src/pages/oldblog/*.md'); + // console.log(blogPosts); + for (let postpath of blogPosts.sort()) { + const rawStr = await fs.readFile(postpath, 'utf8'); + // console.log(rawStr); + const {data, content} = fm(rawStr); + const cleanPath = postpath.replace('src/pages/oldblog/', ''); + const yrStr = parseInt(cleanPath.substr(0, 4), 10); // 2013-06-02 + // console.log(yrStr); + const dateStr = cleanPath.substr(0, 10); // 2013-06-02 + const postFileName = cleanPath.substr(11); + // console.log(postFileName, dateStr); + const datePath = dateStr.split('-').join('/'); + // console.log(datePath); + const newPath = './src/pages/blog/' + datePath + '/' + postFileName; + // console.log(newPath); + await fs.ensureFile(path.resolve(newPath)); + await fs.writeFile( + path.resolve(newPath), + rawStr + .replace('
', '
') + .replace('
', '
') + .replace('layout: post', '') + .replace('\nauthor', '\nlayout: Post\nauthor') + ); + } + }) + .catch(console.error); diff --git a/beta/scripts/migrations/migratePermalinks.js b/beta/scripts/migrations/migratePermalinks.js new file mode 100644 index 000000000..ad0303d4a --- /dev/null +++ b/beta/scripts/migrations/migratePermalinks.js @@ -0,0 +1,35 @@ +const fs = require('fs-extra'); +const path = require('path'); +const fm = require('gray-matter'); +const globby = require('globby'); + +/** + * This script ensures that every file in the docs folder is named corresponding + * to its respective frontmatter permalink. In the old site, the path of the page was set by + * the `permalink` in markdown frontmatter, and not the name of the file itself or it's id. + * In the new Next.js site, with its filesystem router, the name of the file must + * match exactly to its `permalink`. + */ +Promise.resolve() + .then(async () => { + const pages = await globby('src/pages/docs/**/*.{md,mdx}'); + for (let sourcePath of pages.sort()) { + const rawStr = await fs.readFile(sourcePath, 'utf8'); + const {data, content} = fm(rawStr); + const currentPath = sourcePath + .replace('src/pages/', '') + .replace('.md', ''); + const permalink = data.permalink.replace('.html', ''); + if (permalink !== currentPath) { + const destPath = 'src/pages/' + permalink + '.md'; + try { + await fs.move(sourcePath, destPath); + console.log(`MOVED: ${sourcePath} --> ${destPath}`); + } catch (error) { + console.error(`ERROR: ${sourcePath} --> ${destPath}`); + console.error(error); + } + } + } + }) + .catch(console.error); diff --git a/beta/scripts/migrations/migrateRedirects.js b/beta/scripts/migrations/migrateRedirects.js new file mode 100644 index 000000000..5e0a6ed0e --- /dev/null +++ b/beta/scripts/migrations/migrateRedirects.js @@ -0,0 +1,117 @@ +const fs = require('fs-extra'); +const path = require('path'); +const fm = require('gray-matter'); +const globby = require('globby'); + +/** + * This script takes a look at all the redirect frontmatter and converts it + * into a Next.js compatible redirects list. It also merges it with netlify's + * _redirects, which we moved by hand below. + * + * @remarks + * In the old gatsby site, redirects were specified in docs and blog post + * frontmatter that looks like: + * + * --- + * redirect_from: + * - /docs/old-path.html#maybe-an-anchor + * --- + */ + +const netlifyRedirects = [ + { + source: '/html-jsx.html', + destination: 'https://magic.reactjs.net/htmltojsx.htm', + permanent: true, + }, + { + source: '/tips/controlled-input-null-value.html', + destination: '/docs/forms.html#controlled-input-null-value', + permanent: false, // @todo why were these not permanent on netlify? + }, + { + source: '/concurrent', + destination: '/docs/concurrent-mode-intro.html', + permanent: false, + }, + { + source: '/hooks', + destination: '/docs/hooks-intro.html', + permanent: false, + }, + { + source: '/tutorial', + destination: '/tutorial/tutorial.html', + permanent: false, + }, + { + source: '/your-story', + destination: 'https://www.surveymonkey.co.uk/r/MVQV2R9', + permanent: true, + }, + { + source: '/stories', + destination: 'https://medium.com/react-community-stories', + permanent: true, + }, +]; + +Promise.resolve() + .then(async () => { + let contentRedirects = []; + let redirectPageCount = 0; + + // Get all markdown pages + const pages = await globby('src/pages/**/*.{md,mdx}'); + for (let filepath of pages) { + // Read file as string + const rawStr = await fs.readFile(filepath, 'utf8'); + // Extract frontmatter + const {data, content} = fm(rawStr); + // Look for redirect yaml + if (data.redirect_from) { + redirectPageCount++; + + let destinationPath = filepath + .replace('src/pages', '') + .replace('.md', ''); + + // Fix /docs/index -> /docs + if (destinationPath === '/docs/index') { + destinationPath = '/docs'; + } + + if (destinationPath === '/index') { + destinationPath = '/'; + } + + for (let sourcePath of data.redirect_from) { + contentRedirects.push({ + source: '/' + sourcePath, // add slash + destination: destinationPath, + permanent: true, + }); + } + } + } + console.log( + `Found ${redirectPageCount} pages with \`redirect_from\` frontmatter` + ); + console.log( + `Writing ${contentRedirects.length} redirects to redirects.json` + ); + + await fs.writeFile( + path.resolve('./src/redirects.json'), + JSON.stringify( + { + redirects: [...contentRedirects, ...netlifyRedirects], + }, + null, + 2 + ) + ); + + console.log('✅ Done writing redirects'); + }) + .catch(console.error); diff --git a/beta/src/authors.json b/beta/src/authors.json new file mode 100644 index 000000000..76cd5aedd --- /dev/null +++ b/beta/src/authors.json @@ -0,0 +1,130 @@ +{ + "acdlite": { + "name": "Andrew Clark", + "url": "https://twitter.com/acdlite" + }, + "benigeri": { + "name": "Paul Benigeri", + "url": "https://github.com/benigeri" + }, + "bvaughn": { + "name": "Brian Vaughn", + "url": "https://github.com/bvaughn" + }, + "chenglou": { + "name": "Cheng Lou", + "url": "https://twitter.com/_chenglou" + }, + "clemmy": { + "name": "Clement Hoang", + "url": "https://twitter.com/c8hoang" + }, + "Daniel15": { + "name": "Daniel Lo Nigro", + "url": "https://d.sb/" + }, + "fisherwebdev": { + "name": "Bill Fisher", + "url": "https://twitter.com/fisherwebdev" + }, + "flarnie": { + "name": "Flarnie Marchan", + "url": "https://twitter.com/ProbablyFlarnie" + }, + "gaearon": { + "name": "Dan Abramov", + "url": "https://twitter.com/dan_abramov" + }, + "jaredly": { + "name": "Jared Forsyth", + "url": "https://twitter.com/jaredforsyth" + }, + "jgebhardt": { + "name": "Jonas Gebhardt", + "url": "https://twitter.com/jonasgebhardt" + }, + "jimfb": { + "name": "Jim Sproch", + "url": "http: //www.jimsproch.com" + }, + "jingc": { + "name": "Jing Chen", + "url": "https://twitter.com/jingc" + }, + "josephsavona": { + "name": "Joseph Savona", + "url": "https://twitter.com/en_JS" + }, + "keyanzhang": { + "name": "Keyan Zhang", + "url": "https://twitter.com/keyanzhang" + }, + "kmeht": { + "name": "Kunal Mehta", + "url": "https://github.com/kmeht" + }, + "LoukaN": { + "name": "Lou Husson", + "url": "https://twitter.com/loukan42" + }, + "matthewjohnston4": { + "name": "Matthew Johnston", + "url": "https://github.com/matthewathome" + }, + "nhunzaker": { + "name": "Nathan Hunzaker", + "url": "https://github.com/nhunzaker" + }, + "petehunt": { + "name": "Pete Hunt", + "url": "https://twitter.com/floydophone" + }, + "rachelnabors": { + "name": "Rachel Nabors", + "url": "https://twitter.com/rachelnabors" + }, + "schrockn": { + "name": "Nick Schrock", + "url": "https://twitter.com/schrockn" + }, + "sebmarkbage": { + "name": "Sebastian Markbåge", + "url": "https://twitter.com/sebmarkbage" + }, + "sophiebits": { + "name": "Sophie Alpert", + "url": "https://sophiebits.com/" + }, + "steveluscher": { + "name": "Steven Luscher", + "url": "https://twitter.com/steveluscher" + }, + "tesseralis": { + "name": "Nat Alison", + "url": "https://twitter.com/tesseralis" + }, + "threepointone": { + "name": "Sunil Pai", + "url": "https://twitter.com/threepointone" + }, + "timer": { + "name": "Joe Haddad", + "url": "https://twitter.com/timer150" + }, + "vjeux": { + "name": "Vjeux", + "url": "https://twitter.com/vjeux" + }, + "wincent": { + "name": "Greg Hurrell", + "url": "https://twitter.com/wincent" + }, + "zpao": { + "name": "Paul O’Shannessy", + "url": "https://twitter.com/zpao" + }, + "tomocchino": { + "name": "Tom Occhino", + "url": "https://twitter.com/tomocchino" + } +} diff --git a/beta/src/blogIndex.json b/beta/src/blogIndex.json new file mode 100644 index 000000000..088a1f82c --- /dev/null +++ b/beta/src/blogIndex.json @@ -0,0 +1,1400 @@ +{ + "routes": [ + { + "path": "/blog/2020/08/10/react-v17-rc.md", + "date": "2020-08-10", + "title": "React v17.0 Release Candidate: No New Features", + "author": [ + "gaearon", + "rachelnabors" + ], + "excerpt": "

Today, we are publishing the first Release Candidate for React 17. It has been two and a half years since the previous major release of React, which is a long time even by our standards! In this blog post, we will describe the role of this major release, what changes you can expect in it, and how you can try this release.

\n", + "readingTime": "20 min read" + }, + { + "path": "/blog/2020/02/26/react-v16.13.0.md", + "date": "2020-02-26", + "title": "React v16.13.0", + "author": [ + "threepointone" + ], + "excerpt": "

Today we are releasing React 16.13.0. It contains bugfixes and new deprecation warnings to help prepare for a future major release.

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2019/11/06/building-great-user-experiences-with-concurrent-mode-and-suspense.md", + "date": "2019-11-06", + "title": "Building Great User Experiences with Concurrent Mode and Suspense", + "author": [ + "josephsavona" + ], + "excerpt": "

At React Conf 2019 we announced an experimental release of React that supports Concurrent Mode and Suspense. In this post we’ll introduce best practices for using them that we’ve identified through the process of building the new facebook.com.

\n", + "readingTime": "17 min read" + }, + { + "path": "/blog/2019/10/22/react-release-channels.md", + "date": "2019-10-22", + "title": "Preparing for the Future with React Prereleases", + "author": [ + "acdlite" + ], + "excerpt": "

To share upcoming changes with our partners in the React ecosystem, we’re establishing official prerelease channels. We hope this process will help us make changes to React with confidence, and give developers the opportunity to try out experimental features.

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2019/08/15/new-react-devtools.md", + "date": "2019-08-15", + "title": "Introducing the New React DevTools", + "author": [ + "bvaughn" + ], + "excerpt": "

We are excited to announce a new release of the React Developer Tools, available today in Chrome, Firefox, and (Chromium) Edge!

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2019/08/08/react-v16.9.0.md", + "date": "2019-08-08", + "title": "React v16.9.0 and the Roadmap Update", + "author": [ + "gaearon", + "bvaughn" + ], + "excerpt": "

Today we are releasing React 16.9. It contains several new features, bugfixes, and new deprecation warnings to help prepare for a future major release.

\n", + "readingTime": "11 min read" + }, + { + "path": "/blog/2019/02/23/is-react-translated-yet.md", + "date": "2019-02-23", + "title": "Is React Translated Yet? ¡Sí! Sim! はい!", + "author": [ + "tesseralis" + ], + "excerpt": "

We’re excited to announce an ongoing effort to maintain official translations of the React documentation website into different languages. Thanks to the dedicated efforts of React community members from around the world, React is now being translated into over 30 languages! You can find them on the new Languages page.

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2019/02/06/react-v16.8.0.md", + "date": "2019-02-06", + "title": "React v16.8: The One With Hooks", + "author": [ + "gaearon" + ], + "excerpt": "

With React 16.8, React Hooks are available in a stable release!

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2018/12/19/react-v-16-7.md", + "date": "2018-12-19", + "title": "React v16.7: No, This Is Not the One With Hooks", + "author": [ + "acdlite" + ], + "excerpt": "

Our latest release includes an important performance bugfix for React.lazy. Although there are no API changes, we’re releasing it as a minor instead of a patch.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2018/11/27/react-16-roadmap.md", + "date": "2018-11-27", + "title": "React 16.x Roadmap", + "author": [ + "gaearon" + ], + "excerpt": "

You might have heard about features like “Hooks”, “Suspense”, and “Concurrent Rendering” in the previous blog posts and talks. In this post, we’ll look at how they fit together and the expected timeline for their availability in a stable release of React.

\n", + "readingTime": "14 min read" + }, + { + "path": "/blog/2018/11/13/react-conf-recap.md", + "date": "2018-11-13", + "title": "React Conf recap: Hooks, Suspense, and Concurrent Rendering", + "author": [ + "tomocchino" + ], + "excerpt": "

This year’s React Conf took place on October 25 and 26 in Henderson, Nevada, where more than 600 attendees gathered to discuss the latest in UI engineering.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2018/10/23/react-v-16-6.md", + "date": "2018-10-23", + "title": "React v16.6.0: lazy, memo and contextType", + "author": [ + "sebmarkbage" + ], + "excerpt": "

Today we’re releasing React 16.6 with a few new convenient features. A form of PureComponent/shouldComponentUpdate for function components, a way to do code splitting using Suspense and an easier way to consume Context from class components.

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2018/10/01/create-react-app-v2.md", + "date": "2018-10-01", + "title": "Create React App 2.0: Babel 7, Sass, and More", + "author": [ + "timer", + "gaearon" + ], + "excerpt": "

Create React App 2.0 has been released today, and it brings a year’s worth of improvements in a single dependency update.

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2018/09/10/introducing-the-react-profiler.md", + "date": "2018-09-10", + "title": "Introducing the React Profiler", + "author": [ + "bvaughn" + ], + "excerpt": "

React 16.5 adds support for a new DevTools profiler plugin.

\n", + "readingTime": "8 min read" + }, + { + "path": "/blog/2018/08/01/react-v-16-4-2.md", + "date": "2018-08-01", + "title": "React v16.4.2: Server-side vulnerability fix", + "author": [ + "gaearon" + ], + "excerpt": "

We discovered a minor vulnerability that might affect some apps using ReactDOMServer. We are releasing a patch version for every affected React minor release so that you can upgrade with no friction. Read on for more details.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2018/06/07/you-probably-dont-need-derived-state.md", + "date": "2018-06-07", + "title": "You Probably Don't Need Derived State", + "author": [ + "bvaughn" + ], + "excerpt": "

React 16.4 included a bugfix for getDerivedStateFromProps which caused some existing bugs in React components to reproduce more consistently. If this release exposed a case where your application was using an anti-pattern and didn’t work properly after the fix, we’re sorry for the churn. In this post, we will explain some common anti-patterns with derived state and our preferred alternatives.

\n", + "readingTime": "14 min read" + }, + { + "path": "/blog/2018/05/23/react-v-16-4.md", + "date": "2018-05-23", + "title": "React v16.4.0: Pointer Events", + "author": [ + "acdlite" + ], + "excerpt": "

The latest minor release adds support for an oft-requested feature: pointer events!

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2018/03/29/react-v-16-3.md", + "date": "2018-03-29", + "title": "React v16.3.0: New lifecycles and context API", + "author": [ + "bvaughn" + ], + "excerpt": "

A few days ago, we wrote a post about upcoming changes to our legacy lifecycle methods, including gradual migration strategies. In React 16.3.0, we are adding a few new lifecycle methods to assist with that migration. We are also introducing new APIs for long requested features: an official context API, a ref forwarding API, and an ergonomic ref API.

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2018/03/27/update-on-async-rendering.md", + "date": "2018-03-27", + "title": "Update on Async Rendering", + "author": [ + "bvaughn" + ], + "excerpt": "

For over a year, the React team has been working to implement asynchronous rendering. Last month during his talk at JSConf Iceland, Dan unveiled some of the exciting new possibilities async rendering unlocks. Now we’d like to share with you some of the lessons we’ve learned while working on these features, and some recipes to help prepare your components for async rendering when it launches.

\n", + "readingTime": "12 min read" + }, + { + "path": "/blog/2018/03/01/sneak-peek-beyond-react-16.md", + "date": "2018-03-01", + "title": "Sneak Peek: Beyond React 16", + "author": [ + "sophiebits" + ], + "excerpt": "

Dan Abramov from our team just spoke at JSConf Iceland 2018 with a preview of some new features we’ve been working on in React. The talk opens with a question: “With vast differences in computing power and network speed, how do we deliver the best user experience for everyone?”

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2017/12/15/improving-the-repository-infrastructure.md", + "date": "2017-12-15", + "title": "Behind the Scenes: Improving the Repository Infrastructure", + "author": [ + "gaearon", + "bvaughn" + ], + "excerpt": "

As we worked on React 16, we revamped the folder structure and much of the build tooling in the React repository. Among other things, we introduced projects such as Rollup, Prettier, and Google Closure Compiler into our workflow. People often ask us questions about how we use those tools. In this post, we would like to share some of the changes that we’ve made to our build and test infrastructure in 2017, and what motivated them.

\n", + "readingTime": "30 min read" + }, + { + "path": "/blog/2017/12/07/introducing-the-react-rfc-process.md", + "date": "2017-12-07", + "title": "Introducing the React RFC Process", + "author": [ + "acdlite" + ], + "excerpt": "

We’re adopting an RFC (“request for comments”) process for contributing ideas to React.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2017/11/28/react-v16.2.0-fragment-support.md", + "date": "2017-11-28", + "title": "React v16.2.0: Improved Support for Fragments", + "author": [ + "clemmy" + ], + "excerpt": "

React 16.2 is now available! The biggest addition is improved support for returning multiple children from a component’s render method. We call this feature fragments:

\n", + "readingTime": "8 min read" + }, + { + "path": "/blog/2017/09/26/react-v16.0.md", + "date": "2017-09-26", + "title": "React v16.0", + "author": [ + "acdlite" + ], + "excerpt": "

We’re excited to announce the release of React v16.0! Among the changes are some long-standing feature requests, including fragments, error boundaries, portals, support for custom DOM attributes, improved server-side rendering, and reduced file size.

\n", + "readingTime": "11 min read" + }, + { + "path": "/blog/2017/09/25/react-v15.6.2.md", + "date": "2017-09-25", + "title": "React v15.6.2", + "author": [ + "nhunzaker" + ], + "excerpt": "

Today we’re sending out React 15.6.2. In 15.6.1, we shipped a few fixes for change events and inputs that had some unintended consequences. Those regressions have been ironed out, and we’ve also included a few more fixes to improve the stability of React across all browsers.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2017/09/08/dom-attributes-in-react-16.md", + "date": "2017-09-08", + "title": "DOM Attributes in React 16", + "author": [ + "gaearon" + ], + "excerpt": "

In the past, React used to ignore unknown DOM attributes. If you wrote JSX with an attribute that React doesn’t recognize, React would just skip it. For example, this:

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2017/07/26/error-handling-in-react-16.md", + "date": "2017-07-26", + "title": "Error Handling in React 16", + "author": [ + "gaearon" + ], + "excerpt": "

As React 16 release is getting closer, we would like to announce a few changes to how React handles JavaScript errors inside components. These changes are included in React 16 beta versions, and will be a part of React 16.

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2017/06/13/react-v15.6.0.md", + "date": "2017-06-13", + "title": "React v15.6.0", + "author": [ + "flarnie" + ], + "excerpt": "

Today we are releasing React 15.6.0. As we prepare for React 16.0, we have been fixing and cleaning up many things. This release continues to pave the way.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2017/05/18/whats-new-in-create-react-app.md", + "date": "2017-05-18", + "title": "What's New in Create React App", + "author": [ + "gaearon" + ], + "excerpt": "

Less than a year ago, we introduced Create React App as an officially supported way to create apps with zero configuration. The project has since enjoyed tremendous growth, with over 950 commits by more than 250 contributors.

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2017/04/07/react-v15.5.0.md", + "date": "2017-04-07", + "title": "React v15.5.0", + "author": [ + "acdlite" + ], + "excerpt": "

It’s been exactly one year since the last breaking change to React. Our next major release, React 16, will include some exciting improvements, including a complete rewrite of React’s internals. We take stability seriously, and are committed to bringing those improvements to all of our users with minimal effort.

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2016/11/16/react-v15.4.0.md", + "date": "2016-11-16", + "title": "React v15.4.0", + "author": [ + "gaearon" + ], + "excerpt": "

Today we are releasing React 15.4.0.

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2016/09/28/our-first-50000-stars.md", + "date": "2016-09-28", + "title": "Our First 50,000 Stars", + "author": [ + "vjeux" + ], + "excerpt": "

Just three and a half years ago we open sourced a little JavaScript library called React. The journey since that day has been incredibly exciting.

\n", + "readingTime": "10 min read" + }, + { + "path": "/blog/2016/08/05/relay-state-of-the-state.md", + "date": "2016-08-05", + "title": "Relay: State of the State", + "author": [ + "josephsavona" + ], + "excerpt": "

This month marks a year since we released Relay and we’d like to share an update on the project and what’s next.

\n", + "readingTime": "8 min read" + }, + { + "path": "/blog/2016/07/22/create-apps-with-no-configuration.md", + "date": "2016-07-22", + "title": "Create Apps with No Configuration", + "author": [ + "gaearon" + ], + "excerpt": "

Create React App is a new officially supported way to create single-page React applications. It offers a modern build setup with no configuration.

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2016/07/13/mixins-considered-harmful.md", + "date": "2016-07-13", + "title": "Mixins Considered Harmful", + "author": [ + "gaearon" + ], + "excerpt": "

“How do I share the code between several components?” is one of the first questions that people ask when they learn React. Our answer has always been to use component composition for code reuse. You can define a component and use it in several other components.

\n", + "readingTime": "20 min read" + }, + { + "path": "/blog/2016/07/11/introducing-reacts-error-code-system.md", + "date": "2016-07-11", + "title": "Introducing React's Error Code System", + "author": [ + "keyanzhang" + ], + "excerpt": "

Building a better developer experience has been one of the things that React deeply cares about, and a crucial part of it is to detect anti-patterns/potential errors early and provide helpful error messages when things (may) go wrong. However, most of these only exist in development mode; in production, we avoid having extra expensive assertions and sending down full error messages in order to reduce the number of bytes sent over the wire.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2016/04/08/react-v15.0.1.md", + "date": "2016-04-08", + "title": "React v15.0.1", + "author": [ + "zpao" + ], + "excerpt": "

Yesterday afternoon we shipped v15.0.0 and quickly got some feedback about a couple of issues. We apologize for these problems and we’ve been working since then to make sure we get fixes into your hands as quickly as possible.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2016/04/07/react-v15.md", + "date": "2016-04-07", + "title": "React v15.0", + "author": [ + "gaearon" + ], + "excerpt": "

We would like to thank the React community for reporting issues and regressions in the release candidates on our issue tracker. Over the last few weeks we fixed those issues, and now, after two release candidates, we are excited to finally release the stable version of React 15.

\n", + "readingTime": "15 min read" + }, + { + "path": "/blog/2016/03/29/react-v0.14.8.md", + "date": "2016-03-29", + "title": "React v0.14.8", + "author": [ + "gaearon" + ], + "excerpt": "

We have already released two release candidates for React 15, and the final version is coming soon.

\n", + "readingTime": "1 min read" + }, + { + "path": "/blog/2016/03/16/react-v15-rc2.md", + "date": "2016-03-16", + "title": "React v15.0 Release Candidate 2", + "author": [ + "zpao" + ], + "excerpt": "

Today we’re releasing a second release candidate for version 15. Primarily this is to address 2 issues, but we also picked up a few small changes from new contributors, including some improvements to some of our new warnings.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2016/03/07/react-v15-rc1.md", + "date": "2016-03-07", + "title": "React v15.0 Release Candidate", + "author": [ + "zpao" + ], + "excerpt": "

Sorry for the small delay in releasing this. As we said, we’ve been busy binge-watching House of Cards. That scene in the last episode where Francis and Claire Underwood ████████████████████████████████████. WOW!

\n", + "readingTime": "8 min read" + }, + { + "path": "/blog/2016/02/19/new-versioning-scheme.md", + "date": "2016-02-19", + "title": "New Versioning Scheme", + "author": [ + "sebmarkbage" + ], + "excerpt": "

Today we’re announcing that we’re switching to major revisions for React. The current version is 0.14.7. The next release will be: 15.0.0

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2016/01/12/discontinuing-ie8-support.md", + "date": "2016-01-12", + "title": "Discontinuing IE 8 Support in React DOM", + "author": [ + "sophiebits" + ], + "excerpt": "

Since its 2013 release, React has supported all popular browsers, including Internet Explorer 8 and above. We handle normalizing many quirks present in old browser versions, including event system differences, so that your app code doesn’t have to worry about most browser bugs.

\n", + "readingTime": "1 min read" + }, + { + "path": "/blog/2016/01/08/A-implies-B-does-not-imply-B-implies-A.md", + "date": "2016-01-08", + "title": "(A => B) !=> (B => A)", + "author": [ + "jimfb" + ], + "excerpt": "

The documentation for componentWillReceiveProps states that componentWillReceiveProps will be invoked when the props change as the result of a rerender. Some people assume this means “if componentWillReceiveProps is called, then the props must have changed”, but that conclusion is logically incorrect.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2015/12/29/react-v0.14.4.md", + "date": "2015-12-29", + "title": "React v0.14.4", + "author": [ + "sophiebits" + ], + "excerpt": "

Happy December! We have a minor point release today. It has just a few small bug fixes.

\n", + "readingTime": "1 min read" + }, + { + "path": "/blog/2015/12/18/react-components-elements-and-instances.md", + "date": "2015-12-18", + "title": "React Components, Elements, and Instances", + "author": [ + "gaearon" + ], + "excerpt": "

The difference between components, their instances, and elements confuses many React beginners. Why are there three different terms to refer to something that is painted on screen?

\n", + "readingTime": "11 min read" + }, + { + "path": "/blog/2015/12/16/ismounted-antipattern.md", + "date": "2015-12-16", + "title": "isMounted is an Antipattern", + "author": [ + "jimfb" + ], + "excerpt": "

As we move closer to officially deprecating isMounted, it’s worth understanding why the function is an antipattern, and how to write code without the isMounted function.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2015/12/04/react-js-conf-2016-diversity-scholarship.md", + "date": "2015-12-04", + "title": "React.js Conf 2016 Diversity Scholarship", + "author": [ + "zpao" + ], + "excerpt": "

I am thrilled to announced that we will be organizing another diversity scholarship program for the upcoming React.js Conf! The tech industry is suffering from a lack of diversity, but it’s important to us that we have a thriving community that is made up of people with a variety of experiences and viewpoints.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2015/11/18/react-v0.14.3.md", + "date": "2015-11-18", + "title": "React v0.14.3", + "author": [ + "zpao" + ], + "excerpt": "

It’s time for another installment of React patch releases! We didn’t break anything in v0.14.2 but we do have a couple of other bugs we’re fixing. The biggest change in this release is actually an addition of a new built file. We heard from a number of people that they still need the ability to use React to render to a string on the client. While the use cases are not common and there are other ways to achieve this, we decided that it’s still valuable to support. So we’re now building react-dom-server.js, which will be shipped to Bower and in the dist/ directory of the react-dom package on npm. This file works the same way as react-dom.js and therefore requires that the primary React build has already been included on the page.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2015/11/02/react-v0.14.2.md", + "date": "2015-11-02", + "title": "React v0.14.2", + "author": [ + "zpao" + ], + "excerpt": "

We have a quick update following the release of 0.14.1 last week. It turns out we broke a couple things in the development build of React when using Internet Explorer. Luckily it was only the development build, so your production applications were unaffected. This release is mostly to address those issues. There is one notable change if consuming React from npm. For the react-dom package, we moved react from a regular dependency to a peer dependency. This will impact very few people as these two are typically installed together at the top level, but it will fix some issues with dependencies of installed components also using react as a peer dependency.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2015/10/28/react-v0.14.1.md", + "date": "2015-10-28", + "title": "React v0.14.1", + "author": [ + "zpao" + ], + "excerpt": "

After a couple weeks of having more people use v0.14, we’re ready to ship a patch release addressing a few issues. Thanks to everybody who has reported issues and written patches!

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2015/10/19/reactiflux-is-moving-to-discord.md", + "date": "2015-10-19", + "title": "Reactiflux is moving to Discord", + "author": [ + "benigeri" + ], + "excerpt": "

TL;DR: Slack decided that Reactiflux had too many members and disabled new invites. Reactiflux is moving to Discord. Join us: http://join.reactiflux.com

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2015/10/07/react-v0.14.md", + "date": "2015-10-07", + "title": "React v0.14", + "author": [ + "sophiebits" + ], + "excerpt": "

We’re happy to announce the release of React 0.14 today! This release has a few major changes, primarily designed to simplify the code you write every day and to better support environments like React Native.

\n", + "readingTime": "12 min read" + }, + { + "path": "/blog/2015/10/01/react-render-and-top-level-api.md", + "date": "2015-10-01", + "title": "ReactDOM.render and the Top Level React API", + "author": [ + "jimfb", + "sebmarkbage" + ], + "excerpt": "

When you’re in React’s world you are just building components that fit into other components. Everything is a component. Unfortunately not everything around you is built using React. At the root of your tree you still have to write some plumbing code to connect the outer world into React.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2015/09/14/community-roundup-27.md", + "date": "2015-09-14", + "title": "Community Round-up #27 – Relay Edition", + "author": [ + "steveluscher" + ], + "excerpt": "

In the weeks following the open-source release of the Relay technical preview, the community has been abuzz with activity. We are honored to have been able to enjoy a steady stream of ideas and contributions from such a talented group of individuals. Let’s take a look at some of the things we’ve achieved, together!

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2015/09/10/react-v0.14-rc1.md", + "date": "2015-09-10", + "title": "React v0.14 Release Candidate", + "author": [ + "sophiebits" + ], + "excerpt": "

We’re happy to announce our first release candidate for React 0.14! We gave you a sneak peek in July at the upcoming changes but we’ve now stabilized the release more and we’d love for you to try it out before we release the final version.

\n", + "readingTime": "11 min read" + }, + { + "path": "/blog/2015/09/02/new-react-developer-tools.md", + "date": "2015-09-02", + "title": "New React Developer Tools", + "author": [ + "sophiebits" + ], + "excerpt": "

A month ago, we posted a beta of the new React developer tools. Today, we’re releasing the first stable version of the new devtools. We’re calling it version 0.14, but it’s a full rewrite so we think of it more like a 2.0 release.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2015/08/13/reacteurope-roundup.md", + "date": "2015-08-13", + "title": "ReactEurope Round-up", + "author": [ + "matthewjohnston4" + ], + "excerpt": "

Last month, the first React.js European conference took place in the city of Paris, at ReactEurope. Attendees were treated to a range of talks covering React, React Native, Flux, Relay, and GraphQL. Big thanks to everyone involved with organizing the conference, to all the attendees, and everyone who gave their time to speak - it wouldn’t have been possible without the help and support of the React community.

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2015/08/11/relay-technical-preview.md", + "date": "2015-08-11", + "title": "Relay Technical Preview", + "author": [ + "josephsavona" + ], + "excerpt": "

Relay

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2015/08/03/new-react-devtools-beta.md", + "date": "2015-08-03", + "title": "New React Devtools Beta", + "author": [ + "jaredly" + ], + "excerpt": "

We’ve made an entirely new version of the devtools, and we want you to try it

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2015/07/03/react-v0.14-beta-1.md", + "date": "2015-07-03", + "title": "React v0.14 Beta 1", + "author": [ + "sophiebits" + ], + "excerpt": "

This week, many people in the React community are at ReactEurope in the beautiful (and very warm) city of Paris, the second React conference that’s been held to date. At our last conference, we released the first beta of React 0.13, and we figured we’d do the same today with our first beta of React 0.14, giving you something to play with if you’re not at the conference or you’re looking for something to do on the way home.

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2015/06/12/deprecating-jstransform-and-react-tools.md", + "date": "2015-06-12", + "title": "Deprecating JSTransform and react-tools", + "author": [ + "zpao" + ], + "excerpt": "

Today we’re announcing the deprecation of react-tools and JSTransform.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2015/05/22/react-native-release-process.md", + "date": "2015-05-22", + "title": "React Native Release Process", + "author": [ + "vjeux" + ], + "excerpt": "

The React Native release process have been a bit chaotic since we open sourced. It was unclear when new code was released, there was no changelog, we bumped the minor and patch version inconsistently and we often had to submit updates right after a release to fix a bad bug. In order to move fast with stable infra, we are introducing a real release process with a two-week release schedule.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2015/05/08/react-v0.13.3.md", + "date": "2015-05-08", + "title": "React v0.13.3", + "author": [ + "zpao" + ], + "excerpt": "

Today we’re sharing another patch release in the v0.13 branch. There are only a few small changes, with a couple to address some issues that arose around that undocumented feature so many of you are already using: context. We also improved developer ergonomics just a little bit, making some warnings better.

\n", + "readingTime": "1 min read" + }, + { + "path": "/blog/2015/05/01/graphql-introduction.md", + "date": "2015-05-01", + "title": "GraphQL Introduction", + "author": [ + "schrockn" + ], + "excerpt": "

At the React.js conference in late January 2015, we revealed our next major technology in the React family: Relay.

\n", + "readingTime": "11 min read" + }, + { + "path": "/blog/2015/04/18/react-v0.13.2.md", + "date": "2015-04-18", + "title": "React v0.13.2", + "author": [ + "zpao" + ], + "excerpt": "

Yesterday the React Native team shipped v0.4. Those of us working on the web team just a few feet away couldn’t just be shown up like that so we’re shipping v0.13.2 today as well! This is a bug fix release to address a few things while we continue to work towards v0.14.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2015/04/17/react-native-v0.4.md", + "date": "2015-04-17", + "title": "React Native v0.4", + "author": [ + "vjeux" + ], + "excerpt": "

It’s been three weeks since we open sourced React Native and there’s been some insane amount of activity already: over 12.5k stars, 1000 commits, 500 issues, 380 pull requests, and 100 contributors, plus 35 plugins and 1 app in the app store! We were expecting some buzz around the project but this is way beyond anything we imagined. Thank you!

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2015/03/30/community-roundup-26.md", + "date": "2015-03-30", + "title": "Community Round-up #26", + "author": [ + "vjeux" + ], + "excerpt": "

We open sourced React Native last week and the community reception blew away all our expectations! So many of you tried it, made cool stuff with it, raised many issues and even submitted pull requests to fix them! The entire team wants to say thank you!

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2015/03/26/introducing-react-native.md", + "date": "2015-03-26", + "title": "Introducing React Native", + "author": [ + "sophiebits" + ], + "excerpt": "

In January at React.js Conf, we announced React Native, a new framework for building native apps using React. We’re happy to announce that we’re open-sourcing React Native and you can start building your apps with it today.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2015/03/19/building-the-facebook-news-feed-with-relay.md", + "date": "2015-03-19", + "title": "Building The Facebook News Feed With Relay", + "author": [ + "josephsavona" + ], + "excerpt": "

At React.js Conf in January we gave a preview of Relay, a new framework for building data-driven applications in React. In this post, we’ll describe the process of creating a Relay application. This post assumes some familiarity with the concepts of Relay and GraphQL, so if you haven’t already we recommend reading our introductory blog post or watching the conference talk.

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2015/03/16/react-v0.13.1.md", + "date": "2015-03-16", + "title": "React v0.13.1", + "author": [ + "zpao" + ], + "excerpt": "

It’s been less than a week since we shipped v0.13.0 but it’s time to do another quick release. We just released v0.13.1 which contains bugfixes for a number of small issues.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2015/03/10/react-v0.13.md", + "date": "2015-03-10", + "title": "React v0.13", + "author": [ + "sophiebits" + ], + "excerpt": "

Today, we’re happy to release React v0.13!

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2015/03/04/community-roundup-25.md", + "date": "2015-03-04", + "title": "Community Round-up #25", + "author": [ + "matthewjohnston4" + ], + "excerpt": "

React 101

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2015/03/03/react-v0.13-rc2.md", + "date": "2015-03-03", + "title": "React v0.13 RC2", + "author": [ + "sebmarkbage" + ], + "excerpt": "

Thanks to everybody who has already been testing the release candidate. We’ve received some good feedback and as a result we’re going to do a second release candidate. The changes are minimal. We haven’t changed the behavior of any APIs we exposed in the previous release candidate. Here’s a summary of the changes:

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2015/02/24/streamlining-react-elements.md", + "date": "2015-02-24", + "title": "Streamlining React Elements", + "author": [ + "sebmarkbage" + ], + "excerpt": "

React v0.13 is right around the corner and so we wanted to discuss some upcoming changes to ReactElement. In particular, we added several warnings to some esoteric use cases of ReactElement. There are no runtime behavior changes for ReactElement - we’re adding these warnings in the hope that we can change some behavior in v0.14 if the changes are valuable to the community.

\n", + "readingTime": "10 min read" + }, + { + "path": "/blog/2015/02/24/react-v0.13-rc1.md", + "date": "2015-02-24", + "title": "React v0.13 RC", + "author": [ + "zpao" + ], + "excerpt": "

Over the weekend we pushed out our first (and hopefully only) release candidate for React v0.13!

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2015/02/20/introducing-relay-and-graphql.md", + "date": "2015-02-20", + "title": "Introducing Relay and GraphQL", + "author": [ + "wincent" + ], + "excerpt": "

Data fetching for React applications

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2015/02/18/react-conf-roundup-2015.md", + "date": "2015-02-18", + "title": "React.js Conf Round-up 2015", + "author": [ + "steveluscher" + ], + "excerpt": "

It was a privilege to welcome the React community to Facebook HQ on January 28–29 for the first-ever React.js Conf, and a pleasure to be able to unveil three new technologies that we’ve been using internally at Facebook for some time: GraphQL, Relay, and React Native.

\n", + "readingTime": "8 min read" + }, + { + "path": "/blog/2015/01/27/react-v0.13.0-beta-1.md", + "date": "2015-01-27", + "title": "React v0.13.0 Beta 1", + "author": [ + "sebmarkbage" + ], + "excerpt": "

React 0.13 has a lot of nice features but there is one particular feature that I’m really excited about. I couldn’t wait for React.js Conf to start tomorrow morning.

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2014/12/19/react-js-conf-diversity-scholarship.md", + "date": "2014-12-19", + "title": "React.js Conf Diversity Scholarship", + "author": [ + "zpao" + ], + "excerpt": "

Today I’m really happy to announce the React.js Conf Diversity Scholarship! We believe that a diverse set of viewpoints and opinions is really important to build a thriving community. In an ideal world, every part of the tech community would be made up of people from all walks of life. However the reality is that we must be proactive and make an effort to make sure everybody has a voice. As conference organizers we worked closely with the Diversity Team here at Facebook to set aside 10 tickets and provide a scholarship. 10 tickets may not be many in the grand scheme but we really believe that this will have a positive impact on the discussions we have at the conference.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2014/12/18/react-v0.12.2.md", + "date": "2014-12-18", + "title": "React v0.12.2", + "author": [ + "zpao" + ], + "excerpt": "

We just shipped React v0.12.2, bringing the 0.12 branch up to date with a few small fixes that landed in master over the past 2 months.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2014/11/25/community-roundup-24.md", + "date": "2014-11-25", + "title": "Community Round-up #24", + "author": [ + "steveluscher" + ], + "excerpt": "

Keep it Simple

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2014/11/24/react-js-conf-updates.md", + "date": "2014-11-24", + "title": "React.js Conf Updates", + "author": [ + "vjeux" + ], + "excerpt": "

Yesterday was the React.js Conf call for presenters submission deadline. We were

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2014/10/28/react-v0.12.md", + "date": "2014-10-28", + "title": "React v0.12", + "author": [ + "zpao" + ], + "excerpt": "

We’re happy to announce the availability of React v0.12! After over a week of baking as the release candidate, we uncovered and fixed a few small issues. Thanks to all of you who upgraded and gave us feedback!

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2014/10/27/react-js-conf.md", + "date": "2014-10-27", + "title": "React.js Conf", + "author": [ + "vjeux" + ], + "excerpt": "

Every few weeks someone asks us when we are going to organize a conference for React. Our answer has always been “some day”. In the mean time, people have been talking about React at other JavaScript conferences around the world. But now the time has finally come for us to have a conference of our own.

\n", + "readingTime": "1 min read" + }, + { + "path": "/blog/2014/10/17/community-roundup-23.md", + "date": "2014-10-17", + "title": "Community Round-up #23", + "author": [ + "LoukaN" + ], + "excerpt": "

This round-up is a special edition on Flux. If you expect to see diagrams showing arrows that all point in the same direction, you won’t be disappointed!

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2014/10/16/react-v0.12-rc1.md", + "date": "2014-10-16", + "title": "React v0.12 RC", + "author": [ + "sebmarkbage" + ], + "excerpt": "

We are finally ready to share the work we’ve been doing over the past couple months. A lot has gone into this and we want to make sure we iron out any potential issues before we make this final. So, we’re shipping a Release Candidate for React v0.12 today. If you get a chance, please give it a try and report any issues you find! A full changelog will accompany the final release but we’ve highlighted the interesting and breaking changes below.

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2014/10/14/introducing-react-elements.md", + "date": "2014-10-14", + "title": "Introducing React Elements", + "author": [ + "sebmarkbage" + ], + "excerpt": "

The upcoming React 0.12 tweaks some APIs to get us close to the final 1.0 API. This release is all about setting us up for making the ReactElement type really FAST, jest unit testing easier, making classes simpler (in preparation for ES6 classes) and better integration with third-party languages!

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2014/09/24/testing-flux-applications.md", + "date": "2014-09-24", + "title": "Testing Flux Applications", + "author": [ + "fisherwebdev" + ], + "excerpt": "

A more up-to-date version of this post is available as part of the Flux documentation.

\n", + "readingTime": "8 min read" + }, + { + "path": "/blog/2014/09/16/react-v0.11.2.md", + "date": "2014-09-16", + "title": "React v0.11.2", + "author": [ + "zpao" + ], + "excerpt": "

Today we’re releasing React v0.11.2 to add a few small features.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2014/09/12/community-round-up-22.md", + "date": "2014-09-12", + "title": "Community Round-up #22", + "author": [ + "LoukaN" + ], + "excerpt": "

This has been an exciting summer as four big companies: Yahoo, Mozilla, Airbnb and Reddit announced that they were using React!

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2014/09/03/introducing-the-jsx-specification.md", + "date": "2014-09-03", + "title": "Introducing the JSX Specification", + "author": [ + "sebmarkbage" + ], + "excerpt": "

At Facebook we’ve been using JSX for a long time. We originally introduced it to the world last year alongside React, but we actually used it in another form before that to create native DOM nodes. We’ve also seen some similar efforts grow out of our work in order to be used with other libraries and in interesting ways. At this point React JSX is just one of many implementations.

\n", + "readingTime": "1 min read" + }, + { + "path": "/blog/2014/08/03/community-roundup-21.md", + "date": "2014-08-03", + "title": "Community Round-up #21", + "author": [ + "LoukaN" + ], + "excerpt": "

React Router

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2014/07/30/flux-actions-and-the-dispatcher.md", + "date": "2014-07-30", + "title": "Flux: Actions and the Dispatcher", + "author": [ + "fisherwebdev" + ], + "excerpt": "

Flux is the application architecture Facebook uses to build JavaScript applications. It’s based on a unidirectional data flow. We’ve built everything from small widgets to huge applications with Flux, and it’s handled everything we’ve thrown at it. Because we’ve found it to be a great way to structure our code, we’re excited to share it with the open source community. Jing Chen presented Flux at the F8 conference, and since that time we’ve seen a lot of interest in it. We’ve also published an overview of Flux and a TodoMVC example, with an accompanying tutorial.

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2014/07/28/community-roundup-20.md", + "date": "2014-07-28", + "title": "Community Round-up #20", + "author": [ + "LoukaN" + ], + "excerpt": "

It’s an exciting time for React as there are now more commits from open source contributors than from Facebook engineers! Keep up the good work :)

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2014/07/25/react-v0.11.1.md", + "date": "2014-07-25", + "title": "React v0.11.1", + "author": [ + "zpao" + ], + "excerpt": "

Today we’re releasing React v0.11.1 to address a few small issues. Thanks to everybody who has reported them as they’ve begun upgrading.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2014/07/17/react-v0.11.md", + "date": "2014-07-17", + "title": "React v0.11", + "author": [ + "zpao" + ], + "excerpt": "

Update: We missed a few important changes in our initial post and changelog. We’ve updated this post with details about Descriptors and Prop Type Validation.

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2014/07/13/react-v0.11-rc1.md", + "date": "2014-07-13", + "title": "React v0.11 RC", + "author": [ + "zpao" + ], + "excerpt": "

It’s that time again… we’re just about ready to ship a new React release! v0.11 includes a wide array of bug fixes and features. We highlighted some of the most important changes below, along with the full changelog.

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2014/06/27/community-roundup-19.md", + "date": "2014-06-27", + "title": "Community Round-up #19", + "author": [ + "chenglou" + ], + "excerpt": "

React Meetups!

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2014/05/29/one-year-of-open-source-react.md", + "date": "2014-05-29", + "title": "One Year of Open-Source React", + "author": [ + "chenglou" + ], + "excerpt": "

Today marks the one-year open-source anniversary of React.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2014/05/06/flux.md", + "date": "2014-05-06", + "title": "Flux: An Application Architecture for React", + "author": [ + "fisherwebdev", + "jingc" + ], + "excerpt": "

We recently spoke at one of f8’s breakout session about Flux, a data flow architecture that works well with React. Check out the video here:

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2014/04/04/reactnet.md", + "date": "2014-04-04", + "title": "Use React and JSX in ASP.NET MVC", + "author": [ + "Daniel15" + ], + "excerpt": "

Today we’re happy to announce the initial release of

\n", + "readingTime": "1 min read" + }, + { + "path": "/blog/2014/03/28/the-road-to-1.0.md", + "date": "2014-03-28", + "title": "The Road to 1.0", + "author": [ + "zpao" + ], + "excerpt": "

When we launched React last spring, we purposefully decided not to call it 1.0. It was production ready, but we had plans to evolve APIs and behavior as we saw how people were using React, both internally and externally. We’ve learned a lot over the past 9 months and we’ve thought a lot about what 1.0 will mean for React. A couple weeks ago, I outlined [several projects][projects] that we have planned to take us to 1.0 and beyond. Today I’m writing a bit more about them to give our users a better insight into our plans.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2014/03/21/react-v0.10.md", + "date": "2014-03-21", + "title": "React v0.10", + "author": [ + "zpao" + ], + "excerpt": "

Hot on the heels of the release candidate earlier this week, we’re ready to call v0.10 done. The only major issue we discovered had to do with the react-tools package, which has been updated. We’ve copied over the changelog from the RC with some small clarifying changes.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2014/03/19/react-v0.10-rc1.md", + "date": "2014-03-19", + "title": "React v0.10 RC", + "author": [ + "zpao" + ], + "excerpt": "

v0.9 has only been out for a month, but we’re getting ready to push out v0.10 already. Unlike v0.9 which took a long time, we don’t have a long list of changes to talk about.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2014/03/14/community-roundup-18.md", + "date": "2014-03-14", + "title": "Community Round-up #18", + "author": [ + "jgebhardt" + ], + "excerpt": "

In this Round-up, we are taking a few closer looks at React’s interplay with different frameworks and architectures.

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2014/02/24/community-roundup-17.md", + "date": "2014-02-24", + "title": "Community Round-up #17", + "author": [ + "jgebhardt" + ], + "excerpt": "

It’s exciting to see the number of real-world React applications and components skyrocket over the past months! This community round-up features a few examples of inspiring React applications and components.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2014/02/20/react-v0.9.md", + "date": "2014-02-20", + "title": "React v0.9", + "author": [ + "sophiebits" + ], + "excerpt": "

I’m excited to announce that today we’re releasing React v0.9, which incorporates many bug fixes and several new features since the last release. This release contains almost four months of work, including over 800 commits from over 70 committers!

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2014/02/16/react-v0.9-rc1.md", + "date": "2014-02-16", + "title": "React v0.9 RC", + "author": [ + "sophiebits" + ], + "excerpt": "

We’re almost ready to release React v0.9! We’re posting a release candidate so that you can test your apps on it; we’d much prefer to find show-stopping bugs now rather than after we release.

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2014/02/15/community-roundup-16.md", + "date": "2014-02-15", + "title": "Community Round-up #16", + "author": [ + "jgebhardt" + ], + "excerpt": "

There have been many posts recently covering the why and how of React. This week’s community round-up includes a collection of recent articles to help you get started with React, along with a few posts that explain some of the inner workings.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2014/02/05/community-roundup-15.md", + "date": "2014-02-05", + "title": "Community Round-up #15", + "author": [ + "jgebhardt" + ], + "excerpt": "

Interest in React seems to have surged ever since David Nolen (@swannodette)‘s introduction of Om in his post “The Future of JavaScript MVC Frameworks”.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2014/01/06/community-roundup-14.md", + "date": "2014-01-06", + "title": "Community Round-up #14", + "author": [ + "vjeux" + ], + "excerpt": "

The theme of this first round-up of 2014 is integration. I’ve tried to assemble a list of articles and projects that use React in various environments.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2014/01/02/react-chrome-developer-tools.md", + "date": "2014-01-02", + "title": "React Chrome Developer Tools", + "author": [ + "sebmarkbage" + ], + "excerpt": "

With the new year, we thought you’d enjoy some new tools for debugging React code. Today we’re releasing the React Developer Tools, an extension to the Chrome Developer Tools. Download them from the Chrome Web Store.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2013/12/30/community-roundup-13.md", + "date": "2013-12-30", + "title": "Community Round-up #13", + "author": [ + "vjeux" + ], + "excerpt": "

Happy holidays! This blog post is a little-late Christmas present for all the React users. Hopefully it will inspire you to write awesome web apps in 2014!

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2013/12/23/community-roundup-12.md", + "date": "2013-12-23", + "title": "Community Round-up #12", + "author": [ + "vjeux" + ], + "excerpt": "

React got featured on the front-page of Hacker News thanks to the Om library. If you try it out for the first time, take a look at the docs and do not hesitate to ask questions on the Google Group, IRC or Stack Overflow. We are trying our best to help you out!

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2013/12/19/react-v0.8.0.md", + "date": "2013-12-19", + "title": "React v0.8", + "author": [ + "zpao" + ], + "excerpt": "

I’ll start by answering the obvious question:

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2013/12/18/react-v0.5.2-v0.4.2.md", + "date": "2013-12-18", + "title": "React v0.5.2, v0.4.2", + "author": [ + "zpao" + ], + "excerpt": "

Today we’re releasing an update to address a potential XSS vulnerability that can arise when using user data as a key. Typically “safe” data is used for a key, for example, an id from your database, or a unique hash. However there are cases where it may be reasonable to use user generated content. A carefully crafted piece of content could result in arbitrary JS execution. While we make a very concerted effort to ensure all text is escaped before inserting it into the DOM, we missed one case. Immediately following the discovery of this vulnerability, we performed an audit to ensure we this was the only such vulnerability.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2013/11/18/community-roundup-11.md", + "date": "2013-11-18", + "title": "Community Round-up #11", + "author": [ + "vjeux" + ], + "excerpt": "

This round-up is the proof that React has taken off from its Facebook’s root: it features three in-depth presentations of React done by external people. This is awesome, keep them coming!

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2013/11/06/community-roundup-10.md", + "date": "2013-11-06", + "title": "Community Round-up #10", + "author": [ + "vjeux" + ], + "excerpt": "

This is the 10th round-up already and React has come quite far since it was open sourced. Almost all new web projects at Khan Academy, Facebook, and Instagram are being developed using React. React has been deployed in a variety of contexts: a Chrome extension, a Windows 8 application, mobile websites, and desktop websites supporting Internet Explorer 8! Language-wise, React is not only being used within JavaScript but also CoffeeScript and ClojureScript.

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2013/10/29/react-v0-5-1.md", + "date": "2013-10-29", + "title": "React v0.5.1", + "author": [ + "zpao" + ], + "excerpt": "

This release focuses on fixing some small bugs that have been uncovered over the past two weeks. I would like to thank everybody involved, specifically members of the community who fixed half of the issues found. Thanks to [Sophie Alpert][1], [Andrey Popp][2], and [Laurence Rowe][3] for their contributions!

\n", + "readingTime": "1 min read" + }, + { + "path": "/blog/2013/10/16/react-v0.5.0.md", + "date": "2013-10-16", + "title": "React v0.5", + "author": [ + "zpao" + ], + "excerpt": "

This release is the result of several months of hard work from members of the team and the community. While there are no groundbreaking changes in core, we’ve worked hard to improve performance and memory usage. We’ve also worked hard to make sure we are being consistent in our usage of DOM properties.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2013/10/03/community-roundup-9.md", + "date": "2013-10-03", + "title": "Community Round-up #9", + "author": [ + "vjeux" + ], + "excerpt": "

We organized a React hackathon last week-end in the Facebook Seattle office. 50 people, grouped into 15 teams, came to hack for a day on React. It was a lot of fun and we’ll probably organize more in the future.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2013/09/24/community-roundup-8.md", + "date": "2013-09-24", + "title": "Community Round-up #8", + "author": [ + "vjeux" + ], + "excerpt": "

A lot has happened in the month since our last update. Here are some of the more interesting things we’ve found. But first, we have a couple updates before we share links.

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2013/08/26/community-roundup-7.md", + "date": "2013-08-26", + "title": "Community Round-up #7", + "author": [ + "vjeux" + ], + "excerpt": "

It’s been three months since we open sourced React and it is going well. Some stats so far:

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2013/08/19/use-react-and-jsx-in-python-applications.md", + "date": "2013-08-19", + "title": "Use React and JSX in Python Applications", + "author": [ + "kmeht" + ], + "excerpt": "

Today we’re happy to announce the initial release of PyReact, which makes it easier to use React and JSX in your Python applications. It’s designed to provide an API to transform your JSX files into JavaScript, as well as provide access to the latest React source files.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2013/08/05/community-roundup-6.md", + "date": "2013-08-05", + "title": "Community Round-up #6", + "author": [ + "vjeux" + ], + "excerpt": "

This is the first Community Round-up where none of the items are from Facebook/Instagram employees. It’s great to see the adoption of React growing.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2013/07/30/use-react-and-jsx-in-ruby-on-rails.md", + "date": "2013-07-30", + "title": "Use React and JSX in Ruby on Rails", + "author": [ + "zpao" + ], + "excerpt": "

Today we’re releasing a gem to make it easier to use React and JSX in Ruby on Rails applications: react-rails.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2013/07/26/react-v0-4-1.md", + "date": "2013-07-26", + "title": "React v0.4.1", + "author": [ + "zpao" + ], + "excerpt": "

React v0.4.1 is a small update, mostly containing correctness fixes. Some code has been restructured internally but those changes do not impact any of our public APIs.

\n", + "readingTime": "1 min read" + }, + { + "path": "/blog/2013/07/23/community-roundup-5.md", + "date": "2013-07-23", + "title": "Community Round-up #5", + "author": [ + "vjeux" + ], + "excerpt": "

We launched the React Facebook Page along with the React v0.4 launch. 700 people already liked it to get updated on the project :)

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2013/07/17/react-v0-4-0.md", + "date": "2013-07-17", + "title": "React v0.4.0", + "author": [ + "zpao" + ], + "excerpt": "

Over the past 2 months we’ve been taking feedback and working hard to make React even better. We fixed some bugs, made some under-the-hood improvements, and added several features that we think will improve the experience developing with React. Today we’re proud to announce the availability of React v0.4!

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2013/07/11/react-v0-4-prop-validation-and-default-values.md", + "date": "2013-07-11", + "title": "New in React v0.4: Prop Validation and Default Values", + "author": [ + "zpao" + ], + "excerpt": "

Many of the questions we got following the public launch of React revolved around props, specifically that people wanted to do validation and to make sure their components had sensible defaults.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2013/07/03/community-roundup-4.md", + "date": "2013-07-03", + "title": "Community Round-up #4", + "author": [ + "vjeux" + ], + "excerpt": "

React reconciliation process appears to be very well suited to implement a text editor with a live preview as people at Khan Academy show us.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2013/07/02/react-v0-4-autobind-by-default.md", + "date": "2013-07-02", + "title": "New in React v0.4: Autobind by Default", + "author": [ + "zpao" + ], + "excerpt": "

React v0.4 is very close to completion. As we finish it off, we’d like to share with you some of the major changes we’ve made since v0.3. This is the first of several posts we’ll be making over the next week.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2013/06/27/community-roundup-3.md", + "date": "2013-06-27", + "title": "Community Round-up #3", + "author": [ + "vjeux" + ], + "excerpt": "

The highlight of this week is that an interaction-heavy app has been ported to React. React components are solving issues they had with nested views.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2013/06/21/react-v0-3-3.md", + "date": "2013-06-21", + "title": "React v0.3.3", + "author": [ + "zpao" + ], + "excerpt": "

We have a ton of great stuff coming in v0.4, but in the meantime we’re releasing v0.3.3. This release addresses some small issues people were having and simplifies our tools to make them easier to use.

\n", + "readingTime": "1 min read" + }, + { + "path": "/blog/2013/06/19/community-roundup-2.md", + "date": "2013-06-19", + "title": "Community Round-up #2", + "author": [ + "vjeux" + ], + "excerpt": "

Since the launch we have received a lot of feedback and are actively working on React 0.4. In the meantime, here are the highlights of this week.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2013/06/12/community-roundup.md", + "date": "2013-06-12", + "title": "Community Round-up #1", + "author": [ + "vjeux" + ], + "excerpt": "

React was open sourced two weeks ago and it’s time for a little round-up of what has been going on.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2013/06/05/why-react.md", + "date": "2013-06-05", + "title": "Why did we build React?", + "author": [ + "petehunt" + ], + "excerpt": "

There are a lot of JavaScript MVC frameworks out there. Why did we build React

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2013/06/02/jsfiddle-integration.md", + "date": "2013-06-02", + "title": "JSFiddle Integration", + "author": [ + "vjeux" + ], + "excerpt": "

JSFiddle just announced support for React. This is an exciting news as it makes collaboration on snippets of code a lot easier. You can play around this base React JSFiddle, fork it and share it! A fiddle without JSX is also available.

\n", + "readingTime": "1 min read" + } + ] +} \ No newline at end of file diff --git a/beta/src/blogIndexRecent.json b/beta/src/blogIndexRecent.json new file mode 100644 index 000000000..8bf964b5c --- /dev/null +++ b/beta/src/blogIndexRecent.json @@ -0,0 +1,207 @@ +{ + "title": "Recent Posts", + "heading": true, + "path": "/blog", + "routes": [ + { + "path": "/blog/2020/08/10/react-v17-rc.md", + "date": "2020-08-10", + "title": "React v17.0 Release Candidate: No New Features", + "author": ["gaearon", "rachelnabors"], + "excerpt": "

Today, we are publishing the first Release Candidate for React 17. It has been two and a half years since the previous major release of React, which is a long time even by our standards! In this blog post, we will describe the role of this major release, what changes you can expect in it, and how you can try this release.

\n", + "readingTime": "20 min read" + }, + { + "path": "/blog/2020/02/26/react-v16.13.0.md", + "date": "2020-02-26", + "title": "React v16.13.0", + "author": ["threepointone"], + "excerpt": "

Today we are releasing React 16.13.0. It contains bugfixes and new deprecation warnings to help prepare for a future major release.

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2019/11/06/building-great-user-experiences-with-concurrent-mode-and-suspense.md", + "date": "2019-11-06", + "title": "Building Great User Experiences with Concurrent Mode and Suspense", + "author": ["josephsavona"], + "excerpt": "

At React Conf 2019 we announced an experimental release of React that supports Concurrent Mode and Suspense. In this post we’ll introduce best practices for using them that we’ve identified through the process of building the new facebook.com.

\n", + "readingTime": "17 min read" + }, + { + "path": "/blog/2019/10/22/react-release-channels.md", + "date": "2019-10-22", + "title": "Preparing for the Future with React Prereleases", + "author": ["acdlite"], + "excerpt": "

To share upcoming changes with our partners in the React ecosystem, we’re establishing official prerelease channels. We hope this process will help us make changes to React with confidence, and give developers the opportunity to try out experimental features.

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2019/08/15/new-react-devtools.md", + "date": "2019-08-15", + "title": "Introducing the New React DevTools", + "author": ["bvaughn"], + "excerpt": "

We are excited to announce a new release of the React Developer Tools, available today in Chrome, Firefox, and (Chromium) Edge!

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2019/08/08/react-v16.9.0.md", + "date": "2019-08-08", + "title": "React v16.9.0 and the Roadmap Update", + "author": ["gaearon", "bvaughn"], + "excerpt": "

Today we are releasing React 16.9. It contains several new features, bugfixes, and new deprecation warnings to help prepare for a future major release.

\n", + "readingTime": "11 min read" + }, + { + "path": "/blog/2019/02/23/is-react-translated-yet.md", + "date": "2019-02-23", + "title": "Is React Translated Yet? ¡Sí! Sim! はい!", + "author": ["tesseralis"], + "excerpt": "

We’re excited to announce an ongoing effort to maintain official translations of the React documentation website into different languages. Thanks to the dedicated efforts of React community members from around the world, React is now being translated into over 30 languages! You can find them on the new Languages page.

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2019/02/06/react-v16.8.0.md", + "date": "2019-02-06", + "title": "React v16.8: The One With Hooks", + "author": ["gaearon"], + "excerpt": "

With React 16.8, React Hooks are available in a stable release!

\n", + "readingTime": "7 min read" + }, + { + "path": "/blog/2018/12/19/react-v-16-7.md", + "date": "2018-12-19", + "title": "React v16.7: No, This Is Not the One With Hooks", + "author": ["acdlite"], + "excerpt": "

Our latest release includes an important performance bugfix for React.lazy. Although there are no API changes, we’re releasing it as a minor instead of a patch.

\n", + "readingTime": "3 min read" + }, + { + "path": "/blog/2018/11/27/react-16-roadmap.md", + "date": "2018-11-27", + "title": "React 16.x Roadmap", + "author": ["gaearon"], + "excerpt": "

You might have heard about features like “Hooks”, “Suspense”, and “Concurrent Rendering” in the previous blog posts and talks. In this post, we’ll look at how they fit together and the expected timeline for their availability in a stable release of React.

\n", + "readingTime": "14 min read" + }, + { + "path": "/blog/2018/11/13/react-conf-recap.md", + "date": "2018-11-13", + "title": "React Conf recap: Hooks, Suspense, and Concurrent Rendering", + "author": ["tomocchino"], + "excerpt": "

This year’s React Conf took place on October 25 and 26 in Henderson, Nevada, where more than 600 attendees gathered to discuss the latest in UI engineering.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2018/10/23/react-v-16-6.md", + "date": "2018-10-23", + "title": "React v16.6.0: lazy, memo and contextType", + "author": ["sebmarkbage"], + "excerpt": "

Today we’re releasing React 16.6 with a few new convenient features. A form of PureComponent/shouldComponentUpdate for function components, a way to do code splitting using Suspense and an easier way to consume Context from class components.

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2018/10/01/create-react-app-v2.md", + "date": "2018-10-01", + "title": "Create React App 2.0: Babel 7, Sass, and More", + "author": ["timer", "gaearon"], + "excerpt": "

Create React App 2.0 has been released today, and it brings a year’s worth of improvements in a single dependency update.

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2018/09/10/introducing-the-react-profiler.md", + "date": "2018-09-10", + "title": "Introducing the React Profiler", + "author": ["bvaughn"], + "excerpt": "

React 16.5 adds support for a new DevTools profiler plugin.

\n", + "readingTime": "8 min read" + }, + { + "path": "/blog/2018/08/01/react-v-16-4-2.md", + "date": "2018-08-01", + "title": "React v16.4.2: Server-side vulnerability fix", + "author": ["gaearon"], + "excerpt": "

We discovered a minor vulnerability that might affect some apps using ReactDOMServer. We are releasing a patch version for every affected React minor release so that you can upgrade with no friction. Read on for more details.

\n", + "readingTime": "4 min read" + }, + { + "path": "/blog/2018/06/07/you-probably-dont-need-derived-state.md", + "date": "2018-06-07", + "title": "You Probably Don't Need Derived State", + "author": ["bvaughn"], + "excerpt": "

React 16.4 included a bugfix for getDerivedStateFromProps which caused some existing bugs in React components to reproduce more consistently. If this release exposed a case where your application was using an anti-pattern and didn’t work properly after the fix, we’re sorry for the churn. In this post, we will explain some common anti-patterns with derived state and our preferred alternatives.

\n", + "readingTime": "14 min read" + }, + { + "path": "/blog/2018/05/23/react-v-16-4.md", + "date": "2018-05-23", + "title": "React v16.4.0: Pointer Events", + "author": ["acdlite"], + "excerpt": "

The latest minor release adds support for an oft-requested feature: pointer events!

\n", + "readingTime": "5 min read" + }, + { + "path": "/blog/2018/03/29/react-v-16-3.md", + "date": "2018-03-29", + "title": "React v16.3.0: New lifecycles and context API", + "author": ["bvaughn"], + "excerpt": "

A few days ago, we wrote a post about upcoming changes to our legacy lifecycle methods, including gradual migration strategies. In React 16.3.0, we are adding a few new lifecycle methods to assist with that migration. We are also introducing new APIs for long requested features: an official context API, a ref forwarding API, and an ergonomic ref API.

\n", + "readingTime": "6 min read" + }, + { + "path": "/blog/2018/03/27/update-on-async-rendering.md", + "date": "2018-03-27", + "title": "Update on Async Rendering", + "author": ["bvaughn"], + "excerpt": "

For over a year, the React team has been working to implement asynchronous rendering. Last month during his talk at JSConf Iceland, Dan unveiled some of the exciting new possibilities async rendering unlocks. Now we’d like to share with you some of the lessons we’ve learned while working on these features, and some recipes to help prepare your components for async rendering when it launches.

\n", + "readingTime": "12 min read" + }, + { + "path": "/blog/2018/03/01/sneak-peek-beyond-react-16.md", + "date": "2018-03-01", + "title": "Sneak Peek: Beyond React 16", + "author": ["sophiebits"], + "excerpt": "

Dan Abramov from our team just spoke at JSConf Iceland 2018 with a preview of some new features we’ve been working on in React. The talk opens with a question: “With vast differences in computing power and network speed, how do we deliver the best user experience for everyone?”

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2017/12/15/improving-the-repository-infrastructure.md", + "date": "2017-12-15", + "title": "Behind the Scenes: Improving the Repository Infrastructure", + "author": ["gaearon", "bvaughn"], + "excerpt": "

As we worked on React 16, we revamped the folder structure and much of the build tooling in the React repository. Among other things, we introduced projects such as Rollup, Prettier, and Google Closure Compiler into our workflow. People often ask us questions about how we use those tools. In this post, we would like to share some of the changes that we’ve made to our build and test infrastructure in 2017, and what motivated them.

\n", + "readingTime": "30 min read" + }, + { + "path": "/blog/2017/12/07/introducing-the-react-rfc-process.md", + "date": "2017-12-07", + "title": "Introducing the React RFC Process", + "author": ["acdlite"], + "excerpt": "

We’re adopting an RFC (“request for comments”) process for contributing ideas to React.

\n", + "readingTime": "2 min read" + }, + { + "path": "/blog/2017/11/28/react-v16.2.0-fragment-support.md", + "date": "2017-11-28", + "title": "React v16.2.0: Improved Support for Fragments", + "author": ["clemmy"], + "excerpt": "

React 16.2 is now available! The biggest addition is improved support for returning multiple children from a component’s render method. We call this feature fragments:

\n", + "readingTime": "8 min read" + }, + { + "path": "/blog/2017/09/26/react-v16.0.md", + "date": "2017-09-26", + "title": "React v16.0", + "author": ["acdlite"], + "excerpt": "

We’re excited to announce the release of React v16.0! Among the changes are some long-standing feature requests, including fragments, error boundaries, portals, support for custom DOM attributes, improved server-side rendering, and reduced file size.

\n", + "readingTime": "11 min read" + }, + { + "path": "/blog/2017/09/25/react-v15.6.2.md", + "date": "2017-09-25", + "title": "React v15.6.2", + "author": ["nhunzaker"], + "excerpt": "

Today we’re sending out React 15.6.2. In 15.6.1, we shipped a few fixes for change events and inputs that had some unintended consequences. Those regressions have been ironed out, and we’ve also included a few more fixes to improve the stability of React across all browsers.

\n", + "readingTime": "3 min read" + } + ] +} diff --git a/beta/src/components/Breadcrumbs.tsx b/beta/src/components/Breadcrumbs.tsx new file mode 100644 index 000000000..bc8078bf9 --- /dev/null +++ b/beta/src/components/Breadcrumbs.tsx @@ -0,0 +1,45 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import {useRouteMeta} from 'components/Layout/useRouteMeta'; +import Link from 'next/link'; + +function Breadcrumbs() { + const {breadcrumbs} = useRouteMeta(); + if (!breadcrumbs) return null; + return ( +
+ {breadcrumbs.map( + (crumb, i) => + crumb.path && ( +
+ + + + {crumb.title} + + + + + + + + +
+ ) + )} +
+ ); +} + +export default Breadcrumbs; diff --git a/beta/src/components/Button.tsx b/beta/src/components/Button.tsx new file mode 100644 index 000000000..9d4d0cf2b --- /dev/null +++ b/beta/src/components/Button.tsx @@ -0,0 +1,51 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; + +interface ButtonProps { + children: React.ReactNode; + onClick?: (event: React.MouseEvent) => void; + active: boolean; + className?: string; + style?: Record; +} + +export function Button({ + children, + onClick, + active, + className, + style, +}: ButtonProps) { + return ( + + ); +} + +Button.defaultProps = { + active: false, + style: {}, +}; + +export default Button; diff --git a/beta/src/components/ButtonLink.tsx b/beta/src/components/ButtonLink.tsx new file mode 100644 index 000000000..964056ed7 --- /dev/null +++ b/beta/src/components/ButtonLink.tsx @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; +import NextLink from 'next/link'; + +interface ButtonLinkProps { + size?: 'md' | 'lg'; + type?: 'primary' | 'secondary'; + label?: string; + target?: '_self' | '_blank'; +} + +function ButtonLink({ + href, + className, + children, + type = 'primary', + size = 'md', + label, + target = '_self', + ...props +}: JSX.IntrinsicElements['a'] & ButtonLinkProps) { + const classes = cn( + className, + 'inline-flex font-bold items-center border-2 border-transparent outline-none focus:ring-1 focus:ring-offset-2 focus:ring-link active:bg-link active:text-white active:ring-0 active:ring-offset-0 leading-normal', + { + 'bg-link text-white hover:bg-opacity-80': type === 'primary', + 'bg-secondary-button dark:bg-secondary-button-dark text-primary dark:text-primary-dark hover:text-link focus:bg-link focus:text-white focus:border-link focus:border-2': + type === 'secondary', + 'text-lg rounded-lg p-4': size === 'lg', + 'text-base rounded-lg px-4 py-1.5': size === 'md', + } + ); + return ( + + + {children} + + + ); +} + +export default ButtonLink; diff --git a/beta/src/components/DocsFooter.tsx b/beta/src/components/DocsFooter.tsx new file mode 100644 index 000000000..c2ab02255 --- /dev/null +++ b/beta/src/components/DocsFooter.tsx @@ -0,0 +1,93 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import NextLink from 'next/link'; +import * as React from 'react'; +import cn from 'classnames'; +import {removeFromLast} from 'utils/removeFromLast'; +import {IconNavArrow} from './Icon/IconNavArrow'; +import {RouteMeta} from './Layout/useRouteMeta'; + +export type DocsPageFooterProps = Pick< + RouteMeta, + 'route' | 'nextRoute' | 'prevRoute' +>; + +function areEqual(prevProps: DocsPageFooterProps, props: DocsPageFooterProps) { + return prevProps.route?.path === props.route?.path; +} + +export const DocsPageFooter = React.memo( + function DocsPageFooter({nextRoute, prevRoute, route}) { + if (!route || route?.heading) { + return null; + } + + return ( + <> + {prevRoute?.path || nextRoute?.path ? ( + <> +
+ {prevRoute?.path ? ( + + ) : ( +
+ )} + + {nextRoute?.path ? ( + + ) : ( +
+ )} +
+ + ) : null} + + ); + }, + areEqual +); + +function FooterLink({ + href, + title, + type, +}: { + href: string; + title: string; + type: 'Previous' | 'Next'; +}) { + return ( + + + + + + {type} + + {title} + + + + ); +} + +DocsPageFooter.displayName = 'DocsPageFooter'; diff --git a/beta/src/components/ExternalLink.tsx b/beta/src/components/ExternalLink.tsx new file mode 100644 index 000000000..4ab7831da --- /dev/null +++ b/beta/src/components/ExternalLink.tsx @@ -0,0 +1,20 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export function ExternalLink({ + href, + target, + children, + ...props +}: JSX.IntrinsicElements['a']) { + return ( + + {children} + + ); +} + +ExternalLink.displayName = 'ExternalLink'; diff --git a/beta/src/components/Icon/IconArrow.tsx b/beta/src/components/Icon/IconArrow.tsx new file mode 100644 index 000000000..942f8a2db --- /dev/null +++ b/beta/src/components/Icon/IconArrow.tsx @@ -0,0 +1,30 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; + +export const IconArrow = React.memo< + JSX.IntrinsicElements['svg'] & { + displayDirection: 'left' | 'right' | 'up' | 'down'; + } +>(function IconArrow({displayDirection, className, ...rest}) { + return ( + + + + + ); +}); + +IconArrow.displayName = 'IconArrow'; diff --git a/beta/src/components/Icon/IconArrowSmall.tsx b/beta/src/components/Icon/IconArrowSmall.tsx new file mode 100644 index 000000000..ed838882e --- /dev/null +++ b/beta/src/components/Icon/IconArrowSmall.tsx @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; + +export const IconArrowSmall = React.memo< + JSX.IntrinsicElements['svg'] & { + displayDirection: 'left' | 'right' | 'up' | 'down'; + } +>(function IconArrowSmall({displayDirection, className, ...rest}) { + const classes = cn(className, { + 'rotate-180': displayDirection === 'left', + 'rotate-90': displayDirection === 'down', + }); + return ( + + + + ); +}); + +IconArrowSmall.displayName = 'IconArrowSmall'; diff --git a/beta/src/components/Icon/IconChevron.tsx b/beta/src/components/Icon/IconChevron.tsx new file mode 100644 index 000000000..53416a0e5 --- /dev/null +++ b/beta/src/components/Icon/IconChevron.tsx @@ -0,0 +1,42 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; + +export const IconChevron = React.memo< + JSX.IntrinsicElements['svg'] & { + displayDirection: 'up' | 'down' | 'left' | 'right'; + } +>(function IconChevron({className, displayDirection}) { + const classes = cn( + { + 'rotate-0': displayDirection === 'down', + 'rotate-90': displayDirection === 'left', + 'rotate-180': displayDirection === 'up', + '-rotate-90': displayDirection === 'right', + }, + className + ); + return ( + + + + + + + ); +}); + +IconChevron.displayName = 'IconChevron'; diff --git a/beta/src/components/Icon/IconClose.tsx b/beta/src/components/Icon/IconClose.tsx new file mode 100644 index 000000000..c31090302 --- /dev/null +++ b/beta/src/components/Icon/IconClose.tsx @@ -0,0 +1,28 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconClose = React.memo( + function IconClose(props) { + return ( + + + + + ); + } +); + +IconClose.displayName = 'IconClose'; diff --git a/beta/src/components/Icon/IconCodeBlock.tsx b/beta/src/components/Icon/IconCodeBlock.tsx new file mode 100644 index 000000000..9bb678c4b --- /dev/null +++ b/beta/src/components/Icon/IconCodeBlock.tsx @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconCodeBlock = React.memo( + function IconCodeBlock({className}) { + return ( + + + + ); + } +); + +IconCodeBlock.displayName = 'IconCodeBlock'; diff --git a/beta/src/components/Icon/IconCopy.tsx b/beta/src/components/Icon/IconCopy.tsx new file mode 100644 index 000000000..552b30c6f --- /dev/null +++ b/beta/src/components/Icon/IconCopy.tsx @@ -0,0 +1,34 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconCopy = React.memo( + function IconCopy({className}) { + return ( + + {' '} + + + ); + } +); + +IconCopy.displayName = 'IconCopy'; diff --git a/beta/src/components/Icon/IconDeepDive.tsx b/beta/src/components/Icon/IconDeepDive.tsx new file mode 100644 index 000000000..c68635350 --- /dev/null +++ b/beta/src/components/Icon/IconDeepDive.tsx @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconDeepDive = React.memo( + function IconDeepDive({className}) { + return ( + + + + ); + } +); + +IconDeepDive.displayName = 'IconDeepDive'; diff --git a/beta/src/components/Icon/IconError.tsx b/beta/src/components/Icon/IconError.tsx new file mode 100644 index 000000000..71c4a7c9b --- /dev/null +++ b/beta/src/components/Icon/IconError.tsx @@ -0,0 +1,24 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconError = React.memo( + function IconError({className}) { + return ( + + + + + + ); + } +); + +IconError.displayName = 'IconError'; diff --git a/beta/src/components/Icon/IconFacebookCircle.tsx b/beta/src/components/Icon/IconFacebookCircle.tsx new file mode 100644 index 000000000..d04017b36 --- /dev/null +++ b/beta/src/components/Icon/IconFacebookCircle.tsx @@ -0,0 +1,22 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconFacebookCircle = React.memo( + function IconFacebookCircle(props) { + return ( + + + + + ); + } +); diff --git a/beta/src/components/Icon/IconGitHub.tsx b/beta/src/components/Icon/IconGitHub.tsx new file mode 100644 index 000000000..dee377e7c --- /dev/null +++ b/beta/src/components/Icon/IconGitHub.tsx @@ -0,0 +1,22 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconGitHub = React.memo( + function IconGitHub(props) { + return ( + + + + ); + } +); + +IconGitHub.displayName = 'IconGitHub'; diff --git a/beta/src/components/Icon/IconGotcha.tsx b/beta/src/components/Icon/IconGotcha.tsx new file mode 100644 index 000000000..8342cbb6f --- /dev/null +++ b/beta/src/components/Icon/IconGotcha.tsx @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconGotcha = React.memo( + function IconGotcha({className}) { + return ( + + + + ); + } +); + +IconGotcha.displayName = 'IconGotcha'; diff --git a/beta/src/components/Icon/IconHamburger.tsx b/beta/src/components/Icon/IconHamburger.tsx new file mode 100644 index 000000000..c4653d589 --- /dev/null +++ b/beta/src/components/Icon/IconHamburger.tsx @@ -0,0 +1,28 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconHamburger = React.memo( + function IconHamburger(props) { + return ( + + + + + + ); + } +); + +IconHamburger.displayName = 'IconHamburger'; diff --git a/beta/src/components/Icon/IconHint.tsx b/beta/src/components/Icon/IconHint.tsx new file mode 100644 index 000000000..d27b7e119 --- /dev/null +++ b/beta/src/components/Icon/IconHint.tsx @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; + +export const IconHint = React.memo( + function IconHint({className}) { + return ( + + + + ); + } +); + +IconHint.displayName = 'IconHint'; diff --git a/beta/src/components/Icon/IconInstagram.tsx b/beta/src/components/Icon/IconInstagram.tsx new file mode 100644 index 000000000..cda96a674 --- /dev/null +++ b/beta/src/components/Icon/IconInstagram.tsx @@ -0,0 +1,22 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconInstagram = React.memo( + function IconInstagram(props) { + return ( + + + + + ); + } +); diff --git a/beta/src/components/Icon/IconNavArrow.tsx b/beta/src/components/Icon/IconNavArrow.tsx new file mode 100644 index 000000000..4531b3c0f --- /dev/null +++ b/beta/src/components/Icon/IconNavArrow.tsx @@ -0,0 +1,44 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; + +export const IconNavArrow = React.memo< + JSX.IntrinsicElements['svg'] & { + displayDirection: 'right' | 'down' | 'left'; + } +>(function IconNavArrow({displayDirection = 'right', className}) { + const classes = cn( + 'duration-100 ease-in transition', + { + 'rotate-0': displayDirection === 'down', + '-rotate-90': displayDirection === 'right', + 'rotate-90': displayDirection === 'left', + }, + className + ); + + return ( + + + + + + + ); +}); + +IconNavArrow.displayName = 'IconNavArrow'; diff --git a/beta/src/components/Icon/IconNewPage.tsx b/beta/src/components/Icon/IconNewPage.tsx new file mode 100644 index 000000000..d13b5069f --- /dev/null +++ b/beta/src/components/Icon/IconNewPage.tsx @@ -0,0 +1,25 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconNewPage = React.memo( + function IconNewPage(props) { + return ( + + + + ); + } +); + +IconNewPage.displayName = 'IconNewPage'; diff --git a/beta/src/components/Icon/IconNote.tsx b/beta/src/components/Icon/IconNote.tsx new file mode 100644 index 000000000..7ddf92525 --- /dev/null +++ b/beta/src/components/Icon/IconNote.tsx @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconNote = React.memo( + function IconNote({className}) { + return ( + + + + ); + } +); + +IconNote.displayName = 'IconNote'; diff --git a/beta/src/components/Icon/IconRestart.tsx b/beta/src/components/Icon/IconRestart.tsx new file mode 100644 index 000000000..2e52fe6c2 --- /dev/null +++ b/beta/src/components/Icon/IconRestart.tsx @@ -0,0 +1,25 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconRestart = React.memo( + function IconRestart({className}) { + return ( + + + + ); + } +); + +IconRestart.displayName = 'IconRestart'; diff --git a/beta/src/components/Icon/IconRss.tsx b/beta/src/components/Icon/IconRss.tsx new file mode 100644 index 000000000..4cd10de44 --- /dev/null +++ b/beta/src/components/Icon/IconRss.tsx @@ -0,0 +1,29 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconRss = React.memo( + function IconRss(props) { + return ( + + + + + + ); + } +); + +IconRss.displayName = 'IconLogo'; diff --git a/beta/src/components/Icon/IconSearch.tsx b/beta/src/components/Icon/IconSearch.tsx new file mode 100644 index 000000000..a13fbfad1 --- /dev/null +++ b/beta/src/components/Icon/IconSearch.tsx @@ -0,0 +1,24 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconSearch = React.memo( + function IconSearch(props) { + return ( + + + + ); + } +); + +IconSearch.displayName = 'IconSearch'; diff --git a/beta/src/components/Icon/IconSolution.tsx b/beta/src/components/Icon/IconSolution.tsx new file mode 100644 index 000000000..bdc09726e --- /dev/null +++ b/beta/src/components/Icon/IconSolution.tsx @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; + +export const IconSolution = React.memo( + function IconSolution({className}) { + return ( + + + + ); + } +); + +IconSolution.displayName = 'IconSolution'; diff --git a/beta/src/components/Icon/IconTerminal.tsx b/beta/src/components/Icon/IconTerminal.tsx new file mode 100644 index 000000000..8f31199e4 --- /dev/null +++ b/beta/src/components/Icon/IconTerminal.tsx @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconTerminal = React.memo( + function IconTerminal({className}) { + return ( + + + + ); + } +); + +IconTerminal.displayName = 'IconTerminal'; diff --git a/beta/src/components/Icon/IconTwitter.tsx b/beta/src/components/Icon/IconTwitter.tsx new file mode 100644 index 000000000..1e113c9eb --- /dev/null +++ b/beta/src/components/Icon/IconTwitter.tsx @@ -0,0 +1,24 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconTwitter = React.memo( + function IconTwitter(props) { + return ( + + + + + ); + } +); + +IconTwitter.displayName = 'IconTwitter'; diff --git a/beta/src/components/Icon/IconWarning.tsx b/beta/src/components/Icon/IconWarning.tsx new file mode 100644 index 000000000..09a4e03e7 --- /dev/null +++ b/beta/src/components/Icon/IconWarning.tsx @@ -0,0 +1,28 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export const IconWarning = React.memo( + function IconWarning({className}) { + return ( + + + + + + + ); + } +); + +IconWarning.displayName = 'IconWarning'; diff --git a/beta/src/components/Layout/Feedback.tsx b/beta/src/components/Layout/Feedback.tsx new file mode 100644 index 000000000..2a46cb0a6 --- /dev/null +++ b/beta/src/components/Layout/Feedback.tsx @@ -0,0 +1,94 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import {useRouter} from 'next/router'; +// @ts-ignore +import galite from 'ga-lite'; + +export function Feedback({onSubmit = () => {}}: {onSubmit?: () => void}) { + const {pathname} = useRouter(); + // Reset on route changes. + return ; +} + +const thumbsUpIcon = ( + + + +); + +const thumbsDownIcon = ( + + + +); + +function sendGAEvent(isPositive: boolean) { + // Fragile. Don't change unless you've tested the network payload + // and verified that the right events actually show up in GA. + galite( + 'send', + 'event', + 'button', + 'feedback', + window.location.pathname, + isPositive ? '1' : '0' + ); +} + +function SendFeedback({onSubmit}: {onSubmit: () => void}) { + const [isSubmitted, setIsSubmitted] = React.useState(false); + return ( +
+

+ {isSubmitted ? 'Thank you for your feedback!' : 'Is this page useful?'} +

+ {!isSubmitted && ( + + )} + {!isSubmitted && ( + + )} +
+ ); +} diff --git a/beta/src/components/Layout/Footer.tsx b/beta/src/components/Layout/Footer.tsx new file mode 100644 index 000000000..5133dc79c --- /dev/null +++ b/beta/src/components/Layout/Footer.tsx @@ -0,0 +1,185 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import NextLink from 'next/link'; +import cn from 'classnames'; +import {ExternalLink} from 'components/ExternalLink'; +import {IconFacebookCircle} from 'components/Icon/IconFacebookCircle'; +import {IconTwitter} from 'components/Icon/IconTwitter'; + +export function Footer() { + const socialLinkClasses = 'hover:text-primary dark:text-primary-dark'; + return ( + <> +
+
+
+
+
+
+ +
+ + + + + + + + + + + Open Source +
+
+ ©{new Date().getFullYear()} +
+
+
+ + Learn React + + Quick Start + Installation + + Describing the UI + + + Adding Interactivity + + + Managing State + + + Escape Hatches + +
+
+ + API Reference + + React APIs + React DOM APIs +
+
+ + Community + + + Code of Conduct + + + Acknowledgements + + + Meet the Team + + {/* Community Resources */} +
+
+ More + {/* Tutorial */} + {/* Blog */} + {/* Acknowledgements */} + + React Native + + + Privacy + + + Terms + +
+ + + + + + +
+
+
+
+
+ + ); +} + +function FooterLink({ + href, + children, + isHeader = false, +}: { + href?: string; + children: React.ReactNode; + isHeader?: boolean; +}) { + const classes = cn('border-b inline-block border-transparent', { + 'text-sm text-primary dark:text-primary-dark': !isHeader, + 'text-md text-secondary dark:text-secondary-dark my-2 font-bold': isHeader, + 'hover:border-gray-10': href, + }); + + if (!href) { + return
{children}
; + } + + if (href.startsWith('https://')) { + return ( +
+ + {children} + +
+ ); + } + + return ( +
+ + {children} + +
+ ); +} diff --git a/beta/src/components/Layout/LayoutAPI.tsx b/beta/src/components/Layout/LayoutAPI.tsx new file mode 100644 index 000000000..ceebaa322 --- /dev/null +++ b/beta/src/components/Layout/LayoutAPI.tsx @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import sidebarReference from 'sidebarReference.json'; +import {MarkdownPage, MarkdownProps} from './MarkdownPage'; +import {Page} from './Page'; +import {RouteItem} from './useRouteMeta'; + +interface PageFrontmatter { + title: string; + status: string; +} + +export default function withAPI(p: PageFrontmatter) { + function LayoutAPI(props: MarkdownProps) { + return ; + } + LayoutAPI.appShell = AppShell; + return LayoutAPI; +} + +function AppShell(props: {children: React.ReactNode}) { + return ; +} diff --git a/beta/src/components/Layout/LayoutHome.tsx b/beta/src/components/Layout/LayoutHome.tsx new file mode 100644 index 000000000..162ad7e5c --- /dev/null +++ b/beta/src/components/Layout/LayoutHome.tsx @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import sidebarHome from 'sidebarHome.json'; +import {MarkdownPage, MarkdownProps} from './MarkdownPage'; +import {Page} from './Page'; +import {RouteItem} from './useRouteMeta'; + +interface PageFrontmatter { + title: string; + status: string; +} + +export default function withDocs(p: PageFrontmatter) { + function LayoutHome(props: MarkdownProps) { + return ; + } + LayoutHome.appShell = AppShell; + return LayoutHome; +} + +function AppShell(props: {children: React.ReactNode}) { + return ; +} diff --git a/beta/src/components/Layout/LayoutLearn.tsx b/beta/src/components/Layout/LayoutLearn.tsx new file mode 100644 index 000000000..9441b49d0 --- /dev/null +++ b/beta/src/components/Layout/LayoutLearn.tsx @@ -0,0 +1,24 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import {MarkdownPage, MarkdownProps} from './MarkdownPage'; +import {RouteItem} from 'components/Layout/useRouteMeta'; +import {Page} from './Page'; +import sidebarLearn from '../../sidebarLearn.json'; +interface PageFrontmatter { + title: string; +} + +export default function withLearn(meta: PageFrontmatter) { + function LayoutLearn(props: MarkdownProps) { + return ; + } + LayoutLearn.appShell = AppShell; + return LayoutLearn; +} + +function AppShell(props: {children: React.ReactNode}) { + return ; +} diff --git a/beta/src/components/Layout/LayoutPost.tsx b/beta/src/components/Layout/LayoutPost.tsx new file mode 100644 index 000000000..789c75437 --- /dev/null +++ b/beta/src/components/Layout/LayoutPost.tsx @@ -0,0 +1,120 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +// @ts-ignore +import {MDXContext} from '@mdx-js/react'; +import recentPostsRouteTree from 'blogIndexRecent.json'; +import {DocsPageFooter} from 'components/DocsFooter'; +import {ExternalLink} from 'components/ExternalLink'; +import {MDXComponents} from 'components/MDX/MDXComponents'; +import {Seo} from 'components/Seo'; +import {Toc} from 'components/Layout/Toc'; +import format from 'date-fns/format'; +import {useRouter} from 'next/router'; +import * as React from 'react'; +import {getAuthor} from 'utils/getAuthor'; +import toCommaSeparatedList from 'utils/toCommaSeparatedList'; +import {Page} from './Page'; +import {RouteItem, useRouteMeta} from './useRouteMeta'; +import {useTwitter} from './useTwitter'; + +interface PageFrontmatter { + id?: string; + title: string; + author: string[]; + date?: string; +} + +interface LayoutPostProps { + /** Sidebar/Nav */ + routes: RouteItem[]; + /** Markdown frontmatter */ + meta: PageFrontmatter; + /** The mdx */ + children: React.ReactNode; +} + +/** Return the date of the current post given the path */ +function getDateFromPath(path: string) { + // All paths are /blog/year/month/day/title + const [year, month, day] = path + .substr(1) // first `/` + .split('/') // make an array + .slice(1) // ignore blog + .map((i) => parseInt(i, 10)); // convert to numbers + + return { + date: format(new Date(year, month, day), 'MMMM dd, yyyy'), + dateTime: [year, month, day].join('-'), + }; +} + +function LayoutPost({meta, children}: LayoutPostProps) { + const {pathname} = useRouter(); + const {date, dateTime} = getDateFromPath(pathname); + const {route, nextRoute, prevRoute} = useRouteMeta(); + const anchors = React.Children.toArray(children) + .filter( + (child: any) => + child.props?.mdxType && ['h2', 'h3'].includes(child.props.mdxType) + ) + .map((child: any) => ({ + url: '#' + child.props.id, + depth: parseInt(child.props.mdxType.replace('h', ''), 0), + text: child.props.children, + })); + useTwitter(); + return ( + <> +
+
+ +

+ {meta.title} +

+

+ By{' '} + {toCommaSeparatedList(meta.author, (author) => ( + + {getAuthor(author).name} + + ))} + · + + + +

+ + + {children} + + +
+
+
+ +
+ + ); +} + +function AppShell(props: {children: React.ReactNode}) { + return ; +} + +export default function withLayoutPost(meta: any) { + function LayoutPostWrapper(props: LayoutPostProps) { + return ; + } + + LayoutPostWrapper.appShell = AppShell; + + return LayoutPostWrapper; +} diff --git a/beta/src/components/Layout/MarkdownPage.tsx b/beta/src/components/Layout/MarkdownPage.tsx new file mode 100644 index 000000000..746efe992 --- /dev/null +++ b/beta/src/components/Layout/MarkdownPage.tsx @@ -0,0 +1,147 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +// @ts-ignore +import {MDXContext} from '@mdx-js/react'; +import {DocsPageFooter} from 'components/DocsFooter'; +import {MDXComponents} from 'components/MDX/MDXComponents'; +import {Seo} from 'components/Seo'; +import PageHeading from 'components/PageHeading'; +import {useRouteMeta} from './useRouteMeta'; +import {Toc} from './Toc'; +export interface MarkdownProps { + meta: Frontmatter & {description?: string}; + children?: React.ReactNode; +} + +function MaxWidth({children}: {children: any}) { + return
{children}
; +} + +export function MarkdownPage< + T extends {title: string; status?: string} = {title: string; status?: string} +>({children, meta}: MarkdownProps) { + const {route, nextRoute, prevRoute} = useRouteMeta(); + const title = meta.title || route?.title || ''; + const description = meta.description || route?.description || ''; + + let anchors: Array<{ + url: string; + text: React.ReactNode; + depth: number; + }> = React.Children.toArray(children) + .filter((child: any) => { + if (child.props?.mdxType) { + return ['h1', 'h2', 'h3', 'Challenges', 'Recap'].includes( + child.props.mdxType + ); + } + return false; + }) + .map((child: any) => { + if (child.props.mdxType === 'Challenges') { + return { + url: '#challenges', + depth: 0, + text: 'Challenges', + }; + } + if (child.props.mdxType === 'Recap') { + return { + url: '#recap', + depth: 0, + text: 'Recap', + }; + } + return { + url: '#' + child.props.id, + depth: + (child.props?.mdxType && + parseInt(child.props.mdxType.replace('h', ''), 0)) ?? + 0, + text: child.props.children, + }; + }); + if (anchors.length > 0) { + anchors.unshift({ + depth: 1, + text: 'Overview', + url: '#', + }); + } + + if (!route) { + console.error('This page was not added to one of the sidebar JSON files.'); + } + const isHomePage = route?.path === '/'; + + // Auto-wrap everything except a few types into + // wrappers. Keep reusing the same + // wrapper as long as we can until we meet + // a full-width section which interrupts it. + let fullWidthTypes = [ + 'Sandpack', + 'FullWidth', + 'Illustration', + 'IllustrationBlock', + 'Challenges', + 'Recipes', + ]; + let wrapQueue: React.ReactNode[] = []; + let finalChildren: React.ReactNode[] = []; + function flushWrapper(key: string | number) { + if (wrapQueue.length > 0) { + finalChildren.push({wrapQueue}); + wrapQueue = []; + } + } + function handleChild(child: any, key: string | number) { + if (child == null) { + return; + } + if (typeof child !== 'object') { + wrapQueue.push(child); + return; + } + if (fullWidthTypes.includes(child.props.mdxType)) { + flushWrapper(key); + finalChildren.push(child); + } else { + wrapQueue.push(child); + } + } + React.Children.forEach(children, handleChild); + flushWrapper('last'); + + return ( +
+
+ + {!isHomePage && ( + + )} +
+
+ + {finalChildren} + +
+ +
+
+
+ {!isHomePage && anchors.length > 0 && } +
+
+ ); +} diff --git a/beta/src/components/Layout/Nav/MobileNav.tsx b/beta/src/components/Layout/Nav/MobileNav.tsx new file mode 100644 index 000000000..fe96dffc0 --- /dev/null +++ b/beta/src/components/Layout/Nav/MobileNav.tsx @@ -0,0 +1,87 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; +import {RouteItem} from 'components/Layout/useRouteMeta'; +import {useRouter} from 'next/router'; +import {SidebarRouteTree} from '../Sidebar'; +import sidebarHome from '../../../sidebarHome.json'; +import sidebarLearn from '../../../sidebarLearn.json'; +import sidebarReference from '../../../sidebarReference.json'; + +function inferSection(pathname: string): 'learn' | 'apis' | 'home' { + if (pathname.startsWith('/learn')) { + return 'learn'; + } else if (pathname.startsWith('/apis')) { + return 'apis'; + } else { + return 'home'; + } +} + +export function MobileNav() { + const {pathname} = useRouter(); + const [section, setSection] = React.useState(() => inferSection(pathname)); + + let tree = null; + switch (section) { + case 'home': + tree = sidebarHome.routes[0]; + break; + case 'learn': + tree = sidebarLearn.routes[0]; + break; + case 'apis': + tree = sidebarReference.routes[0]; + break; + } + + return ( + <> +
+ setSection('home')}> + Home + + setSection('learn')}> + Learn + + setSection('apis')}> + API + +
+ + + ); +} + +function TabButton({ + children, + onClick, + isActive, +}: { + children: any; + onClick: (event: React.MouseEvent) => void; + isActive: boolean; +}) { + const classes = cn( + 'inline-flex items-center w-full border-b-2 justify-center text-base leading-9 px-3 py-0.5 hover:text-link hover:gray-5', + { + 'text-link dark:text-link-dark dark:border-link-dark border-link font-bold': + isActive, + 'border-transparent': !isActive, + } + ); + return ( + + ); +} diff --git a/beta/src/components/Layout/Nav/Nav.tsx b/beta/src/components/Layout/Nav/Nav.tsx new file mode 100644 index 000000000..a5099cc16 --- /dev/null +++ b/beta/src/components/Layout/Nav/Nav.tsx @@ -0,0 +1,243 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; +import NextLink from 'next/link'; +import {useRouter} from 'next/router'; + +import {IconClose} from 'components/Icon/IconClose'; +import {IconHamburger} from 'components/Icon/IconHamburger'; +import {Search} from 'components/Search'; +import {MenuContext} from 'components/useMenu'; + +import {Logo} from '../../Logo'; +import {Feedback} from '../Feedback'; +import NavLink from './NavLink'; + +declare global { + interface Window { + __theme: string; + __setPreferredTheme: (theme: string) => void; + } +} + +const feedbackIcon = ( + + + + +); + +const darkIcon = ( + + + + + + +); + +const lightIcon = ( + + + + + + + + + +); + +function inferSection(pathname: string): 'learn' | 'apis' | 'home' { + if (pathname.startsWith('/learn')) { + return 'learn'; + } else if (pathname.startsWith('/apis')) { + return 'apis'; + } else { + return 'home'; + } +} + +export default function Nav() { + const {pathname} = useRouter(); + const {isOpen, toggleOpen} = React.useContext(MenuContext); + const [showFeedback, setShowFeedback] = React.useState(false); + const feedbackAutohideRef = React.useRef(null); + const section = inferSection(pathname); + const feedbackPopupRef = React.useRef(null); + + React.useEffect(() => { + if (!showFeedback) { + return; + } + function handleDocumentClickCapture(e: MouseEvent) { + if (!feedbackPopupRef.current!.contains(e.target as any)) { + e.stopPropagation(); + e.preventDefault(); + setShowFeedback(false); + } + } + document.addEventListener('click', handleDocumentClickCapture, { + capture: true, + }); + return () => + document.removeEventListener('click', handleDocumentClickCapture, { + capture: true, + }); + }, [showFeedback]); + + function handleFeedback() { + clearTimeout(feedbackAutohideRef.current); + setShowFeedback(!showFeedback); + } + + return ( + + ); +} diff --git a/beta/src/components/Layout/Nav/NavLink.tsx b/beta/src/components/Layout/Nav/NavLink.tsx new file mode 100644 index 000000000..3fddc6e16 --- /dev/null +++ b/beta/src/components/Layout/Nav/NavLink.tsx @@ -0,0 +1,39 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; +import {ExternalLink} from 'components/ExternalLink'; +import NextLink from 'next/link'; + +interface NavLinkProps { + href: string; + children: React.ReactNode; + isActive: boolean; +} + +export default function NavLink({href, children, isActive}: NavLinkProps) { + const classes = cn( + { + 'text-link border-link dark:text-link-dark dark:border-link-dark font-bold': + isActive, + }, + {'border-transparent': !isActive}, + 'inline-flex w-full items-center border-b-2 justify-center text-base leading-9 px-3 py-0.5 hover:text-link dark:hover:text-link-dark whitespace-nowrap' + ); + + if (href.startsWith('https://')) { + return ( + + {children} + + ); + } + + return ( + + {children} + + ); +} diff --git a/beta/src/components/Layout/Nav/index.tsx b/beta/src/components/Layout/Nav/index.tsx new file mode 100644 index 000000000..0f4d0e78e --- /dev/null +++ b/beta/src/components/Layout/Nav/index.tsx @@ -0,0 +1,5 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +export {default as Nav} from './Nav'; diff --git a/beta/src/components/Layout/Page.tsx b/beta/src/components/Layout/Page.tsx new file mode 100644 index 000000000..e9481b104 --- /dev/null +++ b/beta/src/components/Layout/Page.tsx @@ -0,0 +1,42 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import {MenuProvider} from 'components/useMenu'; +import * as React from 'react'; +import {Nav} from './Nav'; +import {RouteItem, SidebarContext} from './useRouteMeta'; +import {Sidebar} from './Sidebar'; +import {Footer} from './Footer'; +import SocialBanner from '../SocialBanner'; +interface PageProps { + children: React.ReactNode; + routeTree: RouteItem; +} + +export function Page({routeTree, children}: PageProps) { + return ( + <> + + + +
+
+
+ +
+
+
+ {children} +
+
+
+
+
+
+
+ + ); +} diff --git a/beta/src/components/Layout/Sidebar/Sidebar.tsx b/beta/src/components/Layout/Sidebar/Sidebar.tsx new file mode 100644 index 000000000..8a325a2cb --- /dev/null +++ b/beta/src/components/Layout/Sidebar/Sidebar.tsx @@ -0,0 +1,53 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; +import {SidebarContext} from 'components/Layout/useRouteMeta'; +import {MenuContext} from 'components/useMenu'; +import {useMediaQuery} from '../useMediaQuery'; +import {SidebarRouteTree} from './SidebarRouteTree'; +import {Search} from 'components/Search'; +import {MobileNav} from '../Nav/MobileNav'; +import {Feedback} from '../Feedback'; + +const SIDEBAR_BREAKPOINT = 1023; + +export function Sidebar() { + const {menuRef, isOpen} = React.useContext(MenuContext); + const isMobileSidebar = useMediaQuery(SIDEBAR_BREAKPOINT); + let routeTree = React.useContext(SidebarContext); + const isHidden = isMobileSidebar ? !isOpen : false; + + // HACK. Fix up the data structures instead. + if ((routeTree as any).routes.length === 1) { + routeTree = (routeTree as any).routes[0]; + } + return ( + + ); +} diff --git a/beta/src/components/Layout/Sidebar/SidebarButton.tsx b/beta/src/components/Layout/Sidebar/SidebarButton.tsx new file mode 100644 index 000000000..b1276cdb8 --- /dev/null +++ b/beta/src/components/Layout/Sidebar/SidebarButton.tsx @@ -0,0 +1,57 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; +import {IconNavArrow} from 'components/Icon/IconNavArrow'; + +interface SidebarButtonProps { + title: string; + heading: boolean; + level: number; + onClick: (event: React.MouseEvent) => void; + isExpanded?: boolean; + isBreadcrumb?: boolean; +} + +export function SidebarButton({ + title, + heading, + level, + onClick, + isExpanded, + isBreadcrumb, +}: SidebarButtonProps) { + return ( +
1, + })}> + +
+ ); +} diff --git a/beta/src/components/Layout/Sidebar/SidebarLink.tsx b/beta/src/components/Layout/Sidebar/SidebarLink.tsx new file mode 100644 index 000000000..72383a556 --- /dev/null +++ b/beta/src/components/Layout/Sidebar/SidebarLink.tsx @@ -0,0 +1,85 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +import * as React from 'react'; +import scrollIntoView from 'scroll-into-view-if-needed'; +import cn from 'classnames'; +import {IconNavArrow} from 'components/Icon/IconNavArrow'; +import Link from 'next/link'; +import {useIsMobile} from '../useMediaQuery'; + +interface SidebarLinkProps { + href: string; + selected?: boolean; + title: string; + level: number; + icon?: React.ReactNode; + heading?: boolean; + isExpanded?: boolean; + isBreadcrumb?: boolean; + hideArrow?: boolean; +} + +export function SidebarLink({ + href, + selected = false, + title, + level, + heading = false, + isExpanded, + isBreadcrumb, + hideArrow, +}: SidebarLinkProps) { + const ref = React.useRef(null); + const isMobile = useIsMobile(); + + React.useEffect(() => { + if (ref && ref.current && !!selected && !isMobile) { + scrollIntoView(ref.current, { + scrollMode: 'if-needed', + block: 'center', + inline: 'nearest', + }); + } + }, [ref, selected, isMobile]); + + return ( + + 0, + 'pl-5': level < 2, + 'text-base font-bold': level === 0, + 'dark:text-primary-dark text-primary ': level === 0 && !selected, + 'text-base text-link dark:text-link-dark': level === 1 && selected, + 'dark:text-primary-dark text-primary': heading, + 'text-base text-secondary dark:text-secondary-dark': + !selected && !heading, + 'text-base text-link dark:text-link-dark bg-highlight dark:bg-highlight-dark border-blue-40 hover:bg-highlight hover:text-link dark:hover:bg-highlight-dark dark:hover:text-link-dark': + selected, + } + )}> + {title} + {isExpanded != null && !heading && !hideArrow && ( + + + + )} + + + ); +} diff --git a/beta/src/components/Layout/Sidebar/SidebarRouteTree.tsx b/beta/src/components/Layout/Sidebar/SidebarRouteTree.tsx new file mode 100644 index 000000000..0ebb4615b --- /dev/null +++ b/beta/src/components/Layout/Sidebar/SidebarRouteTree.tsx @@ -0,0 +1,156 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; +import {RouteItem} from 'components/Layout/useRouteMeta'; +import {useRouter} from 'next/router'; +import {removeFromLast} from 'utils/removeFromLast'; +import {useRouteMeta} from '../useRouteMeta'; +import {SidebarLink} from './SidebarLink'; +import useCollapse from 'react-collapsed'; +import {useLayoutEffect} from 'react'; + +interface SidebarRouteTreeProps { + isMobile?: boolean; + routeTree: RouteItem; + level?: number; +} + +function CollapseWrapper({ + isExpanded, + duration, + children, +}: { + isExpanded: boolean; + duration: number; + children: any; +}) { + const ref = React.useRef(null); + const timeoutRef = React.useRef(null); + const {getCollapseProps} = useCollapse({ + isExpanded, + duration, + }); + + // Disable pointer events while animating. + const isExpandedRef = React.useRef(isExpanded); + if (typeof window !== 'undefined') { + // eslint-disable-next-line react-hooks/rules-of-hooks + useLayoutEffect(() => { + const wasExpanded = isExpandedRef.current; + if (wasExpanded === isExpanded) { + return; + } + isExpandedRef.current = isExpanded; + if (ref.current !== null) { + const node: HTMLDivElement = ref.current; + node.style.pointerEvents = 'none'; + if (timeoutRef.current !== null) { + window.clearTimeout(timeoutRef.current); + } + timeoutRef.current = window.setTimeout(() => { + node.style.pointerEvents = ''; + }, duration + 100); + } + }); + } + + return ( +
+
{children}
+
+ ); +} + +export function SidebarRouteTree({ + isMobile, + routeTree, + level = 0, +}: SidebarRouteTreeProps) { + const {breadcrumbs} = useRouteMeta(routeTree); + const {pathname} = useRouter(); + const slug = pathname; + + const currentRoutes = routeTree.routes as RouteItem[]; + const expandedPath = currentRoutes.reduce( + (acc: string | undefined, curr: RouteItem) => { + if (acc) return acc; + const breadcrumb = breadcrumbs.find((b) => b.path === curr.path); + if (breadcrumb) { + return curr.path; + } + if (curr.path === pathname) { + return pathname; + } + return undefined; + }, + undefined + ); + + const expanded = expandedPath; + return ( +
    + {currentRoutes.map(({path, title, routes, heading}) => { + const pagePath = path && removeFromLast(path, '.'); + const selected = slug === pagePath; + + // if current route item has no path and children treat it as an API sidebar heading + if (!path || !pagePath || heading) { + return ( + + ); + } + + // if route has a path and child routes, treat it as an expandable sidebar item + if (routes) { + const isExpanded = isMobile || expanded === path; + return ( +
  • + + + + +
  • + ); + } + + // if route has a path and no child routes, treat it as a sidebar link + return ( +
  • + +
  • + ); + })} +
+ ); +} diff --git a/beta/src/components/Layout/Sidebar/index.tsx b/beta/src/components/Layout/Sidebar/index.tsx new file mode 100644 index 000000000..a2204a508 --- /dev/null +++ b/beta/src/components/Layout/Sidebar/index.tsx @@ -0,0 +1,8 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +export {Sidebar} from './Sidebar'; +export {SidebarButton} from './SidebarButton'; +export {SidebarLink} from './SidebarLink'; +export {SidebarRouteTree} from './SidebarRouteTree'; diff --git a/beta/src/components/Layout/Toc.tsx b/beta/src/components/Layout/Toc.tsx new file mode 100644 index 000000000..40219426f --- /dev/null +++ b/beta/src/components/Layout/Toc.tsx @@ -0,0 +1,81 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import cx from 'classnames'; +import * as React from 'react'; +import {useTocHighlight} from './useTocHighlight'; + +export function Toc({ + headings, +}: { + headings: Array<{url: string; text: React.ReactNode; depth: number}>; +}) { + const {currentIndex} = useTocHighlight(); + // TODO: We currently have a mismatch between the headings in the document + // and the headings we find in MarkdownPage (i.e. we don't find Recap or Challenges). + // Select the max TOC item we have here for now, but remove this after the fix. + const selectedIndex = Math.min(currentIndex, headings.length - 1); + return ( + + ); +} diff --git a/beta/src/components/Layout/useMediaQuery.tsx b/beta/src/components/Layout/useMediaQuery.tsx new file mode 100644 index 000000000..db1e61880 --- /dev/null +++ b/beta/src/components/Layout/useMediaQuery.tsx @@ -0,0 +1,52 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import {useState, useCallback, useEffect} from 'react'; + +const useMediaQuery = (width: number) => { + const [targetReached, setTargetReached] = useState(false); + + const updateTarget = useCallback((e) => { + if (e.matches) { + setTargetReached(true); + } else { + setTargetReached(false); + } + }, []); + + useEffect(() => { + const media = window.matchMedia(`(max-width: ${width}px)`); + + try { + // Chrome & Firefox + media.addEventListener('change', updateTarget); + } catch { + // @deprecated method - Safari <= iOS12 + media.addListener(updateTarget); + } + + // Check on mount (callback is not called until a change occurs) + if (media.matches) { + setTargetReached(true); + } + + return () => { + try { + // Chrome & Firefox + media.removeEventListener('change', updateTarget); + } catch { + // @deprecated method - Safari <= iOS12 + media.removeListener(updateTarget); + } + }; + }, [updateTarget, width]); + + return targetReached; +}; + +const useIsMobile = () => { + return useMediaQuery(640); +}; + +export {useMediaQuery, useIsMobile}; diff --git a/beta/src/components/Layout/useRouteMeta.tsx b/beta/src/components/Layout/useRouteMeta.tsx new file mode 100644 index 000000000..5f962f7ea --- /dev/null +++ b/beta/src/components/Layout/useRouteMeta.tsx @@ -0,0 +1,129 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import {useRouter} from 'next/router'; + +/** + * While Next.js provides file-based routing, we still need to construct + * a sidebar for navigation and provide each markdown page + * previous/next links and titles. To do this, we construct a nested + * route object that is infinitely nestable. + */ + +export type RouteTag = + | 'foundation' + | 'intermediate' + | 'advanced' + | 'experimental' + | 'deprecated'; + +export interface RouteItem { + /** Page title (for the sidebar) */ + title: string; + /** Optional page description for heading */ + description?: string; + /* Additional meta info for page tagging */ + tags?: RouteTag[]; + /** Path to page */ + path?: string; + /** Whether the entry is a heading */ + heading?: boolean; + /** List of sub-routes */ + routes?: RouteItem[]; +} + +export interface Routes { + /** List of routes */ + routes: RouteItem[]; +} + +/** Routing metadata about a given route and it's siblings and parent */ +export interface RouteMeta { + /** The previous route */ + prevRoute?: RouteItem; + /** The next route */ + nextRoute?: RouteItem; + /** The current route */ + route?: RouteItem; + /** Trail of parent routes */ + breadcrumbs?: RouteItem[]; +} + +export function useRouteMeta(rootRoute?: RouteItem) { + const sidebarContext = React.useContext(SidebarContext); + const routeTree = rootRoute || sidebarContext; + const router = useRouter(); + const cleanedPath = router.pathname; + if (cleanedPath === '/404') { + return { + breadcrumbs: [], + }; + } + const breadcrumbs = getBreadcrumbs(cleanedPath, routeTree); + return { + ...getRouteMeta(cleanedPath, routeTree), + breadcrumbs: breadcrumbs.length > 0 ? breadcrumbs : [routeTree], + }; +} + +export const SidebarContext = React.createContext({title: 'root'}); + +// Performs a depth-first search to find the current route and its previous/next route +function getRouteMeta( + searchPath: string, + currentRoute: RouteItem, + ctx: RouteMeta = {} +): RouteMeta { + const {routes} = currentRoute; + + if (ctx.route && !ctx.nextRoute) { + ctx.nextRoute = currentRoute; + } + + if (currentRoute.path === searchPath) { + ctx.route = currentRoute; + } + + if (!ctx.route) { + ctx.prevRoute = currentRoute; + } + + if (!routes) { + return ctx; + } + + for (const route of routes) { + getRouteMeta(searchPath, route, ctx); + } + + return ctx; +} + +// iterates the route tree from the current route to find its ancestors for breadcrumbs +function getBreadcrumbs( + path: string, + currentRoute: RouteItem, + breadcrumbs: RouteItem[] = [] +): RouteItem[] { + if (currentRoute.path === path) { + return breadcrumbs; + } + + if (!currentRoute.routes) { + return []; + } + + for (const route of currentRoute.routes) { + const childRoute = getBreadcrumbs(path, route, [ + ...breadcrumbs, + currentRoute, + ]); + if (childRoute?.length) { + return childRoute; + } + } + + return []; +} diff --git a/beta/src/components/Layout/useTocHighlight.tsx b/beta/src/components/Layout/useTocHighlight.tsx new file mode 100644 index 000000000..a8538381f --- /dev/null +++ b/beta/src/components/Layout/useTocHighlight.tsx @@ -0,0 +1,83 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import React from 'react'; +import {siteConfig} from 'siteConfig'; + +const TOP_OFFSET = 75; + +export function getHeaderAnchors(): HTMLAnchorElement[] { + return Array.prototype.filter.call( + document.getElementsByClassName(siteConfig.headerIdConfig.className), + function (testElement) { + return ( + testElement.parentNode.nodeName === 'H1' || + testElement.parentNode.nodeName === 'H2' || + testElement.parentNode.nodeName === 'H3' + ); + } + ); +} + +/** + * Sets up Table of Contents highlighting. + */ +export function useTocHighlight() { + const [currentIndex, setCurrentIndex] = React.useState(0); + const timeoutRef = React.useRef(null); + + React.useEffect(() => { + function updateActiveLink() { + const pageHeight = document.body.scrollHeight; + const scrollPosition = window.scrollY + window.innerHeight; + const headersAnchors = getHeaderAnchors(); + + if (scrollPosition >= 0 && pageHeight - scrollPosition <= TOP_OFFSET) { + // Scrolled to bottom of page. + setCurrentIndex(headersAnchors.length - 1); + return; + } + + let index = -1; + while (index < headersAnchors.length - 1) { + const headerAnchor = headersAnchors[index + 1]; + const {top} = headerAnchor.getBoundingClientRect(); + + if (top >= TOP_OFFSET) { + break; + } + index += 1; + } + + setCurrentIndex(Math.max(index, 0)); + } + + function throttledUpdateActiveLink() { + if (timeoutRef.current === null) { + timeoutRef.current = window.setTimeout(() => { + timeoutRef.current = null; + updateActiveLink(); + }, 100); + } + } + + document.addEventListener('scroll', throttledUpdateActiveLink); + document.addEventListener('resize', throttledUpdateActiveLink); + + updateActiveLink(); + + return () => { + if (timeoutRef.current != null) { + clearTimeout(timeoutRef.current); + timeoutRef.current = null; + } + document.removeEventListener('scroll', throttledUpdateActiveLink); + document.removeEventListener('resize', throttledUpdateActiveLink); + }; + }, []); + + return { + currentIndex, + }; +} diff --git a/beta/src/components/Layout/useTwitter.tsx b/beta/src/components/Layout/useTwitter.tsx new file mode 100644 index 000000000..ee76a6a5c --- /dev/null +++ b/beta/src/components/Layout/useTwitter.tsx @@ -0,0 +1,49 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +/** + * Ported from gatsby-plugin-twitter + * https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-twitter + */ +import * as React from 'react'; + +const injectTwitterScript = function injectTwitterScript() { + function addJS(jsCode: any) { + const s = document.createElement('script'); + s.type = 'text/javascript'; + s.innerText = jsCode; + document.getElementsByTagName('head')[0].appendChild(s); + } + + addJS( + '\n window.twttr = (function(d, s, id) {\n var js,\n fjs = d.getElementsByTagName(s)[0],\n t = window.twttr || {};\n if (d.getElementById(id)) return t;\n js = d.createElement(s);\n js.id = id;\n js.src = "https://platform.twitter.com/widgets.js";\n fjs.parentNode.insertBefore(js, fjs);\n t._e = [];\n t.ready = function(f) {\n t._e.push(f);\n };\n return t;\n })(document, "script", "twitter-wjs");\n ' + ); +}; + +let injectedTwitterScript = false; +const embedClasses = [ + '.twitter-tweet', + '.twitter-timeline', + '.twitter-follow-button', + '.twitter-share-button', +].join(','); + +export function useTwitter() { + React.useEffect(() => { + if (document.querySelector(embedClasses) !== null) { + if (!injectedTwitterScript) { + injectTwitterScript(); + injectedTwitterScript = true; + } + + if ( + typeof (window as any).twttr !== 'undefined' && + (window as any).twttr.widgets && + typeof (window as any).twttr.widgets.load === 'function' + ) { + (window as any).twttr.widgets.load(); + } + } + }, []); +} diff --git a/beta/src/components/Logo.tsx b/beta/src/components/Logo.tsx new file mode 100644 index 000000000..0efa7acc1 --- /dev/null +++ b/beta/src/components/Logo.tsx @@ -0,0 +1,28 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +export function Logo(props: JSX.IntrinsicElements['svg']) { + return ( + + + + + ); +} + +Logo.displayName = 'Logo'; diff --git a/beta/src/components/MDX/Challenges/Challenges.tsx b/beta/src/components/MDX/Challenges/Challenges.tsx new file mode 100644 index 000000000..c6513cbbf --- /dev/null +++ b/beta/src/components/MDX/Challenges/Challenges.tsx @@ -0,0 +1,243 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; +import {Button} from 'components/Button'; +import {H2} from 'components/MDX/Heading'; +import {H4} from 'components/MDX/Heading'; +import {Navigation} from './Navigation'; +import {IconHint} from '../../Icon/IconHint'; +import {IconSolution} from '../../Icon/IconSolution'; +import {IconArrowSmall} from '../../Icon/IconArrowSmall'; + +interface ChallengesProps { + children: React.ReactElement[]; + isRecipes?: boolean; + titleText?: string; + titleId?: string; +} + +export interface ChallengeContents { + id: string; + name: string; + order: number; + content: React.ReactNode; + solution: React.ReactNode; + hint?: React.ReactNode; +} + +const parseChallengeContents = ( + children: React.ReactElement[] +): ChallengeContents[] => { + const contents: ChallengeContents[] = []; + + if (!children) { + return contents; + } + + let challenge: Partial = {}; + let content: React.ReactElement[] = []; + React.Children.forEach(children, (child) => { + const {props} = child; + switch (props.mdxType) { + case 'Solution': { + challenge.solution = child; + challenge.content = content; + contents.push(challenge as ChallengeContents); + challenge = {}; + content = []; + break; + } + case 'Hint': { + challenge.hint = child; + break; + } + case 'h3': { + challenge.order = contents.length + 1; + challenge.name = props.children; + challenge.id = props.id; + break; + } + default: { + content.push(child); + } + } + }); + + return contents; +}; + +export function Challenges({ + children, + isRecipes, + titleText = isRecipes ? 'Try out some examples' : 'Try out some challenges', + titleId = isRecipes ? 'examples' : 'challenges', +}: ChallengesProps) { + const challenges = parseChallengeContents(children); + const scrollAnchorRef = React.useRef(null); + + const [showHint, setShowHint] = React.useState(false); + const [showSolution, setShowSolution] = React.useState(false); + const [activeChallenge, setActiveChallenge] = React.useState( + challenges[0].id + ); + + const handleChallengeChange = (challengeId: string) => { + setShowHint(false); + setShowSolution(false); + setActiveChallenge(challengeId); + }; + + const toggleHint = () => { + if (showSolution && !showHint) { + setShowSolution(false); + } + setShowHint((hint) => !hint); + }; + + const toggleSolution = () => { + if (showHint && !showSolution) { + setShowHint(false); + } + setShowSolution((solution) => !solution); + }; + + const currentChallenge = challenges.find(({id}) => id === activeChallenge); + if (currentChallenge === undefined) { + throw new TypeError('currentChallenge should always exist'); + } + const nextChallenge = challenges.find(({order}) => { + return order === currentChallenge.order + 1; + }); + + const Heading = isRecipes ? H4 : H2; + return ( +
+
+
+ + {titleText} + + {challenges.length > 1 && ( + + )} +
+
+
+

+
+ {isRecipes ? 'Example' : 'Challenge'} {currentChallenge.order}{' '} + of {challenges.length} + : +
+ {currentChallenge.name} +

+ <>{currentChallenge.content} +
+
+ {currentChallenge.hint ? ( +
+ + +
+ ) : ( + !isRecipes && ( + + ) + )} + + {nextChallenge && ( + + )} +
+ {showHint && currentChallenge.hint} + + {showSolution && ( +
+

+ Solution +

+ {currentChallenge.solution} +
+ + {nextChallenge && ( + + )} +
+
+ )} +
+
+
+ ); +} diff --git a/beta/src/components/MDX/Challenges/Navigation.tsx b/beta/src/components/MDX/Challenges/Navigation.tsx new file mode 100644 index 000000000..1e1b76f91 --- /dev/null +++ b/beta/src/components/MDX/Challenges/Navigation.tsx @@ -0,0 +1,136 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import React, {createRef} from 'react'; +import cn from 'classnames'; +import {IconChevron} from 'components/Icon/IconChevron'; +import {ChallengeContents} from './Challenges'; +const debounce = require('debounce'); + +export function Navigation({ + challenges, + handleChange, + currentChallenge, + isRecipes, +}: { + challenges: ChallengeContents[]; + handleChange: (id: string) => void; + currentChallenge: ChallengeContents; + isRecipes?: boolean; +}) { + const containerRef = React.useRef(null); + const challengesNavRef = React.useRef( + challenges.map(() => createRef()) + ); + const scrollPos = currentChallenge.order - 1; + const canScrollLeft = scrollPos > 0; + const canScrollRight = scrollPos < challenges.length - 1; + + const handleScrollRight = () => { + if (scrollPos < challenges.length - 1) { + const currentNavRef = challengesNavRef.current[scrollPos + 1].current; + if (!currentNavRef) { + return; + } + if (containerRef.current) { + containerRef.current.scrollLeft = currentNavRef.offsetLeft; + } + handleChange(challenges[scrollPos + 1].id); + } + }; + + const handleScrollLeft = () => { + if (scrollPos > 0) { + const currentNavRef = challengesNavRef.current[scrollPos - 1].current; + if (!currentNavRef) { + return; + } + if (containerRef.current) { + containerRef.current.scrollLeft = currentNavRef.offsetLeft; + } + handleChange(challenges[scrollPos - 1].id); + } + }; + + const handleSelectNav = (id: string) => { + const selectedChallenge = challenges.findIndex( + (challenge) => challenge.id === id + ); + const currentNavRef = challengesNavRef.current[selectedChallenge].current; + if (containerRef.current) { + containerRef.current.scrollLeft = currentNavRef?.offsetLeft || 0; + } + handleChange(id); + }; + + const handleResize = React.useCallback(() => { + if (containerRef.current) { + const el = containerRef.current; + el.scrollLeft = + challengesNavRef.current[scrollPos].current?.offsetLeft || 0; + } + }, [containerRef, challengesNavRef, scrollPos]); + + React.useEffect(() => { + handleResize(); + window.addEventListener('resize', debounce(handleResize, 200)); + return () => { + window.removeEventListener('resize', handleResize); + }; + }, [handleResize]); + + return ( +
+
+
+ {challenges.map(({name, id, order}, index) => ( + + ))} +
+
+
+ + +
+
+ ); +} diff --git a/beta/src/components/MDX/Challenges/index.tsx b/beta/src/components/MDX/Challenges/index.tsx new file mode 100644 index 000000000..b39b2f223 --- /dev/null +++ b/beta/src/components/MDX/Challenges/index.tsx @@ -0,0 +1,14 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import React from 'react'; +export {Challenges} from './Challenges'; + +export function Hint({children}: {children: React.ReactNode}) { + return
{children}
; +} + +export function Solution({children}: {children: React.ReactNode}) { + return
{children}
; +} diff --git a/beta/src/components/MDX/CodeBlock/CodeBlock.tsx b/beta/src/components/MDX/CodeBlock/CodeBlock.tsx new file mode 100644 index 000000000..965bc816a --- /dev/null +++ b/beta/src/components/MDX/CodeBlock/CodeBlock.tsx @@ -0,0 +1,163 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import cn from 'classnames'; +import { + SandpackCodeViewer, + SandpackProvider, + SandpackThemeProvider, +} from '@codesandbox/sandpack-react'; +import rangeParser from 'parse-numeric-range'; +import {CustomTheme} from '../Sandpack/Themes'; + +interface InlineHiglight { + step: number; + line: number; + startColumn: number; + endColumn: number; +} + +const CodeBlock = function CodeBlock({ + children, + className = 'language-js', + metastring, + noMargin, +}: { + children: string; + className?: string; + metastring: string; + noMargin?: boolean; +}) { + const getDecoratedLineInfo = () => { + if (!metastring) { + return []; + } + + const linesToHighlight = getHighlightLines(metastring); + const highlightedLineConfig = linesToHighlight.map((line) => { + return { + className: 'bg-github-highlight dark:bg-opacity-10', + line, + }; + }); + + const inlineHighlightLines = getInlineHighlights(metastring, children); + const inlineHighlightConfig = inlineHighlightLines.map( + (line: InlineHiglight) => ({ + ...line, + elementAttributes: {'data-step': `${line.step}`}, + className: cn( + 'code-step bg-opacity-10 dark:bg-opacity-20 relative rounded px-1 py-[1.5px] border-b-[2px] border-opacity-60', + { + 'bg-blue-40 border-blue-40': line.step === 1, + 'bg-yellow-40 border-yellow-40': line.step === 2, + 'bg-green-40 border-green-40': line.step === 3, + 'bg-purple-40 border-purple-40': line.step === 4, + } + ), + }) + ); + + return highlightedLineConfig.concat(inlineHighlightConfig); + }; + + // e.g. "language-js" + const language = className.substring(9); + const filename = '/index.' + language; + const decorators = getDecoratedLineInfo(); + return ( +
+ + + + + +
+ ); +}; + +export default CodeBlock; + +/** + * + * @param metastring string provided after the language in a markdown block + * @returns array of lines to highlight + * @example + * ```js {1-3,7} [[1, 1, 20, 33], [2, 4, 4, 8]] App.js active + * ... + * ``` + * + * -> The metastring is `{1-3,7} [[1, 1, 20, 33], [2, 4, 4, 8]] App.js active` + */ +function getHighlightLines(metastring: string): number[] { + const HIGHLIGHT_REGEX = /{([\d,-]+)}/; + const parsedMetastring = HIGHLIGHT_REGEX.exec(metastring); + if (!parsedMetastring) { + return []; + } + return rangeParser(parsedMetastring[1]); +} + +/** + * + * @param metastring string provided after the language in a markdown block + * @returns InlineHighlight[] + * @example + * ```js {1-3,7} [[1, 1, 'count'], [2, 4, 'setCount']] App.js active + * ... + * ``` + * + * -> The metastring is `{1-3,7} [[1, 1, 'count', [2, 4, 'setCount']] App.js active` + */ +function getInlineHighlights(metastring: string, code: string) { + const INLINE_HIGHT_REGEX = /(\[\[.*\]\])/; + const parsedMetastring = INLINE_HIGHT_REGEX.exec(metastring); + if (!parsedMetastring) { + return []; + } + + const lines = code.split('\n'); + const encodedHiglights = JSON.parse(parsedMetastring[1]); + return encodedHiglights.map(([step, lineNo, substr, fromIndex]: any[]) => { + const line = lines[lineNo - 1]; + let index = line.indexOf(substr); + const lastIndex = line.lastIndexOf(substr); + if (index !== lastIndex) { + if (fromIndex === undefined) { + throw Error( + "Found '" + + substr + + "' twice. Specify fromIndex as the fourth value in the tuple." + ); + } + index = line.indexOf(substr, fromIndex); + } + if (index === -1) { + throw Error("Could not find: '" + substr + "'"); + } + return { + step, + line: lineNo, + startColumn: index, + endColumn: index + substr.length, + }; + }); +} diff --git a/beta/src/components/MDX/CodeBlock/index.tsx b/beta/src/components/MDX/CodeBlock/index.tsx new file mode 100644 index 000000000..e380cc84f --- /dev/null +++ b/beta/src/components/MDX/CodeBlock/index.tsx @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ +import cn from 'classnames'; +import * as React from 'react'; +const CodeBlock = React.lazy(() => import('./CodeBlock')); + +export default React.memo(function CodeBlockWrapper(props: { + isFromPackageImport: boolean; + children: string; + className?: string; + metastring: string; + noMargin?: boolean; + noMarkers?: boolean; +}): any { + const {children, isFromPackageImport} = props; + return ( + +
+

{children}

+
+ + }> + +
+ ); +}); diff --git a/beta/src/components/MDX/CodeDiagram.tsx b/beta/src/components/MDX/CodeDiagram.tsx new file mode 100644 index 000000000..d6ad640d1 --- /dev/null +++ b/beta/src/components/MDX/CodeDiagram.tsx @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import CodeBlock from './CodeBlock'; + +interface CodeDiagramProps { + children: React.ReactNode; + flip?: boolean; +} + +export function CodeDiagram({children, flip = false}: CodeDiagramProps) { + const illustration = React.Children.toArray(children).filter((child: any) => { + return child.props?.mdxType === 'img'; + }); + const content = React.Children.toArray(children).map((child: any) => { + if (child.props?.mdxType === 'pre') { + return ( + + ); + } else if (child.props?.mdxType === 'img') { + return null; + } else { + return child; + } + }); + if (flip) { + return ( +
+ {illustration} +
{content}
+
+ ); + } + return ( +
+
{content}
+
{illustration}
+
+ ); +} diff --git a/beta/src/components/MDX/ConsoleBlock.tsx b/beta/src/components/MDX/ConsoleBlock.tsx new file mode 100644 index 000000000..187931a5d --- /dev/null +++ b/beta/src/components/MDX/ConsoleBlock.tsx @@ -0,0 +1,78 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; +import {IconWarning} from '../Icon/IconWarning'; +import {IconError} from '../Icon/IconError'; + +type LogLevel = 'info' | 'warning' | 'error'; + +interface ConsoleBlockProps { + level?: LogLevel; + children: React.ReactNode; +} + +const Box = ({ + width = '60px', + height = '17px', + className, + customStyles, +}: { + width?: string; + height?: string; + className?: string; + customStyles?: Record; +}) => ( +
+); +Box.displayName = 'Box'; + +function ConsoleBlock({level = 'info', children}: ConsoleBlockProps) { + let message: string | undefined; + if (typeof children === 'string') { + message = children; + } else if ( + React.isValidElement(children) && + typeof children.props.children === 'string' + ) { + message = children.props.children; + } + + return ( +
+
+
+ +
+
+
+ Console +
+
+ + + +
+
+
+
+ {level === 'error' && } + {level === 'warning' && } +
{message}
+
+
+ ); +} + +export default ConsoleBlock; diff --git a/beta/src/components/MDX/Convention.tsx b/beta/src/components/MDX/Convention.tsx new file mode 100644 index 000000000..434053aac --- /dev/null +++ b/beta/src/components/MDX/Convention.tsx @@ -0,0 +1,25 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import {H3} from './Heading'; + +interface ConventionProps { + children: React.ReactNode; + name: string; +} + +function Convention({children, name}: ConventionProps) { + const id = name ? `${name}-conventions` : 'conventions'; + return ( +
+

+ Conventions +

+ {children} +
+ ); +} + +export default Convention; diff --git a/beta/src/components/MDX/Diagram.tsx b/beta/src/components/MDX/Diagram.tsx new file mode 100644 index 000000000..f45b8f76a --- /dev/null +++ b/beta/src/components/MDX/Diagram.tsx @@ -0,0 +1,61 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import Image from 'next/image'; + +interface DiagramProps { + name: string; + alt: string; + height: number; + width: number; + children: string; + captionPosition: 'top' | 'bottom' | null; +} + +function Caption({text}: {text: string}) { + return ( +
+
+ {text} +
+
+ ); +} + +export function Diagram({ + name, + alt, + height, + width, + children, + captionPosition, +}: DiagramProps) { + return ( +
+ {captionPosition === 'top' && } +
+ {alt} +
+
+ {alt} +
+ {(!captionPosition || captionPosition === 'bottom') && ( + + )} +
+ ); +} + +export default Diagram; diff --git a/beta/src/components/MDX/DiagramGroup.tsx b/beta/src/components/MDX/DiagramGroup.tsx new file mode 100644 index 000000000..932e72072 --- /dev/null +++ b/beta/src/components/MDX/DiagramGroup.tsx @@ -0,0 +1,20 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import {ReactNode} from 'react'; + +interface DiagramGroupProps { + children: ReactNode; +} + +export function DiagramGroup({children}: DiagramGroupProps) { + return ( +
+ {children} +
+ ); +} + +export default DiagramGroup; diff --git a/beta/src/components/MDX/ExpandableCallout.tsx b/beta/src/components/MDX/ExpandableCallout.tsx new file mode 100644 index 000000000..c4fc53c69 --- /dev/null +++ b/beta/src/components/MDX/ExpandableCallout.tsx @@ -0,0 +1,67 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; +import {IconNote} from '../Icon/IconNote'; +import {IconGotcha} from '../Icon/IconGotcha'; + +type CalloutVariants = 'gotcha' | 'note'; + +interface ExpandableCalloutProps { + children: React.ReactNode; + type: CalloutVariants; +} + +const variantMap = { + note: { + title: 'Note', + Icon: IconNote, + containerClasses: + 'bg-green-5 dark:bg-green-60 dark:bg-opacity-20 text-primary dark:text-primary-dark text-lg', + textColor: 'text-green-60 dark:text-green-40', + overlayGradient: + 'linear-gradient(rgba(245, 249, 248, 0), rgba(245, 249, 248, 1)', + }, + gotcha: { + title: 'Pitfall', + Icon: IconGotcha, + containerClasses: 'bg-yellow-5 dark:bg-yellow-60 dark:bg-opacity-20', + textColor: 'text-yellow-50 dark:text-yellow-40', + overlayGradient: + 'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)', + }, +}; + +function ExpandableCallout({children, type}: ExpandableCalloutProps) { + const contentRef = React.useRef(null); + const variant = variantMap[type]; + + return ( +
+

+ + {variant.title} +

+
+
+ {children} +
+
+
+ ); +} + +ExpandableCallout.defaultProps = { + type: 'note', +}; + +export default ExpandableCallout; diff --git a/beta/src/components/MDX/ExpandableExample.tsx b/beta/src/components/MDX/ExpandableExample.tsx new file mode 100644 index 000000000..3419b9cad --- /dev/null +++ b/beta/src/components/MDX/ExpandableExample.tsx @@ -0,0 +1,87 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; +import {IconChevron} from '../Icon/IconChevron'; +import {IconDeepDive} from '../Icon/IconDeepDive'; +import {IconCodeBlock} from '../Icon/IconCodeBlock'; +import {Button} from '../Button'; + +interface ExpandableExampleProps { + children: React.ReactNode; + title: string; + excerpt?: string; + type: 'DeepDive' | 'Example'; +} + +function ExpandableExample({ + children, + title, + excerpt, + type, +}: ExpandableExampleProps) { + const [isExpanded, setIsExpanded] = React.useState(false); + const isDeepDive = type === 'DeepDive'; + const isExample = type === 'Example'; + + return ( +
+
+
+ {isDeepDive && ( + <> + + Deep Dive + + )} + {isExample && ( + <> + + Example + + )} +
+
+

+ {title} +

+ {excerpt &&
{excerpt}
} +
+ +
+
+ {children} +
+
+ ); +} + +export default ExpandableExample; diff --git a/beta/src/components/MDX/Heading.tsx b/beta/src/components/MDX/Heading.tsx new file mode 100644 index 000000000..1a8be7fa2 --- /dev/null +++ b/beta/src/components/MDX/Heading.tsx @@ -0,0 +1,134 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import cn from 'classnames'; +import * as React from 'react'; +import {siteConfig} from 'siteConfig'; +import {forwardRefWithAs} from 'utils/forwardRefWithAs'; +export interface HeadingProps { + className?: string; + isPageAnchor?: boolean; + children: React.ReactNode; + id?: string; + as?: any; +} + +const anchorClassName = siteConfig.headerIdConfig.className; + +const Heading = forwardRefWithAs(function Heading( + {as: Comp = 'div', className, children, id, isPageAnchor = true, ...props}, + ref +) { + let label = 'Link for this heading'; + if (typeof children === 'string') { + label = 'Link for ' + children; + } + + return ( + <> + + {children} + {isPageAnchor && ( + + + + + + + + + )} + + + + ); +}); + +Heading.displayName = 'Heading'; + +export const H1 = ({className, ...props}: HeadingProps) => ( + +); + +export const H2 = ({className, ...props}: HeadingProps) => ( + +); +export const H3 = ({className, ...props}: HeadingProps) => ( + +); + +export const H4 = ({className, ...props}: HeadingProps) => ( + +); diff --git a/beta/src/components/MDX/HomepageHero.tsx b/beta/src/components/MDX/HomepageHero.tsx new file mode 100644 index 000000000..17953852d --- /dev/null +++ b/beta/src/components/MDX/HomepageHero.tsx @@ -0,0 +1,45 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import {Logo} from 'components/Logo'; +import YouWillLearnCard from 'components/MDX/YouWillLearnCard'; + +function HomepageHero() { + return ( + <> +
+ +
+

+ React Docs +

+
+ Beta +
+
+
+
+
+ +

+ Learn how to think in React with step-by-step explanations and + interactive examples. +

+
+
+
+ +

+ Look up the API signatures of React Hooks, and see their shape + using the visual code diagrams. +

+
+
+
+ + ); +} + +export default HomepageHero; diff --git a/beta/src/components/MDX/InlineCode.tsx b/beta/src/components/MDX/InlineCode.tsx new file mode 100644 index 000000000..5a8c01938 --- /dev/null +++ b/beta/src/components/MDX/InlineCode.tsx @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; + +interface InlineCodeProps { + isLink: boolean; +} +function InlineCode({ + isLink, + ...props +}: JSX.IntrinsicElements['code'] & InlineCodeProps) { + return ( + + ); +} + +InlineCode.displayName = 'InlineCode'; + +export default InlineCode; diff --git a/beta/src/components/MDX/Intro.tsx b/beta/src/components/MDX/Intro.tsx new file mode 100644 index 000000000..68094395a --- /dev/null +++ b/beta/src/components/MDX/Intro.tsx @@ -0,0 +1,21 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import React from 'react'; + +export interface IntroProps { + children?: React.ReactNode; +} + +function Intro({children}: IntroProps) { + return ( +
+ {children} +
+ ); +} + +Intro.displayName = 'Intro'; + +export default Intro; diff --git a/beta/src/components/MDX/Link.tsx b/beta/src/components/MDX/Link.tsx new file mode 100644 index 000000000..ea9b84b03 --- /dev/null +++ b/beta/src/components/MDX/Link.tsx @@ -0,0 +1,59 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import NextLink from 'next/link'; +import cn from 'classnames'; + +import {ExternalLink} from 'components/ExternalLink'; + +function Link({ + href, + className, + children, + ...props +}: JSX.IntrinsicElements['a']) { + const classes = + 'inline text-link dark:text-link-dark break-normal border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal'; + const modifiedChildren = React.Children.toArray(children).map( + (child: any) => { + if (child.props?.mdxType && child.props?.mdxType === 'inlineCode') { + return React.cloneElement(child, { + isLink: true, + }); + } + return child; + } + ); + + if (!href) { + // eslint-disable-next-line jsx-a11y/anchor-has-content + return ; + } + return ( + <> + {href.startsWith('https://') ? ( + + {modifiedChildren} + + ) : href.startsWith('#') ? ( + // eslint-disable-next-line jsx-a11y/anchor-has-content + + {modifiedChildren} + + ) : ( + + {/* eslint-disable-next-line jsx-a11y/anchor-has-content */} + + {modifiedChildren} + + + )} + + ); +} + +Link.displayName = 'Link'; + +export default Link; diff --git a/beta/src/components/MDX/MDXComponents.module.css b/beta/src/components/MDX/MDXComponents.module.css new file mode 100644 index 000000000..9840e77ce --- /dev/null +++ b/beta/src/components/MDX/MDXComponents.module.css @@ -0,0 +1,43 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +/* Stop purging. */ +.markdown p { + @apply mb-4 leading-7 whitespace-pre-wrap text-gray-70; +} + +.markdown ol { + @apply mb-4 ml-8 list-decimal; +} + +.markdown ul { + @apply mb-4 ml-8 list-disc; +} + +.markdown h1 { + @apply mb-6 text-4xl font-extrabold tracking-tight; +} + +.markdown h2 { + @apply mt-12 mb-4 text-3xl font-extrabold tracking-tight; +} +.markdown h3 { + @apply mt-8 mb-3 text-2xl font-extrabold tracking-tight; +} +.markdown h4 { + @apply mt-8 mb-3 text-xl font-extrabold tracking-tight; +} + +.markdown code { + @apply text-gray-70 bg-card dark:bg-card-dark p-1 rounded-lg no-underline; + font-size: 90%; +} + +.markdown li { + @apply mb-2; +} + +.markdown a { + @apply inline text-link dark:text-link-dark break-normal border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal; +} diff --git a/beta/src/components/MDX/MDXComponents.tsx b/beta/src/components/MDX/MDXComponents.tsx new file mode 100644 index 000000000..fb662fe6f --- /dev/null +++ b/beta/src/components/MDX/MDXComponents.tsx @@ -0,0 +1,394 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; + +import CodeBlock from './CodeBlock'; +import {CodeDiagram} from './CodeDiagram'; +import ConsoleBlock from './ConsoleBlock'; +import Convention from './Convention'; +import ExpandableCallout from './ExpandableCallout'; +import ExpandableExample from './ExpandableExample'; +import {H1, H2, H3, H4} from './Heading'; +import HomepageHero from './HomepageHero'; +import InlineCode from './InlineCode'; +import Intro from './Intro'; +import Link from './Link'; +import {PackageImport} from './PackageImport'; +import Recap from './Recap'; +import Sandpack from './Sandpack'; +import Diagram from './Diagram'; +import DiagramGroup from './DiagramGroup'; +import SimpleCallout from './SimpleCallout'; +import TerminalBlock from './TerminalBlock'; +import YouWillLearnCard from './YouWillLearnCard'; +import {Challenges, Hint, Solution} from './Challenges'; +import {IconNavArrow} from '../Icon/IconNavArrow'; +import ButtonLink from 'components/ButtonLink'; + +function CodeStep({children, step}: {children: any; step: number}) { + return ( + + {children} + + ); +} + +const P = (p: JSX.IntrinsicElements['p']) => ( +

+); + +const Strong = (strong: JSX.IntrinsicElements['strong']) => ( + +); + +const OL = (p: JSX.IntrinsicElements['ol']) => ( +

    +); +const LI = (p: JSX.IntrinsicElements['li']) => ( +
  1. +); +const UL = (p: JSX.IntrinsicElements['ul']) => ( +
      +); + +const Divider = () => ( +
      +); + +const Gotcha = ({children}: {children: React.ReactNode}) => ( + {children} +); +const Note = ({children}: {children: React.ReactNode}) => ( + {children} +); + +const Blockquote = ({ + children, + ...props +}: JSX.IntrinsicElements['blockquote']) => { + return ( + <> +
      + {children} +
      + + + ); +}; + +function LearnMore({ + children, + path, +}: { + title: string; + path?: string; + children: any; +}) { + return ( + <> +
      +
      +

      + Ready to learn this topic? +

      + {children} + {path ? ( + + Read More + + + ) : null} +
      +
      +
      + + ); +} + +function Math({children}: {children: any}) { + return ( + + {children} + + ); +} + +function MathI({children}: {children: any}) { + return ( + + {children} + + ); +} + +function YouWillLearn({ + children, + isChapter, +}: { + children: any; + isChapter?: boolean; +}) { + let title = isChapter ? 'In this chapter' : 'You will learn'; + return {children}; +} + +// TODO: typing. +function Recipes(props: any) { + return ; +} + +function AuthorCredit({ + author, + authorLink, +}: { + author: string; + authorLink: string; +}) { + return ( +

      + + Illustrated by{' '} + {authorLink ? ( + + {author} + + ) : ( + author + )} + +

      + ); +} + +function Illustration({ + caption, + src, + alt, + author, + authorLink, +}: { + caption: string; + src: string; + alt: string; + author: string; + authorLink: string; +}) { + return ( +
      +
      + {alt} + {caption ? ( +
      + {caption} +
      + ) : null} +
      + {author ? : null} +
      + ); +} + +function IllustrationBlock({ + title, + sequential, + author, + authorLink, + children, +}: { + title: string; + author: string; + authorLink: string; + sequential: boolean; + children: any; +}) { + const imageInfos = React.Children.toArray(children).map( + (child: any) => child.props + ); + const images = imageInfos.map((info, index) => ( +
      +
      + {info.alt} +
      + {info.caption ? ( +
      + {info.caption} +
      + ) : null} +
      + )); + return ( +
      + {title ? ( +

      + {title} +

      + ) : null} + {sequential ? ( +
        + {images.map((x: any, i: number) => ( +
      1. + {x} +
      2. + ))} +
      + ) : ( +
      {images}
      + )} + {author ? : null} + +
      + ); +} + +export const MDXComponents = { + p: P, + strong: Strong, + blockquote: Blockquote, + ol: OL, + ul: UL, + li: LI, + h1: H1, + h2: H2, + h3: H3, + h4: H4, + inlineCode: InlineCode, + hr: Divider, + a: Link, + code: CodeBlock, + // The code block renders
       so we just want a div here.
      +  pre: (p: JSX.IntrinsicElements['div']) => 
      , + CodeDiagram, + ConsoleBlock, + Convention, + DeepDive: (props: { + children: React.ReactNode; + title: string; + excerpt: string; + }) => , + Diagram, + DiagramGroup, + Gotcha, + HomepageHero, + Illustration, + IllustrationBlock, + Intro, + LearnMore, + Math, + MathI, + Note, + PackageImport, + Recap, + Recipes, + Sandpack, + TerminalBlock, + YouWillLearn, + YouWillLearnCard, + Challenges, + Hint, + Solution, + CodeStep, +}; diff --git a/beta/src/components/MDX/PackageImport.tsx b/beta/src/components/MDX/PackageImport.tsx new file mode 100644 index 000000000..83ae74f8a --- /dev/null +++ b/beta/src/components/MDX/PackageImport.tsx @@ -0,0 +1,37 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import CodeBlock from './CodeBlock'; + +interface PackageImportProps { + children: React.ReactNode; +} + +export function PackageImport({children}: PackageImportProps) { + const terminal = React.Children.toArray(children).filter((child: any) => { + return child.props?.mdxType !== 'pre'; + }); + const code = React.Children.toArray(children).map((child: any, i: number) => { + if (child.props?.mdxType === 'pre') { + return ( + + ); + } else { + return null; + } + }); + return ( +
      +
      {terminal}
      +
      {code}
      +
      + ); +} diff --git a/beta/src/components/MDX/Recap.tsx b/beta/src/components/MDX/Recap.tsx new file mode 100644 index 000000000..d91ed48b4 --- /dev/null +++ b/beta/src/components/MDX/Recap.tsx @@ -0,0 +1,23 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import {H2} from './Heading'; + +interface RecapProps { + children: React.ReactNode; +} + +function Recap({children}: RecapProps) { + return ( +
      +

      + Recap +

      + {children} +
      + ); +} + +export default Recap; diff --git a/beta/src/components/MDX/Sandpack/CustomPreset.tsx b/beta/src/components/MDX/Sandpack/CustomPreset.tsx new file mode 100644 index 000000000..a7b401b4f --- /dev/null +++ b/beta/src/components/MDX/Sandpack/CustomPreset.tsx @@ -0,0 +1,109 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ +import React from 'react'; +// @ts-ignore +import {flushSync} from 'react-dom'; +import { + useSandpack, + useActiveCode, + SandpackCodeEditor, + SandpackThemeProvider, + SandpackReactDevTools, +} from '@codesandbox/sandpack-react'; +import scrollIntoView from 'scroll-into-view-if-needed'; +import cn from 'classnames'; + +import {IconChevron} from 'components/Icon/IconChevron'; +import {NavigationBar} from './NavigationBar'; +import {Preview} from './Preview'; +import {CustomTheme} from './Themes'; +import {useSandpackLint} from './utils'; + +export function CustomPreset({ + isSingleFile, + showDevTools, + onDevToolsLoad, + devToolsLoaded, +}: { + isSingleFile: boolean; + showDevTools: boolean; + devToolsLoaded: boolean; + onDevToolsLoad: () => void; +}) { + const {lintErrors, onLint} = useSandpackLint(); + const lineCountRef = React.useRef<{[key: string]: number}>({}); + const containerRef = React.useRef(null); + const {sandpack} = useSandpack(); + const {code} = useActiveCode(); + const [isExpanded, setIsExpanded] = React.useState(false); + + const {activePath} = sandpack; + if (!lineCountRef.current[activePath]) { + lineCountRef.current[activePath] = code.split('\n').length; + } + const lineCount = lineCountRef.current[activePath]; + const isExpandable = lineCount > 16 || isExpanded; + + return ( + <> +
      + + +
      + + + {isExpandable && ( + + )} +
      + + {showDevTools && ( + + )} +
      +
      + + ); +} diff --git a/beta/src/components/MDX/Sandpack/DownloadButton.tsx b/beta/src/components/MDX/Sandpack/DownloadButton.tsx new file mode 100644 index 000000000..dfec93d00 --- /dev/null +++ b/beta/src/components/MDX/Sandpack/DownloadButton.tsx @@ -0,0 +1,90 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import {useSandpack} from '@codesandbox/sandpack-react'; +import {IconArrowSmall} from '../../Icon/IconArrowSmall'; +export interface DownloadButtonProps {} + +let initialIsSupported = false; + +export const DownloadButton: React.FC = () => { + const {sandpack} = useSandpack(); + const [supported, setSupported] = React.useState(initialIsSupported); + React.useEffect(() => { + // This detection will work in Chrome 97+ + if ( + !supported && + (HTMLScriptElement as any).supports && + (HTMLScriptElement as any).supports('importmap') + ) { + setSupported(true); + initialIsSupported = true; + } + }, [supported]); + + if (!supported) { + return null; + } + + const downloadHTML = () => { + const css = sandpack.files['/styles.css']?.code ?? ''; + const code = sandpack.files['/App.js']?.code ?? ''; + const blob = new Blob([ + ` + + +
      + + + + + + + +`, + ]); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = url; + a.download = 'sandbox.html'; + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(url); + }; + + return ( + + ); +}; diff --git a/beta/src/components/MDX/Sandpack/Error.tsx b/beta/src/components/MDX/Sandpack/Error.tsx new file mode 100644 index 000000000..257ce0369 --- /dev/null +++ b/beta/src/components/MDX/Sandpack/Error.tsx @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; + +interface ErrorType { + title?: string; + message: string; + column?: number; + line?: number; + path?: string; +} + +export function Error({error}: {error: ErrorType}) { + const {message, title} = error; + + return ( +
      +

      {title || 'Error'}

      +
      +        {message}
      +      
      +
      + ); +} diff --git a/beta/src/components/MDX/Sandpack/FilesDropdown.tsx b/beta/src/components/MDX/Sandpack/FilesDropdown.tsx new file mode 100644 index 000000000..68d068ad7 --- /dev/null +++ b/beta/src/components/MDX/Sandpack/FilesDropdown.tsx @@ -0,0 +1,50 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import cn from 'classnames'; +import {IconChevron} from '../../Icon/IconChevron'; +import {useSandpack} from '@codesandbox/sandpack-react'; +import {Listbox} from '@headlessui/react'; + +const getFileName = (filePath: string): string => { + const lastIndexOfSlash = filePath.lastIndexOf('/'); + return filePath.slice(lastIndexOfSlash + 1); +}; + +export function FilesDropdown() { + const {sandpack} = useSandpack(); + const {openPaths, setActiveFile, activePath} = sandpack; + return ( + + + {({open}) => ( + + {getFileName(activePath)} + + + + + )} + + + {openPaths.map((filePath: string) => ( + + {getFileName(filePath)} + + ))} + + + ); +} diff --git a/beta/src/components/MDX/Sandpack/NavigationBar.tsx b/beta/src/components/MDX/Sandpack/NavigationBar.tsx new file mode 100644 index 000000000..6114ef673 --- /dev/null +++ b/beta/src/components/MDX/Sandpack/NavigationBar.tsx @@ -0,0 +1,63 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import { + FileTabs, + useSandpack, + useSandpackNavigation, +} from '@codesandbox/sandpack-react'; +import {OpenInCodeSandboxButton} from './OpenInCodeSandboxButton'; +import {ResetButton} from './ResetButton'; +import {DownloadButton} from './DownloadButton'; +import {FilesDropdown} from './FilesDropdown'; + +export function NavigationBar({showDownload}: {showDownload: boolean}) { + const {sandpack} = useSandpack(); + const [dropdownActive, setDropdownActive] = React.useState(false); + const {openPaths, clients} = sandpack; + const clientId = Object.keys(clients)[0]; + const {refresh} = useSandpackNavigation(clientId); + + const resizeHandler = React.useCallback(() => { + const width = window.innerWidth || document.documentElement.clientWidth; + if (!dropdownActive && width < 640) { + setDropdownActive(true); + } + if (dropdownActive && width >= 640) { + setDropdownActive(false); + } + }, [dropdownActive]); + + React.useEffect(() => { + if (openPaths.length > 1) { + resizeHandler(); + window.addEventListener('resize', resizeHandler); + return () => { + window.removeEventListener('resize', resizeHandler); + }; + } + return; + }, [openPaths.length, resizeHandler]); + + const handleReset = () => { + sandpack.resetAllFiles(); + refresh(); + }; + + return ( +
      +
      + {dropdownActive ? : } +
      +
      + {showDownload && } + + +
      +
      + ); +} diff --git a/beta/src/components/MDX/Sandpack/OpenInCodeSandboxButton.tsx b/beta/src/components/MDX/Sandpack/OpenInCodeSandboxButton.tsx new file mode 100644 index 000000000..2a06123a3 --- /dev/null +++ b/beta/src/components/MDX/Sandpack/OpenInCodeSandboxButton.tsx @@ -0,0 +1,22 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import {UnstyledOpenInCodeSandboxButton} from '@codesandbox/sandpack-react'; +import {IconNewPage} from '../../Icon/IconNewPage'; + +export const OpenInCodeSandboxButton = () => { + return ( + + + Fork + + ); +}; diff --git a/beta/src/components/MDX/Sandpack/Preview.tsx b/beta/src/components/MDX/Sandpack/Preview.tsx new file mode 100644 index 000000000..9712b2896 --- /dev/null +++ b/beta/src/components/MDX/Sandpack/Preview.tsx @@ -0,0 +1,208 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +/* eslint-disable react-hooks/exhaustive-deps */ +import * as React from 'react'; +import {useSandpack, LoadingOverlay} from '@codesandbox/sandpack-react'; +import cn from 'classnames'; + +import {Error} from './Error'; +import {computeViewportSize, generateRandomId} from './utils'; +import type {LintDiagnostic} from './utils'; + +type CustomPreviewProps = { + className?: string; + customStyle?: Record; + isExpanded: boolean; + lintErrors: LintDiagnostic; +}; + +function useDebounced(value: any): any { + const ref = React.useRef(null); + const [saved, setSaved] = React.useState(value); + React.useEffect(() => { + clearTimeout(ref.current); + ref.current = setTimeout(() => { + setSaved(value); + }, 300); + }, [value]); + return saved; +} + +export function Preview({ + customStyle, + isExpanded, + className, + lintErrors, +}: CustomPreviewProps) { + const {sandpack, listen} = useSandpack(); + const [isReady, setIsReady] = React.useState(false); + const [iframeComputedHeight, setComputedAutoHeight] = React.useState< + number | null + >(null); + + let { + error: rawError, + registerBundler, + unregisterBundler, + errorScreenRegisteredRef, + openInCSBRegisteredRef, + loadingScreenRegisteredRef, + status, + } = sandpack; + + if ( + rawError && + rawError.message === '_csbRefreshUtils.prelude is not a function' + ) { + // Work around a noisy internal error. + rawError = null; + } + + if (lintErrors.length > 0) { + if (rawError == null || rawError.title === 'Runtime Exception') { + // When there's a lint error, show it -- even over a runtime error. + // (However, when there's a build error, we keep showing the build one.) + const {line, column, message} = lintErrors[0]; + rawError = { + title: 'Lint Error', + message: `${line}:${column} - ${message}`, + }; + } + } + + // It changes too fast, causing flicker. + const error = useDebounced(rawError); + + const clientId = React.useRef(generateRandomId()); + const iframeRef = React.useRef(null); + + // SandpackPreview immediately registers the custom screens/components so the bundler does not render any of them + // TODO: why are we doing this during render? + openInCSBRegisteredRef.current = true; + errorScreenRegisteredRef.current = true; + loadingScreenRegisteredRef.current = true; + + React.useEffect(function createBundler() { + const iframeElement = iframeRef.current!; + registerBundler(iframeElement, clientId.current); + + return () => { + unregisterBundler(clientId.current); + }; + }, []); + + React.useEffect( + function bundlerListener() { + const unsubscribe = listen((message: any) => { + if (message.type === 'resize') { + setComputedAutoHeight(message.height); + } else if (message.type === 'start') { + if (message.firstLoad) { + setIsReady(false); + } + } else if (message.type === 'done') { + setIsReady(true); + } + }, clientId.current); + + return () => { + setIsReady(false); + setComputedAutoHeight(null); + unsubscribe(); + }; + }, + [status === 'idle'] + ); + + const viewportStyle = computeViewportSize('auto', 'portrait'); + const overrideStyle = error + ? { + // Don't collapse errors + maxHeight: undefined, + } + : null; + const hideContent = !isReady || error; + + // WARNING: + // The layout and styling here is convoluted and really easy to break. + // If you make changes to it, you need to test different cases: + // - Content -> (compile | runtime) error -> content editing flow should work. + // - Errors should expand parent height rather than scroll. + // - Long sandboxes should scroll unless "show more" is toggled. + // - Expanded sandboxes ("show more") have sticky previews and errors. + // - Sandboxes have autoheight based on content. + // - That autoheight should be measured correctly! (Check some long ones.) + // - You shouldn't see nested scrolls (that means autoheight is borked). + // - Ideally you shouldn't see a blank preview tile while recompiling. + // - Container shouldn't be horizontally scrollable (even while loading). + // - It should work on mobile. + // The best way to test it is to actually go through some challenges. + + return ( +
      +
      +
      + + +## Snake in React {/*snake-in-react*/} + +[Tom Occhino](http://tomocchino.com/) implemented Snake in 150 lines with React. + +> [Check the source on GitHub](https://github.com/tomocchino/react-snake/blob/master/src/snake.js) +> +>
      diff --git a/beta/src/pages/blog/2013/07/11/react-v0-4-prop-validation-and-default-values.md b/beta/src/pages/blog/2013/07/11/react-v0-4-prop-validation-and-default-values.md new file mode 100644 index 000000000..59a41733c --- /dev/null +++ b/beta/src/pages/blog/2013/07/11/react-v0-4-prop-validation-and-default-values.md @@ -0,0 +1,60 @@ +--- +title: 'New in React v0.4: Prop Validation and Default Values' +author: [zpao] +--- + +Many of the questions we got following the public launch of React revolved around `props`, specifically that people wanted to do validation and to make sure their components had sensible defaults. + +## Validation {/*validation*/} + +Oftentimes you want to validate your `props` before you use them. Perhaps you want to ensure they are a specific type. Or maybe you want to restrict your prop to specific values. Or maybe you want to make a specific prop required. This was always possible — you could have written validations in your `render` or `componentWillReceiveProps` functions, but that gets clunky fast. + +React v0.4 will provide a nice easy way for you to use built-in validators, or to even write your own. + +```js +React.createClass({ + propTypes: { + // An optional string prop named "description". + description: React.PropTypes.string, + + // A required enum prop named "category". + category: React.PropTypes.oneOf(['News','Photos']).isRequired, + + // A prop named "dialog" that requires an instance of Dialog. + dialog: React.PropTypes.instanceOf(Dialog).isRequired + }, + ... +}); +``` + +## Default Values {/*default-values*/} + +One common pattern we've seen with our React code is to do something like this: + +```js +React.createClass({ + render: function () { + var value = this.props.value || 'default value'; + return
      {value}
      ; + }, +}); +``` + +Do this for a few `props` across a few components and now you have a lot of redundant code. Starting with React v0.4, you can provide default values in a declarative way: + +```js +React.createClass({ + getDefaultProps: function() { + return { + value: 'default value' + }; + } + ... +}); +``` + +We will use the cached result of this function before each `render`. We also perform all validations before each `render` to ensure that you have all of the data you need in the right form before you try to use it. + +--- + +Both of these features are entirely optional. We've found them to be increasingly valuable at Facebook as our applications grow and evolve, and we hope others find them useful as well. diff --git a/beta/src/pages/blog/2013/07/17/react-v0-4-0.md b/beta/src/pages/blog/2013/07/17/react-v0-4-0.md new file mode 100644 index 000000000..65a730234 --- /dev/null +++ b/beta/src/pages/blog/2013/07/17/react-v0-4-0.md @@ -0,0 +1,37 @@ +--- +title: 'React v0.4.0' +author: [zpao] +--- + +Over the past 2 months we've been taking feedback and working hard to make React even better. We fixed some bugs, made some under-the-hood improvements, and added several features that we think will improve the experience developing with React. Today we're proud to announce the availability of React v0.4! + +This release could not have happened without the support of our growing community. Since launch day, the community has contributed blog posts, questions to the [Google Group](https://groups.google.com/group/reactjs), and issues and pull requests on GitHub. We've had contributions ranging from documentation improvements to major changes to React's rendering. We've seen people integrate React into the tools they're using and the products they're building, and we're all very excited to see what our budding community builds next! + +React v0.4 has some big changes. We've also restructured the documentation to better communicate how to use React. We've summarized the changes below and linked to documentation where we think it will be especially useful. + +When you're ready, [go download it](/docs/installation.html)! + +### React {/*react*/} + +- Switch from using `id` attribute to `data-reactid` to track DOM nodes. This allows you to integrate with other JS and CSS libraries more easily. +- Support for more DOM elements and attributes (e.g., ``) +- Improved server-side rendering APIs. `React.renderComponentToString(, callback)` allows you to use React on the server and generate markup which can be sent down to the browser. +- `prop` improvements: validation and default values. [Read our blog post for details...](/blog/2013/07/11/react-v0-4-prop-validation-and-default-values.html) +- Support for the `key` prop, which allows for finer control over reconciliation. [Read the docs for details...](/docs/multiple-components.html) +- Removed `React.autoBind`. [Read our blog post for details...](/blog/2013/07/02/react-v0-4-autobind-by-default.html) +- Improvements to forms. We've written wrappers around ``, ` +
      + + + + +

      That's right!

      + + +``` + + + +Manipulating the UI imperatively works well enough for isolated examples, but it gets exponentially more difficult to manage in more complex systems. Imagine updating a page full of different forms like this one. Adding a new UI element or a new interaction would require carefully checking all existing code to make sure you haven't introduced a bug (for example, forgetting to show or hide something). + +React was built to solve this problem. + +In React, you don't directly manipulate the UI--meaning you don't enable, disable, show, or hide components directly. Instead, you **declare what you want to show**, and React figures out how to update the UI. Think of getting into a taxi and telling the driver where you want to go instead of telling them exactly where to turn. It's the driver's job to get you there, and they might even know some shortcuts you haven't considered! + + + +## Thinking about UI declaratively {/*thinking-about-ui-declaratively*/} + +You've seen how to implement a form imperatively above. To better understand how to think in React, you'll walk through reimplementing this UI in React below: + +1. **Identify** your component's different visual states +2. **Determine** what triggers those state changes +3. **Represent** the state in memory using `useState` +4. **Remove** any non-essential state variables +5. **Connect** the event handlers to set the state + +### Step 1: Identify your component's different visual states {/*step-1-identify-your-components-different-visual-states*/} + +In computer science, you may hear about a ["state machine"](https://en.wikipedia.org/wiki/Finite-state_machine) being in one of several “states”. If you work with a designer, you may have seen mockups for different "visual states". React stands at the intersection of design and computer science, so both of these ideas are sources of inspiration. + +First, you need to visualize all the different "states" of the UI the user might see: + +* **Empty**: Form has a disabled "Submit" button. +* **Typing**: Form has an enabled "Submit" button. +* **Submitting**: Form is completely disabled. Spinner is shown. +* **Success**: "Thank you" message is shown instead of a form. +* **Error**: Same as Typing state, but with an extra error message. + +Just like a designer, you'll want to "mock up" or create "mocks" for the different states before you add logic. For example, here is a mock for just the visual part of the form. This mock is controlled by a prop called `status` with a default value of `'empty'`: + + + +```js +export default function Form({ + status = 'empty' +}) { + if (status === 'success') { + return

      That's right!

      + } + return ( + <> +

      City quiz

      +

      + In which city is there a billboard that turns air into drinkable water? +

      +
      +