Last active
March 21, 2019 15:38
-
-
Save Weffe/c69304a00e5f955a13cae65bbcb0a86d to your computer and use it in GitHub Desktop.
[TS] withValueField HoC for @progress/kendo-react-dropdowns
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from 'react'; | |
import { | |
DropDownList, | |
ComboBox, | |
ComboBoxProps, | |
DropDownListProps, | |
DropDownListBlurEvent, | |
ComboBoxBlurEvent, | |
DropDownListFocusEvent, | |
ComboBoxFocusEvent, | |
DropDownListChangeEvent, | |
ComboBoxChangeEvent, | |
ComboBoxPageChangeEvent, | |
DropDownListPageChangeEvent, | |
ComboBoxFilterChangeEvent, | |
DropDownListFilterChangeEvent | |
} from '@progress/kendo-react-dropdowns' | |
interface WithValueFieldProp { | |
valueField?: string; | |
} | |
// provide and indexing type so that in triggerEvent() on ln 66 & 67 | |
// TypeScript compiler doesn't throw an error at us | |
interface IndexSignature { | |
[key: string]: any; | |
} | |
const isPresent = (value: any) => value !== null && value !== undefined; | |
/** | |
* There was a breaking change for v2.0.0 (2018-09-12) in the kendo-ui-react | |
* component library for DropDownList and ComboBox components. | |
* This change saw the removal of the 'valueField' prop which I was using before | |
* the breaking change. They provided a HOC example to add the functionality | |
* back in. I got it working for TypeScript. | |
* | |
* @example const DropDownListWithValueField = withValueField<DropDownList>(DropDownList); | |
* | |
* @see https://www.telerik.com/kendo-react-ui/components/changelogs/ui-for-react/#release-12856530 | |
* @see https://www.telerik.com/kendo-react-ui/components/dropdowns/dropdownlist/common-scenarios/#toc-using-data-fields-for-values | |
*/ | |
export function withValueField<T = DropDownList>(WrappedDropdownComponent: React.ComponentType) { | |
type DropdownComponentType = T extends DropDownList | |
? typeof DropDownList | |
: typeof ComboBox; | |
type Props = T extends DropDownList | |
? DropDownListProps & WithValueFieldProp & IndexSignature | |
: ComboBoxProps & WithValueFieldProp & IndexSignature; | |
type BlurEvent = T extends DropDownList ? DropDownListBlurEvent : ComboBoxBlurEvent; | |
type FocusEvent = T extends DropDownList ? DropDownListFocusEvent : ComboBoxFocusEvent; | |
type ChangeEvent = T extends DropDownList ? DropDownListChangeEvent : ComboBoxChangeEvent; | |
type PageChangeEvent = T extends DropDownList ? DropDownListPageChangeEvent : ComboBoxPageChangeEvent; | |
type FilterChangeEvent = T extends DropDownList ? DropDownListFilterChangeEvent : ComboBoxFilterChangeEvent; | |
type EventType = | |
| BlurEvent | |
| FocusEvent | |
| ChangeEvent | |
| PageChangeEvent | |
| FilterChangeEvent; | |
return class DropdownComponentWithValueField extends React.Component<Props> { | |
public componentRef = React.createRef<DropdownComponentType>(); | |
public events: object = { | |
onBlur: (event: BlurEvent) => this.triggerEvent('onBlur', event), | |
onFocus: (event: FocusEvent) => this.triggerEvent('onFocus', event), | |
onChange: (event: ChangeEvent) => this.triggerEvent('onChange', event), | |
onPageChange: (event: PageChangeEvent) => this.triggerEvent('onPageChange', event), | |
onFilterChange: (event: FilterChangeEvent) => this.triggerEvent('onFilterChange', event) | |
}; | |
get value() { | |
if (this.componentRef) { | |
// we cast to any because TypeScript cannot infer that the value property will | |
// exist at runtime. This is one of the few cases where we as a developer have | |
// to override TypeScript since we know the property will exist no matter what. | |
const value = (this.componentRef as any).value; | |
return isPresent(value) ? value[this.props.valueField] : value; | |
} | |
} | |
public triggerEvent = (eventType: string, event: EventType) => { | |
if (this.props[eventType]) { | |
this.props[eventType].call(undefined, { | |
...event, | |
target: this | |
}); | |
} | |
} | |
public itemFromValue = (value: any) => { | |
const { data = [], valueField } = this.props; | |
return isPresent(value) ? | |
data.find((item: any) => item[valueField] === value) : value; | |
} | |
public render() { | |
return ( | |
<WrappedDropdownComponent | |
{...this.props} | |
value={this.itemFromValue(this.props.value)} | |
defaultValue={this.itemFromValue(this.props.defaultValue)} | |
ref={this.componentRef} | |
{...this.events} | |
/> | |
); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment