Skip to content

Commit

Permalink
Merge pull request #101 from newrelic/time-range-picker-max-diff
Browse files Browse the repository at this point in the history
feat: hide default and max time range for timeRangePicker
  • Loading branch information
amit-y authored Jul 2, 2024
2 parents 1805705 + e8f1641 commit cab1c36
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 23 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- TimeRangePicker - prop to hide `Default`
- TimeRangePicker - `maxRangeMins` prop to set set a max range in selected date/times

## [1.22.0] - 2024-03-12

### Changed

- FilterBar - minor refactor
Expand Down
2 changes: 2 additions & 0 deletions src/components/time-range-picker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ function MyComponent() {
### Props

- date (date): The default time period
- maxRangeMins (int): The max time range allowed, in minutes
- hideDefault (boolean): If true, hides the `Default` selection
- onChange (function): A function that is called when the user selects a time range. The function is passed an abject in the following format:

```javascript
Expand Down
111 changes: 88 additions & 23 deletions src/components/time-range-picker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import React, { useCallback, useEffect, useState } from 'react';
import PropTypes from 'prop-types';

import {
Button,
Icon,
List,
ListItem,
Popover,
PopoverTrigger,
PopoverBody,
List,
ListItem,
Button,
Tooltip,
} from 'nr1';

import DateTimePicker from '../date-time-picker';
Expand All @@ -20,6 +21,7 @@ const TEXTS = {
CANCEL: 'Cancel',
CUSTOM: 'Custom',
DEFAULT: 'Default',
SELECT: 'Select',
};

const TIME_RANGES = [
Expand All @@ -37,31 +39,73 @@ const TIME_RANGES = [
{ break: true },
];

const normalizedDateTime = (dt = new Date()) =>
new Date(
dt.getFullYear(),
dt.getMonth(),
dt.getDate(),
dt.getHours(),
dt.getMinutes()
);
const normalizedDateTime = (dt = new Date()) => new Date(dt.setSeconds(0, 0));

const formattedText = (num, txt) => {
if (!num || !txt) return '';
return `${num} ${txt}${num > 1 ? 's' : ''}`;
};

const displayMins = (minutes) => {
if (!minutes) return '';
const minsInHour = 60,
minsInDay = 60 * 24;
if (minutes < minsInHour) return formattedText(minutes, 'minute');
const mins = minutes % minsInHour;
if (minutes < minsInDay) {
const hours = Math.floor(minutes / minsInHour);
return `${formattedText(hours, 'hour')} ${formattedText(mins, 'minute')}`;
}
const days = Math.floor(minutes / minsInDay);
const hours = Math.floor((minutes - days * minsInDay) / minsInHour);
return `${formattedText(days, 'day')} ${formattedText(
hours,
'hour'
)} ${formattedText(mins, 'minute')}`;
};

const TimeRangePicker = ({ timeRange, onChange }) => {
const TimeRangePicker = ({
timeRange,
maxRangeMins,
hideDefault,
onChange,
}) => {
const [opened, setOpened] = useState(false);
const [isCustomOpen, setIsCustomOpen] = useState(false);
const [customSaveDisabled, setCustomSaveDisabled] = useState(true);
const [selected, setSelected] = useState('');
const [beginTime, setBeginTime] = useState();
const [endTime, setEndTime] = useState();
const [filteredTimeRanges, setFilteredTimeRanges] = useState(TIME_RANGES);

useEffect(
() =>
setFilteredTimeRanges(() => {
if (!hideDefault && !maxRangeMins) return TIME_RANGES;
return TIME_RANGES.reduce((acc, tr, idx) => {
if (hideDefault && idx < 2) return acc;
if (
(tr.break && !acc[acc.length - 1]?.break) ||
!maxRangeMins ||
tr.offset / 60000 <= maxRangeMins
)
return [...acc, tr];
return acc;
}, []);
}),
[maxRangeMins, hideDefault]
);

useEffect(() => {
const fallbackTimeRangeLabel = hideDefault ? TEXTS.SELECT : TEXTS.DEFAULT;
if (!timeRange) {
setSelected(TEXTS.DEFAULT);
setSelected(fallbackTimeRangeLabel);
setBeginTime(null);
setEndTime(null);
} else if (timeRange.duration) {
setSelected(
TIME_RANGES.find((tr) => tr.offset === timeRange.duration)?.label ||
TEXTS.DEFAULT
filteredTimeRanges.find((tr) => tr.offset === timeRange.duration)
?.label || fallbackTimeRangeLabel
);
setBeginTime(null);
setEndTime(null);
Expand All @@ -80,7 +124,17 @@ const TimeRangePicker = ({ timeRange, onChange }) => {
setEndTime(e);
}
}
}, [timeRange]);
}, [timeRange, filteredTimeRanges, hideDefault]);

useEffect(() => {
if (!beginTime || !endTime) {
setCustomSaveDisabled(true);
} else if (maxRangeMins && (endTime - beginTime) / 60000 > maxRangeMins) {
setCustomSaveDisabled(true);
} else {
setCustomSaveDisabled(false);
}
}, [beginTime, endTime, maxRangeMins]);

const setDurationHandler = (duration) => {
if (onChange)
Expand Down Expand Up @@ -161,7 +215,7 @@ const TimeRangePicker = ({ timeRange, onChange }) => {
<PopoverBody>
<div className={styles['time-range-list']}>
<List className={styles['time-range-list-items']}>
{TIME_RANGES.map((tr, i) => (
{filteredTimeRanges.map((tr, i) => (
<ListItem key={i}>
{tr.break ? (
<hr className={styles['time-range-list-break']} />
Expand Down Expand Up @@ -211,13 +265,22 @@ const TimeRangePicker = ({ timeRange, onChange }) => {
validTill={normalizedDateTime()}
/>
<div className={styles['custom-buttons']}>
<Button
type={Button.TYPE.PRIMARY}
sizeType={Button.SIZE_TYPE.SMALL}
onClick={setCustomHandler}
<Tooltip
text={
customSaveDisabled
? `Only up to ${displayMins(maxRangeMins)} allowed`
: ''
}
>
{TEXTS.APPLY}
</Button>
<Button
type={Button.TYPE.PRIMARY}
sizeType={Button.SIZE_TYPE.SMALL}
disabled={customSaveDisabled}
onClick={setCustomHandler}
>
{TEXTS.APPLY}
</Button>
</Tooltip>
<Button
type={Button.TYPE.PLAIN}
sizeType={Button.SIZE_TYPE.SMALL}
Expand All @@ -242,6 +305,8 @@ TimeRangePicker.propTypes = {
duration: PropTypes.number,
end_time: PropTypes.number,
}),
maxRangeMins: PropTypes.number,
hideDefault: PropTypes.bool,
onChange: PropTypes.func,
};

Expand Down

0 comments on commit cab1c36

Please sign in to comment.