From 65c7526758835bba2c73f9ac83a6fa8aee8f362d Mon Sep 17 00:00:00 2001 From: "Wallar, Todd" Date: Mon, 15 Jun 2020 17:35:27 -0700 Subject: [PATCH 1/7] Typescript conversion config changes. --- babel.config.js | 3 ++- jest.config.js | 12 ++++++------ package.json | 7 ++++++- tsconfig.json | 28 ++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 tsconfig.json diff --git a/babel.config.js b/babel.config.js index 0e7ae84..4a5470b 100644 --- a/babel.config.js +++ b/babel.config.js @@ -11,7 +11,8 @@ module.exports = { } } ], - '@babel/preset-react' + '@babel/preset-react', + '@babel/typescript', ], comments: false, env: { diff --git a/jest.config.js b/jest.config.js index 1a7ddc4..ab53cc9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -17,7 +17,7 @@ module.exports = { setupFiles: [require.resolve('core-js')], - moduleFileExtensions: ['jsx', 'js'], + moduleFileExtensions: ['ts', 'tsx', 'jsx', 'js'], collectCoverageFrom: ['/src/**/*.{js,jsx}'], @@ -30,19 +30,19 @@ module.exports = { branches: 100, function: 100, lines: 100, - statements: 100 - } + statements: 100, + }, }, // A map from regular expressions to paths to transformers transform: { - '^.+\\.(js|jsx)$': require.resolve('./test-harness/preprocessor'), - '^(?!.*\\.(js|jsx|css|json)$)': require.resolve('./test-harness/fileTransform') + '^.+\\.(ts|tsx|js|jsx)$': require.resolve('./test-harness/preprocessor'), + '^(?!.*\\.(ts|tsx|js|jsx|css|json)$)': require.resolve('./test-harness/fileTransform'), }, verbose: true, testURL: 'http://localhost', // The test environment that will be used for testing - testEnvironment: 'jest-environment-jsdom-global' + testEnvironment: 'jest-environment-jsdom-global', }; diff --git a/package.json b/package.json index a881488..13f7b30 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ ], "main": "dist/index.js", "scripts": { - "build": "rimraf dist && NODE_ENV=production babel --config-file=./babel.config.js src -d dist", + "build": "rimraf dist && NODE_ENV=production babel --config-file=./babel.config.js src -d dist --extensions '.js,.jsx,.ts,.tsx' && tsc", "lint": "eslint --config .eslintrc --ext .jsx --ext .js .", "lint:fix": "eslint --config .eslintrc --ext .jsx --ext .js . --fix", "test": "jest --config=./jest.config.js", @@ -40,10 +40,13 @@ "@babel/plugin-transform-runtime": "~7.9.6", "@babel/preset-env": "~7.9.6", "@babel/preset-react": "~7.9.4", + "@babel/preset-typescript": "^7.10.1", "@babel/register": "~7.9.0", "@babel/runtime": "~7.9.6", "@testing-library/jest-dom": "~5.7.0", "@testing-library/react": "~10.0.4", + "@types/jest": "^26.0.0", + "@types/react": "^16.9.36", "babel-eslint": "~10.1.0", "babel-jest": "~26.0.1", "babel-loader": "~8.1.0", @@ -72,6 +75,8 @@ "react-dom": "~16.13.1", "rimraf": "~3.0.2", "snyk": "~1.320.1", + "ts-jest": "^26.1.0", + "typescript": "^3.9.5", "uuid": "~8.0.0" }, "peerDependencies": { diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..3f076d9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "outDir": "lib", + "target": "es5", + "module": "commonjs", + "jsx": "react", + "allowJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "sourceMap": true, + "importHelpers": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "Node", + "noImplicitThis": true, + "noImplicitReturns": true, + "noImplicitAny": true, + "skipLibCheck": true, + "lib": ["es5", "dom"], + "types": ["node", "jest"], + "noUnusedLocals": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "preserveSymlinks": true, + "baseUrl": ".", + "outDir": "./dist" + }, + "include": ["src"], +} From b2fd466f01b8a7f3f23a08a8b2973e7ee498c6ab Mon Sep 17 00:00:00 2001 From: "Wallar, Todd" Date: Mon, 15 Jun 2020 17:35:47 -0700 Subject: [PATCH 2/7] Typescript conversion, first pass. --- ...onsiveContext.jsx => ResponsiveContext.js} | 0 src/ThresholdMap.ts | 4 +++ ...nsiveProps.jsx => WithResponsiveProps.tsx} | 4 +-- src/{WithThreshold.jsx => WithThreshold.tsx} | 26 ++++++++++--------- ...ThresholdMap.js => defaultThresholdMap.ts} | 4 ++- src/{getThreshold.js => getThreshold.ts} | 6 +++-- ...ropBuilder.js => responsivePropBuilder.ts} | 17 +++++++++--- src/{useThreshold.js => useThreshold.ts} | 3 ++- ...pec.jsx => requestAnimationFrame.spec.tsx} | 2 +- 9 files changed, 43 insertions(+), 23 deletions(-) rename src/{ResponsiveContext.jsx => ResponsiveContext.js} (100%) create mode 100644 src/ThresholdMap.ts rename src/{WithResponsiveProps.jsx => WithResponsiveProps.tsx} (91%) rename src/{WithThreshold.jsx => WithThreshold.tsx} (76%) rename src/{defaultThresholdMap.js => defaultThresholdMap.ts} (55%) rename src/{getThreshold.js => getThreshold.ts} (74%) rename src/{responsivePropBuilder.js => responsivePropBuilder.ts} (73%) rename src/{useThreshold.js => useThreshold.ts} (89%) rename tests/{requestAnimationFrame.spec.jsx => requestAnimationFrame.spec.tsx} (99%) diff --git a/src/ResponsiveContext.jsx b/src/ResponsiveContext.js similarity index 100% rename from src/ResponsiveContext.jsx rename to src/ResponsiveContext.js diff --git a/src/ThresholdMap.ts b/src/ThresholdMap.ts new file mode 100644 index 0000000..1fea3e5 --- /dev/null +++ b/src/ThresholdMap.ts @@ -0,0 +1,4 @@ +export type Threshold = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; +export type ThresholdMap = { + [key in Threshold]?: number; +}; diff --git a/src/WithResponsiveProps.jsx b/src/WithResponsiveProps.tsx similarity index 91% rename from src/WithResponsiveProps.jsx rename to src/WithResponsiveProps.tsx index 14b713e..9c87298 100644 --- a/src/WithResponsiveProps.jsx +++ b/src/WithResponsiveProps.tsx @@ -11,8 +11,8 @@ import defaultThresholdMap from './defaultThresholdMap'; // propKeys: [ 'size' ], // } -const WithResponsiveProps = configuration => WrappedComponent => { - const Component = props => { +const WithResponsiveProps = (configuration) => (WrappedComponent) => { + const Component = (props) => { const threshold = useThreshold(); const responsiveContext = useContext(ResponsiveContext); diff --git a/src/WithThreshold.jsx b/src/WithThreshold.tsx similarity index 76% rename from src/WithThreshold.jsx rename to src/WithThreshold.tsx index 0b3c531..adf0c06 100644 --- a/src/WithThreshold.jsx +++ b/src/WithThreshold.tsx @@ -1,17 +1,21 @@ -/* eslint-disable react/prop-types */ -/* eslint-disable react/destructuring-assignment */ import React from 'react'; import getThreshold from './getThreshold'; +import { Threshold } from './ThresholdMap'; import ResponsiveContext from './ResponsiveContext'; import defaultThresholdMap from './defaultThresholdMap'; -const withThreshold = () => Component => { - class WithThreshold extends React.Component { - map; +interface WithThresholdProps { + threshold?: Threshold; +} +function withThreshold

(Component: React.ComponentType

) { + return class extends React.Component { + static contextType = ResponsiveContext; + + map = {}; timeout = 0; - constructor(props) { + constructor(props: P) { super(props); this.state = { @@ -63,10 +67,8 @@ const withThreshold = () => Component => { return ; } - } - WithThreshold.contextType = ResponsiveContext; - - return WithThreshold; -}; + }; +} -export default withThreshold; +const WithThreshold = () => withThreshold; +export default WithThreshold; diff --git a/src/defaultThresholdMap.js b/src/defaultThresholdMap.ts similarity index 55% rename from src/defaultThresholdMap.js rename to src/defaultThresholdMap.ts index c23c368..66f5923 100644 --- a/src/defaultThresholdMap.js +++ b/src/defaultThresholdMap.ts @@ -1,4 +1,6 @@ -const defaultThresholdMap = () => { +import { ThresholdMap } from './ThresholdMap'; + +const defaultThresholdMap = (): ThresholdMap => { return { xs: 0, sm: 480, diff --git a/src/getThreshold.js b/src/getThreshold.ts similarity index 74% rename from src/getThreshold.js rename to src/getThreshold.ts index 15dfadc..e773b4c 100644 --- a/src/getThreshold.js +++ b/src/getThreshold.ts @@ -1,5 +1,7 @@ -const getThreshold = (width = 0, breakpoints = { xs: 0 }) => { - const breakpointKeys = Object.keys(breakpoints); +import { Threshold, ThresholdMap } from './ThresholdMap'; + +const getThreshold = (width: number = 0, breakpoints : ThresholdMap = { xs: 0 }) => { + const breakpointKeys = Object.keys(breakpoints) as Array; let result = breakpointKeys[0]; diff --git a/src/responsivePropBuilder.js b/src/responsivePropBuilder.ts similarity index 73% rename from src/responsivePropBuilder.js rename to src/responsivePropBuilder.ts index 11c2533..9b1ff83 100644 --- a/src/responsivePropBuilder.js +++ b/src/responsivePropBuilder.ts @@ -1,8 +1,17 @@ +import { Threshold } from './ThresholdMap'; import defaultThresholdMap from './defaultThresholdMap'; -const getCurrentValue = value => (value !== undefined ? value : null); +const getCurrentValue = (value: any) => (value !== undefined ? value : null); -const responsivePropBuilder = (currentThreshold, props, configuration, thresholdMap = defaultThresholdMap) => { +export interface ResponsivePropsConfig { + propKeys: Array; +} + +export interface GenericProps { + [key: string]: any; +} + +const responsivePropBuilder = (currentThreshold: Threshold, props: GenericProps, configuration: ResponsivePropsConfig, thresholdMap = defaultThresholdMap) => { // get the keys from the map, e.g. ['xs', 'sm', 'md', 'lg', 'xl'] const thresholdKeys = Object.keys(thresholdMap); @@ -16,10 +25,10 @@ const responsivePropBuilder = (currentThreshold, props, configuration, threshold // only an object can contain responsive values, null is an object also but that's not valid // e.g. size={{xs: 'h4', md: 'h3'}} - const propKeys = configuration.propKeys.filter(propKey => typeof props[propKey] === 'object' && props[propKey] !== null); + const propKeys = configuration.propKeys.filter((propKey: string) => typeof props[propKey] === 'object' && props[propKey] !== null); // loop through the props that have been found as being responsive and extract an object of name/value pairs - const translatedValues = propKeys.reduce((acc, propKey) => { + const translatedValues = propKeys.reduce((acc: any, propKey: string) => { let result = null; // find the first threshold with a value. That is our value because we reversed them above starting at the current threshold and moving to smaller thresholds for (let i = 0; i < thresholds.length; i++) { diff --git a/src/useThreshold.js b/src/useThreshold.ts similarity index 89% rename from src/useThreshold.js rename to src/useThreshold.ts index 832d28c..732e9df 100644 --- a/src/useThreshold.js +++ b/src/useThreshold.ts @@ -1,10 +1,11 @@ /* eslint-disable import/no-named-as-default */ import { useState, useEffect, useContext } from 'react'; import getThreshold from './getThreshold'; +import { ThresholdMap } from './ThresholdMap'; import ResponsiveContext from './ResponsiveContext'; import defaultThresholdMap from './defaultThresholdMap'; -const getCurrentThreshold = map => getThreshold(window.innerWidth, map); +const getCurrentThreshold = (map: ThresholdMap) => getThreshold(window.innerWidth, map); function useThreshold() { const responsiveContext = useContext(ResponsiveContext); diff --git a/tests/requestAnimationFrame.spec.jsx b/tests/requestAnimationFrame.spec.tsx similarity index 99% rename from tests/requestAnimationFrame.spec.jsx rename to tests/requestAnimationFrame.spec.tsx index 2bb729f..eaf110f 100644 --- a/tests/requestAnimationFrame.spec.jsx +++ b/tests/requestAnimationFrame.spec.tsx @@ -8,7 +8,7 @@ const breakpoints = { sm: 480, md: 768, lg: 960, - xl: 1280 + xl: 1280, }; beforeEach(() => { From 1b96a1878a3696d62ed03f4453d82edac3bf55b3 Mon Sep 17 00:00:00 2001 From: "Wallar, Todd" Date: Mon, 15 Jun 2020 17:53:20 -0700 Subject: [PATCH 3/7] more TS conversions --- src/{Hide.jsx => Hide.tsx} | 10 ++++++++-- ...ResponsiveProvider.jsx => ResponsiveProvider.tsx} | 12 +++++++++--- src/{Show.jsx => Show.tsx} | 10 ++++++++-- src/WithResponsiveProps.tsx | 8 ++++++-- src/responsivePropBuilder.ts | 8 ++++---- 5 files changed, 35 insertions(+), 13 deletions(-) rename src/{Hide.jsx => Hide.tsx} (76%) rename src/{ResponsiveProvider.jsx => ResponsiveProvider.tsx} (72%) rename src/{Show.jsx => Show.tsx} (76%) diff --git a/src/Hide.jsx b/src/Hide.tsx similarity index 76% rename from src/Hide.jsx rename to src/Hide.tsx index 074695a..98225d2 100644 --- a/src/Hide.jsx +++ b/src/Hide.tsx @@ -1,8 +1,14 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import useThreshold from './useThreshold'; +import { Threshold } from './ThresholdMap'; -export const Hide = props => { +interface HideThresholdProps { + thresholds?: Array; + children: React.ReactNode; +} + +export const Hide = (props: HideThresholdProps) => { const { children, thresholds } = props; const breakpoints = Array.isArray(thresholds) ? thresholds : [thresholds]; const threshold = useThreshold(); @@ -15,7 +21,7 @@ Hide.propTypes = { /** @ignore */ children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired, /** A single value or an array of values to hide this containers content */ - thresholds: PropTypes.oneOfType([PropTypes.string, PropTypes.array]).isRequired + thresholds: PropTypes.oneOfType([PropTypes.string, PropTypes.array]).isRequired, }; export default Hide; diff --git a/src/ResponsiveProvider.jsx b/src/ResponsiveProvider.tsx similarity index 72% rename from src/ResponsiveProvider.jsx rename to src/ResponsiveProvider.tsx index 8c082a1..e1a1005 100644 --- a/src/ResponsiveProvider.jsx +++ b/src/ResponsiveProvider.tsx @@ -2,9 +2,15 @@ import React from 'react'; import PropTypes from 'prop-types'; import ResponsiveContext from './ResponsiveContext'; +import { ThresholdMap } from './ThresholdMap'; import defaultThresholdMap from './defaultThresholdMap'; -export const ResponsiveProvider = props => { +interface WithThresholdProps { + thresholdMap?: ThresholdMap; + children: React.ReactNode; +} + +export const ResponsiveProvider = (props: WithThresholdProps) => { const { thresholdMap, children } = props; const getThresholdMap = () => { @@ -17,11 +23,11 @@ ResponsiveProvider.propTypes = { /** The names and values of the responsive breakpoints */ thresholdMap: PropTypes.object, /** @ignore */ - children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired + children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired, }; ResponsiveProvider.defaultProps = { - thresholdMap: defaultThresholdMap + thresholdMap: defaultThresholdMap, }; export default ResponsiveProvider; diff --git a/src/Show.jsx b/src/Show.tsx similarity index 76% rename from src/Show.jsx rename to src/Show.tsx index 8c63574..8927be9 100644 --- a/src/Show.jsx +++ b/src/Show.tsx @@ -1,8 +1,14 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import useThreshold from './useThreshold'; +import { Threshold } from './ThresholdMap'; -export const Show = props => { +interface ShowThresholdProps { + thresholds?: Array; + children: React.ReactNode; +} + +export const Show = (props: ShowThresholdProps) => { const { children, thresholds } = props; const breakpoints = Array.isArray(thresholds) ? thresholds : [thresholds]; const threshold = useThreshold(); @@ -15,7 +21,7 @@ Show.propTypes = { /** @ignore */ children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired, /** A single value or an array of values to show this containers content */ - thresholds: PropTypes.oneOfType([PropTypes.string, PropTypes.array]).isRequired + thresholds: PropTypes.oneOfType([PropTypes.string, PropTypes.array]).isRequired, }; export default Show; diff --git a/src/WithResponsiveProps.tsx b/src/WithResponsiveProps.tsx index 9c87298..e2a5b94 100644 --- a/src/WithResponsiveProps.tsx +++ b/src/WithResponsiveProps.tsx @@ -4,6 +4,10 @@ import ResponsiveContext from './ResponsiveContext'; import useThreshold from './useThreshold'; import defaultThresholdMap from './defaultThresholdMap'; +export interface ResponsivePropsConfig { + propKeys: Array; +} + // This component takes a given property, like size, and based on the current threshold replaces that value // for example on a mobile phone the value for size would be replaced with the value from xs // e.g. size={{xs: 'small', 'md': 'large'}} @@ -11,8 +15,8 @@ import defaultThresholdMap from './defaultThresholdMap'; // propKeys: [ 'size' ], // } -const WithResponsiveProps = (configuration) => (WrappedComponent) => { - const Component = (props) => { +const WithResponsiveProps = (configuration: ResponsivePropsConfig) => (WrappedComponent: React.ComponentType) => { + const Component = (props: any) => { const threshold = useThreshold(); const responsiveContext = useContext(ResponsiveContext); diff --git a/src/responsivePropBuilder.ts b/src/responsivePropBuilder.ts index 9b1ff83..3430ff0 100644 --- a/src/responsivePropBuilder.ts +++ b/src/responsivePropBuilder.ts @@ -3,14 +3,14 @@ import defaultThresholdMap from './defaultThresholdMap'; const getCurrentValue = (value: any) => (value !== undefined ? value : null); -export interface ResponsivePropsConfig { - propKeys: Array; -} - export interface GenericProps { [key: string]: any; } +export interface ResponsivePropsConfig { + propKeys: Array; +} + const responsivePropBuilder = (currentThreshold: Threshold, props: GenericProps, configuration: ResponsivePropsConfig, thresholdMap = defaultThresholdMap) => { // get the keys from the map, e.g. ['xs', 'sm', 'md', 'lg', 'xl'] const thresholdKeys = Object.keys(thresholdMap); From 7d7cd877b0dab5311302979932c04ae2350f5878 Mon Sep 17 00:00:00 2001 From: Todd Wallar Date: Tue, 16 Jun 2020 11:07:59 -0700 Subject: [PATCH 4/7] Update src/WithThreshold.tsx Co-authored-by: Jordan Janzen --- src/WithThreshold.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WithThreshold.tsx b/src/WithThreshold.tsx index adf0c06..73bd31c 100644 --- a/src/WithThreshold.tsx +++ b/src/WithThreshold.tsx @@ -12,7 +12,7 @@ function withThreshold

(Component: React.ComponentT return class extends React.Component { static contextType = ResponsiveContext; - map = {}; + private map: ThresholdMap = defaultThresholdMap; timeout = 0; constructor(props: P) { From 0d9bd37fcf17dccc15be554278ddc91cc089d495 Mon Sep 17 00:00:00 2001 From: Todd Wallar Date: Tue, 16 Jun 2020 11:08:07 -0700 Subject: [PATCH 5/7] Update src/WithThreshold.tsx Co-authored-by: Jordan Janzen --- src/WithThreshold.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WithThreshold.tsx b/src/WithThreshold.tsx index 73bd31c..dace336 100644 --- a/src/WithThreshold.tsx +++ b/src/WithThreshold.tsx @@ -13,7 +13,7 @@ function withThreshold

(Component: React.ComponentT static contextType = ResponsiveContext; private map: ThresholdMap = defaultThresholdMap; - timeout = 0; + private timeout = 0; constructor(props: P) { super(props); From d79ed5a794b74745f55c370a0e8a3b414cd3362a Mon Sep 17 00:00:00 2001 From: Todd Wallar Date: Tue, 16 Jun 2020 11:08:24 -0700 Subject: [PATCH 6/7] Update src/WithThreshold.tsx Co-authored-by: Jordan Janzen --- src/WithThreshold.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WithThreshold.tsx b/src/WithThreshold.tsx index dace336..eea6693 100644 --- a/src/WithThreshold.tsx +++ b/src/WithThreshold.tsx @@ -11,6 +11,7 @@ interface WithThresholdProps { function withThreshold

(Component: React.ComponentType

) { return class extends React.Component { static contextType = ResponsiveContext; + static displayName = Component.displayName || `WithThreshold${Component.name}`; private map: ThresholdMap = defaultThresholdMap; private timeout = 0; From ce2e8d5b9dcfc4a0e710304f23780718a6362131 Mon Sep 17 00:00:00 2001 From: Todd Wallar Date: Tue, 16 Jun 2020 11:10:58 -0700 Subject: [PATCH 7/7] Update src/getThreshold.ts Co-authored-by: Jordan Janzen --- src/getThreshold.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/getThreshold.ts b/src/getThreshold.ts index e773b4c..1691797 100644 --- a/src/getThreshold.ts +++ b/src/getThreshold.ts @@ -1,6 +1,6 @@ import { Threshold, ThresholdMap } from './ThresholdMap'; -const getThreshold = (width: number = 0, breakpoints : ThresholdMap = { xs: 0 }) => { +const getThreshold = (width: number = 0, breakpoints: ThresholdMap = { xs: 0 }) => { const breakpointKeys = Object.keys(breakpoints) as Array; let result = breakpointKeys[0];