From 1a1f52918c31a7cb573e2027e49b25526b507b58 Mon Sep 17 00:00:00 2001 From: Eric Schultz Date: Wed, 1 May 2019 17:17:15 -0500 Subject: [PATCH 1/2] Add SelectableTableRow component --- .../SelectableTableRow.spec.tsx | 46 +++++++++++++++ .../SelectableTableRow.tsx | 31 ++++++++++ .../common/selectable_table_row/connect.tsx | 59 +++++++++++++++++++ package-lock.json | 10 ++++ package.json | 2 + 5 files changed, 148 insertions(+) create mode 100644 javascripts/src/components/common/selectable_table_row/SelectableTableRow.spec.tsx create mode 100644 javascripts/src/components/common/selectable_table_row/SelectableTableRow.tsx create mode 100644 javascripts/src/components/common/selectable_table_row/connect.tsx diff --git a/javascripts/src/components/common/selectable_table_row/SelectableTableRow.spec.tsx b/javascripts/src/components/common/selectable_table_row/SelectableTableRow.spec.tsx new file mode 100644 index 00000000..38b6100a --- /dev/null +++ b/javascripts/src/components/common/selectable_table_row/SelectableTableRow.spec.tsx @@ -0,0 +1,46 @@ +// License: LGPL-3.0-or-later +import * as React from 'react'; +import 'jest'; +import SelectableTableRow from './SelectableTableRow' +import { ReactWrapper, mount } from 'enzyme'; +import { connectTableRowSelectHandler, TableRowSelectHandlerContext } from './connect'; + + +class TestReceivedProviderComponent extends React.Component<{m:string} & TableRowSelectHandlerContext, {}>{ + render() { + return ; + } +} + +const ReceivedComponent = connectTableRowSelectHandler(TestReceivedProviderComponent) + +describe('SelectableTableRow', () => { + let providerAndRow: ReactWrapper + let onSelect: any; + beforeEach(() => { + onSelect = jest.fn() + providerAndRow = mount( + +
) + }) + + function getTr(): ReactWrapper { + return providerAndRow.find('tr') + } + + function getProviderComponent() { + return providerAndRow.find('TestReceivedProviderComponent').instance() as any + } + + it('processes the click properly', () => { + getTr().simulate('click') + + expect(onSelect).toBeCalled() + }) + + it('sends the provider down', () => { + const c = getProviderComponent() + expect(c.props.selectHandler.onSelect).toBeTruthy() + expect(c.props.selectHandler.onSelect).toBe(onSelect) + }) +}) \ No newline at end of file diff --git a/javascripts/src/components/common/selectable_table_row/SelectableTableRow.tsx b/javascripts/src/components/common/selectable_table_row/SelectableTableRow.tsx new file mode 100644 index 00000000..dc2e28d7 --- /dev/null +++ b/javascripts/src/components/common/selectable_table_row/SelectableTableRow.tsx @@ -0,0 +1,31 @@ +// License: LGPL-3.0-or-later +import * as React from 'react'; +import { TableRowSelectHandlerProvider } from './connect'; + +export interface SelectableTableRowProps + {/** + * Action you want to take when the row is selected + * @memberof SelectableTableRowProps + */ + onSelect: () => void +} + +/** + * So you want a table row that fires an action when any part of the row is clicked. Well that's what the SelectableTableRow does for you. Is it Aria compatible? Not yet! + * @class SelectableTableRow + * @extends React.Component + */ +class SelectableTableRow extends React.Component { + render() { + return + + {this.props.children} + + ; + } +} + +export default SelectableTableRow + + + diff --git a/javascripts/src/components/common/selectable_table_row/connect.tsx b/javascripts/src/components/common/selectable_table_row/connect.tsx new file mode 100644 index 00000000..a5423a9a --- /dev/null +++ b/javascripts/src/components/common/selectable_table_row/connect.tsx @@ -0,0 +1,59 @@ +//License: LGPL-3.0-or-later +//https://github.com/jaredpalmer/formik/blob/master/src/connect.tsx +import React = require("react"); +import hoistNonReactStatics = require('hoist-non-react-statics'); + + +/** + * Passed via provider to children of the SelectableTableRow + * @interface TableRowSelectHandler + */ +export interface TableRowSelectHandlerContext { + + /** + * Action to take on selection. A child of SelectableTableRow needs this + * because there needs to be a focusable element for keyboard users to use + * @memberof TableRowSelectHandler + */ + selectHandler: { + onSelect: () => void + } +} + +export const { + Provider: TableRowSelectHandlerProvider, + Consumer: TableRowSelectHandlerConsumer, + } = React.createContext<{ + onSelect: () => void + }>({} as any); + + +/** + * Connect any component to Formik context, and inject as a prop called `modal`; + * @param Comp React Component + */ +export function connectTableRowSelectHandler( + Comp: React.ComponentType + ) { + const C: React.SFC = (props: OuterProps) => ( + + {selectHandler => } + + ); + const componentDisplayName = + Comp.displayName || + 'Component'; + + // Assign Comp to C.WrappedComponent so we can access the inner component in tests + // For example, gets us + (C as React.SFC & { + WrappedComponent: React.ReactNode; + }).WrappedComponent = Comp; + + C.displayName = `TableRowSelectHandlerConnect(${componentDisplayName})`; + + return hoistNonReactStatics( + C, + Comp as React.ComponentClass // cast type to ComponentClass (even if SFC) + ); + } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 903db1be..205e12ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5026,6 +5026,16 @@ "es6-promise": "*" } }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dev": true, + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz", diff --git a/package.json b/package.json index a4cb5c71..43afabd0 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@types/enzyme": "^3.1.9", "@types/enzyme-to-json": "^1.5.1", "@types/es6-promise": "^3.3.0", + "@types/hoist-non-react-statics": "^3.3.1", "@types/jest": "^22.2.2", "@types/jquery": "^3.3.1", "@types/jsdom": "^11.0.4", @@ -110,6 +111,7 @@ "focus-group": "^0.3.1", "form-serialize": "0.7.0", "format-number": "2.0.2", + "hoist-non-react-statics": "^3.3.0", "i18n-js": "^3.0.3", "iban": "0.0.8", "imagesloaded": "4.1.1", From f38c757e06b308e0744d7578654a0ff6b44ea757 Mon Sep 17 00:00:00 2001 From: Eric Schultz Date: Wed, 1 May 2019 17:19:04 -0500 Subject: [PATCH 2/2] Update node-sass since we're here --- package-lock.json | 35 +++++++++++++++-------------------- package.json | 2 +- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 205e12ed..9f311b28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13673,12 +13673,6 @@ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -13748,12 +13742,6 @@ "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", "dev": true }, - "lodash.mergewith": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", - "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", - "dev": true - }, "lodash.restparam": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", @@ -14229,7 +14217,8 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", - "dev": true + "dev": true, + "optional": true }, "nanomatch": { "version": "1.2.9", @@ -14488,9 +14477,9 @@ } }, "node-sass": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", - "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", + "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==", "dev": true, "requires": { "async-foreach": "^0.1.3", @@ -14500,18 +14489,24 @@ "get-stdin": "^4.0.1", "glob": "^7.0.3", "in-publish": "^2.0.0", - "lodash.assign": "^4.2.0", - "lodash.clonedeep": "^4.3.2", - "lodash.mergewith": "^4.6.0", + "lodash": "^4.17.11", "meow": "^3.7.0", "mkdirp": "^0.5.1", - "nan": "^2.10.0", + "nan": "^2.13.2", "node-gyp": "^3.8.0", "npmlog": "^4.0.0", "request": "^2.88.0", "sass-graph": "^2.2.4", "stdout-stream": "^1.4.0", "true-case-path": "^1.0.2" + }, + "dependencies": { + "nan": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", + "dev": true + } } }, "nopt": { diff --git a/package.json b/package.json index 43afabd0..aeb0a4d4 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "less": "^3.0.4", "less-loader": "^4.1.0", "lodash": "^4.17.11", - "node-sass": "^4.11.0", + "node-sass": "^4.12.0", "phantomjs-prebuilt": "^2.1.16", "postcss-cssnext": "^2.9.0", "postcss-import": "^9.1.0",