TimeZone: unify the time zone pickers to one that can rule them all. (#24803)

* draft on a unified TimeZonePicker.

* most of the data structures is in place.

* wip.

* wip.

* wip: timezone selector in progress.2

* fixed so we have proper data on all timezones.

* started to add timezone into time picker.

* addeing time zone footer.

* footer is working.

* fixed so we use the timeZone picker in shared preferences.

* Added so we can change timeZone from picker.

* did some styling changes.

* will update timezone on all places that we need to update it.

* removed console.log

* removed magic string.

* fixed border on calendar.

* ignoring eslint cache.

* cleaned up the code a bit.

* made the default selectable.

* corrected so the behaviour about default works as expected.

* excluded timezone from change tracker.

* revert so default will always be the intial value.

* default will always fallback to the one in the config.

* do the country mapping on startup.

* fixed nit.

* updated snapshots for timepicker.

* fixed build errors.

* updating so snapshot tests is in sync.

* removed Date.now from prop since it will change each run in the snapshot tests.

* fixed so e2e tests works as before.

* moved files into separate folders.
This commit is contained in:
Marcus Andersson
2020-06-26 09:08:15 +02:00
committed by GitHub
parent 084542a006
commit 1abbb477cf
38 changed files with 1559 additions and 898 deletions

View File

@ -0,0 +1,159 @@
import React, { PropsWithChildren } from 'react';
import { css, cx } from 'emotion';
import { GrafanaTheme, SelectableValue, getTimeZoneInfo } from '@grafana/data';
import { useTheme } from '../../../themes/ThemeContext';
import { stylesFactory } from '../../../themes/stylesFactory';
import { Icon } from '../../Icon/Icon';
import { TimeZoneOffset } from './TimeZoneOffset';
import { TimeZoneDescription } from './TimeZoneDescription';
import { TimeZoneTitle } from './TimeZoneTitle';
import isString from 'lodash/isString';
interface Props {
isFocused: boolean;
isSelected: boolean;
innerProps: any;
data: SelectableZone;
}
const offsetClassName = 'tz-utc-offset';
export interface SelectableZone extends SelectableValue<string> {
searchIndex: string;
}
export const WideTimeZoneOption: React.FC<PropsWithChildren<Props>> = (props, ref) => {
const { children, innerProps, data, isSelected, isFocused } = props;
const theme = useTheme();
const styles = getStyles(theme);
const timestamp = Date.now();
const containerStyles = cx(styles.container, isFocused && styles.containerFocused);
if (!isString(data.value)) {
return null;
}
return (
<div className={containerStyles} {...innerProps} aria-label="Select option">
<div className={cx(styles.leftColumn, styles.row)}>
<div className={cx(styles.leftColumn, styles.wideRow)}>
<TimeZoneTitle title={children} />
<div className={styles.spacer} />
<TimeZoneDescription info={getTimeZoneInfo(data.value, timestamp)} />
</div>
<div className={styles.rightColumn}>
<TimeZoneOffset timeZone={data.value} timestamp={timestamp} className={offsetClassName} />
{isSelected && (
<span>
<Icon name="check" />
</span>
)}
</div>
</div>
</div>
);
};
export const CompactTimeZoneOption: React.FC<PropsWithChildren<Props>> = (props, ref) => {
const { children, innerProps, data, isSelected, isFocused } = props;
const theme = useTheme();
const styles = getStyles(theme);
const timestamp = Date.now();
const containerStyles = cx(styles.container, isFocused && styles.containerFocused);
if (!isString(data.value)) {
return null;
}
return (
<div className={containerStyles} {...innerProps} aria-label="Select option">
<div className={styles.body}>
<div className={styles.row}>
<div className={styles.leftColumn}>
<TimeZoneTitle title={children} />
</div>
<div className={styles.rightColumn}>
{isSelected && (
<span>
<Icon name="check" />
</span>
)}
</div>
</div>
<div className={styles.row}>
<div className={styles.leftColumn}>
<TimeZoneDescription info={getTimeZoneInfo(data.value, timestamp)} />
</div>
<div className={styles.rightColumn}>
<TimeZoneOffset timestamp={timestamp} timeZone={data.value} className={offsetClassName} />
</div>
</div>
</div>
</div>
);
};
const getStyles = stylesFactory((theme: GrafanaTheme) => {
const offsetHoverBg = theme.isDark ? theme.palette.gray05 : theme.palette.white;
return {
container: css`
display: flex;
align-items: center;
flex-direction: row;
flex-shrink: 0;
white-space: nowrap;
cursor: pointer;
border-left: 2px solid transparent;
padding: 6px 8px 4px;
&:hover {
background: ${theme.colors.dropdownOptionHoverBg};
span.${offsetClassName} {
background: ${offsetHoverBg};
}
}
`,
containerFocused: css`
background: ${theme.colors.dropdownOptionHoverBg};
border-image: linear-gradient(#f05a28 30%, #fbca0a 99%);
border-image-slice: 1;
border-style: solid;
border-top: 0;
border-right: 0;
border-bottom: 0;
border-left-width: 2px;
span.${offsetClassName} {
background: ${offsetHoverBg};
}
`,
body: css`
display: flex;
font-weight: ${theme.typography.weight.semibold};
flex-direction: column;
flex-grow: 1;
`,
row: css`
display: flex;
flex-direction: row;
`,
leftColumn: css`
flex-grow: 1;
text-overflow: ellipsis;
`,
rightColumn: css`
justify-content: flex-end;
align-items: center;
`,
wideRow: css`
display: flex;
flex-direction: row;
align-items: baseline;
`,
spacer: css`
margin-left: 6px;
`,
};
});