diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 8bb3625a5d9..00000000000 --- a/.babelrc +++ /dev/null @@ -1,20 +0,0 @@ -{ - "presets": [ - ["@babel/preset-react", {"modules": false}], - ["@babel/preset-env", {"modules": false}] - ], - "plugins": [ - "macros", - "add-react-displayname", - "babel-plugin-styled-components", - "@babel/plugin-proposal-object-rest-spread" - ], - "env": { - "test": { - "presets": [ - ["@babel/preset-react", {"modules": "commonjs"}], - ["@babel/preset-env", {"modules": "commonjs"}] - ] - } - } -} diff --git a/.eslintrc.json b/.eslintrc.json index b0be5232a9f..14bf0a82e7c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,6 +5,9 @@ "plugin:jsx-a11y/recommended", "plugin:react-hooks/recommended" ], + "globals": { + "__DEV__": "readonly" + }, "rules": { "import/no-namespace": 0, "no-shadow": 0, diff --git a/.gitignore b/.gitignore index 25b249621de..5d4a9095213 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ node_modules .DS_Store coverage/ dist/ +lib/ +lib-esm/ public/ +stats.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ecce2dd5fd3..b753be4cb7e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,14 +1,16 @@ # Contribution guidelines + 1. [Roadmap](#roadmap) 2. [Developing Components](#developing-components) * [Tools we use](#tools-we-use) * [Component patterns](#component-patterns) * [Adding default theme](#adding-default-theme) * [Adding system props](#adding-system-props) + * [Adding the sx prop](#adding-the-sx-prop) * [Linting](#linting) * [Testing](#testing) * [TypeScript support](#typescript-support) - * [Additonal resources](#additional-resources) + * [Additional resources](#additional-resources) 3. [Writing documentation](#writing-documentation) 4. [Creating a pull request](#creating-a-pull-request) * [What to expect after opening a pull request](#what-to-expect-after-opening-a-pull-request) @@ -22,8 +24,8 @@ * [System Props](#system-props) ## Roadmap -If you're looking for something to work on, a great place to start is our issues labeled [up for grabs](https://github.com/primer/components/issues?q=is%3Aopen+is%3Aissue+label%3A%22up+for+grabs%22)! If you've got a feature that you'd like to implement, be sure to check out our [Primer Components Roadmap](https://github.com/primer/components/projects/3) to make sure it hasn't already been started on. +If you're looking for something to work on, a great place to start is our issues labeled [up for grabs](https://github.com/primer/components/issues?q=is%3Aopen+is%3Aissue+label%3A%22up+for+grabs%22)! If you've got a feature that you'd like to implement, be sure to check out our [Primer Components Roadmap](https://github.com/primer/components/projects/3) to make sure it hasn't already been started on. ## Developing components @@ -31,7 +33,7 @@ We primarily use our documentation site as a workspace to develop new components To get the documentation site running locally run the following in your terminal: -``` +```sh yarn start ``` @@ -42,27 +44,31 @@ Navigate to http://localhost:8000/ to see the site in your browser ✨ 1. We use [styled-components](https://www.styled-components.com/) to style our components. 2. We use style functions from [styled-system](https://styled-system.com/) whenever possible, and styled-systems' `style()` function to create new ones. - ### Component patterns With a couple of exceptions, all components should be created with the `styled` function from [styled-components] and should have the appropriate groups of system props attached. Default values for system props can be set in `Component.defaultProps`. -Prop Types from system props such as `COMMON` or `TYPOGRAPHY` as well as styled-system functions can be spread in the component's prop types declaration (see example below). +Prop Types from system props such as `COMMON` or `TYPOGRAPHY` as well as styled-system functions can be spread in the component's prop types declaration (see example below). These need to go *after* any built-in styles that you want to be overridable. ⚠️ **Make sure to always set the default `theme` prop to our [theme](https://github.com/primer/components/blob/master/src/theme.js)! This allows consumers of our components to access our theme values without a ThemeProvider.** +Additionally, every component should support [the `sx` prop](https://primer.style/components/overriding-styles); remember to add `${sx}` to the style literal and `...sx.propTypes` to the component's `propTypes`. + Here's an example of a basic component written in the style of Primer Components: ```jsx import {TYPOGRAPHY, COMMON} from './constants' import theme from './theme' +import sx from './sx const Component = styled.div` - ${TYPOGRAPHY}; - ${COMMON}; // additional styles here + + ${COMMON}; + ${TYPOGRAPHY}; + ${sx}; ` Component.defaultProps = { @@ -73,13 +79,15 @@ Component.defaultProps = { Component.propTypes = { ...COMMON.propTypes, - ...TYPOGRAPHY.propTypes + ...TYPOGRAPHY.propTypes, + ...sx.propTypes } export default Component ``` ### Adding default theme + Each component needs access to our default Primer Theme, so that users of the component can access theme values easily in their consuming applications. To add the default theme to a component, import the theme and assign it to the component's defaultProps object: @@ -94,6 +102,7 @@ Component.defaultProps = { ``` ### Adding system props + Each component should have access to the appropriate system props. Every component has access to `COMMON`. For **most** components added, you'll only need to give the component to `COMMON`. If you are unsure, ping a DS team member on your PR. Categories of system props are exported from `src/constants.js`: @@ -101,8 +110,9 @@ Categories of system props are exported from `src/constants.js`: * `COMMON` includes color and spacing (margin and padding) props * `TYPOGRAPHY` includes font family, font weight, and line-height props * `POSITION` includes positioning props -* `FLEX_CONTAINER` includes flexbox props for containers -* `FLEX_ITEM` includes flexbox props for items in a flex container +* `FLEX` includes flexbox props +* `BORDER` includes border and box-shadow props +* `GRID` includes grid props To give the component access to a group of system props, import the system prop function from `./constants` and include the system prop function in your styled-component like so: @@ -110,8 +120,8 @@ To give the component access to a group of system props, import the system prop import {COMMON} from './constants' const Component = styled.div` - ${COMMON}; // additional styles here + ${COMMON}; ` // don't forget to also add it to your prop type declaration! @@ -121,17 +131,42 @@ Component.propTypes = { } ``` +Remember that the system prop function inside your style declaration needs to go *after* any built-in styles you want to be overridable. + +### Adding the `sx` prop + +Each component should provide access to a prop called `sx` that allows for setting theme-aware ad-hoc styles. See the [overriding styles](https://primer.style/components/overriding-styles) doc for more information on using the prop. + +Adding the `sx` prop is similar to adding system props; import the default export from the `sx` module, add it to your style definition, and add the appropriate prop types. **The `sx` prop should go at the *very end* of your style definition.** + +```jsx +import {COMMON} from './constants' +import sx from './sx' + +const Component = styled.div` + // additional styles here + ${COMMON}; + ${sx}; +` + +// don't forget to also add it to your prop type declaration! + +Component.propTypes = { + ...COMMON.propTypes, + ...sx.propTypes +} +``` + ### Linting We use the [React configuration](https://github.com/github/eslint-plugin-github/blob/master/lib/configs/react.js) from [GitHub's eslint plugin](https://github.com/github/eslint-plugin-github) to lint our JavaScript. To check your work before pushing, run: -``` +```sh yarn run lint ``` Or, you can use [npx] to run eslint on one or more specific files: - ```sh # lint the component and the tests in src/__tests__ npx eslint src/**/MyComponent.js @@ -149,7 +184,7 @@ yarn run lint -- --fix We test our components with [Jest](https://facebook.github.io/jest/) and [react-test-renderer](https://reactjs.org/docs/test-renderer.html). You can run the tests locally with `yarn test`. To run the tests as you work, run Jest in watch mode with: -``` +```sh yarn test -- --watch ``` @@ -159,15 +194,15 @@ See [`src/__tests__/example.js`](src/__tests__/example.js) for examples of ways Several of the projects that consume Primer Components are written in TypeScript. Though Primer Components is not currently written in TS, we do export type definitions in order to make Primer Components compatible with other TS projects. -Whenever adding new components or modifying the props of an existing component, **please make sure to update the type definition** in `index.d.ts`! This is super important to make sure we don't break compatibility :) +Whenever adding new components or modifying the props of an existing component, **please make sure to update the type definition** in `index.d.ts`! This is super important to make sure we don't break compatibility :) ### Additional resources -- [Primer Components Philosophy](https://primer.style/components/philosophy) -- [Primer Components Core Concepts](https://primer.style/components/core-concepts) -- [Primer Components System Props](https://primer.style/components/system-props) -- [Styled Components docs](https://styled-components.com/) -- [Styled System docs](https://styled-system.com/) +* [Primer Components Philosophy](https://primer.style/components/philosophy) +* [Primer Components Core Concepts](https://primer.style/components/core-concepts) +* [Primer Components System Props](https://primer.style/components/system-props) +* [Styled Components docs](https://styled-components.com/) +* [Styled System docs](https://styled-system.com/) ## Writing documentation @@ -180,37 +215,41 @@ To add a new component to our documentation site, create a new file with the `.m When creating a new pull request, please follow the guidelines in the auto-populated pull request template. Be sure to add screenshots of any relevant work and a thoughtful description. ### What to expect after opening a pull request -After opening a pull request, a member of the design systems team will add the appropriate labels (major, minor, patch release labels) and update the base branch to the correct release branch. Usually, you'll receive a response from the design systems team within a day or two. The design systems team member will review the pull request keeping the following items in mind: +After opening a pull request, a member of the design systems team will add the appropriate labels (major, minor, patch release labels) and update the base branch to the correct release branch. Usually, you'll receive a response from the design systems team within a day or two. The design systems team member will review the pull request keeping the following items in mind: ### What we look for in reviews -- If it's a new component, does the component make sense to add to Primer Components? (Ideally this is discussed before the pull request stage, please reach out to a DS member if you aren't sure if a component should be added to Primer Componets!) -- Does the component follow our [Primer Components code style](#component-patterns)? -- Does the component use theme values for most CSS values? -- Does the component have access to the [default theme](#adding-default-theme)? -- Does the component have the [correct system props implemented](#adding-system-props)? -- Is the component API intuitive? -- Does the component have the appropriate [type definitions in `index.d.ts`](#typescript-support)? -- Is the component documented accurately? -- Does the component have appropriate tests? -- Does the pull request increase the bundle size significantly? - -If everything looks great, the design systems team member will approve the pull request and merge when appropriate. Minor and patch changes are released frequently, and we try to bundle up breaking changes and avoid shipping major versions too often. If your pull request is time-senstive, please let a design systems team member know. You do not need to worry about merging pull requests on your own, we'll take care of that for you :) + +* If it's a new component, does the component make sense to add to Primer Components? (Ideally this is discussed before the pull request stage, please reach out to a DS member if you aren't sure if a component should be added to Primer Components!) +* Does the component follow our [Primer Components code style](#component-patterns)? +* Does the component use theme values for most CSS values? +* Does the component have access to the [default theme](#adding-default-theme)? +* Does the component have the [correct system props implemented](#adding-system-props)? +* Is the component API intuitive? +* Does the component have the appropriate [type definitions in `index.d.ts`](#typescript-support)? +* Is the component documented accurately? +* Does the component have appropriate tests? +* Does the pull request increase the bundle size significantly? + +If everything looks great, the design systems team member will approve the pull request and merge when appropriate. Minor and patch changes are released frequently, and we try to bundle up breaking changes and avoid shipping major versions too often. If your pull request is time-sensitive, please let a design systems team member know. You do not need to worry about merging pull requests on your own, we'll take care of that for you :) ## Deploying and publishing ### Deploying -All of our documentation sites use the [Now integration](https://github.com/organizations/primer/settings/installations/1007619) to deploy documentation changes whenever code is merged into master. The integration also creates a preview site every time you commit code to a branch. To view the preview site, navigate to the PR and find the comment from the `now` bot. This will include a link to the preview site for your branch. + +All of our documentation sites use the [Now integration](https://github.com/organizations/primer/settings/installations/1007619) to deploy documentation changes whenever code is merged into master. The integration also creates a preview site every time you commit code to a branch. To view the preview site, navigate to the PR and find the comment from the `now` bot. This will include a link to the preview site for your branch. Once you merge your branch into master, any changes to the docs will automatically deploy. No further action is necessary. ### Path aliasing + This site is served as a subdirectory of [primer.style] using a [path alias](https://zeit.co/docs/features/path-aliases) configured in that repo's [`rules.json`](https://github.com/primer/primer.style/tree/master/rules.json). If you change the production deployment URL for this app, you will also need to change it there and re-deploy that app; otherwise, Now will automatically route requests from [primer.style/components](https://primer.style/components/) to the new deployment whenever you alias this one to `primer-components.now.sh`. ### Publishing + We use a custom GitHub Actions to handle all of our processes relating to publishing to NPM. This includes release candidates, canary releases, and publishing the final release. -The [publish GitHub Action](https://github.com/primer/publish) will automatically publish a canary release for each commit to a branch. If the branch is prefixed with `release-` it will publish a release candidate. To find the canary release or release candidtate, navigate to the PR and find the `publish` check in the merge box. Clicking on the `details` link for the check will navigate you to the unpkg page for that canary release/release candidate. For more documentation on our publish GitHub Action and workflows, please refer to the [`@primer/publish` repo](https://github.com/primer/publish). +The [publish GitHub Action](https://github.com/primer/publish) will automatically publish a canary release for each commit to a branch. If the branch is prefixed with `release-` it will publish a release candidate. To find the canary release or release candidate, navigate to the PR and find the `publish` check in the merge box. Clicking on the `details` link for the check will navigate you to the unpkg page for that canary release/release candidate. For more documentation on our publish GitHub Action and workflows, please refer to the [`@primer/publish` repo](https://github.com/primer/publish). ## Troubleshooting @@ -225,6 +264,7 @@ Ensure you are using the latest minor of Node.js for the major version specified ## Glossary ### System props + System props are style functions that provide one or more props, and can be passed directly the return value of [styled-components]'s `styled()` function: ```jsx diff --git a/babel-defines.js b/babel-defines.js new file mode 100644 index 00000000000..63361e78474 --- /dev/null +++ b/babel-defines.js @@ -0,0 +1,13 @@ +const shared = { + __DEV__: "process.env.NODE_ENV !== 'production'" +} + +module.exports = { + development: shared, + test: shared, + production: { + ...shared, + __DEV__: 'false', + 'process.env.NODE_ENV': "'production'" + } +} diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 00000000000..0643f28d328 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,39 @@ +const defines = require('./babel-defines') + +function replacementPlugin(env) { + return ['babel-plugin-transform-replace-expressions', {replace: defines[env]}] +} + +const sharedPlugins = [ + 'macros', + 'preval', + 'add-react-displayname', + 'babel-plugin-styled-components', + '@babel/plugin-proposal-object-rest-spread' +] + +const runtimePlugins = [['@babel/plugin-transform-runtime', {version: '7.9.2', helpers: true}]] + +function makePresets(moduleValue) { + return [ + ['@babel/preset-react', {modules: moduleValue}], + ['@babel/preset-env', {modules: moduleValue}] + ] +} + +module.exports = { + env: { + development: { + presets: makePresets(process.env.BABEL_MODULE || false), + plugins: [...sharedPlugins, ...runtimePlugins, replacementPlugin('development')] + }, + production: { + presets: makePresets(false), + plugins: [...sharedPlugins, ...runtimePlugins, replacementPlugin('production')] + }, + test: { + presets: makePresets('commonjs'), + plugins: [...sharedPlugins, replacementPlugin('test')] + } + } +} diff --git a/css.js b/css.js deleted file mode 100644 index f6ad7185983..00000000000 --- a/css.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/css') diff --git a/docs/content/BorderBox.md b/docs/content/BorderBox.md index 287115e210f..2a72369a085 100644 --- a/docs/content/BorderBox.md +++ b/docs/content/BorderBox.md @@ -11,15 +11,18 @@ BorderBox is a Box component with a border. When no `borderColor` is present, th This is a BorderBox ``` +Note that `BorderBox` has default props set for `borderWidth`, `borderStyle`, and `borderColor`. This means that you cannot use `border={0} borderBottom={1}` or similar patterns; instead, use individual properties like `borderWidth={0} borderBottomWidth={1}`. + ## System props -BorderBox components get `COMMON`, `LAYOUT` and `BORDER` system props. Read our [System Props](/system-props) doc page for a full list of available props. +BorderBox components get `COMMON`, `LAYOUT`, `BORDER`, and `FLEX` system props. Read our [System Props](/system-props) doc page for a full list of available props. ## Component props | Prop name | Type | Default | Description | | :- | :- | :-: | :- | -| border | String | 'borders.1' (from theme) | Sets the border, use theme values or provide your own. | +| borderWidth | String | '1px' | Sets the border, use theme values or provide your own. | +| borderStyle | String | 'solid' | Sets the border style, use theme values or provide your own. | | borderColor | String | 'gray.2' (from theme) | Sets the border color, use theme values or provide your own. | -| borderRadius | String or Number| 'radii.1' (from theme)| Sets the border radius, use theme values or provide your own. | +| borderRadius | String or Number| 2 (from theme)| Sets the border radius, use theme values or provide your own. | | boxShadow | String | | Sets box shadow, use theme values or provide your own. | diff --git a/docs/content/Box.md b/docs/content/Box.md index e101a340098..ec0d92d401b 100644 --- a/docs/content/Box.md +++ b/docs/content/Box.md @@ -18,7 +18,7 @@ The Box component serves as a wrapper component for most layout related needs. U ## System props -Box components get the `COMMON` and `LAYOUT` categories of system props. Read our [System Props](/system-props) doc page for a full list of available props. +Box components get the `COMMON`, `LAYOUT`, and `FLEX` categories of system props. Read our [System Props](/system-props) doc page for a full list of available props. ## Component props diff --git a/docs/content/FilteredSearch.md b/docs/content/FilteredSearch.md index 471c706f36d..1eb8e3a81f9 100644 --- a/docs/content/FilteredSearch.md +++ b/docs/content/FilteredSearch.md @@ -9,7 +9,8 @@ The FilteredSearch component helps style a Dropdown and a TextInput side-by-side ```jsx live - + + Filter Item 1 Item 2 diff --git a/docs/content/Flash.md b/docs/content/Flash.md index fd07e7421b3..a799c7b1274 100644 --- a/docs/content/Flash.md +++ b/docs/content/Flash.md @@ -7,7 +7,21 @@ The Flash component informs users of successful or pending actions. ## Default example ```jsx live - Flash green +Default Flash +Success Flash +Warning Flash +Danger Flash +``` + +## With an icon + +Flash components with icons inside of them will automatically set the correct color for the icon depending on the type of Flash, as well as applying the correct right margin. + +```jsx live + + + Success! + ``` ## System props @@ -19,4 +33,4 @@ Flash components get `COMMON` system props. Read our [System Props](/system-prop | Name | Type | Default | Description | | :- | :- | :-: | :- | | full | Boolean | | Creates a full width Flash component| -| scheme | String | blue | Can be one of `green`, `yellow`, or `red` - sets the background color, border, and text color of the Flash component +| variant | String | default | Can be one of `default`, `success`, `warning`, or `danger` - sets the background color and border of the Flash component | diff --git a/docs/content/Flex.md b/docs/content/Flex.md index a93087d9d88..245f917151a 100644 --- a/docs/content/Flex.md +++ b/docs/content/Flex.md @@ -2,9 +2,9 @@ title: Flex --- -Flex is a component that will allow you to use flexbox properties. +The `Flex` component behaves the same as the `Box` component except that it has `display: flex` set by default. -*Previously, `Flex.Item` was used for flex item specific properties - we've added all properties to the `Flex` component, but are keeping Flex.Item around for backwards compatibility.* +*Previously, a `Flex.Item` component was used for flex item specific properties; `Box` now contains all those properties and should be used in its place.* ## Default example @@ -28,7 +28,6 @@ Flex is a component that will allow you to use flexbox properties. Flex components get `FLEX`, `COMMON`, and `LAYOUT` system props. - Read our [System Props](/system-props) doc page for a full list of available props. ## Component props diff --git a/docs/content/PointerBox.md b/docs/content/PointerBox.md index 1f0b4b9f7b2..070ccb4a5b0 100644 --- a/docs/content/PointerBox.md +++ b/docs/content/PointerBox.md @@ -14,7 +14,7 @@ PointerBox is a [BorderBox](./BorderBox) component with a caret added to it. ## System props -PointerBox components get `LAYOUT` and `COMMON` system props. Read our [System Props](/system-props) doc page for a full list of available props. +PointerBox components get `COMMON`, `LAYOUT`, `BORDER`, and `FLEX` system props. Read our [System Props](/system-props) doc page for a full list of available props. ## Component props diff --git a/docs/content/Popover.md b/docs/content/Popover.md index 29f20a76ad2..2e412dcd1eb 100644 --- a/docs/content/Popover.md +++ b/docs/content/Popover.md @@ -86,7 +86,7 @@ render() ## System props -`Popover` components get `COMMON`, `LAYOUT`, and `POSITION` system props. `Popover.Content` components get `COMMON`, `LAYOUT`, and `BORDER` system props. Read our [System Props](/system-props) doc page for a full list of available props. +`Popover` components get `COMMON`, `LAYOUT`, and `POSITION` system props. `Popover.Content` components get `COMMON`, `LAYOUT`, `BORDER`, and `FLEX` system props. Read our [System Props](/system-props) doc page for a full list of available props. ## Component props diff --git a/docs/content/Position.md b/docs/content/Position.md index 94062cad059..23e5af9739b 100644 --- a/docs/content/Position.md +++ b/docs/content/Position.md @@ -41,7 +41,7 @@ The Position component is a wrapper component that gives the containing componen ## System props -Position components get `POSITION`, `LAYOUT` and `COMMON` system props. Read our [System Props](/system-props) doc page for a full list of available props. +Position components get `POSITION`, `LAYOUT`, `FLEX, and `COMMON` system props. Read our [System Props](/system-props) doc page for a full list of available props. ## Component props diff --git a/docs/content/SelectMenu.md b/docs/content/SelectMenu.md index fd3d009b130..8f0abe3db7f 100644 --- a/docs/content/SelectMenu.md +++ b/docs/content/SelectMenu.md @@ -86,12 +86,36 @@ Used to wrap the content in a `SelectMenu`. ``` +### Right-aligned modal + +Use the `align='right'` prop to align the modal to the right. Note that this only modifies alignment for the modal, and not the SelectMenu itself. You will need to wrap the SelectMenu in a relatively positioned element for this to work properly. + +```jsx live + + + + + Projects + + Primer Components bugs + Primer Components roadmap + Project 3 + Project 4 + + + + +``` + ### System Props SelectMenu.Modal components get `COMMON` system props. Read our [System Props](/system-props) doc page for a full list of available props. ### Component Props -SelectMenu.Modal components do not get any additional props besides system props. + +| Prop name | Type | Default | Description | +| :-------- | :----- | :------ | ------------------------------------------------- | +| align | String | 'left' | Use `right` to align the select menu to the right | ## SelectMenu.List @@ -114,10 +138,12 @@ SelectMenu.List components do not get any additional props besides system props. ## SelectMenu.Item -Individual items in a select menu. +Individual items in a select menu. SelectMenu.Item renders an anchor tag by default, you'll need to make sure to provide the appropriate `href`. + +You can use a `button` tag instead by utilizing the [`as` prop](/core-concepts#the-as-prop). Be sure to consider [which HTML element is the right choice](https://marcysutton.com/links-vs-buttons-in-modern-web-applications) for your usage of the component. ```jsx - + {/* wraps an individual list item*/} ``` diff --git a/docs/content/SideNav.md b/docs/content/SideNav.md index 61cc1994180..40dc70e461b 100644 --- a/docs/content/SideNav.md +++ b/docs/content/SideNav.md @@ -71,7 +71,7 @@ Add the `variant="lightweight"` prop to `SideNav` to render an alternative, more ```jsx live - + Menu @@ -133,7 +133,7 @@ If using React Router, you can use the `as` prop to render the element as a `Nav ## System props -`SideNav` components get `COMMON`, `BORDER`, and `LAYOUT` system props. `SideNav.Link` components get `COMMON` and `TYPOGRAPHY` system props. Read our [System Props](/system-props) doc page for a full list of available props. +`SideNav` components get `COMMON`, `BORDER`, `LAYOUT`, and `FLEX` system props. `SideNav.Link` components get `COMMON` and `TYPOGRAPHY` system props. Read our [System Props](/system-props) doc page for a full list of available props. ## Component props diff --git a/docs/content/StateLabel.md b/docs/content/StateLabel.md index 4c09d09c125..19b0a6d68c0 100644 --- a/docs/content/StateLabel.md +++ b/docs/content/StateLabel.md @@ -6,7 +6,12 @@ Use StateLabel components to show the status of an issue or pull request. ## Default example ```jsx live - Open +Open +Closed +Open +Closed +Merged +Draft ``` ## System props @@ -17,5 +22,5 @@ StateLabel components get `COMMON` system props. Read our [System Props](/system | Name | Type | Default | Description | | :- | :- | :-: | :- | -| small | Boolean | | Used to create a smaller version of the default StateLabel | -| status | String | | Can be one of `issueOpened`, `issueClosed`, `pullOpened`, `pullClosed` or `pullMerged`. +| variant | String | 'normal' | a value of `small` or `normal` results in a smaller or larger version of the StateLabel. | +| status | String | | Can be one of `issueOpened`, `issueClosed`, `pullOpened`, `pullClosed`, `pullMerged`, or `draft`. diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md index 3a797d99ba5..7e5a5fabd22 100644 --- a/docs/content/getting-started.md +++ b/docs/content/getting-started.md @@ -4,38 +4,55 @@ title: Getting Started ## Installation -To get started using Primer Components, run `npm install @primer/components` in your application. +To get started using Primer Components, install the package and its peer dependencies with your package manager of choice: -You can now start importing Primer Components! There are a few ways to import Primer Components. You can either: - -Import them individually from the main bundle: +```bash +# with npm +npm install @primer/components react react-dom styled-components +# with yarn +yarn add @primer/components react react-dom styled-components ``` + +You can now import Primer Components from the main package module: + +```javascript +// using import syntax import {Box, Flex} from '@primer/components' + +// using require syntax +const {Box, Flex} = require('@primer/components') ``` -or, if you've configured your application to tree-shake with webpack, you can import them individually from the `src` folder: +Alternatively, you can import individual components from the `lib` subfolder: -``` -import Box from '@primer/components/src/Box' -import Flex from '@primer/components/src/Flex' +```javascript +// using import syntax +import Box from '@primer/components/lib/Box' +import Flex from '@primer/components/lib/Flex' +// using require syntax +const Box = require('@primer/components/lib/Box') +const Flex = require('@primer/components/lib/Flex') ``` +Note that the files in the `lib` folder are CommonJS-style modules; if you're using ECMAScript modules (ESM) and a compatible module bundler, importing files individually from `lib` provides no benefit, as unused modules from the ESM build will be removed via tree-shaking. Primer Components has an ESM build specified in its `package.json`, so when using ESM you should always import components from the main entry point (as in the first example, above). -## Installing Peer Dependencies +If you're *not* using ESM, importing components individually from the `lib` folder may drastically decrease your final bundle size, since your module bundler will not have to include the entirety of Primer Components in your bundle. -Primer Components is shipped with a few libraries labeled as peer dependencies. These libraries are separated because they are commonly already installed in the host project. This keeps the bundle size down and allows you to specify the version number you'd like in your own project. +### Peer dependencies -Before getting started using Primer Components, make sure that the following libraries are installed in your host project: +Primer Components ships with a few libraries labeled as peer dependencies. These libraries are separated because they are commonly already installed in the host project and having multiple versions can introduce errors. -- `styled-components` at version 4.0.0 or higher -- `react` at versions 16.8.0 and higher +Primer Components requires the following libraries to be installed along with it: +- `styled-components` at version 4.0.0 or higher +- `react` at versions 16.8.0 or higher +- `react-dom` at versions 16.8.0 or higher ## BaseStyles -In order to set basic color, font-family, and line-heights across your project, you will need to establish base Primer styles for your app by wrapping all of your Primer components in ``: +In order to set baseline color, font-family, and line-heights across your project, you will need to establish base Primer styles for your app by wrapping all of your Primer components in `` at the root of your app: ```jsx import {BaseStyles, Box, Heading} from '@primer/components' @@ -51,3 +68,88 @@ export default const MyApp = () => ( ``` This will apply the same `color`, `font-family`, and `line-height` styles to the `` as [Primer CSS's base styles](https://github.com/primer/css/blob/master/src/base/base.scss#L15-L20). + +## Theming + +Components are styled using Primer's [theme](https://github.com/primer/components/blob/master/src/theme.js) by default, but you can provide your own theme by using [styled-component's](https://styled-components.com/) ``. If you'd like to fully replace the Primer [theme](https://github.com/primer/components/blob/master/src/theme.js) with your custom theme, pass your theme to the `` in the root of your application like so: + +```jsx +import {ThemeProvider} from 'styled-components' + +const theme = { ... } + +const App = (props) => { + return ( +
+ +
your app here
+
+
+ ) +} +``` + +If you'd like to merge the Primer theme with your theme, you can do so by importing the Primer theme and then merging the themes using a library like [deepmerge](https://www.npmjs.com/package/deepmerge): + +```jsx +import {ThemeProvider} from 'styled-components' +import {theme} from '@primer/components' +import deepmerge from 'deepmerge' + +const customTheme = { ... } +const newTheme = deepmerge(theme, customTheme, { + // overwrite arrays instead of concatenating + arrayMerge: (_dest, source) => source +}) + + +const App = (props) => { + return ( +
+ +
your app here
+
+
+ ) +} +``` + +Note that using `Object.assign` to merge themes will only create a shallow merge. This means that if both themes have a `color` object, the _entire_ `color` object will be replaced with the new `color` object, instead of only replacing duplicate values from the original theme's color object. If you want to merge sub-values, be sure to use a deep-merging strategy. + +## Static CSS rendering + +If you're rendering React components both server- and client-side, we suggest following [styled-component's server-side rendering instructions](https://www.styled-components.com/docs/advanced#server-side-rendering) to avoid the flash of unstyled content for server-rendered components. + +## TypeScript + +Primer Components includes TypeScript support and ships with its own typings. You will need still need to to install type typings for the peer dependencies if you import those in your own application code. + +Once installed, you can import components and their prop type interfaces from the `@primer/components` package: + +```typescript +import {BorderBox, BorderBoxProps} from '@primer/components' +``` + +### Fixing "Duplicate identifier 'FormData'" + +Ever since `@types/styled-components` version `14.1.19`, it has had a dependency on both `@types/react` and `@types/react-native`. Unfortunately, those declarations clash; for more information, see [issue 33311](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/33311) and [issue 33015](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/33015) in the DefinitelyTyped repo. + +You may run into this conflict even if you're not importing anything from `react-native` or don't have it installed. This is because some package managers hoist packages to the top-level `node_modules` folder, and the TypeScript compiler automatically includes types from all folders in `node_modules/@types` by default. + +The TypeScript compiler allows you to opt-out of this behavior [using the `typeRoots` and `types` configuration options](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#types-typeroots-and-types), and the best solution for this error — for now — seems to be to opt out the automatic inclusion of `node_modules/@types` and instead list the types you want to be included individually. + +In your `tsconfig.json`, set the `types` array under the `compilerOptions` like so: + +```json +{ + "compilerOptions": { + "types": ["node", "react", "styled-components", "jest"] + } +} +``` + +Of course, customize the array based on the `@types/` packages you have installed for your project. + +## Silencing warnings + +Like React, Primer Components emits warnings to the JavaScript console under certain conditions, like using deprecated components or props. Similar to React, you can silence these warnings by setting the `NODE_ENV` environment variable to `production` during bundling. diff --git a/docs/content/index.md b/docs/content/index.md new file mode 100644 index 00000000000..80971cb6f09 --- /dev/null +++ b/docs/content/index.md @@ -0,0 +1,33 @@ +--- +title: Getting Started +--- + +import {HeroLayout} from '@primer/gatsby-theme-doctocat' +export default HeroLayout + +## Primer Components + +Primer Components is a React implementation of GitHub's [Primer Design System](https://primer.style/) 🎉 + +## Principles + +* Everything is a component. +* Aim for total style encapsulation; don't rely on inheritance to provide default styles. +* Build small building blocks with minimal props to keep complexity low. +* Keep system constrained by only including props needed per component. +* Favor wrapping or extending components for more complex operations. +* Maintain design system consistency with utilities as props (for spacing, color, font-size, line-height, widths, and radii). + +## Getting started + +Check out [our getting started guide](/getting-started) for everything you need to know about installing and using Primer Components. + +## Local development + +To run `@primer/components` locally when adding or updating components: + +1. Clone this repo: `git clone https://github.com/primer/components` +2. Install dependencies: `yarn` +3. Run the dev app: `yarn start` + +> 👉 See [the contributing docs](https://github.com/primer/components/blob/master/CONTRIBUTING.md) for more info on code style, testing, and coverage. diff --git a/docs/content/index.mdx b/docs/content/index.mdx deleted file mode 100644 index c5f8244fcdd..00000000000 --- a/docs/content/index.mdx +++ /dev/null @@ -1,130 +0,0 @@ ---- -title: Primer Components ---- - -import {HeroLayout} from '@primer/gatsby-theme-doctocat' -export default HeroLayout - - -## What are Primer Components? - -Primer Components are React components which implement GitHub's Primer Design System 🎉 - - -## Installation - -Install `@primer/components` in your project with your package manager of choice: - -``` -// yarn -yarn add @primer/components - -//npm -npm i @primer/components -``` - -## Usage - -All of our components are exported by name from `@primer/components`, so you can import them with: - -```js -import { - Box, - Button, - Heading, - Text -} from '@primer/components' -``` - -Primer Components come with all the necessary CSS built-in, so you don't need to worry about including [Primer CSS]. - -#### Base styles - -You can establish base Primer styles for your app by wrapping all of your Primer components in ``: - -```jsx -import {BaseStyles, Box, Heading} from '@primer/components' - -export default () => ( - - - Hello, world! -

This will get Primer text styles.

-
-
-) -``` - -This will set the `color`, `font-family`, and `line-height` CSS properties to the same ones used [to style `` in Primer CSS](https://github.com/primer/css/blob/master/src/base/base.scss#L15-L20). - -#### Theming - -Components are styled using Primer's [theme](https://github.com/primer/components/blob/master/src/theme.js) by default, but you can provide your own theme by using [styled-component's][styled-components] ``. If you'd like to fully replace the Primer [theme](https://github.com/primer/components/blob/master/src/theme.js) with your custom theme, pass your theme to the `` in the root of your application like so: - -```jsx -import {ThemeProvider} from 'styled-components' - -const theme = { ... } - -const App = (props) => { - return ( -
- -
your app here
-
-
- ) -} - -``` - -If you'd like to merge the Primer theme with your theme, you can do so importing the Primer theme and merging using Object.assign: - -```jsx -import {ThemeProvider} from 'styled-components' -import {theme} from '@primer/components' - -const customTheme = { ... } - - -const App = (props) => { - return ( -
- // matching keys in customTheme will override keys in the Primer theme -
your app here
-
-
- ) -} -``` - -*Note that using `Object.assign` will only create a shallow merge. This means that if both themes have a `color` object, the _entire_ `color` object will be replaced with the new `color` object, instead of only replacing duplicate values from the original theme's color object. - -#### Static CSS rendering - -If you're rendering React components both server-side _and_ client-side, we suggest following [styled-component's server-side rendering instructions](https://www.styled-components.com/docs/advanced#server-side-rendering) to avoid the flash of unstyled content for server-rendered components. - -## Local Development - -To run `@primer/components` locally when adding or updating components: - -1. Clone this repo: `git clone https://github.com/primer/components` -1. Install dependencies: `yarn` -1. Run the dev app: `yarn start` - -> 👉 See [the contributing docs](https://github.com/primer/components/blob/master/CONTRIBUTING.md) for more info on code style, testing, and coverage. - - -## Principles - -- Everything is a component. -- Aim for total style encapsulation; don't rely on inheritance to provide default styles. -- Build small building blocks with minimal props to keep complexity low. -- Keep system constrained by only including props needed per component. -- Favor extending or wrapping components for more complex operations. -- Maintain design system consistency with utilities as props (for spacing, color, font-size, line-height, widths, and radii). - - -[styled-components]: https://www.styled-components.com/docs -[Primer CSS]: https://github.com/primer/primer -[flash of unstyled content]: https://en.wikipedia.org/wiki/Flash_of_unstyled_content diff --git a/docs/content/overriding-styles.mdx b/docs/content/overriding-styles.mdx new file mode 100644 index 00000000000..5d227a4c7ff --- /dev/null +++ b/docs/content/overriding-styles.mdx @@ -0,0 +1,73 @@ +--- +title: Overriding styles with the sx prop +--- + +Our goal with Primer Components is to hit the sweet spot between providing too little and too much styling flexibility; too little and the design system is too rigid, and too much and it becomes too difficult to maintain a consistent style. Our components already support a standard set of [system props](/system-props), but sometimes a component just isn't *quite* flexible enough to look the way you need it to look. For those cases, we provide the `sx` prop. + +The `sx` prop allows ad-hoc styling that is still theme aware. Declare the styles you want to apply in camel-cased object notation, and try to use theme values in appropriate CSS properties when possible. If you've passed a custom theme using `ThemeProvider` or a `theme` prop, the `sx` prop will honor the custom theme. For more information on theming in Primer Components, check out [the Primer Theme documentation](/primer-theme). + +## When to use the `sx` prop + +The `sx` prop provides a lot of power, which means it is an easy tool to abuse. To best make use of it, we recommend following these guidelines: + +* Use the `sx` prop for small stylistic changes to components. For more substantial changes, consider abstracting your style changes into your own wrapper component. +* Use [system props](/system-props) instead of the `sx` prop whenever possible. +* Avoid nesting and pseudo-selectors in `sx` prop values when possible. + +## Basic example + +This example demonstrates applying a bottom border to `Heading`, a component that does not receive `BORDER` system props. The `borderBottomWidth` value comes from `theme.borderWidths` and `borderBottomColor` comes from `theme.colors`. + +```jsx live +Heading + + + Heading with bottom border + +``` + +## Responsive values + +Just like [values passed to system props](https://styled-system.com/responsive-styles), values in the `sx` prop can be provided as arrays to provide responsive styling. + +```jsx live + + Responsive background color + +``` + +## Nesting, pseudo-classes, and pseudo-elements + +The `sx` prop also allows for declaring styles based on media queries, psueudo-classes, and pseudo-elements. This example, though contrived, demonstrates the ability: + +```jsx live + *': { + borderWidth: 1, + borderColor: 'border.gray', + borderStyle: 'solid', + borderBottomWidth: 0, + padding: 2, + ':last-child': { + borderBottomWidth: 1 + }, + ':hover': { + bg: 'gray.1' + } + } +}}> + First + Second + Third + +``` diff --git a/docs/content/primer-theme.md b/docs/content/primer-theme.md index d29173ce227..0a5e58a0a9a 100644 --- a/docs/content/primer-theme.md +++ b/docs/content/primer-theme.md @@ -4,25 +4,23 @@ title: Primer Theme import {theme} from '@primer/components' -Primer Components come with built-in access to our Primer theme. The [theme file](https://github.com/primer/components/blob/master/src/theme.js) contains an object which holds values -for common variables such as color, fonts, box shadows, and more. Our theme file pulls many of its color and typography values from [primer-primitives](https://github.com/primer/primer-primitives). +Primer Components come with built-in access to our Primer theme. The [theme file](https://github.com/primer/components/blob/master/src/theme.js) contains an object which holds values for common variables such as color, fonts, box shadows, and more. Our theme file pulls many of its color and typography values from [primer-primitives](https://github.com/primer/primer-primitives). -Many of our theme keys correspond to system props on our components. For example, if you'd like to set the max width on a `` set the `maxWidth` prop to `medium`: -`` +Many of our theme keys correspond to system props on our components. For example, if you'd like to set the max width on a `` set the `maxWidth` prop to `medium`: `` -In the background, [styled-system](https://github.com/jxnblk/styled-system) does the work of finding the `medium` value from `maxWidth` key in the theme file and applying the corresponding CSS. +In the background, [styled-system](https://github.com/styled-system/styled-system) does the work of finding the `medium` value from `maxWidth` key in the theme file and applying the corresponding CSS. Our full theme can be found [here](https://github.com/primer/components/blob/master/src/theme.js). - ### Custom Theming + Custom theming is an optional way to override the Primer values that control color, spacing, typography, and other aspects of our components. There are two ways to change the theme of Primer components: 1. You can override the entire theme for an entire tree of components using the `` from [styled-components]: - ```jsx + ```javascript import {Block, Button, Text, theme as primer} from '@primer/components' import {ThemeProvider} from 'styled-components' @@ -46,29 +44,29 @@ There are two ways to change the theme of Primer components: ``` **⚠️ Note: [styled-components]'s `` only allows exactly one child.** -2. You can merge the Primer theme with your custom theme using Object.assign: -```jsx -import {ThemeProvider} from `styled-components` -import {theme} from '@primer/components' +2. You can merge the Primer theme with your custom theme using Object.assign: -const customTheme = { ... } + ```javascript + import {ThemeProvider} from `styled-components` + import {theme} from '@primer/components' + const customTheme = { ... } -const App = (props) => { - return ( -
- // matching keys in customTheme will override keys in the Primer theme -
your app here
-
-
- ) -} -``` + const App = (props) => { + return ( +
+ // matching keys in customTheme will override keys in the Primer theme +
your app here
+
+
+ ) + } + ``` 3. You can theme individual components by passing the `theme` prop directly: - ```jsx + ```javascript import {Text} from '@primer/components' const theme = { @@ -84,7 +82,6 @@ const App = (props) => { **☝️ This is an intentionally convoluted example, since you can use `` out of the box.** - Read the [styled-system docs](https://styled-system.com/#theming) for more information on theming in styled-system. [styled-components]: https://styled-components.com/ diff --git a/docs/gatsby-config.js b/docs/gatsby-config.js index 7262fd12997..5354f19e60c 100644 --- a/docs/gatsby-config.js +++ b/docs/gatsby-config.js @@ -18,7 +18,7 @@ module.exports = { options: { alias: { '@primer/components': path.resolve(__dirname, '../src'), - 'styled-components': path.resolve(__dirname, 'node_modules', 'styled-components'), + 'styled-components': path.resolve(__dirname, '..', 'node_modules', 'styled-components'), 'react': path.resolve(__dirname, 'node_modules', 'react'), } } diff --git a/docs/gatsby-node.js b/docs/gatsby-node.js new file mode 100644 index 00000000000..1cb066c3883 --- /dev/null +++ b/docs/gatsby-node.js @@ -0,0 +1,23 @@ +const defines = require('../babel-defines') + +exports.onCreateWebpackConfig = ({actions, plugins, loaders, getConfig}) => { + const config = getConfig() + // Add our `__DEV__` and `process.env.NODE_ENV` defines + config.plugins.push(plugins.define(defines[process.env.NODE_ENV || 'development'])) + + config.module.rules = [ + // Remove the original rule that compiles `.js`. and `.jsx` files... + ...config.module.rules.filter(rule => String(rule.test) !== String(/\.jsx?$/)), + // ...and replace it with a custom configuration. + { + // The new configuration is based off the original... + ...loaders.js(), + test: /\.jsx?$/, + exclude: modulePath => /node_modules/.test(modulePath), + // ...except that we want to run Primer Components through webpack as well. + // By default, Gatsby won't use the define plugin we added above on Primer Components. + include: modulePath => /@primer\/components/.test(modulePath) + } + ] + actions.replaceWebpackConfig(config) +} diff --git a/docs/package.json b/docs/package.json index 86ae05a85e0..fb214875db8 100644 --- a/docs/package.json +++ b/docs/package.json @@ -5,7 +5,7 @@ "scripts": { "clean": "gatsby clean", "develop": "gatsby develop", - "build": "cd .. && yarn && cd docs && gatsby build --prefix-paths" + "build": "cd .. && yarn && cd docs && cross-env NODE_ENV=production gatsby build --prefix-paths" }, "engines": { "node": ">= 10.x" @@ -23,6 +23,7 @@ "styled-system": "^5.1.0" }, "devDependencies": { + "cross-env": "7.0.2", "minipass": "^2.9.0" } -} \ No newline at end of file +} diff --git a/docs/src/@primer/gatsby-theme-doctocat/components/code.js b/docs/src/@primer/gatsby-theme-doctocat/components/code.js new file mode 100644 index 00000000000..f9995e5036e --- /dev/null +++ b/docs/src/@primer/gatsby-theme-doctocat/components/code.js @@ -0,0 +1,48 @@ +import {Absolute, BorderBox, Relative, Text} from '@primer/components' +import Highlight, {defaultProps} from 'prism-react-renderer' +import githubTheme from 'prism-react-renderer/themes/github' +import React from 'react' +import ClipboardCopy from '@primer/gatsby-theme-doctocat/src/components/clipboard-copy' +import LiveCode from '@primer/gatsby-theme-doctocat/src/components/live-code' + +function Code({className, children, live, noinline}) { + const language = className ? className.replace(/language-/, '') : '' + const code = children.trim() + + if (live) { + return + } + + return ( + + + + + + {({className, style, tokens, getLineProps, getTokenProps}) => ( + + {tokens.map((line, i) => ( + // eslint-disable-next-line react/no-array-index-key +
+ {line.map((token, key) => ( + // eslint-disable-next-line react/no-array-index-key + + ))} +
+ ))} +
+ )} +
+
+ ) +} + +export default Code diff --git a/docs/src/@primer/gatsby-theme-doctocat/components/layout.js b/docs/src/@primer/gatsby-theme-doctocat/components/layout.js new file mode 100644 index 00000000000..18c4d1b9a02 --- /dev/null +++ b/docs/src/@primer/gatsby-theme-doctocat/components/layout.js @@ -0,0 +1,88 @@ +import {BorderBox, Box, Flex, Grid, Heading, Position, Text, Details, StyledOcticon} from '@primer/components' +import {ChevronDown, ChevronRight} from '@primer/octicons-react' +import React from 'react' +import Head from '@primer/gatsby-theme-doctocat/src/components/head' +import Header, {HEADER_HEIGHT} from '@primer/gatsby-theme-doctocat/src/components/header' +import PageFooter from '@primer/gatsby-theme-doctocat/src/components/page-footer' +import Sidebar from '@primer/gatsby-theme-doctocat/src/components/sidebar' +import SourceLink from '@primer/gatsby-theme-doctocat/src/components/source-link' +import StatusLabel from '@primer/gatsby-theme-doctocat/src/components/status-label' +import TableOfContents from '@primer/gatsby-theme-doctocat/src/components/table-of-contents' + +function Layout({children, pageContext}) { + const {title, description, status, source, additionalContributors = []} = pageContext.frontmatter + + return ( + + +
+ + + + + + + {title} + + {pageContext.tableOfContents.items ? ( + + + Table of contents + + + + ) : null} + + {status || source ? ( + + {status ? : null} + + {source ? : null} + + ) : null} + {pageContext.tableOfContents.items ? ( + +
+ {({open}) => ( + <> + + + Table of contents + + + + + + )} +
+
+ ) : null} + {children} + ({login})))} + /> +
+
+
+ + ) +} + +export default Layout diff --git a/docs/src/@primer/gatsby-theme-doctocat/components/nav-drawer.js b/docs/src/@primer/gatsby-theme-doctocat/components/nav-drawer.js new file mode 100644 index 00000000000..5703f3685aa --- /dev/null +++ b/docs/src/@primer/gatsby-theme-doctocat/components/nav-drawer.js @@ -0,0 +1,131 @@ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +// +// ^- these area here because Doctocat has eslint errors that are not reported +// in Doctocat but are in this project. Since the error exists in an area +// of JSX code where the disable-line comment does not work, we must disable +// it for the whole file until the problem is fixed upstream. +import {BorderBox, Flex, Link, StyledOcticon, Text} from '@primer/components' +import {ChevronDown, ChevronUp, X} from '@primer/octicons-react' +import {Link as GatsbyLink} from 'gatsby' +import debounce from 'lodash.debounce' +import React from 'react' +import navItems from '@primer/gatsby-theme-doctocat/src/nav.yml' +import primerNavItems from '@primer/gatsby-theme-doctocat/src/primer-nav.yml' +import useSiteMetadata from '@primer/gatsby-theme-doctocat/src/use-site-metadata' +import DarkButton from '@primer/gatsby-theme-doctocat/src/components/dark-button' +import Details from '@primer/gatsby-theme-doctocat/src/components/details' +import Drawer from '@primer/gatsby-theme-doctocat/src/components/drawer' +import NavItems from '@primer/gatsby-theme-doctocat/src/components/nav-items' + +export function useNavDrawerState(breakpoint) { + // Handle string values from themes with units at the end + if (typeof breakpoint === 'string') { + breakpoint = parseInt(breakpoint, 10) + } + const [isOpen, setOpen] = React.useState(false) + + const onResize = React.useCallback(() => { + if (window.innerWidth >= breakpoint) { + setOpen(false) + } + }, [setOpen, breakpoint]) + + const debouncedOnResize = React.useCallback(debounce(onResize, 250), [onResize]) + + React.useEffect(() => { + if (isOpen) { + window.addEventListener('resize', debouncedOnResize) + return () => { + // cancel any debounced invocation of the resize handler + debouncedOnResize.cancel() + window.removeEventListener('resize', debouncedOnResize) + } + } + }, [isOpen, debouncedOnResize]) + + return [isOpen, setOpen] +} + +function NavDrawer({isOpen, onDismiss}) { + const siteMetadata = useSiteMetadata() + return ( + + + + + + + Primer + + + + + + + + + + + {navItems.length > 0 ? ( + + + {siteMetadata.title} + + + + ) : null} + + + ) +} + +function PrimerNavItems({items}) { + return items.map((item, index) => { + return ( + + {item.children ? ( + // eslint-disable-next-line react/no-array-index-key +
+ {({open, toggle}) => ( + <> + + + {item.title} + + + + + {item.children.map(child => ( + + {child.title} + + ))} + + + )} +
+ ) : ( + // eslint-disable-next-line react/no-array-index-key + + {item.title} + + )} +
+ ) + }) +} + +export default NavDrawer diff --git a/docs/src/@primer/gatsby-theme-doctocat/components/nav-items.js b/docs/src/@primer/gatsby-theme-doctocat/components/nav-items.js new file mode 100644 index 00000000000..bdf3fd9a788 --- /dev/null +++ b/docs/src/@primer/gatsby-theme-doctocat/components/nav-items.js @@ -0,0 +1,71 @@ +import {BorderBox, Flex, Link, StyledOcticon, themeGet} from '@primer/components' +import {LinkExternal} from '@primer/octicons-react' +import {Link as GatsbyLink} from 'gatsby' +import preval from 'preval.macro' +import React from 'react' +import styled from 'styled-components' + +// This code needs to run at build-time so it can access the file system. +const repositoryUrl = preval` + const readPkgUp = require('read-pkg-up') + const getPkgRepo = require('get-pkg-repo') + try { + const repo = getPkgRepo(readPkgUp.sync().package) + module.exports = \`https://github.com/\${repo.user}/\${repo.project}\` + } catch (error) { + module.exports = '' + } +` + +const NavLink = styled(Link)` + &.active { + font-weight: ${themeGet('fontWeights.bold')}; + color: ${themeGet('colors.gray.8')}; + } +` + +function NavItems({items}) { + return ( + <> + {items.map(item => ( + + + + {item.title} + + {item.children ? ( + + {item.children.map(child => ( + + {child.title} + + ))} + + ) : null} + + + ))} + {repositoryUrl ? ( + + + + GitHub + + + + + ) : null} + + ) +} + +export default NavItems diff --git a/docs/src/@primer/gatsby-theme-doctocat/components/page-footer.js b/docs/src/@primer/gatsby-theme-doctocat/components/page-footer.js new file mode 100644 index 00000000000..5fc04e1997c --- /dev/null +++ b/docs/src/@primer/gatsby-theme-doctocat/components/page-footer.js @@ -0,0 +1,27 @@ +import {BorderBox, Grid, Link, StyledOcticon} from '@primer/components' +import {Pencil} from '@primer/octicons-react' +import React from 'react' +import Contributors from '@primer/gatsby-theme-doctocat/src/components/contributors' + +function PageFooter({editUrl, contributors}) { + return editUrl || contributors.length > 0 ? ( + + + {editUrl ? ( + + + Edit this page on GitHub + + ) : null} + + {contributors.length > 0 ? : null} + + + ) : null +} + +PageFooter.defaultProps = { + contributors: [] +} + +export default PageFooter diff --git a/docs/src/@primer/gatsby-theme-doctocat/components/sidebar.js b/docs/src/@primer/gatsby-theme-doctocat/components/sidebar.js new file mode 100644 index 00000000000..c6d6cc34680 --- /dev/null +++ b/docs/src/@primer/gatsby-theme-doctocat/components/sidebar.js @@ -0,0 +1,26 @@ +import {BorderBox, Flex, Position} from '@primer/components' +import React from 'react' +import navItems from '@primer/gatsby-theme-doctocat/src/nav.yml' +import {HEADER_HEIGHT} from '@primer/gatsby-theme-doctocat/src/components/header' +import NavItems from '@primer/gatsby-theme-doctocat/src/components/nav-items' + +function Sidebar() { + return ( + + + + + + + + ) +} + +export default Sidebar diff --git a/docs/src/@primer/gatsby-theme-doctocat/components/wrap-root-element.js.temp b/docs/src/@primer/gatsby-theme-doctocat/components/wrap-root-element.js.temp new file mode 100644 index 00000000000..49c1a4309c4 --- /dev/null +++ b/docs/src/@primer/gatsby-theme-doctocat/components/wrap-root-element.js.temp @@ -0,0 +1,45 @@ +import {MDXProvider} from '@mdx-js/react' +import {Link, theme} from '@primer/components' +import React from 'react' +import {ThemeProvider} from 'styled-components' +import Blockquote from '@primer/gatsby-theme-doctocat/src/components/blockquote' +import Code from './code' +import DescriptionList from '@primer/gatsby-theme-doctocat/src/components/description-list' +import {H1, H2, H3, H4, H5, H6} from '@primer/gatsby-theme-doctocat/src/components/heading' +import HorizontalRule from '@primer/gatsby-theme-doctocat/src/components/horizontal-rule' +import Image from '@primer/gatsby-theme-doctocat/src/components/image' +import InlineCode from '@primer/gatsby-theme-doctocat/src/components/inline-code' +import List from '@primer/gatsby-theme-doctocat/src/components/list' +import Paragraph from '@primer/gatsby-theme-doctocat/src/components/paragraph' +import Table from '@primer/gatsby-theme-doctocat/src/components/table' + +const components = { + a: Link, + pre: props => props.children, + code: Code, + inlineCode: InlineCode, + table: Table, + img: Image, + p: Paragraph, + hr: HorizontalRule, + blockquote: Blockquote, + h1: H1, + h2: H2, + h3: H3, + h4: H4, + h5: H5, + h6: H6, + ul: List, + ol: List.withComponent('ol'), + dl: DescriptionList +} + +function wrapRootElement({element}) { + return ( + + {element} + + ) +} + +export default wrapRootElement diff --git a/docs/src/@primer/gatsby-theme-doctocat/nav.yml b/docs/src/@primer/gatsby-theme-doctocat/nav.yml index 29e1666c0d2..f078cf08de8 100644 --- a/docs/src/@primer/gatsby-theme-doctocat/nav.yml +++ b/docs/src/@primer/gatsby-theme-doctocat/nav.yml @@ -1,6 +1,9 @@ -- title: Getting Started +- title: About url: /getting-started children: + - title: Getting Started + url: /getting-started + - title: Primer Theme url: /primer-theme @@ -13,8 +16,10 @@ - title: Philosophy url: /philosophy + - title: Overriding Styles + url: /overriding-styles + - title: Components - url: / children: - title: Avatar url: /Avatar diff --git a/docs/yarn.lock b/docs/yarn.lock index b512000106d..01a355bbb35 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -3906,6 +3906,13 @@ create-react-context@^0.2.1: fbjs "^0.8.0" gud "^1.0.0" +cross-env@7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.2.tgz#bd5ed31339a93a3418ac4f3ca9ca3403082ae5f9" + integrity sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw== + dependencies: + cross-spawn "^7.0.1" + cross-fetch@2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.2.tgz#a47ff4f7fc712daba8f6a695a11c948440d45723" @@ -3943,6 +3950,15 @@ cross-spawn@^7.0.0: shebang-command "^2.0.0" which "^2.0.1" +cross-spawn@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.2.tgz#d0d7dcfa74e89115c7619f4f721a94e1fdb716d6" + integrity sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crypt@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" diff --git a/index.d.ts b/index.d.ts index 309e1b315b7..2fc8498b382 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,6 +1,7 @@ declare module '@primer/components' { type Omit = Pick> import * as StyledSystem from 'styled-system' + import {SystemStyleObject} from '@styled-system/css' import * as StyledComponents from 'styled-components' import * as History from 'history' @@ -8,6 +9,7 @@ declare module '@primer/components' { as?: React.ReactType className?: string css?: string + sx?: SystemStyleObject title?: string // NOTE(@mxstbr): Necessary workaround to make work to?: History.LocationDescriptor @@ -22,44 +24,24 @@ declare module '@primer/components' { } interface BorderProps - extends BaseProps, - StyledSystem.BordersProps, - StyledSystem.BorderColorProps, - StyledSystem.BoxShadowProps, - StyledSystem.BorderRadiusProps {} - - interface PositionProps - extends BaseProps, - StyledSystem.PositionProps, - StyledSystem.ZIndexProps, - StyledSystem.TopProps, - StyledSystem.RightProps, - StyledSystem.BottomProps, - StyledSystem.LeftProps {} - - interface FlexItemProps - extends BaseProps, - CommonProps, - LayoutProps, - StyledSystem.FlexProps, - StyledSystem.JustifySelfProps, - StyledSystem.AlignSelfProps, - StyledSystem.OrderProps {} + extends StyledSystem.BordersProps, + StyledSystem.BoxShadowProps {} - interface FlexProps extends BaseProps, CommonProps, LayoutProps, StyledSystem.FlexboxProps, BoxProps {} - - export const Flex: React.FunctionComponent & { - Item: React.FunctionComponent - } + interface PositionProps extends StyledSystem.PositionProps {} export interface BoxProps extends BaseProps, CommonProps, LayoutProps, + StyledSystem.FlexboxProps, Omit, 'color'> {} export const Box: React.FunctionComponent + interface FlexProps extends BoxProps {} + + export const Flex: React.FunctionComponent + export interface TextProps extends BaseProps, CommonProps, @@ -93,6 +75,7 @@ declare module '@primer/components' { export interface ButtonProps extends BaseProps, CommonProps, + LayoutProps, StyledSystem.FontSizeProps, Omit, 'color'> { variant?: 'small' | 'medium' | 'large' @@ -122,12 +105,7 @@ declare module '@primer/components' { export const BaseStyles: React.FunctionComponent - export interface BorderBoxProps extends CommonProps, LayoutProps, BorderProps, BoxProps { - border?: string - borderColor?: string - borderRadius?: string | number - boxShadow?: string - } + export interface BorderBoxProps extends BorderProps, BoxProps {} export const BorderBox: React.FunctionComponent @@ -198,6 +176,10 @@ declare module '@primer/components' { export interface FlashProps extends CommonProps, Omit, 'color'> { full?: boolean + variant?: 'success' | 'default' | 'warning' | 'danger' + /** + * @deprecated since version 19.0.0 + */ scheme?: string } @@ -284,10 +266,7 @@ declare module '@primer/components' { } export interface PositionComponentProps - extends PositionProps, - CommonProps, - LayoutProps, - Omit, 'color'> {} + extends PositionProps, BoxProps {} export const Relative: React.FunctionComponent export const Absolute: React.FunctionComponent @@ -298,14 +277,22 @@ declare module '@primer/components' { initialTab?: string } - export interface SelectMenuModalProps extends CommonProps, Omit, 'color'> {} + export interface SelectMenuModalProps extends CommonProps, Omit, 'color'> { + align?: 'left' | 'right' + } export interface SelectMenuListProps extends CommonProps, Omit, 'color'> {} - export interface SelectMenuItemProps extends Omit, - Omit, 'color'> { - selected?: boolean + interface SelectMenuItemCommonProps extends CommonProps { + selected?: boolean; + } + interface SelectMenuItemAsButtonProps extends SelectMenuItemCommonProps, Omit, 'color'> { + as?: "button" + } + interface SelectMenuItemAsAnchorProps extends SelectMenuItemCommonProps, Omit, 'color'> { + as?: "a" } + export type SelectMenuItemProps = SelectMenuItemAsButtonProps | SelectMenuItemAsAnchorProps; export interface SelectMenuFooterProps extends CommonProps, Omit, 'color'> {} @@ -349,7 +336,7 @@ declare module '@primer/components' { Header: React.FunctionComponent } - export interface SideNavProps extends CommonProps, BorderProps, Omit, 'color'> { + export interface SideNavProps extends CommonProps, BorderBoxProps, Omit, 'color'> { bordered?: boolean variant?: 'normal' | 'lightweight' } @@ -369,7 +356,8 @@ declare module '@primer/components' { export interface StateLabelProps extends CommonProps { small?: boolean - status: 'issueOpened' | 'issueClosed' | 'pullOpened' | 'pullClosed' | 'pullMerged' + variant?: 'small' | 'normal' + status: 'issueOpened' | 'issueClosed' | 'pullOpened' | 'pullClosed' | 'pullMerged' | 'draft' } export const StateLabel: React.FunctionComponent @@ -511,216 +499,216 @@ declare module '@primer/components' { export const ProgressBar: React.FunctionComponent } -declare module '@primer/components/src/Box' { +declare module '@primer/components/lib/Box' { import {Box} from '@primer/components' export default Box } -declare module '@primer/components/src/Text' { +declare module '@primer/components/lib/Text' { import {Text} from '@primer/components' export default Text } -declare module '@primer/components/src/Heading' { +declare module '@primer/components/lib/Heading' { import {Heading} from '@primer/components' export default Heading } -declare module '@primer/components/src/ButtonDanger' { +declare module '@primer/components/lib/ButtonDanger' { import {ButtonDanger} from '@primer/components' export default ButtonDanger } -declare module '@primer/components/src/ButtonPrimary' { +declare module '@primer/components/lib/ButtonPrimary' { import {ButtonPrimary} from '@primer/components' export default ButtonPrimary } -declare module '@primer/components/src/ButtonOutline' { +declare module '@primer/components/lib/ButtonOutline' { import {ButtonOutline} from '@primer/components' export default ButtonOutline } -declare module '@primer/components/src/ButtonTableList' { +declare module '@primer/components/lib/ButtonTableList' { import {ButtonTableList} from '@primer/components' export default ButtonTableList } -declare module '@primer/components/src/ButtonGroup' { +declare module '@primer/components/lib/ButtonGroup' { import {ButtonGroup} from '@primer/components' export default ButtonGroup } -declare module '@primer/components/src/Button' { +declare module '@primer/components/lib/Button' { import {Button} from '@primer/components' export default Button } -declare module '@primer/components/src/Flex' { +declare module '@primer/components/lib/Flex' { import {Flex} from '@primer/components' export default Flex } -declare module '@primer/components/src/Avatar' { +declare module '@primer/components/lib/Avatar' { import {Avatar} from '@primer/components' export default Avatar } -declare module '@primer/components/src/Details' { +declare module '@primer/components/lib/Details' { import {Details} from '@primer/components' export default Details } -declare module '@primer/components/src/BaseStyles' { +declare module '@primer/components/lib/BaseStyles' { import {BaseStyles} from '@primer/components' export default BaseStyles } -declare module '@primer/components/src/BorderBox' { +declare module '@primer/components/lib/BorderBox' { import {BorderBox} from '@primer/components' export default BorderBox } -declare module '@primer/components/src/BranchName' { +declare module '@primer/components/lib/BranchName' { import {BranchName} from '@primer/components' export default BranchName } -declare module '@primer/components/src/CircleBadge' { +declare module '@primer/components/lib/CircleBadge' { import {CircleBadge} from '@primer/components' export default CircleBadge } -declare module '@primer/components/src/CircleOcticon' { +declare module '@primer/components/lib/CircleOcticon' { import {CircleOcticon} from '@primer/components' export default CircleOcticon } -declare module '@primer/components/src/StyledOcticon' { +declare module '@primer/components/lib/StyledOcticon' { import {StyledOcticon} from '@primer/components' export default StyledOcticon } -declare module '@primer/components/src/Dropdown' { +declare module '@primer/components/lib/Dropdown' { import {Dropdown} from '@primer/components' export default Dropdown } -declare module '@primer/components/src/FilterList' { +declare module '@primer/components/lib/FilterList' { import {FilterList} from '@primer/components' export default FilterList } -declare module '@primer/components/src/Flash' { +declare module '@primer/components/lib/Flash' { import {Flash} from '@primer/components' export default Flash } -declare module '@primer/components/src/Grid' { +declare module '@primer/components/lib/Grid' { import {Grid} from '@primer/components' export default Grid } -declare module '@primer/components/src/CounterLabel' { +declare module '@primer/components/lib/CounterLabel' { import {CounterLabel} from '@primer/components' export default CounterLabel } -declare module '@primer/components/src/Label' { +declare module '@primer/components/lib/Label' { import {Label} from '@primer/components' export default Label } -declare module '@primer/components/src/Link' { +declare module '@primer/components/lib/Link' { import {Link} from '@primer/components' export default Link } -declare module '@primer/components/src/Pagination' { +declare module '@primer/components/lib/Pagination' { import {Pagination} from '@primer/components' export default Pagination } -declare module '@primer/components/src/PointerBox' { +declare module '@primer/components/lib/PointerBox' { import {PointerBox} from '@primer/components' export default PointerBox } -declare module '@primer/components/src/Popover' { +declare module '@primer/components/lib/Popover' { import {Popover} from '@primer/components' export default Popover } -declare module '@primer/components/src/Relative' { +declare module '@primer/components/lib/Relative' { import {Relative} from '@primer/components' export default Relative } -declare module '@primer/components/src/Absolute' { +declare module '@primer/components/lib/Absolute' { import {Absolute} from '@primer/components' export default Absolute } -declare module '@primer/components/src/Sticky' { +declare module '@primer/components/lib/Sticky' { import {Sticky} from '@primer/components' export default Sticky } -declare module '@primer/components/src/Fixed' { +declare module '@primer/components/lib/Fixed' { import {Fixed} from '@primer/components' export default Fixed } -declare module '@primer/components/src/SelectMenu' { +declare module '@primer/components/lib/SelectMenu' { import {SelectMenu} from '@primer/components' export default SelectMenu } -declare module '@primer/components/src/StateLabel' { +declare module '@primer/components/lib/StateLabel' { import {StateLabel} from '@primer/components' export default StateLabel } -declare module '@primer/components/src/TabNav' { +declare module '@primer/components/lib/TabNav' { import {TabNav} from '@primer/components' export default TabNav } -declare module '@primer/components/src/TextInput' { +declare module '@primer/components/lib/TextInput' { import {TextInput} from '@primer/components' export default TextInput } -declare module '@primer/components/src/Timeline' { +declare module '@primer/components/lib/Timeline' { import {Timeline} from '@primer/components' export default Timeline } -declare module '@primer/components/src/Tooltip' { +declare module '@primer/components/lib/Tooltip' { import {Tooltip} from '@primer/components' export default Tooltip } -declare module '@primer/components/src/UnderlineNav' { +declare module '@primer/components/lib/UnderlineNav' { import {UnderlineNav} from '@primer/components' export default UnderlineNav } -declare module '@primer/components/src/SideNav' { +declare module '@primer/components/lib/SideNav' { import {SideNav} from '@primer/components' export default SideNav } -declare module '@primer/components/src/SubNav' { +declare module '@primer/components/lib/SubNav' { import {SubNav} from '@primer/components' export default SubNav } -declare module '@primer/components/src/theme' { +declare module '@primer/components/lib/theme' { import {theme} from '@primer/components' export default theme } -declare module '@primer/components/src/Dialog' { +declare module '@primer/components/lib/Dialog' { import {Dialog} from '@primer/components' export default Dialog } -declare module '@primer/components/src/LabelGroup' { +declare module '@primer/components/lib/LabelGroup' { import {LabelGroup} from '@primer/components' export default LabelGroup } -declare module '@primer/components/src/ProgressBar' { +declare module '@primer/components/lib/ProgressBar' { import {ProgressBar} from '@primer/components' export default ProgressBar } -declare module '@primer/components/src/AvatarStack' { +declare module '@primer/components/lib/AvatarStack' { import {AvatarStack} from '@primer/components' export default AvatarStack } -declare module '@primer/components/src/Breadcrumbs' { +declare module '@primer/components/lib/Breadcrumbs' { import {Breadcrumb} from '@primer/components' export default Breadcrumb } diff --git a/jest.config.js b/jest.config.js index 9a1b0e31b34..63e6363fac3 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,5 +2,5 @@ module.exports = { cacheDirectory: '.test', collectCoverage: true, collectCoverageFrom: ['src/*.js'], - setupFilesAfterEnv: ['/src/utils/test-matchers.js'] + setupFilesAfterEnv: ['/src/utils/test-matchers.js', '/src/utils/test-deprecations.js'] } diff --git a/now.json b/now.json index 31791d274ec..92c75b244bc 100644 --- a/now.json +++ b/now.json @@ -2,7 +2,7 @@ "name": "primer-components", "version": 2, "alias": "primer-components.now.sh", - "files": [".babelrc", "yarn.lock", "docs", "rollup.config.js", "src", "static"], + "files": ["babel.config.js", "yarn.lock", "docs", "rollup.config.js", "src", "static"], "routes": [ {"src": "/playroom(/.*)?", "dest": "$1"}, {"src": "/components(/.*)?", "dest": "/docs$1"}, diff --git a/package.json b/package.json index 3662eda1286..c2b4ab6dbb8 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,17 @@ { "name": "@primer/components", - "version": "18.1.1", + "version": "19.0.0", "description": "Primer react components", - "main": "dist/index.umd.js", - "module": "dist/index.esm.js", + "main": "lib/index.js", + "module": "lib-esm/index.js", "typings": "index.d.ts", "scripts": { "start": "cd docs && yarn run develop", "predist": "rm -rf dist", - "dist": "NODE_ENV=production rollup -c", + "dist": "yarn dist:rollup && yarn dist:transpile:cjs && yarn dist:transpile:esm", + "dist:rollup": "cross-env NODE_ENV=production rollup -c", + "dist:transpile:cjs": "rm -rf lib && cross-env BABEL_MODULE=commonjs babel src --out-dir lib", + "dist:transpile:esm": "rm -rf lib-esm && babel src --out-dir lib-esm", "prepublishOnly": "yarn run dist", "lint": "eslint src docs/components", "test": "jest -- src", @@ -28,40 +31,52 @@ "files": [ "codemods", "dist", - "src", + "lib", + "lib-esm", "index.d.ts", - "!src/__tests__", - "!src/utils" + "!lib/__tests__", + "!lib-esm/__tests__" ], "author": "GitHub, Inc.", "license": "MIT", "dependencies": { - "@primer/octicons-react": "^9.2.0", + "@babel/helpers": "7.9.2", + "@babel/runtime": "7.9.2", + "@primer/octicons-react": "^9.6.0", "@primer/primitives": "3.0.0", "@reach/dialog": "0.3.0", + "@styled-system/css": "5.1.5", "@styled-system/prop-types": "5.1.2", "@styled-system/props": "5.1.4", "@styled-system/theme-get": "5.1.2", "@testing-library/react": "9.4.0", "@types/styled-components": "^4.4.0", "@types/styled-system": "5.1.2", - "babel-plugin-macros": "2.6.1", + "babel-plugin-macros": "2.8.0", "babel-polyfill": "6.26.0", "classnames": "^2.2.5", "details-element-polyfill": "2.4.0", "jest-axe": "3.2.0", - "nanoid": "2.1.4", + "polished": "3.5.2", "react": "^16.10.2", "react-is": "16.10.2", "styled-system": "5.1.2" }, "devDependencies": { - "@babel/core": "7.7.7", - "@babel/plugin-proposal-object-rest-spread": "7.6.2", - "@babel/preset-env": "7.7.7", - "@babel/preset-react": "7.6.3", + "@babel/cli": "7.8.4", + "@babel/core": "7.9.0", + "@babel/plugin-proposal-object-rest-spread": "7.9.5", + "@babel/plugin-transform-runtime": "7.9.0", + "@babel/preset-env": "7.9.5", + "@babel/preset-react": "7.9.4", + "@rollup/plugin-commonjs": "11.1.0", + "@rollup/plugin-node-resolve": "7.1.3", + "babel-core": "7.0.0-bridge.0", "babel-plugin-add-react-displayname": "0.0.5", - "babel-plugin-styled-components": "1.10.6", + "babel-plugin-preval": "5.0.0", + "babel-plugin-styled-components": "1.10.7", + "babel-plugin-transform-replace-expressions": "0.2.0", + "cross-env": "7.0.2", "enzyme": "3.10.0", "enzyme-adapter-react-16": "1.15.1", "eslint": "6.5.1", @@ -74,26 +89,30 @@ "jscodeshift": "0.6.4", "playroom": "0.15.1", "raw.macro": "0.3.0", - "react-dom": "16.11.0", + "react-dom": "^16.10.2", "react-test-renderer": "16.10.2", - "rollup": "1.25.1", - "rollup-plugin-babel": "4.3.3", + "rollup": "2.7.3", + "rollup-plugin-babel": "4.4.0", "rollup-plugin-commonjs": "10.1.0", + "rollup-plugin-terser": "5.3.0", + "rollup-plugin-visualizer": "4.0.4", + "semver": "7.3.2", "styled-components": "4.4.0" }, "peerDependencies": { "react": "^16.8.0", + "react-dom": "^16.8.0", "styled-components": "4.x || 5.x" }, "actionBundlesize": { - "build": "yarn && yarn dist", + "build": "yarn && yarn dist:rollup", "files": [ { - "path": "dist/index.esm.js", + "path": "dist/browser.esm.js", "name": "ESM Build" }, { - "path": "dist/index.umd.js", + "path": "dist/browser.umd.js", "name": "UMD Build" } ] diff --git a/rollup.config.js b/rollup.config.js index 4403f6e2f66..4c6d5028bf6 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,18 +1,40 @@ import babel from 'rollup-plugin-babel' import commonjs from 'rollup-plugin-commonjs' +import resolve from '@rollup/plugin-node-resolve' +import {terser} from 'rollup-plugin-terser' +import visualizer from 'rollup-plugin-visualizer' -const formats = ['esm', 'umd'] // 'cjs' ? -const plugins = [babel({exclude: 'node_modules/**'}), commonjs()] +// NOTE: this can be removed once the next version of rollup-plugin-commonjs is released +const namedExports = { + 'prop-types': ['object', 'func', 'oneOfType', 'node', 'bool', 'string', 'any', 'arrayOf'], + 'react-dom': ['createPortal'], + 'react-is': ['isValidElementType'] +} + +const formats = ['esm', 'umd'] +const plugins = [ + babel({exclude: 'node_modules/**', runtimeHelpers: true}), + resolve(), + commonjs({namedExports}), + terser(), + visualizer({sourcemap: true}) +] export default [ { input: 'src/index.js', + external: ['styled-components', 'react', 'react-dom'], plugins, - external: ['styled-components', 'react'], output: formats.map(format => ({ - file: `dist/index.${format}.js`, + file: `dist/browser.${format}.js`, format, - name: 'primer' + sourcemap: true, + name: 'primer', + globals: { + react: 'React', + 'react-dom': 'ReactDOM', + 'styled-components': 'styled' + } })) } ] diff --git a/src/Avatar.js b/src/Avatar.js index 83db587e413..44488d78c65 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -1,5 +1,6 @@ -import PropTypes from 'prop-types' import styled from 'styled-components' +import PropTypes from 'prop-types' +import sx from './sx' import {get} from './constants' import {space} from 'styled-system' import systemPropTypes from '@styled-system/prop-types' @@ -22,6 +23,7 @@ const Avatar = styled.img.attrs(props => ({ vertical-align: middle; ${borderRadius}; ${space}; + ${sx}; ` Avatar.defaultProps = { @@ -35,6 +37,7 @@ Avatar.propTypes = { size: PropTypes.number, src: PropTypes.string, ...systemPropTypes.space, + ...sx.propTypes, theme: PropTypes.object } diff --git a/src/AvatarStack.js b/src/AvatarStack.js index f737e2aad93..6371a0c3513 100644 --- a/src/AvatarStack.js +++ b/src/AvatarStack.js @@ -1,41 +1,10 @@ import React from 'react' import PropTypes from 'prop-types' -import styled from 'styled-components' +import styled, {css} from 'styled-components' +import sx from './sx' import {get, COMMON} from './constants' import theme from './theme' -const alignRightStyles = theme => { - return ` - right: 0; - flex-direction: row-reverse; - - &:hover .AvatarItem { - margin-right: 0; - margin-left: 3px; - } - - .AvatarItem-more { - background: ${get('colors.gray.3')(theme)}; - - &::before { - width: 5px; - } - - &::after { - background: ${get('colors.gray.1')(theme)}; - width: 2px; - } - } - - .AvatarItem { - margin-right: 0; - margin-left: -11px; - border-right: 0; - border-left: ${get('borders.1')(theme)} ${get('colors.white')(theme)}; - } - ` -} - const transformChildren = children => { const count = children.length return React.Children.map(children, (child, index) => { @@ -54,6 +23,7 @@ const AvatarStackWrapper = styled.span` min-width: ${props => (props.count === 1 ? '26px' : props.count === 2 ? '36px' : '46px')}; height: 20px; ${COMMON} + ${sx}; ` const AvatarStackBody = styled.span` @@ -85,7 +55,7 @@ const AvatarStackBody = styled.span` box-sizing: content-box; margin-right: -11px; background-color: ${get('colors.white')}; - border-right: ${get('borders.1')} ${get('colors.white')}; + border-right: ${get('borderWidths.1')} solid ${get('colors.white')}; border-radius: 2px; transition: margin 0.1s ease-in-out; @@ -122,7 +92,7 @@ const AvatarStackBody = styled.span` height: 20px; content: ''; border-radius: 2px; - outline: ${get('borders.1')} ${get('colors.white')}; + outline: ${get('borderWidths.1')} solid ${get('colors.white')}; } &::before { @@ -135,7 +105,38 @@ const AvatarStackBody = styled.span` background: ${get('colors.gray.3')}; } } - ${props => (props.alignRight ? alignRightStyles(props.theme) : '')} + + ${props => + props.alignRight && + css` + right: 0; + flex-direction: row-reverse; + + &:hover .AvatarItem { + margin-right: 0; + margin-left: 3px; + } + + .AvatarItem-more { + background: ${get('colors.gray.3')}; + + &::before { + width: 5px; + } + + &::after { + background: ${get('colors.gray.1')}; + width: 2px; + } + } + + .AvatarItem { + margin-right: 0; + margin-left: -11px; + border-right: 0; + border-left: ${get('borderWidths.1')} solid ${get('colors.white')}; + } + `} ` const AvatarStack = ({children = [], alignRight, ...rest}) => { return ( @@ -153,6 +154,7 @@ AvatarStack.defaultProps = { AvatarStack.propTypes = { ...COMMON.propTypes, - alignRight: PropTypes.bool + alignRight: PropTypes.bool, + ...sx.propTypes } export default AvatarStack diff --git a/src/BorderBox.js b/src/BorderBox.js index 2cdde7b595d..36e46ce54fa 100644 --- a/src/BorderBox.js +++ b/src/BorderBox.js @@ -1,22 +1,28 @@ import styled from 'styled-components' +import sx from './sx' import PropTypes from 'prop-types' import Box from './Box' import theme from './theme' import {BORDER} from './constants' -const BorderBox = styled(Box)(BORDER) +const BorderBox = styled(Box)` + ${BORDER}; + ${sx}; +` BorderBox.defaultProps = { theme, - border: '1px solid', + borderWidth: '1px', + borderStyle: 'solid', borderColor: 'gray.2', borderRadius: 2 } BorderBox.propTypes = { - theme: PropTypes.object, ...Box.propTypes, - ...BORDER.propTypes + ...BORDER.propTypes, + ...sx.propTypes, + theme: PropTypes.object } export default BorderBox diff --git a/src/Box.js b/src/Box.js index 82b53baf5eb..124a1703dd6 100644 --- a/src/Box.js +++ b/src/Box.js @@ -1,22 +1,23 @@ import styled from 'styled-components' import PropTypes from 'prop-types' -import {space, color} from 'styled-system' -import systemPropTypes from '@styled-system/prop-types' -import {LAYOUT} from './constants' +import sx from './sx' +import {COMMON, FLEX, LAYOUT} from './constants' import theme from './theme' const Box = styled.div` + ${COMMON} + ${FLEX} ${LAYOUT} - ${space} - ${color} + ${sx}; ` Box.defaultProps = {theme} Box.propTypes = { + ...COMMON.propTypes, + ...FLEX.propTypes, ...LAYOUT.propTypes, - ...systemPropTypes.space, - ...systemPropTypes.color, + ...sx.propTypes, theme: PropTypes.object } diff --git a/src/BranchName.js b/src/BranchName.js index 3041fb8e756..3c7b342de40 100644 --- a/src/BranchName.js +++ b/src/BranchName.js @@ -1,5 +1,6 @@ import PropTypes from 'prop-types' import styled from 'styled-components' +import sx from './sx' import theme from './theme' import {COMMON, get} from './constants' @@ -12,6 +13,7 @@ const BranchName = styled.a` background-color: #eaf5ff; border-radius: 3px; ${COMMON}; + ${sx}; ` BranchName.defaultProps = { @@ -21,6 +23,7 @@ BranchName.defaultProps = { BranchName.propTypes = { href: PropTypes.string, ...COMMON.propTypes, + ...sx.propTypes, theme: PropTypes.object } diff --git a/src/Breadcrumbs.js b/src/Breadcrumb.js similarity index 88% rename from src/Breadcrumbs.js rename to src/Breadcrumb.js index ab316bedacc..8c6d0263d32 100644 --- a/src/Breadcrumbs.js +++ b/src/Breadcrumb.js @@ -2,6 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' import styled from 'styled-components' +import sx from './sx' import {COMMON, FLEX, get} from './constants' import theme from './theme' import Box from './Box' @@ -46,6 +47,7 @@ const Breadcrumb = styled(BreadcrumbBase)` justify-content: space-between; ${COMMON}; ${FLEX}; + ${sx}; ` Breadcrumb.Item = styled.a.attrs(props => ({ @@ -65,6 +67,7 @@ Breadcrumb.Item = styled.a.attrs(props => ({ pointer-events: none; } ${COMMON} + ${sx}; ` Breadcrumb.defaultProps = { @@ -72,18 +75,24 @@ Breadcrumb.defaultProps = { } Breadcrumb.propTypes = { - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } +Breadcrumb.displayName = 'Breadcrumb' + Breadcrumb.Item.defaultProps = { theme } Breadcrumb.Item.propTypes = { - as: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), + as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]), href: PropTypes.string, selected: PropTypes.bool, + ...sx.propTypes, ...COMMON.propTypes } +Breadcrumb.Item.displayName = 'Breadcrumb.Item' + export default Breadcrumb diff --git a/src/Button.js b/src/Button/Button.js similarity index 83% rename from src/Button.js rename to src/Button/Button.js index 36c30aa44c3..fb193e1e906 100644 --- a/src/Button.js +++ b/src/Button/Button.js @@ -1,8 +1,7 @@ -import PropTypes from 'prop-types' import styled from 'styled-components' -import systemPropTypes from '@styled-system/prop-types' -import {COMMON, get} from './constants' -import theme from './theme' +import sx from '../sx' +import {get} from '../constants' +import theme from '../theme' import ButtonBase from './ButtonBase' const Button = styled(ButtonBase)` @@ -34,6 +33,8 @@ const Button = styled(ButtonBase)` background-color: ${get('buttons.default.bg.disabled')}; border-color: ${get('buttons.default.border.disabled')}; } + + ${sx}; ` Button.defaultProps = { @@ -41,9 +42,8 @@ Button.defaultProps = { } Button.propTypes = { - theme: PropTypes.object, - ...COMMON.propTypes, - ...systemPropTypes.layout + ...ButtonBase.propTypes, + ...sx.propTypes } export default Button diff --git a/src/ButtonBase.js b/src/Button/ButtonBase.js similarity index 80% rename from src/ButtonBase.js rename to src/Button/ButtonBase.js index 3b90de1863e..ab5cb63b964 100644 --- a/src/ButtonBase.js +++ b/src/Button/ButtonBase.js @@ -1,9 +1,9 @@ import PropTypes from 'prop-types' import styled from 'styled-components' -import {COMMON} from './constants' -import theme from './theme' +import {COMMON, LAYOUT} from '../constants' +import theme from '../theme' import buttonBaseStyles from './ButtonStyles' -import {compose, variant, layout, fontSize} from 'styled-system' +import {compose, variant, fontSize} from 'styled-system' import systemPropTypes from '@styled-system/prop-types' const variants = variant({ @@ -27,7 +27,7 @@ const ButtonBase = styled.button.attrs(({disabled, onClick}) => ({ }))` ${buttonBaseStyles} ${variants} - ${compose(fontSize, COMMON, layout)} + ${compose(fontSize, COMMON, LAYOUT)} ` ButtonBase.defaultProps = { @@ -36,7 +36,7 @@ ButtonBase.defaultProps = { } ButtonBase.propTypes = { - as: PropTypes.oneOfType([PropTypes.oneOf(['button', 'a', 'summary', 'input']), PropTypes.func]), + as: PropTypes.oneOfType([PropTypes.oneOf(['button', 'a', 'summary', 'input']), PropTypes.elementType]), children: PropTypes.node, disabled: PropTypes.bool, fontSize: systemPropTypes.typography.fontSize, @@ -44,7 +44,7 @@ ButtonBase.propTypes = { theme: PropTypes.object, variant: PropTypes.oneOf(['small', 'medium', 'large']), ...COMMON.propTypes, - ...systemPropTypes.layout + ...LAYOUT.propTypes } export default ButtonBase diff --git a/src/ButtonDanger.js b/src/Button/ButtonDanger.js similarity index 85% rename from src/ButtonDanger.js rename to src/Button/ButtonDanger.js index b8f74bb58ce..03f63371820 100644 --- a/src/ButtonDanger.js +++ b/src/Button/ButtonDanger.js @@ -1,6 +1,8 @@ import styled from 'styled-components' import ButtonBase from './ButtonBase' -import {get} from './constants' +import {get} from '../constants' +import theme from '../theme' +import sx from '../sx' const ButtonDanger = styled(ButtonBase)` color: ${get('buttons.danger.color.default')}; @@ -32,6 +34,17 @@ const ButtonDanger = styled(ButtonBase)` background-color: ${get('buttons.danger.bg.disabled')}; border: 1px solid ${get('buttons.danger.border.default')}; } + + ${sx}; ` +ButtonDanger.defaultProps = { + theme +} + +ButtonDanger.propTypes = { + ...ButtonBase.propTypes, + ...sx.propTypes +} + export default ButtonDanger diff --git a/src/ButtonGroup.js b/src/Button/ButtonGroup.js similarity index 81% rename from src/ButtonGroup.js rename to src/Button/ButtonGroup.js index 1ac188c8f32..e189c92b541 100644 --- a/src/ButtonGroup.js +++ b/src/Button/ButtonGroup.js @@ -1,6 +1,8 @@ import styled from 'styled-components' -import {get} from './constants' -import Box from './Box' +import {get} from '../constants' +import Box from '../Box' +import theme from '../theme' +import sx from '../sx' const ButtonGroup = styled(Box)` vertical-align: middle; @@ -41,12 +43,18 @@ const ButtonGroup = styled(Box)` z-index: 1; } } + + ${sx}; ` + ButtonGroup.defaultProps = { - display: 'inline-block' + display: 'inline-block', + theme } + ButtonGroup.propTypes = { - ...Box.propTypes + ...Box.propTypes, + ...sx.propTypes } export default ButtonGroup diff --git a/src/ButtonOutline.js b/src/Button/ButtonOutline.js similarity index 85% rename from src/ButtonOutline.js rename to src/Button/ButtonOutline.js index abc6b9a812d..869e78e81f0 100644 --- a/src/ButtonOutline.js +++ b/src/Button/ButtonOutline.js @@ -1,6 +1,8 @@ import styled from 'styled-components' import ButtonBase from './ButtonBase' -import {get} from './constants' +import {get} from '../constants' +import theme from '../theme' +import sx from '../sx' const ButtonOutline = styled(ButtonBase)` color: ${get('buttons.outline.color.default')}; @@ -32,6 +34,17 @@ const ButtonOutline = styled(ButtonBase)` border-color: ${get('buttons.outline.border.default')}; background-color: ${get('buttons.outline.bg.disabled')}; } + + ${sx}; ` +ButtonOutline.defaultProps = { + theme +} + +ButtonOutline.propTypes = { + ...ButtonBase.propTypes, + ...sx.propTypes +} + export default ButtonOutline diff --git a/src/ButtonPrimary.js b/src/Button/ButtonPrimary.js similarity index 84% rename from src/ButtonPrimary.js rename to src/Button/ButtonPrimary.js index 02388e237fe..a73b0e40c5b 100644 --- a/src/ButtonPrimary.js +++ b/src/Button/ButtonPrimary.js @@ -1,6 +1,8 @@ import styled from 'styled-components' import ButtonBase from './ButtonBase' -import {get} from './constants' +import {get} from '../constants' +import theme from '../theme' +import sx from '../sx' const ButtonPrimary = styled(ButtonBase)` color: ${get('buttons.primary.color.default')}; @@ -30,6 +32,17 @@ const ButtonPrimary = styled(ButtonBase)` background-color: ${get('buttons.primary.bg.disabled')}; border-color: ${get('buttons.primary.border.disabled')}; } + + ${sx}; ` +ButtonPrimary.defaultProps = { + theme +} + +ButtonPrimary.propTypes = { + ...ButtonBase.propTypes, + ...sx.propTypes +} + export default ButtonPrimary diff --git a/src/ButtonStyles.js b/src/Button/ButtonStyles.js similarity index 91% rename from src/ButtonStyles.js rename to src/Button/ButtonStyles.js index 06a4c5cad55..f48e32a92b1 100644 --- a/src/ButtonStyles.js +++ b/src/Button/ButtonStyles.js @@ -1,5 +1,5 @@ import {css} from 'styled-components' -import {get} from './constants' +import {get} from '../constants' export default css` position: relative; @@ -14,6 +14,7 @@ export default css` border-radius: ${get('radii.2')}; appearance: none; text-decoration: none; + text-align: center; &:hover { // needed to override link styles diff --git a/src/ButtonTableList.js b/src/Button/ButtonTableList.js similarity index 74% rename from src/ButtonTableList.js rename to src/Button/ButtonTableList.js index 9a767522875..dc06c6041ce 100644 --- a/src/ButtonTableList.js +++ b/src/Button/ButtonTableList.js @@ -1,6 +1,8 @@ import styled from 'styled-components' -import {COMMON, LAYOUT, TYPOGRAPHY, get} from './constants' -import theme from './theme' +import PropTypes from 'prop-types' +import {COMMON, LAYOUT, TYPOGRAPHY, get} from '../constants' +import theme from '../theme' +import sx from '../sx' const ButtonTableList = styled.summary` display: inline-block; @@ -40,10 +42,19 @@ const ButtonTableList = styled.summary` ${COMMON} ${TYPOGRAPHY} ${LAYOUT} + ${sx}; ` ButtonTableList.defaultProps = { theme } +ButtonTableList.propTypes = { + theme: PropTypes.object, + ...sx.propTypes, + ...COMMON.propTypes, + ...TYPOGRAPHY.propTypes, + ...LAYOUT.propTypes +} + export default ButtonTableList diff --git a/src/Button/index.js b/src/Button/index.js new file mode 100644 index 00000000000..58149b8657f --- /dev/null +++ b/src/Button/index.js @@ -0,0 +1,7 @@ +import Button from './Button' +export default Button +export {default as ButtonDanger} from './ButtonDanger' +export {default as ButtonGroup} from './ButtonGroup' +export {default as ButtonOutline} from './ButtonOutline' +export {default as ButtonPrimary} from './ButtonPrimary' +export {default as ButtonTableList} from './ButtonTableList' diff --git a/src/CircleBadge.js b/src/CircleBadge.js index 7529eebf7e8..731df05129a 100644 --- a/src/CircleBadge.js +++ b/src/CircleBadge.js @@ -4,6 +4,7 @@ import Octicon from '@primer/octicons-react' import {COMMON, get} from './constants' import isNumeric from './utils/isNumeric' import theme from './theme' +import sx from './sx' const variantSizes = { small: 56, @@ -27,15 +28,16 @@ const CircleBadge = styled.div` border-radius: 50%; box-shadow: ${get('shadows.medium')}; ${COMMON} ${sizeStyles}; + ${sx}; ` -const Icon = styled(Octicon)` +CircleBadge.Icon = styled(Octicon)` max-width: 60% !important; height: auto !important; max-height: 55% !important; ${COMMON}; + ${sx}; ` -CircleBadge.Icon = Icon CircleBadge.defaultProps = { inline: false, @@ -48,16 +50,20 @@ CircleBadge.propTypes = { size: PropTypes.number, theme: PropTypes.object, variant: PropTypes.oneOf(['small', 'medium', 'large']), - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } CircleBadge.Icon.defaultProps = { theme } -CircleBadge.Icon.propsTypes = { +CircleBadge.Icon.propTypes = { theme: PropTypes.object, - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } +CircleBadge.Icon.displayName = 'CircleBadge.Icon' + export default CircleBadge diff --git a/src/CircleOcticon.js b/src/CircleOcticon.js index e5bddbe059e..72eef37f19c 100644 --- a/src/CircleOcticon.js +++ b/src/CircleOcticon.js @@ -9,7 +9,7 @@ function CircleOcticon(props) { const {size, as} = props const {icon, bg, as: asProp, ...rest} = props return ( - + diff --git a/src/CounterLabel.js b/src/CounterLabel.js index a0c7ae26f98..00164a80ae5 100644 --- a/src/CounterLabel.js +++ b/src/CounterLabel.js @@ -2,6 +2,7 @@ import PropTypes from 'prop-types' import styled from 'styled-components' import {COMMON, get} from './constants' import theme from './theme' +import sx from './sx' const colorStyles = ({scheme, ...props}) => { return { @@ -39,6 +40,8 @@ const CounterLabel = styled.span` &:empty { visibility: hidden; } + + ${sx}; ` CounterLabel.defaultProps = { @@ -49,7 +52,8 @@ CounterLabel.propTypes = { children: PropTypes.node, scheme: PropTypes.oneOf(['gray', 'gray-light']), theme: PropTypes.object, - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } export default CounterLabel diff --git a/src/Details.js b/src/Details.js index 0c097a48dbe..f7c43d2f21a 100644 --- a/src/Details.js +++ b/src/Details.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types' import styled from 'styled-components' import {COMMON} from './constants' import theme from './theme' +import sx from './sx' // The
element is not yet supported in Edge so we have to use a polyfill. // We have to check if window is defined before importing the polyfill @@ -21,6 +22,7 @@ const StyledDetails = styled('details')` } ${COMMON} + ${sx}; ` function getRenderer(children) { return typeof children === 'function' ? children : () => children @@ -90,7 +92,8 @@ Details.propTypes = { overlay: PropTypes.bool, render: PropTypes.func, theme: PropTypes.object, - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } export default Details diff --git a/src/Dialog.js b/src/Dialog.js index b00d4c46ad1..1f266ec8770 100644 --- a/src/Dialog.js +++ b/src/Dialog.js @@ -3,12 +3,11 @@ import {Dialog as ReachDialog} from '@reach/dialog' import raw from 'raw.macro' import styled, {createGlobalStyle} from 'styled-components' import PropTypes from 'prop-types' -import {space, color} from 'styled-system' -import systemPropTypes from '@styled-system/prop-types' import {X} from '@primer/octicons-react' import StyledOcticon from './StyledOcticon' -import {LAYOUT} from './constants' +import {COMMON, LAYOUT} from './constants' import theme from './theme' +import sx from './sx' import Text from './Text' import Flex from './Flex' @@ -22,7 +21,7 @@ const ReachGlobalStyle = createGlobalStyle` } ` -export const StyledDialog = styled(ReachDialog)` +const StyledDialog = styled(ReachDialog)` box-shadow: 0px 4px 32px rgba(0, 0, 0, 0.35); border-radius: 4px; padding: 0 !important; @@ -36,8 +35,8 @@ export const StyledDialog = styled(ReachDialog)` } ${LAYOUT} - ${space} - ${color} + ${COMMON} + ${sx}; ` const UnstyledButton = styled(Flex).attrs({ @@ -59,6 +58,8 @@ const DialogHeaderBase = styled(Flex)` @media screen and (max-width: 750px) { border-radius: 0px; } + + ${sx}; ` function DialogHeader({theme, children, ...rest}) { @@ -77,7 +78,7 @@ function DialogHeader({theme, children, ...rest}) { ) } -const Dialog = ({children, ...props}) => { +function Dialog({children, ...props}) { return ( <> @@ -94,12 +95,12 @@ const Dialog = ({children, ...props}) => { Dialog.defaultProps = {theme} Dialog.propTypes = { + ...COMMON.propTypes, ...LAYOUT.propTypes, - ...systemPropTypes.space, - ...systemPropTypes.color, children: PropTypes.node.isRequired, isOpen: PropTypes.bool.isRequired, onDismiss: PropTypes.func.isRequired, + ...sx.propTypes, theme: PropTypes.object } @@ -112,5 +113,7 @@ DialogHeader.propTypes = { ...Flex.propTypes } +DialogHeader.displayName = 'Dialog.Header' + Dialog.Header = DialogHeader export default Dialog diff --git a/src/Dropdown.js b/src/Dropdown.js index 6363b62d886..d2e539dec87 100644 --- a/src/Dropdown.js +++ b/src/Dropdown.js @@ -6,6 +6,7 @@ import Details from './Details' import {COMMON, get} from './constants' import getDirectionStyles from './DropdownStyles' import theme from './theme' +import sx from './sx' const StyledDetails = styled(Details)` position: relative; @@ -39,7 +40,8 @@ Dropdown.Caret = styled.div` height: 0; vertical-align: middle; width: 0; - ${COMMON} + ${COMMON}; + ${sx}; ` Dropdown.Menu = styled.ul` @@ -85,6 +87,7 @@ Dropdown.Menu = styled.ul` } ${props => (props.direction ? getDirectionStyles(props.theme, props.direction) : '')}; ${COMMON}; + ${sx}; ` Dropdown.Item = styled.li` @@ -120,31 +123,42 @@ Dropdown.Item = styled.li` outline: none; } ${COMMON}; + ${sx}; ` Dropdown.Menu.propTypes = { direction: PropTypes.oneOf(['ne', 'e', 'se', 's', 'sw', 'w']), - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } Dropdown.Menu.defaultProps = { direction: 'sw', theme } +Dropdown.Menu.displayName = 'Dropdown.Menu' Dropdown.Item.defaultProps = {theme} Dropdown.Item.propTypes = { - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } +Dropdown.Item.displayName = 'Dropdown.Item' -Dropdown.Button.defaultProps = {theme} +Dropdown.Button.defaultProps = {theme, ...Button.defaultProps} +Dropdown.Button.propTypes = { + ...Button.propTypes +} +Dropdown.Button.displayName = 'Dropdown.Button' Dropdown.Caret.defaultProps = {theme} -Dropdown.Caret.propTpyes = { - ...COMMON.propTypes +Dropdown.Caret.propTypes = { + ...COMMON.propTypes, + ...sx.propTypes } +Dropdown.Caret.displayName = 'Dropdown.Caret' -Dropdown.defaultProps = {theme} +Dropdown.defaultProps = {theme, ...Details.defaultProps} Dropdown.propTypes = { ...Details.propTypes, ...COMMON.propTypes diff --git a/src/FilterList.js b/src/FilterList.js index 14ac59a555c..1dfb2a0a90c 100644 --- a/src/FilterList.js +++ b/src/FilterList.js @@ -1,13 +1,13 @@ import React from 'react' -import nanoid from 'nanoid' import PropTypes from 'prop-types' import styled from 'styled-components' import {COMMON, get} from './constants' import theme from './theme' +import sx from './sx' function ItemBase({children, count, theme, ...rest}) { return ( - + {count && ( {count} @@ -44,7 +44,8 @@ const Item = styled(ItemBase)` float: right; font-weight: ${get('fontWeights.bold')}; } - ${COMMON}: ; + ${COMMON}; + ${sx}; ` const FilterListBase = ({children, theme, ...rest}) => { @@ -58,6 +59,7 @@ const FilterListBase = ({children, theme, ...rest}) => { const FilterList = styled(FilterListBase)` list-style-type: none; ${COMMON}; + ${sx}; ` FilterList.defaultProps = { @@ -69,7 +71,8 @@ FilterList.defaultProps = { FilterList.propTypes = { children: PropTypes.node, small: PropTypes.bool, - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } FilterList.Item = Item @@ -79,13 +82,16 @@ FilterList.Item.defaultProps = { } FilterList.Item.propTypes = { - as: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), + as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]), children: PropTypes.node, className: PropTypes.string, count: PropTypes.string, selected: PropTypes.bool, theme: PropTypes.object, - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } +FilterList.Item.displayName = 'FilterList.Item' + export default FilterList diff --git a/src/Flash.js b/src/Flash.js index 2aace8f776c..94fb1b941c2 100644 --- a/src/Flash.js +++ b/src/Flash.js @@ -1,43 +1,75 @@ +import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' +import {variant} from 'styled-system' import {COMMON, get} from './constants' import theme from './theme' +import sx from './sx' +import {useDeprecation} from './utils/deprecate' const schemeMap = { - green: {color: 'colors.green.8', bg: 'colors.green.1'}, - red: {color: 'colors.red.9', bg: 'colors.red.1'}, - yellow: {color: 'colors.yellow.9', bg: 'colors.yellow.1'}, - base: {color: 'colors.blue.8', bg: 'colors.blue.1'} + red: 'danger', + blue: 'default', + yellow: 'warning', + green: 'success' } -const Flash = styled.div` +const variants = variant({ + scale: 'flash' +}) + +const getIconColor = (variant, theme) => get(`flashIcon.${variant}`)(theme) + +const StyledFlash = styled.div` position: relative; + color: ${get('colors.text.grayDark')}; padding: ${get('space.3')}; - color: ${props => get(schemeMap[props.scheme] ? schemeMap[props.scheme].color : '')}; - background-color: ${props => get(schemeMap[props.scheme] ? schemeMap[props.scheme].bg : '')}; - border-color: ${get('colors.blackfade15')}; border-style: solid; border-width: ${props => (props.full ? '1px 0px' : '1px')}; - border-radius: ${props => (props.full ? '0' : '3px')}; + border-radius: ${props => (props.full ? '0' : get('radii.2'))}; margin-top: ${props => (props.full ? '-1px' : '0')}; p:last-child { margin-bottom: 0; } + + svg { + color: ${props => getIconColor(props.variant, props.theme)}; + margin-right: ${get('space.2')}; + } + ${COMMON}; + ${variants} + ${sx}; ` +const Flash = ({variant, scheme, ...props}) => { + const deprecate = useDeprecation({ + name: 'Flash "scheme" prop', + version: '20.0.0', + message: 'Use the variant prop instead. See https://primer.style/components/Flash for more details.' + }) + + if (scheme) { + deprecate() + variant = schemeMap[scheme] + } + + return +} + Flash.defaultProps = { theme, - scheme: 'base' + variant: 'default' } Flash.propTypes = { children: PropTypes.node, full: PropTypes.bool, - scheme: PropTypes.oneOf(Object.keys(schemeMap)), - theme: PropTypes.object, - ...COMMON.propTypes + scheme: PropTypes.oneOf(Object.keys(schemeMap)), // deprecate 20.0.0 + variant: PropTypes.oneOf(['default', 'warning', 'success', 'danger']), + ...COMMON.propTypes, + ...sx.propTypes } export default Flash diff --git a/src/Flex.js b/src/Flex.js index b21e7331f80..5f0a95f7c09 100644 --- a/src/Flex.js +++ b/src/Flex.js @@ -1,33 +1,16 @@ import styled from 'styled-components' -import PropTypes from 'prop-types' -import {FLEX} from './constants' import theme from './theme' import Box from './Box' -const Flex = styled(Box)` - ${FLEX}; -` - -// Keeping this around for backwards compatibility, but it's the same as `FLEX` -Flex.Item = styled(Box)` - ${FLEX}; -` +const Flex = styled(Box)`` Flex.defaultProps = { theme, display: 'flex' } -Flex.Item.defaultProps = { - theme -} -Flex.propTypes = { - ...Box.propTypes, - ...FLEX.propTypes -} -Flex.Item.propTypes = { - ...FLEX.propTypes, - theme: PropTypes.object +Flex.propTypes = { + ...Box.propTypes } export default Flex diff --git a/src/Heading.js b/src/Heading.js index aaf63fa0856..c8df9d34f70 100644 --- a/src/Heading.js +++ b/src/Heading.js @@ -1,4 +1,5 @@ import styled from 'styled-components' +import sx from './sx' import PropTypes from 'prop-types' import {TYPOGRAPHY, COMMON, get} from './constants' import theme from './theme' @@ -8,6 +9,7 @@ const Heading = styled.h2` font-size: ${get('fontSizes.5')}; margin: 0; ${TYPOGRAPHY} ${COMMON}; + ${sx}; ` Heading.defaultProps = { @@ -15,8 +17,9 @@ Heading.defaultProps = { } Heading.propTypes = { - theme: PropTypes.object, ...COMMON.propTypes, + ...sx.propTypes, + theme: PropTypes.object, ...TYPOGRAPHY.propTypes } diff --git a/src/Label.js b/src/Label.js index 6d694d07627..abbf600d1b9 100644 --- a/src/Label.js +++ b/src/Label.js @@ -3,12 +3,13 @@ import styled, {css} from 'styled-components' import {variant, borderColor} from 'styled-system' import theme from './theme' import {COMMON, get} from './constants' +import sx from './sx' const outlineStyles = css` margin-top: -1px; // offsets the 1px border margin-bottom: -1px; // offsets the 1px border color: ${get('colors.gray.6')}; - border: ${get('borders.1')} ${get('colors.blackfade15')}; + border: ${get('borderWidths.1')} solid ${get('colors.blackfade15')}; box-shadow: none; ${borderColor}; ${COMMON}; @@ -52,6 +53,7 @@ const Label = styled('span')` ${sizeVariant} ${COMMON} ${props => (props.dropshadow ? 'box-shadow: inset 0 -1px 0 rgba(27, 31, 35, 0.12)' : '')}; ${props => (props.outline ? outlineStyles : '')}; // must be last to override other values + ${sx}; ` Label.defaultProps = { @@ -65,7 +67,8 @@ Label.propTypes = { outline: PropTypes.bool, theme: PropTypes.object, variant: PropTypes.oneOf(['small', 'medium', 'large', 'xl']), - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } export default Label diff --git a/src/LabelGroup.js b/src/LabelGroup.js index f14117d0159..db4d8de10c9 100644 --- a/src/LabelGroup.js +++ b/src/LabelGroup.js @@ -2,31 +2,28 @@ import React from 'react' import styled from 'styled-components' import theme from './theme' import {COMMON, get} from './constants' - -const transformChildren = children => { - return React.Children.map(children, child => { - return React.cloneElement(child, {className: 'LabelItem'}) - }) -} +import sx from './sx' const StyledLabelGroup = styled.span` ${COMMON} - & .LabelItem { + & * { margin-right: ${get('space.1')}; } - & .LabelItem:last-child { + & *:last-child { margin-right: 0; } + ${sx}; ` -const LabelGroup = ({children, ...rest}) => {transformChildren(children)} +const LabelGroup = ({children, ...rest}) => {children} LabelGroup.defaultProps = { theme } LabelGroup.propTypes = { - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } export default LabelGroup diff --git a/src/Link.js b/src/Link.js index e0915715f73..d2b89bbc6b6 100644 --- a/src/Link.js +++ b/src/Link.js @@ -3,7 +3,7 @@ import styled from 'styled-components' import {system} from 'styled-system' import {COMMON, TYPOGRAPHY, get} from './constants' import theme from './theme' -import elementType from './utils/elementType' +import sx from './sx' const buttonStyles = { display: 'inline-block', @@ -34,6 +34,7 @@ const Link = styled.a.attrs(props => ({ } ${props => (props.as === 'button' ? buttonStyles : '')}; ${TYPOGRAPHY} ${COMMON}; + ${sx}; ` Link.defaultProps = { @@ -41,13 +42,14 @@ Link.defaultProps = { } Link.propTypes = { - as: elementType, + as: PropTypes.elementType, href: PropTypes.string, muted: PropTypes.bool, theme: PropTypes.object, underline: PropTypes.bool, ...TYPOGRAPHY.propTypes, - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } export default Link diff --git a/src/Pagination/Pagination.js b/src/Pagination/Pagination.js index 200f6788464..1f943796474 100644 --- a/src/Pagination/Pagination.js +++ b/src/Pagination/Pagination.js @@ -1,6 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' import styled, {css} from 'styled-components' +import sx from '../sx' import {get, COMMON} from '../constants' import theme from '../theme' import Box from '../Box' @@ -20,7 +21,7 @@ const Page = styled.a` cursor: pointer; user-select: none; background: ${get('pagination.colors.normal.bg')}; - border: ${get('borders.1')} ${get('pagination.colors.normal.border')}; + border: ${get('borderWidths.1')} solid ${get('pagination.colors.normal.border')}; text-decoration: none; &:first-child { @@ -102,6 +103,7 @@ const PaginationContainer = styled.nav` margin-top: 20px; margin-bottom: 15px; text-align: center; + ${sx}; ` function Pagination({ @@ -148,7 +150,8 @@ Pagination.propTypes = { pageCount: PropTypes.number.isRequired, showPages: PropTypes.bool, surroundingPageCount: PropTypes.number, - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } Pagination.defaultProps = { diff --git a/src/Popover.js b/src/Popover.js index 60baef64dd2..4a6f8005d3f 100644 --- a/src/Popover.js +++ b/src/Popover.js @@ -3,8 +3,8 @@ import styled from 'styled-components' import classnames from 'classnames' import {COMMON, LAYOUT, POSITION, get} from './constants' import theme from './theme' -import elementType from './utils/elementType' import BorderBox from './BorderBox' +import sx from './sx' const Popover = styled.div.attrs(({className, caret}) => { return { @@ -18,6 +18,7 @@ const Popover = styled.div.attrs(({className, caret}) => { ${COMMON}; ${LAYOUT}; ${POSITION}; + ${sx}; ` Popover.Content = styled(BorderBox)` @@ -196,9 +197,11 @@ Popover.Content = styled(BorderBox)` bottom: calc(${get('space.3')} + 1px); } } + + ${sx}; ` -export const CARET_POSITIONS = [ +Popover.CARET_POSITIONS = [ 'top', 'bottom', 'left', @@ -219,14 +222,15 @@ Popover.defaultProps = { } Popover.propTypes = { - as: elementType, - caret: PropTypes.oneOf(CARET_POSITIONS), + as: PropTypes.elementType, + caret: PropTypes.oneOf(Popover.CARET_POSITIONS), open: PropTypes.bool, relative: PropTypes.bool, theme: PropTypes.object, ...COMMON.propTypes, ...LAYOUT.propTypes, - ...POSITION.propTypes + ...POSITION.propTypes, + ...sx.propTypes } Popover.Content.defaultProps = { @@ -234,9 +238,12 @@ Popover.Content.defaultProps = { } Popover.Content.propTypes = { - as: elementType, + as: PropTypes.elementType, theme: PropTypes.object, - ...BorderBox.propTypes + ...BorderBox.propTypes, + ...sx.propTypes } +Popover.Content.displayName = 'Popover.Content' + export default Popover diff --git a/src/Position.js b/src/Position.js index 7577be7616d..a4fbdd470c5 100644 --- a/src/Position.js +++ b/src/Position.js @@ -1,13 +1,14 @@ import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' -import {COMMON, LAYOUT, POSITION} from './constants' +import {POSITION} from './constants' +import Box from './Box' import theme from './theme' +import sx from './sx' -export const Position = styled.div` - ${LAYOUT} - ${COMMON} - ${POSITION} +const Position = styled(Box)` + ${POSITION}; + ${sx}; ` Position.defaultProps = { @@ -15,24 +16,25 @@ Position.defaultProps = { } Position.propTypes = { - ...COMMON.propTypes, - ...LAYOUT.propTypes, + ...Box.propTypes, ...POSITION.propTypes, - theme: PropTypes.object + theme: PropTypes.object, + ...sx.propTypes } function withPosition(position) { - const WithPosition = props => + const WithPosition = props => WithPosition.propTypes = Position.propTypes WithPosition.defaultProps = Position.defaultProps WithPosition.displayName = `Position.${position}` return WithPosition } -export const Absolute = withPosition('absolute') -export const Fixed = withPosition('fixed') -export const Relative = withPosition('relative') -export const Sticky = withPosition('sticky') +export default Position +export const Absolute = withPosition('Absolute') +export const Fixed = withPosition('Fixed') +export const Relative = withPosition('Relative') +export const Sticky = withPosition('Sticky') Sticky.defaultProps = { theme, top: 0, diff --git a/src/ProgressBar.js b/src/ProgressBar.js index ab8e48de405..739659f4373 100644 --- a/src/ProgressBar.js +++ b/src/ProgressBar.js @@ -5,6 +5,7 @@ import {layout} from 'styled-system' import systemPropTypes from '@styled-system/prop-types' import theme from './theme' import {COMMON, get} from './constants' +import sx from './sx' const Bar = styled.span` width: ${props => (props.progress ? `${props.progress}%` : 0)}; @@ -25,6 +26,7 @@ const ProgressContainer = styled.span` height: ${props => sizeMap[props.barSize]}; ${COMMON} ${layout.width} + ${sx}; ` const ProgressBar = ({progress, bg, theme, ...rest}) => { @@ -46,6 +48,7 @@ ProgressBar.propTypes = { barSize: PropTypes.oneOf(['small', 'default', 'large']), inline: PropTypes.bool, progress: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + ...sx.propTypes, theme: PropTypes.object, width: systemPropTypes.layout.width } diff --git a/src/SelectMenu/SelectMenu.js b/src/SelectMenu/SelectMenu.js index e6969f11570..102f15bcd29 100644 --- a/src/SelectMenu/SelectMenu.js +++ b/src/SelectMenu/SelectMenu.js @@ -1,6 +1,7 @@ import React, {useRef, useState} from 'react' import styled from 'styled-components' import PropTypes from 'prop-types' +import sx from '../sx' import {COMMON} from '../constants' import theme from '../theme' import {MenuContext} from './SelectMenuContext' @@ -46,6 +47,7 @@ const wrapperStyles = ` const StyledSelectMenu = styled.details` ${wrapperStyles} ${COMMON} + ${sx}; ` // 'as' is spread out because we don't want users to be able to change the tag. @@ -95,7 +97,8 @@ SelectMenu.defaultProps = { SelectMenu.propTypes = { initialTab: PropTypes.string, - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } export default SelectMenu diff --git a/src/SelectMenu/SelectMenuDivider.js b/src/SelectMenu/SelectMenuDivider.js index 7ecebf1c9ea..89826ddfca8 100644 --- a/src/SelectMenu/SelectMenuDivider.js +++ b/src/SelectMenu/SelectMenuDivider.js @@ -1,6 +1,7 @@ import styled, {css} from 'styled-components' import theme from '../theme' import {COMMON, get} from '../constants' +import sx from '../sx' const dividerStyles = css` padding: ${get('space.1')} ${get('space.3')}; @@ -9,12 +10,13 @@ const dividerStyles = css` font-weight: ${get('fontWeights.bold')}; color: ${get('colors.text.grayLight')}; background-color: ${get('colors.bg.gray')}; - border-bottom: ${get('borders.1')} ${get('colors.border.grayLight')}; + border-bottom: ${get('borderWidths.1')} solid ${get('colors.border.grayLight')}; ` const SelectMenuDivider = styled.div` ${dividerStyles} ${COMMON} + ${sx}; ` SelectMenuDivider.defaultProps = { @@ -22,7 +24,10 @@ SelectMenuDivider.defaultProps = { } SelectMenuDivider.propTypes = { - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } +SelectMenuDivider.displayName = 'SelectMenu.Divider' + export default SelectMenuDivider diff --git a/src/SelectMenu/SelectMenuFilter.js b/src/SelectMenu/SelectMenuFilter.js index 085868b9127..1066e925e37 100644 --- a/src/SelectMenu/SelectMenuFilter.js +++ b/src/SelectMenu/SelectMenuFilter.js @@ -5,20 +5,23 @@ import {COMMON, get} from '../constants' import theme from '../theme' import TextInput from '../TextInput' import {MenuContext} from './SelectMenuContext' +import sx from '../sx' const StyledForm = styled.form` padding: ${get('space.3')}; margin: 0; - border-top: ${get('borders.1')} ${get('colors.border.gray')}; + border-top: ${get('borderWidths.1')} solid ${get('colors.border.gray')}; background-color: ${get('colors.white')}; ${COMMON}; @media (min-width: ${get('breakpoints.0')}) { padding: ${get('space.2')}; } + + ${sx}; ` -function SelectMenuFilter({theme, value, ...rest}) { +function SelectMenuFilter({theme, value, sx, ...rest}) { const inputRef = useRef(null) const {open} = useContext(MenuContext) @@ -28,8 +31,9 @@ function SelectMenuFilter({theme, value, ...rest}) { inputRef.current.focus() } }, [open]) + return ( - + ) @@ -41,7 +45,10 @@ SelectMenuFilter.defaultProps = { SelectMenuFilter.propTypes = { ...COMMON.propTypes, + ...sx.propTypes, value: PropTypes.string } +SelectMenuFilter.displayName = 'SelectMenu.Filter' + export default SelectMenuFilter diff --git a/src/SelectMenu/SelectMenuFooter.js b/src/SelectMenu/SelectMenuFooter.js index 4e107684879..a3313ebce2b 100644 --- a/src/SelectMenu/SelectMenuFooter.js +++ b/src/SelectMenu/SelectMenuFooter.js @@ -1,6 +1,7 @@ import styled, {css} from 'styled-components' import {COMMON, get} from '../constants' import theme from '../theme' +import sx from '../sx' const footerStyles = css` margin-top: -1px; @@ -8,7 +9,7 @@ const footerStyles = css` font-size: ${get('fontSizes.0')}; color: ${get('colors.text.grayLight')}; text-align: center; - border-top: ${get('borders.1')} ${get('colors.border.gray')}; + border-top: ${get('borderWidths.1')} solid ${get('colors.border.gray')}; @media (min-width: ${get('breakpoints.0')}) { padding: ${get('space.1')} ${get('space.2')}; @@ -18,6 +19,7 @@ const footerStyles = css` const SelectMenuFooter = styled.footer` ${footerStyles} ${COMMON} + ${sx}; ` SelectMenuFooter.defaultProps = { @@ -25,7 +27,10 @@ SelectMenuFooter.defaultProps = { } SelectMenuFooter.propTypes = { - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } +SelectMenuFooter.displayName = 'SelectMenu.Footer' + export default SelectMenuFooter diff --git a/src/SelectMenu/SelectMenuHeader.js b/src/SelectMenu/SelectMenuHeader.js index 8f173cb6a62..b05b521641d 100644 --- a/src/SelectMenu/SelectMenuHeader.js +++ b/src/SelectMenu/SelectMenuHeader.js @@ -1,7 +1,9 @@ import React from 'react' import styled from 'styled-components' +import PropTypes from 'prop-types' import {get, COMMON, TYPOGRAPHY} from '../constants' import theme from '../theme' +import sx from '../sx' // SelectMenu.Header is intentionally not exported, it's an internal component used in // SelectMenu.Modal @@ -21,7 +23,7 @@ const StyledHeader = styled.header` display: flex; flex: none; // fixes header from getting squeezed in Safari iOS padding: ${get('space.3')}; - border-bottom: ${get('borders.1')} ${get('colors.border.gray')}; + border-bottom: ${get('borderWidths')} solid ${get('colors.border.gray')}; ${COMMON} ${TYPOGRAPHY} @@ -29,6 +31,8 @@ const StyledHeader = styled.header` padding-top: ${get('space.2')}; padding-bottom: ${get('space.2')}; } + + ${sx}; ` const SelectMenuHeader = ({children, theme, ...rest}) => { return ( @@ -42,4 +46,13 @@ SelectMenuHeader.defaultProps = { theme } +SelectMenuHeader.propTypes = { + theme: PropTypes.object, + ...COMMON.propTypes, + ...TYPOGRAPHY.propTypes, + ...sx.propTypes +} + +SelectMenuHeader.displayName = 'SelectMenu.Header' + export default SelectMenuHeader diff --git a/src/SelectMenu/SelectMenuItem.js b/src/SelectMenu/SelectMenuItem.js index cbd293bae71..02f36ad0a8f 100644 --- a/src/SelectMenu/SelectMenuItem.js +++ b/src/SelectMenu/SelectMenuItem.js @@ -6,6 +6,7 @@ import {MenuContext} from './SelectMenuContext' import {COMMON, get} from '../constants' import StyledOcticon from '../StyledOcticon' import theme from '../theme' +import sx from '../sx' export const listItemStyles = css` display: flex; @@ -16,9 +17,11 @@ export const listItemStyles = css` cursor: pointer; background-color: ${get('colors.white')}; border: 0; - border-bottom: ${get('borders.1')} ${get('colors.border.grayLight')}; + border-bottom: ${get('borderWidths.1')} solid ${get('colors.border.grayLight')}; color: ${get('colors.text.gray')}; text-decoration: none; + font-size: ${get('fontSizes.0')}; + width: 100%; &:hover { text-decoration: none; @@ -93,11 +96,10 @@ const StyledItem = styled.a.attrs(() => ({ }))` ${listItemStyles} ${COMMON} + ${sx}; ` -// 'as' is spread out because we don't want users to be able to change the tag. using something -// other than 'a' will break a11y. -const SelectMenuItem = ({children, selected, theme, onClick, as, ...rest}) => { +const SelectMenuItem = ({children, selected, theme, onClick, ...rest}) => { const menuContext = useContext(MenuContext) // close the menu when an item is clicked @@ -124,7 +126,10 @@ SelectMenuItem.defaultProps = { SelectMenuItem.propTypes = { selected: PropTypes.bool, - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } +SelectMenuItem.displayName = 'SelectMenu.Item' + export default SelectMenuItem diff --git a/src/SelectMenu/SelectMenuList.js b/src/SelectMenu/SelectMenuList.js index b93dbb114d1..b16afdab4ff 100644 --- a/src/SelectMenu/SelectMenuList.js +++ b/src/SelectMenu/SelectMenuList.js @@ -1,6 +1,7 @@ import styled, {css} from 'styled-components' import theme from '../theme' import {COMMON, get} from '../constants' +import sx from '../sx' const listStyles = css` position: relative; @@ -32,13 +33,17 @@ const listStyles = css` const SelectMenuList = styled.div` ${listStyles} ${COMMON} + ${sx}; ` SelectMenuList.defaultProps = { theme } SelectMenuList.propTypes = { - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } +SelectMenuList.displayName = 'SelectMenu.List' + export default SelectMenuList diff --git a/src/SelectMenu/SelectMenuModal.js b/src/SelectMenu/SelectMenuModal.js index fc172c2cecf..1767e6bf9a0 100644 --- a/src/SelectMenu/SelectMenuModal.js +++ b/src/SelectMenu/SelectMenuModal.js @@ -1,7 +1,9 @@ import React from 'react' +import PropTypes from 'prop-types' import styled, {keyframes, css} from 'styled-components' import {COMMON, get} from '../constants' import theme from '../theme' +import sx from '../sx' const animateModal = keyframes` 0% { @@ -32,7 +34,7 @@ const modalStyles = css` max-height: 350px; margin: ${get('space.1')} 0 ${get('space.3')} 0; font-size: ${get('fontSizes.0')}; - border: ${get('borders.1')} ${get('colors.border.grayDark')}; + border: ${get('borderWidths.1')} solid ${get('colors.border.grayDark')}; border-radius: ${get('radii.2')}; box-shadow: 0 1px 5px ${get('colors.blackfade15')} !default; } @@ -68,7 +70,7 @@ const modalWrapperStyles = css` @media (min-width: ${get('breakpoints.0')}) { position: absolute; top: auto; - right: auto; + right: ${props => (props.align === 'right' ? '0' : 'auto')}; bottom: auto; left: auto; padding: 0; @@ -82,6 +84,7 @@ const Modal = styled.div` const ModalWrapper = styled.div` ${modalWrapperStyles} ${COMMON} + ${sx}; ` const SelectMenuModal = ({children, theme, ...rest}) => { @@ -93,11 +96,16 @@ const SelectMenuModal = ({children, theme, ...rest}) => { } SelectMenuModal.defaultProps = { + align: 'left', theme } - SelectMenuModal.propTypes = { - ...COMMON.propTypes + align: PropTypes.oneOf(['left', 'right']), + theme: PropTypes.object, + ...COMMON.propTypes, + ...sx.propTypes } +SelectMenuModal.displayName = 'SelectMenu.Modal' + export default SelectMenuModal diff --git a/src/SelectMenu/SelectMenuTab.js b/src/SelectMenu/SelectMenuTab.js index d8c989f5b49..5e668c0a71a 100644 --- a/src/SelectMenu/SelectMenuTab.js +++ b/src/SelectMenu/SelectMenuTab.js @@ -5,6 +5,7 @@ import styled, {css} from 'styled-components' import {MenuContext} from './SelectMenuContext' import {get, COMMON} from '../constants' import theme from '../theme' +import sx from '../sx' const tabStyles = css` flex: 1; @@ -20,7 +21,7 @@ const tabStyles = css` @media (min-width: ${get('breakpoints.0')}) { flex: none; padding: ${get('space.1')} ${get('space.3')}; - border: ${get('borders.1')} transparent; + border: ${get('borderWidths.1')} solid transparent; border-bottom-width: 0; border-top-left-radius: ${get('radii.2')}; border-top-right-radius: ${get('radii.2')}; @@ -47,6 +48,7 @@ const tabStyles = css` const StyledTab = styled.button` ${tabStyles} ${COMMON} + ${sx}; ` const SelectMenuTab = ({tabName, index, className, onClick, ...rest}) => { @@ -89,7 +91,10 @@ SelectMenuTab.propTypes = { index: PropTypes.number, onClick: PropTypes.func, tabName: PropTypes.string, - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } +SelectMenuTab.displayName = 'SelectMenu.Tab' + export default SelectMenuTab diff --git a/src/SelectMenu/SelectMenuTabPanel.js b/src/SelectMenu/SelectMenuTabPanel.js index 18166180c72..0eddb9d3cb2 100644 --- a/src/SelectMenu/SelectMenuTabPanel.js +++ b/src/SelectMenu/SelectMenuTabPanel.js @@ -5,6 +5,7 @@ import {MenuContext} from './SelectMenuContext' import SelectMenuList from './SelectMenuList' import theme from '../theme' import {COMMON, get} from '../constants' +import sx from '../sx' const TabPanelBase = ({tabName, className, children, ...rest}) => { const menuContext = useContext(MenuContext) @@ -16,8 +17,9 @@ const TabPanelBase = ({tabName, className, children, ...rest}) => { } const TabPanel = styled(TabPanelBase)` - border-top: ${get('borders.1')} ${get('colors.border.gray')}; + border-top: ${get('borderWidths.1')} solid ${get('colors.border.gray')}; ${COMMON} + ${sx}; ` TabPanel.defaultProps = { @@ -26,7 +28,10 @@ TabPanel.defaultProps = { TabPanel.propTypes = { tabName: PropTypes.string, - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } +TabPanel.displayName = 'SelectMenu.TabPanel' + export default TabPanel diff --git a/src/SelectMenu/SelectMenuTabs.js b/src/SelectMenu/SelectMenuTabs.js index 7e665a2c049..c29af373eb4 100644 --- a/src/SelectMenu/SelectMenuTabs.js +++ b/src/SelectMenu/SelectMenuTabs.js @@ -2,6 +2,7 @@ import React from 'react' import styled, {css} from 'styled-components' import {COMMON, get} from '../constants' import theme from '../theme' +import sx from '../sx' const tabWrapperStyles = css` display: flex; @@ -33,6 +34,7 @@ const Tabs = ({children, ...rest}) => { const SelectMenuTabs = styled(Tabs)` ${tabWrapperStyles} ${COMMON} + ${sx}; ` SelectMenuTabs.defaultProps = { @@ -40,7 +42,10 @@ SelectMenuTabs.defaultProps = { } SelectMenuTabs.propTypes = { - ...COMMON.propTypes + ...COMMON.propTypes, + ...sx.propTypes } +SelectMenuTabs.displayName = 'SelectMenu.Tabs' + export default SelectMenuTabs diff --git a/src/SideNav.js b/src/SideNav.js index 6f0cde046e8..2959c367619 100644 --- a/src/SideNav.js +++ b/src/SideNav.js @@ -4,16 +4,16 @@ import styled, {css} from 'styled-components' import classnames from 'classnames' import {COMMON, get} from './constants' import theme from './theme' -import elementType from './utils/elementType' import Link from './Link' import BorderBox from './BorderBox' +import sx from './sx' function SideNavBase({variant, className, bordered, children, ...props}) { const variantClassName = variant === 'lightweight' ? 'lightweight' : 'normal' const newClassName = classnames(className, `variant-${variantClassName}`) if (!bordered) { - props = {...props, border: 'none'} + props = {...props, borderWidth: 0} } return ( @@ -38,6 +38,7 @@ const SideNav = styled(SideNavBase)` `} ${COMMON}; + ${sx}; ` SideNav.Link = styled(Link).attrs(props => { @@ -81,7 +82,7 @@ SideNav.Link = styled(Link).attrs(props => { color: ${get('colors.gray.6')}; padding: ${get('space.3')}; border: 0; - border-top: ${get('borders.1')} ${get('colors.gray.2')}; + border-top: ${get('borderWidths.1')} solid ${get('colors.gray.2')}; &:first-child { border-top: 0; @@ -136,6 +137,8 @@ SideNav.Link = styled(Link).attrs(props => { font-weight: ${get('fontWeights.semibold')}; } } + + ${sx}; ` SideNav.defaultProps = { @@ -144,13 +147,12 @@ SideNav.defaultProps = { } SideNav.propTypes = { - as: elementType, + as: PropTypes.elementType, bordered: PropTypes.bool, children: PropTypes.node, theme: PropTypes.object, variant: PropTypes.oneOf(['normal', 'lightweight']), - ...BorderBox.propTypes, - ...COMMON.propTypes + ...BorderBox.propTypes } SideNav.Link.defaultProps = { @@ -166,4 +168,6 @@ SideNav.Link.propTypes = { ...Link.propTypes } +SideNav.Link.displayName = 'SideNav.Link' + export default SideNav diff --git a/src/StateLabel.js b/src/StateLabel.js index bd0bda8baae..c756390ee29 100644 --- a/src/StateLabel.js +++ b/src/StateLabel.js @@ -1,64 +1,80 @@ import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' -import {GitMerge, GitPullRequest, IssueClosed, IssueOpened} from '@primer/octicons-react' -import theme, {colors} from './theme' +import {GitMerge, GitPullRequest, IssueClosed, IssueOpened, Question} from '@primer/octicons-react' +import {variant} from 'styled-system' +import theme from './theme' import {COMMON, get} from './constants' import StyledOcticon from './StyledOcticon' - -const statusMap = { - issueClosed: colors.red[6], - pullClosed: colors.red[5], - pullMerged: colors.purple[5], - issueOpened: '#159739', // Custom green - pullOpened: '#2cbe4e', // This was generated by a sass function in Primer, so using a hex here - gray: colors.gray[5] -} +import sx from './sx' +import {useDeprecation} from './utils/deprecate' const octiconMap = { issueOpened: IssueOpened, pullOpened: GitPullRequest, issueClosed: IssueClosed, pullClosed: GitPullRequest, - pullMerged: GitMerge + pullMerged: GitMerge, + draft: GitPullRequest } -function StateLabelBase({className, status, small = false, children}) { - const octiconProps = small ? {width: '1em'} : {} - return ( - - {status && } - {children} - - ) -} +const colorVariants = variant({ + prop: 'status', + scale: 'stateLabels.status' +}) + +const sizeVariants = variant({ + prop: 'variant', + scale: 'stateLabels.sizes' +}) -const StateLabel = styled(StateLabelBase)` +const StateLabelBase = styled.span` display: inline-flex; align-items: center; - padding: ${props => (props.small ? `4px 8px` : `8px 12px`)}; font-weight: 600; line-height: 16px; - color: ${colors.white}; - font-size: ${props => - props.small - ? theme.fontSizes[0] - : theme.fontSizes[1]}; // TODO: these should use the get function instead of referencing the theme directly + color: ${get('colors.white')}; text-align: center; - background-color: ${props => (props.status ? statusMap[props.status] : statusMap.gray)}; border-radius: ${get('radii.3')}; + ${colorVariants}; + ${sizeVariants}; ${COMMON}; + ${sx}; ` +function StateLabel({children, small, status, variant, ...rest}) { + const deprecate = useDeprecation({ + name: "StateLabel 'small' prop", + message: "Use variant='small' or variant='normal' instead.", + version: '20.0.0' + }) + + if (small) { + deprecate() + variant = 'small' + } + + const octiconProps = variant === 'small' ? {width: '1em'} : {} + return ( + + {status && } + {children} + + ) +} + StateLabel.defaultProps = { - theme + theme, + variant: 'normal' } StateLabel.propTypes = { small: PropTypes.bool, - status: PropTypes.oneOf(['issueOpened', 'pullOpened', 'issueClosed', 'pullClosed', 'pullMerged']), + status: PropTypes.oneOf(['issueOpened', 'pullOpened', 'issueClosed', 'pullClosed', 'pullMerged', 'draft']).isRequired, theme: PropTypes.object, - ...COMMON.propTypes + variant: PropTypes.oneOf(['small', 'normal']), + ...COMMON.propTypes, + ...sx.propTypes } export default StateLabel diff --git a/src/StyledOcticon.js b/src/StyledOcticon.js index a07450ceeb9..4b3052bc983 100644 --- a/src/StyledOcticon.js +++ b/src/StyledOcticon.js @@ -3,8 +3,12 @@ import PropTypes from 'prop-types' import styled from 'styled-components' import {COMMON} from './constants' import theme from './theme' +import sx from './sx' -const StyledOcticon = styled(Octicon)(COMMON) +const StyledOcticon = styled(Octicon)` + ${COMMON}; + ${sx}; +` StyledOcticon.defaultProps = { theme @@ -12,6 +16,7 @@ StyledOcticon.defaultProps = { StyledOcticon.propTypes = { ...COMMON.propTypes, + ...sx.propTypes, theme: PropTypes.object } diff --git a/src/SubNav.js b/src/SubNav.js index 6b4ab9f16bd..6eeebd7b0cb 100644 --- a/src/SubNav.js +++ b/src/SubNav.js @@ -5,11 +5,12 @@ import styled from 'styled-components' import {COMMON, FLEX, get} from './constants' import theme from './theme' import Flex from './Flex' +import sx from './sx' const ITEM_CLASS = 'SubNav-item' const SELECTED_CLASS = 'selected' -function SubNavBase({actions, className, children, label, ...rest}) { +function SubNavBase({actions, className, children, label, theme, ...rest}) { const classes = classnames(className, 'SubNav') return (