Skip to content
This repository was archived by the owner on Jun 2, 2024. It is now read-only.

Commit 3ea1766

Browse files
authored
Merge pull request #3 from csvenke/feature/switch-component
feature/switch component
2 parents c1d4f91 + 6b60a4c commit 3ea1766

File tree

5 files changed

+216
-2
lines changed

5 files changed

+216
-2
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { shallow } from 'enzyme';
2+
import * as React from 'react';
3+
4+
import Switch from './Switch';
5+
6+
const createElement = (value, values, withDefault = true) => (
7+
<Switch value={value}>
8+
{values.map((item, index) => (
9+
<Switch.Case key={item} value={item}>
10+
<div>{`case ${index}`}</div>
11+
</Switch.Case>
12+
))}
13+
{withDefault ? (
14+
<Switch.Default>
15+
<div>default</div>
16+
</Switch.Default>
17+
) : null}
18+
</Switch>
19+
);
20+
21+
const getResult = index => `<div>case ${index}</div>`;
22+
23+
describe('without default case', () => {
24+
test('should return content from first case', () => {
25+
const wrapper = shallow(createElement(0, [0, 1, 2], false));
26+
expect(wrapper.html()).toEqual(getResult(0));
27+
});
28+
29+
test('should return content from second case', () => {
30+
const wrapper = shallow(createElement(1, [0, 1, 2], false));
31+
expect(wrapper.html()).toEqual(getResult(1));
32+
});
33+
34+
test('should return content from third case', () => {
35+
const wrapper = shallow(createElement(2, [0, 1, 2], false));
36+
expect(wrapper.html()).toEqual(getResult(2));
37+
});
38+
39+
test('should return null', () => {
40+
const wrapper = shallow(createElement(3, [0, 1, 2], false));
41+
expect(wrapper.html()).toEqual(null);
42+
});
43+
});
44+
45+
describe('with default case', () => {
46+
test('should return content from first case', () => {
47+
const wrapper = shallow(createElement(0, [0, 1, 2]));
48+
expect(wrapper.html()).toEqual(getResult(0));
49+
});
50+
51+
test('should return content from second case', () => {
52+
const wrapper = shallow(createElement(1, [0, 1, 2]));
53+
expect(wrapper.html()).toEqual(getResult(1));
54+
});
55+
56+
test('should return content from third case', () => {
57+
const wrapper = shallow(createElement(2, [0, 1, 2]));
58+
expect(wrapper.html()).toEqual(getResult(2));
59+
});
60+
61+
test('should return null', () => {
62+
const wrapper = shallow(createElement(3, [0, 1, 2]));
63+
expect(wrapper.html()).toEqual('<div>default</div>');
64+
});
65+
});
66+
67+
describe('input tests', () => {
68+
test('should return content when value is number', () => {
69+
const wrapper = shallow(createElement(3, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
70+
expect(wrapper.html()).toEqual(getResult(3));
71+
});
72+
73+
test('should return content when value is string', () => {
74+
const wrapper = shallow(createElement('salsa', [0, 1, 2, 3, 'salsa', 5, 6, 7, 8]));
75+
expect(wrapper.html()).toEqual(getResult(4));
76+
});
77+
78+
test('should return content when value is boolean (true)', () => {
79+
const wrapper = shallow(createElement(true, [0, 1, 2, 3, 4, 5, true, 7, 8, 9]));
80+
expect(wrapper.html()).toEqual(getResult(6));
81+
});
82+
83+
test('should return content when value is boolean (false)', () => {
84+
const wrapper = shallow(createElement(false, [0, 1, 2, 3, 4, 5, false, 7, 8, 9]));
85+
expect(wrapper.html()).toEqual(getResult(6));
86+
});
87+
});
88+
89+
describe('misc tests', () => {
90+
test('should return content from first occurence when multiple matches', () => {
91+
const wrapper = shallow(createElement(true, [0, 1, 2, true, 4, 5, true, 7, 8, 9]));
92+
expect(wrapper.html()).toEqual(getResult(3));
93+
});
94+
});

src/components/Switch/Switch.tsx

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,67 @@
1+
import * as PropTypes from 'prop-types';
12
import * as React from 'react';
23

4+
import SwitchCase, { ISwitchCaseProps } from './SwitchCase';
5+
import SwitchDefault from './SwitchDefault';
6+
37
export interface ISwitchProps {
4-
value: string | boolean | number;
8+
/** Conditional statement. */
9+
value: any;
10+
11+
/** Primary content. */
512
children: React.ReactNode;
613
}
714

15+
/**
16+
* Returns primary content of first case that matches, returns default if no match and null if no default.
17+
*
18+
* @example
19+
*
20+
* <Switch value={3}>
21+
* <Switch.Case value={1}>
22+
* <div>Render me!</div>
23+
* </Switch.Case>
24+
* <Switch.Case value={2}>
25+
* <div>No, render me!</div>
26+
* </Switch.Case>
27+
* <Switch.Default>
28+
* <div>No, render me!</div>
29+
* </Switch.Default>
30+
* </Switch>
31+
*/
832
class Switch extends React.Component<ISwitchProps> {
33+
public static propTypes = {
34+
children: PropTypes.node.isRequired,
35+
value: PropTypes.any.isRequired,
36+
};
37+
38+
public static Case = SwitchCase;
39+
40+
public static Default = SwitchDefault;
41+
942
public render() {
10-
return null;
43+
const switchValue = this.props.value;
44+
let match;
45+
let child;
46+
47+
React.Children.forEach(
48+
this.props.children,
49+
(element: React.ReactElement<ISwitchCaseProps>) => {
50+
if (match === undefined && React.isValidElement(element)) {
51+
const caseValue = element.props.value;
52+
child = element;
53+
match = caseValue === switchValue || undefined;
54+
}
55+
},
56+
);
57+
58+
// No match found, return default if it exists.
59+
if (!match && !child.props.value) {
60+
return React.cloneElement(child);
61+
}
62+
63+
// Return case if its a match.
64+
return match ? React.cloneElement(child) : null;
1165
}
1266
}
1367

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as PropTypes from 'prop-types';
2+
import * as React from 'react';
3+
4+
import { isEmptyChildren } from '../../utils';
5+
6+
export interface ISwitchCaseProps {
7+
/** Conditional statement. */
8+
value: any;
9+
10+
/** Shorthand for primary content. */
11+
render?: () => React.ReactNode;
12+
13+
/** Primary content. */
14+
children?: React.ReactNode;
15+
}
16+
17+
const SwitchCase: React.SFC<ISwitchCaseProps> = ({ value, render, children }) => {
18+
if (value !== undefined) {
19+
if (children && !isEmptyChildren(children)) {
20+
return React.Children.only(children);
21+
}
22+
23+
if (render) {
24+
return <React.Fragment>{render()}</React.Fragment>;
25+
}
26+
}
27+
28+
return null;
29+
};
30+
31+
SwitchCase.propTypes = {
32+
children: PropTypes.node,
33+
render: PropTypes.func,
34+
value: PropTypes.any.isRequired,
35+
};
36+
37+
export default SwitchCase;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import * as PropTypes from 'prop-types';
2+
import * as React from 'react';
3+
4+
import SwitchCase from './SwitchCase';
5+
6+
export interface ISwitchDefaultProps {
7+
/** Shorthand for primary content. */
8+
render?: () => React.ReactNode;
9+
10+
/** Primary content. */
11+
children?: React.ReactNode;
12+
}
13+
14+
const SwitchDefault: React.SFC<ISwitchDefaultProps> = ({ render, children }) => {
15+
const value = '__switch_case_value_override__';
16+
return (
17+
<SwitchCase value={value} render={render}>
18+
{children}
19+
</SwitchCase>
20+
);
21+
};
22+
23+
SwitchDefault.propTypes = {
24+
children: PropTypes.node,
25+
render: PropTypes.func,
26+
};
27+
28+
export default SwitchDefault;

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { default as Map } from './components/Map';
22
export { default as Show } from './components/Show';
3+
export { default as Switch } from './components/Switch';

0 commit comments

Comments
 (0)