Skip to content
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

Support for customizable static range labels #271

Merged
merged 3 commits into from
Jan 21, 2019
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
1 change: 1 addition & 0 deletions . eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/node_modules/*
/lib/*
/dist/*
**/*.snap
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ Make sure your issue or feature doesn't have any related issue at [react-date-ra

3. Run `yarn` to install the dependencies.

4. Run `yarn dev` to start development server.
4. Run `yarn run dev` to start development server.

## Building

. Run `run test` and `run lint` for make sure tests passes and linter doesn't throw any error.
. Run `yarn run test` and `yarn run lint` for make sure tests passes and linter doesn't throw any error.

. Run `yarn build` compile the library and demo source.
. Run `yarn run build` compile the library and demo source.

. Push your changes and create a PR and apply code review decisions.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ showPreview(DateRange) | bool | true | visibility
dragSelectionEnabled(Calendar) | bool | true | whether dates can be selected via drag n drop
onPreviewChange(DateRange) | Object | | Callback function for preview changes
dateDisplayFormat(DateRange) | String | `MMM D, YYYY` | selected range preview formatter. Check out [date-fns's format option](https://date-fns.org/v2.0.0-alpha.7/docs/format)
renderStaticRangeLabel(`DefinedRange`)| Function | | Callback function to be triggered for the static range configurations that have `hasCustomRendering: true` on them. Instead of rendering `staticRange.label`, return value of this callback will be rendered.
mortargrind marked this conversation as resolved.
Show resolved Hide resolved
staticRanges(`DefinedRange`, `DateRangePicker`) | Array | [default preDefined ranges](https://github.com/Adphorus/react-date-range/blob/master/src/defaultRanges.js) | -
inputRanges(`DefinedRange`, `DateRangePicker`) | Array | [default input ranges](https://github.com/Adphorus/react-date-range/blob/master/src/defaultRanges.js) | -

Expand Down
86 changes: 83 additions & 3 deletions demo/src/components/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,51 @@ import * as rdrLocales from '../../../src/locale';
import { format, addDays } from 'date-fns';
import Section from './Section';

function renderStaticRangeLabel(staticRange) {
return (
<CustomStaticRangeLabelContent text={'This is a custom label content: '}/>
);
}

class CustomStaticRangeLabelContent extends React.Component {
constructor(props) {
super(props);

this.state = {
currentDateString: Date(),
};

this.intervalId = setInterval(
() => {
this.setState({
currentDateString: Date(),
});
},
1000
);
}

componentWillUnmount() {
if (this.intervalId) {
clearInterval(this.intervalId);
}
}

render() {
const { currentDateString } = this.state;
const { text } = this.props;

return (
<span>
<i>{text}</i>
<span className={'random-date-string'}>
<b>{currentDateString}</b>
</span>
</span>
);
}
}

const nameMapper = {
ar: 'Arabic (Modern Standard Arabic - Al-fussha)',
bg: 'Bulgarian',
Expand Down Expand Up @@ -135,6 +180,7 @@ export default class Main extends Component {
[which]: payload,
});
}

handleRangeChange(which, payload) {
console.log(which, payload);
this.setState({
Expand Down Expand Up @@ -217,7 +263,7 @@ export default class Main extends Component {
readOnly
value={formatDateDisplay(this.state.multipleRanges.selection1.endDate, 'Continuous')}
/>
<div className={'newLine'} />
<div className={'newLine'}/>

<label className={'label'}>Selection2 Start:</label>
<input
Expand All @@ -231,7 +277,7 @@ export default class Main extends Component {
readOnly
value={formatDateDisplay(this.state.multipleRanges.selection2.endDate, 'Continuous')}
/>
<div className={'newLine'} />
<div className={'newLine'}/>

<label className={'label'}>Selection3 Start:</label>
<input
Expand Down Expand Up @@ -319,9 +365,24 @@ export default class Main extends Component {

<DefinedRange
ranges={[this.state.definedRange.selection]}
renderStaticRangeLabel={renderStaticRangeLabel}
staticRanges={[{
label: "Hoy",
hasCustomRendering: true,
range: () => ({
startDate: new Date(),
endDate: new Date(),
}),
isSelected() {
return (
true
);
},
}]}
onChange={this.handleRangeChange.bind(this, 'definedRange')}
className={'centered'}
/>
>
</DefinedRange>
</Section>
<Section title="RangePicker with disabled dates">
<div>
Expand All @@ -348,6 +409,25 @@ export default class Main extends Component {
disabledDates={[new Date(), addDays(new Date(), 3)]}
minDate={addDays(new Date(), -3)}
/>

<DefinedRange
ranges={[this.state.definedRange.selection]}
renderStaticRangeLabel={renderStaticRangeLabel}
staticRanges={[{
hasCustomRendering: true,
range: () => ({
startDate: new Date(),
endDate: new Date(),
}),
isSelected() {
return (
true
);
},
}]}
onChange={this.handleRangeChange.bind(this, 'definedRange')}
className={'centered'}
/>
</Section>
</main>
);
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
],
"contributors": [
"Burak Can <[email protected]> (https://github.com/burakcan)",
"Mehmet Kamil Morcay <[email protected]> (https://github.com/mkg0)"
"Mehmet Kamil Morcay <[email protected]> (https://github.com/mkg0)",
"Engin Semih Basmacı <[email protected]> (https://github.com/mortargrind)"
],
"license": "MIT",
"repository": {
Expand Down
17 changes: 15 additions & 2 deletions src/components/DefinedRange.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class DefinedRanges extends Component {
};
this.handleRangeChange = this.handleRangeChange.bind(this);
}

handleRangeChange(range) {
const { onChange, ranges, focusedRange } = this.props;
const selectedRange = ranges[focusedRange[0]];
Expand All @@ -22,6 +23,7 @@ class DefinedRanges extends Component {
[selectedRange.key || `range${focusedRange[0] + 1}`]: { ...selectedRange, ...range },
});
}

getSelectedRange(ranges, staticRange) {
const focusedRangeIndex = ranges.findIndex(range => {
if (!range.startDate || !range.endDate || range.disabled) return false;
Expand All @@ -30,14 +32,24 @@ class DefinedRanges extends Component {
const selectedRange = ranges[focusedRangeIndex];
return { selectedRange, focusedRangeIndex };
}

render() {
const { onPreviewChange, ranges, rangeColors, className } = this.props;
const { onPreviewChange, ranges, renderStaticRangeLabel, rangeColors, className } = this.props;

return (
<div className={cx(styles.definedRangesWrapper, className)}>
{this.props.headerContent}
<div className={styles.staticRanges}>
{this.props.staticRanges.map((staticRange, i) => {
const { selectedRange, focusedRangeIndex } = this.getSelectedRange(ranges, staticRange);
let labelContent;

if (staticRange.hasCustomRendering) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's make hasCustomRendering true by default if renderStaticRangeLabel already defined. You can still add another property called renderLabel() to static range object for rendering custom range in order to render just for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

staticRange.label can already be rendered via a custom component (label: <MyComponent/>); there's no need for single renderLabel functions for individual staticRange on configuration object level. Whole point of having a render function on component level is let the callers specify custom props / logic to the label rendering logic on their own component's render phase. So yes; this means that if any of the staticRange configurations have hasCustomRendering; then the renderStaticRangeLabel prop should be present too. But the reverse is not true.

Having renderStaticRangeLabel prop provided does not mean that every staticRange object will be rendered in a custom way. So we cannot change hasCustomRendering to true by default if it's present. For example; in the original use-case we have right now is to be able to render a tooltip on one of the staticRange buttons while the others are continue to use staticRange.label.

labelContent = renderStaticRangeLabel(staticRange);
} else {
labelContent = staticRange.label;
}

return (
<button
type="button"
Expand All @@ -59,7 +71,7 @@ class DefinedRanges extends Component {
this.props.onPreviewChange && this.props.onPreviewChange();
}}>
<span tabIndex={-1} className={styles.staticRangeLabel}>
{staticRange.label}
{labelContent}
</span>
</button>
);
Expand Down Expand Up @@ -106,6 +118,7 @@ DefinedRanges.propTypes = {
headerContent: PropTypes.any,
rangeColors: PropTypes.arrayOf(PropTypes.string),
className: PropTypes.string,
renderStaticRangeLabel: PropTypes.func,
};

DefinedRanges.defaultProps = {
Expand Down
135 changes: 135 additions & 0 deletions src/components/DefinedRange.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React from 'react';
import { mount, shallow } from 'enzyme';

import DefinedRange from './DefinedRange';
import { isSameDay } from 'date-fns';

describe('DefinedRange tests', () => {
test('Should call "renderStaticRangeLabel" callback correct amount of times according to the "hasCustomRendering" option', () => {
const renderStaticRangeLabel = jest.fn();

mount(
<DefinedRange
staticRanges={[
{
label: 'Dynamic Label',
range: {},
isSelected(range) {
const definedRange = this.range();
return (
isSameDay(range.startDate, definedRange.startDate) &&
isSameDay(range.endDate, definedRange.endDate)
);
},
hasCustomRendering: true,
},
{
label: 'Static Label',
range: {},
isSelected(range) {
const definedRange = this.range();
return (
isSameDay(range.startDate, definedRange.startDate) &&
isSameDay(range.endDate, definedRange.endDate)
);
},
},
{
label: 'Hede',
range: {},
isSelected(range) {
const definedRange = this.range();
return (
isSameDay(range.startDate, definedRange.startDate) &&
isSameDay(range.endDate, definedRange.endDate)
);
},
hasCustomRendering: true,
},
]}
renderStaticRangeLabel={renderStaticRangeLabel}
/>
);

expect(renderStaticRangeLabel).toHaveBeenCalledTimes(2);
});

test('Should render dynamic static label contents correctly', () => {
const renderItalicLabelContent = () => (
<i className={'italic-label-content'}>{'Italic Content'}</i>
);
const renderBoldLabelContent = () => <b className={'bold-label-content'}>{'Bold Content'}</b>;
const renderSomethingElse = () => <img className={'random-image'} />;

const renderStaticRangeLabel = function(staticRange) {
let result;

if (staticRange.id === 'italic') {
result = renderItalicLabelContent();
} else if (staticRange.id === 'bold') {
result = renderBoldLabelContent();
} else {
result = renderSomethingElse();
}

return result;
};

const wrapper = shallow(
<DefinedRange
staticRanges={[
{
id: 'italic',
range: {},
isSelected(range) {
const definedRange = this.range();
return (
isSameDay(range.startDate, definedRange.startDate) &&
isSameDay(range.endDate, definedRange.endDate)
);
},
hasCustomRendering: true,
},
{
label: 'Static Label',
range: {},
isSelected(range) {
const definedRange = this.range();
return (
isSameDay(range.startDate, definedRange.startDate) &&
isSameDay(range.endDate, definedRange.endDate)
);
},
},
{
id: 'whatever',
range: {},
isSelected(range) {
const definedRange = this.range();
return (
isSameDay(range.startDate, definedRange.startDate) &&
isSameDay(range.endDate, definedRange.endDate)
);
},
hasCustomRendering: true,
},
{
id: 'bold',
range: {},
isSelected(range) {
const definedRange = this.range();
return (
isSameDay(range.startDate, definedRange.startDate) &&
isSameDay(range.endDate, definedRange.endDate)
);
},
hasCustomRendering: true,
},
]}
renderStaticRangeLabel={renderStaticRangeLabel}
/>
);

expect(wrapper).toMatchSnapshot();
});
});
Loading