├── .yo-rc.json
├── .eslintignore
├── coverage
└── lcov-report
│ ├── sort-arrow-sprite.png
│ ├── prettify.css
│ ├── src
│ ├── index.js.html
│ ├── i18n.js.html
│ ├── index.html
│ ├── utils.js.html
│ ├── TreeSelect.js.html
│ └── RightTreeNode.jsx.html
│ ├── index.html
│ ├── sorter.js
│ ├── base.css
│ └── prettify.js
├── tests
├── index.js
├── const.js
├── utils.spec.js
├── Select.rightTree.spec.js
├── Select.multiple.spec.js
├── Select.checkable.spec.js
└── TreeSelect.spec.js
├── src
├── index.js
├── i18n.js
├── TreeSelect.js
├── utils.js
├── RightTreeNode.jsx
├── SelectTrigger.jsx
└── TreeSelect.less
├── demo
├── index.js
├── TreeSelectDemo.less
└── TreeSelectDemo.js
├── .gitignore
├── .npmignore
├── .eslintrc.json
├── index.html
├── .travis.yml
├── HISTORY.md
├── package.json
└── README.md
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-uxcore": {}
3 | }
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | demo/
2 | build/
3 | mock/
4 | dist/
--------------------------------------------------------------------------------
/coverage/lcov-report/sort-arrow-sprite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WisestCoder/uxcore-tree-select/master/coverage/lcov-report/sort-arrow-sprite.png
--------------------------------------------------------------------------------
/tests/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * only require other specs here
3 | */
4 |
5 | const req = require.context('.', false, /\.spec\.js(x)?$/);
6 | req.keys().forEach(req);
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * TreeSelect Component for uxcore
3 | * @author biangang.bg
4 | *
5 | * Copyright 2014-2015, Uxcore Team, Alinw.
6 | * All rights reserved.
7 | */
8 |
9 | module.exports = require('./TreeSelect');
10 |
--------------------------------------------------------------------------------
/demo/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * TreeSelect Component Demo for uxcore
3 | * @author biangang.bg
4 | *
5 | * Copyright 2014-2015, Uxcore Team, Alinw.
6 | * All rights reserved.
7 | */
8 |
9 | var Demo = require('./TreeSelectDemo');
10 | ReactDOM.render(, document.getElementById('UXCoreDemo'));
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | *.log
3 | .idea/
4 | .ipr
5 | .iws
6 | *~
7 | ~*
8 | *.diff
9 | *.patch
10 | *.bak
11 | .DS_Store
12 | Thumbs.db
13 | .project
14 | .*proj
15 | .svn/
16 | *.swp
17 | *.swo
18 | *.pyc
19 | *.pyo
20 | .build
21 | node_modules
22 | _site
23 | sea-modules
24 | spm_modules
25 | .cache
26 | .happypack
27 | dist
28 | build
29 | assets/**/*.css
30 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | *.cfg
3 | node_modules/
4 | nohup.out
5 | *.iml
6 | .idea/
7 | .ipr
8 | .iws
9 | *~
10 | ~*
11 | *.diff
12 | *.log
13 | *.patch
14 | *.bak
15 | .DS_Store
16 | Thumbs.db
17 | .project
18 | .*proj
19 | .svn/
20 | *.swp
21 | out/
22 | .build
23 | .happypack
24 | node_modules
25 | _site
26 | sea-modules
27 | spm_modules
28 | .cache
29 | dist
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "plugins": [
4 | "react"
5 | ],
6 | "env": {
7 | "browser": true
8 | },
9 | "rules": {
10 | "import/no-extraneous-dependencies": "off",
11 | "react/jsx-no-bind": "off",
12 | "no-underscore-dangle": ["off"],
13 | "arrow-body-style": ["off"]
14 | },
15 | "parser": "babel-eslint"
16 | }
17 |
--------------------------------------------------------------------------------
/src/i18n.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'zh-cn': {
3 | placeholder: '请选择',
4 | confirm: '确定',
5 | alreadyChoosed: '已选择',
6 | pleaseSelectFromLeft: '请从左侧选择',
7 | clear: '清空',
8 | delete: '删除',
9 | all: '全选',
10 | },
11 | 'en-us': {
12 | placeholder: 'Please select',
13 | confirm: 'OK',
14 | alreadyChoosed: 'Choosed',
15 | pleaseSelectFromLeft: 'Please select from left',
16 | clear: 'clear',
17 | delete: 'Delete',
18 | all: 'All',
19 | },
20 | };
21 |
--------------------------------------------------------------------------------
/tests/const.js:
--------------------------------------------------------------------------------
1 | const options = [{
2 | value: 'zhejiang',
3 | label: '浙江',
4 | children: [{
5 | value: 'hangzhou',
6 | label: '杭州',
7 | children: [{
8 | value: 'xihu',
9 | label: '西湖',
10 | }],
11 | }],
12 | }, {
13 | value: 'jiangsu',
14 | label: '江苏',
15 | children: [{
16 | value: 'nanjing',
17 | label: '南京',
18 | children: [{
19 | value: 'zhonghuamen',
20 | label: '中华门',
21 | }],
22 | }],
23 | }];
24 |
25 | export default options;
26 |
--------------------------------------------------------------------------------
/demo/TreeSelectDemo.less:
--------------------------------------------------------------------------------
1 | /**
2 | * TreeSelect Component Demo Style for Uxcore
3 | * @author biangang.bg
4 | *
5 | * Copyright 2014-2015, Uxcore Team, Alinw.
6 | * All rights reserved.
7 | */
8 | html {
9 | box-sizing: border-box;
10 | * {
11 | box-sizing: inherit;
12 | }
13 | }
14 |
15 | @import "../node_modules/kuma-base/variable";
16 | @svg-path: '../node_modules/kuma-base/core/svg/blue';
17 | @import "../node_modules/kuma-base/core";
18 | @import "../src/TreeSelect.less";
19 |
20 |
--------------------------------------------------------------------------------
/coverage/lcov-report/prettify.css:
--------------------------------------------------------------------------------
1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
2 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | uxcore-tree-select
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | sudo: false
4 |
5 | addons:
6 | apt:
7 | packages:
8 | - xvfb
9 |
10 | notification:
11 | email:
12 | - wsj7552715@hotmail.com
13 |
14 | node_js:
15 | - 6.9.0
16 |
17 | before_install:
18 | - |
19 | if ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qve '(\.md$)|(\.html$)'
20 | then
21 | echo "Only docs were updated, stopping build process."
22 | exit
23 | fi
24 | phantomjs --version
25 | install:
26 | - export DISPLAY=':99.0'
27 | - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
28 | - npm install
29 |
30 |
31 | script:
32 | - |
33 | if [ "$TEST_TYPE" = test ]; then
34 | npm test
35 | else
36 | npm run $TEST_TYPE
37 | fi
38 | env:
39 | matrix:
40 | - TEST_TYPE=test
41 | - TEST_TYPE=coverage
42 | - TEST_TYPE=saucelabs
43 |
44 | matrix:
45 | allow_failures:
46 | - env: "TEST_TYPE=saucelabs"
--------------------------------------------------------------------------------
/src/TreeSelect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * TreeSelect Component for uxcore
3 | * @author biangang.bg
4 | *
5 | * Copyright 2014-2015, Uxcore Team, Alinw.
6 | * All rights reserved.
7 | */
8 | import RcTreeSelect from './Select';
9 | import assign from 'object-assign';
10 | import TreeNode from 'rc-tree-select/lib/TreeNode';
11 | import strategies from 'rc-tree-select/lib/strategies';
12 |
13 |
14 | let supportSVG = false;
15 | if (typeof document !== 'undefined') {
16 | supportSVG = document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#BasicStructure', '1.1');
17 | }
18 |
19 | class TreeSelect extends RcTreeSelect {}
20 |
21 | TreeSelect.TreeNode = TreeNode;
22 | assign(TreeSelect, strategies);
23 | TreeSelect.displayName = 'TreeSelect';
24 |
25 | TreeSelect.defaultProps = assign(RcTreeSelect.defaultProps, {
26 | prefixCls: 'uxcore-tree-select',
27 | dropdownClassName: supportSVG ? 'use-svg' : 'no-svg',
28 | transitionName: 'slideUp',
29 | choiceTransitionName: 'uxcore-tree-select-selection__choice-zoom',
30 | showSearch: false,
31 | dropdownMatchSelectWidth: false,
32 | maxTagTextLength: 10,
33 | locale: 'zh-cn',
34 | });
35 |
36 | TreeSelect.propTypes = RcTreeSelect.propTypes;
37 |
38 |
39 | module.exports = TreeSelect;
40 |
--------------------------------------------------------------------------------
/HISTORY.md:
--------------------------------------------------------------------------------
1 | # HISTORY
2 |
3 | ---
4 |
5 | ## 0.4.3
6 |
7 | * `FIXED` add `rc-tree-select` missing utils functions.
8 |
9 | ## 0.4.2
10 |
11 | * `FIXED` fix `rc-tree-select` version to `1.12.10`
12 |
13 | ## 0.4.1
14 |
15 | * `FIX` clear inputValue when nextProps.value is empty.
16 |
17 | ## 0.4.0
18 |
19 | * `UPDATE` rc-tree-select@1.12.0, change extend-style to usage-style.
20 |
21 | ## 0.3.2
22 |
23 | * `FIXED` inputMirrorInstance is undefined [reference](https://github.com/uxcore/uxcore-tree-select/issues/7)
24 |
25 | ## 0.3.1
26 |
27 | * `FIXED` rc-tree-select@1.11.0 is a breaking change for the component
28 |
29 | ## 0.3.0
30 |
31 | * `UPDATE` React 15
32 |
33 | ## 0.2.4
34 |
35 | * `FIXED` lock tree version, fix switcher arrow style
36 |
37 | ## 0.2.3
38 |
39 | * `FIXED` server render bug
40 |
41 | ## 0.2.2
42 |
43 | `FIXED` i18n
44 |
45 | ## 0.2.1
46 |
47 | `FIXED` fix missing dependencies
48 |
49 | ## 0.2.0
50 |
51 | `CHANGED` add new prop `resultsPanelAllClearBtn` , `resultsPanelTitle`, `resultsPanelTitleStyle` & `filterResultsPanel`
52 |
53 | ## 0.1.4
54 |
55 | `FIXED` add missing loading icon.
56 |
57 |
58 | ## 0.1.3
59 | `CHANGED` change hover & selected styles
60 |
61 | ## 0.1.2
62 | `UPDATE` change some styles
63 |
64 | ## 0.1.1
65 | `UPDATE` change some styles
66 |
67 | ## 0.1.0
68 | `NEW` first version base on rc-tree-select
69 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "uxcore-tree-select",
3 | "version": "0.4.3",
4 | "description": "uxcore-tree-select component for uxcore.",
5 | "repository": "https://github.com/uxcore/uxcore-tree-select.git",
6 | "author": "biangang.bg",
7 | "main": "build/index.js",
8 | "scripts": {
9 | "start": "uxcore-tools run start",
10 | "server": "uxcore-tools run server",
11 | "lint": "uxcore-tools run lint",
12 | "build": "uxcore-tools run build",
13 | "test": "uxcore-tools run electron",
14 | "coverage": "uxcore-tools run electron-coverage",
15 | "pub": "uxcore-tools run pub",
16 | "dep": "uxcore-tools run dep",
17 | "tnpm-dep": "uxcore-tools run tnpm-dep",
18 | "chrome": "uxcore-tools run chrome",
19 | "browsers": "uxcore-tools run browsers",
20 | "saucelabs": "uxcore-tools run saucelabs",
21 | "update": "uxcore-tools run update",
22 | "tnpm-update": "uxcore-tools run tnpm-update"
23 | },
24 | "bugs": {
25 | "url": "http://github.com/uxcore/uxcore-tree-select/issues"
26 | },
27 | "keywords": [
28 | "react",
29 | "react-component",
30 | "uxcore-tree-select",
31 | "TreeSelect",
32 | "uxcore",
33 | "react",
34 | "component"
35 | ],
36 | "devDependencies": {
37 | "console-polyfill": "^0.2.2",
38 | "enzyme": "^3.2.0",
39 | "enzyme-adapter-react-15": "^1.0.5",
40 | "es5-shim": "^4.5.8",
41 | "expect.js": "~0.3.1",
42 | "kuma-base": "1.x",
43 | "react": "15.x",
44 | "react-dom": "15.x",
45 | "react-test-renderer": "15.x",
46 | "uxcore-kuma": "*",
47 | "uxcore-tools": "0.2.x"
48 | },
49 | "dependencies": {
50 | "classnames": "^2.1.2",
51 | "object-assign": "~4.1.0",
52 | "prop-types": "15.x",
53 | "rc-tree": "~1.7.1",
54 | "rc-tree-select": "~1.12.10",
55 | "rc-trigger": "^2.2.2",
56 | "rc-util": "^4.0.2"
57 | },
58 | "contributors": [],
59 | "license": "MIT"
60 | }
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | import { isPositionPrefix, isInclude, isMultiple } from 'rc-tree-select/lib/util';
2 |
3 | // Refactor
4 | export function flatToHierarchy(arr, flag = false) {
5 | if (!arr.length) {
6 | return arr;
7 | }
8 | const hierarchyNodes = [];
9 | const levelObj = {};
10 | arr.forEach((item) => {
11 | if (!item.pos) {
12 | return;
13 | }
14 | const posLen = item.pos.split('-').length;
15 | if (!levelObj[posLen]) {
16 | levelObj[posLen] = [];
17 | }
18 | levelObj[posLen].push(item);
19 | });
20 | // levelObj 收集每个层级的child
21 | const levelArr = Object.keys(levelObj).sort((a, b) => b - a);
22 |
23 | // const s = Date.now();
24 | // todo: there are performance issues!
25 | levelArr.reduce((pre, cur) => {
26 | if (cur && cur !== pre) {
27 | levelObj[pre].forEach((item) => {
28 | let haveParent = false;
29 | levelObj[cur].forEach((ii) => {
30 | if (isPositionPrefix(ii.pos, item.pos)) {
31 | haveParent = true;
32 | // select multiple模式下的筛选
33 | if (flag && ii.isAll) {
34 | return;
35 | }
36 | if (!ii.children) {
37 | ii.children = []; // eslint-disable-line
38 | }
39 | ii.children.push(item);
40 | }
41 | });
42 | if (!haveParent) {
43 | hierarchyNodes.push(item);
44 | }
45 | });
46 | }
47 | return cur;
48 | });
49 | // console.log(Date.now() - s);
50 | return levelObj[levelArr[levelArr.length - 1]].concat(hierarchyNodes);
51 | }
52 |
53 | export function isMultipleOrTags(props) {
54 | return !!(isMultiple(props) || props.tags);
55 | }
56 |
57 | export function isMultipleOrTagsOrCombobox(props) {
58 | return (isMultiple(props) || props.tags || props.combobox);
59 | }
60 |
61 | export function isSingleMode(props) {
62 | return !(isMultiple(props) || props.tags || props.combobox);
63 | }
64 |
65 |
66 |
--------------------------------------------------------------------------------
/tests/utils.spec.js:
--------------------------------------------------------------------------------
1 | import { flatToHierarchy, filterCheckedKeysBaseKey } from '../src/utils';
2 | import React from 'react';
3 | import Enzyme from 'enzyme';
4 | import expect from 'expect.js';
5 | import TreeSelect from '../src';
6 | import Adapter from 'enzyme-adapter-react-15';
7 | // import focusTest from './shared/focusTest';
8 |
9 | const { mount, render } = Enzyme;
10 |
11 | Enzyme.configure({ adapter: new Adapter() });
12 |
13 | describe('Utils', () => {
14 | describe('flatToHierarchy', () => {
15 | const array = [
16 | {
17 | value: '0-0',
18 | pos: '0-0',
19 | },
20 | {
21 | value: '0',
22 | pos: '0',
23 | children: [
24 | {
25 | value: '0-0',
26 | pos: '0-0',
27 | },
28 | ],
29 | },
30 | ];
31 | const array2 = [
32 | {
33 | value: '0-0',
34 | pos: '0-0',
35 | isAll: true,
36 | },
37 | {
38 | value: '0',
39 | pos: '0',
40 | isAll: true,
41 | children: [
42 | {
43 | value: '0-0',
44 | pos: '0-0',
45 | },
46 | ],
47 | },
48 | ];
49 | const array3 = [...array2];
50 | it('test equal array to tree with flag=false', () => {
51 | expect(JSON.stringify(flatToHierarchy(array))).to.equal(JSON.stringify([
52 | {
53 | value: '0',
54 | pos: '0',
55 | children: [
56 | {
57 | value: '0-0',
58 | pos: '0-0',
59 | },
60 | {
61 | value: '0-0',
62 | pos: '0-0',
63 | },
64 | ],
65 | },
66 | ]));
67 | });
68 |
69 | it('test equal array to tree with flag=true', () => {
70 | expect(JSON.stringify(flatToHierarchy(array2, true))).to.equal(JSON.stringify([
71 | {
72 | value: '0',
73 | pos: '0',
74 | isAll: true,
75 | children: [
76 | {
77 | value: '0-0',
78 | pos: '0-0',
79 | },
80 | ],
81 | },
82 | ]));
83 | });
84 |
85 | it('test equal array with isAll to tree with flag=false', () => {
86 | expect(JSON.stringify(flatToHierarchy(array3))).to.equal(JSON.stringify([
87 | {
88 | value: '0',
89 | pos: '0',
90 | isAll: true,
91 | children: [
92 | {
93 | value: '0-0',
94 | pos: '0-0',
95 | },
96 | {
97 | value: '0-0',
98 | pos: '0-0',
99 | isAll: true,
100 | },
101 | ],
102 | },
103 | ]));
104 | });
105 | });
106 | });
107 |
--------------------------------------------------------------------------------
/tests/Select.rightTree.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import expect from 'expect.js';
3 | import Enzyme from 'enzyme';
4 | import TreeSelect from '../src';
5 | import Adapter from 'enzyme-adapter-react-15';
6 | import options from './const';
7 |
8 | const { mount, render } = Enzyme;
9 |
10 | Enzyme.configure({ adapter: new Adapter() });
11 |
12 | describe('Tree Right', () => {
13 | const treeData = [
14 | { key: 'a', value: 'a', label: 'labela' },
15 | { key: 'b', value: 'b', label: 'labelb' },
16 | ];
17 | const treeData2 = [
18 | { key: 'a', value: 'a', label: 'labela', children: [
19 | { key: 'b', value: 'b', label: 'labelb' },
20 | ] },
21 | ];
22 |
23 | it('render title', () => {
24 | const wrapper = mount(
25 |
30 | );
31 | const trigger = mount(wrapper.find('Trigger').props().popup);
32 | expect(trigger.find('.uxcore-tree-select-dropdown-right-title').text()).to.equal('haha');
33 | });
34 |
35 | it('render chooseable number', () => {
36 | const wrapper = mount(
37 |
43 | );
44 | const trigger = mount(wrapper.find('Trigger').props().popup);
45 | expect(trigger.find('.uxcore-tree-select-dropdown-right-selected-number').text()).to.equal('(2)');
46 | });
47 |
48 | it('allow to expend', () => {
49 | const wrapper = mount(
50 |
56 | );
57 | const trigger = mount(wrapper.find('Trigger').props().popup);
58 | trigger.find('.uxcore-tree-select-rightTreeNode-arrow-close').simulate('click');
59 | expect(trigger.find('.uxcore-tree-select-rightTreeNode-label')).to.have.length(2);
60 | });
61 |
62 | it('allow to remove', () => {
63 | const wrapper = mount(
64 |
70 | );
71 | const trigger = mount(wrapper.find('Trigger').props().popup);
72 | trigger.find('.uxcore-tree-select-rightTreeNode-clear').simulate('click');
73 | expect(wrapper.state().value).to.have.length(0);
74 | });
75 |
76 | it('allow to removeAll', () => {
77 | const wrapper = mount(
78 |
84 | );
85 | const trigger = mount(wrapper.find('Trigger').props().popup);
86 | trigger.find('.uxcore-tree-select-dropdown-right-allClear').simulate('click');
87 | expect(wrapper.state().value).to.have.length(0);
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/coverage/lcov-report/src/index.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for src/index.js
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 | 100%
24 | Statements
25 | 1/1
26 |
27 |
28 | 100%
29 | Branches
30 | 0/0
31 |
32 |
33 | 100%
34 | Functions
35 | 0/0
36 |
37 |
38 | 100%
39 | Lines
40 | 1/1
41 |
42 |
43 |
44 |
45 |
46 | | 1
47 | 2
48 | 3
49 | 4
50 | 5
51 | 6
52 | 7
53 | 8
54 | 9
55 | 10
56 | 11
57 | 12 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | 1×
68 | | 'use strict';
69 |
70 | /**
71 | * TreeSelect Component for uxcore
72 | * @author biangang.bg
73 | *
74 | * Copyright 2014-2015, Uxcore Team, Alinw.
75 | * All rights reserved.
76 | */
77 |
78 | module.exports = require('./TreeSelect');
79 | |
80 |
81 |
82 |
83 |
87 |
88 |
89 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/tests/Select.multiple.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import React from 'react';
3 | import Enzyme from 'enzyme';
4 | import expect from 'expect.js';
5 | import TreeSelect from '../src';
6 | import Adapter from 'enzyme-adapter-react-15';
7 |
8 | const { mount, render } = Enzyme;
9 |
10 | Enzyme.configure({ adapter: new Adapter() });
11 |
12 | describe('TreeSelect.multiple', () => {
13 | const treeData = [
14 | { key: '0', value: '0', label: 'label0' },
15 | { key: '1', value: '1', label: 'label1' },
16 | ];
17 | const createSelect = (props) => (
18 |
23 | );
24 | const select = (wrapper, index = 0) => {
25 | // console.log('-----xxxxx-----', wrapper.find('.uxcore-tree-select-tree-node-content-wrapper'));
26 | wrapper.find('.uxcore-tree-select-tree-node-content-wrapper').at(index).simulate('click');
27 | };
28 |
29 | it('render by inputValue', () => {
30 | const wrapper = mount(createSelect({ inputValue: '0' }));
31 | const choices = wrapper.find('.uxcore-tree-select-search__field');
32 | expect(choices.prop('value')).to.equal('0');
33 | });
34 |
35 | it('select multiple nodes', () => {
36 | const wrapper = mount(createSelect({ open: true }));
37 | const trigger = mount(wrapper.find('Trigger').props().popup);
38 | select(trigger, 0);
39 | select(trigger, 1);
40 | wrapper.update();
41 | const result = wrapper.find('.uxcore-tree-select-selection__choice');
42 | const choices = wrapper.find('.uxcore-tree-select-selection__choice__content');
43 | expect(result).to.have.length(2);
44 | expect(choices.at(0).prop('children')).to.be('label0');
45 | expect(choices.at(1).prop('children')).to.be('label1');
46 | });
47 |
48 | it('remove selected node', () => {
49 | const wrapper = mount(createSelect({ defaultValue: ['0', '1'] }));
50 | wrapper.find('.uxcore-tree-select-selection__choice__remove').first().simulate('click');
51 | // const choice = wrapper.find('.uxcore-tree-select-selection__choice');
52 | // expect(choice).to.have.length(1);
53 | // expect(choice.prop('children')).to.be('label1');
54 | expect(wrapper.state('value')).to.have.length(1);
55 | expect(wrapper.state('value')[0].label).to.be('label1');
56 | });
57 |
58 | it('remove by backspace key', () => {
59 | const wrapper = mount(createSelect({ defaultValue: ['0', '1'] }));
60 | wrapper.find('input').simulate('keyDown', { keyCode: 8 }); // 回退键
61 | // const choice = wrapper.find('.uxcore-tree-select-selection__choice__content');
62 | // expect(choice).to.have.length(1);
63 | // expect(choice.prop('children')).to.be('label0');
64 | expect(wrapper.state('value')).to.have.length(1);
65 | expect(wrapper.state('value')[0].label).to.be('label0');
66 | });
67 |
68 | it('focus', () => {
69 | const handleFocus = () => {};
70 | const treeData2 = [
71 | { key: '0', value: '0', label: '0 label' },
72 | ];
73 | const wrapper = mount(
74 |
79 | );
80 | wrapper.instance().focus();
81 | expect(handleFocus).to.not.throwException();
82 | });
83 |
84 | it('blur', () => {
85 | const handleBlur = () => {};
86 | const treeData2 = [
87 | { key: '0', value: '0', label: '0 label' },
88 | ];
89 | const wrapper = mount(
90 |
95 | );
96 | wrapper.instance().focus();
97 | wrapper.instance().blur();
98 | expect(handleBlur).to.not.throwException();
99 | });
100 | });
101 |
--------------------------------------------------------------------------------
/coverage/lcov-report/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for All files
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 | /
20 |
21 |
22 |
23 | 81.79%
24 | Statements
25 | 804/983
26 |
27 |
28 | 69.81%
29 | Branches
30 | 481/689
31 |
32 |
33 | 83.72%
34 | Functions
35 | 144/172
36 |
37 |
38 | 86.27%
39 | Lines
40 | 754/874
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | | File |
50 | |
51 | Statements |
52 | |
53 | Branches |
54 | |
55 | Functions |
56 | |
57 | Lines |
58 | |
59 |
60 |
61 |
62 | | src/ |
63 | |
64 | 81.79% |
65 | 804/983 |
66 | 69.81% |
67 | 481/689 |
68 | 83.72% |
69 | 144/172 |
70 | 86.27% |
71 | 754/874 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
82 |
83 |
84 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/src/RightTreeNode.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * RightTreeNode Component for Tree
3 | * @author chenqiu wb-cq231719@alibaba-inc.com
4 | */
5 |
6 | import React from 'react';
7 | import PropTypes from 'prop-types';
8 | import classnames from 'classnames';
9 | import i18n from './i18n';
10 |
11 | export default class RightTreeNode extends React.Component {
12 | constructor(props) {
13 | super(props);
14 | this.state = {
15 | expand: !(props.isAll || (this.isSelectNode() && props.children)),
16 | };
17 | }
18 |
19 | componentWillReceiveProps(nextProps) {
20 | if (this.props.isAll !== nextProps.isAll) {
21 | this.setState({
22 | expand: !nextProps.isAll,
23 | });
24 | }
25 | }
26 |
27 | getMaxWidth(isSelectNode, paddingLeftStyle) {
28 | const { locale, children, isAll, dropdownWidth, disabled } = this.props;
29 | // padding:32, delete:26, isALL: 38, arrow: 18
30 | let padWidth = 20;
31 | if (isAll || (isSelectNode && children)) {
32 | padWidth += locale === 'en-us' ? 25 : 36;
33 | }
34 | if (isSelectNode && !disabled) {
35 | padWidth += locale === 'en-us' ? 38 : 26;
36 | }
37 | if (children) {
38 | padWidth += 18;
39 | }
40 |
41 | return (dropdownWidth - padWidth - paddingLeftStyle);
42 | }
43 |
44 | removeSelected = () => {
45 | const { removeSelected, value } = this.props;
46 |
47 | removeSelected(value);
48 | }
49 |
50 | isSelectNode() {
51 | const { pos, keys } = this.props;
52 |
53 | return keys.indexOf(pos) > -1;
54 | }
55 |
56 | expand = () => {
57 | this.setState({
58 | expand: !this.state.expand,
59 | });
60 | }
61 |
62 | render() {
63 | const { treeNodeLabelProp, children, isAll, prefixCls, level, locale, disabled } = this.props;
64 | const { expand } = this.state;
65 | // padding 无箭头 +36 有箭头+18
66 | let paddingLeft = 0;
67 | if (level > 1) {
68 | paddingLeft = !children ? (16 + (level - 1) * 18) : (16 + (level - 2) * 18);
69 | } else if (level === 1 && !children) {
70 | // fix style for the first level label which has no Children
71 | paddingLeft = 5;
72 | }
73 | const arrowCls = {
74 | [`${prefixCls}-arrow-close`]: !expand,
75 | [`${prefixCls}-arrow-open`]: expand,
76 | [`${prefixCls}-arrow-switch`]: true,
77 | };
78 | const isSelectNode = this.isSelectNode();
79 |
80 | const maxWidth = this.getMaxWidth(isSelectNode, paddingLeft);
81 |
82 | const content = (
87 | {this.props[treeNodeLabelProp]}
88 | );
89 |
90 | return (
91 |
92 |
93 | {
94 | children ?
95 |
96 | : null
97 | }
98 | {content}
99 | {isAll || (isSelectNode && children) ?
100 | {i18n[locale].all} : null}
101 | {
102 | (isSelectNode && !disabled) ?
103 |
104 | {i18n[locale].delete}
105 |
106 | : null
107 | }
108 |
109 | {
110 | expand && children ? children : null
111 | }
112 |
113 | );
114 | }
115 | }
116 |
117 | RightTreeNode.defaultProps = {
118 | locale: 'zh-cn',
119 | keys: [],
120 | };
121 |
122 | RightTreeNode.propTypes = {
123 | value: PropTypes.oneOfType([PropTypes.array, PropTypes.string, PropTypes.object]),
124 | treeNodeLabelProp: PropTypes.any,
125 | children: PropTypes.any,
126 | isAll: PropTypes.bool,
127 | prefixCls: PropTypes.string,
128 | level: PropTypes.number,
129 | removeSelected: PropTypes.func,
130 | pos: PropTypes.string,
131 | locale: PropTypes.oneOf(['zh-cn', 'en-us']),
132 | dropdownWidth: PropTypes.number,
133 | keys: PropTypes.array,
134 | disabled: PropTypes.bool,
135 | };
136 |
--------------------------------------------------------------------------------
/coverage/lcov-report/src/i18n.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for src/i18n.js
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 | 100%
24 | Statements
25 | 1/1
26 |
27 |
28 | 100%
29 | Branches
30 | 0/0
31 |
32 |
33 | 100%
34 | Functions
35 | 0/0
36 |
37 |
38 | 100%
39 | Lines
40 | 1/1
41 |
42 |
43 |
44 |
45 |
46 | | 1
47 | 2
48 | 3
49 | 4
50 | 5
51 | 6
52 | 7
53 | 8
54 | 9
55 | 10
56 | 11
57 | 12
58 | 13
59 | 14
60 | 15
61 | 16
62 | 17
63 | 18
64 | 19
65 | 20
66 | 21
67 | 22
68 | 23 |
69 |
70 | 1×
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | | 'use strict';
91 |
92 | module.exports = {
93 | 'zh-cn': {
94 | placeholder: '请选择',
95 | confirm: '确定',
96 | alreadyChoosed: '已选择',
97 | pleaseSelectFromLeft: '请从左侧选择',
98 | clear: '清空',
99 | 'delete': '删除',
100 | all: '全选'
101 | },
102 | 'en-us': {
103 | placeholder: 'Please select',
104 | confirm: 'OK',
105 | alreadyChoosed: 'Choosed',
106 | pleaseSelectFromLeft: 'Please select from left',
107 | clear: 'clear',
108 | 'delete': 'Delete',
109 | all: 'All'
110 | }
111 | };
112 | |
113 |
114 |
115 |
116 |
120 |
121 |
122 |
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/tests/Select.checkable.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef, react/no-multi-comp */
2 | import React from 'react';
3 | import Enzyme from 'enzyme';
4 | import TreeSelect, { SHOW_PARENT, SHOW_ALL } from '../src';
5 | import Adapter from 'enzyme-adapter-react-15';
6 | import options from './const';
7 |
8 | const { mount, render } = Enzyme;
9 |
10 | Enzyme.configure({ adapter: new Adapter() });
11 |
12 | describe('TreeSelect.checkable', () => {
13 | it('can be single checked', () => {
14 | const wrapper = mount();
25 | // select
26 | const trigger = mount(wrapper.find('Trigger').props().popup);
27 | trigger.find('.uxcore-tree-select-tree-checkbox').last().simulate('click');
28 | wrapper.update();
29 | expect(wrapper.find('.uxcore-tree-select-selection__choice')).to.have.length(1);
30 | });
31 |
32 | it('can be multiple checked', () => {
33 | const wrapper = mount();
44 | // select
45 | const trigger = mount(wrapper.find('Trigger').props().popup);
46 | trigger.find('.uxcore-tree-select-tree-checkbox').first().simulate('click');
47 | trigger.find('.uxcore-tree-select-tree-checkbox').at(1).simulate('click');
48 | wrapper.update();
49 | expect(wrapper.find('.uxcore-tree-select-selection__choice')).to.have.length(2);
50 | });
51 |
52 | it('can be controled by inputValue', () => {
53 | const wrapper = mount();
64 | wrapper.find('input').simulate('change', { target: { value: '3' } });
65 | // expect(trigger.find('.uxcore-tree-select-tree-checkbox')).to.have.length(3);
66 | const trigger = mount(wrapper.find('Trigger').props().popup);
67 | // trigger.update();
68 | expect(trigger.find('.uxcore-tree-select-tree-checkbox')).to.have.length(1);
69 | });
70 |
71 | it('remove selected value with showCheckedStrategy=SHOW_ALL', () => {
72 | const wrapper = mount(
73 |
84 | );
85 | wrapper.find('.uxcore-tree-select-selection__choice__remove').first().simulate('click');
86 | expect(wrapper.state().value).to.have.length(1);
87 | expect(wrapper.state().value[0].value).to.equal('2');
88 | });
89 |
90 | it('remove selected value with showCheckedStrategy=SHOW_PARENT', () => {
91 | const wrapper = mount(
92 |
103 | );
104 | wrapper.find('.uxcore-tree-select-selection__choice__remove').first().simulate('click');
105 | expect(wrapper.state().value).to.have.length(1);
106 | expect(wrapper.state().value[0].value).to.equal('2');
107 | });
108 |
109 | it('clear selected value and input value', () => {
110 | const treeData = [
111 | {
112 | key: '0',
113 | value: '0',
114 | label: 'label0',
115 | },
116 | ];
117 |
118 | const wrapper = mount(
119 |
126 | );
127 | // open
128 | wrapper.find('.uxcore-tree-select').simulate('click');
129 | const trigger = mount(wrapper.find('Trigger').props().popup);
130 | trigger.find('.uxcore-tree-select-tree-checkbox').at(0).simulate('click');
131 | wrapper.find('input').simulate('change', { target: { value: 'foo' } });
132 | wrapper.find('.uxcore-tree-select-selection__clear').simulate('click');
133 | expect(JSON.stringify(wrapper.state().value)).to.equal(JSON.stringify([]));
134 | expect(wrapper.state().inputValue).to.be('');
135 | });
136 | });
137 |
--------------------------------------------------------------------------------
/coverage/lcov-report/sorter.js:
--------------------------------------------------------------------------------
1 | var addSorting = (function () {
2 | "use strict";
3 | var cols,
4 | currentSort = {
5 | index: 0,
6 | desc: false
7 | };
8 |
9 | // returns the summary table element
10 | function getTable() { return document.querySelector('.coverage-summary'); }
11 | // returns the thead element of the summary table
12 | function getTableHeader() { return getTable().querySelector('thead tr'); }
13 | // returns the tbody element of the summary table
14 | function getTableBody() { return getTable().querySelector('tbody'); }
15 | // returns the th element for nth column
16 | function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; }
17 |
18 | // loads all columns
19 | function loadColumns() {
20 | var colNodes = getTableHeader().querySelectorAll('th'),
21 | colNode,
22 | cols = [],
23 | col,
24 | i;
25 |
26 | for (i = 0; i < colNodes.length; i += 1) {
27 | colNode = colNodes[i];
28 | col = {
29 | key: colNode.getAttribute('data-col'),
30 | sortable: !colNode.getAttribute('data-nosort'),
31 | type: colNode.getAttribute('data-type') || 'string'
32 | };
33 | cols.push(col);
34 | if (col.sortable) {
35 | col.defaultDescSort = col.type === 'number';
36 | colNode.innerHTML = colNode.innerHTML + '';
37 | }
38 | }
39 | return cols;
40 | }
41 | // attaches a data attribute to every tr element with an object
42 | // of data values keyed by column name
43 | function loadRowData(tableRow) {
44 | var tableCols = tableRow.querySelectorAll('td'),
45 | colNode,
46 | col,
47 | data = {},
48 | i,
49 | val;
50 | for (i = 0; i < tableCols.length; i += 1) {
51 | colNode = tableCols[i];
52 | col = cols[i];
53 | val = colNode.getAttribute('data-value');
54 | if (col.type === 'number') {
55 | val = Number(val);
56 | }
57 | data[col.key] = val;
58 | }
59 | return data;
60 | }
61 | // loads all row data
62 | function loadData() {
63 | var rows = getTableBody().querySelectorAll('tr'),
64 | i;
65 |
66 | for (i = 0; i < rows.length; i += 1) {
67 | rows[i].data = loadRowData(rows[i]);
68 | }
69 | }
70 | // sorts the table using the data for the ith column
71 | function sortByIndex(index, desc) {
72 | var key = cols[index].key,
73 | sorter = function (a, b) {
74 | a = a.data[key];
75 | b = b.data[key];
76 | return a < b ? -1 : a > b ? 1 : 0;
77 | },
78 | finalSorter = sorter,
79 | tableBody = document.querySelector('.coverage-summary tbody'),
80 | rowNodes = tableBody.querySelectorAll('tr'),
81 | rows = [],
82 | i;
83 |
84 | if (desc) {
85 | finalSorter = function (a, b) {
86 | return -1 * sorter(a, b);
87 | };
88 | }
89 |
90 | for (i = 0; i < rowNodes.length; i += 1) {
91 | rows.push(rowNodes[i]);
92 | tableBody.removeChild(rowNodes[i]);
93 | }
94 |
95 | rows.sort(finalSorter);
96 |
97 | for (i = 0; i < rows.length; i += 1) {
98 | tableBody.appendChild(rows[i]);
99 | }
100 | }
101 | // removes sort indicators for current column being sorted
102 | function removeSortIndicators() {
103 | var col = getNthColumn(currentSort.index),
104 | cls = col.className;
105 |
106 | cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
107 | col.className = cls;
108 | }
109 | // adds sort indicators for current column being sorted
110 | function addSortIndicators() {
111 | getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted';
112 | }
113 | // adds event listeners for all sorter widgets
114 | function enableUI() {
115 | var i,
116 | el,
117 | ithSorter = function ithSorter(i) {
118 | var col = cols[i];
119 |
120 | return function () {
121 | var desc = col.defaultDescSort;
122 |
123 | if (currentSort.index === i) {
124 | desc = !currentSort.desc;
125 | }
126 | sortByIndex(i, desc);
127 | removeSortIndicators();
128 | currentSort.index = i;
129 | currentSort.desc = desc;
130 | addSortIndicators();
131 | };
132 | };
133 | for (i =0 ; i < cols.length; i += 1) {
134 | if (cols[i].sortable) {
135 | // add the click event handler on the th so users
136 | // dont have to click on those tiny arrows
137 | el = getNthColumn(i).querySelector('.sorter').parentElement;
138 | if (el.addEventListener) {
139 | el.addEventListener('click', ithSorter(i));
140 | } else {
141 | el.attachEvent('onclick', ithSorter(i));
142 | }
143 | }
144 | }
145 | }
146 | // adds sorting functionality to the UI
147 | return function () {
148 | if (!getTable()) {
149 | return;
150 | }
151 | cols = loadColumns();
152 | loadData(cols);
153 | addSortIndicators();
154 | enableUI();
155 | };
156 | })();
157 |
158 | window.addEventListener('load', addSorting);
159 |
--------------------------------------------------------------------------------
/coverage/lcov-report/base.css:
--------------------------------------------------------------------------------
1 | body, html {
2 | margin:0; padding: 0;
3 | height: 100%;
4 | }
5 | body {
6 | font-family: Helvetica Neue, Helvetica, Arial;
7 | font-size: 14px;
8 | color:#333;
9 | }
10 | .small { font-size: 12px; }
11 | *, *:after, *:before {
12 | -webkit-box-sizing:border-box;
13 | -moz-box-sizing:border-box;
14 | box-sizing:border-box;
15 | }
16 | h1 { font-size: 20px; margin: 0;}
17 | h2 { font-size: 14px; }
18 | pre {
19 | font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
20 | margin: 0;
21 | padding: 0;
22 | -moz-tab-size: 2;
23 | -o-tab-size: 2;
24 | tab-size: 2;
25 | }
26 | a { color:#0074D9; text-decoration:none; }
27 | a:hover { text-decoration:underline; }
28 | .strong { font-weight: bold; }
29 | .space-top1 { padding: 10px 0 0 0; }
30 | .pad2y { padding: 20px 0; }
31 | .pad1y { padding: 10px 0; }
32 | .pad2x { padding: 0 20px; }
33 | .pad2 { padding: 20px; }
34 | .pad1 { padding: 10px; }
35 | .space-left2 { padding-left:55px; }
36 | .space-right2 { padding-right:20px; }
37 | .center { text-align:center; }
38 | .clearfix { display:block; }
39 | .clearfix:after {
40 | content:'';
41 | display:block;
42 | height:0;
43 | clear:both;
44 | visibility:hidden;
45 | }
46 | .fl { float: left; }
47 | @media only screen and (max-width:640px) {
48 | .col3 { width:100%; max-width:100%; }
49 | .hide-mobile { display:none!important; }
50 | }
51 |
52 | .quiet {
53 | color: #7f7f7f;
54 | color: rgba(0,0,0,0.5);
55 | }
56 | .quiet a { opacity: 0.7; }
57 |
58 | .fraction {
59 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
60 | font-size: 10px;
61 | color: #555;
62 | background: #E8E8E8;
63 | padding: 4px 5px;
64 | border-radius: 3px;
65 | vertical-align: middle;
66 | }
67 |
68 | div.path a:link, div.path a:visited { color: #333; }
69 | table.coverage {
70 | border-collapse: collapse;
71 | margin: 10px 0 0 0;
72 | padding: 0;
73 | }
74 |
75 | table.coverage td {
76 | margin: 0;
77 | padding: 0;
78 | vertical-align: top;
79 | }
80 | table.coverage td.line-count {
81 | text-align: right;
82 | padding: 0 5px 0 20px;
83 | }
84 | table.coverage td.line-coverage {
85 | text-align: right;
86 | padding-right: 10px;
87 | min-width:20px;
88 | }
89 |
90 | table.coverage td span.cline-any {
91 | display: inline-block;
92 | padding: 0 5px;
93 | width: 100%;
94 | }
95 | .missing-if-branch {
96 | display: inline-block;
97 | margin-right: 5px;
98 | border-radius: 3px;
99 | position: relative;
100 | padding: 0 4px;
101 | background: #333;
102 | color: yellow;
103 | }
104 |
105 | .skip-if-branch {
106 | display: none;
107 | margin-right: 10px;
108 | position: relative;
109 | padding: 0 4px;
110 | background: #ccc;
111 | color: white;
112 | }
113 | .missing-if-branch .typ, .skip-if-branch .typ {
114 | color: inherit !important;
115 | }
116 | .coverage-summary {
117 | border-collapse: collapse;
118 | width: 100%;
119 | }
120 | .coverage-summary tr { border-bottom: 1px solid #bbb; }
121 | .keyline-all { border: 1px solid #ddd; }
122 | .coverage-summary td, .coverage-summary th { padding: 10px; }
123 | .coverage-summary tbody { border: 1px solid #bbb; }
124 | .coverage-summary td { border-right: 1px solid #bbb; }
125 | .coverage-summary td:last-child { border-right: none; }
126 | .coverage-summary th {
127 | text-align: left;
128 | font-weight: normal;
129 | white-space: nowrap;
130 | }
131 | .coverage-summary th.file { border-right: none !important; }
132 | .coverage-summary th.pct { }
133 | .coverage-summary th.pic,
134 | .coverage-summary th.abs,
135 | .coverage-summary td.pct,
136 | .coverage-summary td.abs { text-align: right; }
137 | .coverage-summary td.file { white-space: nowrap; }
138 | .coverage-summary td.pic { min-width: 120px !important; }
139 | .coverage-summary tfoot td { }
140 |
141 | .coverage-summary .sorter {
142 | height: 10px;
143 | width: 7px;
144 | display: inline-block;
145 | margin-left: 0.5em;
146 | background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
147 | }
148 | .coverage-summary .sorted .sorter {
149 | background-position: 0 -20px;
150 | }
151 | .coverage-summary .sorted-desc .sorter {
152 | background-position: 0 -10px;
153 | }
154 | .status-line { height: 10px; }
155 | /* dark red */
156 | .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
157 | .low .chart { border:1px solid #C21F39 }
158 | /* medium red */
159 | .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
160 | /* light red */
161 | .low, .cline-no { background:#FCE1E5 }
162 | /* light green */
163 | .high, .cline-yes { background:rgb(230,245,208) }
164 | /* medium green */
165 | .cstat-yes { background:rgb(161,215,106) }
166 | /* dark green */
167 | .status-line.high, .high .cover-fill { background:rgb(77,146,33) }
168 | .high .chart { border:1px solid rgb(77,146,33) }
169 | /* dark yellow (gold) */
170 | .medium .chart { border:1px solid #f9cd0b; }
171 | .status-line.medium, .medium .cover-fill { background: #f9cd0b; }
172 | /* light yellow */
173 | .medium { background: #fff4c2; }
174 | /* light gray */
175 | span.cline-neutral { background: #eaeaea; }
176 |
177 | .cbranch-no { background: yellow !important; color: #111; }
178 |
179 | .cstat-skip { background: #ddd; color: #111; }
180 | .fstat-skip { background: #ddd; color: #111 !important; }
181 | .cbranch-skip { background: #ddd !important; color: #111; }
182 |
183 |
184 | .cover-fill, .cover-empty {
185 | display:inline-block;
186 | height: 12px;
187 | }
188 | .chart {
189 | line-height: 0;
190 | }
191 | .cover-empty {
192 | background: white;
193 | }
194 | .cover-full {
195 | border-right: none !important;
196 | }
197 | pre.prettyprint {
198 | border: none !important;
199 | padding: 0 !important;
200 | margin: 0 !important;
201 | }
202 | .com { color: #999 !important; }
203 | .ignore-none { color: #999; font-weight: normal; }
204 |
205 | .wrapper {
206 | min-height: 100%;
207 | height: auto !important;
208 | height: 100%;
209 | margin: 0 auto -48px;
210 | }
211 | .footer, .push {
212 | height: 48px;
213 | }
214 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## uxcore-tree-select
2 |
3 | [![NPM version][npm-image]][npm-url]
4 | [![build status][travis-image]][travis-url]
5 | [![Test Coverage][coveralls-image]][coveralls-url]
6 | [![Dependency Status][dep-image]][dep-url]
7 | [![devDependency Status][devdep-image]][devdep-url]
8 | [![NPM downloads][downloads-image]][npm-url]
9 |
10 | [![Sauce Test Status][sauce-image]][sauce-url]
11 |
12 | [npm-image]: http://img.shields.io/npm/v/uxcore-tree-select.svg?style=flat-square
13 | [npm-url]: http://npmjs.org/package/uxcore-tree-select
14 | [travis-image]: https://img.shields.io/travis/uxcore/uxcore-tree-select.svg?style=flat-square
15 | [travis-url]: https://travis-ci.org/uxcore/uxcore-tree-select
16 | [coveralls-image]: https://img.shields.io/coveralls/uxcore/uxcore-tree-select.svg?style=flat-square
17 | [coveralls-url]: https://coveralls.io/r/uxcore/uxcore-tree-select?branch=master
18 | [dep-image]: http://img.shields.io/david/uxcore/uxcore-tree-select.svg?style=flat-square
19 | [dep-url]: https://david-dm.org/uxcore/uxcore-tree-select
20 | [devdep-image]: http://img.shields.io/david/dev/uxcore/uxcore-tree-select.svg?style=flat-square
21 | [devdep-url]: https://david-dm.org/uxcore/uxcore-tree-select#info=devDependencies
22 | [downloads-image]: https://img.shields.io/npm/dm/uxcore-tree-select.svg
23 | [sauce-image]: https://saucelabs.com/browser-matrix/uxcore-tree-select.svg
24 | [sauce-url]: https://saucelabs.com/u/uxcore-tree-select
25 |
26 | ## TL;DR
27 |
28 | uxcore-tree-select ui component for react
29 |
30 | #### setup develop environment
31 |
32 | ```sh
33 | $ git clone https://github.com/uxcore/uxcore-tree-select
34 | $ cd uxcore-tree-select
35 | $ npm install
36 | $ gulp server
37 | ```
38 |
39 | ## Usage
40 |
41 | ## demo
42 | http://uxcore.github.io/
43 |
44 | ## API
45 |
46 | ### TreeSelect props
47 |
48 | | name | description | type | default | since Ver. |
49 | |----------|----------------|----------|--------------|--------------|
50 | |className | additional css class of root dom node | String | '' |
51 | |prefixCls | prefix class | String | 'uxcore-tree-select' |
52 | |animation | dropdown animation name. only support slide-up now | String | '' |
53 | |transitionName | dropdown css animation name | String | '' |
54 | |choiceTransitionName | css animation name for selected items at multiple mode | String | '' |
55 | |dropdownMatchSelectWidth | whether dropdown's with is same with select | bool | true |
56 | |dropdownClassName | additional className applied to dropdown | String | - |
57 | |dropdownStyle | additional style applied to dropdown | Object | {} |
58 | |dropdownPopupAlign | specify alignment for dropdown | Object | - |
59 | |notFoundContent | specify content to show when no result matches. | String | 'Not Found' |
60 | |showSearch | whether show search input in single mode | bool | false |
61 | |allowClear | whether allowClear | bool | false |
62 | |maxTagTextLe\ngth | max tag text length to show | number | - |
63 | |multiple | whether multiple select (true when enable treeCheckable) | bool | false |
64 | |disabled | whether disabled select | bool | false |
65 | |inputValue | if enable search, you can set default input's value, if set to null, auto clear input value when finish select/unselect operation | string/null | '' |
66 | |defaultValue | initial selected treeNode(s) | same as value type | - |
67 | |value | current selected treeNode(s). | normal: String/Array. labelInValue: {value:String,label:React.Node}/Array<{value,label}>. treeCheckStrictly(halfChecked default false): {value:String,label:React.Node, halfChecked}/Array<{value,label,halfChecked}>. | - |
68 | |labelInValue| whether to embed label in value, see above value type | Bool | false |
69 | |onChange | called when select treeNode or input value change | function(value, label(null), extra) | - |
70 | |onSelect | called when select treeNode | function(value, node, extra) | - |
71 | |onSearch | called when input changed | function | - |
72 | |showCheckedStrategy | `TreeSelect.SHOW_ALL`: show all checked treeNodes (Include parent treeNode). `TreeSelect.SHOW_PARENT`: show checked treeNodes (Just show parent treeNode). Default just show child. | enum{TreeSelect.SHOW_ALL, TreeSelect.SHOW_PARENT, TreeSelect.SHOW_CHILD } | TreeSelect.SHOW_CHILD |
73 | |treeIcon | show tree icon | bool | false |
74 | |treeLine | show tree line | bool | false |
75 | |treeDefaultExpandAll | default expand all treeNode | bool | false |
76 | |treeCheckable | whether tree show checkbox (select callback will not fire) | bool | false |
77 | |treeCheckStrictly | check node precisely, parent and children nodes are not associated| bool | false |
78 | |filterTreeNode | whether filter treeNodes by input value. default filter by treeNode's treeNodeFilterProp prop's value | bool/Function(inputValue:string, treeNode:TreeNode) | Function |
79 | |treeNodeFilterProp | which prop value of treeNode will be used for filter if filterTreeNode return true | String | 'value' |
80 | |treeNodeLabelProp | which prop value of treeNode will render as content of select | String | 'title' |
81 | |treeData | treeNodes data Array, if set it then you need not to construct children TreeNode. (value should be unique across the whole array) | array<{value,label,children, [disabled]}> | [] |
82 | |treeDataSimpleMode | enable simple mode of treeData.(treeData should be like this: [{"id":1, "pId":0, "label":"test1"},...], `pId` is parent node's id) | bool/object{id:'id', pId:'pId', rootPId:null} | false |
83 | |loadData | load data asynchronously | function(node) | - |
84 | | resultsPanelAllClearBtn | 在下拉框右半部分中是否显示清除按钮 | bool | true | 0.2.0 |
85 | | resultsPanelTitle | 在下拉框右半部分中显示标题/说明 | string | '' | 0.2.0 |
86 | | resultsPanelTitleStyle | 下拉框右半部分中的标题/说明的样式 | object | {} | 0.2.0 |
87 | | filterResultsPanel | 下拉框右半部分中的结果是否受inputValue影响,如果为true,则右边树形结果也会根据inputValue过滤 | bool | true | 0.2.0 |
88 | | locale | string | false | `'zh-cn'` | 国际化,枚举值 `'en-us'` `'zh-cn'`| 0.2.2 |
89 |
90 | ### TreeNode props
91 | > note: you'd better to use `treeData` instead of using TreeNode.
92 |
93 | | name | description | type | default |
94 | |----------|----------------|----------|--------------|
95 | |disabled | disable treeNode | bool | false |
96 | |key | it's value must be unique across the tree's all TreeNode, you must set it | String | - |
97 | |value | default as treeNodeFilterProp (be unique across the tree's all TreeNode) | String | '' |
98 | |title | tree/subTree's title | String/element | '---' |
99 | |isLeaf | whether it's leaf node | bool | false |
100 |
--------------------------------------------------------------------------------
/demo/TreeSelectDemo.js:
--------------------------------------------------------------------------------
1 | /**
2 | * TreeSelect Component Demo for uxcore
3 | * @author biangang.bg
4 | *
5 | * Copyright 2014-2015, Uxcore Team, Alinw.
6 | * All rights reserved.
7 | */
8 | import React from 'react';
9 | import TreeSelect, { TreeNode, SHOW_PARENT } from '../src';
10 |
11 | function generateData(x = 3, y = 2, z = 1, gData = []) {
12 | // x:每一级下的节点总数。y:每级节点里有y个节点、存在子节点。z:树的level层级数(0表示一级)
13 | function _loop(_level, _preKey, _tns) {
14 | const preKey = _preKey || '0';
15 | const tns = _tns || gData;
16 |
17 | const children = [];
18 | for (let i = 0; i < x; i++) {
19 | const key = `${preKey}-${i}`;
20 | tns.push({ label: `${key}-label`, value: `${key}-value`, key, disabled: key === '0-0-0-1' });
21 | if (i < y) {
22 | children.push(key);
23 | }
24 | }
25 | if (_level < 0) {
26 | return tns;
27 | }
28 | const __level = _level - 1;
29 | children.forEach((key, index) => {
30 | tns[index].children = [];
31 | return _loop(__level, key, tns[index].children);
32 | });
33 | }
34 | _loop(z);
35 | return gData;
36 | }
37 |
38 | let gData = generateData();
39 |
40 | class Demo extends React.Component {
41 |
42 | constructor(props) {
43 | super(props);
44 | this.state = {
45 | visible: false,
46 | inputValue: '0-0-0-label',
47 | value: '0-0-0-value',
48 | multipleValue: [],
49 | simpleTreeData: [
50 | { key: 1, pId: 0, label: 'test1', value: '1' },
51 | { key: 2, pId: 0, label: 'test2', value: '2' },
52 | { key: 11, pId: 1, label: 'test11', value: '3' },
53 | { key: 12, pId: 2, label: 'test12', value: '4' },
54 | { key: 111, pId: 11, label: 'test111', value: 'a' },
55 | ],
56 | treeDataSimpleMode: {
57 | id: 'key',
58 | rootPId: 0,
59 | },
60 | };
61 | }
62 |
63 | onClick() {
64 | this.setState({
65 | visible: true,
66 | });
67 | }
68 |
69 | onClose() {
70 | this.setState({
71 | visible: false,
72 | });
73 | }
74 | onSearch(value) {
75 | console.log(value, arguments);
76 | }
77 | onChange(value) {
78 | console.log('onChange', arguments);
79 | this.setState({ value });
80 | }
81 | onMultipleChange(value) {
82 | console.log('onMultipleChange', arguments);
83 | this.setState({ multipleValue: value });
84 | }
85 | onSelect() {
86 | // use onChange instead
87 | console.log('onselect', arguments);
88 | }
89 | filterTreeNode(input, child) { // 开头符合过滤
90 | return String(child.props.title).indexOf(input) === 0;
91 | }
92 | render() {
93 | return (
94 |
95 |
single select
96 | 测试标题}
100 | resultsPanelTitleStyle={{ fontWeight: 'bold' }}
101 | placeholder={请下拉选择}
102 | searchPlaceholder="please search"
103 | allowClear
104 | showSearch
105 | value={this.state.value}
106 | treeData={gData}
107 | treeNodeFilterProp="label"
108 | onSearch={this.onSearch.bind(this)}
109 | onChange={this.onChange.bind(this)}
110 | onSelect={this.onSelect.bind(this)}
111 | />
112 |
113 |
114 | multiple select
115 |
130 |
135 |
136 | check select
137 |
152 |
153 | use treeDataSimpleMode
154 | 请下拉选择}
157 | searchPlaceholder="please search"
158 | maxTagTextLength={10}
159 | value={this.state.value}
160 | treeData={this.state.simpleTreeData}
161 | treeNodeFilterProp="title"
162 | treeDataSimpleMode={this.state.treeDataSimpleMode}
163 | treeCheckable showCheckedStrategy={SHOW_PARENT}
164 | onChange={this.onChange.bind(this)}
165 | onSelect={this.onSelect.bind(this)}
166 | />
167 |
168 | use TreeNode Component (not recommend)
169 |
177 |
178 |
179 |
180 |
181 |
182 |
183 | sss}
186 | key="0-1-1-0"
187 | />
188 |
189 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 | );
206 | }
207 | }
208 |
209 | export default Demo;
210 |
--------------------------------------------------------------------------------
/coverage/lcov-report/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for src/
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 | 81.79%
24 | Statements
25 | 804/983
26 |
27 |
28 | 69.81%
29 | Branches
30 | 481/689
31 |
32 |
33 | 83.72%
34 | Functions
35 | 144/172
36 |
37 |
38 | 86.27%
39 | Lines
40 | 754/874
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | | File |
50 | |
51 | Statements |
52 | |
53 | Branches |
54 | |
55 | Functions |
56 | |
57 | Lines |
58 | |
59 |
60 |
61 |
62 | | RightTreeNode.jsx |
63 | |
64 | 85.71% |
65 | 72/84 |
66 | 72.6% |
67 | 53/73 |
68 | 92.86% |
69 | 13/14 |
70 | 98.39% |
71 | 61/62 |
72 |
73 |
74 |
75 | | Select.jsx |
76 | |
77 | 79.28% |
78 | 440/555 |
79 | 68.67% |
80 | 274/399 |
81 | 80.21% |
82 | 77/96 |
83 | 81.71% |
84 | 420/514 |
85 |
86 |
87 |
88 | | SelectTrigger.jsx |
89 | |
90 | 85.11% |
91 | 223/262 |
92 | 73.29% |
93 | 118/161 |
94 | 85.71% |
95 | 42/49 |
96 | 90.6% |
97 | 212/234 |
98 |
99 |
100 |
101 | | TreeSelect.js |
102 | |
103 | 78.26% |
104 | 36/46 |
105 | 53.13% |
106 | 17/32 |
107 | 85.71% |
108 | 6/7 |
109 | 100% |
110 | 28/28 |
111 |
112 |
113 |
114 | | i18n.js |
115 | |
116 | 100% |
117 | 1/1 |
118 | 100% |
119 | 0/0 |
120 | 100% |
121 | 0/0 |
122 | 100% |
123 | 1/1 |
124 |
125 |
126 |
127 | | index.js |
128 | |
129 | 100% |
130 | 1/1 |
131 | 100% |
132 | 0/0 |
133 | 100% |
134 | 0/0 |
135 | 100% |
136 | 1/1 |
137 |
138 |
139 |
140 | | utils.js |
141 | |
142 | 91.18% |
143 | 31/34 |
144 | 79.17% |
145 | 19/24 |
146 | 100% |
147 | 6/6 |
148 | 91.18% |
149 | 31/34 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
160 |
161 |
162 |
169 |
170 |
171 |
172 |
--------------------------------------------------------------------------------
/coverage/lcov-report/src/utils.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for src/utils.js
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 | 91.18%
24 | Statements
25 | 31/34
26 |
27 |
28 | 79.17%
29 | Branches
30 | 19/24
31 |
32 |
33 | 100%
34 | Functions
35 | 6/6
36 |
37 |
38 | 91.18%
39 | Lines
40 | 31/34
41 |
42 |
43 |
44 |
45 |
46 | | 1
47 | 2
48 | 3
49 | 4
50 | 5
51 | 6
52 | 7
53 | 8
54 | 9
55 | 10
56 | 11
57 | 12
58 | 13
59 | 14
60 | 15
61 | 16
62 | 17
63 | 18
64 | 19
65 | 20
66 | 21
67 | 22
68 | 23
69 | 24
70 | 25
71 | 26
72 | 27
73 | 28
74 | 29
75 | 30
76 | 31
77 | 32
78 | 33
79 | 34
80 | 35
81 | 36
82 | 37
83 | 38
84 | 39
85 | 40
86 | 41
87 | 42
88 | 43
89 | 44
90 | 45
91 | 46
92 | 47
93 | 48
94 | 49
95 | 50
96 | 51
97 | 52
98 | 53
99 | 54
100 | 55
101 | 56
102 | 57
103 | 58
104 | 59
105 | 60
106 | 61
107 | 62
108 | 63 |
109 |
110 | 1×
111 |
112 |
113 | 1×
114 |
115 | 1×
116 |
117 |
118 | 1×
119 | 118×
120 |
121 | 118×
122 | 63×
123 |
124 | 55×
125 | 55×
126 | 55×
127 | 71×
128 |
129 |
130 | 71×
131 | 71×
132 | 58×
133 |
134 | 71×
135 |
136 |
137 | 55×
138 | 3×
139 |
140 |
141 |
142 |
143 | 55×
144 | 3×
145 | 3×
146 | 3×
147 | 3×
148 | 3×
149 | 3×
150 |
151 | 3×
152 | 1×
153 |
154 | 2×
155 |
156 |
157 | 2×
158 |
159 |
160 | 3×
161 |
162 |
163 |
164 |
165 | 3×
166 |
167 |
168 | 55×
169 |
170 | | 'use strict';
171 |
172 | Object.defineProperty(exports, "__esModule", {
173 | value: true
174 | });
175 | exports.flatToHierarchy = flatToHierarchy;
176 |
177 | var _util = require('rc-tree-select/lib/util');
178 |
179 | // Refactor
180 | function flatToHierarchy(arr) {
181 | var flag = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
182 |
183 | if (!arr.length) {
184 | return arr;
185 | }
186 | var hierarchyNodes = [];
187 | var levelObj = {};
188 | arr.forEach(function (item) {
189 | Iif (!item.pos) {
190 | return;
191 | }
192 | var posLen = item.pos.split('-').length;
193 | if (!levelObj[posLen]) {
194 | levelObj[posLen] = [];
195 | }
196 | levelObj[posLen].push(item);
197 | });
198 | // levelObj 收集每个层级的child
199 | var levelArr = Object.keys(levelObj).sort(function (a, b) {
200 | return b - a;
201 | });
202 |
203 | // const s = Date.now();
204 | // todo: there are performance issues!
205 | levelArr.reduce(function (pre, cur) {
206 | Eif (cur && cur !== pre) {
207 | levelObj[pre].forEach(function (item) {
208 | var haveParent = false;
209 | levelObj[cur].forEach(function (ii) {
210 | Eif ((0, _util.isPositionPrefix)(ii.pos, item.pos)) {
211 | haveParent = true;
212 | // select multiple模式下的筛选
213 | if (flag && ii.isAll) {
214 | return;
215 | }
216 | Iif (!ii.children) {
217 | ii.children = []; // eslint-disable-line
218 | }
219 | ii.children.push(item);
220 | }
221 | });
222 | Iif (!haveParent) {
223 | hierarchyNodes.push(item);
224 | }
225 | });
226 | }
227 | return cur;
228 | });
229 | // console.log(Date.now() - s);
230 | return levelObj[levelArr[levelArr.length - 1]].concat(hierarchyNodes);
231 | }
232 | |
233 |
234 |
235 |
236 |
240 |
241 |
242 |
249 |
250 |
251 |
252 |
--------------------------------------------------------------------------------
/tests/TreeSelect.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import React from 'react';
3 | import expect from 'expect.js';
4 | import Enzyme from 'enzyme';
5 | import TreeSelect from '../src';
6 | import Adapter from 'enzyme-adapter-react-15';
7 | import options from './const';
8 |
9 | const { TreeNode } = TreeSelect;
10 | const { mount, render } = Enzyme;
11 |
12 | Enzyme.configure({ adapter: new Adapter() });
13 |
14 | describe('TreeSelect', () => {
15 | it('should render single select', () => {
16 | const wrapper = mount();
17 | expect(wrapper.find('.uxcore-tree-select-selection__placeholder').html())
18 | .to
19 | .equal('请选择');
20 | });
21 |
22 | it('should render sub-menu', () => {
23 | const wrapper = mount();
24 | wrapper.find('.uxcore-tree-select').simulate('click');
25 | const trigger = mount(wrapper.find('Trigger').props().popup);
26 | expect(trigger.length).to.be(1);
27 | });
28 |
29 | it('render width inputValue', () => {
30 | // only one treeNode will be render
31 | const wrapper = mount(
32 |
39 | );
40 | const trigger = mount(wrapper.find('Trigger').props().popup);
41 | expect(trigger.find('Tree')).to.have.length(1);
42 | });
43 |
44 | it('sets default value', () => {
45 | const treeData = [
46 | { key: '0', value: '0', label: 'label0' },
47 | ];
48 | const wrapper = mount(
49 |
50 | );
51 | expect(
52 | wrapper.find('.uxcore-tree-select-selection__rendered > span').props().children
53 | ).to.equal('label0');
54 | });
55 |
56 | it('can be controlled by value', () => {
57 | const treeData = [
58 | { key: '0', value: '0', label: 'label0' },
59 | { key: '1', value: '1', label: 'label1' },
60 | ];
61 | const wrapper = mount(
62 |
63 | );
64 | let choice = wrapper.find('.uxcore-tree-select-selection__rendered > span');
65 | expect(choice.prop('children')).to.equal('label0');
66 | wrapper.setProps({ value: '1' });
67 | choice = wrapper.find('.uxcore-tree-select-selection__rendered > span');
68 | expect(choice.prop('children')).to.equal('label1');
69 | });
70 |
71 | it('embed label to value by add props:labelInValue', () => {
72 | const treeData = [
73 | { key: '0', value: '0', label: 'label0' },
74 | { key: '1', value: '1', label: 'label1' },
75 | ];
76 | const wrapper = mount(
77 |
78 | );
79 | wrapper.setProps({ value: { value: '0', label: 'label0' } });
80 | expect(JSON.stringify(wrapper.state().value)).to.equal(JSON.stringify([
81 | { value: '0', label: 'label0' },
82 | ]));
83 | });
84 |
85 | it('close tree when press ESC', () => {
86 | const wrapper = mount(
87 |
88 |
89 |
90 | );
91 | wrapper.setState({ open: true });
92 | const trigger = mount(wrapper.find('Trigger').props().popup);
93 | trigger.find('.uxcore-tree-select-search__field').simulate('keyDown', { keyCode: 27 });
94 | expect(wrapper.state('open')).to.equal(false);
95 | });
96 |
97 | it('checks node correctly after treeData updated', () => {
98 | const wrapper = mount(
99 |
100 | );
101 | wrapper.setProps({ treeData: [{ key: '0', value: '0', label: 'label0' }] });
102 | const trigger = mount(wrapper.find('Trigger').props().popup);
103 | trigger.find('.uxcore-tree-select-tree-checkbox').simulate('click');
104 | expect(JSON.stringify(wrapper.state().value)).to.equal(JSON.stringify([{ value: '0', label: 'label0' }]));
105 | });
106 |
107 | it('expands tree nodes by treeDefaultExpandedKeys', () => {
108 | const wrapper = mount(
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | );
117 | const trigger = mount(wrapper.find('Trigger').props().popup);
118 | const node = trigger.find('.uxcore-tree-select-tree-node-content-wrapper').at(1);
119 | expect(node.hasClass('uxcore-tree-select-tree-node-content-wrapper-open')).to.equal(true);
120 | });
121 |
122 | describe('select', () => {
123 | const treeData = [
124 | { key: '0', value: '0', label: 'label0' },
125 | { key: '1', value: '1', label: 'label1' },
126 | ];
127 | const createSelect = (props) => (
128 |
133 | );
134 |
135 | it('render result by treeNodeLabelProp', () => {
136 | const wrapper = mount(createSelect({ treeNodeLabelProp: 'value', value: '0' }));
137 | // const trigger = mount(wrapper.find('Trigger').props().popup);
138 | expect(wrapper.find('.uxcore-tree-select-selection__rendered > span').prop('children')).to.equal('0');
139 | });
140 | });
141 |
142 | describe('search nodes', () => {
143 | const treeData = [
144 | { key: 'a', value: 'a', label: 'labela' },
145 | { key: 'b', value: 'b', label: 'labelb' },
146 | ];
147 | const createSelect = (props) => (
148 |
154 | );
155 |
156 | it('fires search event', () => {
157 | const onSearch = x => x;
158 | const wrapper = mount(createSelect({ onSearch }));
159 | const trigger = mount(wrapper.find('Trigger').props().popup);
160 | trigger.find('input').simulate('change', { target: { value: 'a' } });
161 | // expect(onSearch).to.equalCalledWith('a');
162 | expect(onSearch).withArgs('a').to.not.throwException();
163 | });
164 |
165 | it('search nodes by filterTreeNode', () => {
166 | const filter = (value, node) => node.props.value.toLowerCase() === value.toLowerCase();
167 | const wrapper = mount(createSelect({ filterTreeNode: filter }));
168 | const trigger = mount(wrapper.find('Trigger').props().popup);
169 | trigger.find('input').simulate('change', { target: { value: 'A' } });
170 | // expect(trigger.find('TreeNode')).to.have.length(1);
171 | // expect(trigger.find('TreeNode').prop('value')).to.equal('a');
172 | expect(wrapper.state().inputValue).to.equal('A');
173 | });
174 |
175 | it('search nodes by treeNodeFilterProp', () => {
176 | const wrapper = mount(createSelect({ treeNodeFilterProp: 'label' }));
177 | const trigger = mount(wrapper.find('Trigger').props().popup);
178 | trigger.find('input').simulate('change', { target: { value: 'labela' } });
179 | // expect(trigger.find('TreeNode')).to.have.length(1);
180 | // expect(trigger.find('TreeNode').prop('value')).to.equal('a');
181 | expect(wrapper.state().inputValue).to.equal('labela');
182 | });
183 | });
184 |
185 | describe('allowClear', () => {
186 | it('allowClear when defaultValue is not exist', () => {
187 | const wrapper = mount(
188 |
189 |
190 |
191 | );
192 | const trigger = mount(wrapper.find('Trigger').props().popup);
193 | trigger.find('.uxcore-tree-select-tree-title').simulate('click');
194 | wrapper.update();
195 | wrapper.find('.uxcore-tree-select-selection__clear').simulate('click');
196 | expect(JSON.stringify(wrapper.state().value)).to.equal(JSON.stringify([]));
197 | });
198 |
199 | it('allowClear when defaultValue is exist', () => {
200 | const wrapper = mount(
204 |
205 | );
206 | wrapper.find('.uxcore-tree-select-selection__clear').simulate('click');
207 | expect(JSON.stringify(wrapper.state().value)).to.equal(JSON.stringify([]));
208 | });
209 | });
210 |
211 | describe('node unmount', () => {
212 | const App = (isMount) => {
213 | if (!isMount) {
214 | return null;
215 | }
216 | return (
217 |
218 |
219 |
220 | );
221 | };
222 |
223 | const wrapper = mount();
226 | expect(wrapper.find('TreeSelect')).to.have.length(1);
227 | wrapper.setProps({ isMount: false });
228 | wrapper.unmount();
229 | expect(wrapper.find('TreeSelect')).to.have.length(0);
230 | });
231 |
232 | describe('focus and blur test', () => {
233 | it('focus', () => {
234 | const handleFocus = () => {};
235 | const treeData = [
236 | { key: '0', value: '0', label: '0 label' },
237 | ];
238 | const wrapper = mount(
239 |
244 | );
245 | wrapper.instance().focus();
246 | expect(handleFocus).to.not.throwException();
247 | });
248 |
249 | it('blur', () => {
250 | const handleBlur = () => {};
251 | const treeData = [
252 | { key: '0', value: '0', label: '0 label' },
253 | ];
254 | const wrapper = mount(
255 |
260 | );
261 | wrapper.instance().focus();
262 | wrapper.instance().blur();
263 | expect(handleBlur).to.not.throwException();
264 | });
265 | });
266 | });
267 |
--------------------------------------------------------------------------------
/coverage/lcov-report/src/TreeSelect.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for src/TreeSelect.js
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 | all files / src/ TreeSelect.js
20 |
21 |
22 |
23 | 78.26%
24 | Statements
25 | 36/46
26 |
27 |
28 | 53.13%
29 | Branches
30 | 17/32
31 |
32 |
33 | 85.71%
34 | Functions
35 | 6/7
36 |
37 |
38 | 100%
39 | Lines
40 | 28/28
41 |
42 |
43 |
44 |
45 |
46 | | 1
47 | 2
48 | 3
49 | 4
50 | 5
51 | 6
52 | 7
53 | 8
54 | 9
55 | 10
56 | 11
57 | 12
58 | 13
59 | 14
60 | 15
61 | 16
62 | 17
63 | 18
64 | 19
65 | 20
66 | 21
67 | 22
68 | 23
69 | 24
70 | 25
71 | 26
72 | 27
73 | 28
74 | 29
75 | 30
76 | 31
77 | 32
78 | 33
79 | 34
80 | 35
81 | 36
82 | 37
83 | 38
84 | 39
85 | 40
86 | 41
87 | 42
88 | 43
89 | 44
90 | 45
91 | 46
92 | 47
93 | 48
94 | 49
95 | 50
96 | 51
97 | 52
98 | 53
99 | 54
100 | 55
101 | 56
102 | 57
103 | 58
104 | 59
105 | 60
106 | 61
107 | 62
108 | 63
109 | 64
110 | 65
111 | 66
112 | 67
113 | 68
114 | 69
115 | 70
116 | 71 |
117 |
118 | 1×
119 |
120 | 1×
121 |
122 | 1×
123 |
124 | 1×
125 |
126 | 1×
127 |
128 | 1×
129 |
130 | 1×
131 |
132 | 1×
133 |
134 | 4×
135 |
136 | 1×
137 |
138 | 35×
139 |
140 | 35×
141 |
142 | 1×
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 | 1×
152 | 1×
153 | 1×
154 |
155 |
156 | 1×
157 | 1×
158 |
159 | 1×
160 | 35×
161 |
162 | 35×
163 |
164 |
165 | 1×
166 |
167 |
168 | 1×
169 | 1×
170 | 1×
171 |
172 | 1×
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 | 1×
184 |
185 | 1×
186 | | 'use strict';
187 |
188 | var _Select = require('./Select');
189 |
190 | var _Select2 = _interopRequireDefault(_Select);
191 |
192 | var _objectAssign = require('object-assign');
193 |
194 | var _objectAssign2 = _interopRequireDefault(_objectAssign);
195 |
196 | var _TreeNode = require('rc-tree-select/lib/TreeNode');
197 |
198 | var _TreeNode2 = _interopRequireDefault(_TreeNode);
199 |
200 | var _strategies = require('rc-tree-select/lib/strategies');
201 |
202 | var _strategies2 = _interopRequireDefault(_strategies);
203 |
204 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
205 |
206 | function _defaults(obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; }
207 |
208 | function _classCallCheck(instance, Constructor) { Iif (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
209 |
210 | function _possibleConstructorReturn(self, call) { Iif (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
211 |
212 | function _inherits(subClass, superClass) { Iif (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); Eif (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : _defaults(subClass, superClass); } /**
213 | * TreeSelect Component for uxcore
214 | * @author biangang.bg
215 | *
216 | * Copyright 2014-2015, Uxcore Team, Alinw.
217 | * All rights reserved.
218 | */
219 |
220 |
221 | var supportSVG = false;
222 | Eif (typeof document !== 'undefined') {
223 | supportSVG = document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#BasicStructure', '1.1');
224 | }
225 |
226 | var TreeSelect = function (_RcTreeSelect) {
227 | _inherits(TreeSelect, _RcTreeSelect);
228 |
229 | function TreeSelect() {
230 | _classCallCheck(this, TreeSelect);
231 |
232 | return _possibleConstructorReturn(this, _RcTreeSelect.apply(this, arguments));
233 | }
234 |
235 | return TreeSelect;
236 | }(_Select2['default']);
237 |
238 | TreeSelect.TreeNode = _TreeNode2['default'];
239 | (0, _objectAssign2['default'])(TreeSelect, _strategies2['default']);
240 | TreeSelect.displayName = 'TreeSelect';
241 |
242 | TreeSelect.defaultProps = (0, _objectAssign2['default'])(_Select2['default'].defaultProps, {
243 | prefixCls: 'uxcore-tree-select',
244 | dropdownClassName: supportSVG ? 'use-svg' : 'no-svg',
245 | transitionName: 'slideUp',
246 | choiceTransitionName: 'uxcore-tree-select-selection__choice-zoom',
247 | showSearch: false,
248 | dropdownMatchSelectWidth: false,
249 | maxTagTextLength: 10,
250 | locale: 'zh-cn'
251 | });
252 |
253 | TreeSelect.propTypes = _Select2['default'].propTypes;
254 |
255 | module.exports = TreeSelect;
256 | |
257 |
258 |
259 |
260 |
264 |
265 |
266 |
273 |
274 |
275 |
276 |
--------------------------------------------------------------------------------
/coverage/lcov-report/prettify.js:
--------------------------------------------------------------------------------
1 | window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^
629 |
636 |
637 |