Skip to content

Commit

Permalink
feat: 增加 Select 组件
Browse files Browse the repository at this point in the history
  • Loading branch information
lijinke666 committed Oct 12, 2018
1 parent 77e0424 commit bed843d
Show file tree
Hide file tree
Showing 11 changed files with 446 additions and 60 deletions.
23 changes: 3 additions & 20 deletions components/cityPicker/styles.less
Original file line number Diff line number Diff line change
@@ -1,33 +1,27 @@
@import "../styles/vars.less";

@prefixCls: cuke-city-picker;
@corePrefixCls : cuke-city-picker-core;

.@{prefixCls} {
position: relative;

display: inline-block;
&-inner {
display: inline-block;
position: relative;
cursor: pointer;
> .cuke-input:not(.cuke-input-disabled) {
>.cuke-input:not(.cuke-input-disabled) {
cursor: pointer;
}
}

&-open {
animation: cuke-picker-open @default-transition forwards;
}

&-close {
animation: cuke-picker-close @default-transition forwards;
pointer-events: none;
}

&-input {
width: @cuke-city-picker-width - 180px;
}

&-content {
position: absolute;
left: 0;
Expand All @@ -36,7 +30,6 @@
transform: scale(0);
z-index: @cuke-city-picker-z-index;
}

&-arrow {
position: absolute;
right: 20px;
Expand Down Expand Up @@ -64,13 +57,11 @@
min-height: 221px;
background-color: #fff;
padding: 5px 17px 10px 17px;

&-header {
height: @address-select-section-header-height;
line-height: @address-select-section-header-height;
color: @font-color;
position: relative;

&::before {
content: ' ';
position: absolute;
Expand All @@ -82,7 +73,6 @@
left: 0;
top: 100%;
}

&-wrap {
display: flex;
align-items: center;
Expand All @@ -91,14 +81,11 @@
cursor: pointer;
transition: all @default-transition;
position: relative;

&:not(.@{corePrefixCls}-disabled):hover {
color: @primary-color;
}

&.@{corePrefixCls}-active {
color: @primary-color;

&::after {
content: ' ';
position: absolute;
Expand All @@ -113,28 +100,24 @@
}
}
&-content {

&-wrap {
padding-top: 25px;
display: flex;
flex-wrap: wrap;
align-items: center;

.city {
cursor: pointer;
margin-bottom: 16px;
transition: all @default-transition;
width: 20%;

&:hover {
color: @primary-color;
}

&.selected {
color: @primary-color;
}
}
}
}
}
}
}
1 change: 1 addition & 0 deletions components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ export { default as Switch } from "./switch";
export { default as Tag } from "./tag";
export { default as CityPicker } from "./cityPicker";
export { default as Collapse } from "./collapse";
export { default as Select } from "./select";

export { default as version } from "./version";
92 changes: 92 additions & 0 deletions components/select/Select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import cls from "classnames";
import Input from "../input";
import { DownIcon } from "../icon";

export default class Select extends PureComponent {
state = {
selectedValue: this.props.defaultValue || this.props.value || "",
visible: false
};
static defaultProps = {
prefixCls: "cuke-select",
onPanelVisibleChange: () => {},
onChange: () => {}
};
static propTypes = {
prefixCls: PropTypes.string.isRequired,
onPanelVisibleChange: PropTypes.func,
onChange: PropTypes.func,
overlay: PropTypes.oneOfType([
PropTypes.element,
PropTypes.string,
PropTypes.object
])
};
constructor(props) {
super(props);
}

onOpenOptionPanel = () => {
this.setState({ visible: true });
this.props.onPanelVisibleChange(true);
};

onCloseOptionPanel = () => {
setTimeout(() => {
this.setState({ visible: false });
this.props.onPanelVisibleChange(false);
}, 100);
};

onChange = value => {
this.setState({ selectedValue: value });
this.props.onChange(value);
};
render() {
const { visible } = this.state;
const {
prefixCls,
className,
disabled,
placeholder,
children,
onPanelVisibleChange, //eslint-disable-line
...attr
} = this.props;

const { selectedValue } = this.state;

return (
<div className={cls(`${prefixCls}`, className)} {...attr}>
<div className={cls(`${prefixCls}-inner`)}>
<Input
disabled={disabled}
readonly
placeholder={placeholder}
className={cls(`${prefixCls}-input`)}
value={selectedValue}
onFocus={this.onOpenOptionPanel}
onBlur={this.onCloseOptionPanel}
/>
<DownIcon className={`${prefixCls}-arrow`} />
</div>
<div
className={cls(`${prefixCls}-content`, {
[`${prefixCls}-open`]: visible,
[`${prefixCls}-close`]: !visible
})}
>
{React.Children.map(children, (element, index) => {
return React.cloneElement(element, {
key: index,
selectedValue,
onChange: this.onChange
});
})}
</div>
</div>
);
}
}
44 changes: 44 additions & 0 deletions components/select/SelectOption.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from "react";
import cls from "classnames";
import PropTypes from "prop-types";

export default class Option extends React.PureComponent {
static defaultProps = {
prefixCls: "cuke-select-option",
disabled: false,
value: ""
};
static propTypes = {
prefixCls: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};
onClick = value => {
if (this.props.onChange) {
this.props.onChange(value);
}
};
render() {
const {
children,
className,
prefixCls,
disabled,
value,
selectedValue,
...attr
} = this.props;

return (
<div
className={cls(prefixCls, className, {
[`${prefixCls}-selected`]: selectedValue === value,
[`${prefixCls}-disabled`]: disabled
})}
onClick={disabled ? undefined : () => this.onClick(value)}
{...attr}
>
{children}
</div>
);
}
}
54 changes: 54 additions & 0 deletions components/select/__tests__/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<Select/> should render a <Select/> components 1`] = `
<div
class="cuke-select"
>
<div
class="cuke-select-inner"
>
<input
class="cuke-input cuke-select-input"
placeholder=""
readonly=""
type="text"
value=""
/>
<svg
attr="[object Object]"
class="undefined cuke-select-arrow"
fill="none"
height="1em"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="1em"
>
<polyline
points="6 9 12 15 18 9"
/>
</svg>
</div>
<div
class="cuke-select-content cuke-select-close"
>
<div
class="cuke-select-option"
>
黄瓜
</div>
<div
class="cuke-select-option"
>
茄子
</div>
<div
class="cuke-select-option"
>
番茄
</div>
</div>
</div>
`;
67 changes: 67 additions & 0 deletions components/select/__tests__/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from "react";
import assert from "power-assert";
import { render, shallow, mount } from "enzyme";
import toJson from "enzyme-to-json";
import Select from "../index";

describe("<Select/>", () => {
it("should render a <Select/> components", () => {
const wrapper = render(
<Select>
<Select.Option value="黄瓜">黄瓜</Select.Option>
<Select.Option value="茄子">茄子</Select.Option>
<Select.Option value="番茄">番茄</Select.Option>
</Select>
);
expect(toJson(wrapper)).toMatchSnapshot();
});

it("should find cuke-select classnames", () => {
const wrapper = mount(
<Select>
<Select.Option value="黄瓜">黄瓜</Select.Option>
<Select.Option value="茄子">茄子</Select.Option>
<Select.Option value="番茄">番茄</Select.Option>
</Select>
);
assert(wrapper.find(".cuke-select").length >= 1);
assert(wrapper.find(".cuke-select-option").length >= 1);
});

it("should cannot click when disabled", () => {
const onChange = jest.fn();
const wrapper = shallow(
<Select disabled onChange={onChange}>
<Select.Option value="黄瓜">黄瓜</Select.Option>
<Select.Option value="茄子">茄子</Select.Option>
<Select.Option value="番茄">番茄</Select.Option>
</Select>
);
wrapper.find(".cuke-select").simulate("click");
expect(onChange).not.toHaveBeenCalled();
});

it("should render init default value", () => {
const wrapper = shallow(
<Select defaultValue="黄瓜">
<Select.Option value="黄瓜">黄瓜</Select.Option>
<Select.Option value="茄子">茄子</Select.Option>
<Select.Option value="番茄">番茄</Select.Option>
</Select>
);
wrapper.find(".cuke-select").simulate("click");
assert(wrapper.state().selectedValue === "黄瓜");
});

it("should render init value", () => {
const wrapper = shallow(
<Select value="黄瓜">
<Select.Option value="黄瓜">黄瓜</Select.Option>
<Select.Option value="茄子">茄子</Select.Option>
<Select.Option value="番茄">番茄</Select.Option>
</Select>
);
wrapper.find(".cuke-select").simulate("click");
assert(wrapper.state().selectedValue === "黄瓜");
});
});
6 changes: 6 additions & 0 deletions components/select/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Select from "./Select";
import SelectOption from "./SelectOption";

Select.Option = SelectOption;

export default Select;
Loading

0 comments on commit bed843d

Please sign in to comment.