Skip to content

Commit

Permalink
[EuiSuperDatePicker] add showApplyButton prop (#1399)
Browse files Browse the repository at this point in the history
* [EuiSuperDatePicker] add showApplyButton prop

* use getDerivedStateFromProps instead of key so component does not get remounted which causes the popovers to close unexpectedly

* update changelog

* rename showApplyButton to showUpdateButton, move fields in example to under super date picker

* update changelog
  • Loading branch information
nreese authored Jan 3, 2019
1 parent 8b81242 commit 29e07d0
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 93 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Convert the other of the services to TypeScript ([#1392](https://github.com/elastic/eui/pull/1392))
- Changed single selection to select existing option in the list ([#1391](https://github.com/elastic/eui/pull/1391))
- Added `showUpdateButton` prop to `EuiSuperDatePicker` ([#1399](https://github.com/elastic/eui/pull/1399))

## [`6.0.1`](https://github.com/elastic/eui/tree/v6.0.1)

Expand All @@ -22,7 +23,6 @@

- Only style anchor tags in `EuiText` that have no class attribute ([#1373](https://github.com/elastic/eui/pull/1373))
- Fixed some EUI services' TS definitions ([#1380](https://github.com/elastic/eui/pull/1380))
- `EuiColorPicker` align color picker popup with color selector when page is scrolled ([#1397](https://github.com/elastic/eui/pull/1397))

**Breaking changes**

Expand Down
17 changes: 17 additions & 0 deletions src-docs/src/views/date_picker/date_picker_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,23 @@ export const DatePickerExample = {
in either datemath format (e.g.: now, now-15m, now-15m/m)
or as absolute date in the format <EuiCode>YYYY-MM-DDTHH:mm:ss.sssZ</EuiCode>
</p>
<p>
<EuiCode>onTimeChange</EuiCode> will be immediately invoked when{' '}
<EuiCode>start</EuiCode> and <EuiCode>end</EuiCode> change from interactions with{' '}
<strong> Quick select</strong>, <strong>Commonly used</strong>, or <strong>Recently used date ranges</strong>{' '}
since these interactions set both <EuiCode>start</EuiCode> and <EuiCode>end</EuiCode> in a single event.
</p>
<p>
<EuiCode>onTimeChange</EuiCode> will <strong>not</strong> be invoked when
<EuiCode>start</EuiCode> and <EuiCode>end</EuiCode> change from interactions with{' '}
<strong>Absolute</strong>, <strong>Relative</strong>, and <strong>Now</strong> tabs.{' '}
<EuiCode>onTimeChange</EuiCode> will be invoked when the user clicks the <strong>Update</strong> button.
This gives users the ability to set both <EuiCode>start</EuiCode> and <EuiCode>end</EuiCode>{' '}
before triggering <EuiCode>onTimeChange</EuiCode>.
Set <EuiCode>showUpdateButton</EuiCode> to <EuiCode>false</EuiCode>{' '}
to immediately invoke <EuiCode>onTimeChange</EuiCode>{' '}
for all <EuiCode>start</EuiCode> and <EuiCode>end</EuiCode> changes.
</p>
</div>
),
demo: <SuperDatePicker />,
Expand Down
62 changes: 51 additions & 11 deletions src-docs/src/views/date_picker/super_date_picker.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@

import React, { Component } from 'react';
import React, { Component, Fragment } from 'react';

import {
EuiSuperDatePicker,
EuiSwitch,
EuiSpacer,
EuiFormRow,
EuiFieldText,
} from '../../../../src/components';

export default class extends Component {

state = {
recentlyUsedRanges: [],
isLoading: false,
showUpdateButton: true,
start: 'now-30m',
end: 'now',
}

onTimeChange = ({ start, end }) => {
Expand Down Expand Up @@ -47,18 +54,51 @@ export default class extends Component {
});
}

toggleShowApplyButton = () => {
this.setState(prevState => ({
showUpdateButton: !prevState.showUpdateButton,
}));
}

render() {
return (
<EuiSuperDatePicker
isLoading={this.state.isLoading}
start={this.state.start}
end={this.state.end}
onTimeChange={this.onTimeChange}
isPaused={this.state.isPaused}
refreshInterval={this.state.refreshInterval}
onRefreshChange={this.onRefreshChange}
recentlyUsedRanges={this.state.recentlyUsedRanges}
/>
<Fragment>
<EuiSwitch
label="Show apply button"
onChange={this.toggleShowApplyButton}
checked={this.state.showUpdateButton}
/>
<EuiSpacer />

<EuiSuperDatePicker
isLoading={this.state.isLoading}
start={this.state.start}
end={this.state.end}
onTimeChange={this.onTimeChange}
isPaused={this.state.isPaused}
refreshInterval={this.state.refreshInterval}
onRefreshChange={this.onRefreshChange}
recentlyUsedRanges={this.state.recentlyUsedRanges}
showUpdateButton={this.state.showUpdateButton}
/>

<EuiFormRow
label="start"
>
<EuiFieldText
readOnly
value={this.state.start}
/>
</EuiFormRow>
<EuiFormRow
label="end"
>
<EuiFieldText
readOnly
value={this.state.end}
/>
</EuiFormRow>
</Fragment>
);
}
}
6 changes: 2 additions & 4 deletions src/components/date_picker/super_date_picker/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import {
WrappedEuiSuperDatePicker,
export {
EuiSuperDatePicker,
} from './super_date_picker';

export { WrappedEuiSuperDatePicker as EuiSuperDatePicker };
176 changes: 99 additions & 77 deletions src/components/date_picker/super_date_picker/super_date_picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,79 +15,89 @@ import { EuiButton } from '../../button';
import { EuiFlexGroup, EuiFlexItem } from '../../flex';
import { EuiToolTip } from '../../tool_tip';

// EuiSuperDatePicker has state that needs to be reset when start or end change.
// Instead of using getDerivedStateFromProps, this wrapper adds a key to the component.
// When a key changes, React will create a new component instance rather than update the current one
// https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
export function WrappedEuiSuperDatePicker(props) {
return (
<EuiSuperDatePicker
key={`${props.start}-${props.end}`}
{...props}
/>
);
}
export class EuiSuperDatePicker extends Component {

WrappedEuiSuperDatePicker.propTypes = {
isLoading: PropTypes.bool,
/**
* String as either datemath (e.g.: now, now-15m, now-15m/m) or
* absolute date in the format 'YYYY-MM-DDTHH:mm:ss.sssZ'
*/
start: PropTypes.string,
/**
* String as either datemath (e.g.: now, now-15m, now-15m/m) or
* absolute date in the format 'YYYY-MM-DDTHH:mm:ss.sssZ'
*/
end: PropTypes.string,
/**
* Callback for when the time changes. Called with { start, end }
*/
onTimeChange: PropTypes.func.isRequired,
isPaused: PropTypes.bool,
/**
* Refresh interval in milliseconds
*/
refreshInterval: PropTypes.number,
/**
* Callback for when the refresh interval changes. Called with { isPaused, refreshInterval }
* Supply onRefreshChange to show refresh interval inputs in quick select popover
*/
onRefreshChange: PropTypes.func,

/**
* 'start' and 'end' must be string as either datemath (e.g.: now, now-15m, now-15m/m) or
* absolute date in the format 'YYYY-MM-DDTHH:mm:ss.sssZ'
*/
commonlyUsedRanges: PropTypes.arrayOf(commonlyUsedRangeShape),
dateFormat: PropTypes.string,
/**
* 'start' and 'end' must be string as either datemath (e.g.: now, now-15m, now-15m/m) or
* absolute date in the format 'YYYY-MM-DDTHH:mm:ss.sssZ'
*/
recentlyUsedRanges: PropTypes.arrayOf(recentlyUsedRangeShape),
};

WrappedEuiSuperDatePicker.defaultProps = {
start: 'now-15m',
end: 'now',
isPaused: true,
refreshInterval: 0,
commonlyUsedRanges: [
{ start: 'now/d', end: 'now/d', label: 'Today' },
{ start: 'now-1d/d', end: 'now-1d/d', label: 'Yesterday' },
{ start: 'now/w', end: 'now/w', label: 'This week' },
{ start: 'now/w', end: 'now', label: 'Week to date' },
{ start: 'now/M', end: 'now/M', label: 'This month' },
{ start: 'now/M', end: 'now', label: 'Month to date' },
{ start: 'now/y', end: 'now/y', label: 'This year' },
{ start: 'now/y', end: 'now', label: 'Year to date' },
],
dateFormat: 'MMM D, YYYY @ HH:mm:ss.SSS',
recentlyUsedRanges: [],
};
static propTypes = {
isLoading: PropTypes.bool,
/**
* String as either datemath (e.g.: now, now-15m, now-15m/m) or
* absolute date in the format 'YYYY-MM-DDTHH:mm:ss.sssZ'
*/
start: PropTypes.string,
/**
* String as either datemath (e.g.: now, now-15m, now-15m/m) or
* absolute date in the format 'YYYY-MM-DDTHH:mm:ss.sssZ'
*/
end: PropTypes.string,
/**
* Callback for when the time changes. Called with { start, end }
*/
onTimeChange: PropTypes.func.isRequired,
isPaused: PropTypes.bool,
/**
* Refresh interval in milliseconds
*/
refreshInterval: PropTypes.number,
/**
* Callback for when the refresh interval changes. Called with { isPaused, refreshInterval }
* Supply onRefreshChange to show refresh interval inputs in quick select popover
*/
onRefreshChange: PropTypes.func,

/**
* 'start' and 'end' must be string as either datemath (e.g.: now, now-15m, now-15m/m) or
* absolute date in the format 'YYYY-MM-DDTHH:mm:ss.sssZ'
*/
commonlyUsedRanges: PropTypes.arrayOf(commonlyUsedRangeShape),
dateFormat: PropTypes.string,
/**
* 'start' and 'end' must be string as either datemath (e.g.: now, now-15m, now-15m/m) or
* absolute date in the format 'YYYY-MM-DDTHH:mm:ss.sssZ'
*/
recentlyUsedRanges: PropTypes.arrayOf(recentlyUsedRangeShape),
/**
* Set showUpdateButton to false to immediately invoke onTimeChange for all start and end changes.
*/
showUpdateButton: PropTypes.bool,
}

export class EuiSuperDatePicker extends Component {
static defaultProps = {
start: 'now-15m',
end: 'now',
isPaused: true,
refreshInterval: 0,
commonlyUsedRanges: [
{ start: 'now/d', end: 'now/d', label: 'Today' },
{ start: 'now-1d/d', end: 'now-1d/d', label: 'Yesterday' },
{ start: 'now/w', end: 'now/w', label: 'This week' },
{ start: 'now/w', end: 'now', label: 'Week to date' },
{ start: 'now/M', end: 'now/M', label: 'This month' },
{ start: 'now/M', end: 'now', label: 'Month to date' },
{ start: 'now/y', end: 'now/y', label: 'This year' },
{ start: 'now/y', end: 'now', label: 'Year to date' },
],
dateFormat: 'MMM D, YYYY @ HH:mm:ss.SSS',
recentlyUsedRanges: [],
showUpdateButton: true,
}

static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.start !== prevState.prevProps.start
|| nextProps.end !== prevState.prevProps.end) {
return {
prevProps: {
start: nextProps.start,
end: nextProps.end,
},
start: nextProps.start,
end: nextProps.end,
isInvalid: false,
hasChanged: false,
};
}

return null;
}

constructor(props) {
super(props);
Expand All @@ -99,6 +109,10 @@ export class EuiSuperDatePicker extends Component {
} = this.props;

this.state = {
prevProps: {
start: props.start,
end: props.end,
},
start,
end,
isInvalid: false,
Expand All @@ -118,13 +132,13 @@ export class EuiSuperDatePicker extends Component {
setTootipRef = node => (this.tooltip = node);

showTooltip = () => {
if (!this._isMounted) {
if (!this._isMounted || !this.tooltip) {
return;
}
this.tooltip.showToolTip();
}
hideTooltip = () => {
if (!this._isMounted) {
if (!this._isMounted || !this.tooltip) {
return;
}
this.tooltip.hideToolTip();
Expand All @@ -149,6 +163,11 @@ export class EuiSuperDatePicker extends Component {
});

if (!isInvalid) {
if (!this.props.showUpdateButton) {
this.props.onTimeChange({ start, end });
return;
}

this.showTooltip();
this.tooltipTimeout = setTimeout(() => {
this.hideTooltip();
Expand All @@ -169,6 +188,9 @@ export class EuiSuperDatePicker extends Component {
}

applyQuickTime = ({ start, end }) => {
this.setState({
showPrettyDuration: showPrettyDuration(start, end, this.props.commonlyUsedRanges),
});
this.props.onTimeChange({ start, end });
}

Expand Down Expand Up @@ -236,6 +258,10 @@ export class EuiSuperDatePicker extends Component {
}

renderUpdateButton = () => {
if (!this.props.showUpdateButton) {
return;
}

let buttonText = 'Refresh';
if (this.state.hasChanged || this.props.isLoading) {
buttonText = this.props.isLoading ? 'Updating' : 'Update';
Expand Down Expand Up @@ -305,7 +331,3 @@ export class EuiSuperDatePicker extends Component {
);
}
}

EuiSuperDatePicker.propTypes = WrappedEuiSuperDatePicker.propTypes;
EuiSuperDatePicker.defaultProps = WrappedEuiSuperDatePicker.defaultProps;

0 comments on commit 29e07d0

Please sign in to comment.