Skip to content

Commit

Permalink
REFACTOR: migrate DropDown component to TS
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesAlias committed Dec 6, 2018
1 parent 1d44d35 commit b0dd010
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ exports[`<DropDownWrapper/> should render correctly. 1`] = `
onClose={[Function]}
onToggle={[Function]}
style="default"
theme={Object {}}
theme={
Object {
"dropDown": "dropDownClassName",
"dropDown--padded": "paddedClassName",
"dropDown__btn": "btnClassName",
}
}
>
Foo children
</clickOutside(StatelessDropDownWrapperWithoutClickOutsideBehavior)>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
import {themr} from '@friendsofreactjs/react-css-themr';
import identifiers from '../identifiers';
import style from './style.css';

import identifiers from '@neos-project/react-ui-components/src/identifiers';
import ContextDropDownWrapper, {
StatelessDropDownWrapper,
ContextDropDownHeader,
ContextDropDownContents
} from './wrapper';
} from '@neos-project/react-ui-components/src/DropDown/wrapper';

import style from './style.css';

const DropDown = themr(identifiers.dropDown, style)(ContextDropDownWrapper);
const StatelessDropDown = themr(identifiers.dropDown, style)(StatelessDropDownWrapper);
const DropDownHeader = themr(identifiers.dropDownHeader, style)(ContextDropDownHeader);
const DropDownContents = themr(identifiers.dropDownContents, style)(ContextDropDownContents);

//
// Dependency injection
//
import injectProps from './../_lib/injectProps';
import Icon from './../Icon';

DropDown.Header = injectProps({
IconComponent: Icon
})(DropDownHeader);
// @ts-ignore
DropDown.Header = DropDownHeader;
// @ts-ignore
DropDown.Contents = DropDownContents;
// @ts-ignore
DropDown.Stateless = StatelessDropDown;

export default DropDown;
5 changes: 4 additions & 1 deletion packages/react-ui-components/src/DropDown/wrapper.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import {shallow} from 'enzyme';
import toJson from 'enzyme-to-json';

import {DropDownWrapper, DropDownWrapperProps, defaultProps} from '@neos-project/react-ui-components/src/DropDown/wrapper';
import {DropDownWrapper, DropDownWrapperProps, defaultProps} from './wrapper';

describe('<DropDownWrapper/>', () => {
const props: DropDownWrapperProps = {
Expand Down Expand Up @@ -36,17 +36,20 @@ describe('<DropDownWrapper/>', () => {
it('should set the "isOpen" state value to opposite when calling the toggle method.', () => {
const wrapper = shallow(<DropDownWrapper {...props}/>);

// @ts-ignore
wrapper.instance().handleToggle();

expect(wrapper.state('isOpen')).toBe(true);

// @ts-ignore
wrapper.instance().handleToggle();

expect(wrapper.state('isOpen')).toBe(false);
});
it('should set the "isOpen" state value to false when calling the close method.', () => {
const wrapper = shallow(<DropDownWrapper {...props}/>);

// @ts-ignore
wrapper.instance().handleClose();

expect(wrapper.state('isOpen')).toBe(false);
Expand Down
90 changes: 43 additions & 47 deletions packages/react-ui-components/src/DropDown/wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import enhanceWithClickOutside from 'react-click-outside';
import { PickDefaultProps } from '../../types';
import ShallowDropDownHeader from './header';
import ShallowDropDownContents from './contents';
import PropTypes from 'prop-types';

export interface DropDownWrapperProps {
/**
Expand Down Expand Up @@ -60,33 +61,6 @@ interface DropDownWrapperState {
readonly isOpen: boolean;
}

export default class DropDownWrapper extends PureComponent<DropDownWrapperProps, DropDownWrapperState> {
public static readonly defaultProps = defaultProps;

constructor(props: DropDownWrapperProps) {
super(props);
this.state = {
isOpen: props.isOpen
};
}

public render(): JSX.Element {
return <StatelessDropDownWrapper {...this.props} isOpen={this.state.isOpen} onToggle={this.handleToggle} onClose={this.handleClose}/>;
}

private readonly handleToggle = (event: MouseEvent) => {
if (this.props.onToggle) {
this.props.onToggle(event);
}

this.setState({isOpen: !this.state.isOpen});
}

private readonly handleClose = () => {
this.setState({isOpen: false});
}
}

export interface StatelessDropDownWrapperWithoutClickOutsideBehaviorProps extends DropDownWrapperProps {
onToggle: (event: MouseEvent) => void;
onClose: (event?: MouseEvent) => void;
Expand All @@ -100,7 +74,12 @@ export interface ChildContext {
class StatelessDropDownWrapperWithoutClickOutsideBehavior extends PureComponent<StatelessDropDownWrapperWithoutClickOutsideBehaviorProps> {
public static readonly defaultProps = defaultProps;

public getChildContext = (): ChildContext => ({
public static readonly childContextTypes = {
toggleDropDown: PropTypes.func.isRequired,
closeDropDown: PropTypes.func.isRequired,
};

public readonly getChildContext = (): ChildContext => ({
toggleDropDown: this.handleToggle,
closeDropDown: this.handleClose,
})
Expand All @@ -125,16 +104,11 @@ class StatelessDropDownWrapperWithoutClickOutsideBehavior extends PureComponent<

return (
<div {...rest} className={finalClassName}>
{React.Children.map(children, child => {
if (React.isValidElement<{context: ChildContext}>(child)) {
return React.cloneElement(child, {
...child.props,
context: this.getChildContext()
});
} else {
return child;
}
})}
{React.Children.map(
children,
// @ts-ignore
child => typeof child.type === 'string' ? child : <child.type {...child.props} isDropdownOpen={this.props.isOpen}/>
)}
</div>
);
}
Expand All @@ -161,13 +135,41 @@ class StatelessDropDownWrapperWithoutClickOutsideBehavior extends PureComponent<
export const StatelessDropDownWrapper = enhanceWithClickOutside(StatelessDropDownWrapperWithoutClickOutsideBehavior);


export class DropDownWrapper extends PureComponent<DropDownWrapperProps, DropDownWrapperState> {
public static readonly defaultProps = defaultProps;

constructor(props: DropDownWrapperProps) {
super(props);
this.state = {
isOpen: props.isOpen
};
}

public render(): JSX.Element {
return <StatelessDropDownWrapper {...this.props} isOpen={this.state.isOpen} onToggle={this.handleToggle} onClose={this.handleClose}/>;
}

private readonly handleToggle = (event: MouseEvent) => {
if (this.props.onToggle) {
this.props.onToggle(event);
}

this.setState({isOpen: !this.state.isOpen});
}

private readonly handleClose = () => {
this.setState({isOpen: false});
}
}

export default DropDownWrapper;

export interface ContextDropDownProps extends DropDownWrapperProps {
isDropdownOpen?: boolean;
}

export class ContextDropDownHeader extends PureComponent<ContextDropDownProps> {
public static contextTypes = {
public static readonly contextTypes = {
toggleDropDown: PropTypes.func.isRequired
};

Expand All @@ -177,15 +179,9 @@ export class ContextDropDownHeader extends PureComponent<ContextDropDownProps> {
return <ShallowDropDownHeader isOpen={isDropdownOpen} {...rest} {...this.context}/>;
}
}
export class ContextDropDownContents extends PureComponent<ContextDropDownProps> {
public static propTypes = {
/**
* The propagated isOpen state from the dropDown
*/
isDropdownOpen: PropTypes.bool
};

public static contextTypes = {
export class ContextDropDownContents extends PureComponent<ContextDropDownProps> {
public static readonly contextTypes = {
closeDropDown: PropTypes.func.isRequired
};

Expand Down

0 comments on commit b0dd010

Please sign in to comment.