├── .bettercodehub.yml ├── .circleci └── config.yml ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .npmignore ├── .prettierrc.json ├── .storybook ├── addons.js ├── config.js └── webpack.config.js ├── .stylelintrc.json ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── jest.config.json ├── linting ├── core.js ├── index.js ├── react.js └── sonar.js ├── package.json ├── src ├── __snapshots__ │ └── index.test.ts.snap ├── __utils__ │ ├── Story.tsx │ ├── StoryProperties.ts │ ├── Title.tsx │ └── flatten.ts ├── break │ ├── Break.story.tsx │ ├── Break.test.tsx │ ├── Break.tsx │ ├── __snapshots__ │ │ ├── Break.test.tsx.snap │ │ └── index.test.ts.snap │ ├── index.test.ts │ └── index.ts ├── column │ ├── Column.test.tsx │ ├── Column.tsx │ ├── ColumnAlignSelf.story.tsx │ ├── ColumnAlignSelf.ts │ ├── ColumnFlex.story.tsx │ ├── ColumnFlex.ts │ ├── ColumnOffset.story.tsx │ ├── ColumnOffset.ts │ ├── ColumnOrder.story.tsx │ ├── ColumnOrder.ts │ ├── ColumnProperties.ts │ ├── ColumnSize.ts │ ├── Columns.ts │ ├── __snapshots__ │ │ ├── Column.test.tsx.snap │ │ └── index.test.ts.snap │ ├── index.test.ts │ ├── index.ts │ ├── renderAlignSelf.test.ts │ ├── renderAlignSelf.ts │ ├── renderFlex.test.ts │ ├── renderFlex.ts │ ├── renderOffset.test.ts │ ├── renderOffset.ts │ ├── renderOrder.test.ts │ ├── renderOrder.ts │ ├── renderSize.test.ts │ └── renderSize.ts ├── container │ ├── Container.story.tsx │ ├── Container.test.tsx │ ├── Container.tsx │ ├── ContainerProperties.ts │ ├── __snapshots__ │ │ ├── Container.test.tsx.snap │ │ └── index.test.ts.snap │ ├── defaultWidth.ts │ ├── fullscreen.test.ts │ ├── fullscreen.ts │ ├── index.test.ts │ ├── index.ts │ ├── renderWidth.test.ts │ ├── renderWidth.ts │ ├── width.test.ts │ └── width.ts ├── index.test.ts ├── index.ts ├── media │ ├── BreakpointMap.ts │ ├── BreakpointValue.ts │ ├── BreakpointValues.ts │ ├── BreakpointValuesMap.ts │ ├── PropertyValue.ts │ ├── PropertyValues.ts │ ├── PropertyValuesMap.ts │ ├── __snapshots__ │ │ └── index.test.ts.snap │ ├── breakpoints.test.ts │ ├── breakpoints.ts │ ├── defaultBreakpointKey.test.ts │ ├── defaultBreakpointKey.ts │ ├── defaultBreakpoints.ts │ ├── index.test.ts │ └── index.ts ├── row │ ├── Row.story.tsx │ ├── Row.test.tsx │ ├── Row.tsx │ ├── RowAlignContent.story.tsx │ ├── RowAlignContent.ts │ ├── RowAlignItems.story.tsx │ ├── RowAlignItems.ts │ ├── RowDirection.story.tsx │ ├── RowDirection.ts │ ├── RowJustifyContent.story.tsx │ ├── RowJustifyContent.ts │ ├── RowProperties.ts │ ├── RowWrap.story.tsx │ ├── RowWrap.ts │ ├── __snapshots__ │ │ ├── Row.test.tsx.snap │ │ └── index.test.ts.snap │ ├── index.test.ts │ ├── index.ts │ ├── renderAlignContent.test.ts │ ├── renderAlignContent.ts │ ├── renderAlignItems.test.ts │ ├── renderAlignItems.ts │ ├── renderDirection.test.ts │ ├── renderDirection.ts │ ├── renderGutter.test.ts │ ├── renderGutter.ts │ ├── renderJustifyContent.test.ts │ ├── renderJustifyContent.ts │ ├── renderWrap.test.ts │ └── renderWrap.ts ├── theme │ ├── StyledComponents.ts │ ├── Theme.ts │ ├── ThemeProperties.ts │ ├── ThemeProvider.test.tsx │ ├── __snapshots__ │ │ ├── ThemeProvider.test.tsx.snap │ │ └── index.test.ts.snap │ ├── gutterWidth.test.ts │ ├── gutterWidth.ts │ ├── index.test.ts │ └── index.ts └── utils │ ├── RenderProvider.ts │ ├── __snapshots__ │ └── index.test.ts.snap │ ├── bootstrap.test.ts │ ├── bootstrap.ts │ ├── css.test.ts │ ├── css.ts │ ├── flatten.test.ts │ ├── flatten.ts │ ├── index.test.ts │ ├── index.ts │ ├── map.test.ts │ ├── map.ts │ ├── percentage.test.ts │ ├── percentage.ts │ ├── prepare.test.ts │ ├── prepare.ts │ ├── render.test.ts │ ├── render.ts │ ├── resolve.test.ts │ └── resolve.ts ├── test.config.ts ├── tsconfig.es5.json ├── tsconfig.json ├── tslint.json └── yarn.lock /.bettercodehub.yml: -------------------------------------------------------------------------------- 1 | component_depth: 2 2 | languages: 3 | - name: typescript 4 | production: 5 | exclude: 6 | - .*/__snapshots__/.*\.snap 7 | - .*/__utils__/.*\.ts 8 | - .*/__utils__/.*\.tsx 9 | - .*\.story\.tsx 10 | - .*\.test\.ts 11 | - .*\.test\.tsx 12 | test: 13 | include: 14 | - .*\.test\.ts 15 | - .*\.test\.tsx -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: ~/tmp 5 | docker: 6 | - image: circleci/node:8 7 | steps: 8 | - checkout 9 | - restore_cache: 10 | keys: 11 | - v1-dependencies-{{ checksum "package.json" }} 12 | # fallback to using the latest cache if no exact match is found 13 | - v1-dependencies- 14 | - run: yarn install 15 | - save_cache: 16 | paths: 17 | - node_modules 18 | key: v1-dependencies-{{ checksum "package.json" }} 19 | - run: yarn build 20 | 21 | cover: 22 | working_directory: ~/tmp 23 | docker: 24 | - image: circleci/node:8 25 | steps: 26 | - checkout 27 | - restore_cache: 28 | keys: 29 | - v1-dependencies-{{ checksum "package.json" }} 30 | # fallback to using the latest cache if no exact match is found 31 | - v1-dependencies- 32 | - run: yarn install 33 | - run: yarn build 34 | - run: yarn cover 35 | 36 | deploy: 37 | working_directory: ~/tmp 38 | docker: 39 | - image: circleci/node:8 40 | steps: 41 | - checkout 42 | - restore_cache: 43 | keys: 44 | - v1-dependencies-{{ checksum "package.json" }} 45 | # fallback to using the latest cache if no exact match is found 46 | - v1-dependencies- 47 | - run: yarn install 48 | - run: yarn build 49 | - run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/tmp/.npmrc 50 | - run: yarn publish --new-version ${CIRCLE_TAG} --no-git-tag-version 51 | 52 | workflows: 53 | version: 2 54 | build: 55 | jobs: 56 | - build: 57 | filters: 58 | branches: 59 | ignore: master 60 | tags: 61 | ignore: /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(\-\w*?\.[1-9]\d*)?$/ 62 | build_and_cover: 63 | jobs: 64 | - cover: 65 | filters: 66 | branches: 67 | only: master 68 | tags: 69 | ignore: /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(\-\w*?\.[1-9]\d*)?$/ 70 | build_and_deploy: 71 | jobs: 72 | - deploy: 73 | filters: 74 | branches: 75 | ignore: /.*/ 76 | tags: 77 | only: /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(\-\w*?\.[1-9]\d*)?$/ 78 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | max_line_length = 80 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | max_line_length = 0 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text-based files and normalize line endings 2 | * text=auto 3 | 4 | # For the following file types, normalize line endings to LF on 5 | # checkin and prevent conversion to CRLF when they are checked out 6 | .* text eol=lf 7 | *.html text eol=lf 8 | *.ejs text eol=lf 9 | *.css text eol=lf 10 | *.scss text eol=lf 11 | *.js text eol=lf 12 | *.jsx text eol=lf 13 | *.ts text eol=lf 14 | *.tsx text eol=lf 15 | *.json text eol=lf 16 | *.yaml text eol=lf 17 | *.yml text eol=lf 18 | *.md text eol=lf 19 | *.svg text eol=lf 20 | *.txt text eol=lf 21 | *.sh text eol=lf 22 | *.xml text eol=lf 23 | 24 | # Don't diff or textually merge source maps 25 | *.map binary 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | lib/ 3 | node_modules/ 4 | *.tgz 5 | 6 | # Editors & OS 7 | .DS_Store 8 | .idea/ 9 | 10 | # Logs 11 | *.log 12 | 13 | # Test 14 | coverage/ 15 | test/ 16 | junit.xml 17 | 18 | # Output 19 | *.js.map 20 | *.d.ts 21 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Build 2 | .circleci/ 3 | .storybook/ 4 | linting/ 5 | node_modules/ 6 | *.tgz 7 | 8 | # Editors & OS 9 | .DS_Store 10 | .idea/ 11 | .vscode/ 12 | 13 | # Logs 14 | *.log 15 | 16 | # Test 17 | coverage/ 18 | test/ 19 | junit.xml 20 | 21 | # Source 22 | src/__utils__/ 23 | src/**/__snapshots__/*.* 24 | src/**/*.story.* 25 | src/**/*.test.* 26 | 27 | # Config 28 | /.bettercodehub.yml 29 | /.editorconfig 30 | /.gitattributes 31 | /.prettierrc.json 32 | /.stylelintrc.json 33 | /jest.config.json 34 | /test.config.ts 35 | /tsconfig.json 36 | /tsconfig.*.json 37 | /tslint.config 38 | /tslint.json 39 | /yarn.lock 40 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": false, 7 | "trailingComma": "all", 8 | "bracketSpacing": false, 9 | "jsxBracketSameLine": false, 10 | "arrowParens": "always" 11 | } 12 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import "@storybook/addon-actions/register"; 2 | import "@storybook/addon-links/register"; 3 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from "@storybook/react"; 2 | 3 | // automatically import all files ending in *.stories.js 4 | const req = require.context("../src", true, /.*?\.story\.(ts|tsx)$/); 5 | function loadStories() { 6 | req.keys().forEach(filename => req(filename)); 7 | } 8 | 9 | configure(loadStories, module); 10 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | // load the default config generator. 2 | const genDefaultConfig = require("@storybook/react/dist/server/config/defaults/webpack.config.js"); 3 | module.exports = (baseConfig, env) => { 4 | const config = genDefaultConfig(baseConfig, env); 5 | // Extend it as you need. 6 | // For example, add typescript loader: 7 | config.module.rules.push({ 8 | test: /\.(ts|tsx)$/, 9 | loader: require.resolve("awesome-typescript-loader") 10 | }); 11 | config.resolve.extensions.push(".ts", ".tsx"); 12 | return config; 13 | }; 14 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "stylelint-config-standard", 4 | "stylelint-config-styled-components" 5 | ], 6 | "processors": ["stylelint-processor-styled-components"] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Jest Current File", 11 | "args": [ 12 | "--inspect-brk", 13 | "${workspaceRoot}/node_modules/jest/bin/jest.js", 14 | "--runInBand", 15 | "--config", 16 | "\"${workspaceRoot}/jest.config.json\"", 17 | "${relativeFile}" 18 | ], 19 | "protocol": "inspector", 20 | "console": "integratedTerminal", 21 | "internalConsoleOptions": "neverOpen" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules\\typescript\\lib" 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 ChilliCream (Michael & Rafael Staib) 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 | ![React Rasta](https://cdn.rawgit.com/ChilliCream/react-rasta-logo/master/img/react-rasta-banner-light.svg) 2 | 3 | [![release](https://img.shields.io/github/release/ChilliCream/react-rasta.svg)](https://github.com/ChilliCream/react-rasta/releases) [ 4 | ![package](https://img.shields.io/npm/v/react-rasta.svg)](https://www.npmjs.com/package/react-rasta) [![license](https://img.shields.io/github/license/ChilliCream/react-rasta.svg)](https://github.com/ChilliCream/react-rasta/blob/master/LICENSE) 5 | [![build](https://img.shields.io/circleci/project/github/ChilliCream/react-rasta/master.svg)](https://circleci.com/gh/ChilliCream/react-rasta/tree/master) [![coverage](https://img.shields.io/coveralls/ChilliCream/react-rasta.svg)](https://coveralls.io/github/ChilliCream/react-rasta?branch=master) [![better code](https://bettercodehub.com/edge/badge/ChilliCream/react-rasta)](https://bettercodehub.com/results/ChilliCream/react-rasta) [![code prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) 6 | 7 | --- 8 | 9 | **The most powerful and flexible grid system for _React_** 10 | 11 | _React Rasta_ is a 12 column grid system built on top of the _CSS flexbox_ layout and `styled-components`. 12 | 13 | ## Getting Started 14 | 15 | Here you will find all you need to get started quickly. 16 | 17 | ### Install Package 18 | 19 | First things first. Install the package `react-rasta` with _yarn_ or _npm_. 20 | 21 | When using _yarn_ it looks like this. 22 | 23 | ```powershell 24 | yarn add react-rasta 25 | ``` 26 | 27 | And when using _npm_ it looks like this. 28 | 29 | ```powershell 30 | npm install react-rasta --save 31 | ``` 32 | 33 | #### Required Dependencies 34 | 35 | _React Rasta_ depends on the following packages which need to be installed manually. 36 | 37 | | Package | Version | 38 | | ------------------- | ------------ | 39 | | `react` | 16 or higher | 40 | | `styled-components` | 3 or higher | 41 | 42 | ### Code Examples 43 | 44 | ```tsx 45 | import React, {Component} from "react"; 46 | import {Column, Container, Row} from "react-rasta"; 47 | 48 | export default class App extends Component { 49 | render() { 50 | return ( 51 | 52 | 53 | Left 54 | Middle 1 55 | Middle 2 56 | Right 57 | 58 | 59 | ); 60 | } 61 | } 62 | ``` 63 | 64 | Breakpoints (which will end up in media queries) could be redefined via `ThemeProvider`. 65 | 66 | ```tsx 67 | import React, {Component} from "react"; 68 | import {Column, Container, Row, ThemeProvider} from "react-rasta"; 69 | 70 | const breakpoints = { 71 | phone: 0, 72 | tablet: 600, 73 | desktop: 800, 74 | }; 75 | 76 | const containerWidth = { 77 | // do not specify phone here 78 | tablet: 560, 79 | desktop: 760, 80 | }; 81 | 82 | export default class App extends Component { 83 | render() { 84 | return ( 85 | 86 | 87 | 88 | Left 89 | Middle 1 90 | Middle 2 91 | Right 92 | 93 | 94 | 95 | ); 96 | } 97 | } 98 | ``` 99 | 100 | ## Documentation 101 | 102 | Click [here](http://react-rasta.com) for the documentation. 103 | -------------------------------------------------------------------------------- /jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectCoverageFrom": [ 3 | "src/**/*.{js,jsx,ts,tsx}", 4 | "!src/**/__snapshots__/**/*.*", 5 | "!src/__utils__/**/*.*", 6 | "!src/**/*.story.{js,jsx,ts,tsx}" 7 | ], 8 | "coverageReporters": ["cobertura", "lcov"], 9 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"], 10 | "setupTestFrameworkScriptFile": "./test.config.ts", 11 | "snapshotSerializers": ["enzyme-to-json/serializer"], 12 | "testRegex": ".*?\\.(spec|test)\\.(jsx?|tsx?)$", 13 | "testResultsProcessor": "jest-junit", 14 | "transform": { 15 | "^.+\\.tsx?$": "ts-jest" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /linting/core.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const ON = true; 4 | const OFF = false; 5 | 6 | module.exports = { 7 | rules: { 8 | // TypeScript Specific 9 | "adjacent-overload-signatures": ON, 10 | "ban-types": [ 11 | ON, 12 | [ 13 | ["Object", "Avoid using the `Object` type. Did you mean `object`?"], 14 | [ 15 | "Function", 16 | "Avoid using the `Function` type. Prefer a specific function type, like `() => void`.", 17 | ], 18 | ["Boolean", "Avoid using the `Boolean` type. Did you mean `boolean`?"], 19 | ["Number", "Avoid using the `Number` type. Did you mean `number`?"], 20 | ["String", "Avoid using the `String` type. Did you mean `string`?"], 21 | ["Symbol", "Avoid using the `Symbol` type. Did you mean `symbol`?"], 22 | ], 23 | ], 24 | "member-access": [ON, "no-public"], 25 | "member-ordering": [ 26 | ON, 27 | { 28 | order: [ 29 | "static-field", 30 | "static-method", 31 | "instance-field", 32 | "constructor", 33 | "instance-method", 34 | ], 35 | }, 36 | ], 37 | "no-any": ON, 38 | "no-empty-interface": OFF, 39 | "no-import-side-effect": OFF, 40 | "no-inferrable-types": [ON, "ignore-params"], 41 | "no-internal-module": ON, 42 | "no-magic-numbers": OFF, 43 | "no-namespace": ON, 44 | "no-non-null-assertion": OFF, 45 | "no-reference": ON, 46 | "no-this-assignment": ON, 47 | "no-var-requires": ON, 48 | "only-arrow-functions": OFF, 49 | "prefer-for-of": ON, 50 | "prefer-readonly": OFF, 51 | "promise-function-async": OFF, 52 | typedef: [ON, "parameter", "property-declaration"], 53 | "typedef-whitespace": [ 54 | ON, 55 | { 56 | "call-signature": "nospace", 57 | "index-signature": "nospace", 58 | parameter: "nospace", 59 | "property-declaration": "nospace", 60 | "variable-declaration": "nospace", 61 | }, 62 | { 63 | "call-signature": "onespace", 64 | "index-signature": "onespace", 65 | parameter: "onespace", 66 | "property-declaration": "onespace", 67 | "variable-declaration": "onespace", 68 | }, 69 | ], 70 | "unified-signatures": ON, 71 | 72 | // Functionality 73 | "await-promise": ON, 74 | ban: [ 75 | ON, 76 | "alert", 77 | "prompt", 78 | "confirm", 79 | ["_", "isNull", "Use plain JS: == null"], 80 | ["_", "isDefined", "Use plain JS: != null"], 81 | ["Object", "assign", "Use object spread: {...a, ...b}"], 82 | ["Object", "getOwnPropertyNames", "Use Object.keys()"], 83 | ["describe", "only", "Should not be committed to repo"], 84 | ["it", "only", "Should not be committed to repo"], 85 | ["test", "only", "Should not be committed to repo"], 86 | ], 87 | "ban-comma-operator": ON, 88 | curly: ON, 89 | forin: ON, 90 | "import-blacklist": OFF, 91 | "label-position": ON, 92 | "no-arg": ON, 93 | "no-bitwise": ON, 94 | "no-conditional-assignment": ON, 95 | "no-console": ON, 96 | "no-construct": ON, 97 | "no-debugger": ON, 98 | "no-duplicate-super": ON, 99 | "no-duplicate-switch-case": ON, 100 | "no-duplicate-variable": [ON, "check-parameters"], 101 | "no-dynamic-delete": ON, 102 | "no-empty": ON, 103 | "no-eval": ON, 104 | "no-floating-promises": OFF, 105 | "no-for-in-array": ON, 106 | "no-implicit-dependencies": [ON, "dev", "optional"], 107 | "no-inferred-empty-object-type": ON, 108 | "no-invalid-template-strings": ON, 109 | "no-invalid-this": OFF, 110 | "no-misused-new": ON, 111 | "no-null-keyword": OFF, 112 | "no-object-literal-type-assertion": ON, 113 | "no-return-await": ON, 114 | "no-shadowed-variable": ON, 115 | "no-string-literal": ON, 116 | "no-string-throw": ON, 117 | "no-sparse-arrays": ON, 118 | "no-submodule-imports": OFF, 119 | "no-switch-case-fall-through": OFF, 120 | "no-unbound-method": OFF, 121 | "no-unnecessary-class": [ON, "allow-empty-class"], 122 | "no-unsafe-any": OFF, 123 | "no-unsafe-finally": ON, 124 | "no-unused-expression": ON, 125 | "no-unused-variable": OFF, 126 | "no-use-before-declare": ON, 127 | "no-var-keyword": ON, 128 | "no-void-expression": ON, 129 | "prefer-conditional-expression": ON, 130 | radix: ON, 131 | "restrict-plus-operands": ON, 132 | "strict-boolean-expressions": OFF, 133 | "strict-type-predicates": ON, 134 | "switch-default": ON, 135 | "typeof-compare": OFF, 136 | "triple-equals": [ON, "allow-null-check", "allow-undefined-check"], 137 | "use-default-type-parameter": ON, 138 | "use-isnan": ON, 139 | 140 | // Maintainability 141 | "cyclomatic-complexity": ON, 142 | eofline: ON, 143 | indent: [ON, "spaces", 2], 144 | "linebreak-style": [ON, "LF"], 145 | "max-classes-per-file": [ON, 1], 146 | "max-file-line-count": [ON, 1000], 147 | "max-line-length": OFF, 148 | "no-default-export": OFF, 149 | "no-duplicate-imports": ON, 150 | "no-irregular-whitespace": ON, 151 | "no-mergeable-namespace": ON, 152 | "no-parameter-reassignment": ON, 153 | "no-require-imports": ON, 154 | "no-trailing-whitespace": ON, 155 | "object-literal-sort-keys": OFF, 156 | "prefer-const": ON, 157 | "trailing-comma": [ 158 | ON, 159 | { 160 | multiline: "always", 161 | singleline: "never", 162 | esSpecCompliant: true, 163 | }, 164 | ], 165 | 166 | // Style 167 | align: OFF, 168 | "array-type": [ON, "array-simple"], 169 | "arrow-parens": ON, 170 | "arrow-return-shorthand": [ON, "multiline"], 171 | "binary-expression-operand-order": ON, 172 | "callable-types": ON, 173 | "class-name": ON, 174 | "comment-format": [ON, "check-space"], 175 | "completed-docs": OFF, 176 | "file-header": OFF, 177 | deprecation: ON, 178 | encoding: ON, 179 | "file-name-casing": OFF, 180 | "import-spacing": ON, 181 | "interface-name": [ON, "never-prefix"], 182 | "interface-over-type-literal": ON, 183 | "jsdoc-format": [ON, "check-multiline-start"], 184 | "match-default-export-name": ON, 185 | "new-parens": ON, 186 | "newline-before-return": ON, 187 | "newline-per-chained-call": OFF, 188 | "no-angle-bracket-type-assertion": ON, 189 | "no-boolean-literal-compare": ON, 190 | "no-consecutive-blank-lines": ON, 191 | "no-parameter-properties": ON, 192 | "no-redundant-jsdoc": OFF, 193 | "no-reference-import": ON, 194 | "no-unnecessary-callback-wrapper": ON, 195 | "no-unnecessary-initializer": ON, 196 | "no-unnecessary-qualifier": ON, 197 | "no-unnecessary-type-assertion": ON, 198 | "number-literal-format": ON, 199 | "object-literal-key-quotes": [ON, "consistent-as-needed"], 200 | "object-literal-shorthand": ON, 201 | "one-line": [ 202 | ON, 203 | "check-catch", 204 | "check-finally", 205 | "check-else", 206 | "check-open-brace", 207 | "check-whitespace", 208 | ], 209 | "one-variable-per-declaration": ON, 210 | "ordered-imports": OFF, 211 | "prefer-function-over-method": OFF, 212 | "prefer-method-signature": OFF, 213 | "prefer-object-spread": ON, 214 | "prefer-switch": ON, 215 | "prefer-template": ON, 216 | quotemark: [ON, "double", "jsx-double", "avoid-escape"], 217 | "return-undefined": OFF, 218 | semicolon: [OFF, "always", "ignore-bound-class-methods"], 219 | "space-before-function-paren": [ 220 | ON, 221 | { 222 | anonymous: "never", 223 | asyncArrow: "always", 224 | constructor: "never", 225 | method: "never", 226 | named: "never", 227 | }, 228 | ], 229 | "space-within-parens": [ON, 0], 230 | "switch-final-break": [ON, "always"], 231 | "type-literal-delimiter": ON, 232 | "variable-name": [ON, "ban-keywords", "check-format", "allow-pascal-case"], 233 | whitespace: [ 234 | ON, 235 | "check-branch", 236 | "check-decl", 237 | "check-operator", 238 | "check-separator", 239 | "check-rest-spread", 240 | "check-type", 241 | "check-type-operator", 242 | "check-preblock", 243 | ], 244 | }, 245 | }; 246 | -------------------------------------------------------------------------------- /linting/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | extends: ["./core", "./react", "./sonar"], 5 | linterOptions: { 6 | exclude: ["node_modules/**"], 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /linting/react.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const ON = true; 4 | const OFF = false; 5 | 6 | module.exports = { 7 | extends: "tslint-react", 8 | rules: { 9 | // All 10 | "jsx-alignment": ON, 11 | "jsx-boolean-value": ON, 12 | "jsx-curly-spacing": [ON, "never"], 13 | "jsx-equals-spacing": [ON, "never"], 14 | "jsx-key": ON, 15 | "jsx-no-bind": ON, 16 | "jsx-no-lambda": OFF, 17 | "jsx-no-multiline-js": OFF, 18 | "jsx-no-string-ref": ON, 19 | "jsx-self-close": ON, 20 | "jsx-wrap-multiline": ON, 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /linting/sonar.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const ON = true; 4 | const OFF = false; 5 | 6 | module.exports = { 7 | extends: "tslint-sonarts", 8 | rules: { 9 | // Bugs 10 | "no-all-duplicated-branches": ON, 11 | "no-case-with-or": ON, 12 | "no-collection-size-mischeck": ON, 13 | "no-element-overwrite": ON, 14 | "no-empty-destructuring": ON, 15 | "no-identical-conditions": ON, 16 | "no-identical-expressions": ON, 17 | "no-ignored-initial-value": ON, 18 | "no-misleading-array-reverse": ON, 19 | "no-misspelled-operator": ON, 20 | "no-self-assignment": ON, 21 | "no-unthrown-error": ON, 22 | "no-use-of-empty-return-value": ON, 23 | "no-useless-increment": ON, 24 | "no-useless-intersection": ON, 25 | 26 | // Code Smell 27 | "cognitive-complexity": ON, 28 | "mccabe-complexity": OFF, 29 | "no-accessor-field-mismatch": ON, 30 | "no-array-delete": ON, 31 | "no-big-function": [ON, 300], 32 | "no-commented-code": ON, 33 | "no-dead-store": ON, 34 | "no-duplicate-string": ON, 35 | "no-duplicated-branches": ON, 36 | "no-empty-nested-blocks": ON, 37 | "no-extra-semicolon": ON, 38 | "no-gratuitous-expressions": ON, 39 | "no-hardcoded-credentials": ON, 40 | "no-identical-functions": ON, 41 | "no-ignored-return": ON, 42 | "no-inconsistent-return": OFF, 43 | "no-multiline-string-literals": ON, 44 | "no-nested-incdec": ON, 45 | "no-redundant-boolean": ON, 46 | "no-redundant-parentheses": ON, 47 | "no-return-type-any": ON, 48 | "no-same-line-conditional": ON, 49 | "no-small-switch": ON, 50 | "no-statements-same-line": ON, 51 | "no-unconditional-jump": ON, 52 | "no-unenclosed-multiline-block": ON, 53 | "no-unused-array": ON, 54 | "no-useless-cast": ON, 55 | "no-variable-usage-before-declaration": ON, 56 | "parameters-max-number": ON, 57 | "prefer-immediate-return": ON, 58 | "use-primitive-type": ON, 59 | "use-type-alias": ON, 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-rasta", 3 | "version": "1.0.0", 4 | "description": "The most powerful and flexible grid system for React", 5 | "main": "./lib/es6/index.js", 6 | "types": "./lib/es6/index.d.ts", 7 | "maintainers": ["Rafael Staib"], 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/ChilliCream/react-rasta" 11 | }, 12 | "author": "ChilliCream", 13 | "license": "MIT", 14 | "keywords": ["grid", "layout", "css", "flexbox"], 15 | "scripts": { 16 | "build": 17 | "yarn prettier && yarn lint && yarn test:coverage && (tsc & tsc -p ./tsconfig.es5.json)", 18 | "build-storybook": "build-storybook", 19 | "cover": "coveralls < coverage/lcov.info", 20 | "lint": "yarn lint:code && yarn lint:style", 21 | "lint:code": "tslint -p ./tsconfig.json \"./src/**/*.ts*\"", 22 | "lint:style": "stylelint \"./src/**/*.ts*\"", 23 | "precommit": "pretty-quick --staged", 24 | "prettier": "prettier \"./*.+(js|json|md)\" \"./src/**/*.*\" --write", 25 | "storybook": "start-storybook -p 6006", 26 | "test": "jest --runInBand --config jest.config.json", 27 | "test:coverage": "jest --runInBand --coverage --config jest.config.json" 28 | }, 29 | "peerDependencies": { 30 | "styled-components": ">= 3 < 4" 31 | }, 32 | "devDependencies": { 33 | "@storybook/addon-actions": "^3.4.0", 34 | "@storybook/addon-links": "^3.4.0", 35 | "@storybook/addons": "^3.4.0", 36 | "@storybook/react": "^3.4.0", 37 | "@types/enzyme": "^3.1.6", 38 | "@types/enzyme-adapter-react-16": "^1.0.1", 39 | "@types/jest": "^22.0.1", 40 | "@types/react": "^16.0.40", 41 | "@types/react-dom": "^16.0.4", 42 | "@types/storybook__addon-actions": "^3.0.3", 43 | "@types/storybook__addon-links": "^3.3.0", 44 | "@types/storybook__react": "^3.0.7", 45 | "awesome-typescript-loader": "^4.0.1", 46 | "babel-core": "^6.26.0", 47 | "babel-runtime": "^6.26.0", 48 | "coveralls": "^3.0.0", 49 | "enzyme": "^3.3.0", 50 | "enzyme-adapter-react-16": "^1.1.1", 51 | "enzyme-to-json": "^3.3.3", 52 | "husky": "^0.14.3", 53 | "jest": "^22.0.5", 54 | "jest-junit": "^3.6.0", 55 | "jest-styled-components": "^5.0.1", 56 | "prettier": "^1.11.1", 57 | "pretty-quick": "^1.4.1", 58 | "react": "^16.2.0", 59 | "react-dom": "^16.2.0", 60 | "styled-components": "^3.4.9", 61 | "stylelint": "^9.2.1", 62 | "stylelint-config-standard": "^18.2.0", 63 | "stylelint-config-styled-components": "^0.1.1", 64 | "stylelint-processor-styled-components": "^1.3.1", 65 | "ts-jest": "^22.0.1", 66 | "tslint": "^5.9.1", 67 | "tslint-react": "^3.3.3", 68 | "tslint-sonarts": "^1.6.0", 69 | "typescript": "^2.9.1" 70 | }, 71 | "dependencies": {} 72 | } 73 | -------------------------------------------------------------------------------- /src/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`index should verify the API shape 1`] = ` 4 | Object { 5 | "Break": [Function], 6 | "BreakpointMap": Object {}, 7 | "BreakpointValue": Object {}, 8 | "BreakpointValues": Object {}, 9 | "Column": [Function], 10 | "Container": [Function], 11 | "Row": [Function], 12 | "Theme": Object {}, 13 | "ThemeProvider": [Function], 14 | } 15 | `; 16 | -------------------------------------------------------------------------------- /src/__utils__/Story.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import Column from "../column"; 3 | import Container from "../container"; 4 | import Row from "../row"; 5 | import StoryProperties from "./StoryProperties"; 6 | 7 | const Story = styled.div` 8 | font-family: 9 | -apple-system, 10 | BlinkMacSystemFont, 11 | "Segoe UI", 12 | Roboto, 13 | "Helvetica Neue", 14 | Arial, 15 | sans-serif, 16 | "Apple Color Emoji", 17 | "Segoe UI Emoji", 18 | "Segoe UI Symbol"; 19 | ${(props: StoryProperties) => (props.fullscreen ? "height: 100%;" : "")} 20 | 21 | ${/*sc-selector*/ Container}, 22 | ${/*sc-selector*/ Row} { 23 | background: #ccc; 24 | ${(props: StoryProperties) => (props.fullscreen ? "" : "height: 300px;")} 25 | } 26 | 27 | ${/*sc-selector*/ Row} > ${/*sc-selector*/ Column} { 28 | background: #0a5991; 29 | border: 5px solid #ccc; 30 | color: #fff; 31 | font-size: 2em; 32 | line-height: 65px; 33 | text-align: center; 34 | } 35 | `; 36 | 37 | export default Story; 38 | -------------------------------------------------------------------------------- /src/__utils__/StoryProperties.ts: -------------------------------------------------------------------------------- 1 | interface StoryProperties { 2 | fullscreen?: boolean; 3 | } 4 | 5 | export default StoryProperties; 6 | -------------------------------------------------------------------------------- /src/__utils__/Title.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | const Title = styled.div` 4 | color: #0a5991; 5 | font-size: 3em; 6 | margin: 30px 0; 7 | text-align: center; 8 | `; 9 | 10 | export default Title; 11 | -------------------------------------------------------------------------------- /src/__utils__/flatten.ts: -------------------------------------------------------------------------------- 1 | export default (source?: string) => { 2 | if (source == null) { 3 | return ""; 4 | } else { 5 | return source.replace(/\n|\r|\s|\t/gi, ""); 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /src/break/Break.story.tsx: -------------------------------------------------------------------------------- 1 | import {action} from "@storybook/addon-actions"; 2 | import {storiesOf} from "@storybook/react"; 3 | import React from "react"; 4 | import Story from "../__utils__/Story"; 5 | import Title from "../__utils__/Title"; 6 | 7 | import {Break, Column, Container, Row} from ".."; 8 | 9 | storiesOf("Break", module).add("break", () => ( 10 | 11 | break 12 | 13 | 14 | 1 15 | 16 | 2 17 | 18 | 19 | 20 | )); 21 | -------------------------------------------------------------------------------- /src/break/Break.test.tsx: -------------------------------------------------------------------------------- 1 | import {shallow} from "enzyme"; 2 | import "jest-styled-components"; 3 | import React from "react"; 4 | import Break from "./Break"; 5 | 6 | describe("", () => { 7 | it("should match the snapshot", () => { 8 | // act 9 | const result = shallow(); 10 | 11 | // assert 12 | expect(result).toMatchSnapshot(); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/break/Break.tsx: -------------------------------------------------------------------------------- 1 | import {ClassAttributes, HTMLAttributes} from "react"; 2 | import {StyledComponentClass} from "styled-components"; 3 | import {styled, Theme} from "../theme"; 4 | import "../utils/bootstrap"; 5 | 6 | const Break = styled.div` 7 | width: 100%; 8 | height: 0; 9 | `; 10 | 11 | export default Break; 12 | -------------------------------------------------------------------------------- /src/break/__snapshots__/Break.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` should match the snapshot 1`] = ` 4 | .c0 { 5 | width: 100%; 6 | height: 0; 7 | } 8 | 9 |
12 | `; 13 | -------------------------------------------------------------------------------- /src/break/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`index should verify the API shape 1`] = ` 4 | Object { 5 | "default": [Function], 6 | } 7 | `; 8 | -------------------------------------------------------------------------------- /src/break/index.test.ts: -------------------------------------------------------------------------------- 1 | import * as module from "."; 2 | 3 | describe("index", () => { 4 | it("should verify the API shape", () => { 5 | expect(module).toMatchSnapshot(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/break/index.ts: -------------------------------------------------------------------------------- 1 | import Break from "./Break"; 2 | 3 | export default Break; 4 | -------------------------------------------------------------------------------- /src/column/Column.test.tsx: -------------------------------------------------------------------------------- 1 | import {shallow} from "enzyme"; 2 | import "jest-styled-components"; 3 | import React from "react"; 4 | import Column from "./Column"; 5 | 6 | describe("", () => { 7 | it("should match the snapshot (no properties set)", () => { 8 | // act 9 | const result = shallow(); 10 | 11 | // assert 12 | expect(result).toMatchSnapshot(); 13 | }); 14 | 15 | it("should match the snapshot (alignSelf: 'strech') ", () => { 16 | // act 17 | const result = shallow(); 18 | 19 | // assert 20 | expect(result).toMatchSnapshot(); 21 | }); 22 | 23 | it("should match the snapshot (alignSelf: { xs: 'baseline', sm: 'center', md: 'flex-end' })", () => { 24 | // act 25 | const result = shallow( 26 | , 27 | ); 28 | 29 | // assert 30 | expect(result).toMatchSnapshot(); 31 | }); 32 | 33 | it("should match the snapshot (offset: 3) ", () => { 34 | // arrange 35 | const offset = 3; 36 | 37 | // act 38 | const result = shallow(); 39 | 40 | // assert 41 | expect(result).toMatchSnapshot(); 42 | }); 43 | 44 | it("should match the snapshot (offset: { xs: 3, sm: 4, md: 8 }) ", () => { 45 | // act 46 | const result = shallow(); 47 | 48 | // assert 49 | expect(result).toMatchSnapshot(); 50 | }); 51 | 52 | it("should match the snapshot (order: 5) ", () => { 53 | // act 54 | const result = shallow(); 55 | 56 | // assert 57 | expect(result).toMatchSnapshot(); 58 | }); 59 | 60 | it("should match the snapshot (order: { xs: 'first', sm: 3, md: 'last' }) ", () => { 61 | // act 62 | const result = shallow(); 63 | 64 | // assert 65 | expect(result).toMatchSnapshot(); 66 | }); 67 | 68 | it("should match the snapshot (size: 7) ", () => { 69 | // act 70 | const result = shallow(); 71 | 72 | // assert 73 | expect(result).toMatchSnapshot(); 74 | }); 75 | 76 | it("should match the snapshot (size: { xs: 'auto', sm: 7, md: 'none' }) ", () => { 77 | // act 78 | const result = shallow(); 79 | 80 | // assert 81 | expect(result).toMatchSnapshot(); 82 | }); 83 | 84 | it("should match the snapshot (alignSelf: 'stretch', offset: 3, order: 3, size: 9)", () => { 85 | // arrange 86 | const offset = 3; 87 | const order = 3; 88 | const size = 9; 89 | 90 | // act 91 | const result = shallow( 92 | , 98 | ); 99 | 100 | // assert 101 | expect(result).toMatchSnapshot(); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /src/column/Column.tsx: -------------------------------------------------------------------------------- 1 | import {ClassAttributes, HTMLAttributes} from "react"; 2 | import {StyledComponentClass} from "styled-components"; 3 | import {BreakpointValue, PropertyValue} from "../media"; 4 | import {styled, Theme} from "../theme"; 5 | import {render} from "../utils"; 6 | import "../utils/bootstrap"; 7 | import ColumnAlignSelf from "./ColumnAlignSelf"; 8 | import ColumnFlex from "./ColumnFlex"; 9 | import ColumnOffset from "./ColumnOffset"; 10 | import ColumnOrder from "./ColumnOrder"; 11 | import ColumnProperties from "./ColumnProperties"; 12 | import ColumnSize from "./ColumnSize"; 13 | import renderAlignSelf from "./renderAlignSelf"; 14 | import renderFlex from "./renderFlex"; 15 | import renderOffset from "./renderOffset"; 16 | import renderOrder from "./renderOrder"; 17 | import renderSize from "./renderSize"; 18 | 19 | const Column = styled.div` 20 | position: relative; 21 | width: 100%; 22 | min-height: 1px; 23 | 24 | ${(props: ColumnProperties) => { 25 | const renderer = { 26 | alignSelf: (value?: PropertyValue) => 27 | renderAlignSelf(value as ColumnAlignSelf), 28 | flex: (value?: PropertyValue) => renderFlex(value as ColumnFlex), 29 | offset: (value?: PropertyValue) => renderOffset(value as ColumnOffset), 30 | order: (value?: PropertyValue) => renderOrder(value as ColumnOrder), 31 | size: (value?: PropertyValue) => renderSize(value as ColumnSize), 32 | }; 33 | const valueMap = { 34 | alignSelf: props.alignSelf, 35 | flex: props.flex, 36 | offset: props.offset, 37 | order: props.order, 38 | size: props.size, 39 | }; 40 | 41 | return render(valueMap, renderer, props.theme); 42 | }}; 43 | `; 44 | 45 | export default Column; 46 | -------------------------------------------------------------------------------- /src/column/ColumnAlignSelf.story.tsx: -------------------------------------------------------------------------------- 1 | import {action} from "@storybook/addon-actions"; 2 | import {storiesOf} from "@storybook/react"; 3 | import React from "react"; 4 | import Story from "../__utils__/Story"; 5 | import Title from "../__utils__/Title"; 6 | 7 | import {Column, Container, Row} from ".."; 8 | 9 | storiesOf("ColumnAlignSelf", module) 10 | .add("baseline", () => ( 11 | 12 | baseline 13 | 14 | 15 | 16 | 1 17 | 18 | 19 | 2 20 | 21 | 22 | 3 23 | 24 | 25 | 4 26 | 27 | 28 | 29 | 30 | )) 31 | .add("center", () => ( 32 | 33 | center 34 | 35 | 36 | 37 | 1 38 | 39 | 40 | 2 41 | 42 | 43 | 3 44 | 45 | 46 | 4 47 | 48 | 49 | 50 | 51 | )) 52 | .add("flex-end", () => ( 53 | 54 | flex-end 55 | 56 | 57 | 58 | 1 59 | 60 | 61 | 2 62 | 63 | 64 | 3 65 | 66 | 67 | 4 68 | 69 | 70 | 71 | 72 | )) 73 | .add("flex-start", () => ( 74 | 75 | flex-start 76 | 77 | 78 | 79 | 1 80 | 81 | 82 | 2 83 | 84 | 85 | 3 86 | 87 | 88 | 4 89 | 90 | 91 | 92 | 93 | )) 94 | .add("stretch", () => ( 95 | 96 | stretch 97 | 98 | 99 | 100 | 1 101 | 102 | 103 | 2 104 | 105 | 106 | 3 107 | 108 | 109 | 4 110 | 111 | 112 | 113 | 114 | )); 115 | -------------------------------------------------------------------------------- /src/column/ColumnAlignSelf.ts: -------------------------------------------------------------------------------- 1 | type ColumnAlignSelf = 2 | | "baseline" 3 | | "center" 4 | | "flex-end" 5 | | "flex-start" 6 | | "stretch"; 7 | 8 | export default ColumnAlignSelf; 9 | -------------------------------------------------------------------------------- /src/column/ColumnFlex.story.tsx: -------------------------------------------------------------------------------- 1 | import {action} from "@storybook/addon-actions"; 2 | import {storiesOf} from "@storybook/react"; 3 | import React from "react"; 4 | import Story from "../__utils__/Story"; 5 | import Title from "../__utils__/Title"; 6 | 7 | import {Column, Container, Row} from ".."; 8 | 9 | storiesOf("ColumnFlex", module).add("flex", () => ( 10 | 11 | flex 12 | 13 | 14 | 15 | grow grow grow grow 16 | 17 | 18 | shrink 19 | 20 | 21 | none 22 | 23 | undefined 24 | 25 | 26 | 27 | )); 28 | -------------------------------------------------------------------------------- /src/column/ColumnFlex.ts: -------------------------------------------------------------------------------- 1 | type ColumnFlex = "grow" | "none" | "shrink"; 2 | 3 | export default ColumnFlex; 4 | -------------------------------------------------------------------------------- /src/column/ColumnOffset.story.tsx: -------------------------------------------------------------------------------- 1 | import {action} from "@storybook/addon-actions"; 2 | import {storiesOf} from "@storybook/react"; 3 | import React from "react"; 4 | import Story from "../__utils__/Story"; 5 | import Title from "../__utils__/Title"; 6 | 7 | import {Column, Container, Row} from ".."; 8 | 9 | storiesOf("ColumnOffset", module).add("offset", () => ( 10 | 11 | offset 12 | 13 | 14 | 15 | Offset 3 16 | 17 | 18 | Offset 3 19 | 20 | Offset 0 21 | 22 | Offset 6 23 | 24 | 25 | 26 | 27 | )); 28 | -------------------------------------------------------------------------------- /src/column/ColumnOffset.ts: -------------------------------------------------------------------------------- 1 | type ColumnOffset = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11; 2 | 3 | export default ColumnOffset; 4 | -------------------------------------------------------------------------------- /src/column/ColumnOrder.story.tsx: -------------------------------------------------------------------------------- 1 | import {action} from "@storybook/addon-actions"; 2 | import {storiesOf} from "@storybook/react"; 3 | import React from "react"; 4 | import Story from "../__utils__/Story"; 5 | import Title from "../__utils__/Title"; 6 | 7 | import {Column, Container, Row} from ".."; 8 | 9 | storiesOf("ColumnOrder", module).add("order", () => ( 10 | 11 | order 12 | 13 | 14 | 18 | 1 19 | 20 | 21 | 2 22 | 23 | 24 | 3 25 | 26 | 27 | 4 28 | 29 | 30 | 5 31 | 32 | 36 | 6 37 | 38 | 39 | 40 | 41 | )); 42 | -------------------------------------------------------------------------------- /src/column/ColumnOrder.ts: -------------------------------------------------------------------------------- 1 | import Columns from "./Columns"; 2 | 3 | type ColumnOrder = "first" | "last" | Columns; 4 | 5 | export default ColumnOrder; 6 | -------------------------------------------------------------------------------- /src/column/ColumnProperties.ts: -------------------------------------------------------------------------------- 1 | import {BreakpointValue} from "../media"; 2 | import {ThemeProperties} from "../theme"; 3 | import ColumnAlignSelf from "./ColumnAlignSelf"; 4 | import ColumnFlex from "./ColumnFlex"; 5 | import ColumnOffset from "./ColumnOffset"; 6 | import ColumnOrder from "./ColumnOrder"; 7 | import ColumnSize from "./ColumnSize"; 8 | 9 | interface ColumnProperties extends ThemeProperties { 10 | alignSelf?: BreakpointValue; 11 | flex?: BreakpointValue; 12 | offset?: BreakpointValue; 13 | order?: BreakpointValue; 14 | size?: BreakpointValue; 15 | } 16 | 17 | export default ColumnProperties; 18 | -------------------------------------------------------------------------------- /src/column/ColumnSize.ts: -------------------------------------------------------------------------------- 1 | import Columns from "./Columns"; 2 | 3 | type ColumnSize = "auto" | "none" | Columns; 4 | 5 | export default ColumnSize; 6 | -------------------------------------------------------------------------------- /src/column/Columns.ts: -------------------------------------------------------------------------------- 1 | type Columns = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; 2 | 3 | export default Columns; 4 | -------------------------------------------------------------------------------- /src/column/__snapshots__/Column.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` should match the snapshot (alignSelf: 'strech') 1`] = ` 4 | .c0 { 5 | position: relative; 6 | width: 100%; 7 | min-height: 1px; 8 | -webkit-align-self: stretch !important; 9 | -ms-flex-item-align: stretch !important; 10 | align-self: stretch !important; 11 | } 12 | 13 |
16 | `; 17 | 18 | exports[ 19 | ` should match the snapshot (alignSelf: 'stretch', offset: 3, order: 3, size: 9) 1` 20 | ] = ` 21 | .c0 { 22 | position: relative; 23 | width: 100%; 24 | min-height: 1px; 25 | -webkit-align-self: stretch !important; 26 | -ms-flex-item-align: stretch !important; 27 | align-self: stretch !important; 28 | margin-left: 25.000000%; 29 | -webkit-order: 2; 30 | -ms-flex-order: 2; 31 | order: 2; 32 | -webkit-flex: 0 0 75.000000%; 33 | -ms-flex: 0 0 75.000000%; 34 | flex: 0 0 75.000000%; 35 | max-width: 75.000000%; 36 | } 37 | 38 |
44 | `; 45 | 46 | exports[ 47 | ` should match the snapshot (alignSelf: { xs: 'baseline', sm: 'center', md: 'flex-end' }) 1` 48 | ] = ` 49 | .c0 { 50 | position: relative; 51 | width: 100%; 52 | min-height: 1px; 53 | -webkit-align-self: baseline !important; 54 | -ms-flex-item-align: baseline !important; 55 | align-self: baseline !important; 56 | } 57 | 58 | @media (min-width:576px) { 59 | .c0 { 60 | -webkit-align-self: center !important; 61 | -ms-flex-item-align: center !important; 62 | align-self: center !important; 63 | } 64 | } 65 | 66 | @media (min-width:768px) { 67 | .c0 { 68 | -webkit-align-self: flex-end !important; 69 | -ms-flex-item-align: end !important; 70 | align-self: flex-end !important; 71 | } 72 | } 73 | 74 |
77 | `; 78 | 79 | exports[` should match the snapshot (no properties set) 1`] = ` 80 | .c0 { 81 | position: relative; 82 | width: 100%; 83 | min-height: 1px; 84 | } 85 | 86 |
89 | `; 90 | 91 | exports[ 92 | ` should match the snapshot (offset: { xs: 3, sm: 4, md: 8 }) 1` 93 | ] = ` 94 | .c0 { 95 | position: relative; 96 | width: 100%; 97 | min-height: 1px; 98 | margin-left: 25.000000%; 99 | } 100 | 101 | @media (min-width:576px) { 102 | .c0 { 103 | margin-left: 33.333333%; 104 | } 105 | } 106 | 107 | @media (min-width:768px) { 108 | .c0 { 109 | margin-left: 66.666667%; 110 | } 111 | } 112 | 113 |
123 | `; 124 | 125 | exports[` should match the snapshot (offset: 3) 1`] = ` 126 | .c0 { 127 | position: relative; 128 | width: 100%; 129 | min-height: 1px; 130 | margin-left: 25.000000%; 131 | } 132 | 133 |
137 | `; 138 | 139 | exports[ 140 | ` should match the snapshot (order: { xs: 'first', sm: 3, md: 'last' }) 1` 141 | ] = ` 142 | .c0 { 143 | position: relative; 144 | width: 100%; 145 | min-height: 1px; 146 | -webkit-order: -1; 147 | -ms-flex-order: -1; 148 | order: -1; 149 | } 150 | 151 | @media (min-width:576px) { 152 | .c0 { 153 | -webkit-order: 2; 154 | -ms-flex-order: 2; 155 | order: 2; 156 | } 157 | } 158 | 159 | @media (min-width:768px) { 160 | .c0 { 161 | -webkit-order: 12; 162 | -ms-flex-order: 12; 163 | order: 12; 164 | } 165 | } 166 | 167 |
177 | `; 178 | 179 | exports[` should match the snapshot (order: 5) 1`] = ` 180 | .c0 { 181 | position: relative; 182 | width: 100%; 183 | min-height: 1px; 184 | -webkit-order: 4; 185 | -ms-flex-order: 4; 186 | order: 4; 187 | } 188 | 189 |
193 | `; 194 | 195 | exports[ 196 | ` should match the snapshot (size: { xs: 'auto', sm: 7, md: 'none' }) 1` 197 | ] = ` 198 | .c0 { 199 | position: relative; 200 | width: 100%; 201 | min-height: 1px; 202 | -webkit-flex-basis: 0; 203 | -ms-flex-preferred-size: 0; 204 | flex-basis: 0; 205 | -webkit-box-flex: 1; 206 | -webkit-flex-grow: 1; 207 | -ms-flex-positive: 1; 208 | flex-grow: 1; 209 | max-width: 100%; 210 | } 211 | 212 | @media (min-width:576px) { 213 | .c0 { 214 | -webkit-flex: 0 0 58.333333%; 215 | -ms-flex: 0 0 58.333333%; 216 | flex: 0 0 58.333333%; 217 | max-width: 58.333333%; 218 | } 219 | } 220 | 221 | @media (min-width:768px) { 222 | .c0 { 223 | -webkit-flex: 0 0 auto; 224 | -ms-flex: 0 0 auto; 225 | flex: 0 0 auto; 226 | width: auto; 227 | max-width: none; 228 | } 229 | } 230 | 231 |
241 | `; 242 | 243 | exports[` should match the snapshot (size: 7) 1`] = ` 244 | .c0 { 245 | position: relative; 246 | width: 100%; 247 | min-height: 1px; 248 | -webkit-flex: 0 0 58.333333%; 249 | -ms-flex: 0 0 58.333333%; 250 | flex: 0 0 58.333333%; 251 | max-width: 58.333333%; 252 | } 253 | 254 |
258 | `; 259 | -------------------------------------------------------------------------------- /src/column/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`index should verify the API shape 1`] = ` 4 | Object { 5 | "default": [Function], 6 | } 7 | `; 8 | -------------------------------------------------------------------------------- /src/column/index.test.ts: -------------------------------------------------------------------------------- 1 | import * as module from "."; 2 | 3 | describe("index", () => { 4 | it("should verify the API shape", () => { 5 | expect(module).toMatchSnapshot(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/column/index.ts: -------------------------------------------------------------------------------- 1 | import Column from "./Column"; 2 | 3 | export default Column; 4 | -------------------------------------------------------------------------------- /src/column/renderAlignSelf.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import renderAlignSelf from "./renderAlignSelf"; 3 | 4 | describe("renderAlignSelf", () => { 5 | it("should render align-self appropriate (input: 'baseline')", () => { 6 | // arrange 7 | const input = "baseline"; 8 | 9 | // act 10 | const output = renderAlignSelf(input); 11 | 12 | // assert 13 | expect(flatten(output)).toBe("align-self:baseline!important;"); 14 | }); 15 | 16 | it("should render align-self appropriate (input: 'center')", () => { 17 | // arrange 18 | const input = "center"; 19 | 20 | // act 21 | const output = renderAlignSelf(input); 22 | 23 | // assert 24 | expect(flatten(output)).toBe("align-self:center!important;"); 25 | }); 26 | 27 | it("should render align-self appropriate (input: 'flex-end')", () => { 28 | // arrange 29 | const input = "flex-end"; 30 | 31 | // act 32 | const output = renderAlignSelf(input); 33 | 34 | // assert 35 | expect(flatten(output)).toBe("align-self:flex-end!important;"); 36 | }); 37 | 38 | it("should render align-self appropriate (input: 'flex-start')", () => { 39 | // arrange 40 | const input = "flex-start"; 41 | 42 | // act 43 | const output = renderAlignSelf(input); 44 | 45 | // assert 46 | expect(flatten(output)).toBe("align-self:flex-start!important;"); 47 | }); 48 | 49 | it("should render align-self appropriate (input: 'stretch')", () => { 50 | // arrange 51 | const input = "stretch"; 52 | 53 | // act 54 | const output = renderAlignSelf(input); 55 | 56 | // assert 57 | expect(flatten(output)).toBe("align-self:stretch!important;"); 58 | }); 59 | 60 | it("should render allign-items appropriate (input: undefined)", () => { 61 | // arrange 62 | const input = undefined; 63 | 64 | // act 65 | const output = renderAlignSelf(input); 66 | 67 | // assert 68 | expect(flatten(output)).toBe(""); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/column/renderAlignSelf.ts: -------------------------------------------------------------------------------- 1 | import {css} from "../utils"; 2 | import ColumnAlignSelf from "./ColumnAlignSelf"; 3 | 4 | export default (alignSelf?: ColumnAlignSelf): string => { 5 | const allowedValues = [ 6 | "baseline", 7 | "center", 8 | "flex-end", 9 | "flex-start", 10 | "stretch", 11 | ]; 12 | 13 | if (allowedValues.includes(alignSelf!)) { 14 | return css` 15 | align-self: ${alignSelf!} !important; 16 | `; 17 | } else { 18 | return ""; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/column/renderFlex.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import renderFlex from "./renderFlex"; 3 | 4 | describe("renderFlex", () => { 5 | it("should render flex appropriate (input: 'grow')", () => { 6 | // arrange 7 | const input = "grow"; 8 | 9 | // act 10 | const output = renderFlex(input); 11 | 12 | // assert 13 | expect(flatten(output)).toBe("flex-grow:1!important;"); 14 | }); 15 | 16 | it("should render flex appropriate (input: 'none')", () => { 17 | // arrange 18 | const input = "none"; 19 | 20 | // act 21 | const output = renderFlex(input); 22 | 23 | // assert 24 | expect(flatten(output)).toBe(""); 25 | }); 26 | 27 | it("should render flex appropriate (input: 'shrink')", () => { 28 | // arrange 29 | const input = "shrink"; 30 | 31 | // act 32 | const output = renderFlex(input); 33 | 34 | // assert 35 | expect(flatten(output)).toBe("flex-shrink:0!important;"); 36 | }); 37 | 38 | it("should render flex appropriate (input: undefined)", () => { 39 | // arrange 40 | const input = undefined; 41 | 42 | // act 43 | const output = renderFlex(input); 44 | 45 | // assert 46 | expect(flatten(output)).toBe(""); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /src/column/renderFlex.ts: -------------------------------------------------------------------------------- 1 | import {css} from "../utils"; 2 | import ColumnFlex from "./ColumnFlex"; 3 | 4 | export default (flex?: ColumnFlex): string => { 5 | switch (flex) { 6 | case "grow": 7 | return css` 8 | flex-grow: 1 !important; 9 | `; 10 | 11 | case "shrink": 12 | return css` 13 | flex-shrink: 0 !important; 14 | `; 15 | 16 | default: 17 | return ""; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/column/renderOffset.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import renderOffset from "./renderOffset"; 3 | 4 | describe("renderOffset", () => { 5 | it("should return css for offset (input: 1)", () => { 6 | // arrange 7 | const input = 1; 8 | 9 | // act 10 | const output = renderOffset(input); 11 | 12 | // assert 13 | expect(flatten(output)).toBe("margin-left:8.333333%;"); 14 | }); 15 | 16 | it("should return empty string (input: undefined)", () => { 17 | // arrange 18 | const input = undefined; 19 | 20 | // act 21 | const output = renderOffset(input); 22 | 23 | // assert 24 | expect(flatten(output)).toBe(""); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/column/renderOffset.ts: -------------------------------------------------------------------------------- 1 | import {percentage} from "../utils"; 2 | import ColumnOffset from "./ColumnOffset"; 3 | 4 | export default (offset?: ColumnOffset): string => { 5 | // the next expression is for JS projects 6 | // tslint:disable:strict-type-predicates 7 | if ( 8 | offset != null && 9 | typeof offset === "number" && 10 | offset > 0 && 11 | offset < 12 12 | ) { 13 | return `margin-left: ${percentage(offset)}%;`; 14 | } 15 | // tslint:enable:strict-type-predicates 16 | 17 | return ""; 18 | }; 19 | -------------------------------------------------------------------------------- /src/column/renderOrder.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import renderOrder from "./renderOrder"; 3 | 4 | describe("renderOrder", () => { 5 | it("should render css for flex order (input: 'first')", () => { 6 | // arrange 7 | const input = "first"; 8 | 9 | // act 10 | const output = renderOrder(input); 11 | 12 | // assert 13 | expect(flatten(output)).toBe("order:-1;"); 14 | }); 15 | 16 | it("should render css for flex order (input: 0)", () => { 17 | // arrange 18 | const input = 0; 19 | 20 | // act 21 | const output = renderOrder(input); 22 | 23 | // assert 24 | expect(flatten(output)).toBe("order:-1;"); 25 | }); 26 | 27 | it("should render css for flex order (input: 5)", () => { 28 | // arrange 29 | const input = 5; 30 | 31 | // act 32 | const output = renderOrder(input); 33 | 34 | // assert 35 | expect(flatten(output)).toBe("order:4;"); 36 | }); 37 | 38 | it("should render css for flex order (input: 'last')", () => { 39 | // arrange 40 | const input = "last"; 41 | 42 | // act 43 | const output = renderOrder(input); 44 | 45 | // assert 46 | expect(flatten(output)).toBe("order:12;"); 47 | }); 48 | 49 | it("should render css for flex order (input: 13)", () => { 50 | // arrange 51 | const input = 13; 52 | 53 | // act 54 | const output = renderOrder(input); 55 | 56 | // assert 57 | expect(flatten(output)).toBe("order:12;"); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /src/column/renderOrder.ts: -------------------------------------------------------------------------------- 1 | import ColumnOrder from "./ColumnOrder"; 2 | 3 | const renderOrder = (order?: ColumnOrder | 0 | 13): string => { 4 | // the next expression is for JS projects 5 | // tslint:disable:strict-type-predicates 6 | if (order !== undefined && typeof order !== "object") { 7 | if (order === "first") { 8 | return renderOrder(0); 9 | } 10 | 11 | if (order === "last") { 12 | return renderOrder(13); 13 | } 14 | 15 | // the next expression is for JS projects 16 | if (typeof order === "number") { 17 | return `order: ${order - 1};`; 18 | } 19 | } 20 | // tslint:enable:strict-type-predicates 21 | 22 | return ""; 23 | }; 24 | 25 | export default renderOrder; 26 | -------------------------------------------------------------------------------- /src/column/renderSize.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import renderSize from "./renderSize"; 3 | 4 | describe("renderSize", () => { 5 | it("should render css for flex size (input: 'auto')", () => { 6 | // arrange 7 | const input = "auto"; 8 | 9 | // act 10 | const output = renderSize(input); 11 | 12 | // assert 13 | expect(flatten(output)).toBe("flex-basis:0;flex-grow:1;max-width:100%;"); 14 | }); 15 | 16 | it("should render css for flex size (input: 'none')", () => { 17 | // arrange 18 | const input = "none"; 19 | 20 | // act 21 | const output = renderSize(input); 22 | 23 | // assert 24 | expect(flatten(output)).toBe("flex:00auto;width:auto;max-width:none;"); 25 | }); 26 | 27 | it("should render css for flex size (input: undefined)", () => { 28 | // arrange 29 | const input = undefined; 30 | 31 | // act 32 | const output = renderSize(input); 33 | 34 | // assert 35 | expect(flatten(output)).toBe(""); 36 | }); 37 | 38 | it("should render css for flex size (input: 0)", () => { 39 | // arrange 40 | const input = 0; 41 | 42 | // act 43 | const output = renderSize(input as any); 44 | 45 | // assert 46 | expect(flatten(output)).toBe(""); 47 | }); 48 | 49 | it("should render css for flex size (input: -1)", () => { 50 | // arrange 51 | const input = -1; 52 | 53 | // act 54 | const output = renderSize(input as any); 55 | 56 | // assert 57 | expect(flatten(output)).toBe(""); 58 | }); 59 | 60 | it("should render css for flex size (input: 6)", () => { 61 | // arrange 62 | const input = 6; 63 | 64 | // act 65 | const output = renderSize(input); 66 | 67 | // assert 68 | expect(flatten(output)).toBe("flex:0050.000000%;max-width:50.000000%;"); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/column/renderSize.ts: -------------------------------------------------------------------------------- 1 | import {percentage} from "../utils"; 2 | import ColumnSize from "./ColumnSize"; 3 | 4 | export default (size?: ColumnSize): string => { 5 | // the next expression is for JS projects 6 | // tslint:disable-next-line:strict-type-predicates 7 | if (size === undefined || typeof size === "object" || size < 1 || size > 12) { 8 | return ""; 9 | } 10 | 11 | if (size === "none") { 12 | return ` 13 | flex: 0 0 auto; 14 | width: auto; 15 | max-width: none; 16 | `; 17 | } 18 | 19 | if (size === "auto") { 20 | return ` 21 | flex-basis: 0; 22 | flex-grow: 1; 23 | max-width: 100%; 24 | `; 25 | } 26 | 27 | const calculatedPercentage = percentage(size); 28 | 29 | return ` 30 | flex: 0 0 ${calculatedPercentage}%; 31 | max-width: ${calculatedPercentage}%; 32 | `; 33 | }; 34 | -------------------------------------------------------------------------------- /src/container/Container.story.tsx: -------------------------------------------------------------------------------- 1 | import {action} from "@storybook/addon-actions"; 2 | import {storiesOf} from "@storybook/react"; 3 | import React from "react"; 4 | import Story from "../__utils__/Story"; 5 | import Title from "../__utils__/Title"; 6 | 7 | import {Column, Container, Row} from ".."; 8 | import {ThemeProvider} from "../theme/StyledComponents"; 9 | 10 | const breakpoints = { 11 | phone: 0, 12 | tablet: 600, 13 | desktop: 800, 14 | }; 15 | 16 | const containerWidth = { 17 | // do not specify phone here 18 | tablet: 560, 19 | desktop: 760, 20 | }; 21 | 22 | const gutterWidth = 100; 23 | 24 | storiesOf("Container", module) 25 | .add("fluid", () => ( 26 | 27 | fluid 28 | 29 | 30 | 1 31 | 32 | 33 | 34 | )) 35 | .add("fullscreen", () => ( 36 | 37 | 38 | 39 | fullscreen 40 | 41 | 42 | 43 | )) 44 | .add("themeProvider", () => ( 45 | 46 | themeProvider 47 | 48 | 49 | 50 | 1 51 | 2 52 | 3 53 | 4 54 | 55 | 56 | 57 | 58 | )) 59 | .add("gutterWidth100", () => ( 60 | 61 | themeProvider 62 | 63 | 64 | 65 | 1 66 | 2 67 | 3 68 | 4 69 | 70 | 71 | 72 | 73 | )); 74 | -------------------------------------------------------------------------------- /src/container/Container.test.tsx: -------------------------------------------------------------------------------- 1 | import {shallow} from "enzyme"; 2 | import "jest-styled-components"; 3 | import React from "react"; 4 | import Container from "./Container"; 5 | 6 | describe("", () => { 7 | it("should match the snapshot (no properties set)", () => { 8 | // act 9 | const result = shallow(); 10 | 11 | // assert 12 | expect(result).toMatchSnapshot(); 13 | }); 14 | 15 | it("should match the snapshot (fluid property set to true)", () => { 16 | // act 17 | const result = shallow(); 18 | 19 | // assert 20 | expect(result).toMatchSnapshot(); 21 | }); 22 | 23 | it("should match the snapshot (fullscreen property set to true)", () => { 24 | // act 25 | const result = shallow(); 26 | 27 | // assert 28 | expect(result).toMatchSnapshot(); 29 | }); 30 | 31 | it("should match the snapshot (width property set to { xs: 100, sm: 200, md: 300 })", () => { 32 | // act 33 | const result = shallow(); 34 | 35 | // assert 36 | expect(result).toMatchSnapshot(); 37 | }); 38 | 39 | it("should match the snapshot (ignores width when fluid is set)", () => { 40 | // act 41 | const result = shallow( 42 | , 43 | ); 44 | 45 | // assert 46 | expect(result).toMatchSnapshot(); 47 | }); 48 | 49 | it("should match the snapshot (ignores fluid and width when fullscreen is set)", () => { 50 | // act 51 | const result = shallow( 52 | , 53 | ); 54 | 55 | // assert 56 | expect(result).toMatchSnapshot(); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /src/container/Container.tsx: -------------------------------------------------------------------------------- 1 | import {ClassAttributes, HTMLAttributes} from "react"; 2 | import {StyledComponentClass} from "styled-components"; 3 | import {PropertyValue} from "../media"; 4 | import Row from "../row"; 5 | import {gutterWidth, styled, Theme} from "../theme"; 6 | import {css, render} from "../utils"; 7 | import "../utils/bootstrap"; 8 | import ContainerProperties from "./ContainerProperties"; 9 | import fullscreen from "./fullscreen"; 10 | import renderWidth from "./renderWidth"; 11 | import getWidth from "./width"; 12 | 13 | const Container = styled.div` 14 | width: 100%; 15 | margin-right: auto; 16 | margin-left: auto; 17 | 18 | ${(props: ContainerProperties) => { 19 | const width = gutterWidth(props.theme); 20 | 21 | return ` 22 | padding-right: ${width}px; 23 | padding-left: ${width}px; 24 | `; 25 | }} ${(props: ContainerProperties) => { 26 | if (props.fullscreen) { 27 | fullscreen(); 28 | 29 | return css` 30 | height: 100%; 31 | 32 | > ${Row} { 33 | height: 100%; 34 | } 35 | `; 36 | } else if (props.fluid) { 37 | return ""; 38 | } else { 39 | const renderer = { 40 | width: (value?: PropertyValue) => renderWidth(value as number), 41 | }; 42 | const valueMap = { 43 | width: getWidth(props), 44 | }; 45 | 46 | return render(valueMap, renderer, props.theme); 47 | } 48 | }}; 49 | `; 50 | 51 | export default Container; 52 | -------------------------------------------------------------------------------- /src/container/ContainerProperties.ts: -------------------------------------------------------------------------------- 1 | import {BreakpointValues} from "../media"; 2 | import {ThemeProperties} from "../theme"; 3 | 4 | interface ContainerProperties extends ThemeProperties { 5 | fluid?: boolean; 6 | fullscreen?: boolean; 7 | width?: BreakpointValues; 8 | } 9 | 10 | export default ContainerProperties; 11 | -------------------------------------------------------------------------------- /src/container/__snapshots__/Container.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[ 4 | ` should match the snapshot (fluid property set to true) 1` 5 | ] = ` 6 | .c0 { 7 | width: 100%; 8 | margin-right: auto; 9 | margin-left: auto; 10 | padding-right: 15px; 11 | padding-left: 15px; 12 | } 13 | 14 |
17 | `; 18 | 19 | exports[ 20 | ` should match the snapshot (fullscreen property set to true) 1` 21 | ] = ` 22 | .c0 { 23 | width: 100%; 24 | margin-right: auto; 25 | margin-left: auto; 26 | padding-right: 15px; 27 | padding-left: 15px; 28 | height: 100%; 29 | } 30 | 31 | .c0 > .sc-bwzfXH { 32 | height: 100%; 33 | } 34 | 35 |
38 | `; 39 | 40 | exports[ 41 | ` should match the snapshot (ignores fluid and width when fullscreen is set) 1` 42 | ] = ` 43 | .c0 { 44 | width: 100%; 45 | margin-right: auto; 46 | margin-left: auto; 47 | padding-right: 15px; 48 | padding-left: 15px; 49 | height: 100%; 50 | } 51 | 52 | .c0 > .sc-bwzfXH { 53 | height: 100%; 54 | } 55 | 56 |
66 | `; 67 | 68 | exports[ 69 | ` should match the snapshot (ignores width when fluid is set) 1` 70 | ] = ` 71 | .c0 { 72 | width: 100%; 73 | margin-right: auto; 74 | margin-left: auto; 75 | padding-right: 15px; 76 | padding-left: 15px; 77 | } 78 | 79 |
89 | `; 90 | 91 | exports[` should match the snapshot (no properties set) 1`] = ` 92 | .c0 { 93 | width: 100%; 94 | margin-right: auto; 95 | margin-left: auto; 96 | padding-right: 15px; 97 | padding-left: 15px; 98 | } 99 | 100 | @media (min-width:576px) { 101 | .c0 { 102 | max-width: 540px; 103 | } 104 | } 105 | 106 | @media (min-width:768px) { 107 | .c0 { 108 | max-width: 720px; 109 | } 110 | } 111 | 112 | @media (min-width:992px) { 113 | .c0 { 114 | max-width: 960px; 115 | } 116 | } 117 | 118 | @media (min-width:1200px) { 119 | .c0 { 120 | max-width: 1140px; 121 | } 122 | } 123 | 124 |
127 | `; 128 | 129 | exports[ 130 | ` should match the snapshot (width property set to { xs: 100, sm: 200, md: 300 }) 1` 131 | ] = ` 132 | .c0 { 133 | width: 100%; 134 | margin-right: auto; 135 | margin-left: auto; 136 | padding-right: 15px; 137 | padding-left: 15px; 138 | max-width: 100px; 139 | } 140 | 141 | @media (min-width:576px) { 142 | .c0 { 143 | max-width: 200px; 144 | } 145 | } 146 | 147 | @media (min-width:768px) { 148 | .c0 { 149 | max-width: 300px; 150 | } 151 | } 152 | 153 |
163 | `; 164 | -------------------------------------------------------------------------------- /src/container/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`index should verify the API shape 1`] = ` 4 | Object { 5 | "default": [Function], 6 | } 7 | `; 8 | -------------------------------------------------------------------------------- /src/container/defaultWidth.ts: -------------------------------------------------------------------------------- 1 | import {BreakpointValues} from "../media"; 2 | 3 | const defaultWidth: BreakpointValues = { 4 | sm: 540, 5 | md: 720, 6 | lg: 960, 7 | xl: 1140, 8 | }; 9 | 10 | export default defaultWidth; 11 | -------------------------------------------------------------------------------- /src/container/fullscreen.test.ts: -------------------------------------------------------------------------------- 1 | import fullscreen from "./fullscreen"; 2 | 3 | describe("fullscreen", () => { 4 | it("should not break", () => { 5 | // arrange 6 | jest.mock("../theme/StyledComponents"); 7 | 8 | // act 9 | fullscreen(); 10 | 11 | // assert 12 | // should not break 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/container/fullscreen.ts: -------------------------------------------------------------------------------- 1 | import {injectGlobal} from "../theme/StyledComponents"; 2 | 3 | let applied = false; 4 | 5 | export default () => { 6 | if (!applied) { 7 | // tslint:disable-next-line:no-unused-expression 8 | injectGlobal` 9 | html, 10 | body, 11 | body > div { 12 | height: 100% !important; 13 | margin: 0 !important; 14 | padding: 0 !important; 15 | } 16 | `; 17 | applied = true; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/container/index.test.ts: -------------------------------------------------------------------------------- 1 | import * as module from "."; 2 | 3 | describe("index", () => { 4 | it("should verify the API shape", () => { 5 | expect(module).toMatchSnapshot(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/container/index.ts: -------------------------------------------------------------------------------- 1 | import Container from "./Container"; 2 | 3 | export default Container; 4 | -------------------------------------------------------------------------------- /src/container/renderWidth.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import renderWidth from "./renderWidth"; 3 | 4 | describe("renderWidth", () => { 5 | it("should render container width (input: 500)", () => { 6 | // arrange 7 | const input = 500; 8 | 9 | // act 10 | const output = renderWidth(input); 11 | 12 | // assert 13 | expect(flatten(output)).toBe("max-width:500px;"); 14 | }); 15 | 16 | it("should render container width (input: undefined)", () => { 17 | // arrange 18 | const input = undefined; 19 | 20 | // act 21 | const output = renderWidth(input); 22 | 23 | // assert 24 | expect(flatten(output)).toBe(""); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/container/renderWidth.ts: -------------------------------------------------------------------------------- 1 | export default (width?: number): string => { 2 | // the next expression is for JS projects 3 | // tslint:disable-next-line:strict-type-predicates 4 | if (width != null && typeof width === "number") { 5 | return `max-width: ${width}px;`; 6 | } 7 | 8 | return ""; 9 | }; 10 | -------------------------------------------------------------------------------- /src/container/width.test.ts: -------------------------------------------------------------------------------- 1 | import defaultWidth from "./defaultWidth"; 2 | import width from "./width"; 3 | 4 | describe("width", () => { 5 | it("should get container width from the props", () => { 6 | // arrange 7 | const input = { 8 | width: {xs: 50}, 9 | theme: { 10 | containerWidth: {md: 100}, 11 | }, 12 | }; 13 | 14 | // act 15 | const output = width(input); 16 | 17 | // assert 18 | expect({xs: 50}).toEqual(output); 19 | }); 20 | 21 | it("should get container width from the theme", () => { 22 | // arrange 23 | const input = { 24 | theme: { 25 | containerWidth: {md: 100}, 26 | }, 27 | }; 28 | 29 | // act 30 | const output = width(input); 31 | 32 | // assert 33 | expect({md: 100}).toEqual(output); 34 | }); 35 | 36 | it("should get default container width (input: {})", () => { 37 | // arrange 38 | const input = {}; 39 | 40 | // act 41 | const output = width(input); 42 | 43 | // assert 44 | expect(defaultWidth).toEqual(output); 45 | }); 46 | 47 | it("should get default container width (input: undefined)", () => { 48 | // arrange 49 | const input = undefined; 50 | 51 | // act 52 | const output = width(input); 53 | 54 | // assert 55 | expect(defaultWidth).toEqual(output); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /src/container/width.ts: -------------------------------------------------------------------------------- 1 | import {BreakpointValues} from "../media"; 2 | import ContainerProperties from "./ContainerProperties"; 3 | import defaultWidth from "./defaultWidth"; 4 | 5 | export default (props?: ContainerProperties): BreakpointValues => 6 | (props && (props.width || (props.theme && props.theme.containerWidth))) || 7 | defaultWidth; 8 | -------------------------------------------------------------------------------- /src/index.test.ts: -------------------------------------------------------------------------------- 1 | import * as module from "."; 2 | 3 | describe("index", () => { 4 | it("should verify the API shape", () => { 5 | expect(module).toMatchSnapshot(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export {default as Break} from "./break"; 2 | export {default as Column} from "./column"; 3 | export {default as Container} from "./container"; 4 | export {BreakpointMap, BreakpointValue, BreakpointValues} from "./media"; 5 | export {default as Row} from "./row"; 6 | export {ThemeProvider} from "./theme/StyledComponents"; 7 | export {default as Theme} from "./theme/Theme"; 8 | -------------------------------------------------------------------------------- /src/media/BreakpointMap.ts: -------------------------------------------------------------------------------- 1 | interface BreakpointMap { 2 | [key: string]: number; 3 | } 4 | 5 | export default BreakpointMap; 6 | -------------------------------------------------------------------------------- /src/media/BreakpointValue.ts: -------------------------------------------------------------------------------- 1 | import BreakpointValues from "./BreakpointValues"; 2 | import PropertyValue from "./PropertyValue"; 3 | 4 | type BreakpointValue = 5 | | TValue 6 | | BreakpointValues; 7 | 8 | export default BreakpointValue; 9 | -------------------------------------------------------------------------------- /src/media/BreakpointValues.ts: -------------------------------------------------------------------------------- 1 | import PropertyValue from "./PropertyValue"; 2 | 3 | interface BreakpointValues { 4 | [key: string]: TValue; 5 | } 6 | 7 | export default BreakpointValues; 8 | -------------------------------------------------------------------------------- /src/media/BreakpointValuesMap.ts: -------------------------------------------------------------------------------- 1 | import PropertyValues from "./PropertyValues"; 2 | 3 | interface BreakpointValuesMap { 4 | [key: string]: PropertyValues; 5 | } 6 | 7 | export default BreakpointValuesMap; 8 | -------------------------------------------------------------------------------- /src/media/PropertyValue.ts: -------------------------------------------------------------------------------- 1 | type PropertyValue = boolean | number | string | undefined; 2 | 3 | export default PropertyValue; 4 | -------------------------------------------------------------------------------- /src/media/PropertyValues.ts: -------------------------------------------------------------------------------- 1 | import PropertyValue from "./PropertyValue"; 2 | 3 | interface PropertyValues { 4 | [key: string]: PropertyValue; 5 | } 6 | 7 | export default PropertyValues; 8 | -------------------------------------------------------------------------------- /src/media/PropertyValuesMap.ts: -------------------------------------------------------------------------------- 1 | import BreakpointValue from "./BreakpointValue"; 2 | import PropertyValue from "./PropertyValue"; 3 | 4 | interface PropertyValuesMap { 5 | [key: string]: BreakpointValue; 6 | } 7 | 8 | export default PropertyValuesMap; 9 | -------------------------------------------------------------------------------- /src/media/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`index should verify the API shape 1`] = ` 4 | Object { 5 | "BreakpointMap": Object {}, 6 | "BreakpointValue": Object {}, 7 | "BreakpointValues": Object {}, 8 | "BreakpointValuesMap": Object {}, 9 | "PropertyValue": Object {}, 10 | "PropertyValues": Object {}, 11 | "PropertyValuesMap": Object {}, 12 | "breakpoints": [Function], 13 | "defaultBreakpointKey": [Function], 14 | "defaultBreakpoints": Object { 15 | "lg": 992, 16 | "md": 768, 17 | "sm": 576, 18 | "xl": 1200, 19 | "xs": 0, 20 | }, 21 | } 22 | `; 23 | -------------------------------------------------------------------------------- /src/media/breakpoints.test.ts: -------------------------------------------------------------------------------- 1 | import breakpoints from "./breakpoints"; 2 | import defaultBreakpoints from "./defaultBreakpoints"; 3 | 4 | describe("breakpoints", () => { 5 | it("should get breakpoints from the theme", () => { 6 | // arrange 7 | const input = { 8 | breakpoints: {phone: 30, tablet: 60}, 9 | }; 10 | 11 | // act 12 | const output = breakpoints(input); 13 | 14 | // assert 15 | expect({phone: 30, tablet: 60}).toEqual(output); 16 | }); 17 | 18 | it("should get default breakpoints (input: {})", () => { 19 | // arrange 20 | const input = {}; 21 | 22 | // act 23 | const output = breakpoints(input); 24 | 25 | // assert 26 | expect(defaultBreakpoints).toEqual(output); 27 | }); 28 | 29 | it("should get default breakpoints (input: undefined)", () => { 30 | // arrange 31 | const input = undefined; 32 | 33 | // act 34 | const output = breakpoints(input); 35 | 36 | // assert 37 | expect(defaultBreakpoints).toEqual(output); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /src/media/breakpoints.ts: -------------------------------------------------------------------------------- 1 | import {Theme} from "../theme"; 2 | import BreakpointMap from "./BreakpointMap"; 3 | import defaultBreakpoints from "./defaultBreakpoints"; 4 | 5 | export default (theme?: Theme) => 6 | (theme && theme.breakpoints) || defaultBreakpoints; 7 | -------------------------------------------------------------------------------- /src/media/defaultBreakpointKey.test.ts: -------------------------------------------------------------------------------- 1 | import {defaultBreakpointKey} from "."; 2 | 3 | describe("defaultBreakpointKey", () => { 4 | it("should return 'xs'", () => { 5 | // arrange 6 | const input = undefined; 7 | 8 | // act 9 | const output = defaultBreakpointKey(input); 10 | 11 | // assert 12 | expect(output).toBe("xs"); 13 | }); 14 | 15 | it("should return 'small'", () => { 16 | // arrange 17 | const input = { 18 | breakpoints: { 19 | small: 0, 20 | big: 500, 21 | }, 22 | }; 23 | 24 | // act 25 | const output = defaultBreakpointKey(input); 26 | 27 | // assert 28 | expect(output).toBe("small"); 29 | }); 30 | 31 | it("should return null", () => { 32 | // arrange 33 | const input = { 34 | breakpoints: { 35 | small: 200, 36 | big: 500, 37 | }, 38 | }; 39 | 40 | // act 41 | const output = defaultBreakpointKey(input); 42 | 43 | // assert 44 | expect(output).toBeNull(); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/media/defaultBreakpointKey.ts: -------------------------------------------------------------------------------- 1 | import {Theme} from "../theme"; 2 | import breakpoints from "./breakpoints"; 3 | 4 | export default (theme?: Theme): string | null => { 5 | const breakpointMap = breakpoints(theme); 6 | 7 | for (const key in breakpointMap) { 8 | if (breakpointMap[key] === 0) { 9 | return key; 10 | } 11 | } 12 | 13 | return null; 14 | }; 15 | -------------------------------------------------------------------------------- /src/media/defaultBreakpoints.ts: -------------------------------------------------------------------------------- 1 | import BreakpointMap from "./BreakpointMap"; 2 | 3 | const defaultBreakpoints: BreakpointMap = { 4 | xs: 0, 5 | sm: 576, // 540 6 | md: 768, // 720 7 | lg: 992, // 960 8 | xl: 1200, // 1140 9 | }; 10 | 11 | export default defaultBreakpoints; 12 | -------------------------------------------------------------------------------- /src/media/index.test.ts: -------------------------------------------------------------------------------- 1 | import * as module from "."; 2 | 3 | describe("index", () => { 4 | it("should verify the API shape", () => { 5 | expect(module).toMatchSnapshot(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/media/index.ts: -------------------------------------------------------------------------------- 1 | export {default as BreakpointMap} from "./BreakpointMap"; 2 | export {default as breakpoints} from "./breakpoints"; 3 | export {default as BreakpointValue} from "./BreakpointValue"; 4 | export {default as BreakpointValues} from "./BreakpointValues"; 5 | export {default as BreakpointValuesMap} from "./BreakpointValuesMap"; 6 | export {default as defaultBreakpointKey} from "./defaultBreakpointKey"; 7 | export {default as defaultBreakpoints} from "./defaultBreakpoints"; 8 | export {default as PropertyValue} from "./PropertyValue"; 9 | export {default as PropertyValues} from "./PropertyValues"; 10 | export {default as PropertyValuesMap} from "./PropertyValuesMap"; 11 | -------------------------------------------------------------------------------- /src/row/Row.story.tsx: -------------------------------------------------------------------------------- 1 | import {action} from "@storybook/addon-actions"; 2 | import {storiesOf} from "@storybook/react"; 3 | import React from "react"; 4 | import Story from "../__utils__/Story"; 5 | import Title from "../__utils__/Title"; 6 | 7 | import {Column, Container, Row} from ".."; 8 | import {ThemeProvider} from "../theme/StyledComponents"; 9 | 10 | const breakpoints = { 11 | phone: 0, 12 | tablet: 600, 13 | desktop: 800, 14 | }; 15 | 16 | const containerWidth = { 17 | // do not specify phone here 18 | tablet: 560, 19 | desktop: 760, 20 | }; 21 | 22 | storiesOf("Row", module).add("noGutter", () => ( 23 | 24 | noGutter 25 | 26 | 1 27 | 2 28 | 29 | 30 | )); 31 | -------------------------------------------------------------------------------- /src/row/Row.test.tsx: -------------------------------------------------------------------------------- 1 | import {shallow} from "enzyme"; 2 | import "jest-styled-components"; 3 | import React from "react"; 4 | import Row from "./Row"; 5 | 6 | describe("", () => { 7 | it("should match the snapshot (no properties set)", () => { 8 | // act 9 | const result = shallow(); 10 | 11 | // assert 12 | expect(result).toMatchSnapshot(); 13 | }); 14 | 15 | it("should match the snapshot (alignContent: 'space-between') ", () => { 16 | // act 17 | const result = shallow(); 18 | 19 | // assert 20 | expect(result).toMatchSnapshot(); 21 | }); 22 | 23 | it("should match the snapshot (alignContent: { xs: 'space-around', sm: 'flex-end', md: 'flex-start' })", () => { 24 | // act 25 | const result = shallow( 26 | , 33 | ); 34 | 35 | // assert 36 | expect(result).toMatchSnapshot(); 37 | }); 38 | 39 | it("should match the snapshot (alignItems: 'strech') ", () => { 40 | // act 41 | const result = shallow(); 42 | 43 | // assert 44 | expect(result).toMatchSnapshot(); 45 | }); 46 | 47 | it("should match the snapshot (alignItems: { xs: 'baseline', sm: 'center', md: 'flex-end' })", () => { 48 | // act 49 | const result = shallow( 50 | , 51 | ); 52 | 53 | // assert 54 | expect(result).toMatchSnapshot(); 55 | }); 56 | 57 | it("should match the snapshot (direction: 'row') ", () => { 58 | // act 59 | const result = shallow(); 60 | 61 | // assert 62 | expect(result).toMatchSnapshot(); 63 | }); 64 | 65 | it("should match the snapshot (direction: { xs: 'row', sm: 'row-reverse', md: 'column' })", () => { 66 | // act 67 | const result = shallow( 68 | , 69 | ); 70 | 71 | // assert 72 | expect(result).toMatchSnapshot(); 73 | }); 74 | 75 | it("should match the snapshot (justifyContent: 'space-between') ", () => { 76 | // act 77 | const result = shallow(); 78 | 79 | // assert 80 | expect(result).toMatchSnapshot(); 81 | }); 82 | 83 | it("should match the snapshot (justifyContent: { xs: 'space-around', sm: 'flex-end', md: 'flex-start' })", () => { 84 | // act 85 | const result = shallow( 86 | , 93 | ); 94 | 95 | // assert 96 | expect(result).toMatchSnapshot(); 97 | }); 98 | 99 | it("should match the snapshot (noGutter: true) ", () => { 100 | // act 101 | const result = shallow(); 102 | 103 | // assert 104 | expect(result).toMatchSnapshot(); 105 | }); 106 | 107 | it("should match the snapshot (noGutter: { xs: true, sm: false, md: true })", () => { 108 | // act 109 | const result = shallow(); 110 | 111 | // assert 112 | expect(result).toMatchSnapshot(); 113 | }); 114 | 115 | it("should match the snapshot (wrap: 'wrap-reverse') ", () => { 116 | // act 117 | const result = shallow(); 118 | 119 | // assert 120 | expect(result).toMatchSnapshot(); 121 | }); 122 | 123 | it("should match the snapshot (wrap: { xs: 'nowrap', sm: 'wrap-reverse', md: 'wrap' })", () => { 124 | // act 125 | const result = shallow( 126 | , 127 | ); 128 | 129 | // assert 130 | expect(result).toMatchSnapshot(); 131 | }); 132 | 133 | it("should match the snapshot (alignContent: 'flex-end', alignItems: 'baseline', direction: 'column', justifyContent: center, noGutter: true, wrap: 'nowrap')", () => { 134 | // act 135 | const result = shallow( 136 | , 144 | ); 145 | 146 | // assert 147 | expect(result).toMatchSnapshot(); 148 | }); 149 | }); 150 | -------------------------------------------------------------------------------- /src/row/Row.tsx: -------------------------------------------------------------------------------- 1 | import {ClassAttributes, HTMLAttributes} from "react"; 2 | import {StyledComponentClass} from "styled-components"; 3 | import {BreakpointValue, PropertyValue} from "../media"; 4 | import {styled, Theme} from "../theme"; 5 | import {render} from "../utils"; 6 | import "../utils/bootstrap"; 7 | import renderAlignContent from "./renderAlignContent"; 8 | import renderAlignItems from "./renderAlignItems"; 9 | import renderDirection from "./renderDirection"; 10 | import renderGutter from "./renderGutter"; 11 | import renderJustifyContent from "./renderJustifyContent"; 12 | import renderWrap from "./renderWrap"; 13 | import RowAlignContent from "./RowAlignContent"; 14 | import RowAlignItems from "./RowAlignItems"; 15 | import RowDirection from "./RowDirection"; 16 | import RowJustifyContent from "./RowJustifyContent"; 17 | import RowProperties from "./RowProperties"; 18 | import RowWrap from "./RowWrap"; 19 | 20 | const Row = styled.div` 21 | display: flex; 22 | 23 | ${(props: RowProperties) => { 24 | const renderer = { 25 | alignContent: (value?: PropertyValue) => 26 | renderAlignContent(value as RowAlignContent), 27 | alignItems: (value?: PropertyValue) => 28 | renderAlignItems(value as RowAlignItems), 29 | justifyContent: (value?: PropertyValue) => 30 | renderJustifyContent(value as RowJustifyContent), 31 | direction: (value?: PropertyValue) => 32 | renderDirection(value as RowDirection), 33 | gutter: (value?: PropertyValue) => 34 | renderGutter(value as boolean, props.theme), 35 | wrap: (value?: PropertyValue) => renderWrap(value as RowWrap), 36 | }; 37 | const valueMap = { 38 | alignContent: props.alignContent, 39 | alignItems: props.alignItems, 40 | justifyContent: props.justifyContent, 41 | direction: props.direction, 42 | gutter: props.noGutter, 43 | wrap: props.wrap, 44 | }; 45 | 46 | return render(valueMap, renderer, props.theme); 47 | }}; 48 | `; 49 | 50 | export default Row; 51 | -------------------------------------------------------------------------------- /src/row/RowAlignContent.story.tsx: -------------------------------------------------------------------------------- 1 | import {action} from "@storybook/addon-actions"; 2 | import {storiesOf} from "@storybook/react"; 3 | import React from "react"; 4 | import Story from "../__utils__/Story"; 5 | import Title from "../__utils__/Title"; 6 | 7 | import {Column, Container, Row} from ".."; 8 | 9 | storiesOf("RowAlignContent", module) 10 | .add("center", () => ( 11 | 12 | center 13 | 14 | 15 | 1 16 | 2 17 | 3 18 | 4 19 | 20 | 21 | 22 | )) 23 | .add("flex-end", () => ( 24 | 25 | flex-end 26 | 27 | 28 | 1 29 | 2 30 | 3 31 | 4 32 | 33 | 34 | 35 | )) 36 | .add("flex-start", () => ( 37 | 38 | flex-start 39 | 40 | 41 | 1 42 | 2 43 | 3 44 | 4 45 | 46 | 47 | 48 | )) 49 | .add("space-around", () => ( 50 | 51 | space-around 52 | 53 | 54 | 1 55 | 2 56 | 3 57 | 4 58 | 59 | 60 | 61 | )) 62 | .add("space-between", () => ( 63 | 64 | space-between 65 | 66 | 67 | 1 68 | 2 69 | 3 70 | 4 71 | 72 | 73 | 74 | )) 75 | .add("stretch", () => ( 76 | 77 | stretch 78 | 79 | 80 | 1 81 | 2 82 | 3 83 | 4 84 | 85 | 86 | 87 | )); 88 | -------------------------------------------------------------------------------- /src/row/RowAlignContent.ts: -------------------------------------------------------------------------------- 1 | type RowAlignContent = 2 | | "center" 3 | | "flex-end" 4 | | "flex-start" 5 | | "space-around" 6 | | "space-between" 7 | | "stretch"; 8 | 9 | export default RowAlignContent; 10 | -------------------------------------------------------------------------------- /src/row/RowAlignItems.story.tsx: -------------------------------------------------------------------------------- 1 | import {action} from "@storybook/addon-actions"; 2 | import {storiesOf} from "@storybook/react"; 3 | import React from "react"; 4 | import Story from "../__utils__/Story"; 5 | import Title from "../__utils__/Title"; 6 | 7 | import {Column, Container, Row} from ".."; 8 | 9 | storiesOf("RowAlignItems", module) 10 | .add("baseline", () => ( 11 | 12 | baseline 13 | 14 | 15 | 1 16 | 2 17 | 3 18 | 4 19 | 20 | 21 | 22 | )) 23 | .add("center", () => ( 24 | 25 | center 26 | 27 | 28 | 1 29 | 2 30 | 3 31 | 4 32 | 33 | 34 | 35 | )) 36 | .add("flex-end", () => ( 37 | 38 | flex-end 39 | 40 | 41 | 1 42 | 2 43 | 3 44 | 4 45 | 46 | 47 | 48 | )) 49 | .add("flex-start", () => ( 50 | 51 | flex-start 52 | 53 | 54 | 1 55 | 2 56 | 3 57 | 4 58 | 59 | 60 | 61 | )) 62 | .add("stretch", () => ( 63 | 64 | stretch 65 | 66 | 67 | 1 68 | 2 69 | 3 70 | 4 71 | 72 | 73 | 74 | )); 75 | -------------------------------------------------------------------------------- /src/row/RowAlignItems.ts: -------------------------------------------------------------------------------- 1 | type RowAlignItems = 2 | | "baseline" 3 | | "center" 4 | | "flex-end" 5 | | "flex-start" 6 | | "stretch"; 7 | 8 | export default RowAlignItems; 9 | -------------------------------------------------------------------------------- /src/row/RowDirection.story.tsx: -------------------------------------------------------------------------------- 1 | import {action} from "@storybook/addon-actions"; 2 | import {storiesOf} from "@storybook/react"; 3 | import React from "react"; 4 | import Story from "../__utils__/Story"; 5 | import Title from "../__utils__/Title"; 6 | 7 | import {Column, Container, Row} from ".."; 8 | 9 | storiesOf("RowDirection", module) 10 | .add("column", () => ( 11 | 12 | column 13 | 14 | 15 | 1 16 | 2 17 | 3 18 | 4 19 | 20 | 21 | 22 | )) 23 | .add("column-reverse", () => ( 24 | 25 | column-reverse 26 | 27 | 28 | 1 29 | 2 30 | 3 31 | 4 32 | 33 | 34 | 35 | )) 36 | .add("row", () => ( 37 | 38 | row 39 | 40 | 41 | 1 42 | 2 43 | 3 44 | 4 45 | 46 | 47 | 48 | )) 49 | .add("row-reverse", () => ( 50 | 51 | row-reverse 52 | 53 | 54 | 1 55 | 2 56 | 3 57 | 4 58 | 59 | 60 | 61 | )); 62 | -------------------------------------------------------------------------------- /src/row/RowDirection.ts: -------------------------------------------------------------------------------- 1 | type RowDirection = "column" | "column-reverse" | "row" | "row-reverse"; 2 | 3 | export default RowDirection; 4 | -------------------------------------------------------------------------------- /src/row/RowJustifyContent.story.tsx: -------------------------------------------------------------------------------- 1 | import {action} from "@storybook/addon-actions"; 2 | import {storiesOf} from "@storybook/react"; 3 | import React from "react"; 4 | import Story from "../__utils__/Story"; 5 | import Title from "../__utils__/Title"; 6 | 7 | import {Column, Container, Row} from ".."; 8 | 9 | storiesOf("RowJustifyContent", module) 10 | .add("center", () => ( 11 | 12 | center 13 | 14 | 15 | 1 16 | 2 17 | 3 18 | 4 19 | 20 | 21 | 22 | )) 23 | .add("flex-end", () => ( 24 | 25 | flex-end 26 | 27 | 28 | 1 29 | 2 30 | 3 31 | 4 32 | 33 | 34 | 35 | )) 36 | .add("flex-start", () => ( 37 | 38 | flex-start 39 | 40 | 41 | 1 42 | 2 43 | 3 44 | 4 45 | 46 | 47 | 48 | )) 49 | .add("space-around", () => ( 50 | 51 | space-around 52 | 53 | 54 | 1 55 | 2 56 | 3 57 | 4 58 | 59 | 60 | 61 | )) 62 | .add("space-between", () => ( 63 | 64 | space-between 65 | 66 | 67 | 1 68 | 2 69 | 3 70 | 4 71 | 72 | 73 | 74 | )); 75 | -------------------------------------------------------------------------------- /src/row/RowJustifyContent.ts: -------------------------------------------------------------------------------- 1 | type RowJustifyContent = 2 | | "center" 3 | | "flex-end" 4 | | "flex-start" 5 | | "space-around" 6 | | "space-between"; 7 | 8 | export default RowJustifyContent; 9 | -------------------------------------------------------------------------------- /src/row/RowProperties.ts: -------------------------------------------------------------------------------- 1 | import {BreakpointValue} from "../media"; 2 | import {ThemeProperties} from "../theme"; 3 | import RowAlignContent from "./RowAlignContent"; 4 | import RowAlignItems from "./RowAlignItems"; 5 | import RowDirection from "./RowDirection"; 6 | import RowJustifyContent from "./RowJustifyContent"; 7 | import RowWrap from "./RowWrap"; 8 | 9 | interface RowProperties extends ThemeProperties { 10 | alignContent?: BreakpointValue; 11 | alignItems?: BreakpointValue; 12 | direction?: BreakpointValue; 13 | justifyContent?: BreakpointValue; 14 | noGutter?: BreakpointValue; 15 | wrap?: BreakpointValue; 16 | } 17 | 18 | export default RowProperties; 19 | -------------------------------------------------------------------------------- /src/row/RowWrap.story.tsx: -------------------------------------------------------------------------------- 1 | import {action} from "@storybook/addon-actions"; 2 | import {storiesOf} from "@storybook/react"; 3 | import React from "react"; 4 | import Story from "../__utils__/Story"; 5 | import Title from "../__utils__/Title"; 6 | 7 | import {Column, Container, Row} from ".."; 8 | 9 | storiesOf("RowWrap", module) 10 | .add("nowrap", () => ( 11 | 12 | nowrap 13 | 14 | 15 | 1 16 | 2 17 | 3 18 | 4 19 | 20 | 21 | 22 | )) 23 | .add("wrap", () => ( 24 | 25 | wrap 26 | 27 | 28 | 1 29 | 2 30 | 3 31 | 4 32 | 33 | 34 | 35 | )) 36 | .add("wrap-reverse", () => ( 37 | 38 | wrap-reverse 39 | 40 | 41 | 1 42 | 2 43 | 3 44 | 4 45 | 46 | 47 | 48 | )); 49 | -------------------------------------------------------------------------------- /src/row/RowWrap.ts: -------------------------------------------------------------------------------- 1 | type RowWrap = "nowrap" | "wrap" | "wrap-reverse"; 2 | 3 | export default RowWrap; 4 | -------------------------------------------------------------------------------- /src/row/__snapshots__/Row.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[ 4 | ` should match the snapshot (alignContent: 'flex-end', alignItems: 'baseline', direction: 'column', justifyContent: center, noGutter: true, wrap: 'nowrap') 1` 5 | ] = ` 6 | .c0 { 7 | display: -webkit-box; 8 | display: -webkit-flex; 9 | display: -ms-flexbox; 10 | display: flex; 11 | -webkit-align-content: flex-end !important; 12 | -ms-flex-line-pack: end !important; 13 | align-content: flex-end !important; 14 | -webkit-align-items: baseline !important; 15 | -webkit-box-align: baseline !important; 16 | -ms-flex-align: baseline !important; 17 | align-items: baseline !important; 18 | -webkit-box-pack: center !important; 19 | -webkit-justify-content: center !important; 20 | -ms-flex-pack: center !important; 21 | justify-content: center !important; 22 | -webkit-flex-direction: column !important; 23 | -ms-flex-direction: column !important; 24 | flex-direction: column !important; 25 | margin-right: 0; 26 | margin-left: 0; 27 | -webkit-flex-wrap: nowrap; 28 | -ms-flex-wrap: nowrap; 29 | flex-wrap: nowrap; 30 | } 31 | 32 | .c0 > .sc-bdVaJa { 33 | padding-right: 0; 34 | padding-left: 0; 35 | } 36 | 37 |
42 | `; 43 | 44 | exports[ 45 | ` should match the snapshot (alignContent: 'space-between') 1` 46 | ] = ` 47 | .c0 { 48 | display: -webkit-box; 49 | display: -webkit-flex; 50 | display: -ms-flexbox; 51 | display: flex; 52 | -webkit-align-content: space-between !important; 53 | -ms-flex-line-pack: space-between !important; 54 | align-content: space-between !important; 55 | margin-right: -15px; 56 | margin-left: -15px; 57 | -webkit-flex-wrap: wrap; 58 | -ms-flex-wrap: wrap; 59 | flex-wrap: wrap; 60 | } 61 | 62 | .c0 > .sc-bdVaJa { 63 | padding-right: 15px; 64 | padding-left: 15px; 65 | } 66 | 67 |
70 | `; 71 | 72 | exports[ 73 | ` should match the snapshot (alignContent: { xs: 'space-around', sm: 'flex-end', md: 'flex-start' }) 1` 74 | ] = ` 75 | .c0 { 76 | display: -webkit-box; 77 | display: -webkit-flex; 78 | display: -ms-flexbox; 79 | display: flex; 80 | -webkit-align-content: space-around !important; 81 | -ms-flex-line-pack: space-around !important; 82 | align-content: space-around !important; 83 | margin-right: -15px; 84 | margin-left: -15px; 85 | -webkit-flex-wrap: wrap; 86 | -ms-flex-wrap: wrap; 87 | flex-wrap: wrap; 88 | } 89 | 90 | .c0 > .sc-bdVaJa { 91 | padding-right: 15px; 92 | padding-left: 15px; 93 | } 94 | 95 | @media (min-width:576px) { 96 | .c0 { 97 | -webkit-align-content: flex-end !important; 98 | -ms-flex-line-pack: end !important; 99 | align-content: flex-end !important; 100 | } 101 | } 102 | 103 | @media (min-width:768px) { 104 | .c0 { 105 | -webkit-align-content: flex-start !important; 106 | -ms-flex-line-pack: start !important; 107 | align-content: flex-start !important; 108 | } 109 | } 110 | 111 |
114 | `; 115 | 116 | exports[` should match the snapshot (alignItems: 'strech') 1`] = ` 117 | .c0 { 118 | display: -webkit-box; 119 | display: -webkit-flex; 120 | display: -ms-flexbox; 121 | display: flex; 122 | -webkit-align-items: stretch !important; 123 | -webkit-box-align: stretch !important; 124 | -ms-flex-align: stretch !important; 125 | align-items: stretch !important; 126 | margin-right: -15px; 127 | margin-left: -15px; 128 | -webkit-flex-wrap: wrap; 129 | -ms-flex-wrap: wrap; 130 | flex-wrap: wrap; 131 | } 132 | 133 | .c0 > .sc-bdVaJa { 134 | padding-right: 15px; 135 | padding-left: 15px; 136 | } 137 | 138 |
141 | `; 142 | 143 | exports[ 144 | ` should match the snapshot (alignItems: { xs: 'baseline', sm: 'center', md: 'flex-end' }) 1` 145 | ] = ` 146 | .c0 { 147 | display: -webkit-box; 148 | display: -webkit-flex; 149 | display: -ms-flexbox; 150 | display: flex; 151 | -webkit-align-items: baseline !important; 152 | -webkit-box-align: baseline !important; 153 | -ms-flex-align: baseline !important; 154 | align-items: baseline !important; 155 | margin-right: -15px; 156 | margin-left: -15px; 157 | -webkit-flex-wrap: wrap; 158 | -ms-flex-wrap: wrap; 159 | flex-wrap: wrap; 160 | } 161 | 162 | .c0 > .sc-bdVaJa { 163 | padding-right: 15px; 164 | padding-left: 15px; 165 | } 166 | 167 | @media (min-width:576px) { 168 | .c0 { 169 | -webkit-align-items: center !important; 170 | -webkit-box-align: center !important; 171 | -ms-flex-align: center !important; 172 | align-items: center !important; 173 | } 174 | } 175 | 176 | @media (min-width:768px) { 177 | .c0 { 178 | -webkit-align-items: flex-end !important; 179 | -webkit-box-align: flex-end !important; 180 | -ms-flex-align: flex-end !important; 181 | align-items: flex-end !important; 182 | } 183 | } 184 | 185 |
188 | `; 189 | 190 | exports[` should match the snapshot (direction: 'row') 1`] = ` 191 | .c0 { 192 | display: -webkit-box; 193 | display: -webkit-flex; 194 | display: -ms-flexbox; 195 | display: flex; 196 | -webkit-flex-direction: row !important; 197 | -ms-flex-direction: row !important; 198 | flex-direction: row !important; 199 | margin-right: -15px; 200 | margin-left: -15px; 201 | -webkit-flex-wrap: wrap; 202 | -ms-flex-wrap: wrap; 203 | flex-wrap: wrap; 204 | } 205 | 206 | .c0 > .sc-bdVaJa { 207 | padding-right: 15px; 208 | padding-left: 15px; 209 | } 210 | 211 |
215 | `; 216 | 217 | exports[ 218 | ` should match the snapshot (direction: { xs: 'row', sm: 'row-reverse', md: 'column' }) 1` 219 | ] = ` 220 | .c0 { 221 | display: -webkit-box; 222 | display: -webkit-flex; 223 | display: -ms-flexbox; 224 | display: flex; 225 | -webkit-flex-direction: row !important; 226 | -ms-flex-direction: row !important; 227 | flex-direction: row !important; 228 | margin-right: -15px; 229 | margin-left: -15px; 230 | -webkit-flex-wrap: wrap; 231 | -ms-flex-wrap: wrap; 232 | flex-wrap: wrap; 233 | } 234 | 235 | .c0 > .sc-bdVaJa { 236 | padding-right: 15px; 237 | padding-left: 15px; 238 | } 239 | 240 | @media (min-width:576px) { 241 | .c0 { 242 | -webkit-flex-direction: row-reverse !important; 243 | -ms-flex-direction: row-reverse !important; 244 | flex-direction: row-reverse !important; 245 | } 246 | } 247 | 248 | @media (min-width:768px) { 249 | .c0 { 250 | -webkit-flex-direction: column !important; 251 | -ms-flex-direction: column !important; 252 | flex-direction: column !important; 253 | } 254 | } 255 | 256 |
266 | `; 267 | 268 | exports[ 269 | ` should match the snapshot (justifyContent: 'space-between') 1` 270 | ] = ` 271 | .c0 { 272 | display: -webkit-box; 273 | display: -webkit-flex; 274 | display: -ms-flexbox; 275 | display: flex; 276 | -webkit-box-pack: justify !important; 277 | -webkit-justify-content: space-between !important; 278 | -ms-flex-pack: justify !important; 279 | justify-content: space-between !important; 280 | margin-right: -15px; 281 | margin-left: -15px; 282 | -webkit-flex-wrap: wrap; 283 | -ms-flex-wrap: wrap; 284 | flex-wrap: wrap; 285 | } 286 | 287 | .c0 > .sc-bdVaJa { 288 | padding-right: 15px; 289 | padding-left: 15px; 290 | } 291 | 292 |
295 | `; 296 | 297 | exports[ 298 | ` should match the snapshot (justifyContent: { xs: 'space-around', sm: 'flex-end', md: 'flex-start' }) 1` 299 | ] = ` 300 | .c0 { 301 | display: -webkit-box; 302 | display: -webkit-flex; 303 | display: -ms-flexbox; 304 | display: flex; 305 | -webkit-box-pack: space-around !important; 306 | -webkit-justify-content: space-around !important; 307 | -ms-flex-pack: space-around !important; 308 | justify-content: space-around !important; 309 | margin-right: -15px; 310 | margin-left: -15px; 311 | -webkit-flex-wrap: wrap; 312 | -ms-flex-wrap: wrap; 313 | flex-wrap: wrap; 314 | } 315 | 316 | .c0 > .sc-bdVaJa { 317 | padding-right: 15px; 318 | padding-left: 15px; 319 | } 320 | 321 | @media (min-width:576px) { 322 | .c0 { 323 | -webkit-box-pack: end !important; 324 | -webkit-justify-content: flex-end !important; 325 | -ms-flex-pack: end !important; 326 | justify-content: flex-end !important; 327 | } 328 | } 329 | 330 | @media (min-width:768px) { 331 | .c0 { 332 | -webkit-box-pack: start !important; 333 | -webkit-justify-content: flex-start !important; 334 | -ms-flex-pack: start !important; 335 | justify-content: flex-start !important; 336 | } 337 | } 338 | 339 |
342 | `; 343 | 344 | exports[` should match the snapshot (no properties set) 1`] = ` 345 | .c0 { 346 | display: -webkit-box; 347 | display: -webkit-flex; 348 | display: -ms-flexbox; 349 | display: flex; 350 | margin-right: -15px; 351 | margin-left: -15px; 352 | -webkit-flex-wrap: wrap; 353 | -ms-flex-wrap: wrap; 354 | flex-wrap: wrap; 355 | } 356 | 357 | .c0 > .sc-bdVaJa { 358 | padding-right: 15px; 359 | padding-left: 15px; 360 | } 361 | 362 |
365 | `; 366 | 367 | exports[ 368 | ` should match the snapshot (noGutter: { xs: true, sm: false, md: true }) 1` 369 | ] = ` 370 | .c0 { 371 | display: -webkit-box; 372 | display: -webkit-flex; 373 | display: -ms-flexbox; 374 | display: flex; 375 | margin-right: 0; 376 | margin-left: 0; 377 | -webkit-flex-wrap: wrap; 378 | -ms-flex-wrap: wrap; 379 | flex-wrap: wrap; 380 | } 381 | 382 | .c0 > .sc-bdVaJa { 383 | padding-right: 0; 384 | padding-left: 0; 385 | } 386 | 387 | @media (min-width:576px) { 388 | .c0 { 389 | margin-right: -15px; 390 | margin-left: -15px; 391 | } 392 | 393 | .c0 > .sc-bdVaJa { 394 | padding-right: 15px; 395 | padding-left: 15px; 396 | } 397 | } 398 | 399 | @media (min-width:768px) { 400 | .c0 { 401 | margin-right: 0; 402 | margin-left: 0; 403 | } 404 | 405 | .c0 > .sc-bdVaJa { 406 | padding-right: 0; 407 | padding-left: 0; 408 | } 409 | } 410 | 411 |
414 | `; 415 | 416 | exports[` should match the snapshot (noGutter: true) 1`] = ` 417 | .c0 { 418 | display: -webkit-box; 419 | display: -webkit-flex; 420 | display: -ms-flexbox; 421 | display: flex; 422 | margin-right: 0; 423 | margin-left: 0; 424 | -webkit-flex-wrap: wrap; 425 | -ms-flex-wrap: wrap; 426 | flex-wrap: wrap; 427 | } 428 | 429 | .c0 > .sc-bdVaJa { 430 | padding-right: 0; 431 | padding-left: 0; 432 | } 433 | 434 |
437 | `; 438 | 439 | exports[` should match the snapshot (wrap: 'wrap-reverse') 1`] = ` 440 | .c0 { 441 | display: -webkit-box; 442 | display: -webkit-flex; 443 | display: -ms-flexbox; 444 | display: flex; 445 | margin-right: -15px; 446 | margin-left: -15px; 447 | -webkit-flex-wrap: wrap-reverse; 448 | -ms-flex-wrap: wrap-reverse; 449 | flex-wrap: wrap-reverse; 450 | } 451 | 452 | .c0 > .sc-bdVaJa { 453 | padding-right: 15px; 454 | padding-left: 15px; 455 | } 456 | 457 |
461 | `; 462 | 463 | exports[ 464 | ` should match the snapshot (wrap: { xs: 'nowrap', sm: 'wrap-reverse', md: 'wrap' }) 1` 465 | ] = ` 466 | .c0 { 467 | display: -webkit-box; 468 | display: -webkit-flex; 469 | display: -ms-flexbox; 470 | display: flex; 471 | margin-right: -15px; 472 | margin-left: -15px; 473 | -webkit-flex-wrap: nowrap; 474 | -ms-flex-wrap: nowrap; 475 | flex-wrap: nowrap; 476 | } 477 | 478 | .c0 > .sc-bdVaJa { 479 | padding-right: 15px; 480 | padding-left: 15px; 481 | } 482 | 483 | @media (min-width:576px) { 484 | .c0 { 485 | -webkit-flex-wrap: wrap-reverse; 486 | -ms-flex-wrap: wrap-reverse; 487 | flex-wrap: wrap-reverse; 488 | } 489 | } 490 | 491 | @media (min-width:768px) { 492 | .c0 { 493 | -webkit-flex-wrap: wrap; 494 | -ms-flex-wrap: wrap; 495 | flex-wrap: wrap; 496 | } 497 | } 498 | 499 |
509 | `; 510 | -------------------------------------------------------------------------------- /src/row/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`index should verify the API shape 1`] = ` 4 | Object { 5 | "default": [Function], 6 | } 7 | `; 8 | -------------------------------------------------------------------------------- /src/row/index.test.ts: -------------------------------------------------------------------------------- 1 | import * as module from "."; 2 | 3 | describe("index", () => { 4 | it("should verify the API shape", () => { 5 | expect(module).toMatchSnapshot(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/row/index.ts: -------------------------------------------------------------------------------- 1 | import Row from "./Row"; 2 | 3 | export default Row; 4 | -------------------------------------------------------------------------------- /src/row/renderAlignContent.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import renderAlignContent from "./renderAlignContent"; 3 | 4 | describe("renderAlignContent", () => { 5 | it("should render align-content appropriate (input: 'center')", () => { 6 | // arrange 7 | const input = "center"; 8 | 9 | // act 10 | const output = renderAlignContent(input); 11 | 12 | // assert 13 | expect(flatten(output)).toBe("align-content:center!important;"); 14 | }); 15 | 16 | it("should render align-content appropriate (input: 'flex-end')", () => { 17 | // arrange 18 | const input = "flex-end"; 19 | 20 | // act 21 | const output = renderAlignContent(input); 22 | 23 | // assert 24 | expect(flatten(output)).toBe("align-content:flex-end!important;"); 25 | }); 26 | 27 | it("should render align-content appropriate (input: 'flex-start')", () => { 28 | // arrange 29 | const input = "flex-start"; 30 | 31 | // act 32 | const output = renderAlignContent(input); 33 | 34 | // assert 35 | expect(flatten(output)).toBe("align-content:flex-start!important;"); 36 | }); 37 | 38 | it("should render align-content appropriate (input: 'space-around')", () => { 39 | // arrange 40 | const input = "space-around"; 41 | 42 | // act 43 | const output = renderAlignContent(input); 44 | 45 | // assert 46 | expect(flatten(output)).toBe("align-content:space-around!important;"); 47 | }); 48 | 49 | it("should render align-content appropriate (input: 'space-between')", () => { 50 | // arrange 51 | const input = "space-between"; 52 | 53 | // act 54 | const output = renderAlignContent(input); 55 | 56 | // assert 57 | expect(flatten(output)).toBe("align-content:space-between!important;"); 58 | }); 59 | 60 | it("should render align-content appropriate (input: 'stretch')", () => { 61 | // arrange 62 | const input = "stretch"; 63 | 64 | // act 65 | const output = renderAlignContent(input); 66 | 67 | // assert 68 | expect(flatten(output)).toBe("align-content:stretch!important;"); 69 | }); 70 | 71 | it("should render align-content appropriate (input: undefined)", () => { 72 | // arrange 73 | const input = undefined; 74 | 75 | // act 76 | const output = renderAlignContent(input); 77 | 78 | // assert 79 | expect(flatten(output)).toBe(""); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /src/row/renderAlignContent.ts: -------------------------------------------------------------------------------- 1 | import {css} from "../utils"; 2 | import RowAlignContent from "./RowAlignContent"; 3 | 4 | export default (alignContent?: RowAlignContent): string => { 5 | const allowedValues = [ 6 | "center", 7 | "flex-end", 8 | "flex-start", 9 | "space-around", 10 | "space-between", 11 | "stretch", 12 | ]; 13 | 14 | if (allowedValues.includes(alignContent!)) { 15 | return css` 16 | align-content: ${alignContent!} !important; 17 | `; 18 | } else { 19 | return ""; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/row/renderAlignItems.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import renderAlignItems from "./renderAlignItems"; 3 | 4 | describe("renderAlignItems", () => { 5 | it("should render align-items appropriate (input: 'baseline')", () => { 6 | // arrange 7 | const input = "baseline"; 8 | 9 | // act 10 | const output = renderAlignItems(input); 11 | 12 | // assert 13 | expect(flatten(output)).toBe("align-items:baseline!important;"); 14 | }); 15 | 16 | it("should render align-items appropriate (input: 'center')", () => { 17 | // arrange 18 | const input = "center"; 19 | 20 | // act 21 | const output = renderAlignItems(input); 22 | 23 | // assert 24 | expect(flatten(output)).toBe("align-items:center!important;"); 25 | }); 26 | 27 | it("should render align-items appropriate (input: 'flex-end')", () => { 28 | // arrange 29 | const input = "flex-end"; 30 | 31 | // act 32 | const output = renderAlignItems(input); 33 | 34 | // assert 35 | expect(flatten(output)).toBe("align-items:flex-end!important;"); 36 | }); 37 | 38 | it("should render align-items appropriate (input: 'flex-start')", () => { 39 | // arrange 40 | const input = "flex-start"; 41 | 42 | // act 43 | const output = renderAlignItems(input); 44 | 45 | // assert 46 | expect(flatten(output)).toBe("align-items:flex-start!important;"); 47 | }); 48 | 49 | it("should render align-items appropriate (input: 'stretch')", () => { 50 | // arrange 51 | const input = "stretch"; 52 | 53 | // act 54 | const output = renderAlignItems(input); 55 | 56 | // assert 57 | expect(flatten(output)).toBe("align-items:stretch!important;"); 58 | }); 59 | 60 | it("should render allign-items appropriate (input: undefined)", () => { 61 | // arrange 62 | const input = undefined; 63 | 64 | // act 65 | const output = renderAlignItems(input); 66 | 67 | // assert 68 | expect(flatten(output)).toBe(""); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/row/renderAlignItems.ts: -------------------------------------------------------------------------------- 1 | import {css} from "../utils"; 2 | import RowAlignItems from "./RowAlignItems"; 3 | 4 | export default (alignItems?: RowAlignItems): string => { 5 | const allowedValues = [ 6 | "baseline", 7 | "center", 8 | "flex-end", 9 | "flex-start", 10 | "stretch", 11 | ]; 12 | 13 | if (allowedValues.includes(alignItems!)) { 14 | return css` 15 | align-items: ${alignItems!} !important; 16 | `; 17 | } else { 18 | return ""; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/row/renderDirection.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import renderDirection from "./renderDirection"; 3 | 4 | describe("renderDirection", () => { 5 | it("should render direction appropriate (input: 'column')", () => { 6 | // arrange 7 | const input = "column"; 8 | 9 | // act 10 | const output = renderDirection(input); 11 | 12 | // assert 13 | expect(flatten(output)).toBe("flex-direction:column!important;"); 14 | }); 15 | 16 | it("should render direction appropriate (input: 'column-reverse')", () => { 17 | // arrange 18 | const input = "column-reverse"; 19 | 20 | // act 21 | const output = renderDirection(input); 22 | 23 | // assert 24 | expect(flatten(output)).toBe("flex-direction:column-reverse!important;"); 25 | }); 26 | 27 | it("should render direction appropriate (input: 'row')", () => { 28 | // arrange 29 | const input = "row"; 30 | 31 | // act 32 | const output = renderDirection(input); 33 | 34 | // assert 35 | expect(flatten(output)).toBe("flex-direction:row!important;"); 36 | }); 37 | 38 | it("should render direction appropriate (input: 'row-reverse')", () => { 39 | // arrange 40 | const input = "row-reverse"; 41 | 42 | // act 43 | const output = renderDirection(input); 44 | 45 | // assert 46 | expect(flatten(output)).toBe("flex-direction:row-reverse!important;"); 47 | }); 48 | 49 | it("should render direction appropriate (input: undefined)", () => { 50 | // arrange 51 | const input = undefined; 52 | 53 | // act 54 | const output = renderDirection(input); 55 | 56 | // assert 57 | expect(flatten(output)).toBe(""); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /src/row/renderDirection.ts: -------------------------------------------------------------------------------- 1 | import {css} from "../utils"; 2 | import RowDirection from "./RowDirection"; 3 | 4 | export default (direction?: RowDirection): string => { 5 | const allowedValues = ["column", "column-reverse", "row", "row-reverse"]; 6 | 7 | if (allowedValues.includes(direction!)) { 8 | return css` 9 | flex-direction: ${direction!} !important; 10 | `; 11 | } else { 12 | return ""; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /src/row/renderGutter.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import renderGutter from "./renderGutter"; 3 | 4 | describe("renderGutter", () => { 5 | it("should render container width (input: true)", () => { 6 | // arrange 7 | const input = true; 8 | 9 | // act 10 | const output = renderGutter(input); 11 | 12 | // assert 13 | expect(flatten(output)).toMatch( 14 | /margin\-right:0;margin\-left:0;>.*?{padding\-right:0;padding\-left:0;}/gi, 15 | ); 16 | }); 17 | 18 | it("should render container width (input: false)", () => { 19 | // arrange 20 | const input = false; 21 | 22 | // act 23 | const output = renderGutter(input); 24 | 25 | // assert 26 | expect(flatten(output)).toMatch( 27 | /margin\-right:\-15px;margin\-left:\-15px;>.*?{padding\-right:15px;padding\-left:15px;}/gi, 28 | ); 29 | }); 30 | 31 | it("should render container width (input: undefined)", () => { 32 | // arrange 33 | const input = undefined; 34 | 35 | // act 36 | const output = renderGutter(input); 37 | 38 | // assert 39 | expect(flatten(output)).toMatch( 40 | /margin\-right:\-15px;margin\-left:\-15px;>.*?{padding\-right:15px;padding\-left:15px;}/gi, 41 | ); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/row/renderGutter.ts: -------------------------------------------------------------------------------- 1 | import Column from "../column"; 2 | import {gutterWidth, Theme} from "../theme"; 3 | import {css} from "../utils"; 4 | 5 | export default (noGutter?: boolean, theme?: Theme): string => { 6 | // the next expression is for JS projects 7 | // tslint:disable-next-line:strict-type-predicates 8 | if (noGutter !== undefined && typeof noGutter === "boolean" && noGutter) { 9 | return css` 10 | margin-right: 0; 11 | margin-left: 0; 12 | 13 | > ${/* sc-selector */ Column} { 14 | padding-right: 0; 15 | padding-left: 0; 16 | } 17 | `; 18 | } else { 19 | const width = gutterWidth(theme); 20 | 21 | return css` 22 | margin-right: -${width}px; 23 | margin-left: -${width}px; 24 | 25 | > ${/* sc-selector */ Column} { 26 | padding-right: ${width}px; 27 | padding-left: ${width}px; 28 | } 29 | `; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/row/renderJustifyContent.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import renderJustifyContent from "./renderJustifyContent"; 3 | 4 | describe("renderJustifyContent", () => { 5 | it("should render justify-content appropriate (input: 'center')", () => { 6 | // arrange 7 | const input = "center"; 8 | 9 | // act 10 | const output = renderJustifyContent(input); 11 | 12 | // assert 13 | expect(flatten(output)).toBe("justify-content:center!important;"); 14 | }); 15 | 16 | it("should render justify-content appropriate (input: 'flex-end')", () => { 17 | // arrange 18 | const input = "flex-end"; 19 | 20 | // act 21 | const output = renderJustifyContent(input); 22 | 23 | // assert 24 | expect(flatten(output)).toBe("justify-content:flex-end!important;"); 25 | }); 26 | 27 | it("should render justify-content appropriate (input: 'flex-start')", () => { 28 | // arrange 29 | const input = "flex-start"; 30 | 31 | // act 32 | const output = renderJustifyContent(input); 33 | 34 | // assert 35 | expect(flatten(output)).toBe("justify-content:flex-start!important;"); 36 | }); 37 | 38 | it("should render justify-content appropriate (input: 'space-around')", () => { 39 | // arrange 40 | const input = "space-around"; 41 | 42 | // act 43 | const output = renderJustifyContent(input); 44 | 45 | // assert 46 | expect(flatten(output)).toBe("justify-content:space-around!important;"); 47 | }); 48 | 49 | it("should render justify-content appropriate (input: 'space-between')", () => { 50 | // arrange 51 | const input = "space-between"; 52 | 53 | // act 54 | const output = renderJustifyContent(input); 55 | 56 | // assert 57 | expect(flatten(output)).toBe("justify-content:space-between!important;"); 58 | }); 59 | 60 | it("should render justify-content appropriate (input: undefined)", () => { 61 | // arrange 62 | const input = undefined; 63 | 64 | // act 65 | const output = renderJustifyContent(input); 66 | 67 | // assert 68 | expect(flatten(output)).toBe(""); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/row/renderJustifyContent.ts: -------------------------------------------------------------------------------- 1 | import {css} from "../utils"; 2 | import RowJustifyContent from "./RowJustifyContent"; 3 | 4 | export default (contentJustify?: RowJustifyContent): string => { 5 | const allowedValues = [ 6 | "center", 7 | "flex-end", 8 | "flex-start", 9 | "space-around", 10 | "space-between", 11 | ]; 12 | 13 | if (allowedValues.includes(contentJustify!)) { 14 | return css` 15 | justify-content: ${contentJustify!} !important; 16 | `; 17 | } else { 18 | return ""; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/row/renderWrap.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import renderWrap from "./renderWrap"; 3 | 4 | describe("renderWrap", () => { 5 | it("should render wrap appropriate (input: 'nowrap')", () => { 6 | // arrange 7 | const input = "nowrap"; 8 | 9 | // act 10 | const output = renderWrap(input); 11 | 12 | // assert 13 | expect(flatten(output)).toBe("flex-wrap:nowrap;"); 14 | }); 15 | 16 | it("should render wrap appropriate (input: 'wrap')", () => { 17 | // arrange 18 | const input = "wrap"; 19 | 20 | // act 21 | const output = renderWrap(input); 22 | 23 | // assert 24 | expect(flatten(output)).toBe("flex-wrap:wrap;"); 25 | }); 26 | 27 | it("should render wrap appropriate (input: 'wrap-reverse')", () => { 28 | // arrange 29 | const input = "wrap-reverse"; 30 | 31 | // act 32 | const output = renderWrap(input); 33 | 34 | // assert 35 | expect(flatten(output)).toBe("flex-wrap:wrap-reverse;"); 36 | }); 37 | 38 | it("should render wrap appropriate (input: undefined)", () => { 39 | // arrange 40 | const input = undefined; 41 | 42 | // act 43 | const output = renderWrap(input); 44 | 45 | // assert 46 | expect(flatten(output)).toBe("flex-wrap:wrap;"); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /src/row/renderWrap.ts: -------------------------------------------------------------------------------- 1 | import {css} from "../utils"; 2 | import RowWrap from "./RowWrap"; 3 | 4 | function renderWrap(wrap?: RowWrap): string { 5 | const allowedValues = ["nowrap", "wrap", "wrap-reverse"]; 6 | 7 | // the next expression is for JS projects 8 | // tslint:disable-next-line:strict-type-predicates 9 | if (wrap == null || !allowedValues.includes(wrap)) { 10 | return renderWrap("wrap"); 11 | } else { 12 | return css` 13 | flex-wrap: ${wrap}; 14 | `; 15 | } 16 | } 17 | 18 | export default renderWrap; 19 | -------------------------------------------------------------------------------- /src/theme/StyledComponents.ts: -------------------------------------------------------------------------------- 1 | import {ComponentClass, StatelessComponent} from "react"; 2 | import * as styledComponents from "styled-components"; 3 | import Theme from "./Theme"; 4 | 5 | const { 6 | default: styled, 7 | css, 8 | injectGlobal, 9 | keyframes, 10 | ThemeProvider, 11 | } = styledComponents as styledComponents.ThemedStyledComponentsModule; 12 | 13 | export default styled; 14 | export {css, injectGlobal, keyframes, ThemeProvider}; 15 | -------------------------------------------------------------------------------- /src/theme/Theme.ts: -------------------------------------------------------------------------------- 1 | import {BreakpointMap, BreakpointValues} from "../media"; 2 | 3 | interface Theme { 4 | breakpoints?: BreakpointMap; 5 | containerWidth?: BreakpointValues; 6 | gutterWidth?: number; 7 | } 8 | 9 | export default Theme; 10 | -------------------------------------------------------------------------------- /src/theme/ThemeProperties.ts: -------------------------------------------------------------------------------- 1 | import Theme from "./Theme"; 2 | 3 | interface ThemeProperties { 4 | theme?: Theme; 5 | } 6 | 7 | export default ThemeProperties; 8 | -------------------------------------------------------------------------------- /src/theme/ThemeProvider.test.tsx: -------------------------------------------------------------------------------- 1 | import {mount} from "enzyme"; 2 | import "jest-styled-components"; 3 | import React from "react"; 4 | import {Column, Container, Row} from ".."; 5 | import {ThemeProvider} from "./StyledComponents"; 6 | 7 | describe("", () => { 8 | it("should match the snapshot (gutterWidth set to 100)", () => { 9 | // act 10 | const result = mount( 11 | 12 | 13 | 14 | Test 15 | 16 | 17 | , 18 | ); 19 | 20 | // assert 21 | expect(result).toMatchSnapshot(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/theme/__snapshots__/ThemeProvider.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[ 4 | ` should match the snapshot (gutterWidth set to 100) 1` 5 | ] = ` 6 | .c3 { 7 | position: relative; 8 | width: 100%; 9 | min-height: 1px; 10 | } 11 | 12 | .c1 { 13 | display: -webkit-box; 14 | display: -webkit-flex; 15 | display: -ms-flexbox; 16 | display: flex; 17 | margin-right: -50px; 18 | margin-left: -50px; 19 | -webkit-flex-wrap: wrap; 20 | -ms-flex-wrap: wrap; 21 | flex-wrap: wrap; 22 | } 23 | 24 | .c1 > .c2 { 25 | padding-right: 50px; 26 | padding-left: 50px; 27 | } 28 | 29 | .c0 { 30 | width: 100%; 31 | margin-right: auto; 32 | margin-left: auto; 33 | padding-right: 50px; 34 | padding-left: 50px; 35 | } 36 | 37 | @media (min-width:576px) { 38 | .c0 { 39 | max-width: 540px; 40 | } 41 | } 42 | 43 | @media (min-width:768px) { 44 | .c0 { 45 | max-width: 720px; 46 | } 47 | } 48 | 49 | @media (min-width:992px) { 50 | .c0 { 51 | max-width: 960px; 52 | } 53 | } 54 | 55 | @media (min-width:1200px) { 56 | .c0 { 57 | max-width: 1140px; 58 | } 59 | } 60 | 61 | 68 | 69 |
72 | 73 |
76 | 77 |
80 | Test 81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | `; 89 | -------------------------------------------------------------------------------- /src/theme/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`index should verify the API shape 1`] = ` 4 | Object { 5 | "Theme": Object {}, 6 | "ThemeProperties": Object {}, 7 | "gutterWidth": [Function], 8 | "styled": [Function], 9 | } 10 | `; 11 | -------------------------------------------------------------------------------- /src/theme/gutterWidth.test.ts: -------------------------------------------------------------------------------- 1 | import gutterWidth from "./gutterWidth"; 2 | 3 | describe("gutterWidth", () => { 4 | it("should return 15 (default value) if not specified", () => { 5 | // arrange 6 | const theme = {}; 7 | 8 | // act 9 | const output = gutterWidth(theme); 10 | 11 | // arrange 12 | expect(output).toEqual(15); 13 | }); 14 | 15 | it("should return 20 if set to 40", () => { 16 | // arrange 17 | const theme = {gutterWidth: 40}; 18 | 19 | // act 20 | const output = gutterWidth(theme); 21 | 22 | // arrange 23 | expect(output).toEqual(20); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/theme/gutterWidth.ts: -------------------------------------------------------------------------------- 1 | import Theme from "./Theme"; 2 | 3 | export default (theme?: Theme) => { 4 | const gutterWidth = (theme && theme.gutterWidth) || 30; 5 | 6 | return gutterWidth / 2; 7 | }; 8 | -------------------------------------------------------------------------------- /src/theme/index.test.ts: -------------------------------------------------------------------------------- 1 | import * as module from "."; 2 | 3 | describe("index", () => { 4 | it("should verify the API shape", () => { 5 | expect(module).toMatchSnapshot(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/theme/index.ts: -------------------------------------------------------------------------------- 1 | export {default as gutterWidth} from "./gutterWidth"; 2 | export {default as styled} from "./StyledComponents"; 3 | export {default as Theme} from "./Theme"; 4 | export {default as ThemeProperties} from "./ThemeProperties"; 5 | -------------------------------------------------------------------------------- /src/utils/RenderProvider.ts: -------------------------------------------------------------------------------- 1 | import {PropertyValue} from "../media"; 2 | 3 | interface RenderProvider { 4 | [key: string]: (value?: PropertyValue) => string; 5 | } 6 | 7 | export default RenderProvider; 8 | -------------------------------------------------------------------------------- /src/utils/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`index should verify the API shape 1`] = ` 4 | Object { 5 | "RenderProvider": Object {}, 6 | "css": [Function], 7 | "flatten": [Function], 8 | "map": [Function], 9 | "percentage": [Function], 10 | "render": [Function], 11 | "resolve": [Function], 12 | } 13 | `; 14 | -------------------------------------------------------------------------------- /src/utils/bootstrap.test.ts: -------------------------------------------------------------------------------- 1 | describe("bootstrap", () => { 2 | it("should not break", () => { 3 | // arrange 4 | jest.mock("../theme/StyledComponents"); 5 | 6 | // act 7 | require("./bootstrap"); 8 | 9 | // assert 10 | // should not break 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/utils/bootstrap.ts: -------------------------------------------------------------------------------- 1 | import {injectGlobal} from "../theme/StyledComponents"; 2 | 3 | let initialized = false; 4 | 5 | function bootstrap() { 6 | if (!initialized) { 7 | // tslint:disable-next-line:no-unused-expression 8 | injectGlobal` 9 | @-ms-viewport { 10 | width: device-width; 11 | } 12 | 13 | html { 14 | box-sizing: border-box; 15 | -ms-overflow-style: scrollbar; 16 | } 17 | 18 | *, 19 | *::before, 20 | *::after { 21 | box-sizing: inherit; 22 | } 23 | `; 24 | initialized = true; 25 | } 26 | } 27 | 28 | bootstrap(); 29 | -------------------------------------------------------------------------------- /src/utils/css.test.ts: -------------------------------------------------------------------------------- 1 | import css from "./css"; 2 | 3 | describe("cssAndFlatten", () => { 4 | it("should render css and flatten it", () => { 5 | // arrange 6 | const input = "width: 50px;"; 7 | 8 | // act 9 | const output = css` 10 | ${input}; 11 | `; 12 | 13 | // assert 14 | expect(output).toContain("width: 50px;"); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/utils/css.ts: -------------------------------------------------------------------------------- 1 | import {SimpleInterpolation} from "styled-components"; 2 | import {css} from "../theme/StyledComponents"; 3 | import flatten from "./flatten"; 4 | 5 | export default ( 6 | strings: TemplateStringsArray, 7 | ...interpolations: SimpleInterpolation[] 8 | ): string => flatten(css(strings, ...interpolations)); 9 | -------------------------------------------------------------------------------- /src/utils/flatten.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "./flatten"; 2 | 3 | describe("flatten", () => { 4 | it("should flatten multiple interpolations", () => { 5 | // arrange 6 | const input = ["an ", "amazing ", "test"]; 7 | 8 | // act 9 | const output = flatten(input); 10 | 11 | // assert 12 | expect(output).toBe("an amazing test"); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/utils/flatten.ts: -------------------------------------------------------------------------------- 1 | import {InterpolationValue} from "styled-components"; 2 | 3 | export default (interpolations: InterpolationValue[]): string => 4 | interpolations.join(""); 5 | -------------------------------------------------------------------------------- /src/utils/index.test.ts: -------------------------------------------------------------------------------- 1 | import * as module from "."; 2 | 3 | describe("index", () => { 4 | it("should verify the API shape", () => { 5 | expect(module).toMatchSnapshot(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export {default as css} from "./css"; 2 | export {default as flatten} from "./flatten"; 3 | export {default as map} from "./map"; 4 | export {default as percentage} from "./percentage"; 5 | export {default as render} from "./render"; 6 | export {default as RenderProvider} from "./RenderProvider"; 7 | export {default as resolve} from "./resolve"; 8 | -------------------------------------------------------------------------------- /src/utils/map.test.ts: -------------------------------------------------------------------------------- 1 | import map from "./map"; 2 | 3 | describe("map", () => { 4 | it("should map a PropertyValuesMap to BreakpointValuesMap", () => { 5 | // arrange 6 | const input = { 7 | number: { 8 | xs: 540, 9 | md: 670, 10 | }, 11 | string: { 12 | sm: "value 1", 13 | md: "value 2", 14 | }, 15 | }; 16 | 17 | // act 18 | const output = map(input); 19 | 20 | // arrange 21 | expect(output).toEqual({ 22 | xs: { 23 | number: 540, 24 | }, 25 | sm: { 26 | string: "value 1", 27 | }, 28 | md: { 29 | number: 670, 30 | string: "value 2", 31 | }, 32 | }); 33 | }); 34 | 35 | it("should map if a property is set to 5", () => { 36 | // arrange 37 | const input = { 38 | number: 5, 39 | }; 40 | 41 | // act 42 | const output = map(input); 43 | 44 | // arrange 45 | expect(output).toEqual({ 46 | xs: { 47 | number: 5, 48 | }, 49 | }); 50 | }); 51 | 52 | it("should map if a property is set to undefined", () => { 53 | // arrange 54 | const input = { 55 | number: undefined, 56 | }; 57 | 58 | // act 59 | const output = map(input); 60 | 61 | // arrange 62 | expect(output).toEqual({ 63 | xs: { 64 | number: undefined, 65 | }, 66 | }); 67 | }); 68 | 69 | it("should map if the valueMap is {}", () => { 70 | // arrange 71 | const input = {}; 72 | 73 | // act 74 | const output = map(input); 75 | 76 | // arrange 77 | expect(output).toEqual({}); 78 | }); 79 | 80 | it("should map if the valueMap is undefined", () => { 81 | // arrange 82 | const input = undefined; 83 | 84 | // act 85 | const output = map(input); 86 | 87 | // arrange 88 | expect(output).toBeNull(); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /src/utils/map.ts: -------------------------------------------------------------------------------- 1 | import {BreakpointValuesMap, PropertyValuesMap} from "../media"; 2 | import {Theme} from "../theme"; 3 | import prepare from "./prepare"; 4 | 5 | export default ( 6 | source?: PropertyValuesMap, 7 | theme?: Theme, 8 | ): BreakpointValuesMap | null => { 9 | if (source !== undefined) { 10 | return Object.keys(source).reduce( 11 | (destination, propertyKey) => { 12 | const values = prepare(source[propertyKey], theme); 13 | 14 | Object.keys(values).forEach((breakpointKey) => { 15 | destination[breakpointKey] = destination[breakpointKey] || {}; 16 | destination[breakpointKey][propertyKey] = values[breakpointKey]; 17 | }); 18 | 19 | return destination; 20 | }, 21 | {}, 22 | ); 23 | } 24 | 25 | return null; 26 | }; 27 | -------------------------------------------------------------------------------- /src/utils/percentage.test.ts: -------------------------------------------------------------------------------- 1 | import percentage from "./percentage"; 2 | 3 | describe("percentage", () => { 4 | it("should return '33.333333' (input: 4)", () => { 5 | // arrange 6 | const input = 4; 7 | 8 | // act 9 | const output = percentage(input); 10 | 11 | // assert 12 | expect(output).toBe("33.333333"); 13 | }); 14 | 15 | it("should return '50.000000' (input: 6)", () => { 16 | // arrange 17 | const input = 6; 18 | 19 | // act 20 | const output = percentage(input); 21 | 22 | // assert 23 | expect(output).toBe("50.000000"); 24 | }); 25 | 26 | it("should return '100.000000' (input: 12)", () => { 27 | // arrange 28 | const input = 12; 29 | 30 | // act 31 | const output = percentage(input); 32 | 33 | // assert 34 | expect(output).toBe("100.000000"); 35 | }); 36 | 37 | it("should return '0' (input: 13)", () => { 38 | // arrange 39 | const input = 13; 40 | 41 | // act 42 | const output = percentage(input); 43 | 44 | // assert 45 | expect(output).toBe("0"); 46 | }); 47 | 48 | it("should return '0' (input: undefined)", () => { 49 | // arrange 50 | const input = undefined; 51 | 52 | // act 53 | const output = percentage(input); 54 | 55 | // assert 56 | expect(output).toBe("0"); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /src/utils/percentage.ts: -------------------------------------------------------------------------------- 1 | export default function renderOrder(size: number): string { 2 | // the next expression is for JS projects 3 | // tslint:disable-next-line:strict-type-predicates 4 | if (size != null && size > 0 && size < 13) { 5 | return (size / 12 * 100).toFixed(6); 6 | } 7 | 8 | return "0"; 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/prepare.test.ts: -------------------------------------------------------------------------------- 1 | import prepare from "./prepare"; 2 | 3 | describe("prepare", () => { 4 | it("should return { xs: 444, md: 666 }", () => { 5 | // arrange 6 | const input = { 7 | xs: 444, 8 | md: 666, 9 | }; 10 | 11 | // act 12 | const output = prepare(input); 13 | 14 | // arrange 15 | expect(output).toEqual({ 16 | xs: 444, 17 | md: 666, 18 | }); 19 | }); 20 | 21 | it("should return { xs: undefined, md: 999 }", () => { 22 | // arrange 23 | const input = { 24 | md: 999, 25 | }; 26 | 27 | // act 28 | const output = prepare(input); 29 | 30 | // arrange 31 | expect(output).toEqual({ 32 | xs: undefined, 33 | md: 999, 34 | }); 35 | }); 36 | 37 | it("should return { xs: 888 }", () => { 38 | // arrange 39 | const input = { 40 | xs: 888, 41 | }; 42 | 43 | // act 44 | const output = prepare(input); 45 | 46 | // arrange 47 | expect(output).toEqual({ 48 | xs: 888, 49 | }); 50 | }); 51 | 52 | it("should return { xs: 777 }", () => { 53 | // arrange 54 | const input = 777; 55 | 56 | // act 57 | const output = prepare(input); 58 | 59 | // arrange 60 | expect(output).toEqual({ 61 | xs: 777, 62 | }); 63 | }); 64 | 65 | it("should return { xs: undefined }", () => { 66 | // arrange 67 | const input = undefined; 68 | 69 | // act 70 | const output = prepare(input); 71 | 72 | // arrange 73 | expect(output).toEqual({ 74 | xs: undefined, 75 | }); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /src/utils/prepare.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BreakpointValue, 3 | BreakpointValues, 4 | defaultBreakpointKey, 5 | PropertyValue, 6 | } from "../media"; 7 | import {Theme} from "../theme"; 8 | 9 | export default ( 10 | values?: BreakpointValue, 11 | theme?: Theme, 12 | ): BreakpointValues => { 13 | const defaultKey = defaultBreakpointKey(theme); 14 | let output = values || {}; 15 | 16 | if (typeof output !== "object") { 17 | const newValues = {}; 18 | 19 | if (defaultKey !== null) { 20 | newValues[defaultKey] = output; 21 | } 22 | 23 | output = newValues; 24 | } 25 | 26 | if (defaultKey !== null && output[defaultKey] === undefined) { 27 | output[defaultKey] = undefined; 28 | } 29 | 30 | return output; 31 | }; 32 | -------------------------------------------------------------------------------- /src/utils/render.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import render from "./render"; 3 | 4 | describe("render", () => { 5 | it("should render as expected (input: { width: { xs: 111 } })", () => { 6 | // arrange 7 | const renderer = { 8 | width: (value?) => (value && `width: ${value}px;`) || "", 9 | }; 10 | const valueMap = { 11 | width: { 12 | xs: 111, 13 | }, 14 | }; 15 | 16 | // act 17 | const output = render(valueMap, renderer); 18 | 19 | // assert 20 | expect(flatten(output)).toBe("width:111px;"); 21 | }); 22 | 23 | it("should render as expected (input: { width: { md: 222 } })", () => { 24 | // arrange 25 | const renderer = { 26 | width: (value?) => (value && `width: ${value}px;`) || "", 27 | }; 28 | const valueMap = { 29 | width: { 30 | md: 222, 31 | }, 32 | }; 33 | 34 | // act 35 | const output = render(valueMap, renderer); 36 | 37 | // assert 38 | expect(flatten(output)).toBe("@media(min-width:768px){width:222px;}"); 39 | }); 40 | 41 | it("should render as expected (input: { width: { xs: 111, sm: 111, md: 222 } })", () => { 42 | // arrange 43 | const renderer = { 44 | width: (value?) => (value && `width: ${value}px;`) || "", 45 | }; 46 | const valueMap = { 47 | width: { 48 | xs: 111, 49 | sm: 111, 50 | md: 222, 51 | }, 52 | }; 53 | 54 | // act 55 | const output = render(valueMap, renderer); 56 | 57 | // assert 58 | expect(flatten(output)).toBe( 59 | "width:111px;@media(min-width:768px){width:222px;}", 60 | ); 61 | }); 62 | 63 | it("should render as expected (input: { width: {} })", () => { 64 | // arrange 65 | const renderer = { 66 | width: (value?) => (value && `width: ${value}px;`) || "", 67 | }; 68 | const valueMap = { 69 | width: {}, 70 | }; 71 | 72 | // act 73 | const output = render(valueMap, renderer); 74 | 75 | // assert 76 | expect(flatten(output)).toBe(""); 77 | }); 78 | 79 | it("should render as expected (input: {})", () => { 80 | // arrange 81 | const renderer = { 82 | width: (value?) => (value && `width: ${value}px;`) || "", 83 | }; 84 | const valueMap = {}; 85 | 86 | // act 87 | const output = render(valueMap, renderer); 88 | 89 | // assert 90 | expect(flatten(output)).toBe(""); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /src/utils/render.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BreakpointMap, 3 | breakpoints, 4 | PropertyValues, 5 | PropertyValuesMap, 6 | } from "../media"; 7 | import {Theme} from "../theme"; 8 | import map from "./map"; 9 | import RenderProvider from "./RenderProvider"; 10 | import resolve from "./resolve"; 11 | 12 | export default ( 13 | valueMap: PropertyValuesMap, 14 | renderer: RenderProvider, 15 | theme?: Theme, 16 | ): string => { 17 | const breakpointsMap = breakpoints(theme); 18 | const breakpointValues = map(valueMap, theme) || {}; 19 | // the next expression is for JS projects 20 | // tslint:disable:strict-type-predicates 21 | const breakpointKeys = Object.keys(breakpointValues).filter( 22 | (breakpointKey) => 23 | breakpointsMap[breakpointKey] != null && 24 | typeof breakpointValues[breakpointKey] === "object", 25 | ); 26 | const propertyKeys = Object.keys(valueMap).filter( 27 | (propertyKey) => renderer[propertyKey] != null, 28 | ); 29 | // tslint:enable:strict-type-predicates 30 | const previousValues: PropertyValues = {}; 31 | let count = 0; 32 | 33 | return breakpointKeys.reduce((acc, breakpointKey) => { 34 | count++; 35 | 36 | return ( 37 | acc + 38 | resolve(breakpointsMap, breakpointKey)` 39 | ${propertyKeys.reduce((acc2, propertyKey) => { 40 | const value = 41 | breakpointValues[breakpointKey] && 42 | breakpointValues[breakpointKey][propertyKey]; 43 | 44 | if (count > 1 && previousValues[propertyKey] === value) { 45 | return acc2; 46 | } else { 47 | previousValues[propertyKey] = value; 48 | 49 | return acc2 + renderer[propertyKey](value); 50 | } 51 | }, "")} 52 | ` 53 | ); 54 | }, ""); 55 | }; 56 | -------------------------------------------------------------------------------- /src/utils/resolve.test.ts: -------------------------------------------------------------------------------- 1 | import flatten from "../__utils__/flatten"; 2 | import {breakpoints} from "../media"; 3 | import resolve from "./resolve"; 4 | 5 | describe("resolve", () => { 6 | it("should resolve media breakpoints (input: 'xs')", () => { 7 | // arrange 8 | const breakpointsMap = breakpoints(); 9 | 10 | // act 11 | const output = resolve(breakpointsMap, "xs")` 12 | width: 444px; 13 | `; 14 | 15 | // assert 16 | expect(flatten(output)).toBe("width:444px;"); 17 | }); 18 | 19 | it("should resolve media breakpoints (input: 'sm')", () => { 20 | // arrange 21 | const breakpointsMap = breakpoints(); 22 | 23 | // act 24 | const output = resolve(breakpointsMap, "sm")` 25 | width: 555px; 26 | `; 27 | 28 | // assert 29 | expect(flatten(output)).toBe("@media(min-width:576px){width:555px;}"); 30 | }); 31 | 32 | it("should resolve media breakpoints (input: 'not-exists')", () => { 33 | // arrange 34 | const breakpointsMap = breakpoints(); 35 | 36 | // act 37 | const output = resolve(breakpointsMap, "not-exists")` 38 | width: 666px; 39 | `; 40 | 41 | // assert 42 | expect(flatten(output)).toBe("width:666px;"); 43 | }); 44 | 45 | it("should resolve media breakpoints (input: 'sm', css body empty)", () => { 46 | // arrange 47 | const breakpointsMap = breakpoints(); 48 | 49 | // act 50 | const output = resolve(breakpointsMap, "sm")``; 51 | 52 | // assert 53 | expect(flatten(output)).toBe(""); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /src/utils/resolve.ts: -------------------------------------------------------------------------------- 1 | import {SimpleInterpolation} from "styled-components"; 2 | import {BreakpointMap} from "../media"; 3 | import _css from "./css"; 4 | 5 | export default (breakpoints: BreakpointMap, key: string) => ( 6 | strings: TemplateStringsArray, 7 | ...interpolations: SimpleInterpolation[] 8 | ): string => { 9 | const minWidth = breakpoints[key]; 10 | const mediaBody = _css(strings, ...interpolations); 11 | 12 | if (minWidth && mediaBody.match(/\S/g)) { 13 | return _css` 14 | @media (min-width: ${minWidth}px) { 15 | ${mediaBody} 16 | } 17 | `; 18 | } 19 | 20 | return mediaBody; 21 | }; 22 | -------------------------------------------------------------------------------- /test.config.ts: -------------------------------------------------------------------------------- 1 | import { configure } from "enzyme"; 2 | import Adapter from "enzyme-adapter-react-16"; 3 | 4 | configure({ adapter: new Adapter() }); 5 | jest.mock("./src/utils/bootstrap", () => jest.fn()); 6 | -------------------------------------------------------------------------------- /tsconfig.es5.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib/es5", 5 | "target": "es5" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lib/es6", 4 | "module": "commonjs", 5 | "target": "es6", 6 | "lib": ["esnext", "dom"], 7 | "allowSyntheticDefaultImports": true, 8 | "allowJs": false, 9 | "declaration": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "esModuleInterop": true, 12 | "jsx": "react", 13 | "moduleResolution": "node", 14 | "noImplicitAny": true, 15 | "noImplicitReturns": true, 16 | "noImplicitThis": true, 17 | "noUnusedParameters": false, 18 | "rootDir": "src", 19 | "strictNullChecks": true, 20 | "sourceMap": true, 21 | "suppressImplicitAnyIndexErrors": true 22 | }, 23 | "include": ["src/**/*.ts*"], 24 | "exclude": ["src/__utils__/**/*.*", "src/**/*.story.*", "src/**/*.test.*"] 25 | } 26 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["./linting"] 3 | } 4 | --------------------------------------------------------------------------------