Merge pull request #189 from houdiniproject/add_selectable_table_row
Add SelectableTableRow component
This commit is contained in:
commit
231a371fed
5 changed files with 164 additions and 21 deletions
|
@ -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)
|
||||||
|
})
|
||||||
|
})
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
45
package-lock.json
generated
|
@ -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": {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue