Skip to content

Commit

Permalink
Add componentType for component matching
Browse files Browse the repository at this point in the history
  • Loading branch information
cheton committed Dec 14, 2017
1 parent 72ae6b7 commit 1341b03
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 21 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
15 changes: 13 additions & 2 deletions src/Nav.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import cx from 'classnames';
import PropTypes from 'prop-types';
import React, { PureComponent, cloneElement } from 'react';
import NavItem from './NavItem';
import match from './match-component';
import styles from './index.styl';

const noop = () => {};
Expand Down Expand Up @@ -41,6 +42,8 @@ class Nav extends PureComponent {
defaultSelected: this.props.defaultSelected
};

isNavItem = match(NavItem);

handleClickOnExpanded(eventKey, event) {
if (this.props.expanded) {
this.setState(state => ({
Expand Down Expand Up @@ -71,6 +74,7 @@ class Nav extends PureComponent {
}
render() {
const {
componentType, // eslint-disable-line
componentClass: Component,
onSelect,
selected,
Expand All @@ -92,10 +96,14 @@ class Nav extends PureComponent {
<Component
{...props}
role="menu"
className={cx(className, styles.sidenavNav)}
className={cx(
className,
styles.sidenavNav,
{ [styles.expanded]: expanded }
)}
>
{React.Children.map(children, child => {
if (React.isValidElement(child) && (child.type === NavItem)) {
if (React.isValidElement(child) && this.isNavItem(child)) {
return this.renderNavItem(child, {
onSelect,
selected: currentSelected,
Expand All @@ -111,4 +119,7 @@ class Nav extends PureComponent {
}
}

// For component matching
Nav.defaultProps.componentType = Nav;

export default Nav;
6 changes: 6 additions & 0 deletions src/NavIcon.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@ const NavIcon = () => {
throw new Error('should not render NavIcon component');
};

// For component matching
NavIcon.defaultProps = {
...NavIcon.defaultProps,
componentType: NavIcon
};

export default NavIcon;
21 changes: 15 additions & 6 deletions src/NavItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import React, { PureComponent, cloneElement } from 'react';
import NavIcon from './NavIcon';
import NavText from './NavText';
import findComponent from './find-component';
import match from './match-component';
import styles from './index.styl';

const noop = () => {};
const findNavIcon = findComponent(NavIcon);
const findNavText = findComponent(NavText);

class NavItem extends PureComponent {
static propTypes = {
Expand Down Expand Up @@ -62,6 +61,12 @@ class NavItem extends PureComponent {
expanded: false
};

findNavIcon = findComponent(NavIcon);
findNavText = findComponent(NavText);
isNavItem = match(NavItem);
isNavIcon = match(NavIcon);
isNavText = match(NavText);

handleSelect = (event) => {
const { href, disabled, onSelect, eventKey } = this.props;

Expand All @@ -80,6 +85,7 @@ class NavItem extends PureComponent {

render() {
const {
componentType, // eslint-disable-line
componentClass: Component,
active,
disabled,
Expand All @@ -101,8 +107,8 @@ class NavItem extends PureComponent {
...props
} = this.props;

const navIcon = findNavIcon(children);
const navText = findNavText(children);
const navIcon = this.findNavIcon(children);
const navText = this.findNavText(children);

if (subnav) {
const highlighted = active ||
Expand Down Expand Up @@ -141,7 +147,7 @@ class NavItem extends PureComponent {
const activeNavItems = [];
const navItems = React.Children.toArray(children)
.filter(child => {
return React.isValidElement(child) && (child.type === NavItem);
return React.isValidElement(child) && this.isNavItem(child);
})
.map(child => {
if (child.props.active || (!!selected && selected === child.props.eventKey)) {
Expand All @@ -159,7 +165,7 @@ class NavItem extends PureComponent {
});
const others = React.Children.toArray(children)
.filter(child => {
if (React.isValidElement(child) && (child.type === NavIcon || child.type === NavText || child.type === NavItem)) {
if (React.isValidElement(child) && (this.isNavIcon(child) || this.isNavText(child) || this.isNavItem(child))) {
return false;
}
return true;
Expand Down Expand Up @@ -224,4 +230,7 @@ class NavItem extends PureComponent {
}
}

// For component matching
NavItem.defaultProps.componentType = NavItem;

export default NavItem;
6 changes: 6 additions & 0 deletions src/NavText.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@ const NavText = () => {
throw new Error('should not render NavText component');
};

// For component matching
NavText.defaultProps = {
...NavText.defaultProps,
componentType: NavText
};

export default NavText;
23 changes: 13 additions & 10 deletions src/SideNav.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import NavItem from './NavItem';
import NavIcon from './NavIcon';
import NavText from './NavText';
import styles from './index.styl';
import match from './match-component';

class SideNav extends PureComponent {
static propTypes = {
Expand All @@ -35,11 +36,13 @@ class SideNav extends PureComponent {
componentClass: 'nav'
};

c = {
isToggle = match(Toggle);
isNav = match(Nav);

child = {
toggle: null,
nav: null
};
lastToggleEventType = null;

handleClick = (event) => {
if (this.props.disabled) {
Expand All @@ -52,17 +55,13 @@ class SideNav extends PureComponent {
toggleExpanded(eventType) {
const expanded = !this.props.expanded;

if (expanded) {
this.lastToggleEventType = eventType;
}

if (this.props.onToggle) {
this.props.onToggle(expanded);
}
}
renderToggle(child, props) {
let ref = c => {
this.c.toggle = c;
this.child.toggle = c;
};

if (typeof child.ref === 'string') {
Expand All @@ -86,7 +85,7 @@ class SideNav extends PureComponent {
}
renderNav(child, { onSelect, ...props }) {
let ref = c => {
this.c.nav = c;
this.child.nav = c;
};

if (typeof child.ref === 'string') {
Expand All @@ -110,6 +109,7 @@ class SideNav extends PureComponent {
}
render() {
const {
componentType, // eslint-disable-line
componentClass: Component,
disabled,
expanded,
Expand Down Expand Up @@ -137,13 +137,13 @@ class SideNav extends PureComponent {
return child;
}

if (child.type === Toggle) {
if (this.isToggle(child)) {
return this.renderToggle(child, {
disabled, expanded
});
}

if (child.type === Nav) {
if (this.isNav(child)) {
return this.renderNav(child, {
onSelect, expanded
});
Expand All @@ -156,6 +156,9 @@ class SideNav extends PureComponent {
}
}

// For component matching
SideNav.defaultProps.componentType = SideNav;

const UncontrollableSideNav = uncontrollable(SideNav, {
// Define the pairs of prop/handlers you want to be uncontrollable
expanded: 'onToggle'
Expand Down
8 changes: 6 additions & 2 deletions src/Toggle.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ class Toggle extends PureComponent {

render() {
const {
componentType, // eslint-disable-line
componentClass: Component,
expanded,
className,
children,
...props
} = this.props;

delete props.children;

return (
<Component
{...props}
Expand All @@ -46,9 +46,13 @@ class Toggle extends PureComponent {
<span className={styles.iconBar} />
<span className={styles.iconBar} />
<span className={styles.iconBar} />
{children}
</Component>
);
}
}

// For component matching
Toggle.defaultProps.componentType = Toggle;

export default Toggle;
5 changes: 4 additions & 1 deletion src/find-component.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React from 'react';
import match from './match-component';

const findComponent = (component) => (children) => {
const matchComponent = match(component);

return React.Children.toArray(children).reduce((found, next) => {
if (found === null && next !== null && next.type === component) {
if (found === null && next !== null && matchComponent(next)) {
return next;
}
return found;
Expand Down
15 changes: 15 additions & 0 deletions src/match-component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const matchComponent = (Component) => (c) => {
// React Component
if (c.type === Component) {
return true;
}

// Matching componentType for SideNav, Nav, NavItem, NavIcon, NavText
if (c.props && c.props.componentType === Component) {
return true;
}

return false;
};

export default matchComponent;

0 comments on commit 1341b03

Please sign in to comment.