├── .eslintignore ├── .eslintrc ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .husky ├── .gitignore └── pre-commit ├── .percy.js ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── babel.config.js ├── jest.config.js ├── package-lock.json ├── package.json ├── renovate.json ├── scripts └── bumpversion.mjs ├── src ├── components │ ├── Accordion.js │ ├── Accordion.test.js │ ├── Button.js │ ├── Button.test.js │ ├── Checkbox.js │ ├── Checkbox.test.js │ ├── CheckboxGroup.js │ ├── Container.js │ ├── Container.test.js │ ├── DatePicker.js │ ├── DatePicker.test.js │ ├── Divider.js │ ├── Divider.test.js │ ├── Dropdown.js │ ├── Dropdown.test.js │ ├── Flex.js │ ├── Flex.test.js │ ├── Footer.js │ ├── Footer.test.js │ ├── Form.js │ ├── Form.test.js │ ├── Frequency.js │ ├── Frequency.test.js │ ├── Grid.js │ ├── Grid.test.js │ ├── Header.js │ ├── Header.test.js │ ├── Icon.js │ ├── Icon.test.js │ ├── Input.js │ ├── Input.test.js │ ├── Link.js │ ├── Link.test.js │ ├── List.js │ ├── List.test.js │ ├── LoadingIcon.js │ ├── LoadingIcon.test.js │ ├── Logo.js │ ├── Message.js │ ├── Message.test.js │ ├── Placeholder.js │ ├── Placeholder.test.js │ ├── RadioGroup.js │ ├── RadioGroup.test.js │ ├── Select.js │ ├── Select.test.js │ ├── ShadowContainer.js │ ├── Stack.js │ ├── Stack.test.js │ ├── Stepper.js │ ├── Stepper.test.js │ ├── Sticky.js │ ├── Sticky.test.js │ ├── Text.js │ ├── Text.test.js │ ├── Textarea.js │ ├── Textarea.test.js │ ├── TimeSpan.js │ ├── TimeSpan.test.js │ ├── VisuallyHidden.js │ ├── icon-list.js │ ├── index.js │ ├── internal │ │ ├── Field.js │ │ ├── FocusVisiblePolyfill.js │ │ ├── InternalCheckbox.js │ │ ├── InternalDropdown.js │ │ ├── InternalInput.js │ │ ├── InternalRadioGroup.js │ │ └── InternalSelect.js │ └── logo-list.js ├── hooks │ ├── internal │ │ ├── useField.js │ │ └── useForm.js │ ├── useAccordion.js │ ├── useAccordionItem.js │ ├── useAllResponsiveProps.js │ ├── useAllResponsiveProps.test.js │ ├── useBackground.js │ ├── useBreakpoint.js │ ├── useFooterLinks.js │ ├── useListType.js │ ├── useResponsiveProp.js │ ├── useResponsivePropsCSS.js │ ├── useResponsivePropsCSS.test.js │ ├── useSticky.js │ ├── useTextStyle.js │ ├── useTheme.js │ └── useWindow.js ├── icons │ ├── arrow-back.js │ ├── arrow-forward.js │ ├── assistance.js │ ├── birthday.js │ ├── blocking.js │ ├── calculator.js │ ├── call.js │ ├── chevron-down.js │ ├── chevron-left.js │ ├── chevron-right.js │ ├── chevron-up.js │ ├── comparison.js │ ├── critical.js │ ├── cross-small.js │ ├── cross.js │ ├── devices.js │ ├── document.js │ ├── download.js │ ├── duplicate.js │ ├── edit.js │ ├── external-link.js │ ├── eye-hidden.js │ ├── eye-visible.js │ ├── face-id.js │ ├── facebook.js │ ├── fingerprint.js │ ├── github.js │ ├── hamburger.js │ ├── home.js │ ├── info-or-minor.js │ ├── instagram.js │ ├── link.js │ ├── linkedin.js │ ├── lock-small.js │ ├── lock.js │ ├── logout.js │ ├── mail.js │ ├── message.js │ ├── notification-new.js │ ├── notification.js │ ├── overflow-menu.js │ ├── payment.js │ ├── person.js │ ├── question.js │ ├── search.js │ ├── select-object.js │ ├── shield.js │ ├── stop.js │ ├── stopwatch-alt.js │ ├── stopwatch.js │ ├── success.js │ ├── tick-small.js │ ├── tick.js │ ├── time.js │ ├── trash.js │ ├── triangle-down.js │ ├── triangle-up.js │ ├── twitter.js │ ├── warning-or-significant.js │ └── youtube.js ├── index.js ├── logos │ ├── gem.js │ └── latitude.js ├── providers │ ├── BasisProvider.js │ ├── BreakpointProvider.js │ ├── LinkProvider.js │ └── WindowProvider.js ├── themes │ └── default │ │ ├── accordion.js │ │ ├── button.js │ │ ├── checkbox.js │ │ ├── dropdown.js │ │ ├── field.js │ │ ├── index.js │ │ ├── input.js │ │ ├── link.js │ │ ├── list.js │ │ ├── radioGroup.js │ │ ├── select.js │ │ ├── stepper.js │ │ ├── text.js │ │ ├── textStyles.js │ │ └── textarea.js └── utils │ ├── array.js │ ├── component.js │ ├── component.test.js │ ├── constants.js │ ├── core.js │ ├── core.test.js │ ├── css.js │ ├── css.test.js │ ├── getDataAttributes.js │ ├── objectPath.js │ ├── sticky.js │ ├── sticky.test.js │ ├── string.js │ ├── string.test.js │ ├── test.js │ ├── theme.js │ └── theme.test.js ├── vercel.json ├── website ├── componentsStatus.js ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── gatsby-ssr.js ├── package-lock.json ├── package.json ├── react-error-overlay.js └── src │ ├── components │ ├── CacheProviderWithContainer.js │ ├── CheckboxesSetting.js │ ├── ComponentCode.js │ ├── ComponentContainer.js │ ├── ComponentPreview.js │ ├── ComponentStatusIndicator.js │ ├── ErrorBoundary.js │ ├── RadioGroupSetting.js │ ├── RangeSetting.js │ ├── SEO.js │ ├── Sidebar.js │ ├── Splitbee.js │ ├── kitchen-sink │ │ ├── KitchenSinkForm.js │ │ └── KitchenSinkLayout.js │ └── playground │ │ ├── PlaygroundCodeError.js │ │ ├── PlaygroundCodePanel.js │ │ ├── PlaygroundNewScreenSettings.js │ │ ├── PlaygroundScreen.js │ │ ├── PlaygroundScreenSettings.js │ │ ├── PlaygroundSettings.js │ │ ├── PlaygroundSettingsButton.js │ │ ├── PlaygroundSettingsInput.js │ │ ├── recoilState.js │ │ ├── useLocalStorageValue.js │ │ ├── utils.js │ │ └── utils.test.js │ ├── hooks │ ├── useCanary.js │ ├── useCopyToClipboard.js │ ├── useDebounce.js │ └── useLocalStorage.js │ ├── images │ └── gatsby-icon.png │ ├── layouts │ ├── Page.js │ ├── Resources.js │ └── Usage.js │ ├── pages │ ├── 404.js │ ├── colors │ │ ├── accessibility.js │ │ ├── index.js │ │ └── resources.mdx │ ├── components │ │ ├── accordion │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── button │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── checkbox-group │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── checkbox │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── container │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── date-picker │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── divider │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── dropdown │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── flex │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── footer │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── form │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── frequency │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── grid │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── header │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── icon │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── input │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── link │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── list │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── loading-icon │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── message │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── placeholder │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── radio-group │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── select │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── shadow-container │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── stack │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── stepper │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── sticky │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── text │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ ├── textarea │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ │ └── time-span │ │ │ ├── index.js │ │ │ ├── resources.mdx │ │ │ └── usage.mdx │ ├── index.js │ ├── kitchen-sink │ │ └── components │ │ │ ├── accordion.js │ │ │ ├── button.js │ │ │ ├── checkbox-group.js │ │ │ ├── checkbox.js │ │ │ ├── container.js │ │ │ ├── date-picker.js │ │ │ ├── divider.js │ │ │ ├── dropdown.js │ │ │ ├── flex.js │ │ │ ├── footer.js │ │ │ ├── frequency.js │ │ │ ├── grid.js │ │ │ ├── header.js │ │ │ ├── input.js │ │ │ ├── link.js │ │ │ ├── list.js │ │ │ ├── message.js │ │ │ ├── placeholder.js │ │ │ ├── radio-group.js │ │ │ ├── select.js │ │ │ ├── shadow-container.js │ │ │ ├── stack.js │ │ │ ├── stepper.js │ │ │ ├── sticky.js │ │ │ ├── text.js │ │ │ ├── textarea.js │ │ │ └── time-span.js │ ├── playground │ │ ├── index.js │ │ └── index.test.js │ ├── preview │ │ └── index.js │ ├── spacing │ │ ├── index.js │ │ └── usage.mdx │ └── typography │ │ ├── index.js │ │ └── usage.mdx │ ├── themes │ └── website │ │ ├── button.js │ │ ├── field.js │ │ ├── index.js │ │ ├── input.js │ │ ├── link.js │ │ └── select.js │ └── utils │ ├── ast.js │ ├── ast.test.js │ ├── color.js │ ├── color.test.js │ ├── constants.js │ ├── formatting.js │ ├── meta.js │ ├── meta.test.js │ └── url.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | public 4 | website/.cache 5 | website/node_modules 6 | website/public 7 | website/package-lock.json 8 | package-lock.json 9 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "eslint:recommended", 5 | "plugin:react/recommended", 6 | "plugin:jest/recommended", 7 | "plugin:testing-library/recommended", 8 | "plugin:testing-library/react", 9 | "plugin:jest-dom/recommended", 10 | "plugin:jsx-a11y/strict", 11 | "plugin:import/errors" 12 | ], 13 | "plugins": [ 14 | "import", 15 | "react-hooks", 16 | "jest", 17 | "testing-library", 18 | "jest-dom", 19 | "jsx-a11y" 20 | ], 21 | "rules": { 22 | "no-console": ["error", { "allow": ["error"] }], 23 | "no-unused-vars": [ 24 | "error", 25 | { "argsIgnorePattern": "^_", "ignoreRestSiblings": true } 26 | ], 27 | "import/no-cycle": "error", 28 | "react/jsx-boolean-value": [ 29 | "error", 30 | "never", 31 | { "always": ["initialValue"] } 32 | ], 33 | "react-hooks/rules-of-hooks": "error", 34 | "react-hooks/exhaustive-deps": "error", 35 | "jsx-a11y/label-has-for": "off" 36 | }, 37 | "env": { 38 | "browser": true, 39 | "es6": true, 40 | "node": true, 41 | "jest": true 42 | }, 43 | "settings": { 44 | "react": { 45 | "version": "detect" 46 | }, 47 | "import/resolver": { 48 | "alias": [["basis", "./src"]] 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | node-version: [12.x] 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v2 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | 22 | - name: Install 23 | run: npm install 24 | 25 | - name: Lint 26 | run: npm run lint 27 | 28 | - name: Test 29 | run: npm test 30 | 31 | - name: Build basis 32 | run: npm run build-basis 33 | 34 | - name: Build website 35 | run: npm run build 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | dist 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # Typescript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # gatsby files 61 | .cache/ 62 | public 63 | website/public 64 | 65 | # Mac files 66 | .DS_Store 67 | 68 | # Yarn 69 | yarn-error.log 70 | .pnp/ 71 | .pnp.js 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # VSCode 76 | .vscode 77 | 78 | # Vercel 79 | .vercel 80 | 81 | # JetBrains 82 | .idea -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npm run prettier && npm run lint && npm test 2 | -------------------------------------------------------------------------------- /.percy.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | version: 1, 3 | snapshot: { 4 | widths: [1280], 5 | }, 6 | "static-snapshots": { 7 | path: "public", 8 | "snapshot-files": "kitchen-sink", 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | public 4 | package-lock.json 5 | website/.cache 6 | website/node_modules 7 | website/public 8 | website/package-lock.json 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "useTabs": false, 4 | "tabWidth": 2, 5 | "semi": true, 6 | "singleQuote": false, 7 | "printWidth": 80 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Latitude Financial Services 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |  2 | 3 | # Basis Design System 4 | 5 | ## Installation 6 | 7 | ```shell 8 | npm install --save basis @emotion/core prop-types 9 | ``` 10 | 11 | Install the fonts that your theme needs. For example, if you are using the default theme: 12 | 13 | ```shell 14 | npm install --save typeface-{montserrat,roboto} 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```jsx 20 | import React from "react"; 21 | import { BasisProvider, defaultTheme, Text } from "basis"; 22 | import "typeface-montserrat"; 23 | import "typeface-roboto"; 24 | 25 | function App() { 26 | return ( 27 | 28 | Hello World 29 | 30 | ); 31 | } 32 | 33 | export default App; 34 | ``` 35 | 36 | ## Developing locally 37 | 38 | ```shell 39 | npm install 40 | npm start 41 | ``` 42 | 43 | ## Thanks 44 | 45 | - [Formidable Labs](https://formidable.com/) for creating [react-live](https://www.npmjs.com/package/react-live). 46 | - [Ryan Seddon](https://twitter.com/ryanseddon) for creating [react-frame-component](https://www.npmjs.com/package/react-frame-component). 47 | - [Sharvil Nanavati](https://twitter.com/snrrrub) for providing the `basis` npm package name. 48 | - [Vercel](https://vercel.com) for outstanding deployment experience. 49 | 50 | ## License 51 | 52 | MIT 53 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const presets = [ 2 | [ 3 | "@babel/env", 4 | { 5 | modules: process.env.BABEL_ENV === "cjs" ? "cjs" : false, 6 | }, 7 | ], 8 | "@babel/react", 9 | "@emotion/babel-preset-css-prop", 10 | ]; 11 | const plugins = ["@babel/plugin-proposal-class-properties"]; 12 | 13 | module.exports = { 14 | presets, 15 | plugins, 16 | }; 17 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testMatch: [ 3 | "/src/**/*.test.js", 4 | "/website/src/**/*.test.js", 5 | ], 6 | moduleNameMapper: { 7 | "^react($|/.+)": "/node_modules/react$1", 8 | basis: "/src", 9 | "typeface-montserrat": "identity-obj-proxy", 10 | "typeface-roboto": "identity-obj-proxy", 11 | }, 12 | bail: true, 13 | }; 14 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"], 3 | "schedule": "every weekend", 4 | "baseBranches": ["master", "2.0"], 5 | "packageRules": [ 6 | { 7 | "depTypeList": ["dependencies", "devDependencies"], 8 | "updateTypes": ["minor", "patch"], 9 | "rangeStrategy": "pin", 10 | "groupName": "minor and patch" 11 | }, 12 | { 13 | "depTypeList": ["dependencies", "devDependencies"], 14 | "updateTypes": ["major"], 15 | "rangeStrategy": "pin", 16 | "dependencyDashboardApproval": true, 17 | "dependencyDashboardAutoclose": true 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /src/components/Divider.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { nanoid } from "nanoid"; 4 | import useTheme from "../hooks/useTheme"; 5 | import { responsiveMarginType } from "../hooks/useResponsiveProp"; 6 | import useResponsivePropsCSS from "../hooks/useResponsivePropsCSS"; 7 | import { responsiveMargin } from "../utils/css"; 8 | 9 | const COLORS = ["grey.t07"]; 10 | const DEFAULT_PROPS = { 11 | color: "grey.t07", 12 | }; 13 | 14 | Divider.DEFAULT_PROPS = DEFAULT_PROPS; 15 | 16 | function Divider(_props) { 17 | const props = { ...DEFAULT_PROPS, ..._props }; 18 | const { color, testId } = props; 19 | const [patternId] = useState(() => `divider-${nanoid()}`); 20 | const theme = useTheme(); 21 | const circleColor = theme.getColor(color); 22 | const responsiveCSS = useResponsivePropsCSS(props, DEFAULT_PROPS, { 23 | margin: responsiveMargin, 24 | }); 25 | 26 | return ( 27 | 35 | 43 | 44 | 45 | 46 | 47 | 57 | 58 | ); 59 | } 60 | 61 | Divider.propTypes = { 62 | ...responsiveMarginType, 63 | color: PropTypes.oneOf(COLORS), 64 | testId: PropTypes.string, 65 | }; 66 | 67 | export default Divider; 68 | -------------------------------------------------------------------------------- /src/components/Divider.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "@testing-library/jest-dom/extend-expect"; 3 | import Divider from "./Divider"; 4 | import { render, screen } from "../utils/test"; 5 | 6 | describe("Divider", () => { 7 | it("with margin", () => { 8 | render(); 9 | 10 | const svg = screen.getByRole("img"); 11 | 12 | expect(svg).toHaveStyle({ 13 | margin: "24px 0px", 14 | }); 15 | }); 16 | 17 | it("with testId", () => { 18 | render(); 19 | 20 | const svg = screen.getByRole("img"); 21 | 22 | expect(svg).toHaveAttribute("data-testid", "my-divider"); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/components/Flex.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import useResponsivePropsCSS from "../hooks/useResponsivePropsCSS"; 4 | import { 5 | responsiveMarginType, 6 | responsiveWidthType, 7 | responsiveHeightType, 8 | responsivePropType, 9 | } from "../hooks/useResponsiveProp"; 10 | import { 11 | responsiveMargin, 12 | responsiveSize, 13 | responsiveFlexDirection, 14 | responsiveFlexPlaceItems, 15 | } from "../utils/css"; 16 | import { FLEX_DIRECTIONS, FLEX_PLACE_ITEMS } from "../utils/constants"; 17 | 18 | const DIRECTIONS = FLEX_DIRECTIONS; 19 | const PLACE_ITEMS = FLEX_PLACE_ITEMS; 20 | 21 | const DEFAULT_PROPS = { 22 | direction: "row", 23 | }; 24 | 25 | Flex.DIRECTIONS = DIRECTIONS; 26 | Flex.PLACE_ITEMS = PLACE_ITEMS; 27 | Flex.DEFAULT_PROPS = DEFAULT_PROPS; 28 | 29 | function Flex(_props) { 30 | const props = { ...DEFAULT_PROPS, ..._props }; 31 | const { children, testId } = props; 32 | const wrapperCSS = useResponsivePropsCSS(props, DEFAULT_PROPS, { 33 | margin: responsiveMargin, 34 | width: responsiveSize("width"), 35 | height: responsiveSize("height"), 36 | }); 37 | const flexCSS = useResponsivePropsCSS(props, DEFAULT_PROPS, { 38 | placeItems: responsiveFlexPlaceItems, 39 | direction: responsiveFlexDirection, 40 | }); 41 | 42 | return ( 43 | 50 | 58 | {children} 59 | 60 | 61 | ); 62 | } 63 | 64 | Flex.propTypes = { 65 | ...responsiveMarginType, 66 | ...responsiveWidthType, 67 | ...responsiveHeightType, 68 | ...responsivePropType("direction", PropTypes.oneOf(DIRECTIONS)), 69 | ...responsivePropType("placeItems", PropTypes.oneOf(PLACE_ITEMS)), 70 | children: PropTypes.node.isRequired, 71 | testId: PropTypes.string, 72 | }; 73 | 74 | export default Flex; 75 | -------------------------------------------------------------------------------- /src/components/Flex.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "../utils/test"; 3 | import "@testing-library/jest-dom/extend-expect"; 4 | import Flex from "./Flex"; 5 | 6 | describe("Flex", () => { 7 | it("with width", () => { 8 | const { container } = render(Content goes here); 9 | 10 | expect(container.firstChild).toHaveStyle({ 11 | width: "320px", 12 | }); 13 | }); 14 | 15 | it("with height", () => { 16 | const { container } = render(Content goes here); 17 | 18 | expect(container.firstChild).toHaveStyle({ 19 | height: "100%", 20 | }); 21 | }); 22 | 23 | it("with testId", () => { 24 | const { container } = render( 25 | Content goes here 26 | ); 27 | 28 | expect(container.firstChild).toHaveAttribute("data-testid", "my-flex"); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/components/Grid.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "@testing-library/jest-dom/extend-expect"; 3 | import Grid from "./Grid"; 4 | import { render, screen } from "../utils/test"; 5 | 6 | describe("Grid", () => { 7 | it("with margin", () => { 8 | render(Hello); 9 | 10 | expect(screen.getByText("Hello")).toHaveStyle({ 11 | margin: "24px 0 0 0", 12 | }); 13 | }); 14 | 15 | it("with height", () => { 16 | render(Hello); 17 | 18 | expect(screen.getByText("Hello")).toHaveStyle({ 19 | height: "300px", 20 | }); 21 | }); 22 | 23 | it("with testId", () => { 24 | render( 25 | 26 | Hello 27 | 28 | ); 29 | 30 | const gridItem = screen.getByText("Hello"); 31 | 32 | expect(gridItem).toHaveAttribute("data-testid", "my-grid-item"); 33 | expect(gridItem.parentElement).toHaveAttribute("data-testid", "my-grid"); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import PropTypes from "prop-types"; 3 | import Container from "./Container"; 4 | import Flex from "./Flex"; 5 | import Logo from "./Logo"; 6 | import { getPropsFromMap } from "../utils/component"; 7 | 8 | function HeaderLogo({ name, testId }) { 9 | return ( 10 | 19 | ); 20 | } 21 | 22 | HeaderLogo.propTypes = { 23 | name: PropTypes.oneOf(Logo.NAMES).isRequired, 24 | testId: PropTypes.string, 25 | }; 26 | 27 | const DEFAULT_PROPS = {}; 28 | 29 | Header.DEFAULT_PROPS = DEFAULT_PROPS; 30 | Header.ID = "Header"; 31 | Header.HEIGHT_MAP = { 32 | default: 56, 33 | lg: 80, 34 | }; 35 | 36 | function Header(_props) { 37 | const props = { ...DEFAULT_PROPS, ..._props }; 38 | const { children, testId } = props; 39 | const heightMap = Header.HEIGHT_MAP; 40 | const heightProps = useMemo(() => getPropsFromMap("height", heightMap), [ 41 | heightMap, 42 | ]); 43 | 44 | return ( 45 | 46 | 47 | 48 | 49 | {children} 50 | 51 | 52 | 53 | 54 | ); 55 | } 56 | 57 | Header.propTypes = { 58 | children: PropTypes.node.isRequired, 59 | testId: PropTypes.string, 60 | }; 61 | 62 | Header.Logo = HeaderLogo; 63 | 64 | export default Header; 65 | -------------------------------------------------------------------------------- /src/components/Header.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "@testing-library/jest-dom/extend-expect"; 3 | import Header from "./Header"; 4 | import { render, screen } from "../utils/test"; 5 | 6 | describe("Header", () => { 7 | it("exposes an ID", () => { 8 | expect(Header.ID).toBe("Header"); 9 | }); 10 | 11 | it("exposes a HEIGHT_MAP", () => { 12 | expect(Header.HEIGHT_MAP).toStrictEqual({ 13 | default: 56, 14 | lg: 80, 15 | }); 16 | }); 17 | 18 | it("with testId", () => { 19 | const { container } = render( 20 | 21 | 22 | 23 | ); 24 | 25 | expect(container.firstChild).toHaveAttribute("data-testid", "my-header"); 26 | expect(screen.getByTestId("my-header-logo")).toBeInTheDocument(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/components/Icon.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "../utils/test"; 3 | import "@testing-library/jest-dom/extend-expect"; 4 | import Icon from "./Icon"; 5 | 6 | describe("Icon", () => { 7 | Icon.NAMES.forEach((name) => { 8 | it(`Icon - ${name}`, () => { 9 | const { container } = render(); 10 | const svg = container.firstChild; 11 | 12 | expect(svg).toHaveAttribute("role", "img"); 13 | expect(svg).toHaveAttribute("data-testid", `my-${name}`); 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/components/LoadingIcon.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "../utils/test"; 3 | import "@testing-library/jest-dom/extend-expect"; 4 | import LoadingIcon from "./LoadingIcon"; 5 | 6 | describe("LoadingIcon", () => { 7 | it("with testId", () => { 8 | const { container } = render(); 9 | 10 | expect(container.firstChild).toHaveAttribute( 11 | "data-testid", 12 | "my-loading-icon" 13 | ); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/components/Logo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { 4 | responsiveHeightType, 5 | responsiveMaxWidthType, 6 | } from "../hooks/useResponsiveProp"; 7 | import useAllResponsiveProps from "../hooks/useAllResponsiveProps"; 8 | import { logoListMapping } from "./logo-list"; 9 | 10 | const NAMES = ["latitude", "gem"]; 11 | const COLORS = ["primary.blue.t100", "black", "white"]; 12 | 13 | const DEFAULT_PROPS = {}; 14 | 15 | Logo.NAMES = NAMES; 16 | Logo.COLORS = COLORS; 17 | Logo.DEFAULT_PROPS = DEFAULT_PROPS; 18 | 19 | function Logo(_props) { 20 | const props = { ...DEFAULT_PROPS, ..._props }; 21 | const { name, color, testId } = props; 22 | const heightProps = useAllResponsiveProps(props, "height"); 23 | const maxWidthProps = useAllResponsiveProps(props, "maxWidth"); 24 | 25 | if (!NAMES.includes(name)) { 26 | return null; 27 | } 28 | 29 | const LogoComponent = logoListMapping[name]; 30 | const logoProps = { 31 | color, 32 | ...heightProps, 33 | ...maxWidthProps, 34 | testId, 35 | }; 36 | 37 | return ; 38 | } 39 | 40 | Logo.propTypes = { 41 | name: PropTypes.oneOf(NAMES).isRequired, 42 | color: PropTypes.oneOf(COLORS), 43 | ...responsiveHeightType, 44 | ...responsiveMaxWidthType, 45 | testId: PropTypes.string, 46 | }; 47 | 48 | export default Logo; 49 | -------------------------------------------------------------------------------- /src/components/Placeholder.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import useTheme from "../hooks/useTheme"; 4 | import { 5 | responsiveWidthType, 6 | responsiveHeightType, 7 | } from "../hooks/useResponsiveProp"; 8 | import useResponsivePropsCSS from "../hooks/useResponsivePropsCSS"; 9 | import { responsiveSize } from "../utils/css"; 10 | import { mergeProps } from "../utils/component"; 11 | import Flex from "./Flex"; 12 | import Text from "./Text"; 13 | 14 | const DEFAULT_PROPS = { 15 | height: "72px", 16 | label: "Placeholder", 17 | }; 18 | 19 | function Placeholder(props) { 20 | const theme = useTheme(); 21 | const mergedProps = mergeProps( 22 | props, 23 | DEFAULT_PROPS, 24 | {}, 25 | { 26 | label: (label) => typeof label === "string" && label.trim().length > 0, 27 | } 28 | ); 29 | const { label, testId } = mergedProps; 30 | const responsivePropsCSS = useResponsivePropsCSS(props, DEFAULT_PROPS, { 31 | width: responsiveSize("width"), 32 | height: responsiveSize("height"), 33 | }); 34 | 35 | return ( 36 | 45 | 46 | {label} 47 | 48 | 49 | ); 50 | } 51 | 52 | Placeholder.propTypes = { 53 | ...responsiveWidthType, 54 | ...responsiveHeightType, 55 | label: PropTypes.string, 56 | testId: PropTypes.string, 57 | }; 58 | 59 | export default Placeholder; 60 | -------------------------------------------------------------------------------- /src/components/Placeholder.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from "../utils/test"; 3 | import "@testing-library/jest-dom/extend-expect"; 4 | import Placeholder from "./Placeholder"; 5 | 6 | describe("Placeholder", () => { 7 | it("with width", () => { 8 | const { container } = render(); 9 | 10 | expect(container.firstChild).toHaveStyle({ 11 | width: "80px", 12 | }); 13 | }); 14 | 15 | it("with height", () => { 16 | const { container } = render(); 17 | 18 | expect(container.firstChild).toHaveStyle({ 19 | height: "100%", 20 | }); 21 | }); 22 | 23 | it("with label", () => { 24 | render(); 25 | 26 | expect(screen.getByText("Navigation placeholder")).toBeInTheDocument(); 27 | }); 28 | 29 | it("with testId", () => { 30 | const { container } = render(); 31 | 32 | expect(container.firstChild).toHaveAttribute( 33 | "data-testid", 34 | "my-placeholder" 35 | ); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/components/Stack.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "@testing-library/jest-dom/extend-expect"; 3 | import { Stack, Text } from "."; 4 | import { render, screen } from "../utils/test"; 5 | 6 | describe("Stack", () => { 7 | it("renders all children", () => { 8 | const { container } = render( 9 | 10 | Item 1 11 | Item 2 12 | Item 3 13 | Item 4 14 | 15 | ); 16 | 17 | expect(container.firstChild.firstChild.childElementCount).toBe(4); 18 | expect(screen.getByText("Item 1")).toBeInTheDocument(); 19 | expect(screen.getByText("Item 2")).toBeInTheDocument(); 20 | expect(screen.getByText("Item 3")).toBeInTheDocument(); 21 | expect(screen.getByText("Item 4")).toBeInTheDocument(); 22 | }); 23 | 24 | it("with margin", () => { 25 | const { container } = render( 26 | 27 | Hello 28 | World 29 | 30 | ); 31 | 32 | expect(container.firstChild).toHaveStyle({ 33 | margin: "16px 32px", 34 | }); 35 | }); 36 | 37 | it("with width", () => { 38 | const { container } = render( 39 | 40 | Hello 41 | World 42 | 43 | ); 44 | 45 | expect(container.firstChild).toHaveStyle({ 46 | width: "320px", 47 | }); 48 | }); 49 | 50 | it("with flatten", () => { 51 | const { container } = render( 52 | 53 | 1 54 | <> 55 | 2 56 | 3 57 | > 58 | 4 59 | 60 | ); 61 | 62 | expect(container.firstChild.firstChild.childElementCount).toBe(4); 63 | }); 64 | 65 | it("with testId", () => { 66 | const { container } = render( 67 | 68 | Hello 69 | World 70 | 71 | ); 72 | 73 | expect(container.firstChild).toHaveAttribute("data-testid", "my-stack"); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /src/components/Stepper.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "../utils/test"; 3 | import "@testing-library/jest-dom/extend-expect"; 4 | import Stepper from "./Stepper"; 5 | 6 | describe("Stepper", () => { 7 | it("exposes an ID", () => { 8 | expect(Stepper.ID).toBe("Stepper"); 9 | }); 10 | 11 | it("exposes a HEIGHT_MAP", () => { 12 | expect(Stepper.HEIGHT_MAP).toStrictEqual({ 13 | default: 100, 14 | }); 15 | }); 16 | 17 | it("with testId", () => { 18 | const { container } = render( 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | ); 32 | 33 | expect(container.firstChild).toHaveAttribute("data-testid", "my-stepper"); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/components/VisuallyHidden.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | // See: https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html 5 | const css = { 6 | position: "absolute", 7 | width: "1px", 8 | height: "1px", 9 | overflow: "hidden", 10 | whiteSpace: "nowrap", 11 | clip: "rect(0, 0, 0, 0)", 12 | clipPath: "inset(50%)", 13 | }; 14 | 15 | function VisuallyHidden({ children }) { 16 | if (typeof children === "string") { 17 | return {children}; 18 | } 19 | 20 | const child = React.Children.only(children); 21 | 22 | /* 23 | Note: We avoid adding extra DOM elements here since we rely on elements order in some places. 24 | For example, Radio has the following DOM structure: 25 | 26 | 27 | 28 | 29 | and one of the CSS selectors we use on the input is: ":checked + label" 30 | If we wrapped the input with a span/div, we'd break the styling. 31 | */ 32 | return React.cloneElement(child, { 33 | // See: https://github.com/emotion-js/emotion/issues/1713#issuecomment-574121500 34 | style: css, 35 | }); 36 | } 37 | 38 | VisuallyHidden.propTypes = { 39 | children: PropTypes.node.isRequired, 40 | }; 41 | 42 | export default VisuallyHidden; 43 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as Accordion } from "./Accordion"; 2 | export { default as Button } from "./Button"; 3 | export { default as Checkbox } from "./Checkbox"; 4 | export { default as CheckboxGroup } from "./CheckboxGroup"; 5 | export { default as Container } from "./Container"; 6 | export { default as DatePicker } from "./DatePicker"; 7 | export { default as Divider } from "./Divider"; 8 | export { default as Dropdown } from "./Dropdown"; 9 | export { default as Flex } from "./Flex"; 10 | export { default as Form } from "./Form"; 11 | export { default as Footer } from "./Footer"; 12 | export { default as Frequency } from "./Frequency"; 13 | export { default as Grid } from "./Grid"; 14 | export { default as Header } from "./Header"; 15 | export { default as Icon } from "./Icon"; 16 | export { default as Input } from "./Input"; 17 | export { default as Logo } from "./Logo"; 18 | export { default as Link } from "./Link"; 19 | export { default as List } from "./List"; 20 | export { default as LoadingIcon } from "./LoadingIcon"; 21 | export { default as Message } from "./Message"; 22 | export { default as Placeholder } from "./Placeholder"; 23 | export { default as RadioGroup } from "./RadioGroup"; 24 | export { default as Select } from "./Select"; 25 | export { default as ShadowContainer } from "./ShadowContainer"; 26 | export { default as Stack } from "./Stack"; 27 | export { default as Stepper } from "./Stepper"; 28 | export { default as Sticky } from "./Sticky"; 29 | export { default as Text } from "./Text"; 30 | export { default as Textarea } from "./Textarea"; 31 | export { default as TimeSpan } from "./TimeSpan"; 32 | export { default as VisuallyHidden } from "./VisuallyHidden"; 33 | -------------------------------------------------------------------------------- /src/components/internal/FocusVisiblePolyfill.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import useWindow from "../../hooks/useWindow"; 3 | 4 | function FocusVisiblePolyfill() { 5 | const [isKeyboardMode, setIsKeyboardMode] = useState(false); 6 | const onKeyDown = () => setIsKeyboardMode(true); 7 | const onMouseDown = () => setIsKeyboardMode(false); 8 | const window = useWindow(); 9 | 10 | useEffect(() => { 11 | if (window) { 12 | window.addEventListener("keydown", onKeyDown); 13 | window.addEventListener("mousedown", onMouseDown); 14 | } 15 | 16 | return () => { 17 | if (window) { 18 | window.removeEventListener("keydown", onKeyDown); 19 | window.removeEventListener("mousedown", onMouseDown); 20 | } 21 | }; 22 | }, [window]); 23 | 24 | useEffect(() => { 25 | if (window) { 26 | window.document.body.dataset.basisKeyboardMode = String(isKeyboardMode); 27 | } 28 | }, [window, isKeyboardMode]); 29 | 30 | return null; 31 | } 32 | 33 | export default FocusVisiblePolyfill; 34 | -------------------------------------------------------------------------------- /src/components/logo-list.js: -------------------------------------------------------------------------------- 1 | import Gem from "../logos/gem"; 2 | import Latitude from "../logos/latitude"; 3 | 4 | export const logoListMapping = { 5 | gem: Gem, 6 | latitude: Latitude, 7 | }; 8 | -------------------------------------------------------------------------------- /src/hooks/internal/useField.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react"; 2 | import useForm from "./useForm"; 3 | import { getPath } from "../../utils/objectPath"; 4 | 5 | function useField(componentName, { name, disabled, optional, validate, data }) { 6 | if (typeof name !== "string" || name.trim() === "") { 7 | throw new Error(`${componentName} component is missing a name prop`); 8 | } 9 | const { 10 | state, 11 | onFocus, 12 | onBlur, 13 | onChange, 14 | onMouseDown, 15 | registerField, 16 | unregisterField, 17 | } = useForm(componentName); 18 | 19 | const registerDataRef = useRef({ 20 | registerField, 21 | unregisterField, 22 | name, 23 | disabled, 24 | optional, 25 | validate, 26 | data, 27 | }); 28 | 29 | if (typeof state.values === "undefined") { 30 | throw new Error("Form is missing initialValues"); 31 | } 32 | 33 | const value = getPath(state.values, name); 34 | 35 | if (typeof value === "undefined") { 36 | throw new Error(`${name} is missing in Form's initialValues`); 37 | } 38 | 39 | const errors = getPath(state.errors, name); 40 | const hasErrors = Array.isArray(errors) && errors.length > 0; 41 | 42 | // we need to store the data in a ref so that if the validateData prop changes, we have a reference 43 | // that we can update with the new value. 44 | useEffect(() => { 45 | registerDataRef.current = { 46 | registerField, 47 | unregisterField, 48 | name, 49 | disabled, 50 | optional, 51 | validate, 52 | data, 53 | }; 54 | }, [ 55 | name, 56 | disabled, 57 | optional, 58 | validate, 59 | data, 60 | registerField, 61 | unregisterField, 62 | ]); 63 | 64 | useEffect(() => { 65 | registerField(name, registerDataRef); 66 | 67 | return () => { 68 | unregisterField(name); 69 | }; 70 | }, [registerField, unregisterField, name]); 71 | 72 | return { 73 | value, 74 | errors, 75 | hasErrors, 76 | onFocus, 77 | onBlur, 78 | onChange, 79 | onMouseDown, 80 | }; 81 | } 82 | 83 | export default useField; 84 | -------------------------------------------------------------------------------- /src/hooks/internal/useForm.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | 3 | const FormContext = React.createContext(); 4 | 5 | export const FormProvider = FormContext.Provider; 6 | 7 | function useForm(componentName) { 8 | const context = useContext(FormContext); 9 | 10 | if (context) { 11 | return context; 12 | } 13 | 14 | throw new Error(`${componentName} must be placed inside a Form`); 15 | } 16 | 17 | export default useForm; 18 | -------------------------------------------------------------------------------- /src/hooks/useAccordion.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | 3 | const AccordionContext = React.createContext(); 4 | 5 | export const AccordionProvider = AccordionContext.Provider; 6 | 7 | function useAccordion() { 8 | const accordionInfo = useContext(AccordionContext); 9 | 10 | return accordionInfo; 11 | } 12 | 13 | export default useAccordion; 14 | -------------------------------------------------------------------------------- /src/hooks/useAccordionItem.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | 3 | const AccordionItemContext = React.createContext(); 4 | 5 | export const AccordionItemProvider = AccordionItemContext.Provider; 6 | 7 | function useAccordionItem() { 8 | const accordionItemInfo = useContext(AccordionItemContext); 9 | const { 10 | headerId, 11 | contentId, 12 | isOpen, 13 | toggleAccordionItem, 14 | } = accordionItemInfo ?? { 15 | headerId: null, 16 | contentId: null, 17 | isOpen: false, 18 | toggleAccordionItem: () => {}, 19 | }; 20 | 21 | return { 22 | headerId, 23 | contentId, 24 | isOpen, 25 | toggleAccordionItem, 26 | }; 27 | } 28 | 29 | export default useAccordionItem; 30 | -------------------------------------------------------------------------------- /src/hooks/useAllResponsiveProps.js: -------------------------------------------------------------------------------- 1 | import useTheme from "./useTheme"; 2 | import { getPropName } from "./useResponsiveProp"; 3 | import { hasOwnProperty } from "../utils/core"; 4 | 5 | function useAllResponsiveProps(props, propName) { 6 | const theme = useTheme(); 7 | const breakpoints = Object.keys(theme.breakpoints); 8 | const result = {}; 9 | 10 | if (hasOwnProperty(props, propName)) { 11 | result[propName] = props[propName]; 12 | } 13 | 14 | breakpoints.forEach((bp) => { 15 | const prop = getPropName(propName, bp); 16 | 17 | if (hasOwnProperty(props, prop)) { 18 | result[prop] = props[prop]; 19 | } 20 | }); 21 | 22 | return result; 23 | } 24 | 25 | export default useAllResponsiveProps; 26 | -------------------------------------------------------------------------------- /src/hooks/useAllResponsiveProps.test.js: -------------------------------------------------------------------------------- 1 | import { renderHook } from "@testing-library/react-hooks"; 2 | import useAllResponsiveProps from "./useAllResponsiveProps"; 3 | import { TestWrapper } from "../utils/test"; 4 | 5 | describe("useAllResponsiveProps", () => { 6 | it("collects all the responsive props", () => { 7 | const props = { 8 | padding: "4", 9 | "padding-xs": 5, 10 | "padding-sm": "0", 11 | "padding-md": "6", 12 | "padding-lg": "1 2 3", 13 | "padding-xl": "0 8", 14 | anotherProp: "some value", 15 | }; 16 | const { result } = renderHook( 17 | () => useAllResponsiveProps(props, "padding"), 18 | { wrapper: TestWrapper } 19 | ); 20 | 21 | expect(result.current).toStrictEqual({ 22 | padding: "4", 23 | "padding-xs": 5, 24 | "padding-sm": "0", 25 | "padding-md": "6", 26 | "padding-lg": "1 2 3", 27 | "padding-xl": "0 8", 28 | }); 29 | }); 30 | 31 | it("drops responsive props that do not exist", () => { 32 | const props = { 33 | "padding-xs": 5, 34 | "padding-md": "6", 35 | anotherProp: "some value", 36 | }; 37 | const { result } = renderHook( 38 | () => useAllResponsiveProps(props, "padding"), 39 | { wrapper: TestWrapper } 40 | ); 41 | 42 | expect(result.current).toStrictEqual({ 43 | "padding-xs": 5, 44 | "padding-md": "6", 45 | }); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /src/hooks/useBackground.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import PropTypes from "prop-types"; 3 | import useTheme from "./useTheme"; 4 | import { DEFAULT_BREAKPOINT } from "../utils/css"; 5 | 6 | function getInputColor(backgroundColor) { 7 | return [undefined, "transparent", "white"].includes(backgroundColor) 8 | ? "grey.t05" 9 | : "white"; 10 | } 11 | 12 | export function mapResponsiveValues(map, mapFn, theme) { 13 | if (map) { 14 | const result = { 15 | [DEFAULT_BREAKPOINT]: mapFn(map[DEFAULT_BREAKPOINT]), 16 | }; 17 | 18 | for (const bp in theme.breakpoints) { 19 | result[bp] = mapFn(map[bp]); 20 | } 21 | 22 | return result; 23 | } 24 | 25 | const defaultValue = mapFn(); 26 | const result = { 27 | [DEFAULT_BREAKPOINT]: defaultValue, 28 | }; 29 | 30 | for (const bp in theme.breakpoints) { 31 | result[bp] = defaultValue; 32 | } 33 | 34 | return result; 35 | } 36 | 37 | function getBgMapFromValue(value, theme) { 38 | const result = { 39 | [DEFAULT_BREAKPOINT]: value, 40 | }; 41 | 42 | for (const bp in theme.breakpoints) { 43 | result[bp] = value; 44 | } 45 | 46 | return result; 47 | } 48 | 49 | const BackgroundContext = React.createContext(); 50 | 51 | export function BackgroundProvider({ value, children }) { 52 | const theme = useTheme(); 53 | const bgMap = 54 | typeof value === "object" ? value : getBgMapFromValue(value, theme); 55 | 56 | return ( 57 | 58 | {children} 59 | 60 | ); 61 | } 62 | 63 | BackgroundProvider.propTypes = { 64 | value: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), 65 | children: PropTypes.node, 66 | }; 67 | 68 | function useBackground() { 69 | const theme = useTheme(); 70 | const bgMap = useContext(BackgroundContext); 71 | const inputColorMap = mapResponsiveValues(bgMap, getInputColor, theme); 72 | 73 | return { 74 | bgMap, 75 | inputColorMap, 76 | }; 77 | } 78 | 79 | export default useBackground; 80 | -------------------------------------------------------------------------------- /src/hooks/useBreakpoint.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | 3 | export const BreakpointContext = React.createContext(); 4 | 5 | function useBreakpoint() { 6 | const breakpoint = useContext(BreakpointContext); 7 | 8 | return breakpoint; 9 | } 10 | 11 | export default useBreakpoint; 12 | -------------------------------------------------------------------------------- /src/hooks/useFooterLinks.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | 3 | const FooterLinksContext = React.createContext(); 4 | 5 | export const FooterLinksProvider = FooterLinksContext.Provider; 6 | 7 | function useFooterLinks() { 8 | return useContext(FooterLinksContext); 9 | } 10 | 11 | export default useFooterLinks; 12 | -------------------------------------------------------------------------------- /src/hooks/useListType.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | 3 | const ListTypeContext = React.createContext(); 4 | 5 | export const ListTypeProvider = ListTypeContext.Provider; 6 | 7 | function useListType() { 8 | const listInfo = useContext(ListTypeContext); 9 | const { type, variant } = listInfo ?? { 10 | type: null, 11 | variant: null, 12 | }; 13 | 14 | return { 15 | type, 16 | variant, 17 | }; 18 | } 19 | 20 | export default useListType; 21 | -------------------------------------------------------------------------------- /src/hooks/useSticky.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import useTheme from "./useTheme"; 3 | import { getStickyItemCSS } from "../utils/sticky"; 4 | 5 | const StickyContext = React.createContext(); 6 | 7 | export const StickyProvider = StickyContext.Provider; 8 | 9 | function useSticky({ id, heightMap }) { 10 | const theme = useTheme(); 11 | const stickyInfo = useContext(StickyContext); 12 | 13 | if (!stickyInfo) { 14 | throw new Error("Sticky.Item must be placed inside Sticky."); 15 | } 16 | 17 | const calculatedOffsetMap = stickyInfo.offsetMap[id]; 18 | const css = getStickyItemCSS({ 19 | heightMap, 20 | offsetMap: calculatedOffsetMap ?? stickyInfo.accumulativeOffsetMap, 21 | theme, 22 | }); 23 | 24 | if (!calculatedOffsetMap) { 25 | stickyInfo.updateOffsetMap(id, heightMap); 26 | } 27 | 28 | return { 29 | css, 30 | }; 31 | } 32 | 33 | export default useSticky; 34 | -------------------------------------------------------------------------------- /src/hooks/useTextStyle.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import useTheme from "./useTheme"; 3 | 4 | const TextStyleContext = React.createContext(); 5 | 6 | export const TextStyleProvider = TextStyleContext.Provider; 7 | 8 | function useTextStyle() { 9 | const theme = useTheme(); 10 | const textStyle = useContext(TextStyleContext); 11 | 12 | return { 13 | textStyle, 14 | textStyleCSS: theme.getTextStyleCSS(textStyle), 15 | }; 16 | } 17 | 18 | export default useTextStyle; 19 | -------------------------------------------------------------------------------- /src/hooks/useTheme.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | 3 | export const ThemeContext = React.createContext(); 4 | 5 | function useTheme() { 6 | const theme = useContext(ThemeContext); 7 | 8 | return theme; 9 | } 10 | 11 | export default useTheme; 12 | -------------------------------------------------------------------------------- /src/hooks/useWindow.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { WindowContext } from "../providers/WindowProvider"; 3 | 4 | function useWindow() { 5 | const windowFromContext = useContext(WindowContext); 6 | 7 | return windowFromContext ?? (typeof window === "undefined" ? null : window); 8 | } 9 | 10 | export default useWindow; 11 | -------------------------------------------------------------------------------- /src/icons/arrow-back.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function ArrowBack({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | ArrowBack.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default ArrowBack; 32 | -------------------------------------------------------------------------------- /src/icons/arrow-forward.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function ArrowForward({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | ArrowForward.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default ArrowForward; 32 | -------------------------------------------------------------------------------- /src/icons/assistance.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Assistance({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | Assistance.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default Assistance; 33 | -------------------------------------------------------------------------------- /src/icons/birthday.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Birthday({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Birthday.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Birthday; 32 | -------------------------------------------------------------------------------- /src/icons/blocking.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Blocking({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | Blocking.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default Blocking; 33 | -------------------------------------------------------------------------------- /src/icons/calculator.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Calculator({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Calculator.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Calculator; 32 | -------------------------------------------------------------------------------- /src/icons/call.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Call({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | Call.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default Call; 33 | -------------------------------------------------------------------------------- /src/icons/chevron-down.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function ChevronDown({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | ChevronDown.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default ChevronDown; 32 | -------------------------------------------------------------------------------- /src/icons/chevron-left.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function ChevronLeft({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | ChevronLeft.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default ChevronLeft; 32 | -------------------------------------------------------------------------------- /src/icons/chevron-right.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function ChevronRight({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | ChevronRight.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default ChevronRight; 32 | -------------------------------------------------------------------------------- /src/icons/chevron-up.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function ChevronUp({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | ChevronUp.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default ChevronUp; 32 | -------------------------------------------------------------------------------- /src/icons/comparison.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Comparison({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Comparison.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Comparison; 32 | -------------------------------------------------------------------------------- /src/icons/critical.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Critical({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | Critical.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default Critical; 33 | -------------------------------------------------------------------------------- /src/icons/cross-small.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function CrossSmall({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | CrossSmall.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default CrossSmall; 32 | -------------------------------------------------------------------------------- /src/icons/cross.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Cross({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Cross.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Cross; 32 | -------------------------------------------------------------------------------- /src/icons/devices.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Devices({ size, primaryColor, testId }) { 5 | return ( 6 | 18 | 19 | 20 | ); 21 | } 22 | 23 | Devices.propTypes = { 24 | size: PropTypes.string.isRequired, 25 | primaryColor: PropTypes.string.isRequired, 26 | testId: PropTypes.string, 27 | }; 28 | 29 | export default Devices; 30 | -------------------------------------------------------------------------------- /src/icons/document.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Document({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | Document.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default Document; 33 | -------------------------------------------------------------------------------- /src/icons/download.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Download({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Download.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Download; 32 | -------------------------------------------------------------------------------- /src/icons/duplicate.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Duplicate({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | Duplicate.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default Duplicate; 33 | -------------------------------------------------------------------------------- /src/icons/edit.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Edit({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Edit.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Edit; 32 | -------------------------------------------------------------------------------- /src/icons/external-link.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function ExternalLink({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | ExternalLink.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default ExternalLink; 32 | -------------------------------------------------------------------------------- /src/icons/eye-hidden.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function EyeHidden({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | EyeHidden.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default EyeHidden; 32 | -------------------------------------------------------------------------------- /src/icons/eye-visible.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function EyeVisible({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | EyeVisible.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default EyeVisible; 32 | -------------------------------------------------------------------------------- /src/icons/face-id.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function FaceID({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | FaceID.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default FaceID; 32 | -------------------------------------------------------------------------------- /src/icons/facebook.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import useTheme from "../hooks/useTheme"; 4 | 5 | function Facebook({ size, primaryColor, hoverColor, testId }) { 6 | const theme = useTheme(); 7 | 8 | return ( 9 | 25 | 29 | 30 | ); 31 | } 32 | 33 | Facebook.propTypes = { 34 | size: PropTypes.string.isRequired, 35 | primaryColor: PropTypes.string.isRequired, 36 | hoverColor: PropTypes.string, 37 | testId: PropTypes.string, 38 | }; 39 | 40 | export default Facebook; 41 | -------------------------------------------------------------------------------- /src/icons/fingerprint.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Fingerprint({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Fingerprint.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Fingerprint; 32 | -------------------------------------------------------------------------------- /src/icons/github.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import useTheme from "../hooks/useTheme"; 4 | 5 | function GitHub({ size, primaryColor, hoverColor, testId }) { 6 | const theme = useTheme(); 7 | 8 | return ( 9 | 25 | 29 | 30 | ); 31 | } 32 | 33 | GitHub.propTypes = { 34 | size: PropTypes.string.isRequired, 35 | primaryColor: PropTypes.string.isRequired, 36 | hoverColor: PropTypes.string, 37 | testId: PropTypes.string, 38 | }; 39 | 40 | export default GitHub; 41 | -------------------------------------------------------------------------------- /src/icons/hamburger.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Hamburger({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Hamburger.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Hamburger; 32 | -------------------------------------------------------------------------------- /src/icons/home.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Home({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | Home.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default Home; 33 | -------------------------------------------------------------------------------- /src/icons/info-or-minor.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function InfoOrMinor({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | InfoOrMinor.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default InfoOrMinor; 33 | -------------------------------------------------------------------------------- /src/icons/link.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Link({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 25 | 26 | ); 27 | } 28 | 29 | Link.propTypes = { 30 | size: PropTypes.string.isRequired, 31 | primaryColor: PropTypes.string.isRequired, 32 | testId: PropTypes.string, 33 | }; 34 | 35 | export default Link; 36 | -------------------------------------------------------------------------------- /src/icons/linkedin.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import useTheme from "../hooks/useTheme"; 4 | 5 | function LinkedIn({ size, primaryColor, hoverColor, testId }) { 6 | const theme = useTheme(); 7 | 8 | return ( 9 | 25 | 29 | 30 | ); 31 | } 32 | 33 | LinkedIn.propTypes = { 34 | size: PropTypes.string.isRequired, 35 | primaryColor: PropTypes.string.isRequired, 36 | hoverColor: PropTypes.string, 37 | testId: PropTypes.string, 38 | }; 39 | 40 | export default LinkedIn; 41 | -------------------------------------------------------------------------------- /src/icons/lock-small.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function LockSmall({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | LockSmall.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default LockSmall; 32 | -------------------------------------------------------------------------------- /src/icons/lock.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Lock({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Lock.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Lock; 32 | -------------------------------------------------------------------------------- /src/icons/logout.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Logout({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 25 | 29 | 30 | ); 31 | } 32 | 33 | Logout.propTypes = { 34 | size: PropTypes.string.isRequired, 35 | primaryColor: PropTypes.string.isRequired, 36 | testId: PropTypes.string, 37 | }; 38 | 39 | export default Logout; 40 | -------------------------------------------------------------------------------- /src/icons/mail.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Mail({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Mail.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Mail; 32 | -------------------------------------------------------------------------------- /src/icons/message.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Message({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Message.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Message; 32 | -------------------------------------------------------------------------------- /src/icons/notification-new.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function NotificationNew({ size, primaryColor, secondaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | 23 | ); 24 | } 25 | 26 | NotificationNew.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | secondaryColor: PropTypes.string.isRequired, 30 | testId: PropTypes.string, 31 | }; 32 | 33 | export default NotificationNew; 34 | -------------------------------------------------------------------------------- /src/icons/notification.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Notification({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Notification.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Notification; 32 | -------------------------------------------------------------------------------- /src/icons/overflow-menu.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function OverflowMenu({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | OverflowMenu.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default OverflowMenu; 32 | -------------------------------------------------------------------------------- /src/icons/payment.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Payment({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | Payment.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default Payment; 33 | -------------------------------------------------------------------------------- /src/icons/person.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Person({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Person.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Person; 32 | -------------------------------------------------------------------------------- /src/icons/question.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Question({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Question.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Question; 32 | -------------------------------------------------------------------------------- /src/icons/search.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Search({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Search.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Search; 32 | -------------------------------------------------------------------------------- /src/icons/select-object.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import useTheme from "../hooks/useTheme"; 4 | 5 | function SelectObject({ size, primaryColor, hoverColor, testId }) { 6 | const theme = useTheme(); 7 | 8 | return ( 9 | 25 | 26 | 27 | 28 | ); 29 | } 30 | 31 | SelectObject.propTypes = { 32 | size: PropTypes.string.isRequired, 33 | primaryColor: PropTypes.string.isRequired, 34 | hoverColor: PropTypes.string, 35 | testId: PropTypes.string, 36 | }; 37 | 38 | export default SelectObject; 39 | -------------------------------------------------------------------------------- /src/icons/shield.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Shield({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Shield.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Shield; 32 | -------------------------------------------------------------------------------- /src/icons/stop.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Stop({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | Stop.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default Stop; 33 | -------------------------------------------------------------------------------- /src/icons/stopwatch-alt.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function StopwatchAlt({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | StopwatchAlt.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default StopwatchAlt; 32 | -------------------------------------------------------------------------------- /src/icons/stopwatch.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Stopwatch({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Stopwatch.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Stopwatch; 32 | -------------------------------------------------------------------------------- /src/icons/success.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Success({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | Success.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default Success; 33 | -------------------------------------------------------------------------------- /src/icons/tick-small.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function TickSmall({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | TickSmall.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default TickSmall; 32 | -------------------------------------------------------------------------------- /src/icons/tick.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Tick({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Tick.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Tick; 32 | -------------------------------------------------------------------------------- /src/icons/time.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Time({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | Time.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default Time; 32 | -------------------------------------------------------------------------------- /src/icons/trash.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function Trash({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | Trash.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default Trash; 33 | -------------------------------------------------------------------------------- /src/icons/triangle-down.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function TriangleDown({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | TriangleDown.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default TriangleDown; 32 | -------------------------------------------------------------------------------- /src/icons/triangle-up.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function TriangleUp({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 21 | 22 | ); 23 | } 24 | 25 | TriangleUp.propTypes = { 26 | size: PropTypes.string.isRequired, 27 | primaryColor: PropTypes.string.isRequired, 28 | testId: PropTypes.string, 29 | }; 30 | 31 | export default TriangleUp; 32 | -------------------------------------------------------------------------------- /src/icons/twitter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import useTheme from "../hooks/useTheme"; 4 | 5 | function Twitter({ size, primaryColor, hoverColor, testId }) { 6 | const theme = useTheme(); 7 | 8 | return ( 9 | 25 | 29 | 30 | ); 31 | } 32 | 33 | Twitter.propTypes = { 34 | size: PropTypes.string.isRequired, 35 | primaryColor: PropTypes.string.isRequired, 36 | hoverColor: PropTypes.string, 37 | testId: PropTypes.string, 38 | }; 39 | 40 | export default Twitter; 41 | -------------------------------------------------------------------------------- /src/icons/warning-or-significant.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function WarningOrSignificant({ size, primaryColor, testId }) { 5 | return ( 6 | 17 | 22 | 23 | ); 24 | } 25 | 26 | WarningOrSignificant.propTypes = { 27 | size: PropTypes.string.isRequired, 28 | primaryColor: PropTypes.string.isRequired, 29 | testId: PropTypes.string, 30 | }; 31 | 32 | export default WarningOrSignificant; 33 | -------------------------------------------------------------------------------- /src/icons/youtube.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import useTheme from "../hooks/useTheme"; 4 | 5 | function YouTube({ size, primaryColor, hoverColor, testId }) { 6 | const theme = useTheme(); 7 | 8 | return ( 9 | 25 | 29 | 30 | ); 31 | } 32 | 33 | YouTube.propTypes = { 34 | size: PropTypes.string.isRequired, 35 | primaryColor: PropTypes.string.isRequired, 36 | hoverColor: PropTypes.string, 37 | testId: PropTypes.string, 38 | }; 39 | 40 | export default YouTube; 41 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // Providers 2 | export { default as BasisProvider } from "./providers/BasisProvider"; 3 | 4 | // Themes 5 | export { default as defaultTheme } from "./themes/default"; 6 | 7 | // Hooks 8 | export { default as useTheme } from "./hooks/useTheme"; 9 | export { default as useBreakpoint } from "./hooks/useBreakpoint"; 10 | 11 | // Utils 12 | export { getUniqueId } from "./utils/core"; 13 | 14 | // Components 15 | export * from "./components"; 16 | -------------------------------------------------------------------------------- /src/providers/BasisProvider.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import PropTypes from "prop-types"; 3 | import WindowProvider from "./WindowProvider"; 4 | import BreakpointProvider from "./BreakpointProvider"; 5 | import LinkProvider from "./LinkProvider"; 6 | import { ThemeContext } from "../hooks/useTheme"; 7 | import { enhanceTheme } from "../utils/theme"; 8 | import FocusVisiblePolyfill from "../components/internal/FocusVisiblePolyfill"; 9 | 10 | function BasisProvider({ 11 | theme, 12 | window, 13 | InternalLink, 14 | isLinkInternal, 15 | children, 16 | }) { 17 | const enhancedTheme = useMemo(() => enhanceTheme(theme), [theme]); 18 | 19 | return ( 20 | 21 | 22 | 23 | 24 | 28 | {children} 29 | 30 | 31 | 32 | 33 | ); 34 | } 35 | 36 | BasisProvider.propTypes = { 37 | theme: PropTypes.object.isRequired, 38 | window: PropTypes.object, 39 | InternalLink: PropTypes.elementType, 40 | isLinkInternal: PropTypes.func, 41 | children: PropTypes.node.isRequired, 42 | }; 43 | 44 | export default BasisProvider; 45 | -------------------------------------------------------------------------------- /src/providers/BreakpointProvider.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useLayoutEffect } from "react"; 2 | import PropTypes from "prop-types"; 3 | import useTheme from "../hooks/useTheme"; 4 | import useWindow from "../hooks/useWindow"; 5 | import { BreakpointContext } from "../hooks/useBreakpoint"; 6 | 7 | function BreakpointProvider(props) { 8 | const { children } = props; 9 | const theme = useTheme(); 10 | const windowObj = useWindow(); 11 | const [mediaQueryListMap, setMediaQueryListMap] = useState(null); 12 | const [breakpoint, setBreakpoint] = useState(null); 13 | 14 | useLayoutEffect(() => { 15 | if (!windowObj.matchMedia) { 16 | return; 17 | } 18 | 19 | const mediaQueryListMap = {}; 20 | 21 | for (const bp in theme.exclusiveMediaQueries) { 22 | mediaQueryListMap[bp] = windowObj.matchMedia( 23 | theme.exclusiveMediaQueries[bp] 24 | ); 25 | 26 | if (mediaQueryListMap[bp].matches) { 27 | setBreakpoint(bp); 28 | } 29 | } 30 | 31 | setMediaQueryListMap(mediaQueryListMap); 32 | }, [windowObj, theme.exclusiveMediaQueries]); 33 | 34 | useEffect(() => { 35 | let mounted = true; 36 | const removeListeners = []; 37 | 38 | for (const bp in mediaQueryListMap) { 39 | const mediaQueryList = mediaQueryListMap[bp]; 40 | // eslint-disable-next-line no-loop-func 41 | const listener = (event) => { 42 | if (event.matches && mounted) { 43 | setBreakpoint(bp); 44 | } 45 | }; 46 | 47 | mediaQueryList.addListener(listener); 48 | 49 | removeListeners.push(() => { 50 | mediaQueryList.removeListener(listener); 51 | }); 52 | } 53 | 54 | return () => { 55 | mounted = false; 56 | 57 | removeListeners.forEach((fn) => { 58 | fn(); 59 | }); 60 | }; 61 | }, [mediaQueryListMap]); 62 | 63 | return ( 64 | 65 | {children} 66 | 67 | ); 68 | } 69 | 70 | BreakpointProvider.propTypes = { 71 | children: PropTypes.node.isRequired, 72 | }; 73 | 74 | export default BreakpointProvider; 75 | -------------------------------------------------------------------------------- /src/providers/LinkProvider.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | export const LinkContext = React.createContext(); 5 | 6 | // See: https://www.gatsbyjs.org/docs/gatsby-link/#reminder-use-link-only-for-internal-links 7 | function defaultIsLinkInternal(href) { 8 | return /^\/(?!\/)/.test(href); 9 | } 10 | 11 | function LinkProvider({ 12 | InternalLink, 13 | isLinkInternal = defaultIsLinkInternal, 14 | children, 15 | }) { 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | } 22 | 23 | LinkProvider.propTypes = { 24 | InternalLink: PropTypes.elementType, 25 | isLinkInternal: PropTypes.func, 26 | children: PropTypes.node.isRequired, 27 | }; 28 | 29 | export default LinkProvider; 30 | -------------------------------------------------------------------------------- /src/providers/WindowProvider.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | export const WindowContext = React.createContext(); 5 | 6 | function WindowProvider({ window, children }) { 7 | return ( 8 | {children} 9 | ); 10 | } 11 | 12 | WindowProvider.propTypes = { 13 | window: PropTypes.object, 14 | children: PropTypes.node.isRequired, 15 | }; 16 | 17 | export default WindowProvider; 18 | -------------------------------------------------------------------------------- /src/themes/default/checkbox.js: -------------------------------------------------------------------------------- 1 | export default (theme, { getColor }) => { 2 | return { 3 | getCSS: ({ 4 | targetElement, 5 | color, 6 | isChecked, 7 | __internal__keyboardFocus, 8 | }) => { 9 | switch (targetElement) { 10 | case "container": { 11 | return { 12 | display: "flex", 13 | flexDirection: "column", 14 | flex: 1, 15 | }; 16 | } 17 | 18 | case "input": { 19 | return { 20 | ...theme.focusStyles.focusVisibleAdjacentLabel, 21 | ":checked + label": { 22 | backgroundColor: theme.colors.secondary.lightBlue.t25, 23 | color: theme.colors.primary.blue.t100, 24 | }, 25 | }; 26 | } 27 | 28 | case "label": { 29 | return { 30 | display: "inline-flex", 31 | alignItems: "flex-start", 32 | padding: `${theme.space[3]} ${theme.space[4]}`, 33 | minHeight: "24px", 34 | fontSize: theme.fontSizes[1], 35 | fontWeight: theme.fontWeights.light, 36 | lineHeight: theme.lineHeights[2], 37 | fontFamily: theme.fonts.body, 38 | color: theme.colors.black, 39 | backgroundColor: getColor(color), 40 | borderRadius: theme.radii[0], 41 | ...(__internal__keyboardFocus && 42 | theme.focusStyles.__keyboardFocusAdjacentLabel), 43 | }; 44 | } 45 | 46 | case "svg": { 47 | return { 48 | flexShrink: 0, 49 | width: "24px", 50 | height: "24px", 51 | marginRight: theme.space[3], 52 | }; 53 | } 54 | 55 | case "svgRect": { 56 | return { 57 | fill: getColor( 58 | color === "grey.t05" || isChecked 59 | ? "white" 60 | : "secondary.lightBlue.t25" 61 | ), 62 | }; 63 | } 64 | 65 | case "svgPath": { 66 | return { 67 | stroke: theme.colors.primary.blue.t100, 68 | }; 69 | } 70 | 71 | default: { 72 | return null; 73 | } 74 | } 75 | }, 76 | }; 77 | }; 78 | -------------------------------------------------------------------------------- /src/themes/default/field.js: -------------------------------------------------------------------------------- 1 | export default (theme) => { 2 | return { 3 | getCSS: ({ targetElement, fullWidth, disabled }) => { 4 | switch (targetElement) { 5 | case "fieldContainer": { 6 | return { 7 | display: "inline-flex", 8 | flexDirection: "column", 9 | position: "relative", 10 | ...(fullWidth && { 11 | display: "flex", 12 | width: "100%", 13 | minWidth: 0, // See: https://stackoverflow.com/a/36247448/247243 14 | }), 15 | ...(disabled && { opacity: 0.5 }), 16 | }; 17 | } 18 | 19 | case "label": { 20 | return { 21 | display: "flex", 22 | fontFamily: theme.fonts.body, 23 | fontSize: theme.fontSizes[1], 24 | fontWeight: theme.fontWeights.medium, 25 | lineHeight: theme.lineHeights[2], 26 | color: theme.colors.grey.t75, 27 | marginBottom: theme.space[2], 28 | }; 29 | } 30 | 31 | case "optionalTag": { 32 | return { 33 | alignSelf: "flex-end", 34 | fontSize: theme.fontSizes[0], 35 | lineHeight: theme.lineHeights[0], 36 | paddingLeft: theme.space[2], 37 | paddingRight: theme.space[2], 38 | border: `${theme.borderWidths[1]} solid ${theme.colors.grey.t75}`, 39 | borderRadius: theme.radii[2], 40 | opacity: 0.66, 41 | marginLeft: "auto", 42 | }; 43 | } 44 | 45 | case "helpText": { 46 | return { 47 | paddingTop: theme.space[2], 48 | }; 49 | } 50 | 51 | case "errorsContainer": { 52 | return { 53 | paddingTop: theme.space[1], 54 | borderTop: `${theme.borderWidths[2]} solid ${theme.colors.conditional.negative.graphics}`, 55 | zIndex: 1, 56 | }; 57 | } 58 | 59 | default: { 60 | return null; 61 | } 62 | } 63 | }, 64 | }; 65 | }; 66 | -------------------------------------------------------------------------------- /src/themes/default/radioGroup.js: -------------------------------------------------------------------------------- 1 | export default (theme, { getColor, getTextStyle }) => { 2 | return { 3 | getCSS: ({ targetElement, color, isChecked }) => { 4 | switch (targetElement) { 5 | case "radio": { 6 | return { 7 | display: "flex", 8 | flexDirection: "column", 9 | height: "100%", 10 | }; 11 | } 12 | 13 | case "radioInput": { 14 | return { 15 | ...theme.focusStyles.focusVisibleAdjacentLabel, 16 | ":checked + label": { 17 | backgroundColor: theme.colors.secondary.lightBlue.t25, 18 | color: theme.colors.primary.blue.t100, 19 | }, 20 | }; 21 | } 22 | 23 | case "radioLabel": { 24 | return { 25 | display: "inline-flex", 26 | alignItems: "flex-start", 27 | padding: `${theme.space[3]} ${theme.space[4]}`, 28 | minHeight: "24px", 29 | height: "100%", 30 | ...getTextStyle({ name: "body1", mode: "container" }), 31 | color: theme.colors.black, 32 | backgroundColor: getColor(color), 33 | borderRadius: theme.radii[0], 34 | }; 35 | } 36 | 37 | case "circleSvg": { 38 | return { 39 | flexShrink: 0, 40 | width: "24px", 41 | height: "24px", 42 | marginRight: theme.space[3], 43 | }; 44 | } 45 | 46 | case "outerCircle": { 47 | return { 48 | fill: getColor( 49 | color === "grey.t05" || isChecked 50 | ? "white" 51 | : "secondary.lightBlue.t25" 52 | ), 53 | }; 54 | } 55 | 56 | case "innerCircle": { 57 | return { 58 | fill: theme.colors.primary.blue.t100, 59 | }; 60 | } 61 | 62 | case "description": { 63 | return { 64 | marginTop: theme.space[1], 65 | }; 66 | } 67 | 68 | default: { 69 | return null; 70 | } 71 | } 72 | }, 73 | }; 74 | }; 75 | -------------------------------------------------------------------------------- /src/themes/default/select.js: -------------------------------------------------------------------------------- 1 | export default (theme, { getColor }) => { 2 | return { 3 | getCSS: ({ color, fullWidth, __internal__focus }) => { 4 | const focusStyle = { 5 | outline: 0, 6 | borderRadius: theme.radii[0], 7 | boxShadow: theme.shadows.focus, 8 | }; 9 | 10 | return { 11 | display: "inline-block", 12 | fontSize: theme.fontSizes[1], 13 | fontWeight: theme.fontWeights.light, 14 | lineHeight: theme.lineHeights[2], 15 | fontFamily: theme.fonts.body, 16 | color: theme.colors.black, 17 | width: fullWidth ? "100%" : null, 18 | height: "48px", 19 | paddingLeft: theme.space[4], 20 | paddingRight: theme.space[10], 21 | margin: 0, 22 | border: 0, 23 | borderRadius: 0, 24 | appearance: "none", 25 | backgroundColor: getColor(color), 26 | backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32' role='img' aria-label='Triangle down'%3E%3Cpath d='M20.747 14.509l-4.181 4.25a.786.786 0 01-1.132 0l-4.179-4.247a.885.885 0 01-.231-.827c.07-.3.287-.536.569-.62.282-.084 8.607-.101 8.912.035a.86.86 0 01.495.802.874.874 0 01-.253.607z' fill='%23000'%3E%3C/path%3E%3C/svg%3E")`, 27 | backgroundRepeat: "no-repeat", 28 | backgroundPosition: `right ${theme.space[4]} top 50%`, 29 | alignSelf: "flex-start", 30 | // See: https://stackoverflow.com/a/19451423/247243 31 | ":-moz-focusring": { 32 | color: "transparent", 33 | textShadow: "0 0 0 #000", 34 | }, 35 | ":focus": focusStyle, 36 | ...(__internal__focus && focusStyle), 37 | }; 38 | }, 39 | }; 40 | }; 41 | -------------------------------------------------------------------------------- /src/themes/default/text.js: -------------------------------------------------------------------------------- 1 | export default (theme, { getColor }) => { 2 | return { 3 | getCSS: ({ color, align, wrap }) => { 4 | return { 5 | margin: 0, 6 | color: getColor(color), 7 | textAlign: align, 8 | ...(!wrap && { 9 | whiteSpace: "nowrap", 10 | overflow: "hidden", 11 | textOverflow: "ellipsis", 12 | }), 13 | }; 14 | }, 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /src/themes/default/textarea.js: -------------------------------------------------------------------------------- 1 | export default (theme, { getColor }) => { 2 | return { 3 | getCSS: ({ color, __internal__focus }) => { 4 | const focusStyle = { 5 | outline: 0, 6 | borderRadius: theme.radii[0], 7 | boxShadow: theme.shadows.focus, 8 | }; 9 | 10 | return { 11 | boxSizing: "border-box", 12 | width: "100%", 13 | minHeight: "60px", 14 | resize: "vertical", 15 | border: 0, 16 | margin: 0, 17 | fontSize: theme.fontSizes[1], 18 | fontWeight: theme.fontWeights.light, 19 | lineHeight: theme.lineHeights[2], 20 | fontFamily: theme.fonts.body, 21 | color: theme.colors.black, 22 | backgroundColor: getColor(color), 23 | padding: `${theme.space[2]} ${theme.space[4]}`, 24 | ":focus": focusStyle, 25 | ...(__internal__focus && focusStyle), 26 | }; 27 | }, 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /src/utils/array.js: -------------------------------------------------------------------------------- 1 | export function formatArray(arr) { 2 | return `[${arr.map((item) => `"${item}"`).join(", ")}]`; 3 | } 4 | -------------------------------------------------------------------------------- /src/utils/constants.js: -------------------------------------------------------------------------------- 1 | export const TEXT_STYLES = [ 2 | "hero", 3 | "heading1", 4 | "heading2", 5 | "heading3", 6 | "heading4", 7 | "heading5", 8 | "heading6", 9 | "subtitle1", 10 | "subtitle2", 11 | "body1", 12 | "body2", 13 | "legal", 14 | "overline", 15 | ]; 16 | export const TEXT_ALIGNS = ["inherit", "left", "center", "right"]; 17 | 18 | export const FLEX_DIRECTIONS = [ 19 | "row", 20 | "row-reverse", 21 | "column", 22 | "column-reverse", 23 | ]; 24 | export const FLEX_PLACE_ITEMS = [ 25 | "top left", 26 | "top center", 27 | "top right", 28 | "center left", 29 | "center center", 30 | "center right", 31 | "bottom left", 32 | "bottom center", 33 | "bottom right", 34 | 35 | "left top", 36 | "center top", 37 | "right top", 38 | "left center", 39 | "right center", 40 | "left bottom", 41 | "center bottom", 42 | "right bottom", 43 | 44 | "center", 45 | ]; 46 | -------------------------------------------------------------------------------- /src/utils/core.js: -------------------------------------------------------------------------------- 1 | import { nanoid } from "nanoid"; 2 | 3 | export function isObjectEmpty(obj) { 4 | for (const _key in obj) { 5 | return false; 6 | } 7 | 8 | return true; 9 | } 10 | 11 | /* 12 | ESLint complains about: 13 | obj.hasOwnProperty(key) 14 | 15 | See: https://eslint.org/docs/rules/no-prototype-builtins 16 | */ 17 | export function hasOwnProperty(obj, key) { 18 | return Object.prototype.hasOwnProperty.call(obj, key); 19 | } 20 | 21 | export const getUniqueId = nanoid; 22 | -------------------------------------------------------------------------------- /src/utils/core.test.js: -------------------------------------------------------------------------------- 1 | import { isObjectEmpty, hasOwnProperty } from "./core"; 2 | 3 | describe("isObjectEmpty", () => { 4 | it("empty", () => { 5 | expect(isObjectEmpty({})).toBe(true); 6 | }); 7 | 8 | it("not empty", () => { 9 | expect(isObjectEmpty({ a: 1 })).toBe(false); 10 | expect(isObjectEmpty({ b: undefined })).toBe(false); 11 | }); 12 | }); 13 | 14 | describe("hasOwnProperty", () => { 15 | it("has property", () => { 16 | expect(hasOwnProperty({ a: 1, b: undefined }, "b")).toBe(true); 17 | }); 18 | 19 | it("does not have property", () => { 20 | expect(hasOwnProperty({ a: 1, b: undefined }, "c")).toBe(false); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/utils/getDataAttributes.js: -------------------------------------------------------------------------------- 1 | export const getDataAttributes = (data) => 2 | Object.fromEntries( 3 | Object.entries(data).map(([key, value]) => [`data-${key}`, value]) 4 | ); 5 | -------------------------------------------------------------------------------- /src/utils/string.js: -------------------------------------------------------------------------------- 1 | export function pluralize(count, word) { 2 | return count === 1 ? `1 ${word}` : `${count} ${word}s`; 3 | } 4 | -------------------------------------------------------------------------------- /src/utils/string.test.js: -------------------------------------------------------------------------------- 1 | import { pluralize } from "./string"; 2 | 3 | describe("pluralize", () => { 4 | it("count = 1", () => { 5 | expect(pluralize(1, "day")).toBe("1 day"); 6 | }); 7 | 8 | it("count > 1", () => { 9 | expect(pluralize(5, "day")).toBe("5 days"); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /src/utils/theme.test.js: -------------------------------------------------------------------------------- 1 | import theme from "../themes/default"; 2 | import { enhanceTheme } from "./theme"; 3 | 4 | describe("enhanceTheme", () => { 5 | it("adds minMediaQueries", () => { 6 | expect(enhanceTheme(theme)).toHaveProperty("minMediaQueries"); 7 | }); 8 | 9 | it("adds exclusiveMediaQueries", () => { 10 | expect(enhanceTheme(theme)).toHaveProperty("exclusiveMediaQueries"); 11 | }); 12 | 13 | it("adds getColor", () => { 14 | expect(enhanceTheme(theme)).toHaveProperty("getColor"); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /website/componentsStatus.js: -------------------------------------------------------------------------------- 1 | const { COMPONENT_STATUS } = require("./src/utils/constants"); 2 | 3 | module.exports = { 4 | Accordion: { 5 | status: COMPONENT_STATUS.READY, 6 | }, 7 | Button: { 8 | status: COMPONENT_STATUS.READY, 9 | }, 10 | Checkbox: { 11 | status: COMPONENT_STATUS.READY, 12 | }, 13 | CheckboxGroup: { 14 | status: COMPONENT_STATUS.READY, 15 | }, 16 | Container: { 17 | status: COMPONENT_STATUS.READY, 18 | }, 19 | DatePicker: { 20 | status: COMPONENT_STATUS.READY, 21 | }, 22 | Divider: { 23 | status: COMPONENT_STATUS.READY, 24 | }, 25 | Dropdown: { 26 | status: COMPONENT_STATUS.READY, 27 | }, 28 | Flex: { 29 | status: COMPONENT_STATUS.READY, 30 | }, 31 | Footer: { 32 | status: COMPONENT_STATUS.READY, 33 | }, 34 | Form: { 35 | status: COMPONENT_STATUS.READY, 36 | }, 37 | Frequency: { 38 | status: COMPONENT_STATUS.READY, 39 | }, 40 | Grid: { 41 | status: COMPONENT_STATUS.READY, 42 | }, 43 | Header: { 44 | status: COMPONENT_STATUS.READY, 45 | }, 46 | Icon: { 47 | status: COMPONENT_STATUS.DRAFT, 48 | }, 49 | Input: { 50 | status: COMPONENT_STATUS.READY, 51 | }, 52 | Link: { 53 | status: COMPONENT_STATUS.READY, 54 | }, 55 | List: { 56 | status: COMPONENT_STATUS.READY, 57 | }, 58 | LoadingIcon: { 59 | status: COMPONENT_STATUS.DRAFT, 60 | }, 61 | Message: { 62 | status: COMPONENT_STATUS.READY, 63 | }, 64 | Placeholder: { 65 | status: COMPONENT_STATUS.READY, 66 | }, 67 | RadioGroup: { 68 | status: COMPONENT_STATUS.READY, 69 | }, 70 | Select: { 71 | status: COMPONENT_STATUS.READY, 72 | }, 73 | ShadowContainer: { 74 | status: COMPONENT_STATUS.READY, 75 | }, 76 | Stack: { 77 | status: COMPONENT_STATUS.READY, 78 | }, 79 | Stepper: { 80 | status: COMPONENT_STATUS.READY, 81 | }, 82 | Sticky: { 83 | status: COMPONENT_STATUS.READY, 84 | }, 85 | Text: { 86 | status: COMPONENT_STATUS.READY, 87 | }, 88 | Textarea: { 89 | status: COMPONENT_STATUS.READY, 90 | }, 91 | TimeSpan: { 92 | status: COMPONENT_STATUS.READY, 93 | }, 94 | }; 95 | -------------------------------------------------------------------------------- /website/gatsby-browser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement Gatsby's Browser APIs in this file. 3 | * 4 | * See: https://www.gatsbyjs.org/docs/browser-apis/ 5 | */ 6 | 7 | // You can delete this file if you're not using it 8 | -------------------------------------------------------------------------------- /website/gatsby-ssr.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LatitudeFinancialOSS/basis/a6adc69b7200cadef0c57bbeff01a33cf21a48ac/website/gatsby-ssr.js -------------------------------------------------------------------------------- /website/react-error-overlay.js: -------------------------------------------------------------------------------- 1 | /** 2 | * For react-error-overlay disabling see: https://github.com/gatsbyjs/gatsby/issues/20420 3 | * This is used by webpack alias in gatsby-node.js 4 | */ 5 | 6 | module.exports = { 7 | setEditorHandler: () => {}, 8 | reportBuildError: () => {}, 9 | reportRuntimeError: () => {}, 10 | dismissBuildError: () => {}, 11 | startReportingRuntimeErrors: () => {}, 12 | dismissRuntimeErrors: () => {}, 13 | stopReportingRuntimeErrors: () => {}, 14 | }; 15 | -------------------------------------------------------------------------------- /website/src/components/CacheProviderWithContainer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import weakMemoize from "@emotion/weak-memoize"; 4 | import createCache from "@emotion/cache"; 5 | import { CacheProvider } from "@emotion/core"; 6 | 7 | // Inspired by: https://github.com/emotion-js/emotion/issues/760#issuecomment-404353706 8 | const memoizedCreateCacheWithContainer = weakMemoize((container) => { 9 | return createCache({ 10 | key: "basis", // This key must exist! See: https://emotion.sh/docs/@emotion/cache#key 11 | container, 12 | }); 13 | }); 14 | 15 | function CacheProviderWithContainer({ container, children }) { 16 | const cache = memoizedCreateCacheWithContainer(container); 17 | 18 | return {children}; 19 | } 20 | 21 | CacheProviderWithContainer.propTypes = { 22 | container: PropTypes.object.isRequired, 23 | children: PropTypes.node.isRequired, 24 | }; 25 | 26 | export default CacheProviderWithContainer; 27 | -------------------------------------------------------------------------------- /website/src/components/ComponentCode.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { LiveEditor } from "react-live"; 4 | import { CopyToClipboard } from "react-copy-to-clipboard"; 5 | import { useTheme } from "basis"; 6 | 7 | function ComponentCode({ className, code }) { 8 | const theme = useTheme(); 9 | 10 | return ( 11 | 20 | 21 | 22 | Copy Code 23 | 24 | 25 | ); 26 | } 27 | 28 | ComponentCode.propTypes = { 29 | className: PropTypes.string, 30 | code: PropTypes.string.isRequired, 31 | }; 32 | 33 | export default ComponentCode; 34 | -------------------------------------------------------------------------------- /website/src/components/ComponentStatusIndicator.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { COMPONENT_STATUS } from "../utils/constants"; 4 | import { useTheme } from "basis"; 5 | 6 | const statusDescriptions = { 7 | DRAFT: 8 | "We started working on this component, but it's not ready for consumption yet.", 9 | READY: "You can use this component today.", 10 | }; 11 | 12 | function ComponentStatusIndicator({ status }) { 13 | const theme = useTheme(); 14 | 15 | return ( 16 | 31 | {status} 32 | 33 | ); 34 | } 35 | 36 | ComponentStatusIndicator.propTypes = { 37 | status: PropTypes.oneOf(Object.values(COMPONENT_STATUS)), 38 | }; 39 | 40 | export default ComponentStatusIndicator; 41 | -------------------------------------------------------------------------------- /website/src/components/ErrorBoundary.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | class ErrorBoundary extends React.Component { 5 | state = { 6 | hasError: false, 7 | }; 8 | 9 | static getDerivedStateFromError(_error) { 10 | return { 11 | hasError: true, 12 | }; 13 | } 14 | 15 | render() { 16 | if (this.state.hasError) { 17 | return Something went wrong.; 18 | } 19 | 20 | return this.props.children; 21 | } 22 | } 23 | 24 | ErrorBoundary.propTypes = { 25 | children: PropTypes.node.isRequired, 26 | }; 27 | 28 | export default ErrorBoundary; 29 | -------------------------------------------------------------------------------- /website/src/components/RangeSetting.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { Text, Flex, useTheme } from "basis"; 4 | 5 | function RangeSetting({ 6 | className, 7 | heading, 8 | min, 9 | max, 10 | selectedValue, 11 | setSelectedValue, 12 | selectedValueText, 13 | selectedValueAuxText, 14 | }) { 15 | const theme = useTheme(); 16 | 17 | return ( 18 | 19 | 20 | {heading.toUpperCase()} 21 | 22 | 23 | { 29 | setSelectedValue(Number(e.target.value)); 30 | }} 31 | /> 32 | 33 | {selectedValueText || selectedValue} 34 | {selectedValueAuxText && ( 35 | 36 | {selectedValueAuxText} 37 | 38 | )} 39 | 40 | 41 | 42 | ); 43 | } 44 | 45 | RangeSetting.propTypes = { 46 | className: PropTypes.string, 47 | heading: PropTypes.string.isRequired, 48 | min: PropTypes.number.isRequired, 49 | max: PropTypes.number.isRequired, 50 | selectedValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) 51 | .isRequired, 52 | setSelectedValue: PropTypes.func.isRequired, 53 | selectedValueText: PropTypes.string, 54 | selectedValueAuxText: PropTypes.string, 55 | }; 56 | 57 | export default RangeSetting; 58 | -------------------------------------------------------------------------------- /website/src/components/SEO.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { Helmet, HelmetProvider } from "react-helmet-async"; 4 | 5 | function SEO({ title, description = "Basis Design System" }) { 6 | return ( 7 | 8 | 9 | {title} 10 | 11 | 12 | 13 | ); 14 | } 15 | 16 | SEO.propTypes = { 17 | title: PropTypes.string, 18 | description: PropTypes.string, 19 | }; 20 | 21 | export default SEO; 22 | -------------------------------------------------------------------------------- /website/src/components/Splitbee.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | const SPLITBEE_SCRIPT_ID = "basis-splitbee"; 4 | 5 | function isSplitbeeEnabled() { 6 | return window.location.host === "basis.vercel.app"; 7 | } 8 | 9 | export function trackEvent(eventName) { 10 | if (isSplitbeeEnabled()) { 11 | window.splitbee.track(eventName); 12 | } 13 | } 14 | 15 | function Splitbee() { 16 | useEffect(() => { 17 | if (!isSplitbeeEnabled()) { 18 | return; 19 | } 20 | 21 | const script = document.createElement("script"); 22 | 23 | script.id = SPLITBEE_SCRIPT_ID; 24 | script.async = true; 25 | script.src = "https://cdn.splitbee.io/sb.js"; 26 | 27 | document.body.appendChild(script); 28 | 29 | return () => { 30 | const script = document.getElementById(SPLITBEE_SCRIPT_ID); 31 | 32 | if (script !== null) { 33 | script.remove(); 34 | } 35 | }; 36 | }, []); 37 | 38 | return null; 39 | } 40 | 41 | export default Splitbee; 42 | -------------------------------------------------------------------------------- /website/src/components/kitchen-sink/KitchenSinkForm.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { Form } from "basis"; 4 | 5 | function FormContent({ onMount, children }) { 6 | useEffect(() => { 7 | onMount && onMount(); 8 | }, [onMount]); 9 | 10 | return children; 11 | } 12 | 13 | FormContent.propTypes = { 14 | onMount: PropTypes.func, 15 | children: PropTypes.node.isRequired, 16 | }; 17 | 18 | function KitchenSinkForm({ initialValues, submitOnMount, children }) { 19 | return ( 20 | 21 | {({ submitForm }) => ( 22 | 23 | {children} 24 | 25 | )} 26 | 27 | ); 28 | } 29 | 30 | KitchenSinkForm.propTypes = { 31 | initialValues: PropTypes.object.isRequired, 32 | submitOnMount: PropTypes.bool, 33 | children: PropTypes.node.isRequired, 34 | }; 35 | 36 | export default KitchenSinkForm; 37 | -------------------------------------------------------------------------------- /website/src/components/kitchen-sink/KitchenSinkLayout.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { BasisProvider, defaultTheme, Container, Text } from "basis"; 4 | import "typeface-montserrat"; 5 | import "typeface-roboto"; 6 | 7 | function KitchenSinkLayout({ name, children }) { 8 | return ( 9 | 10 | 11 | 12 | 13 | {name} 14 | 15 | 16 | {children} 17 | 18 | 19 | ); 20 | } 21 | 22 | KitchenSinkLayout.propTypes = { 23 | name: PropTypes.string.isRequired, 24 | children: PropTypes.node.isRequired, 25 | }; 26 | 27 | export default KitchenSinkLayout; 28 | -------------------------------------------------------------------------------- /website/src/components/playground/PlaygroundCodeError.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { LiveError, withLive } from "react-live"; 3 | import { useTheme } from "basis"; 4 | 5 | const PlaygroundCodeError = withLive(({ live }) => { 6 | const theme = useTheme(); 7 | 8 | if (typeof window === "undefined" || !live.error || live.code.trim() === "") { 9 | return null; 10 | } 11 | 12 | return ( 13 | pre": { 27 | margin: 0, 28 | }, 29 | }} 30 | > 31 | 32 | 33 | ); 34 | }); 35 | 36 | export default PlaygroundCodeError; 37 | -------------------------------------------------------------------------------- /website/src/components/playground/PlaygroundSettingsButton.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { useTheme } from "basis"; 4 | 5 | const TYPES = ["button", "submit"]; 6 | 7 | function PlaygroundSettingsButton({ 8 | type = "button", 9 | title, 10 | width, 11 | onClick, 12 | children, 13 | }) { 14 | const theme = useTheme(); 15 | const buttonCSS = { 16 | boxSizing: "border-box", 17 | width, 18 | height: 22, 19 | fontSize: "14px", 20 | fontWeight: "inherit", 21 | fontFamily: "inherit", 22 | padding: 0, 23 | border: 0, 24 | borderRadius: 2, 25 | backgroundColor: theme.getColor("grey.t10"), 26 | ":hover": { 27 | backgroundColor: theme.getColor("grey.t16"), 28 | }, 29 | }; 30 | 31 | return ( 32 | 33 | {children} 34 | 35 | ); 36 | } 37 | 38 | PlaygroundSettingsButton.propTypes = { 39 | type: PropTypes.oneOf(TYPES), 40 | title: PropTypes.string, 41 | width: PropTypes.number.isRequired, 42 | onClick: PropTypes.func, 43 | children: PropTypes.node.isRequired, 44 | }; 45 | 46 | export default PlaygroundSettingsButton; 47 | -------------------------------------------------------------------------------- /website/src/components/playground/recoilState.js: -------------------------------------------------------------------------------- 1 | import { atom } from "recoil"; 2 | 3 | export const codeState = atom({ 4 | key: "playgroundCode", 5 | default: "", 6 | }); 7 | 8 | export const screensState = atom({ 9 | key: "playgroundScreens", 10 | default: [], 11 | }); 12 | 13 | export const componentPreviewCounterState = atom({ 14 | key: "playgroundComponentPreviewCounter", 15 | default: 0, 16 | }); 17 | 18 | export const isInspectModeState = atom({ 19 | key: "playgroundIsInspectMode", 20 | default: false, 21 | }); 22 | -------------------------------------------------------------------------------- /website/src/components/playground/useLocalStorageValue.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | function useLocalStorageValue(localStorageKey, fallbackValue) { 4 | const [value, setValue] = useState(null); 5 | 6 | useEffect(() => { 7 | const value = localStorage?.getItem(localStorageKey); 8 | 9 | // value is undefined when localStorage doesn't exist, and null when the key doesn't exist. 10 | if (value == null) { 11 | setValue(fallbackValue); 12 | } else { 13 | try { 14 | setValue(JSON.parse(value)); 15 | } catch { 16 | console.error(`useLocalStorageValue: Couldn't parse: ${value}`); 17 | 18 | setValue(fallbackValue); 19 | } 20 | } 21 | }, [localStorageKey, fallbackValue]); 22 | 23 | return value; 24 | } 25 | 26 | export default useLocalStorageValue; 27 | -------------------------------------------------------------------------------- /website/src/components/playground/utils.test.js: -------------------------------------------------------------------------------- 1 | import { getComponentsAtPoint } from "./utils"; 2 | 3 | describe("getComponentsAtPoint", () => { 4 | it("find all components that contain the given point", () => { 5 | const componentsLocation = { 6 | foo: { 7 | left: 20, 8 | top: 80, 9 | right: 60, 10 | bottom: 100, 11 | }, 12 | bar: { 13 | left: 30, 14 | top: 100, 15 | right: 40, 16 | bottom: 120, 17 | }, 18 | baz: { 19 | left: 10, 20 | top: 70, 21 | right: 50, 22 | bottom: 90, 23 | }, 24 | }; 25 | 26 | expect( 27 | getComponentsAtPoint({ x: 35, y: 85 }, componentsLocation) 28 | ).toStrictEqual({ 29 | foo: { 30 | left: 20, 31 | top: 80, 32 | right: 60, 33 | bottom: 100, 34 | }, 35 | baz: { 36 | left: 10, 37 | top: 70, 38 | right: 50, 39 | bottom: 90, 40 | }, 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /website/src/hooks/useCanary.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | function useCanary() { 4 | const [isCanary, setIsCanary] = useState(false); 5 | 6 | useEffect(() => { 7 | if (localStorage.getItem("basisCanary") === "true") { 8 | setIsCanary(true); 9 | } 10 | }, []); 11 | 12 | return isCanary; 13 | } 14 | 15 | export default useCanary; 16 | -------------------------------------------------------------------------------- /website/src/hooks/useCopyToClipboard.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import copyToClipboard from "copy-to-clipboard"; 3 | 4 | const COPIED_DURATION = 3000; 5 | 6 | function useCopyToClipboard(getTextToCopy) { 7 | const [isCopied, setIsCopied] = useState(false); 8 | 9 | useEffect(() => { 10 | if (isCopied) { 11 | const timeoutId = setTimeout(() => { 12 | setIsCopied(false); 13 | }, COPIED_DURATION); 14 | 15 | return () => { 16 | clearTimeout(timeoutId); 17 | }; 18 | } 19 | }, [isCopied]); 20 | 21 | return [ 22 | isCopied, 23 | () => { 24 | const didCopy = copyToClipboard(getTextToCopy()); 25 | 26 | setIsCopied(didCopy); 27 | }, 28 | ]; 29 | } 30 | 31 | export default useCopyToClipboard; 32 | -------------------------------------------------------------------------------- /website/src/hooks/useDebounce.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | // Taken from https://usehooks.com/useDebounce 4 | function useDebounce(value, delay) { 5 | const [debouncedValue, setDebouncedValue] = useState(value); 6 | 7 | useEffect(() => { 8 | const handler = setTimeout(() => { 9 | setDebouncedValue(value); 10 | }, delay); 11 | 12 | return () => { 13 | clearTimeout(handler); 14 | }; 15 | }, [value, delay]); 16 | 17 | return debouncedValue; 18 | } 19 | 20 | export default useDebounce; 21 | -------------------------------------------------------------------------------- /website/src/hooks/useLocalStorage.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useCallback } from "react"; 2 | 3 | function useLocalStorage(key, initialValue) { 4 | const [value, setValue] = useState(null); 5 | const setInitialValue = useCallback(() => { 6 | const value = localStorage?.getItem(key); 7 | 8 | // value is undefined when localStorage doesn't exist, and null when the key doesn't exist. 9 | if (value == null) { 10 | return setValue(initialValue); 11 | } 12 | 13 | try { 14 | setValue(JSON.parse(value)); 15 | } catch { 16 | console.error(`useLocalStorage: Couldn't parse: [${value}]`); 17 | 18 | setValue(initialValue); 19 | } 20 | }, [key, initialValue]); 21 | 22 | // We set the initial value here to make sure that server and client render the same thing. 23 | useEffect(() => { 24 | setInitialValue(); 25 | }, [setInitialValue]); 26 | 27 | useEffect(() => { 28 | try { 29 | localStorage && localStorage.setItem(key, JSON.stringify(value)); 30 | } catch (error) { 31 | console.error( 32 | `useLocalStorage: Saving in localStorage failed: ${error.message}` 33 | ); 34 | } 35 | }, [key, value]); 36 | 37 | return [value, setValue]; 38 | } 39 | 40 | export default useLocalStorage; 41 | -------------------------------------------------------------------------------- /website/src/images/gatsby-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LatitudeFinancialOSS/basis/a6adc69b7200cadef0c57bbeff01a33cf21a48ac/website/src/images/gatsby-icon.png -------------------------------------------------------------------------------- /website/src/layouts/Resources.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { MDXProvider } from "@mdx-js/react"; 4 | import { Link as GatsbyLink } from "gatsby"; 5 | import * as allDesignSystem from "basis"; 6 | 7 | const { BasisProvider, defaultTheme, Container } = allDesignSystem; 8 | 9 | function Resources({ children }) { 10 | return ( 11 | 12 | 13 | {children} 14 | 15 | 16 | ); 17 | } 18 | 19 | Resources.propTypes = { 20 | children: PropTypes.node.isRequired, 21 | }; 22 | 23 | export default Resources; 24 | -------------------------------------------------------------------------------- /website/src/layouts/Usage.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { MDXProvider } from "@mdx-js/react"; 4 | import { Link as GatsbyLink } from "gatsby"; 5 | import * as allDesignSystem from "basis"; 6 | 7 | const { BasisProvider, defaultTheme, Container } = allDesignSystem; 8 | 9 | function Usage({ children }) { 10 | return ( 11 | 12 | 13 | {children} 14 | 15 | 16 | ); 17 | } 18 | 19 | Usage.propTypes = { 20 | children: PropTypes.node.isRequired, 21 | }; 22 | 23 | export default Usage; 24 | -------------------------------------------------------------------------------- /website/src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function NotFoundPage() { 4 | return ( 5 | 6 | {"This page doesn't exist."} 7 | 8 | ); 9 | } 10 | 11 | export default NotFoundPage; 12 | -------------------------------------------------------------------------------- /website/src/pages/colors/resources.mdx: -------------------------------------------------------------------------------- 1 | Colors resources are coming soon. 2 | 3 | Meantime, enjoy the Palettes{" and the "} 4 | Accessibility playground. 5 | 6 | -------------------------------------------------------------------------------- /website/src/pages/components/accordion/resources.mdx: -------------------------------------------------------------------------------- 1 | Accordion resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/accordion/usage.mdx: -------------------------------------------------------------------------------- 1 | Accordion usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/button/resources.mdx: -------------------------------------------------------------------------------- 1 | Button resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/button/usage.mdx: -------------------------------------------------------------------------------- 1 | Button usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/checkbox-group/resources.mdx: -------------------------------------------------------------------------------- 1 | CheckboxGroup resources are coming soon. 2 | 3 | Meantime, enjoy the{" "} 4 | 5 | Playground 6 | 7 | . 8 | 9 | -------------------------------------------------------------------------------- /website/src/pages/components/checkbox-group/usage.mdx: -------------------------------------------------------------------------------- 1 | CheckboxGroup usage is coming soon. 2 | 3 | Meantime, enjoy the{" "} 4 | 5 | Playground 6 | 7 | . 8 | 9 | -------------------------------------------------------------------------------- /website/src/pages/components/checkbox/resources.mdx: -------------------------------------------------------------------------------- 1 | Checkbox resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/checkbox/usage.mdx: -------------------------------------------------------------------------------- 1 | Checkbox usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/container/resources.mdx: -------------------------------------------------------------------------------- 1 | Container resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/container/usage.mdx: -------------------------------------------------------------------------------- 1 | Container usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/date-picker/resources.mdx: -------------------------------------------------------------------------------- 1 | DatePicker resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/date-picker/usage.mdx: -------------------------------------------------------------------------------- 1 | DatePicker usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/divider/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import * as allDesignSystem from "basis"; 3 | import ComponentContainer from "../../../components/ComponentContainer"; 4 | import { formatCode } from "../../../utils/formatting"; 5 | 6 | const scope = allDesignSystem; 7 | 8 | function DividerPage() { 9 | const code = formatCode(` 10 | 11 | `); 12 | 13 | return ; 14 | } 15 | 16 | export default DividerPage; 17 | -------------------------------------------------------------------------------- /website/src/pages/components/divider/resources.mdx: -------------------------------------------------------------------------------- 1 | Divider resources are coming soon. 2 | 3 | Meantime, enjoy the{" "} 4 | 5 | Playground 6 | 7 | . 8 | 9 | -------------------------------------------------------------------------------- /website/src/pages/components/divider/usage.mdx: -------------------------------------------------------------------------------- 1 | Divider usage is coming soon. 2 | 3 | Meantime, enjoy the{" "} 4 | 5 | Playground 6 | 7 | . 8 | 9 | -------------------------------------------------------------------------------- /website/src/pages/components/dropdown/resources.mdx: -------------------------------------------------------------------------------- 1 | Dropdown resources are coming soon. 2 | 3 | Meantime, enjoy the{" "} 4 | 5 | Playground 6 | 7 | . 8 | 9 | -------------------------------------------------------------------------------- /website/src/pages/components/dropdown/usage.mdx: -------------------------------------------------------------------------------- 1 | Dropdown usage is coming soon. 2 | 3 | Meantime, enjoy the{" "} 4 | 5 | Playground 6 | 7 | . 8 | 9 | -------------------------------------------------------------------------------- /website/src/pages/components/flex/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import * as allDesignSystem from "basis"; 3 | import ComponentContainer from "../../../components/ComponentContainer"; 4 | import { formatCode } from "../../../utils/formatting"; 5 | 6 | const scope = allDesignSystem; 7 | 8 | function FlexPage() { 9 | const code = formatCode(` 10 | 11 | Horizontal centering: 12 | 13 | 14 | Button 15 | 16 | 17 | 18 | Vertical centering: 19 | 20 | 21 | Button 22 | 23 | 24 | 25 | Horizontal and vertical centering: 26 | 27 | 28 | Button 29 | 30 | 31 | 32 | `); 33 | 34 | return ; 35 | } 36 | 37 | export default FlexPage; 38 | -------------------------------------------------------------------------------- /website/src/pages/components/flex/resources.mdx: -------------------------------------------------------------------------------- 1 | Flex resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/flex/usage.mdx: -------------------------------------------------------------------------------- 1 | Flex usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/footer/resources.mdx: -------------------------------------------------------------------------------- 1 | Footer resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/footer/usage.mdx: -------------------------------------------------------------------------------- 1 | Footer usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/form/resources.mdx: -------------------------------------------------------------------------------- 1 | Form resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/form/usage.mdx: -------------------------------------------------------------------------------- 1 | Form usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/frequency/resources.mdx: -------------------------------------------------------------------------------- 1 | Frequency resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/frequency/usage.mdx: -------------------------------------------------------------------------------- 1 | Frequency usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/grid/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import * as allDesignSystem from "basis"; 3 | import ComponentContainer from "../../../components/ComponentContainer"; 4 | import { formatCode } from "../../../utils/formatting"; 5 | 6 | const scope = allDesignSystem; 7 | 8 | function GridPage() { 9 | const code = formatCode(` 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | `); 31 | 32 | return ( 33 | 39 | ); 40 | } 41 | 42 | export default GridPage; 43 | -------------------------------------------------------------------------------- /website/src/pages/components/grid/resources.mdx: -------------------------------------------------------------------------------- 1 | Grid resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/grid/usage.mdx: -------------------------------------------------------------------------------- 1 | Grid usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/header/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import * as allDesignSystem from "basis"; 3 | import ComponentContainer from "../../../components/ComponentContainer"; 4 | import { formatCode } from "../../../utils/formatting"; 5 | 6 | const scope = allDesignSystem; 7 | 8 | function HeaderPage() { 9 | const code = formatCode(` 10 | 11 | 12 | 13 | `); 14 | 15 | return ( 16 | 23 | ); 24 | } 25 | 26 | export default HeaderPage; 27 | -------------------------------------------------------------------------------- /website/src/pages/components/header/resources.mdx: -------------------------------------------------------------------------------- 1 | Header resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/header/usage.mdx: -------------------------------------------------------------------------------- 1 | Header usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/icon/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import * as allDesignSystem from "basis"; 3 | import RadioGroupSetting, { 4 | getRadioOptions, 5 | } from "../../../components/RadioGroupSetting"; 6 | import ComponentContainer from "../../../components/ComponentContainer"; 7 | import { formatCode } from "../../../utils/formatting"; 8 | 9 | const { useTheme, Icon } = allDesignSystem; 10 | const { NAMES, COLORS, DEFAULT_PROPS } = Icon; 11 | const scope = allDesignSystem; 12 | 13 | const nameOptions = getRadioOptions(["all", ...NAMES]); 14 | const colorOptions = getRadioOptions(COLORS); 15 | 16 | function IconPage() { 17 | const theme = useTheme(); 18 | const [name, setName] = useState("all"); 19 | const [color, setColor] = useState(DEFAULT_PROPS.color); 20 | const code = formatCode( 21 | name === "all" 22 | ? "<>" + 23 | NAMES.map((name) => ``).join( 24 | "" 25 | ) + 26 | ">" 27 | : `` 28 | ); 29 | 30 | return ( 31 | <> 32 | 41 | 47 | 54 | 55 | 60 | > 61 | ); 62 | } 63 | 64 | export default IconPage; 65 | -------------------------------------------------------------------------------- /website/src/pages/components/icon/resources.mdx: -------------------------------------------------------------------------------- 1 | Icon resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/icon/usage.mdx: -------------------------------------------------------------------------------- 1 | Icon usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/input/resources.mdx: -------------------------------------------------------------------------------- 1 | Input resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/input/usage.mdx: -------------------------------------------------------------------------------- 1 | Input usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/link/resources.mdx: -------------------------------------------------------------------------------- 1 | Link resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/link/usage.mdx: -------------------------------------------------------------------------------- 1 | Link usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/list/resources.mdx: -------------------------------------------------------------------------------- 1 | List resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/list/usage.mdx: -------------------------------------------------------------------------------- 1 | List usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/loading-icon/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import * as allDesignSystem from "basis"; 3 | import RadioGroupSetting, { 4 | getRadioOptions, 5 | } from "../../../components/RadioGroupSetting"; 6 | import ComponentContainer from "../../../components/ComponentContainer"; 7 | import { formatCode, nonDefaultProps } from "../../../utils/formatting"; 8 | 9 | const { useTheme, LoadingIcon } = allDesignSystem; 10 | const { SIZES, COLORS, DEFAULT_PROPS } = LoadingIcon; 11 | const scope = allDesignSystem; 12 | 13 | const sizeOptions = getRadioOptions(SIZES); 14 | const colorOptions = getRadioOptions(COLORS); 15 | 16 | function LoadingIconPage() { 17 | const theme = useTheme(); 18 | const [size, setSize] = useState(DEFAULT_PROPS.size); 19 | const [color, setColor] = useState(DEFAULT_PROPS.color); 20 | const code = formatCode(` 21 | 33 | `); 34 | 35 | return ( 36 | <> 37 | 44 | 50 | 57 | 58 | 63 | > 64 | ); 65 | } 66 | 67 | export default LoadingIconPage; 68 | -------------------------------------------------------------------------------- /website/src/pages/components/loading-icon/resources.mdx: -------------------------------------------------------------------------------- 1 | LoadingIcon resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/loading-icon/usage.mdx: -------------------------------------------------------------------------------- 1 | LoadingIcon usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/message/resources.mdx: -------------------------------------------------------------------------------- 1 | Message resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/message/usage.mdx: -------------------------------------------------------------------------------- 1 | Message usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/placeholder/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import * as allDesignSystem from "basis"; 3 | import ComponentContainer from "../../../components/ComponentContainer"; 4 | import { formatCode } from "../../../utils/formatting"; 5 | 6 | const scope = allDesignSystem; 7 | 8 | function PlaceholderPage() { 9 | const code = formatCode(` 10 | 11 | `); 12 | 13 | return ; 14 | } 15 | 16 | export default PlaceholderPage; 17 | -------------------------------------------------------------------------------- /website/src/pages/components/placeholder/resources.mdx: -------------------------------------------------------------------------------- 1 | Placeholder resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/placeholder/usage.mdx: -------------------------------------------------------------------------------- 1 | Placeholder usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/radio-group/resources.mdx: -------------------------------------------------------------------------------- 1 | RadioGroup resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/radio-group/usage.mdx: -------------------------------------------------------------------------------- 1 | RadioGroup usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/select/resources.mdx: -------------------------------------------------------------------------------- 1 | Select resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/select/usage.mdx: -------------------------------------------------------------------------------- 1 | Select usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/shadow-container/resources.mdx: -------------------------------------------------------------------------------- 1 | ShadowContainer resources are coming soon. 2 | 3 | Meantime, enjoy the{" "} 4 | 5 | Playground 6 | 7 | . 8 | 9 | -------------------------------------------------------------------------------- /website/src/pages/components/shadow-container/usage.mdx: -------------------------------------------------------------------------------- 1 | ShadowContainer usage is coming soon. 2 | 3 | Meantime, enjoy the{" "} 4 | 5 | Playground 6 | 7 | . 8 | 9 | -------------------------------------------------------------------------------- /website/src/pages/components/stack/resources.mdx: -------------------------------------------------------------------------------- 1 | Stack resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/stack/usage.mdx: -------------------------------------------------------------------------------- 1 | Stack usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/stepper/resources.mdx: -------------------------------------------------------------------------------- 1 | Stepper resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/stepper/usage.mdx: -------------------------------------------------------------------------------- 1 | Stepper usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/sticky/resources.mdx: -------------------------------------------------------------------------------- 1 | Sticky resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/sticky/usage.mdx: -------------------------------------------------------------------------------- 1 | Sticky usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/text/resources.mdx: -------------------------------------------------------------------------------- 1 | Text resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/text/usage.mdx: -------------------------------------------------------------------------------- 1 | Text usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/textarea/resources.mdx: -------------------------------------------------------------------------------- 1 | Textarea resources are coming soon. 2 | 3 | Meantime, enjoy the{" "} 4 | 5 | Playground 6 | 7 | . 8 | 9 | -------------------------------------------------------------------------------- /website/src/pages/components/textarea/usage.mdx: -------------------------------------------------------------------------------- 1 | Textarea usage is coming soon. 2 | 3 | Meantime, enjoy the{" "} 4 | 5 | Playground 6 | 7 | . 8 | 9 | -------------------------------------------------------------------------------- /website/src/pages/components/time-span/resources.mdx: -------------------------------------------------------------------------------- 1 | TimeSpan resources are coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/components/time-span/usage.mdx: -------------------------------------------------------------------------------- 1 | TimeSpan usage is coming soon. 2 | Meantime, enjoy the Playground. 3 | -------------------------------------------------------------------------------- /website/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function HomePage() { 4 | return ( 5 | 6 | Home page is coming soon. 7 | 8 | Meantime, explore the components on the left. 9 | 10 | 11 | ); 12 | } 13 | 14 | export default HomePage; 15 | -------------------------------------------------------------------------------- /website/src/pages/kitchen-sink/components/container.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Container, Text } from "basis"; 3 | import KitchenSinkLayout from "../../../components/kitchen-sink/KitchenSinkLayout"; 4 | 5 | function KitchenSinkContainer() { 6 | return ( 7 | 8 | {Container.BACKGROUNDS.map((bg) => ( 9 | 10 | {bg} 11 | 12 | ))} 13 | 14 | {Container.BACKGROUNDS.map((bg) => ( 15 | 16 | {bg} with border 17 | 18 | ))} 19 | 20 | 21 | Has breakpoint width 22 | 23 | 24 | 25 | Left aligned 26 | 27 | 28 | 29 | Center aligned 30 | 31 | 32 | 33 | Right aligned 34 | 35 | 36 | {Container.BOX_SHADOWS.map((boxShadow) => ( 37 | 38 | 39 | Box shadow: {boxShadow} 40 | 41 | 42 | ))} 43 | 44 | ); 45 | } 46 | 47 | export default KitchenSinkContainer; 48 | -------------------------------------------------------------------------------- /website/src/pages/kitchen-sink/components/divider.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Container, Divider } from "basis"; 3 | import KitchenSinkLayout from "../../../components/kitchen-sink/KitchenSinkLayout"; 4 | 5 | function KitchenSinkDivider() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export default KitchenSinkDivider; 16 | -------------------------------------------------------------------------------- /website/src/pages/kitchen-sink/components/flex.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Button, Container, Flex, Placeholder } from "basis"; 3 | import KitchenSinkLayout from "../../../components/kitchen-sink/KitchenSinkLayout"; 4 | 5 | function KitchenSinkFlex() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | top center 32 | 33 | 34 | 35 | 36 | 37 | center right 38 | 39 | 40 | 41 | 42 | 43 | center 44 | 45 | 46 | 47 | ); 48 | } 49 | 50 | export default KitchenSinkFlex; 51 | -------------------------------------------------------------------------------- /website/src/pages/kitchen-sink/components/grid.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Container, Grid, Placeholder } from "basis"; 3 | import KitchenSinkLayout from "../../../components/kitchen-sink/KitchenSinkLayout"; 4 | 5 | function KitchenSinkGrid() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 21 | 28 | 29 | 30 | 37 | 38 | 39 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | ); 60 | } 61 | 62 | export default KitchenSinkGrid; 63 | -------------------------------------------------------------------------------- /website/src/pages/kitchen-sink/components/header.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Container, Header } from "basis"; 3 | import KitchenSinkLayout from "../../../components/kitchen-sink/KitchenSinkLayout"; 4 | 5 | function KitchenSinkHeader() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | } 16 | 17 | export default KitchenSinkHeader; 18 | -------------------------------------------------------------------------------- /website/src/pages/kitchen-sink/components/placeholder.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Container, Placeholder } from "basis"; 3 | import KitchenSinkLayout from "../../../components/kitchen-sink/KitchenSinkLayout"; 4 | 5 | function KitchenSinkPlaceholder() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | } 22 | 23 | export default KitchenSinkPlaceholder; 24 | -------------------------------------------------------------------------------- /website/src/pages/kitchen-sink/components/shadow-container.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Container, ShadowContainer, Stack, Text } from "basis"; 3 | import KitchenSinkLayout from "../../../components/kitchen-sink/KitchenSinkLayout"; 4 | 5 | function KitchenSinkShadowContainer() { 6 | return ( 7 | 8 | 9 | {ShadowContainer.SHADOW_DIRECTIONS.map((shadowDirection) => ( 10 | 11 | {ShadowContainer.SHADOW_SIZES.map((shadowSize) => ( 12 | 13 | 19 | 20 | {shadowSize} {shadowDirection} 21 | 22 | 23 | 24 | ))} 25 | 26 | ))} 27 | 28 | {ShadowContainer.SHADOW_CONTRASTS.map((shadowContrast) => ( 29 | 30 | {ShadowContainer.SHADOW_COLORS.map((shadowColor) => ( 31 | 32 | 38 | 39 | {shadowColor} {shadowContrast} 40 | 41 | 42 | 43 | ))} 44 | 45 | ))} 46 | 47 | 48 | ); 49 | } 50 | 51 | export default KitchenSinkShadowContainer; 52 | -------------------------------------------------------------------------------- /website/src/pages/kitchen-sink/components/text.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Container, Text } from "basis"; 3 | import KitchenSinkLayout from "../../../components/kitchen-sink/KitchenSinkLayout"; 4 | 5 | function KitchenSinkText() { 6 | return ( 7 | 8 | 9 | Default 10 | 11 | 12 | 13 | {Text.TEXT_STYLES.map((textStyle) => ( 14 | 15 | {textStyle} bold 16 | 17 | ))} 18 | 19 | 20 | {Text.COLORS.map((color) => ( 21 | 26 | {color} 27 | 28 | ))} 29 | 30 | {Text.ALIGNS.map((align) => ( 31 | 32 | {align} 33 | 34 | ))} 35 | 36 | 37 | This text should wrap to the next line. 38 | 39 | 40 | 41 | This text should NOT wrap to the next line. 42 | 43 | 44 | ); 45 | } 46 | 47 | export default KitchenSinkText; 48 | -------------------------------------------------------------------------------- /website/src/pages/playground/index.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { RecoilRoot } from "recoil"; 3 | import { render } from "../../../../src/utils/test"; 4 | import "@testing-library/jest-dom/extend-expect"; 5 | import Playground from "./index"; 6 | 7 | describe("Playground", () => { 8 | it("loads successfully", () => { 9 | expect(() => { 10 | render( 11 | 12 | 13 | 14 | ); 15 | }).not.toThrow(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /website/src/pages/preview/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useMemo } from "react"; 2 | import { LiveProvider } from "react-live"; 3 | import * as allDesignSystem from "basis"; 4 | import ComponentPreview from "../../components/ComponentPreview"; 5 | import { getReactLiveNoInline } from "../../utils/ast"; 6 | import { getPreviewCodeFromUrl } from "../../utils/url"; 7 | 8 | const scope = allDesignSystem; 9 | 10 | function Preview() { 11 | const [code, setCode] = useState(null); 12 | const noInline = useMemo(() => getReactLiveNoInline(code), [code]); 13 | 14 | useEffect(() => { 15 | setCode(getPreviewCodeFromUrl()); 16 | }, []); 17 | 18 | if (code === null) { 19 | return null; 20 | } 21 | 22 | return ( 23 | 24 | 31 | 32 | ); 33 | } 34 | 35 | export default Preview; 36 | -------------------------------------------------------------------------------- /website/src/pages/spacing/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTheme, Container, Stack, Text } from "basis"; 3 | 4 | function SpacingPage() { 5 | const theme = useTheme(); 6 | 7 | return ( 8 | 9 | 10 | We use the following scale for padding and margin: 11 | 12 | 13 | {theme.space.map((spacePx, index) => { 14 | if (spacePx === "0px") { 15 | return null; 16 | } 17 | 18 | return ( 19 | 20 | {index} 21 | 29 | {spacePx} 30 | 31 | ); 32 | })} 33 | 34 | 35 | ); 36 | } 37 | 38 | export default SpacingPage; 39 | -------------------------------------------------------------------------------- /website/src/pages/spacing/usage.mdx: -------------------------------------------------------------------------------- 1 | Spacing usage is coming soon. 2 | Meantime, check out the Overview. 3 | -------------------------------------------------------------------------------- /website/src/pages/typography/usage.mdx: -------------------------------------------------------------------------------- 1 | Typography usage is coming soon. 2 | Meantime, check out the Overview. 3 | -------------------------------------------------------------------------------- /website/src/themes/website/field.js: -------------------------------------------------------------------------------- 1 | export default (theme) => { 2 | return { 3 | getCSS: ({ targetElement, fullWidth, disabled }) => { 4 | switch (targetElement) { 5 | case "fieldContainer": { 6 | return { 7 | display: "inline-flex", 8 | flexDirection: "column", 9 | position: "relative", 10 | ...(fullWidth && { 11 | display: "flex", 12 | width: "100%", 13 | minWidth: 0, // See: https://stackoverflow.com/a/36247448/247243 14 | }), 15 | ...(disabled && { opacity: 0.5 }), 16 | }; 17 | } 18 | 19 | case "label": { 20 | return { 21 | display: "flex", 22 | fontFamily: theme.fonts.body, 23 | fontSize: theme.fontSizes[0], 24 | fontWeight: theme.fontWeights.medium, 25 | lineHeight: theme.lineHeights[1], 26 | color: theme.colors.grey.t65, 27 | marginBottom: theme.space[1], 28 | }; 29 | } 30 | 31 | case "helpText": { 32 | return { 33 | padding: `${theme.space[1]} ${theme.space[1]} 0`, 34 | }; 35 | } 36 | 37 | default: { 38 | return null; 39 | } 40 | } 41 | }, 42 | }; 43 | }; 44 | -------------------------------------------------------------------------------- /website/src/themes/website/index.js: -------------------------------------------------------------------------------- 1 | import { defaultTheme as theme } from "basis"; 2 | import button from "./button"; 3 | import field from "./field"; 4 | import input from "./input"; 5 | import link from "./link"; 6 | import select from "./select"; 7 | 8 | export default { 9 | ...theme, 10 | button: button(theme), 11 | field: field(theme), 12 | input: input(theme), 13 | link: link(theme), 14 | select: select(theme), 15 | }; 16 | -------------------------------------------------------------------------------- /website/src/themes/website/input.js: -------------------------------------------------------------------------------- 1 | export default (theme) => { 2 | return { 3 | getCSS: ({ targetElement }) => { 4 | switch (targetElement) { 5 | case "inputContainer": { 6 | return { 7 | position: "relative", 8 | fontSize: theme.fontSizes[0], 9 | fontWeight: theme.fontWeights.light, 10 | lineHeight: theme.lineHeights[1], 11 | fontFamily: theme.fonts.body, 12 | color: theme.colors.black, 13 | }; 14 | } 15 | 16 | case "input": { 17 | return { 18 | boxSizing: "border-box", 19 | width: "100%", 20 | height: "32px", 21 | margin: 0, 22 | paddingTop: 0, 23 | paddingBottom: 0, 24 | fontSize: "inherit", 25 | fontWeight: "inherit", 26 | lineHeight: "inherit", 27 | fontFamily: "inherit", 28 | color: theme.colors.grey.t65, 29 | backgroundColor: "transparent", 30 | padding: `0 ${theme.space[2]}`, 31 | borderRadius: theme.radii[1], 32 | borderWidth: theme.borderWidths[0], 33 | borderStyle: "solid", 34 | borderColor: theme.colors.grey.t30, 35 | ":focus": { 36 | outline: 0, 37 | color: theme.colors.black, 38 | borderColor: theme.colors.black, 39 | borderRadius: theme.radii[0], 40 | boxShadow: theme.shadows.focus, 41 | }, 42 | ":hover": { 43 | color: theme.colors.black, 44 | borderColor: theme.colors.black, 45 | }, 46 | }; 47 | } 48 | 49 | default: { 50 | return null; 51 | } 52 | } 53 | }, 54 | }; 55 | }; 56 | -------------------------------------------------------------------------------- /website/src/themes/website/link.js: -------------------------------------------------------------------------------- 1 | export default (theme) => { 2 | return { 3 | getCSS: ({ appearance }) => { 4 | let css = { 5 | display: "inline-block", 6 | textDecoration: "none", 7 | borderRadius: theme.radii[0], 8 | outline: 0, 9 | ...theme.focusStyles.focusVisible, 10 | fontFamily: "inherit", 11 | fontWeight: "inherit", 12 | color: "inherit", 13 | borderBottomColor: "inherit", 14 | ":hover, :active": { 15 | color: "inherit", 16 | borderBottomColor: "inherit", 17 | }, 18 | }; 19 | 20 | switch (appearance) { 21 | case "icon": { 22 | css = { 23 | ...css, 24 | display: "inline-block", 25 | verticalAlign: "top", 26 | }; 27 | break; 28 | } 29 | 30 | default: { 31 | break; 32 | } 33 | } 34 | 35 | return css; 36 | }, 37 | }; 38 | }; 39 | -------------------------------------------------------------------------------- /website/src/themes/website/select.js: -------------------------------------------------------------------------------- 1 | export default (theme) => { 2 | return { 3 | getCSS: ({ fullWidth }) => { 4 | return { 5 | display: "inline-block", 6 | fontSize: theme.fontSizes[0], 7 | lineHeight: theme.lineHeights[0], 8 | fontFamily: theme.fonts.body, 9 | fontWeight: theme.fontWeights.light, 10 | alignSelf: fullWidth ? "auto" : "flex-start", 11 | height: "32px", 12 | paddingLeft: theme.space[3], 13 | paddingRight: theme.space[9], 14 | margin: 0, 15 | borderRadius: theme.radii[1], 16 | borderWidth: theme.borderWidths[0], 17 | borderStyle: "solid", 18 | borderColor: theme.colors.grey.t30, 19 | color: theme.colors.grey.t65, 20 | backgroundColor: "transparent", 21 | appearance: "none", 22 | backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32' role='img' aria-label='Triangle down'%3E%3Cpath d='M20.747 14.509l-4.181 4.25a.786.786 0 01-1.132 0l-4.179-4.247a.885.885 0 01-.231-.827c.07-.3.287-.536.569-.62.282-.084 8.607-.101 8.912.035a.86.86 0 01.495.802.874.874 0 01-.253.607z' fill='%23414141'%3E%3C/path%3E%3C/svg%3E")`, 23 | backgroundRepeat: "no-repeat", 24 | backgroundPosition: `right 0 top 50%`, 25 | transition: "color 100ms ease, border-color 100ms ease", 26 | ":focus": { 27 | outline: 0, 28 | borderRadius: theme.radii[0], 29 | boxShadow: theme.shadows.focus, 30 | color: theme.colors.black, 31 | borderColor: theme.colors.black, 32 | }, 33 | ":hover, :active": { 34 | color: theme.colors.black, 35 | borderColor: theme.colors.black, 36 | }, 37 | }; 38 | }, 39 | }; 40 | }; 41 | -------------------------------------------------------------------------------- /website/src/utils/ast.js: -------------------------------------------------------------------------------- 1 | import { parse } from "@babel/parser"; 2 | import traverse from "@babel/traverse"; 3 | import generate from "@babel/generator"; 4 | import * as t from "@babel/types"; 5 | import { allComponentNames } from "./meta"; 6 | 7 | function getASTfromCode(code) { 8 | try { 9 | return parse(code, { plugins: ["jsx"] }); 10 | } catch (_e) { 11 | return null; 12 | } 13 | } 14 | 15 | export function getReactLiveNoInline(code) { 16 | try { 17 | const ast = getASTfromCode(code); 18 | 19 | if (ast === null) { 20 | return false; 21 | } 22 | 23 | const { body } = ast.program; 24 | const lastItem = body[body.length - 1]; 25 | 26 | if (!lastItem) { 27 | return false; 28 | } 29 | 30 | const { expression } = lastItem; 31 | 32 | if (expression.type !== "CallExpression") { 33 | return false; 34 | } 35 | 36 | return lastItem.expression.callee.name === "render"; 37 | } catch (_e) { 38 | return false; 39 | } 40 | } 41 | 42 | function getComponentName(nameObj) { 43 | switch (nameObj.type) { 44 | case "JSXIdentifier": { 45 | return nameObj.name; 46 | } 47 | 48 | case "JSXMemberExpression": { 49 | return `${getComponentName(nameObj.object)}.${getComponentName( 50 | nameObj.property 51 | )}`; 52 | } 53 | 54 | default: { 55 | return null; 56 | } 57 | } 58 | } 59 | 60 | export function annotateCodeForPlayground(code) { 61 | const ast = getASTfromCode(code); 62 | 63 | if (ast === null) { 64 | return code; 65 | } 66 | 67 | let count = 0; 68 | 69 | traverse(ast, { 70 | JSXOpeningElement: (path) => { 71 | const componentName = getComponentName(path.node.name); 72 | 73 | if (allComponentNames.includes(componentName) === false) { 74 | return; 75 | } 76 | 77 | const testId = `playground:${componentName}:${count}`; 78 | 79 | path.pushContainer( 80 | "attributes", 81 | t.jsxAttribute(t.jsxIdentifier("testId"), t.stringLiteral(testId)) 82 | ); 83 | 84 | count++; 85 | }, 86 | }); 87 | 88 | return generate(ast).code; 89 | } 90 | -------------------------------------------------------------------------------- /website/src/utils/color.js: -------------------------------------------------------------------------------- 1 | function str2rgb(str) { 2 | return { 3 | r: parseInt(str.slice(1, 3), 16), 4 | g: parseInt(str.slice(3, 5), 16), 5 | b: parseInt(str.slice(5, 7), 16), 6 | }; 7 | } 8 | 9 | // http://www.w3.org/WAI/GL/wiki/Relative_luminance 10 | function relativeLuminance({ r, g, b }) { 11 | [r, g, b] = [r, g, b].map((c) => { 12 | c = c / 255; 13 | 14 | if (c <= 0.03928) { 15 | return c / 12.92; 16 | } 17 | 18 | return Math.pow((c + 0.055) / 1.055, 2.4); 19 | }); 20 | 21 | return 0.2126 * r + 0.7152 * g + 0.0722 * b; 22 | } 23 | 24 | // http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html#key-terms 25 | export function colorContrast(str1, str2) { 26 | const L1 = relativeLuminance(str2rgb(str1)); 27 | const L2 = relativeLuminance(str2rgb(str2)); 28 | 29 | if (L1 < L2) { 30 | return (L2 + 0.05) / (L1 + 0.05); 31 | } 32 | 33 | return (L1 + 0.05) / (L2 + 0.05); 34 | } 35 | 36 | // http://www.w3.org/TR/2008/REC-WCAG20-20081211/#larger-scaledef 37 | function isLargeScale(fontSize, isBold) { 38 | const points = fontSize / 1.333; // 1pt = 1.333px according to http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html#visual-audio-contrast-contrast-73-head 39 | 40 | return isBold ? points >= 14 : points >= 18; 41 | } 42 | 43 | export function accessibleContrast(accessibilityLevel, fontSize, isBold) { 44 | switch (accessibilityLevel) { 45 | // http://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast-contrast 46 | case "AA": { 47 | return isLargeScale(fontSize, isBold) ? 3 : 4.5; 48 | } 49 | 50 | // http://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast7 51 | case "AAA": { 52 | return isLargeScale(fontSize, isBold) ? 4.5 : 7; 53 | } 54 | 55 | default: { 56 | return null; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /website/src/utils/color.test.js: -------------------------------------------------------------------------------- 1 | import { colorContrast } from "./color"; 2 | 3 | describe("colorContrast", () => { 4 | it("calculates the contrast", () => { 5 | expect(colorContrast("#123456", "#123456")).toBe(1); 6 | expect(colorContrast("#e7aa90", "#193ccb")).toBeCloseTo(4.137350318176988); 7 | expect(colorContrast("#000000", "#ffffff")).toBe(21); 8 | expect(colorContrast("#ffffff", "#000000")).toBe(21); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /website/src/utils/constants.js: -------------------------------------------------------------------------------- 1 | const COMPONENT_STATUS = { 2 | DRAFT: "DRAFT", 3 | READY: "READY", 4 | }; 5 | 6 | // Based on: https://github.com/FormidableLabs/prism-react-renderer/blob/078596ea53e0852dfe120a37aea30b33ae480603/src/themes/github.js 7 | const reactLiveEditorTheme = { 8 | plain: {}, 9 | styles: [ 10 | { 11 | types: ["comment", "prolog", "doctype", "cdata"], 12 | style: { 13 | color: "#999988", 14 | fontStyle: "italic", 15 | }, 16 | }, 17 | { 18 | types: ["namespace"], 19 | style: { 20 | opacity: 0.7, 21 | }, 22 | }, 23 | { 24 | types: ["string", "attr-value"], 25 | style: { 26 | color: "#e3116c", 27 | }, 28 | }, 29 | { 30 | types: ["punctuation", "operator"], 31 | style: { 32 | color: "#393A34", 33 | }, 34 | }, 35 | { 36 | types: [ 37 | "entity", 38 | "url", 39 | "symbol", 40 | "number", 41 | "boolean", 42 | "variable", 43 | "constant", 44 | "property", 45 | "regex", 46 | "inserted", 47 | ], 48 | style: { 49 | color: "#36acaa", 50 | }, 51 | }, 52 | { 53 | types: ["atrule", "keyword", "attr-name", "selector"], 54 | style: { 55 | color: "#00a4db", 56 | }, 57 | }, 58 | { 59 | types: ["function", "deleted", "tag"], 60 | style: { 61 | color: "#d73a49", 62 | }, 63 | }, 64 | { 65 | types: ["function-variable"], 66 | style: { 67 | color: "#6f42c1", 68 | }, 69 | }, 70 | { 71 | types: ["tag", "selector", "keyword"], 72 | style: { 73 | color: "#00009f", 74 | }, 75 | }, 76 | ], 77 | }; 78 | 79 | module.exports = { 80 | COMPONENT_STATUS, 81 | reactLiveEditorTheme, 82 | }; 83 | -------------------------------------------------------------------------------- /website/src/utils/formatting.js: -------------------------------------------------------------------------------- 1 | import prettier from "prettier/standalone"; 2 | import babel from "prettier/parser-babel"; 3 | 4 | export function formatCode(code, { printWidth = 56 } = {}) { 5 | let formattedCode; 6 | 7 | try { 8 | formattedCode = prettier.format(code.trim(), { 9 | parser: "babel", 10 | plugins: [babel], 11 | printWidth, 12 | semi: false, 13 | }); 14 | } catch (_e) { 15 | formattedCode = code; 16 | } 17 | 18 | if (formattedCode[0] === ";") { 19 | return formattedCode.slice(1); 20 | } 21 | 22 | return formattedCode; 23 | } 24 | 25 | export function nonDefaultProps(props) { 26 | return props 27 | .filter(({ value, defaultValue }) => value !== defaultValue && value !== "") 28 | .map(({ prop, value, type = "string" }) => { 29 | if (type === "number" && value !== "Unspecified") { 30 | return `${prop}={${value}}`; 31 | } 32 | 33 | if (type === "boolean" && value === true) { 34 | return prop; 35 | } 36 | 37 | if ( 38 | (type === "boolean" && value === false) || 39 | type === "function" || 40 | type === "jsx" 41 | ) { 42 | return `${prop}={${value}}`; 43 | } 44 | 45 | return `${prop}="${value}"`; 46 | }) 47 | .join(" "); 48 | } 49 | -------------------------------------------------------------------------------- /website/src/utils/meta.js: -------------------------------------------------------------------------------- 1 | import * as components from "../../../src/components"; 2 | 3 | const COMPONENT_NAME_REGEX = /^[A-Z][a-z]/; 4 | 5 | function getComponentNames(initialQueue) { 6 | const queue = initialQueue; 7 | const result = []; 8 | 9 | while (queue.length > 0) { 10 | const [componentName, componentFunction] = queue.shift(); 11 | 12 | result.push(componentName); 13 | 14 | Object.keys(componentFunction).forEach((key) => { 15 | if ( 16 | typeof componentFunction[key] === "function" && 17 | /* 18 | We test against this regex because forwardRef, for example, 19 | adds a "render" function onto the given component. 20 | */ 21 | COMPONENT_NAME_REGEX.test(key) 22 | ) { 23 | queue.push([`${componentName}.${key}`, componentFunction[key]]); 24 | } 25 | }); 26 | } 27 | 28 | return result; 29 | } 30 | 31 | export const allComponentNames = getComponentNames(Object.entries(components)); 32 | -------------------------------------------------------------------------------- /website/src/utils/meta.test.js: -------------------------------------------------------------------------------- 1 | import { pascalCase } from "pascal-case"; 2 | import { allComponentNames } from "./meta"; 3 | 4 | describe("allComponentNames", () => { 5 | it("all component names must be PascalCase with optional dots in between", () => { 6 | allComponentNames.forEach((componentName) => { 7 | const parts = componentName.split("."); 8 | 9 | parts.forEach((part) => { 10 | expect(pascalCase(part)).toBe(part); 11 | }); 12 | }); 13 | }); 14 | }); 15 | --------------------------------------------------------------------------------
{"This page doesn't exist."}
Home page is coming soon.
8 | Meantime, explore the components on the left. 9 |