├── .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 | 
2 |
3 | [](https://github.com/ChilliCream/react-rasta/releases) [
4 | ](https://www.npmjs.com/package/react-rasta) [](https://github.com/ChilliCream/react-rasta/blob/master/LICENSE)
5 | [](https://circleci.com/gh/ChilliCream/react-rasta/tree/master) [](https://coveralls.io/github/ChilliCream/react-rasta?branch=master) [](https://bettercodehub.com/results/ChilliCream/react-rasta) [](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 |
--------------------------------------------------------------------------------