From 43c13c0731e2145870b4ca7bbb4d1f7d13e9ded6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Curcio?= Date: Wed, 11 Jul 2018 17:50:21 -0300 Subject: [PATCH] Responsive Components <3 --- package-lock.json | 48 +++++++++++++++---- package.json | 2 + .../client/ui/components/Center/Center.tsx | 2 +- src/core/client/ui/components/Flex/Flex.css | 37 ++++++++++++++ .../client/ui/components/Flex/Flex.spec.tsx | 17 +++++++ src/core/client/ui/components/Flex/Flex.tsx | 38 +++++++++++++++ .../Flex/__snapshots__/Flex.spec.tsx.snap | 7 +++ src/core/client/ui/components/Flex/index.ts | 2 + .../components/MatchMedia/MatchMedia.spec.tsx | 31 ++++++++++++ .../ui/components/MatchMedia/MediaMedia.tsx | 28 +++++++++++ .../__snapshots__/MatchMedia.spec.tsx.snap | 26 ++++++++++ .../client/ui/components/MatchMedia/index.ts | 2 + src/core/client/ui/theme/variables.ts | 9 ++++ src/core/common/utils/index.ts | 1 + src/core/common/utils/pascalCase.ts | 5 ++ src/locales/es/common.ftl | 0 16 files changed, 245 insertions(+), 10 deletions(-) create mode 100644 src/core/client/ui/components/Flex/Flex.css create mode 100644 src/core/client/ui/components/Flex/Flex.spec.tsx create mode 100644 src/core/client/ui/components/Flex/Flex.tsx create mode 100644 src/core/client/ui/components/Flex/__snapshots__/Flex.spec.tsx.snap create mode 100644 src/core/client/ui/components/Flex/index.ts create mode 100644 src/core/client/ui/components/MatchMedia/MatchMedia.spec.tsx create mode 100644 src/core/client/ui/components/MatchMedia/MediaMedia.tsx create mode 100644 src/core/client/ui/components/MatchMedia/__snapshots__/MatchMedia.spec.tsx.snap create mode 100644 src/core/client/ui/components/MatchMedia/index.ts create mode 100644 src/core/common/utils/pascalCase.ts create mode 100644 src/locales/es/common.ftl diff --git a/package-lock.json b/package-lock.json index 7f7c9169e..9cf345ffd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1772,7 +1772,6 @@ "version": "16.4.2", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.4.2.tgz", "integrity": "sha512-oVcVteCDNiVc/fkDjowRfAZQDEOR76j3CJ3FvwXNvfV6zJguhghy1lMgpAzYox+9AZsWch+JPV6Imml3wvIUeg==", - "dev": true, "requires": { "csstype": "^2.2.0" } @@ -1792,6 +1791,14 @@ "from": "github:coralproject/patched#types/react-relay", "dev": true }, + "@types/react-responsive": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/react-responsive/-/react-responsive-3.0.1.tgz", + "integrity": "sha512-ti7MKe0AuWCGpNkRzRsRB8ExS7NZotXtDtD27rX4HiZML37SetbIp3cw5FDXIeqIC7a3TzWuPjv/I9Qcu4YL3w==", + "requires": { + "@types/react": "*" + } + }, "@types/react-test-renderer": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.0.1.tgz", @@ -6183,6 +6190,11 @@ } } }, + "css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha1-aiw3NEkoYYYxxUvTPO3TAdoYvqA=" + }, "css-modules-loader-core": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz", @@ -6472,8 +6484,7 @@ "csstype": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.5.5.tgz", - "integrity": "sha512-EGMjeoiN3aqEX5u/cyH5mSdGBDGdLcCQvcEcBWNGFSPXKd9uOTIeVG91YQ22OxI44DKpvI+4C7VUSmEpsHWJaA==", - "dev": true + "integrity": "sha512-EGMjeoiN3aqEX5u/cyH5mSdGBDGdLcCQvcEcBWNGFSPXKd9uOTIeVG91YQ22OxI44DKpvI+4C7VUSmEpsHWJaA==" }, "currently-unhandled": { "version": "0.4.1", @@ -10086,6 +10097,11 @@ "decamelize": "^1.0.0" } }, + "hyphenate-style-name": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz", + "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es=" + }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", @@ -11709,8 +11725,7 @@ "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" }, "js-yaml": { "version": "3.12.0", @@ -12472,7 +12487,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true, "requires": { "js-tokens": "^3.0.0" } @@ -12583,6 +12597,14 @@ "integrity": "sha1-iQwsGzv+g/sA5BKbjkz+ZFJw+dE=", "dev": true }, + "matchmediaquery": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.2.1.tgz", + "integrity": "sha1-IjxwBXk94D5HzpKxMoWnLEStos8=", + "requires": { + "css-mediaquery": "^0.1.2" + } + }, "math-expression-evaluator": { "version": "1.2.17", "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", @@ -13421,8 +13443,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-copy": { "version": "0.1.0", @@ -16371,7 +16392,6 @@ "version": "15.6.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", - "dev": true, "requires": { "loose-envify": "^1.3.1", "object-assign": "^4.1.1" @@ -16958,6 +16978,16 @@ "prop-types": "^15.5.8" } }, + "react-responsive": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-4.1.0.tgz", + "integrity": "sha512-ZuDraf0qsJlyiTwzeva+foHx83IP6SIhru9o7BvMwQ4ZHjRIL5WjdgVNNrKSRbmeWO9rEJoMpabei/5lJn8KaA==", + "requires": { + "hyphenate-style-name": "^1.0.0", + "matchmediaquery": "^0.2.1", + "prop-types": "^15.6.1" + } + }, "react-router": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", diff --git a/package.json b/package.json index 944a521f6..2d312573d 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "author": "", "license": "Apache-2.0", "dependencies": { + "@types/react-responsive": "^3.0.1", "apollo-server-express": "^1.3.6", "bunyan": "^1.8.12", "convict": "^4.3.0", @@ -45,6 +46,7 @@ "mongodb": "^3.0.10", "passport": "^0.4.0", "performance-now": "^2.1.0", + "react-responsive": "^4.1.0", "subscriptions-transport-ws": "^0.9.11", "uuid": "^3.2.1" }, diff --git a/src/core/client/ui/components/Center/Center.tsx b/src/core/client/ui/components/Center/Center.tsx index 892277ac6..369d9cc8e 100644 --- a/src/core/client/ui/components/Center/Center.tsx +++ b/src/core/client/ui/components/Center/Center.tsx @@ -1,5 +1,5 @@ import cn from "classnames"; -import * as React from "react"; +import React from "react"; import { ReactNode, StatelessComponent } from "react"; import { withStyles } from "talk-ui/hocs"; diff --git a/src/core/client/ui/components/Flex/Flex.css b/src/core/client/ui/components/Flex/Flex.css new file mode 100644 index 000000000..07559d116 --- /dev/null +++ b/src/core/client/ui/components/Flex/Flex.css @@ -0,0 +1,37 @@ +.root { +} + +.justifyFlexStart { + justify-content: "flex-start"; +} +.justifyFlexEnd { + justify-content: "flex-end"; +} +.justifyCenter { + justify-content: "center"; +} +.justifySpaceBetween { + justify-content: "space-between"; +} +.justifySpaceAround { + justify-content: "space-around"; +} +.justifySpaceEvenly { + justify-content: "space-evenly"; +} + +.alignFlexStart { + align-items: "flex-start"; +} +.alignFlexEnd { + align-items: "flex-end"; +} +.alignCenter { + align-items: "center"; +} +.alignBaseline { + align-items: "baseline"; +} +.alignStretch { + align-items: "stretch"; +} diff --git a/src/core/client/ui/components/Flex/Flex.spec.tsx b/src/core/client/ui/components/Flex/Flex.spec.tsx new file mode 100644 index 000000000..485e5bd9f --- /dev/null +++ b/src/core/client/ui/components/Flex/Flex.spec.tsx @@ -0,0 +1,17 @@ +import { shallow } from "enzyme"; +import React from "react"; + +import Flex from "./Flex"; + +it("renders correctly", () => { + const props = { + justifyContent: "center", + alignItems: "center", + }; + const wrapper = shallow( + +
Hello World
+
+ ); + expect(wrapper).toMatchSnapshot(); +}); diff --git a/src/core/client/ui/components/Flex/Flex.tsx b/src/core/client/ui/components/Flex/Flex.tsx new file mode 100644 index 000000000..b2e0e69ea --- /dev/null +++ b/src/core/client/ui/components/Flex/Flex.tsx @@ -0,0 +1,38 @@ +import cn from "classnames"; +import React from "react"; +import { StatelessComponent } from "react"; + +import { pascalCase } from "talk-common/utils"; + +import * as styles from "./Flex.css"; + +interface InnerProps { + justifyContent?: + | "flex-start" + | "flex-end" + | "center" + | "space-around" + | "space-between" + | "space-evenly"; + alignItems?: "flex-start" | "flex-end" | "center" | "baseline" | "stretch"; +} + +const Flex: StatelessComponent = props => { + const { justifyContent, alignItems } = props; + + const classObject: Record = {}; + + if (justifyContent) { + classObject[`justify${pascalCase(justifyContent)}`] = true; + } + + if (alignItems) { + classObject[`align${pascalCase(alignItems)}`] = true; + } + + const classNames: string = cn(styles.root, classObject); + + return
; +}; + +export default Flex; diff --git a/src/core/client/ui/components/Flex/__snapshots__/Flex.spec.tsx.snap b/src/core/client/ui/components/Flex/__snapshots__/Flex.spec.tsx.snap new file mode 100644 index 000000000..e9dd085c5 --- /dev/null +++ b/src/core/client/ui/components/Flex/__snapshots__/Flex.spec.tsx.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` +
+`; diff --git a/src/core/client/ui/components/Flex/index.ts b/src/core/client/ui/components/Flex/index.ts new file mode 100644 index 000000000..27e09f37b --- /dev/null +++ b/src/core/client/ui/components/Flex/index.ts @@ -0,0 +1,2 @@ +export * from "./Flex"; +export { default } from "./Flex"; diff --git a/src/core/client/ui/components/MatchMedia/MatchMedia.spec.tsx b/src/core/client/ui/components/MatchMedia/MatchMedia.spec.tsx new file mode 100644 index 000000000..527e5921f --- /dev/null +++ b/src/core/client/ui/components/MatchMedia/MatchMedia.spec.tsx @@ -0,0 +1,31 @@ +import { shallow } from "enzyme"; +import React from "react"; + +import MatchMedia from "./MatchMedia"; + +it("renders correctly", () => { + const props = { + minWidth: "xs", + maxWidth: "sm", + component: "div", + screen: true, + }; + const wrapper = shallow( + +
Hello World
+
+ ); + expect(wrapper).toMatchSnapshot(); +}); + +it("map new speech prop to older aural prop", () => { + const props = { + speech: true, + }; + const wrapper = shallow( + +
Hello World
+
+ ); + expect(wrapper).toMatchSnapshot(); +}); diff --git a/src/core/client/ui/components/MatchMedia/MediaMedia.tsx b/src/core/client/ui/components/MatchMedia/MediaMedia.tsx new file mode 100644 index 000000000..ebd4ed210 --- /dev/null +++ b/src/core/client/ui/components/MatchMedia/MediaMedia.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import { ReactNode, StatelessComponent } from "react"; +import Responsive from "react-responsive"; + +interface InnerProps { + minWidth?: string; + maxWidth?: string; + children: ReactNode; + className?: string; + component?: + | string + | React.SFC + | React.ClassType + | React.ComponentClass; + all?: boolean; + print?: boolean; + screen?: boolean; + speech?: boolean; +} + +const MatchMedia: StatelessComponent = props => { + // TODO: Temporarily map newer speech to older aural type until + // react-responsive supports the speech prop. + const { speech, ...rest } = props; + return ; +}; + +export default MatchMedia; diff --git a/src/core/client/ui/components/MatchMedia/__snapshots__/MatchMedia.spec.tsx.snap b/src/core/client/ui/components/MatchMedia/__snapshots__/MatchMedia.spec.tsx.snap new file mode 100644 index 000000000..4ce9b2fd8 --- /dev/null +++ b/src/core/client/ui/components/MatchMedia/__snapshots__/MatchMedia.spec.tsx.snap @@ -0,0 +1,26 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`map new speech prop to older aural prop 1`] = ` + +
+ Hello World +
+
+`; + +exports[`renders correctly 1`] = ` + +
+ Hello World +
+
+`; diff --git a/src/core/client/ui/components/MatchMedia/index.ts b/src/core/client/ui/components/MatchMedia/index.ts new file mode 100644 index 000000000..71f16f6f8 --- /dev/null +++ b/src/core/client/ui/components/MatchMedia/index.ts @@ -0,0 +1,2 @@ +export * from "./MatchMedia"; +export { default } from "./MatchMedia"; diff --git a/src/core/client/ui/theme/variables.ts b/src/core/client/ui/theme/variables.ts index 4c4a54332..2e9499883 100644 --- a/src/core/client/ui/theme/variables.ts +++ b/src/core/client/ui/theme/variables.ts @@ -68,6 +68,15 @@ const variables = { fontWeightLight: 300, fontWeightRegular: 400, fontWeightMedium: 550, + /* Breakpoints */ + breakpoints: { + xs: 0, + sm: 320, + md: 640, + lg: 1024, + xl: 1400, + xxl: 1600, + }, }; module.exports = variables; diff --git a/src/core/common/utils/index.ts b/src/core/common/utils/index.ts index 88c1f3fec..e91489017 100644 --- a/src/core/common/utils/index.ts +++ b/src/core/common/utils/index.ts @@ -1 +1,2 @@ export { default as timeout } from "./timeout"; +export { default as pascalCase } from "./pascalCase"; diff --git a/src/core/common/utils/pascalCase.ts b/src/core/common/utils/pascalCase.ts new file mode 100644 index 000000000..14b036ccc --- /dev/null +++ b/src/core/common/utils/pascalCase.ts @@ -0,0 +1,5 @@ +import { camelCase } from "lodash"; +/** pascalCase */ +export default function pascalCase(value: string) { + return value[0].toUpperCase() + camelCase(value).slice(1); +} diff --git a/src/locales/es/common.ftl b/src/locales/es/common.ftl new file mode 100644 index 000000000..e69de29bb