Merge pull request #189 from houdiniproject/add_selectable_table_row

Add SelectableTableRow component
This commit is contained in:
Eric Schultz 2019-05-02 10:33:04 -05:00 committed by GitHub
commit 231a371fed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 164 additions and 21 deletions

View file

@ -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 <td></td>;
}
}
const ReceivedComponent = connectTableRowSelectHandler(TestReceivedProviderComponent)
describe('SelectableTableRow', () => {
let providerAndRow: ReactWrapper
let onSelect: any;
beforeEach(() => {
onSelect = jest.fn()
providerAndRow = mount(<table><tbody><SelectableTableRow onSelect={onSelect}>
<ReceivedComponent m={"something"}/>
</SelectableTableRow></tbody></table>)
})
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)
})
})

View file

@ -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<SelectableTableRowProps, {}>
*/
class SelectableTableRow extends React.Component<SelectableTableRowProps, {}> {
render() {
return <TableRowSelectHandlerProvider value={{onSelect: this.props.onSelect}}>
<tr onClick={this.props.onSelect}>
{this.props.children}
</tr>
</TableRowSelectHandlerProvider>;
}
}
export default SelectableTableRow

View file

@ -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<OuterProps>(
Comp: React.ComponentType<OuterProps & TableRowSelectHandlerContext>
) {
const C: React.SFC<OuterProps> = (props: OuterProps) => (
<TableRowSelectHandlerConsumer>
{selectHandler => <Comp {...props} selectHandler={selectHandler} />}
</TableRowSelectHandlerConsumer>
);
const componentDisplayName =
Comp.displayName ||
'Component';
// Assign Comp to C.WrappedComponent so we can access the inner component in tests
// For example, <Field.WrappedComponent /> gets us <FieldInner/>
(C as React.SFC<OuterProps> & {
WrappedComponent: React.ReactNode;
}).WrappedComponent = Comp;
C.displayName = `TableRowSelectHandlerConnect(${componentDisplayName})`;
return hoistNonReactStatics(
C,
Comp as React.ComponentClass<OuterProps & TableRowSelectHandlerContext> // cast type to ComponentClass (even if SFC)
);
}

45
package-lock.json generated
View file

@ -5026,6 +5026,16 @@
"es6-promise": "*" "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": { "@types/istanbul-lib-coverage": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz",
@ -13663,12 +13673,6 @@
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
"dev": true "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": { "lodash.defaults": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
@ -13738,12 +13742,6 @@
"integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=",
"dev": true "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": { "lodash.restparam": {
"version": "3.6.1", "version": "3.6.1",
"resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
@ -14219,7 +14217,8 @@
"version": "2.10.0", "version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
"dev": true "dev": true,
"optional": true
}, },
"nanomatch": { "nanomatch": {
"version": "1.2.9", "version": "1.2.9",
@ -14478,9 +14477,9 @@
} }
}, },
"node-sass": { "node-sass": {
"version": "4.11.0", "version": "4.12.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz",
"integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"async-foreach": "^0.1.3", "async-foreach": "^0.1.3",
@ -14490,18 +14489,24 @@
"get-stdin": "^4.0.1", "get-stdin": "^4.0.1",
"glob": "^7.0.3", "glob": "^7.0.3",
"in-publish": "^2.0.0", "in-publish": "^2.0.0",
"lodash.assign": "^4.2.0", "lodash": "^4.17.11",
"lodash.clonedeep": "^4.3.2",
"lodash.mergewith": "^4.6.0",
"meow": "^3.7.0", "meow": "^3.7.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"nan": "^2.10.0", "nan": "^2.13.2",
"node-gyp": "^3.8.0", "node-gyp": "^3.8.0",
"npmlog": "^4.0.0", "npmlog": "^4.0.0",
"request": "^2.88.0", "request": "^2.88.0",
"sass-graph": "^2.2.4", "sass-graph": "^2.2.4",
"stdout-stream": "^1.4.0", "stdout-stream": "^1.4.0",
"true-case-path": "^1.0.2" "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": { "nopt": {

View file

@ -22,6 +22,7 @@
"@types/enzyme": "^3.1.9", "@types/enzyme": "^3.1.9",
"@types/enzyme-to-json": "^1.5.1", "@types/enzyme-to-json": "^1.5.1",
"@types/es6-promise": "^3.3.0", "@types/es6-promise": "^3.3.0",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/jest": "^22.2.2", "@types/jest": "^22.2.2",
"@types/jquery": "^3.3.1", "@types/jquery": "^3.3.1",
"@types/jsdom": "^11.0.4", "@types/jsdom": "^11.0.4",
@ -65,7 +66,7 @@
"less": "^3.0.4", "less": "^3.0.4",
"less-loader": "^4.1.0", "less-loader": "^4.1.0",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"node-sass": "^4.11.0", "node-sass": "^4.12.0",
"phantomjs-prebuilt": "^2.1.16", "phantomjs-prebuilt": "^2.1.16",
"postcss-cssnext": "^2.9.0", "postcss-cssnext": "^2.9.0",
"postcss-import": "^9.1.0", "postcss-import": "^9.1.0",
@ -110,6 +111,7 @@
"focus-group": "^0.3.1", "focus-group": "^0.3.1",
"form-serialize": "0.7.0", "form-serialize": "0.7.0",
"format-number": "2.0.2", "format-number": "2.0.2",
"hoist-non-react-statics": "^3.3.0",
"i18n-js": "^3.0.3", "i18n-js": "^3.0.3",
"iban": "0.0.8", "iban": "0.0.8",
"imagesloaded": "4.1.1", "imagesloaded": "4.1.1",