|
@@ -1,17 +1,21 @@
|
|
|
import * as React from 'react';
|
|
|
import {withRouter, WithRouterProps} from 'react-router';
|
|
|
+import {ClassNames} from '@emotion/react';
|
|
|
import styled from '@emotion/styled';
|
|
|
|
|
|
-import DropdownMenu, {GetActorPropsFn} from 'sentry/components/dropdownMenu';
|
|
|
+import DropdownAutoComplete from 'sentry/components/dropdownAutoComplete';
|
|
|
+import {Item} from 'sentry/components/dropdownAutoComplete/types';
|
|
|
+import {GetActorPropsFn} from 'sentry/components/dropdownMenu';
|
|
|
import HookOrDefault from 'sentry/components/hookOrDefault';
|
|
|
import HeaderItem from 'sentry/components/organizations/headerItem';
|
|
|
import MultipleSelectorSubmitRow from 'sentry/components/organizations/multipleSelectorSubmitRow';
|
|
|
+import PageFilterPinButton from 'sentry/components/organizations/pageFilters/pageFilterPinButton';
|
|
|
import DateRange from 'sentry/components/organizations/timeRangeSelector/dateRange';
|
|
|
-import SelectorItems from 'sentry/components/organizations/timeRangeSelector/dateRange/selectorItems';
|
|
|
import DateSummary from 'sentry/components/organizations/timeRangeSelector/dateSummary';
|
|
|
import {getRelativeSummary} from 'sentry/components/organizations/timeRangeSelector/utils';
|
|
|
import {DEFAULT_STATS_PERIOD} from 'sentry/constants';
|
|
|
import {IconCalendar} from 'sentry/icons';
|
|
|
+import {t} from 'sentry/locale';
|
|
|
import space from 'sentry/styles/space';
|
|
|
import {DateString, Organization} from 'sentry/types';
|
|
|
import {defined} from 'sentry/utils';
|
|
@@ -28,6 +32,8 @@ import {
|
|
|
import getDynamicText from 'sentry/utils/getDynamicText';
|
|
|
import getRouteStringFromRoutes from 'sentry/utils/getRouteStringFromRoutes';
|
|
|
|
|
|
+import SelectorItems from './selectorItems';
|
|
|
+
|
|
|
const DateRangeHook = HookOrDefault({
|
|
|
hookName: 'component:header-date-range',
|
|
|
defaultComponent: DateRange,
|
|
@@ -239,6 +245,14 @@ class TimeRangeSelector extends React.PureComponent<Props, State> {
|
|
|
);
|
|
|
};
|
|
|
|
|
|
+ handleSelect = (item: Item) => {
|
|
|
+ if (item.value === 'absolute') {
|
|
|
+ this.handleAbsoluteClick();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.handleSelectRelative(item.value);
|
|
|
+ };
|
|
|
+
|
|
|
handleAbsoluteClick = () => {
|
|
|
const {relative, onChange, defaultPeriod, defaultAbsolute} = this.props;
|
|
|
|
|
@@ -378,6 +392,8 @@ class TimeRangeSelector extends React.PureComponent<Props, State> {
|
|
|
} = this.props;
|
|
|
const {start, end, relative} = this.state;
|
|
|
|
|
|
+ const hasNewPageFilters = organization.features.includes('selection-filters-v2');
|
|
|
+
|
|
|
const shouldShowAbsolute = showAbsolute;
|
|
|
const shouldShowRelative = showRelative;
|
|
|
const isAbsoluteSelected = !!start && !!end;
|
|
@@ -392,64 +408,42 @@ class TimeRangeSelector extends React.PureComponent<Props, State> {
|
|
|
)
|
|
|
);
|
|
|
|
|
|
- const relativeSelected = isAbsoluteSelected
|
|
|
- ? ''
|
|
|
- : relative || defaultPeriod || DEFAULT_STATS_PERIOD;
|
|
|
-
|
|
|
return (
|
|
|
- <DropdownMenu
|
|
|
- isOpen={this.state.isOpen}
|
|
|
- onOpen={this.handleOpen}
|
|
|
- onClose={this.handleCloseMenu}
|
|
|
- keepMenuOpen
|
|
|
+ <SelectorItemsHook
|
|
|
+ shouldShowAbsolute={shouldShowAbsolute}
|
|
|
+ shouldShowRelative={shouldShowRelative}
|
|
|
+ relativePeriods={relativeOptions}
|
|
|
+ handleSelectRelative={this.handleSelectRelative}
|
|
|
>
|
|
|
- {({isOpen, getRootProps, getActorProps, getMenuProps}) => {
|
|
|
- const dropdownButton = customDropdownButton ? (
|
|
|
- customDropdownButton({getActorProps, isOpen})
|
|
|
- ) : (
|
|
|
- <StyledHeaderItem
|
|
|
- data-test-id="global-header-timerange-selector"
|
|
|
- icon={label ?? <IconCalendar />}
|
|
|
- isOpen={isOpen}
|
|
|
- hasSelected={
|
|
|
- (!!this.props.relative && this.props.relative !== defaultPeriod) ||
|
|
|
- isAbsoluteSelected
|
|
|
- }
|
|
|
- hasChanges={this.state.hasChanges}
|
|
|
- onClear={this.handleClear}
|
|
|
- allowClear
|
|
|
- hint={hint}
|
|
|
- {...getActorProps()}
|
|
|
- >
|
|
|
- {getDynamicText({
|
|
|
- value: summary,
|
|
|
- fixed: 'start to end',
|
|
|
- })}
|
|
|
- </StyledHeaderItem>
|
|
|
- );
|
|
|
-
|
|
|
- return (
|
|
|
- <TimeRangeRoot {...getRootProps()}>
|
|
|
- {dropdownButton}
|
|
|
- {isOpen && (
|
|
|
- <Menu
|
|
|
- {...getMenuProps()}
|
|
|
- isAbsoluteSelected={isAbsoluteSelected}
|
|
|
- detached={detached}
|
|
|
- alignDropdown={alignDropdown}
|
|
|
- >
|
|
|
- <SelectorList isAbsoluteSelected={isAbsoluteSelected}>
|
|
|
- <SelectorItemsHook
|
|
|
- handleSelectRelative={this.handleSelectRelative}
|
|
|
- handleAbsoluteClick={this.handleAbsoluteClick}
|
|
|
- isAbsoluteSelected={isAbsoluteSelected}
|
|
|
- relativeSelected={relativeSelected}
|
|
|
- relativePeriods={relativeOptions}
|
|
|
- shouldShowAbsolute={shouldShowAbsolute}
|
|
|
- shouldShowRelative={shouldShowRelative}
|
|
|
- />
|
|
|
- </SelectorList>
|
|
|
- {isAbsoluteSelected && (
|
|
|
+ {items => (
|
|
|
+ <ClassNames>
|
|
|
+ {({css}) => (
|
|
|
+ <StyledDropdownAutoComplete
|
|
|
+ allowActorToggle
|
|
|
+ alignMenu={alignDropdown ?? (isAbsoluteSelected ? 'right' : 'left')}
|
|
|
+ isOpen={this.state.isOpen}
|
|
|
+ onOpen={this.handleOpen}
|
|
|
+ onClose={this.handleCloseMenu}
|
|
|
+ hideInput={!shouldShowRelative}
|
|
|
+ closeOnSelect={false}
|
|
|
+ blendCorner={false}
|
|
|
+ maxHeight={400}
|
|
|
+ detached={detached}
|
|
|
+ items={items}
|
|
|
+ searchPlaceholder={t('Filter time range')}
|
|
|
+ rootClassName={css`
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ height: 100%;
|
|
|
+ `}
|
|
|
+ inputActions={
|
|
|
+ hasNewPageFilters ? (
|
|
|
+ <StyledPinButton size="xsmall" filter="datetime" />
|
|
|
+ ) : undefined
|
|
|
+ }
|
|
|
+ onSelect={this.handleSelect}
|
|
|
+ subPanel={
|
|
|
+ isAbsoluteSelected && (
|
|
|
<div>
|
|
|
<DateRangeHook
|
|
|
start={start ?? null}
|
|
@@ -470,13 +464,40 @@ class TimeRangeSelector extends React.PureComponent<Props, State> {
|
|
|
/>
|
|
|
</SubmitRow>
|
|
|
</div>
|
|
|
- )}
|
|
|
- </Menu>
|
|
|
- )}
|
|
|
- </TimeRangeRoot>
|
|
|
- );
|
|
|
- }}
|
|
|
- </DropdownMenu>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ >
|
|
|
+ {({isOpen, getActorProps}) =>
|
|
|
+ customDropdownButton ? (
|
|
|
+ customDropdownButton({getActorProps, isOpen})
|
|
|
+ ) : (
|
|
|
+ <StyledHeaderItem
|
|
|
+ data-test-id="global-header-timerange-selector"
|
|
|
+ icon={label ?? <IconCalendar />}
|
|
|
+ isOpen={isOpen}
|
|
|
+ hasSelected={
|
|
|
+ (!!this.props.relative &&
|
|
|
+ this.props.relative !== defaultPeriod) ||
|
|
|
+ isAbsoluteSelected
|
|
|
+ }
|
|
|
+ hasChanges={this.state.hasChanges}
|
|
|
+ onClear={this.handleClear}
|
|
|
+ allowClear
|
|
|
+ hint={hint}
|
|
|
+ {...getActorProps()}
|
|
|
+ >
|
|
|
+ {getDynamicText({
|
|
|
+ value: summary,
|
|
|
+ fixed: 'start to end',
|
|
|
+ })}
|
|
|
+ </StyledHeaderItem>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ </StyledDropdownAutoComplete>
|
|
|
+ )}
|
|
|
+ </ClassNames>
|
|
|
+ )}
|
|
|
+ </SelectorItemsHook>
|
|
|
);
|
|
|
}
|
|
|
}
|
|
@@ -485,66 +506,34 @@ const TimeRangeRoot = styled('div')`
|
|
|
position: relative;
|
|
|
`;
|
|
|
|
|
|
-const StyledHeaderItem = styled(HeaderItem)`
|
|
|
- height: 100%;
|
|
|
-`;
|
|
|
-
|
|
|
-type MenuProps = {
|
|
|
- isAbsoluteSelected: boolean;
|
|
|
- alignDropdown?: Props['alignDropdown'];
|
|
|
- detached?: Props['detached'];
|
|
|
-};
|
|
|
-
|
|
|
-const Menu = styled('div')<MenuProps>`
|
|
|
- ${p =>
|
|
|
- p.alignDropdown
|
|
|
- ? `
|
|
|
- ${p.alignDropdown === 'left' && 'left: -1px'};
|
|
|
- ${p.alignDropdown === 'right' && 'right: -1px'};
|
|
|
- `
|
|
|
- : `
|
|
|
- ${!p.isAbsoluteSelected && 'left: -1px'};
|
|
|
- ${p.isAbsoluteSelected && 'right: -1px'};
|
|
|
- `}
|
|
|
-
|
|
|
- display: flex;
|
|
|
- background: ${p => p.theme.background};
|
|
|
- border: 1px solid ${p => p.theme.border};
|
|
|
+const StyledDropdownAutoComplete = styled(DropdownAutoComplete)`
|
|
|
+ font-size: ${p => p.theme.fontSizeMedium};
|
|
|
position: absolute;
|
|
|
top: 100%;
|
|
|
- min-width: 100%;
|
|
|
- z-index: ${p => p.theme.zIndex.dropdown};
|
|
|
- font-size: 0.8em;
|
|
|
- overflow: hidden;
|
|
|
|
|
|
${p =>
|
|
|
- p.detached
|
|
|
- ? `
|
|
|
- border-radius: ${p.theme.borderRadius};
|
|
|
- margin-top: ${space(1)};
|
|
|
- box-shadow: ${p.theme.dropShadowHeavy};
|
|
|
- `
|
|
|
- : `
|
|
|
- border-radius: ${p.theme.borderRadiusBottom};
|
|
|
- box-shadow: ${p.theme.dropShadowLight};
|
|
|
- `}
|
|
|
+ !p.detached &&
|
|
|
+ `
|
|
|
+ margin-top: 0;
|
|
|
+ border-radius: ${p.theme.borderRadiusBottom};
|
|
|
+ `};
|
|
|
`;
|
|
|
|
|
|
-const SelectorList = styled('div')<MenuProps>`
|
|
|
- display: flex;
|
|
|
- flex: 1;
|
|
|
- flex-direction: column;
|
|
|
- flex-shrink: 0;
|
|
|
- min-width: ${p => (p.isAbsoluteSelected ? '160px' : '220px')};
|
|
|
- min-height: 305px;
|
|
|
+const StyledHeaderItem = styled(HeaderItem)`
|
|
|
+ height: 100%;
|
|
|
`;
|
|
|
|
|
|
const SubmitRow = styled('div')`
|
|
|
+ height: 100%;
|
|
|
padding: ${space(0.5)} ${space(1)};
|
|
|
border-top: 1px solid ${p => p.theme.innerBorder};
|
|
|
border-left: 1px solid ${p => p.theme.border};
|
|
|
`;
|
|
|
|
|
|
+const StyledPinButton = styled(PageFilterPinButton)`
|
|
|
+ margin: 0 ${space(1)};
|
|
|
+`;
|
|
|
+
|
|
|
export default withRouter(TimeRangeSelector);
|
|
|
|
|
|
export {TimeRangeRoot};
|