Skip to content

Commit

Permalink
feat: New time range label (#22317)
Browse files Browse the repository at this point in the history
  • Loading branch information
kgabryje authored Dec 5, 2022
1 parent aafb993 commit 2d30e9c
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,17 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { useState, useEffect, useMemo } from 'react';
import { css, styled, t, useTheme, NO_TIME_RANGE } from '@superset-ui/core';
import React, { ReactNode, useState, useEffect, useMemo } from 'react';
import {
css,
styled,
t,
useTheme,
NO_TIME_RANGE,
SupersetTheme,
} from '@superset-ui/core';
import Button from 'src/components/Button';
import ControlHeader from 'src/explore/components/ControlHeader';
import Label from 'src/components/Label';
import Modal from 'src/components/Modal';
import { Divider } from 'src/components';
import Icons from 'src/components/Icons';
Expand All @@ -29,6 +35,7 @@ import { Tooltip } from 'src/components/Tooltip';
import { useDebouncedEffect } from 'src/explore/exploreUtils';
import { SLOW_DEBOUNCE } from 'src/constants';
import { noOp } from 'src/utils/common';
import { useCSSTextTruncation } from 'src/hooks/useTruncation';
import ControlPopover from '../ControlPopover/ControlPopover';

import { DateFilterControlProps, FrameType } from './types';
Expand All @@ -44,6 +51,7 @@ import {
CalendarFrame,
CustomFrame,
AdvancedFrame,
DateLabel,
} from './components';

const StyledRangeType = styled(Select)`
Expand Down Expand Up @@ -120,6 +128,28 @@ const IconWrapper = styled.span`
}
`;

const getTooltipTitle = (
isLabelTruncated: boolean,
label: string | undefined,
range: string | undefined,
) =>
isLabelTruncated ? (
<div>
{label && <strong>{label}</strong>}
{range && (
<div
css={(theme: SupersetTheme) => css`
margin-top: ${theme.gridUnit}px;
`}
>
{range}
</div>
)}
</div>
) : (
range || null
);

export default function DateFilterLabel(props: DateFilterControlProps) {
const {
onChange,
Expand All @@ -139,21 +169,22 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
const [timeRangeValue, setTimeRangeValue] = useState(value);
const [validTimeRange, setValidTimeRange] = useState<boolean>(false);
const [evalResponse, setEvalResponse] = useState<string>(value);
const [tooltipTitle, setTooltipTitle] = useState<string>(value);
const [tooltipTitle, setTooltipTitle] = useState<ReactNode | null>(value);
const theme = useTheme();
const [labelRef, labelIsTruncated] = useCSSTextTruncation<HTMLSpanElement>();

useEffect(() => {
if (value === NO_TIME_RANGE) {
setActualTimeRange(NO_TIME_RANGE);
setTooltipTitle(NO_TIME_RANGE);
setTooltipTitle(null);
setValidTimeRange(true);
return;
}
fetchTimeRange(value).then(({ value: actualRange, error }) => {
if (error) {
setEvalResponse(error || '');
setValidTimeRange(false);
setTooltipTitle(value || '');
setTooltipTitle(value || null);
} else {
/*
HRT == human readable text
Expand All @@ -172,16 +203,21 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
guessedFrame === 'No filter'
) {
setActualTimeRange(value);
setTooltipTitle(
getTooltipTitle(labelIsTruncated, value, actualRange),
);
} else {
setActualTimeRange(actualRange || '');
setTooltipTitle(value || '');
setTooltipTitle(
getTooltipTitle(labelIsTruncated, actualRange, value),
);
}
setValidTimeRange(true);
}
setLastFetchedTimeRange(value);
setEvalResponse(actualRange || value);
});
}, [value]);
}, [guessedFrame, labelIsTruncated, labelRef, value]);

useDebouncedEffect(
() => {
Expand Down Expand Up @@ -322,26 +358,28 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
overlayStyle={{ width: '600px' }}
>
<Tooltip placement="top" title={tooltipTitle}>
<Label
className="pointer"
<DateLabel
label={actualTimeRange}
isActive={show}
isPlaceholder={actualTimeRange === NO_TIME_RANGE}
data-test={DATE_FILTER_TEST_KEY.popoverOverlay}
>
{actualTimeRange}
</Label>
ref={labelRef}
/>
</Tooltip>
</ControlPopover>
);

const modalContent = (
<>
<Tooltip placement="top" title={tooltipTitle}>
<Label
className="pointer"
<DateLabel
onClick={toggleOverlay}
label={actualTimeRange}
isActive={show}
isPlaceholder={actualTimeRange === NO_TIME_RANGE}
data-test={DATE_FILTER_TEST_KEY.modalOverlay}
>
{actualTimeRange}
</Label>
ref={labelRef}
/>
</Tooltip>
{/* the zIndex value is from trying so that the Modal doesn't overlay the AdhocFilter when GENERIC_CHART_AXES is enabled */}
<Modal
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React, { forwardRef, ReactNode, RefObject } from 'react';
import { css, styled, useTheme } from '@superset-ui/core';
import Icons from 'src/components/Icons';

export type DateLabelProps = {
label: ReactNode;
isActive?: boolean;
isPlaceholder?: boolean;
onClick?: (event: React.MouseEvent) => void;
};

// This is the color that antd components (such as Select or Input) use on hover
// TODO: use theme.colors.primary.base here and in antd components
const ACTIVE_BORDER_COLOR = '#45BED6';

const LabelContainer = styled.div<{
isActive?: boolean;
isPlaceholder?: boolean;
}>`
${({ theme, isActive, isPlaceholder }) => css`
width: 100%;
height: ${theme.gridUnit * 8}px;
display: flex;
align-items: center;
flex-wrap: nowrap;
padding: 0 ${theme.gridUnit * 3}px;
background-color: ${theme.colors.grayscale.light5};
border: 1px solid
${isActive ? ACTIVE_BORDER_COLOR : theme.colors.grayscale.light2};
border-radius: ${theme.borderRadius}px;
cursor: pointer;
transition: border-color 0.3s cubic-bezier(0.65, 0.05, 0.36, 1);
:hover,
:focus {
border-color: ${ACTIVE_BORDER_COLOR};
}
.date-label-content {
color: ${isPlaceholder
? theme.colors.grayscale.light1
: theme.colors.grayscale.dark1};
overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
flex-shrink: 1;
white-space: nowrap;
}
span[role='img'] {
margin-left: auto;
padding-left: ${theme.gridUnit}px;
& > span[role='img'] {
line-height: 0;
}
}
`}
`;

export const DateLabel = forwardRef(
(props: DateLabelProps, ref: RefObject<HTMLSpanElement>) => {
const theme = useTheme();
return (
<LabelContainer {...props} tabIndex={0}>
<span className="date-label-content" ref={ref}>
{props.label}
</span>
<Icons.CalendarOutlined
iconSize="s"
iconColor={theme.colors.grayscale.base}
/>
</LabelContainer>
);
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export { CommonFrame } from './CommonFrame';
export { CalendarFrame } from './CalendarFrame';
export { CustomFrame } from './CustomFrame';
export { AdvancedFrame } from './AdvancedFrame';
export { DateLabel } from './DateLabel';
28 changes: 5 additions & 23 deletions superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,11 @@ const ControlContainer = styled.div<{
display: flex;
height: 100%;
max-width: 100%;
padding: 2px;
& > span,
& > span:hover {
border: 2px solid transparent;
display: inline-block;
border: ${({ theme, validateStatus }) =>
validateStatus && `2px solid ${theme.colors[validateStatus]?.base}`};
}
&:focus {
& > span {
border: 2px solid
${({ theme, validateStatus }) =>
validateStatus
? theme.colors[validateStatus]?.base
: theme.colors.primary.base};
outline: 0;
box-shadow: 0 0 0 2px
${({ validateStatus }) =>
validateStatus
? 'rgba(224, 67, 85, 12%)'
: 'rgba(32, 167, 201, 0.2)'};
}
width: 100%;
& > div,
& > div:hover {
${({ validateStatus, theme }) =>
validateStatus && `border-color: ${theme.colors[validateStatus]?.base}`}
}
`;

Expand Down Expand Up @@ -101,7 +84,6 @@ export default function TimeFilterPlugin(props: PluginFilterTimeProps) {
return props.formData?.inView ? (
<TimeFilterStyles width={width} height={height}>
<ControlContainer
tabIndex={-1}
ref={inputRef}
validateStatus={filterState.validateStatus}
onFocus={setFocusedFilter}
Expand Down

0 comments on commit 2d30e9c

Please sign in to comment.