Skip to content

Commit 2637259

Browse files
committed
feat(component): add <ToggleAndPattern />, <ToggleOrPattern /> components (#2)
These component provide "and", "or" matching mechanism. `<TogglePattern />` is still or matching.
1 parent b54a9c2 commit 2637259

9 files changed

+347
-16
lines changed

README.md

+77-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ Install with [npm](https://www.npmjs.com/):
1010

1111
## Usage
1212

13+
`react-toggle-pattern` provide three components.
14+
15+
- `<TogglePattern />` or pattern. This is same with `<ToggleOrPattern />`
16+
- `<ToggleOrPattern />` or pattern
17+
- `<ToggleAndPattern />` and pattern
18+
1319
Put `<YourComponent />` into `<TogglePattern />`.
1420

1521
```js
@@ -38,6 +44,51 @@ It means that
3844
- `anyAttribute` is any name.
3945
- `anyValue` is any type.
4046

47+
`<ToggleOrPattern />` and `<ToggleAndPattern />` has same interface.
48+
49+
### OR AND pattern
50+
51+
#### OR
52+
53+
`<ToggleOrPattern />` filter child components by **OR** matching.
54+
55+
```js
56+
<ToggleOrPattern a={true}>
57+
<LeaveEditingButton a={true} b={false} />
58+
<EnterEditingButton a={true} />
59+
</ToggleOrPattern>
60+
```
61+
62+
Result to:
63+
64+
```html
65+
<div class="TogglePattern ToggleOrPattern">
66+
<LeaveEditingButton a={true} b={false} />
67+
<EnterEditingButton a={true} />
68+
</div>
69+
```
70+
71+
Both components are **or** match with TogglePattern.
72+
73+
#### AND
74+
75+
`<ToggleAndPattern />` filter child components by **AND** matching.
76+
77+
```js
78+
<ToggleAndPattern a={true}>
79+
<LeaveEditingButton a={true} b={false} />
80+
<EnterEditingButton a={true} />
81+
</ToggleAndPattern>
82+
```
83+
84+
Result to:
85+
86+
```html
87+
<LeaveEditingButton a={true} b={false} />
88+
```
89+
90+
`<EnterEditingButton />` is not **and** match with TogglePattern.
91+
4192
### Example
4293

4394
Show component that has truly attribute with `<TogglePattern attribute />`
@@ -88,7 +139,7 @@ Show component**s** that match attribute and value with `<TogglePattern attribut
88139
</TogglePattern>
89140
```
90141

91-
Result to `<div class="TogglePattern"><ComponentX /><ComponentX /></div>`
142+
Result to `<div class="TogglePattern ToggleOrPattern"><ComponentX /><ComponentX /></div>`
92143

93144
-----
94145

@@ -103,6 +154,31 @@ Not show when not match
103154

104155
Result to `null`.
105156

157+
------
158+
159+
OR match
160+
161+
```js
162+
<ToggleOrPattern pattern1={1} pattern2={2}>
163+
<ComponentX pattern1={1} pattern2={2}/>
164+
<ComponentY pattern1={1}/>
165+
</ToggleOrPattern>
166+
```
167+
168+
Result to `<div class="TogglePattern ToggleOrPattern"><div>Visible</div><div>Hidden</div></div>`.
169+
170+
------
171+
172+
And match
173+
174+
````js
175+
<ToggleAndPattern pattern1={1} pattern2={2}>
176+
<ComponentX pattern1={1} pattern2={2}/>
177+
<ComponentY pattern1={1} />
178+
</ToggleAndPattern>
179+
```
180+
181+
Result to `<ComponentX pattern1={1} pattern2={2}/>`.
106182

107183

108184
## Changelog

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
],
1818
"version": "1.1.1",
1919
"description": "React Component that provide toggle pattern",
20-
"main": "lib/react-toggle-pattern.js",
20+
"main": "lib/index.js",
2121
"directories": {
2222
"test": "test"
2323
},

src/react-toggle-pattern.js renamed to src/ToggleAndPattern.js

+8-12
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
// LICENSE : MIT
22
"use strict";
33
const React = require("react");
4-
5-
export class TogglePattern extends React.Component {
4+
import {matchAnd} from "./utils/match-props";
5+
export default class ToggleAndPattern extends React.Component {
66
getFlagNames() {
7-
return Object.keys(this.props);
7+
return Object.keys(this.props).filter(key => {
8+
return key !== "children";
9+
});
810
}
911

1012
/**
@@ -19,14 +21,8 @@ export class TogglePattern extends React.Component {
1921
if (!child.props) {
2022
return false;
2123
}
22-
const childKeys = Object.keys(child.props);
23-
return childKeys.some(childKey => {
24-
return flagKeyNames.some(parentKey => {
25-
const parentValue = this.props[parentKey];
26-
const childValue = child.props[childKey];
27-
return childValue === parentValue;
28-
});
29-
});
24+
// all match
25+
return matchAnd(flagKeyNames, this.props, child.props);
3026
});
3127
};
3228

@@ -38,7 +34,7 @@ export class TogglePattern extends React.Component {
3834
if (components.length === 1) {
3935
return components[0];
4036
}
41-
return <div className="TogglePattern">
37+
return <div className="TogglePattern ToggleAndPattern">
4238
{components}
4339
</div>;
4440
}

src/ToggleOrPattern.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// LICENSE : MIT
2+
"use strict";
3+
const React = require("react");
4+
import {matchOr} from "./utils/match-props";
5+
export default class ToggleOrPattern extends React.Component {
6+
getFlagNames() {
7+
return Object.keys(this.props).filter(key => {
8+
return key !== "children";
9+
});
10+
}
11+
12+
/**
13+
* get components from `children` that matches key and value with own props.
14+
* @returns {ReactComponent[]}
15+
*/
16+
getMatchedComponent() {
17+
const children = [].concat(this.props.children);
18+
const flagKeyNames = this.getFlagNames();
19+
return children.filter(child => {
20+
// ignore text child
21+
if (!child.props) {
22+
return false;
23+
}
24+
// all match
25+
return matchOr(flagKeyNames, this.props, child.props);
26+
});
27+
};
28+
29+
render() {
30+
const components = this.getMatchedComponent();
31+
if (components.length === 0) {
32+
return null;
33+
}
34+
if (components.length === 1) {
35+
return components[0];
36+
}
37+
return <div className="TogglePattern ToggleOrPattern">
38+
{components}
39+
</div>;
40+
}
41+
}

src/index.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// LICENSE : MIT
2+
"use strict";
3+
import ToggleAndPattern from "./ToggleAndPattern";
4+
import ToggleOrPattern from "./ToggleOrPattern";
5+
module.exports = {
6+
// default: or pattern
7+
TogglePattern: ToggleOrPattern,
8+
ToggleOrPattern,
9+
ToggleAndPattern
10+
};

src/utils/match-props.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// LICENSE : MIT
2+
"use strict";
3+
export function matchAnd(keys, parentProps, childProps) {
4+
const childKeys = Object.keys(childProps);
5+
// all match
6+
return keys.every(parentKey => {
7+
return childKeys.some(childKey => {
8+
const parentValue = parentProps[parentKey];
9+
const childValue = childProps[childKey];
10+
if (childValue === parentValue) {
11+
return true
12+
} else if (childValue === undefined && parentKey === true) {
13+
// <X attr />
14+
return true;
15+
}
16+
return false;
17+
});
18+
});
19+
}
20+
export function matchOr(keys, parentProps, childProps) {
21+
const childKeys = Object.keys(childProps);
22+
// some match
23+
return keys.some(parentKey => {
24+
return childKeys.some(childKey => {
25+
const parentValue = parentProps[parentKey];
26+
const childValue = childProps[childKey];
27+
if (childValue === parentValue) {
28+
return true
29+
} else if (childValue === undefined && parentKey === true) {
30+
// <X attr />
31+
return true;
32+
}
33+
return false;
34+
});
35+
});
36+
37+
}

test/ToggleAndPattern-test.js

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
const assert = require("power-assert");
2+
import React from "react";
3+
import {shallow} from 'enzyme';
4+
import ToggleAndPattern from "../src/ToggleAndPattern";
5+
class ComponentY extends React.Component {
6+
render() {
7+
return <div>Hidden</div>
8+
}
9+
}
10+
class ComponentX extends React.Component {
11+
render() {
12+
return <div>Visible</div>
13+
}
14+
}
15+
describe('<ToggleAndPattern />', () => {
16+
it('renders 1 <ComponentX /> components', () => {
17+
const result = shallow(<ToggleAndPattern isEditing={true}>
18+
<ComponentX isEditing/>
19+
<ComponentY/>
20+
</ToggleAndPattern>);
21+
assert(result.is(ComponentX));
22+
});
23+
it('renders 1 <ComponentY /> components', () => {
24+
const result = shallow(<ToggleAndPattern isEditing={false}>
25+
<ComponentX isEditing={true}/>
26+
<ComponentY isEditing={false}/>
27+
</ToggleAndPattern>);
28+
assert(result.is(ComponentY));
29+
});
30+
it('renders 0 components', () => {
31+
const result = shallow(<ToggleAndPattern isEditing={false}>
32+
<ComponentX isEditing={true}/>
33+
<ComponentY />
34+
</ToggleAndPattern>);
35+
assert(result.node === null);
36+
});
37+
it('renders 2 <ComponentX /> components', () => {
38+
const wrapper = shallow(<ToggleAndPattern isEditing={true}>
39+
<ComponentX isEditing={true}/>
40+
<ComponentX isEditing={true}/>
41+
</ToggleAndPattern>);
42+
const result = wrapper.find(ComponentX);
43+
assert(result.length === 2);
44+
assert.equal(wrapper.html(), `<div class="TogglePattern ToggleAndPattern"><div>Visible</div><div>Visible</div></div>`)
45+
});
46+
it('no renders <ComponentY /> components', () => {
47+
const wrapper = shallow(<ToggleAndPattern isEditing={true}>
48+
<ComponentY />
49+
</ToggleAndPattern>);
50+
const result = wrapper.find(ComponentX);
51+
assert(result.length === 0);
52+
});
53+
54+
it('match any type value', () => {
55+
const wrapper = shallow(<ToggleAndPattern pattern="one">
56+
<ComponentX pattern="one"/>
57+
<ComponentY pattern="two"/>
58+
</ToggleAndPattern>);
59+
assert(wrapper.is(ComponentX));
60+
61+
const symbol = {};
62+
const wrapper1 = shallow(<ToggleAndPattern pattern={symbol}>
63+
<ComponentX pattern={symbol}/>
64+
<ComponentY pattern="two"/>
65+
</ToggleAndPattern>);
66+
assert(wrapper1.is(ComponentX));
67+
});
68+
it('safe handling mixed text', () => {
69+
const wrapper = shallow(<ToggleAndPattern pattern={1}>
70+
<ComponentX pattern={1}/>
71+
text
72+
<ComponentY pattern={2}/>
73+
</ToggleAndPattern>);
74+
assert(wrapper.is(ComponentX));
75+
});
76+
it('render match And pattern', () => {
77+
const wrapper = shallow(<ToggleAndPattern pattern1={1} pattern2={2}>
78+
<ComponentX pattern1={1} pattern2={2}/>
79+
<ComponentY pattern1={1} />
80+
</ToggleAndPattern>);
81+
assert(wrapper.is(ComponentX));
82+
});
83+
});

0 commit comments

Comments
 (0)