Add SelectableTableRow component

This commit is contained in:
Eric Schultz 2019-05-01 17:17:15 -05:00
parent d67e489ccb
commit 1a1f52918c
5 changed files with 148 additions and 0 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)
);
}

10
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",

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",
@ -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",