Skip to content

feat(component): add <ToggleAndPattern />, <ToggleOrPattern /> components #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 77 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ Install with [npm](https://www.npmjs.com/):

## Usage

`react-toggle-pattern` provide three components.

- `<TogglePattern />` or pattern. This is same with `<ToggleOrPattern />`
- `<ToggleOrPattern />` or pattern
- `<ToggleAndPattern />` and pattern

Put `<YourComponent />` into `<TogglePattern />`.

```js
Expand Down Expand Up @@ -38,6 +44,51 @@ It means that
- `anyAttribute` is any name.
- `anyValue` is any type.

`<ToggleOrPattern />` and `<ToggleAndPattern />` has same interface.

### OR AND pattern

#### OR

`<ToggleOrPattern />` filter child components by **OR** matching.

```js
<ToggleOrPattern a={true}>
<LeaveEditingButton a={true} b={false} />
<EnterEditingButton a={true} />
</ToggleOrPattern>
```

Result to:

```html
<div class="TogglePattern ToggleOrPattern">
<LeaveEditingButton a={true} b={false} />
<EnterEditingButton a={true} />
</div>
```

Both components are **or** match with TogglePattern.

#### AND

`<ToggleAndPattern />` filter child components by **AND** matching.

```js
<ToggleAndPattern a={true}>
<LeaveEditingButton a={true} b={false} />
<EnterEditingButton a={true} />
</ToggleAndPattern>
```

Result to:

```html
<LeaveEditingButton a={true} b={false} />
```

`<EnterEditingButton />` is not **and** match with TogglePattern.

### Example

Show component that has truly attribute with `<TogglePattern attribute />`
Expand Down Expand Up @@ -88,7 +139,7 @@ Show component**s** that match attribute and value with `<TogglePattern attribut
</TogglePattern>
```

Result to `<div class="TogglePattern"><ComponentX /><ComponentX /></div>`
Result to `<div class="TogglePattern ToggleOrPattern"><ComponentX /><ComponentX /></div>`

-----

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

Result to `null`.

------

OR match

```js
<ToggleOrPattern pattern1={1} pattern2={2}>
<ComponentX pattern1={1} pattern2={2}/>
<ComponentY pattern1={1}/>
</ToggleOrPattern>
```

Result to `<div class="TogglePattern ToggleOrPattern"><div>Visible</div><div>Hidden</div></div>`.

------

And match

````js
<ToggleAndPattern pattern1={1} pattern2={2}>
<ComponentX pattern1={1} pattern2={2}/>
<ComponentY pattern1={1} />
</ToggleAndPattern>
```

Result to `<ComponentX pattern1={1} pattern2={2}/>`.


## Changelog
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
],
"version": "1.1.1",
"description": "React Component that provide toggle pattern",
"main": "lib/react-toggle-pattern.js",
"main": "lib/index.js",
"directories": {
"test": "test"
},
Expand Down
20 changes: 8 additions & 12 deletions src/react-toggle-pattern.js → src/ToggleAndPattern.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// LICENSE : MIT
"use strict";
const React = require("react");

export class TogglePattern extends React.Component {
import {matchAnd} from "./utils/match-props";
export default class ToggleAndPattern extends React.Component {
getFlagNames() {
return Object.keys(this.props);
return Object.keys(this.props).filter(key => {
return key !== "children";
});
}

/**
Expand All @@ -19,14 +21,8 @@ export class TogglePattern extends React.Component {
if (!child.props) {
return false;
}
const childKeys = Object.keys(child.props);
return childKeys.some(childKey => {
return flagKeyNames.some(parentKey => {
const parentValue = this.props[parentKey];
const childValue = child.props[childKey];
return childValue === parentValue;
});
});
// all match
return matchAnd(flagKeyNames, this.props, child.props);
});
};

Expand All @@ -38,7 +34,7 @@ export class TogglePattern extends React.Component {
if (components.length === 1) {
return components[0];
}
return <div className="TogglePattern">
return <div className="TogglePattern ToggleAndPattern">
{components}
</div>;
}
Expand Down
41 changes: 41 additions & 0 deletions src/ToggleOrPattern.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// LICENSE : MIT
"use strict";
const React = require("react");
import {matchOr} from "./utils/match-props";
export default class ToggleOrPattern extends React.Component {
getFlagNames() {
return Object.keys(this.props).filter(key => {
return key !== "children";
});
}

/**
* get components from `children` that matches key and value with own props.
* @returns {ReactComponent[]}
*/
getMatchedComponent() {
const children = [].concat(this.props.children);
const flagKeyNames = this.getFlagNames();
return children.filter(child => {
// ignore text child
if (!child.props) {
return false;
}
// all match
return matchOr(flagKeyNames, this.props, child.props);
});
};

render() {
const components = this.getMatchedComponent();
if (components.length === 0) {
return null;
}
if (components.length === 1) {
return components[0];
}
return <div className="TogglePattern ToggleOrPattern">
{components}
</div>;
}
}
10 changes: 10 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// LICENSE : MIT
"use strict";
import ToggleAndPattern from "./ToggleAndPattern";
import ToggleOrPattern from "./ToggleOrPattern";
module.exports = {
// default: or pattern
TogglePattern: ToggleOrPattern,
ToggleOrPattern,
ToggleAndPattern
};
37 changes: 37 additions & 0 deletions src/utils/match-props.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// LICENSE : MIT
"use strict";
export function matchAnd(keys, parentProps, childProps) {
const childKeys = Object.keys(childProps);
// all match
return keys.every(parentKey => {
return childKeys.some(childKey => {
const parentValue = parentProps[parentKey];
const childValue = childProps[childKey];
if (childValue === parentValue) {
return true
} else if (childValue === undefined && parentKey === true) {
// <X attr />
return true;
}
return false;
});
});
}
export function matchOr(keys, parentProps, childProps) {
const childKeys = Object.keys(childProps);
// some match
return keys.some(parentKey => {
return childKeys.some(childKey => {
const parentValue = parentProps[parentKey];
const childValue = childProps[childKey];
if (childValue === parentValue) {
return true
} else if (childValue === undefined && parentKey === true) {
// <X attr />
return true;
}
return false;
});
});

}
83 changes: 83 additions & 0 deletions test/ToggleAndPattern-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const assert = require("power-assert");
import React from "react";
import {shallow} from 'enzyme';
import ToggleAndPattern from "../src/ToggleAndPattern";
class ComponentY extends React.Component {
render() {
return <div>Hidden</div>
}
}
class ComponentX extends React.Component {
render() {
return <div>Visible</div>
}
}
describe('<ToggleAndPattern />', () => {
it('renders 1 <ComponentX /> components', () => {
const result = shallow(<ToggleAndPattern isEditing={true}>
<ComponentX isEditing/>
<ComponentY/>
</ToggleAndPattern>);
assert(result.is(ComponentX));
});
it('renders 1 <ComponentY /> components', () => {
const result = shallow(<ToggleAndPattern isEditing={false}>
<ComponentX isEditing={true}/>
<ComponentY isEditing={false}/>
</ToggleAndPattern>);
assert(result.is(ComponentY));
});
it('renders 0 components', () => {
const result = shallow(<ToggleAndPattern isEditing={false}>
<ComponentX isEditing={true}/>
<ComponentY />
</ToggleAndPattern>);
assert(result.node === null);
});
it('renders 2 <ComponentX /> components', () => {
const wrapper = shallow(<ToggleAndPattern isEditing={true}>
<ComponentX isEditing={true}/>
<ComponentX isEditing={true}/>
</ToggleAndPattern>);
const result = wrapper.find(ComponentX);
assert(result.length === 2);
assert.equal(wrapper.html(), `<div class="TogglePattern ToggleAndPattern"><div>Visible</div><div>Visible</div></div>`)
});
it('no renders <ComponentY /> components', () => {
const wrapper = shallow(<ToggleAndPattern isEditing={true}>
<ComponentY />
</ToggleAndPattern>);
const result = wrapper.find(ComponentX);
assert(result.length === 0);
});

it('match any type value', () => {
const wrapper = shallow(<ToggleAndPattern pattern="one">
<ComponentX pattern="one"/>
<ComponentY pattern="two"/>
</ToggleAndPattern>);
assert(wrapper.is(ComponentX));

const symbol = {};
const wrapper1 = shallow(<ToggleAndPattern pattern={symbol}>
<ComponentX pattern={symbol}/>
<ComponentY pattern="two"/>
</ToggleAndPattern>);
assert(wrapper1.is(ComponentX));
});
it('safe handling mixed text', () => {
const wrapper = shallow(<ToggleAndPattern pattern={1}>
<ComponentX pattern={1}/>
text
<ComponentY pattern={2}/>
</ToggleAndPattern>);
assert(wrapper.is(ComponentX));
});
it('render match And pattern', () => {
const wrapper = shallow(<ToggleAndPattern pattern1={1} pattern2={2}>
<ComponentX pattern1={1} pattern2={2}/>
<ComponentY pattern1={1} />
</ToggleAndPattern>);
assert(wrapper.is(ComponentX));
});
});
Loading