Skip to content

Commit

Permalink
Merge pull request #280 from cwmoo740/master
Browse files Browse the repository at this point in the history
Allow passing in custom React.Components for navbarElement, captionElement, weekdayElement
  • Loading branch information
gpbl authored Mar 8, 2017
2 parents c9ab28f + a8cb204 commit 9fb17f8
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 20 deletions.
12 changes: 6 additions & 6 deletions docs/APIProps.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ Enable the navigation between months.

### captionElement

**Type**: `Element`
**Type**: `Element` || `React.Component` || `(props) => Element`

A React element to use as caption. This element is cloned with the following props:
A React element or constructor to use as caption. This element will receive the following props:

* `date: Date` The currently displayed month.
* `localeUtils: Object` The [localeUtils](#localeutils-object) object passed to the component.
Expand Down Expand Up @@ -183,9 +183,9 @@ An array containing the long weekdays names to use in the month's header.

### navbarElement

**Type**: `Element`
**Type**: `Element` || `React.Component` || `(props) => Element`

Custom React element to render the navigation bar. It will receive the following props:
Custom React element or constructor to render the navigation bar. It will receive the following props:

* className `String`
* previousMonth `Date`
Expand Down Expand Up @@ -238,9 +238,9 @@ The last allowed month. Users won't be able to navigate or interact with the day

### weekdayElement

**Type**: `Element`
**Type**: `Element` || `React.Component` || `(props) => Element`

Custom React element to render the weekday cells in the header. It will receive the following props:
Custom React element or constructor to render the weekday cells in the header. It will receive the following props:

* weekday `Number`
* className `String`
Expand Down
28 changes: 19 additions & 9 deletions src/DayPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,21 @@ export default class DayPicker extends Component {

// Custom elements
renderDay: PropTypes.func,
weekdayElement: PropTypes.element,
navbarElement: PropTypes.element,
captionElement: PropTypes.element,
weekdayElement: PropTypes.oneOfType([
PropTypes.element,
PropTypes.func,
PropTypes.instanceOf(Component),
]),
navbarElement: PropTypes.oneOfType([
PropTypes.element,
PropTypes.func,
PropTypes.instanceOf(Component),
]),
captionElement: PropTypes.oneOfType([
PropTypes.element,
PropTypes.func,
PropTypes.instanceOf(Component),
]),

// Events
onBlur: PropTypes.func,
Expand Down Expand Up @@ -418,7 +430,9 @@ export default class DayPicker extends Component {
locale,
localeUtils,
};
return React.cloneElement(navbarElement, props);
return React.isValidElement(navbarElement) ?
React.cloneElement(navbarElement, props) :
React.createElement(navbarElement, props);
}
renderDayInMonth(day, month) {
let dayModifiers = [];
Expand Down Expand Up @@ -489,11 +503,7 @@ export default class DayPicker extends Component {
months={ this.props.months }

weekdayElement={ this.props.weekdayElement }
captionElement={
React.cloneElement(this.props.captionElement, {
classNames: this.props.classNames,
})
}
captionElement={ this.props.captionElement }
fixedWeeks={ this.props.fixedWeeks }

weekdaysShort={ this.props.weekdaysShort }
Expand Down
19 changes: 16 additions & 3 deletions src/Month.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,20 @@ export default function Month({
}) {
const captionProps = {
date: month,
classNames,
months,
localeUtils,
locale,
onClick: onCaptionClick ? e => onCaptionClick(month, e) : undefined,
};
const caption = React.isValidElement(captionElement) ?
React.cloneElement(captionElement, captionProps) :
React.createElement(captionElement, captionProps);

const weeks = getWeekArray(month, firstDayOfWeek, fixedWeeks);
return (
<div className={ classNames.month }>
{React.cloneElement(captionElement, captionProps)}
{caption}
<Weekdays
classNames={ classNames }
weekdaysShort={ weekdaysShort }
Expand Down Expand Up @@ -65,8 +70,16 @@ Month.propTypes = {
months: React.PropTypes.arrayOf(React.PropTypes.string),

fixedWeeks: PropTypes.bool,
captionElement: PropTypes.node.isRequired,
weekdayElement: PropTypes.element,
captionElement: PropTypes.oneOfType([
PropTypes.element,
PropTypes.func,
PropTypes.instanceOf(React.Component),
]).isRequired,
weekdayElement: PropTypes.oneOfType([
PropTypes.element,
PropTypes.func,
PropTypes.instanceOf(React.Component),
]),

locale: PropTypes.string.isRequired,
localeUtils: DayPickerPropTypes.localeUtils.isRequired,
Expand Down
10 changes: 8 additions & 2 deletions src/Weekdays.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export default function Weekdays({
localeUtils,
locale,
};
const element = React.cloneElement(weekdayElement, elementProps);
const element = React.isValidElement(weekdayElement) ?
React.cloneElement(weekdayElement, elementProps) :
React.createElement(weekdayElement, elementProps);
days.push(element);
}

Expand All @@ -48,5 +50,9 @@ Weekdays.propTypes = {
weekdaysShort: PropTypes.arrayOf(PropTypes.string),
locale: PropTypes.string.isRequired,
localeUtils: DayPickerPropTypes.localeUtils.isRequired,
weekdayElement: PropTypes.element,
weekdayElement: PropTypes.oneOf([
PropTypes.element,
PropTypes.func,
PropTypes.instanceOf(React.Component),
]),
};
79 changes: 79 additions & 0 deletions test/daypicker/rendering.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,24 @@ describe('DayPicker’s rendering', () => {
const wrapper = mount(<DayPicker captionElement={ <Caption /> } />);
expect(wrapper.containsMatchingElement(<Caption />)).to.be.true;
});
it('should render a custom caption element as a function', () => {
const Caption = () => <p>boo</p>;
const wrapper = mount(<DayPicker captionElement={ Caption } />);
expect(wrapper.containsMatchingElement(<Caption />)).to.be.true;
});
it('should render a custom caption element as a class', () => {
/* eslint-disable react/prefer-stateless-function */
/* eslint-disable react/no-multi-comp */
class Caption extends React.Component {
render() {
return <p>boo</p>;
}
}
const wrapper = mount(<DayPicker captionElement={ Caption } />);
expect(wrapper.containsMatchingElement(<Caption />)).to.be.true;
/* eslint-enable react/no-multi-comp */
/* eslint-enable react/prefer-stateless-function */
});
it('should render a custom navbar element', () => {
const CustomNavbar = ({ className }) => <div className={ className }>Navbar</div>;
CustomNavbar.propTypes = { className: PropTypes.string };
Expand All @@ -166,6 +184,32 @@ describe('DayPicker’s rendering', () => {
expect(wrapper.find('.DayPicker-NavBar')).to.exist;
expect(wrapper.find('.DayPicker-NavBar').at(0)).to.have.text('Navbar');
});
it('should render a custom navbar element as a function', () => {
const CustomNavbar = ({ className }) => <div className={ className }>Navbar</div>;
CustomNavbar.propTypes = { className: PropTypes.string };
const wrapper = mount(<DayPicker navbarElement={ CustomNavbar } />);

expect(wrapper.containsMatchingElement(<CustomNavbar />)).to.be.true;
expect(wrapper.find('.DayPicker-NavBar')).to.exist;
expect(wrapper.find('.DayPicker-NavBar').at(0)).to.have.text('Navbar');
});
it('should render a custom navbar element as a class', () => {
/* eslint-disable react/prefer-stateless-function */
/* eslint-disable react/no-multi-comp */
class CustomNavbar extends React.Component {
static propTypes = { className: PropTypes.string };
render() {
return <div className={ this.props.className }>Navbar</div>;
}
}
const wrapper = mount(<DayPicker navbarElement={ CustomNavbar } />);

expect(wrapper.containsMatchingElement(<CustomNavbar />)).to.be.true;
expect(wrapper.find('.DayPicker-NavBar')).to.exist;
expect(wrapper.find('.DayPicker-NavBar').at(0)).to.have.text('Navbar');
/* eslint-enable react/prefer-stateless-function */
/* eslint-enable react/no-multi-comp */
});
it('should render a custom weekday element', () => {
const CustomWeekday = ({ className, weekday }) =>
<div className={ className }>{weekday}</div>;
Expand All @@ -182,6 +226,41 @@ describe('DayPicker’s rendering', () => {
expect(weekdayDoms.at(i)).to.have.text(i);
});
});
it('should render a custom weekday element as a function', () => {
const CustomWeekday = ({ className, weekday }) =>
<div className={ className }>{weekday}</div>;
CustomWeekday.propTypes = { className: PropTypes.string, weekday: PropTypes.number };
const dayPicker = <DayPicker weekdayElement={ CustomWeekday } />;
const wrapper = mount(dayPicker);

expect(wrapper.containsMatchingElement(<CustomWeekday />)).to.be.true;
expect(wrapper.find('.DayPicker-Weekday')).to.have.length(7);
const weekdayDoms = wrapper.find('.DayPicker-Weekday');
weekdayDoms.forEach((_, i) => {
expect(weekdayDoms.at(i)).to.have.text(i);
});
});
it('should render a custom weekday element as a class', () => {
/* eslint-disable react/prefer-stateless-function */
/* eslint-disable react/no-multi-comp */
class CustomWeekday extends React.Component {
static propTypes = { className: PropTypes.string, weekday: PropTypes.number };
render() {
return <div className={ this.props.className }>{this.props.weekday}</div>;
}
}
const dayPicker = <DayPicker weekdayElement={ CustomWeekday } />;
const wrapper = mount(dayPicker);

expect(wrapper.containsMatchingElement(<CustomWeekday />)).to.be.true;
expect(wrapper.find('.DayPicker-Weekday')).to.have.length(7);
const weekdayDoms = wrapper.find('.DayPicker-Weekday');
weekdayDoms.forEach((_, i) => {
expect(weekdayDoms.at(i)).to.have.text(i);
});
/* eslint-enable react/prefer-stateless-function */
/* eslint-enable react/no-multi-comp */
});
it('should not render the outside days', () => {
const wrapper = mount(<DayPicker initialMonth={ new Date(2015, 6) } />);
expect(wrapper.find('.DayPicker-Day').at(0)).to.have.text('');
Expand Down

0 comments on commit 9fb17f8

Please sign in to comment.