Skip to content

Commit

Permalink
✨ [feature] 添加Autocomplete,Tirgger组件测试.
Browse files Browse the repository at this point in the history
  • Loading branch information
lanjingling0510 committed Apr 14, 2017
1 parent 710d53e commit 04cef46
Show file tree
Hide file tree
Showing 13 changed files with 388 additions and 61 deletions.
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
[options]
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
suppress_comment= \\(.\\|\n\\)*\\$FlowIgnore
74 changes: 42 additions & 32 deletions components/autocomplete/Autocomplete.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
import React, { Component, PropTypes } from 'react';
// @flow
import React from 'react';
import pureRender from '../decorator/pureRender.js';
import clamp from 'ramda/src/clamp';

const factory = (Trigger, Input, Menu, MenuItem) => {
class Autocomplete extends Component {

static propTypes = {
align: PropTypes.object,
value: PropTypes.string,
defaultValue: PropTypes.string,
dataSource: PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape({
label: PropTypes.string,
value: PropTypes.string,
})
])),
onSelect: PropTypes.func,
onChange: PropTypes.func,
children: PropTypes.node,
theme: PropTypes.shape({
menu: PropTypes.string,
menuItem: PropTypes.string,
selected: PropTypes.string,
})
}
type Theme = {
menu: string,
menuItem: string,
selected: string,
}

type Component = Class<React.Component<{}, {}, mixed>>;

type SourceItem = {label: string, value: string};

type Props = {
align: {},
value: string,
defaultValue: string,
dataSource: Array<SourceItem>,
onSelect: () => void,
onChange: () => void,
children: React.Element<*>,
theme: Theme,
}

type State = {
open: boolean,
selectedItem: number,
}

type DefaultProps = {
onSelect: () => void,
onChange: () => void,
dataSource: Array<SourceItem>,
align: {},
}

const factory = (Trigger: Component, Input: Component, Menu: Component, MenuItem: Component) => {
class Autocomplete extends React.Component<DefaultProps, Props, State> {

static defaultProps = {
onChange: () => void 0,
Expand All @@ -37,13 +49,9 @@ const factory = (Trigger, Input, Menu, MenuItem) => {
}
}

constructor(props) {
super(props);

this.state = {
open: false,
selectedItem: -1,
}
state: State = {
open: false,
selectedItem: -1,
}

componentWillReceiveProps(nextProps, nextState) {
Expand Down Expand Up @@ -84,6 +92,7 @@ const factory = (Trigger, Input, Menu, MenuItem) => {
const {dataSource} = this.props;
const {selectedItem} = this.state;

// 按回车
if (event.which === 13 && selectedItem !== -1) {
this.handleMenuItemClick(dataSource[selectedItem])();
}
Expand All @@ -92,6 +101,7 @@ const factory = (Trigger, Input, Menu, MenuItem) => {
handleInputKeyUp = event => {
const {dataSource} = this.props;

// 按上下键
if ([40, 38].indexOf(event.which) !== -1) {
const selectRange = clamp(0, dataSource.length - 1);
const selectedItem = selectRange(this.state.selectedItem + (event.which === 40 ? +1 : -1));
Expand Down
103 changes: 103 additions & 0 deletions components/autocomplete/__test__/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React from 'react';
import {mount} from 'enzyme';
import Autocomplete from '../../autocomplete';

const dataSource = [
{label: 'Tom', name: 'Tom'},
{label: 'Jack', name: 'Jack'},
{label: 'Lizy', name: 'Lizy'},
];

describe('Autocomplete', () => {
let props;
let mountedAutocomplete;

const autocomplete = () => {
if (!mountedAutocomplete) {
mountedAutocomplete = mount(<Autocomplete {...props}/>);
}
return mountedAutocomplete;
}

beforeEach(() => {
props = {
align: undefined,
value: undefined,
defaultValue: undefined,
dataSource: undefined,
onSelect: undefined,
onChange: undefined,
children: undefined,
theme: undefined,
};

mountedAutocomplete = undefined;
});

//
it('总是渲染一个Input组件', () => {
expect(autocomplete().find('Input').length).toBeGreaterThan(0);
});

//
describe('被渲染的Input组件', () => {

it('接受onKeyDown属性', () => {
expect(autocomplete().find('Input').props()).toHaveProperty('onKeyDown');
});

it('接受onKeyUp属性', () => {
expect(autocomplete().find('Input').props()).toHaveProperty('onKeyUp');
});

it('当传入value,接受value属性', () => {
props.value = '10';
expect(autocomplete().find('Input').props()).toHaveProperty('value');
});

it('当不传入value,不接受value属性', () => {
expect(autocomplete().find('Input').prop('value')).toBeUndefined();
});

it('当传入defaultvalue,接受defaultvalue属性', () => {
props.defaultValue = '10';
expect(autocomplete().find('Input').props()).toHaveProperty('defaultValue');
});

it('当不传入defaultvalue,不接受defaultvalue属性', () => {
expect(autocomplete().find('Input').prop('defaultValue')).toBeUndefined();
});
});

//
describe('当设置dataSource时', () => {

it('值为空数组,Trigger组件popupVisible为false', () => {
props.dataSource = dataSource;
autocomplete().setProps({dataSource: []});
autocomplete().find('Input').first().defaultValue = 'tom';
expect(autocomplete().find('Trigger').prop('popupVisible')).toBe(false);
});

it('值不为空数组,Trigger组件popupVisible为true', () => {
props.dataSource = [];
autocomplete().setProps({dataSource: dataSource});
autocomplete().find('Input').first().defaultValue = 'tom';
expect(autocomplete().find('Trigger').prop('popupVisible')).toBe(true);
});
});

//
describe('当设置onChange时', () => {
beforeEach(() => {
props.onChange = jest.fn();
});

it('和被渲染的Input的onChange属性相等', () => {
const input = autocomplete().find('Input');
expect(input.props().onChange).toBe(props.onChange);
});

});

});
42 changes: 42 additions & 0 deletions components/badge/__test__/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import {mount} from 'enzyme';
import Button from '../../button';
import Badge from '../index';

const BadgeButton = Badge(Button);

describe('Badge', () => {
let props;
let mountedBadge;

const badge = () => {
if (!mountedBadge) {
mountedBadge = mount(<BadgeButton {...props}/>);
}
return mountedBadge;
}

beforeEach(() => {
props = {
count: undefined,
text: undefined,
dot: undefined,
showZero: undefined,
overflowCount: undefined,
className: undefined,
theme: undefined
};

mountedBadge = undefined;
});

describe('当传入属性count等于10', () => {

it('渲染正确的数字', () => {
props.count = '10';
expect(badge().find('sup').text()).toBe('10');
});

});

});
16 changes: 10 additions & 6 deletions components/button/Button.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// flow
// @flow

import React, { Component } from 'react';
import React from 'react';
import classnames from 'classnames';
import rippleFactory from '../ripple/Ripple';
import FontIcon from '../font_icon';

type Component = Class<React.Component<{}, {}, mixed>>;

type Theme = {
accent: string,
button: string,
Expand All @@ -17,6 +19,10 @@ type Theme = {
raised: string,
rippleWrapper: string,
toggle: string,
normal: string,
large: string,
small: string,
fullWidth: string,
};

type DefaultProps = {
Expand Down Expand Up @@ -55,11 +61,9 @@ type Props = {
type: string,
};

type State = empty;


const factory = (ripple, FontIcon) => {
class Button extends Component<DefaultProps, Props, State> {
const factory = (ripple: () => void, FontIcon: Component) => {
class Button extends React.Component<DefaultProps, Props, *> {

static defaultProps = {
accent: false,
Expand Down
14 changes: 8 additions & 6 deletions components/button/ButtonGroup.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// flow
// @flow

import React, { Component } from 'react';
import React from 'react';
import classnames from 'classnames';

type Component = Class<React.Component<{}, {}, mixed>>;

type Theme = {
buttonGroup: string,
}
Expand All @@ -12,16 +14,16 @@ type DefaultProps = {
}

type Props = {
children: React.Element<*>,
children: Array<React.Element<*>>,
size: 'small' | 'normal' | 'large',
theme: Theme,
}

const factory = (Button: React.Class<*>) => {
const factory = (Button: Component) => {

const isButton = (child: React.Element<*>): boolean => child.type === Button;

class ButtonGroup extends Component<DefaultProps, Props, State> {
class ButtonGroup extends React.Component<DefaultProps, Props, *> {

static defaultProps = {
size: 'normal',
Expand All @@ -44,7 +46,7 @@ const factory = (Button: React.Class<*>) => {
children,
} = this.props;

const classes: object = classnames(theme.buttonGroup);
const classes = classnames(theme.buttonGroup);

return (
<div className={classes}>
Expand Down
16 changes: 11 additions & 5 deletions components/popup/Popup.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//flow
// @flow

import React from 'react';
import { findDOMNode } from 'react-dom';
Expand Down Expand Up @@ -39,6 +39,8 @@ type Props = {
// 请求关闭popup
onRequestClose: () => void,

onOverlayClick: () => void,

theme: Theme,
}

Expand All @@ -50,14 +52,18 @@ class Popup extends React.Component<DefaultProps, Props, any> {
matchTargetWidth: false,
}

// 控制document事件的监听
clickOutsideHandler: boolean;

componentDidMount() {
const { active, align, matchTargetWidth, getRootDomNode } = this.props;
const source = this.getPopupDomNode();
const target = getRootDomNode && getRootDomNode();

if (target) {
if (target && source) {
// set popup width
const widthProp = matchTargetWidth ? 'width' : 'minWidth';
// $FlowIgnore
source.style[widthProp] = `${target.offsetWidth}px`;

// set popup position
Expand All @@ -72,7 +78,7 @@ class Popup extends React.Component<DefaultProps, Props, any> {
}
}

componentDidUpdate(prevProps, prevState) {
componentDidUpdate(prevProps: Props, prevState: any) {
if (this.props.active && !this.clickOutsideHandler) {
this.clickOutsideHandler = true;
events.addEventsToDocument({
Expand All @@ -92,11 +98,11 @@ class Popup extends React.Component<DefaultProps, Props, any> {
});
}

setPopupAlign = (sourceNode, targetNode, popupAlign) => {
setPopupAlign = (sourceNode: Element | Text, targetNode: Element | Text, popupAlign: {}) => {
domAlign(sourceNode, targetNode, popupAlign);
}

onDocumentClick = (event) => {
onDocumentClick = (event: MouseEvent) => {
if (this.props.mask && !this.props.maskClosable) {
return;
}
Expand Down
Loading

0 comments on commit 04cef46

Please sign in to comment.