Chore: Clean up usage of deprecated stylesFactory function (#78419)

This commit is contained in:
kay delaney
2023-11-21 14:01:53 +00:00
committed by GitHub
parent 165de515cd
commit 9e11779921
54 changed files with 964 additions and 1064 deletions

View File

@@ -1,20 +1,20 @@
import { css, cx } from '@emotion/css'; import { css, cx } from '@emotion/css';
import React from 'react'; import React from 'react';
import { stylesFactory } from '../../themes'; import { useStyles2 } from '../../themes';
export interface Props { export interface Props {
className?: string; className?: string;
} }
export const FullWidthButtonContainer = ({ className, children }: React.PropsWithChildren<Props>) => { export const FullWidthButtonContainer = ({ className, children }: React.PropsWithChildren<Props>) => {
const styles = getStyles(); const styles = useStyles2(getStyles);
return <div className={cx(styles, className)}>{children}</div>; return <div className={cx(styles, className)}>{children}</div>;
}; };
const getStyles = stylesFactory(() => { const getStyles = () =>
return css({ css({
display: 'flex', display: 'flex',
button: { button: {
@@ -31,4 +31,3 @@ const getStyles = stylesFactory(() => {
textAlign: 'center', textAlign: 'center',
}, },
}); });
});

View File

@@ -4,7 +4,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../themes'; import { useStyles2 } from '../../themes';
import { IconName } from '../../types/icon'; import { IconName } from '../../types/icon';
import { Button, ButtonProps } from '../Button'; import { Button, ButtonProps } from '../Button';
import { CascaderOption } from '../Cascader/Cascader'; import { CascaderOption } from '../Cascader/Cascader';
@@ -27,27 +27,9 @@ export interface ButtonCascaderProps {
hideDownIcon?: boolean; hideDownIcon?: boolean;
} }
const getStyles = stylesFactory((theme: GrafanaTheme2) => {
return {
popup: css({
label: 'popup',
zIndex: theme.zIndex.dropdown,
}),
icons: {
right: css({
margin: '1px 0 0 4px',
}),
left: css({
margin: '-1px 4px 0 0',
}),
},
};
});
export const ButtonCascader = (props: ButtonCascaderProps) => { export const ButtonCascader = (props: ButtonCascaderProps) => {
const { onChange, className, loadData, icon, buttonProps, hideDownIcon, variant, disabled, ...rest } = props; const { onChange, className, loadData, icon, buttonProps, hideDownIcon, variant, disabled, ...rest } = props;
const theme = useTheme2(); const styles = useStyles2(getStyles);
const styles = getStyles(theme);
// Weird way to do this bit it goes around a styling issue in Button where even null/undefined child triggers // Weird way to do this bit it goes around a styling issue in Button where even null/undefined child triggers
// styling change which messes up the look if there is only single icon content. // styling change which messes up the look if there is only single icon content.
@@ -72,3 +54,20 @@ export const ButtonCascader = (props: ButtonCascaderProps) => {
}; };
ButtonCascader.displayName = 'ButtonCascader'; ButtonCascader.displayName = 'ButtonCascader';
const getStyles = (theme: GrafanaTheme2) => {
return {
popup: css({
label: 'popup',
zIndex: theme.zIndex.dropdown,
}),
icons: {
right: css({
margin: '1px 0 0 4px',
}),
left: css({
margin: '-1px 4px 0 0',
}),
},
};
};

View File

@@ -3,7 +3,7 @@ import React, { memo, cloneElement, FC, useMemo, useContext, ReactNode } from 'r
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { useStyles2, useTheme2 } from '../../themes'; import { useStyles2 } from '../../themes';
import { getFocusStyles } from '../../themes/mixins'; import { getFocusStyles } from '../../themes/mixins';
import { CardContainer, CardContainerProps, getCardContainerStyles } from './CardContainer'; import { CardContainer, CardContainerProps, getCardContainerStyles } from './CardContainer';
@@ -55,8 +55,7 @@ export const Card: CardInterface = ({ disabled, href, onClick, children, isSelec
const disableHover = disabled || (!onClick && !href); const disableHover = disabled || (!onClick && !href);
const onCardClick = onClick && !disabled ? onClick : undefined; const onCardClick = onClick && !disabled ? onClick : undefined;
const theme = useTheme2(); const styles = useStyles2(getCardContainerStyles, disabled, disableHover, isSelected);
const styles = getCardContainerStyles(theme, disabled, disableHover, isSelected);
return ( return (
<CardContainer <CardContainer

View File

@@ -3,7 +3,7 @@ import React, { HTMLAttributes } from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { styleMixins, stylesFactory, useStyles2, useTheme2 } from '../../themes'; import { styleMixins, useStyles2 } from '../../themes';
/** /**
* @public * @public
@@ -57,8 +57,8 @@ export const CardContainer = ({
href, href,
...props ...props
}: CardContainerProps) => { }: CardContainerProps) => {
const theme = useTheme2(); const { oldContainer } = useStyles2(getCardContainerStyles, disableEvents, disableHover, isSelected);
const { oldContainer } = getCardContainerStyles(theme, disableEvents, disableHover, isSelected);
return ( return (
<div {...props} className={cx(oldContainer, className)}> <div {...props} className={cx(oldContainer, className)}>
<CardInner href={href}>{children}</CardInner> <CardInner href={href}>{children}</CardInner>
@@ -66,71 +66,74 @@ export const CardContainer = ({
); );
}; };
export const getCardContainerStyles = stylesFactory( export const getCardContainerStyles = (
(theme: GrafanaTheme2, disabled = false, disableHover = false, isSelected) => { theme: GrafanaTheme2,
const isSelectable = isSelected !== undefined; disabled = false,
disableHover = false,
isSelected?: boolean
) => {
const isSelectable = isSelected !== undefined;
return { return {
container: css({ container: css({
display: 'grid', display: 'grid',
position: 'relative', position: 'relative',
gridTemplateColumns: 'auto 1fr auto', gridTemplateColumns: 'auto 1fr auto',
gridTemplateRows: '1fr auto auto auto', gridTemplateRows: '1fr auto auto auto',
gridAutoColumns: '1fr', gridAutoColumns: '1fr',
gridAutoFlow: 'row', gridAutoFlow: 'row',
gridTemplateAreas: ` gridTemplateAreas: `
"Figure Heading Tags" "Figure Heading Tags"
"Figure Meta Tags" "Figure Meta Tags"
"Figure Description Tags" "Figure Description Tags"
"Figure Actions Secondary"`, "Figure Actions Secondary"`,
width: '100%', width: '100%',
padding: theme.spacing(2), padding: theme.spacing(2),
background: theme.colors.background.secondary, background: theme.colors.background.secondary,
borderRadius: theme.shape.radius.default, borderRadius: theme.shape.radius.default,
marginBottom: '8px', marginBottom: '8px',
pointerEvents: disabled ? 'none' : 'auto', pointerEvents: disabled ? 'none' : 'auto',
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], { transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
duration: theme.transitions.duration.short, duration: theme.transitions.duration.short,
}), }),
...(!disableHover && { ...(!disableHover && {
'&:hover': { '&:hover': {
background: theme.colors.emphasize(theme.colors.background.secondary, 0.03), background: theme.colors.emphasize(theme.colors.background.secondary, 0.03),
cursor: 'pointer',
zIndex: 1,
},
'&:focus': styleMixins.getFocusStyles(theme),
}),
...(isSelectable && {
cursor: 'pointer', cursor: 'pointer',
}), zIndex: 1,
},
...(isSelected && { '&:focus': styleMixins.getFocusStyles(theme),
outline: `solid 2px ${theme.colors.primary.border}`,
}),
}), }),
oldContainer: css({
display: 'flex',
width: '100%',
background: theme.colors.background.secondary,
borderRadius: theme.shape.radius.default,
position: 'relative',
pointerEvents: disabled ? 'none' : 'auto',
marginBottom: theme.spacing(1),
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
duration: theme.transitions.duration.short,
}),
...(!disableHover && { ...(isSelectable && {
'&:hover': { cursor: 'pointer',
background: theme.colors.emphasize(theme.colors.background.secondary, 0.03),
cursor: 'pointer',
zIndex: 1,
},
'&:focus': styleMixins.getFocusStyles(theme),
}),
}), }),
};
} ...(isSelected && {
); outline: `solid 2px ${theme.colors.primary.border}`,
}),
}),
oldContainer: css({
display: 'flex',
width: '100%',
background: theme.colors.background.secondary,
borderRadius: theme.shape.radius.default,
position: 'relative',
pointerEvents: disabled ? 'none' : 'auto',
marginBottom: theme.spacing(1),
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
duration: theme.transitions.duration.short,
}),
...(!disableHover && {
'&:hover': {
background: theme.colors.emphasize(theme.colors.background.secondary, 0.03),
cursor: 'pointer',
zIndex: 1,
},
'&:focus': styleMixins.getFocusStyles(theme),
}),
}),
};
};

View File

@@ -4,7 +4,7 @@ import React, { useState } from 'react';
import { DataFrame, DataLink, GrafanaTheme2, VariableSuggestion } from '@grafana/data'; import { DataFrame, DataLink, GrafanaTheme2, VariableSuggestion } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../../themes'; import { useStyles2 } from '../../../themes';
import { Button } from '../../Button/Button'; import { Button } from '../../Button/Button';
import { Modal } from '../../Modal/Modal'; import { Modal } from '../../Modal/Modal';
@@ -19,11 +19,10 @@ interface DataLinksInlineEditorProps {
} }
export const DataLinksInlineEditor = ({ links, onChange, getSuggestions, data }: DataLinksInlineEditorProps) => { export const DataLinksInlineEditor = ({ links, onChange, getSuggestions, data }: DataLinksInlineEditorProps) => {
const theme = useTheme2();
const [editIndex, setEditIndex] = useState<number | null>(null); const [editIndex, setEditIndex] = useState<number | null>(null);
const [isNew, setIsNew] = useState(false); const [isNew, setIsNew] = useState(false);
const styles = getDataLinksInlineEditorStyles(theme); const styles = useStyles2(getDataLinksInlineEditorStyles);
const linksSafe: DataLink[] = links ?? []; const linksSafe: DataLink[] = links ?? [];
const isEditing = editIndex !== null; const isEditing = editIndex !== null;
@@ -110,10 +109,8 @@ export const DataLinksInlineEditor = ({ links, onChange, getSuggestions, data }:
); );
}; };
const getDataLinksInlineEditorStyles = stylesFactory((theme: GrafanaTheme2) => { const getDataLinksInlineEditorStyles = (theme: GrafanaTheme2) => ({
return { wrapper: css({
wrapper: css({ marginBottom: theme.spacing(2),
marginBottom: theme.spacing(2), }),
}),
};
}); });

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { DataFrame, DataLink, GrafanaTheme2 } from '@grafana/data'; import { DataFrame, DataLink, GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../../themes'; import { useStyles2 } from '../../../themes';
import { isCompactUrl } from '../../../utils/dataLinks'; import { isCompactUrl } from '../../../utils/dataLinks';
import { FieldValidationMessage } from '../../Forms/FieldValidationMessage'; import { FieldValidationMessage } from '../../Forms/FieldValidationMessage';
import { IconButton } from '../../IconButton/IconButton'; import { IconButton } from '../../IconButton/IconButton';
@@ -19,8 +19,7 @@ export interface DataLinksListItemProps {
} }
export const DataLinksListItem = ({ link, onEdit, onRemove }: DataLinksListItemProps) => { export const DataLinksListItem = ({ link, onEdit, onRemove }: DataLinksListItemProps) => {
const theme = useTheme2(); const styles = useStyles2(getDataLinkListItemStyles);
const styles = getDataLinkListItemStyles(theme);
const { title = '', url = '' } = link; const { title = '', url = '' } = link;
const hasTitle = title.trim() !== ''; const hasTitle = title.trim() !== '';
@@ -52,7 +51,7 @@ export const DataLinksListItem = ({ link, onEdit, onRemove }: DataLinksListItemP
); );
}; };
const getDataLinkListItemStyles = stylesFactory((theme: GrafanaTheme2) => { const getDataLinkListItemStyles = (theme: GrafanaTheme2) => {
return { return {
wrapper: css({ wrapper: css({
marginBottom: theme.spacing(2), marginBottom: theme.spacing(2),
@@ -95,4 +94,4 @@ const getDataLinkListItemStyles = stylesFactory((theme: GrafanaTheme2) => {
maxWidth: '90%', maxWidth: '90%',
}), }),
}; };
}); };

View File

@@ -4,7 +4,7 @@ import React, { PureComponent } from 'react';
import { DataSourceSettings } from '@grafana/data'; import { DataSourceSettings } from '@grafana/data';
import { stylesFactory } from '../../themes'; import { useStyles2 } from '../../themes';
import { Button } from '../Button'; import { Button } from '../Button';
import { FormField } from '../FormField/FormField'; import { FormField } from '../FormField/FormField';
import { Icon } from '../Icon/Icon'; import { Icon } from '../Icon/Icon';
@@ -36,26 +36,25 @@ interface CustomHeaderRowProps {
onBlur: () => void; onBlur: () => void;
} }
const getCustomHeaderRowStyles = stylesFactory(() => { const getCustomHeaderRowStyles = () => ({
return { layout: css({
layout: css({ display: 'flex',
display: 'flex', alignItems: 'center',
alignItems: 'center', marginBottom: '4px',
marginBottom: '4px', '> *': {
'> *': { marginLeft: '4px',
marginLeft: '4px', marginBottom: 0,
marginBottom: 0, height: '100%',
height: '100%', '&:first-child, &:last-child': {
'&:first-child, &:last-child': { marginLeft: 0,
marginLeft: 0,
},
}, },
}), },
}; }),
}); });
const CustomHeaderRow = ({ header, onBlur, onChange, onRemove, onReset }: CustomHeaderRowProps) => { const CustomHeaderRow = ({ header, onBlur, onChange, onRemove, onReset }: CustomHeaderRowProps) => {
const styles = getCustomHeaderRowStyles(); const styles = useStyles2(getCustomHeaderRowStyles);
return ( return (
<div className={styles.layout}> <div className={styles.layout}>
<FormField <FormField

View File

@@ -72,7 +72,7 @@ export const DateTimePicker = ({
const { dialogProps } = useDialog({}, ref); const { dialogProps } = useDialog({}, ref);
const theme = useTheme2(); const theme = useTheme2();
const { modalBackdrop } = getModalStyles(theme); const { modalBackdrop } = useStyles2(getModalStyles);
const isFullscreen = useMedia(`(min-width: ${theme.breakpoints.values.lg}px)`); const isFullscreen = useMedia(`(min-width: ${theme.breakpoints.values.lg}px)`);
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);

View File

@@ -4,8 +4,7 @@ import React, { FormEvent, MouseEvent, useState } from 'react';
import { dateTime, getDefaultTimeRange, GrafanaTheme2, TimeRange, TimeZone } from '@grafana/data'; import { dateTime, getDefaultTimeRange, GrafanaTheme2, TimeRange, TimeZone } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { stylesFactory } from '../../themes'; import { useStyles2 } from '../../themes/ThemeContext';
import { useTheme2 } from '../../themes/ThemeContext';
import { ClickOutsideWrapper } from '../ClickOutsideWrapper/ClickOutsideWrapper'; import { ClickOutsideWrapper } from '../ClickOutsideWrapper/ClickOutsideWrapper';
import { Icon } from '../Icon/Icon'; import { Icon } from '../Icon/Icon';
import { getInputStyles } from '../Input/Input'; import { getInputStyles } from '../Input/Input';
@@ -47,8 +46,7 @@ export const TimeRangeInput = ({
showIcon = false, showIcon = false,
}: TimeRangeInputProps) => { }: TimeRangeInputProps) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const theme = useTheme2(); const styles = useStyles2(getStyles, disabled);
const styles = getStyles(theme, disabled);
const onOpen = (event: FormEvent<HTMLButtonElement>) => { const onOpen = (event: FormEvent<HTMLButtonElement>) => {
event.stopPropagation(); event.stopPropagation();
@@ -115,7 +113,7 @@ export const TimeRangeInput = ({
); );
}; };
const getStyles = stylesFactory((theme: GrafanaTheme2, disabled = false) => { const getStyles = (theme: GrafanaTheme2, disabled = false) => {
const inputStyles = getInputStyles({ theme, invalid: false }); const inputStyles = getInputStyles({ theme, invalid: false });
return { return {
container: css({ container: css({
@@ -163,4 +161,4 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, disabled = false) => {
marginRight: theme.spacing(0.5), marginRight: theme.spacing(0.5),
}), }),
}; };
}); };

View File

@@ -16,7 +16,7 @@ import {
} from '@grafana/data'; } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { useStyles2, useTheme2 } from '../../themes/ThemeContext'; import { useStyles2 } from '../../themes/ThemeContext';
import { t, Trans } from '../../utils/i18n'; import { t, Trans } from '../../utils/i18n';
import { ButtonGroup } from '../Button'; import { ButtonGroup } from '../Button';
import { getModalStyles } from '../Modal/getModalStyles'; import { getModalStyles } from '../Modal/getModalStyles';
@@ -106,9 +106,8 @@ export function TimeRangePicker(props: TimeRangePickerProps) {
); );
const { dialogProps } = useDialog({}, overlayRef); const { dialogProps } = useDialog({}, overlayRef);
const theme = useTheme2();
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const { modalBackdrop } = getModalStyles(theme); const { modalBackdrop } = useStyles2(getModalStyles);
const hasAbsolute = isDateTime(value.raw.from) || isDateTime(value.raw.to); const hasAbsolute = isDateTime(value.raw.from) || isDateTime(value.raw.to);
const variant = isSynced ? 'active' : isOnCanvas ? 'canvas' : 'default'; const variant = isSynced ? 'active' : isOnCanvas ? 'canvas' : 'default';

View File

@@ -7,7 +7,7 @@ import React, { FormEvent, memo } from 'react';
import { DateTime, GrafanaTheme2, TimeZone } from '@grafana/data'; import { DateTime, GrafanaTheme2, TimeZone } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { useTheme2 } from '../../../themes'; import { useStyles2, useTheme2 } from '../../../themes';
import { getModalStyles } from '../../Modal/getModalStyles'; import { getModalStyles } from '../../Modal/getModalStyles';
import { Body } from './CalendarBody'; import { Body } from './CalendarBody';
@@ -68,7 +68,7 @@ export interface TimePickerCalendarProps {
function TimePickerCalendar(props: TimePickerCalendarProps) { function TimePickerCalendar(props: TimePickerCalendarProps) {
const theme = useTheme2(); const theme = useTheme2();
const { modalBackdrop } = getModalStyles(theme); const { modalBackdrop } = useStyles2(getModalStyles);
const styles = getStyles(theme, props.isReversed); const styles = getStyles(theme, props.isReversed);
const { isOpen, isFullscreen, onClose } = props; const { isOpen, isFullscreen, onClose } = props;
const ref = React.createRef<HTMLElement>(); const ref = React.createRef<HTMLElement>();

View File

@@ -4,7 +4,7 @@ import React, { memo, useMemo, useState } from 'react';
import { GrafanaTheme2, isDateTime, rangeUtil, RawTimeRange, TimeOption, TimeRange, TimeZone } from '@grafana/data'; import { GrafanaTheme2, isDateTime, rangeUtil, RawTimeRange, TimeOption, TimeRange, TimeZone } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { stylesFactory, useTheme2 } from '../../../themes'; import { useStyles2, useTheme2 } from '../../../themes';
import { getFocusStyles } from '../../../themes/mixins'; import { getFocusStyles } from '../../../themes/mixins';
import { t, Trans } from '../../../utils/i18n'; import { t, Trans } from '../../../utils/i18n';
import { CustomScrollbar } from '../../CustomScrollbar/CustomScrollbar'; import { CustomScrollbar } from '../../CustomScrollbar/CustomScrollbar';
@@ -63,8 +63,7 @@ export const TimePickerContentWithScreenSize = (props: PropsWithScreenSize) => {
const isHistoryEmpty = !history?.length; const isHistoryEmpty = !history?.length;
const isContainerTall = const isContainerTall =
(isFullscreen && showHistory) || (!isFullscreen && ((showHistory && !isHistoryEmpty) || !hideQuickRanges)); (isFullscreen && showHistory) || (!isFullscreen && ((showHistory && !isHistoryEmpty) || !hideQuickRanges));
const theme = useTheme2(); const styles = useStyles2(getStyles, isReversed, hideQuickRanges, isContainerTall, isFullscreen);
const styles = getStyles(theme, isReversed, hideQuickRanges, isContainerTall, isFullscreen);
const historyOptions = mapToHistoryOptions(history, timeZone); const historyOptions = mapToHistoryOptions(history, timeZone);
const timeOption = useTimeOption(value.raw, quickOptions); const timeOption = useTimeOption(value.raw, quickOptions);
const [searchTerm, setSearchQuery] = useState(''); const [searchTerm, setSearchQuery] = useState('');
@@ -124,8 +123,7 @@ export const TimePickerContent = (props: Props) => {
const NarrowScreenForm = (props: FormProps) => { const NarrowScreenForm = (props: FormProps) => {
const { value, hideQuickRanges, onChange, timeZone, historyOptions = [], showHistory } = props; const { value, hideQuickRanges, onChange, timeZone, historyOptions = [], showHistory } = props;
const theme = useTheme2(); const styles = useStyles2(getNarrowScreenStyles);
const styles = getNarrowScreenStyles(theme);
const isAbsolute = isDateTime(value.raw.from) || isDateTime(value.raw.to); const isAbsolute = isDateTime(value.raw.from) || isDateTime(value.raw.to);
const [collapsedFlag, setCollapsedFlag] = useState(!isAbsolute); const [collapsedFlag, setCollapsedFlag] = useState(!isAbsolute);
const collapsed = hideQuickRanges ? false : collapsedFlag; const collapsed = hideQuickRanges ? false : collapsedFlag;
@@ -176,8 +174,7 @@ const NarrowScreenForm = (props: FormProps) => {
const FullScreenForm = (props: FormProps) => { const FullScreenForm = (props: FormProps) => {
const { onChange, value, timeZone, fiscalYearStartMonth, isReversed, historyOptions } = props; const { onChange, value, timeZone, fiscalYearStartMonth, isReversed, historyOptions } = props;
const theme = useTheme2(); const styles = useStyles2(getFullScreenStyles, props.hideQuickRanges);
const styles = getFullScreenStyles(theme, props.hideQuickRanges);
const onChangeTimeOption = (timeOption: TimeOption) => { const onChangeTimeOption = (timeOption: TimeOption) => {
return onChange(mapOptionToTimeRange(timeOption, timeZone)); return onChange(mapOptionToTimeRange(timeOption, timeZone));
}; };
@@ -214,8 +211,7 @@ const FullScreenForm = (props: FormProps) => {
}; };
const EmptyRecentList = memo(() => { const EmptyRecentList = memo(() => {
const theme = useTheme2(); const styles = useStyles2(getEmptyListStyles);
const styles = getEmptyListStyles(theme);
const emptyRecentListText = t( const emptyRecentListText = t(
'time-picker.content.empty-recent-list-info', 'time-picker.content.empty-recent-list-info',
"It looks like you haven't used this time picker before. As soon as you enter some time intervals, recently used intervals will appear here." "It looks like you haven't used this time picker before. As soon as you enter some time intervals, recently used intervals will appear here."
@@ -263,104 +259,102 @@ const useTimeOption = (raw: RawTimeRange, quickOptions: TimeOption[]): TimeOptio
}, [raw, quickOptions]); }, [raw, quickOptions]);
}; };
const getStyles = stylesFactory((theme: GrafanaTheme2, isReversed, hideQuickRanges, isContainerTall, isFullscreen) => { const getStyles = (
return { theme: GrafanaTheme2,
container: css({ isReversed?: boolean,
background: theme.colors.background.primary, hideQuickRanges?: boolean,
boxShadow: theme.shadows.z3, isContainerTall?: boolean,
width: `${isFullscreen ? '546px' : '262px'}`, isFullscreen?: boolean
borderRadius: theme.shape.radius.default, ) => ({
border: `1px solid ${theme.colors.border.weak}`, container: css({
[`${isReversed ? 'left' : 'right'}`]: 0, background: theme.colors.background.primary,
}), boxShadow: theme.shadows.z3,
body: css({ width: `${isFullscreen ? '546px' : '262px'}`,
display: 'flex', borderRadius: theme.shape.radius.default,
flexDirection: 'row-reverse', border: `1px solid ${theme.colors.border.weak}`,
height: `${isContainerTall ? '381px' : '217px'}`, [`${isReversed ? 'left' : 'right'}`]: 0,
maxHeight: '100vh', }),
}), body: css({
leftSide: css({ display: 'flex',
display: 'flex', flexDirection: 'row-reverse',
flexDirection: 'column', height: `${isContainerTall ? '381px' : '217px'}`,
borderRight: `${isReversed ? 'none' : `1px solid ${theme.colors.border.weak}`}`, maxHeight: '100vh',
width: `${!hideQuickRanges ? '60%' : '100%'}`, }),
overflow: 'hidden', leftSide: css({
order: isReversed ? 1 : 0, display: 'flex',
}), flexDirection: 'column',
rightSide: css({ borderRight: `${isReversed ? 'none' : `1px solid ${theme.colors.border.weak}`}`,
width: `${isFullscreen ? '40%' : '100%'}; !important`, width: `${!hideQuickRanges ? '60%' : '100%'}`,
borderRight: isReversed ? `1px solid ${theme.colors.border.weak}` : 'none', overflow: 'hidden',
display: 'flex', order: isReversed ? 1 : 0,
flexDirection: 'column', }),
}), rightSide: css({
timeRangeFilter: css({ width: `${isFullscreen ? '40%' : '100%'}; !important`,
padding: theme.spacing(1), borderRight: isReversed ? `1px solid ${theme.colors.border.weak}` : 'none',
}), display: 'flex',
spacing: css({ flexDirection: 'column',
marginTop: '16px', }),
}), timeRangeFilter: css({
}; padding: theme.spacing(1),
}),
spacing: css({
marginTop: '16px',
}),
}); });
const getNarrowScreenStyles = stylesFactory((theme: GrafanaTheme2) => { const getNarrowScreenStyles = (theme: GrafanaTheme2) => ({
return { header: css({
header: css({ display: 'flex',
display: 'flex', flexDirection: 'row',
flexDirection: 'row', justifyContent: 'space-between',
justifyContent: 'space-between', alignItems: 'center',
alignItems: 'center', borderBottom: `1px solid ${theme.colors.border.weak}`,
borderBottom: `1px solid ${theme.colors.border.weak}`, padding: '7px 9px 7px 9px',
padding: '7px 9px 7px 9px', }),
}), expandButton: css({
expandButton: css({ backgroundColor: 'transparent',
backgroundColor: 'transparent', border: 'none',
border: 'none', display: 'flex',
display: 'flex', width: '100%',
width: '100%',
'&:focus-visible': getFocusStyles(theme), '&:focus-visible': getFocusStyles(theme),
}), }),
body: css({ body: css({
borderBottom: `1px solid ${theme.colors.border.weak}`, borderBottom: `1px solid ${theme.colors.border.weak}`,
}), }),
form: css({ form: css({
padding: '7px 9px 7px 9px', padding: '7px 9px 7px 9px',
}), }),
};
}); });
const getFullScreenStyles = stylesFactory((theme: GrafanaTheme2, hideQuickRanges?: boolean) => { const getFullScreenStyles = (theme: GrafanaTheme2, hideQuickRanges?: boolean) => ({
return { container: css({
container: css({ paddingTop: '9px',
paddingTop: '9px', paddingLeft: '11px',
paddingLeft: '11px', paddingRight: !hideQuickRanges ? '20%' : '11px',
paddingRight: !hideQuickRanges ? '20%' : '11px', }),
}), title: css({
title: css({ marginBottom: '11px',
marginBottom: '11px', }),
}), recent: css({
recent: css({ flexGrow: 1,
flexGrow: 1, display: 'flex',
display: 'flex', flexDirection: 'column',
flexDirection: 'column', justifyContent: 'flex-end',
justifyContent: 'flex-end', paddingTop: theme.spacing(1),
paddingTop: theme.spacing(1), }),
}),
};
}); });
const getEmptyListStyles = stylesFactory((theme: GrafanaTheme2) => { const getEmptyListStyles = (theme: GrafanaTheme2) => ({
return { container: css({
container: css({ padding: '12px',
padding: '12px', margin: '12px',
margin: '12px',
'a, span': { 'a, span': {
fontSize: '13px', fontSize: '13px',
}, },
}), }),
link: css({ link: css({
color: theme.colors.text.link, color: theme.colors.text.link,
}), }),
};
}); });

View File

@@ -5,7 +5,7 @@ import React, { useCallback, useState } from 'react';
import { getTimeZoneInfo, GrafanaTheme2, TimeZone } from '@grafana/data'; import { getTimeZoneInfo, GrafanaTheme2, TimeZone } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { stylesFactory, useTheme2 } from '../../../themes'; import { useStyles2 } from '../../../themes';
import { t, Trans } from '../../../utils/i18n'; import { t, Trans } from '../../../utils/i18n';
import { Button } from '../../Button'; import { Button } from '../../Button';
import { Field } from '../../Forms/Field'; import { Field } from '../../Forms/Field';
@@ -46,8 +46,7 @@ export const TimePickerFooter = (props: Props) => {
[isEditing, setEditing] [isEditing, setEditing]
); );
const theme = useTheme2(); const style = useStyles2(getStyle);
const style = getStyle(theme);
if (!isString(timeZone)) { if (!isString(timeZone)) {
return null; return null;
@@ -136,43 +135,41 @@ export const TimePickerFooter = (props: Props) => {
); );
}; };
const getStyle = stylesFactory((theme: GrafanaTheme2) => { const getStyle = (theme: GrafanaTheme2) => ({
return { container: css({
container: css({ borderTop: `1px solid ${theme.colors.border.weak}`,
borderTop: `1px solid ${theme.colors.border.weak}`, padding: '11px',
padding: '11px', display: 'flex',
display: 'flex', flexDirection: 'row',
flexDirection: 'row', justifyContent: 'space-between',
justifyContent: 'space-between', alignItems: 'center',
alignItems: 'center', }),
}), editContainer: css({
editContainer: css({ borderTop: `1px solid ${theme.colors.border.weak}`,
borderTop: `1px solid ${theme.colors.border.weak}`, padding: '11px',
padding: '11px', justifyContent: 'space-between',
justifyContent: 'space-between', alignItems: 'center',
alignItems: 'center', }),
}), spacer: css({
spacer: css({ marginLeft: '7px',
marginLeft: '7px', }),
}), timeSettingContainer: css({
timeSettingContainer: css({ paddingTop: theme.spacing(1),
paddingTop: theme.spacing(1), }),
}), fiscalYearField: css({
fiscalYearField: css({ marginBottom: 0,
marginBottom: 0, }),
}), timeZoneContainer: css({
timeZoneContainer: css({ display: 'flex',
display: 'flex', flexDirection: 'row',
flexDirection: 'row', justifyContent: 'space-between',
justifyContent: 'space-between', alignItems: 'center',
alignItems: 'center', flexGrow: 1,
flexGrow: 1, }),
}), timeZone: css({
timeZone: css({ display: 'flex',
display: 'flex', flexDirection: 'row',
flexDirection: 'row', alignItems: 'baseline',
alignItems: 'baseline', flexGrow: 1,
flexGrow: 1, }),
}),
};
}); });

View File

@@ -3,32 +3,12 @@ import React, { ReactNode } from 'react';
import { TimeOption } from '@grafana/data'; import { TimeOption } from '@grafana/data';
import { stylesFactory } from '../../../themes'; import { useStyles2 } from '../../../themes';
import { t } from '../../../utils/i18n'; import { t } from '../../../utils/i18n';
import { TimePickerTitle } from './TimePickerTitle'; import { TimePickerTitle } from './TimePickerTitle';
import { TimeRangeOption } from './TimeRangeOption'; import { TimeRangeOption } from './TimeRangeOption';
const getStyles = stylesFactory(() => {
return {
title: css({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '8px 16px 5px 9px',
}),
};
});
const getOptionsStyles = stylesFactory(() => {
return {
grow: css({
flexGrow: 1,
alignItems: 'flex-start',
}),
};
});
interface Props { interface Props {
title?: string; title?: string;
options: TimeOption[]; options: TimeOption[];
@@ -38,7 +18,7 @@ interface Props {
} }
export const TimeRangeList = (props: Props) => { export const TimeRangeList = (props: Props) => {
const styles = getStyles(); const styles = useStyles2(getStyles);
const { title, options, placeholderEmpty } = props; const { title, options, placeholderEmpty } = props;
if (typeof placeholderEmpty !== 'undefined' && options.length <= 0) { if (typeof placeholderEmpty !== 'undefined' && options.length <= 0) {
@@ -62,7 +42,7 @@ export const TimeRangeList = (props: Props) => {
}; };
const Options = ({ options, value, onChange, title }: Props) => { const Options = ({ options, value, onChange, title }: Props) => {
const styles = getOptionsStyles(); const styles = useStyles2(getOptionsStyles);
return ( return (
<> <>
@@ -92,3 +72,19 @@ function isEqual(x: TimeOption, y?: TimeOption): boolean {
} }
return y.from === x.from && y.to === x.to; return y.from === x.from && y.to === x.to;
} }
const getStyles = () => ({
title: css({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '8px 16px 5px 9px',
}),
});
const getOptionsStyles = () => ({
grow: css({
flexGrow: 1,
alignItems: 'flex-start',
}),
});

View File

@@ -4,8 +4,7 @@ import React, { PropsWithChildren, RefCallback } from 'react';
import { GrafanaTheme2, SelectableValue, getTimeZoneInfo } from '@grafana/data'; import { GrafanaTheme2, SelectableValue, getTimeZoneInfo } from '@grafana/data';
import { useTheme2 } from '../../../themes/ThemeContext'; import { useStyles2 } from '../../../themes';
import { stylesFactory } from '../../../themes/stylesFactory';
import { Icon } from '../../Icon/Icon'; import { Icon } from '../../Icon/Icon';
import { TimeZoneDescription } from './TimeZoneDescription'; import { TimeZoneDescription } from './TimeZoneDescription';
@@ -28,8 +27,7 @@ export interface SelectableZone extends SelectableValue<string> {
export const WideTimeZoneOption = (props: PropsWithChildren<Props>) => { export const WideTimeZoneOption = (props: PropsWithChildren<Props>) => {
const { children, innerProps, innerRef, data, isSelected, isFocused } = props; const { children, innerProps, innerRef, data, isSelected, isFocused } = props;
const theme = useTheme2(); const styles = useStyles2(getStyles);
const styles = getStyles(theme);
const timestamp = Date.now(); const timestamp = Date.now();
const containerStyles = cx(styles.container, isFocused && styles.containerFocused); const containerStyles = cx(styles.container, isFocused && styles.containerFocused);
@@ -68,8 +66,7 @@ export const WideTimeZoneOption = (props: PropsWithChildren<Props>) => {
export const CompactTimeZoneOption = (props: React.PropsWithChildren<Props>) => { export const CompactTimeZoneOption = (props: React.PropsWithChildren<Props>) => {
const { children, innerProps, innerRef, data, isSelected, isFocused } = props; const { children, innerProps, innerRef, data, isSelected, isFocused } = props;
const theme = useTheme2(); const styles = useStyles2(getStyles);
const styles = getStyles(theme);
const timestamp = Date.now(); const timestamp = Date.now();
const containerStyles = cx(styles.container, isFocused && styles.containerFocused); const containerStyles = cx(styles.container, isFocused && styles.containerFocused);
@@ -113,49 +110,47 @@ export const CompactTimeZoneOption = (props: React.PropsWithChildren<Props>) =>
); );
}; };
const getStyles = stylesFactory((theme: GrafanaTheme2) => { const getStyles = (theme: GrafanaTheme2) => ({
return { container: css({
container: css({ display: 'flex',
display: 'flex', alignItems: 'center',
alignItems: 'center', flexDirection: 'row',
flexDirection: 'row', flexShrink: 0,
flexShrink: 0, whiteSpace: 'nowrap',
whiteSpace: 'nowrap', cursor: 'pointer',
cursor: 'pointer', padding: '6px 8px 4px',
padding: '6px 8px 4px',
'&:hover': { '&:hover': {
background: theme.colors.action.hover,
},
}),
containerFocused: css({
background: theme.colors.action.hover, background: theme.colors.action.hover,
}), },
body: css({ }),
display: 'flex', containerFocused: css({
fontWeight: theme.typography.fontWeightMedium, background: theme.colors.action.hover,
flexDirection: 'column', }),
flexGrow: 1, body: css({
}), display: 'flex',
row: css({ fontWeight: theme.typography.fontWeightMedium,
display: 'flex', flexDirection: 'column',
flexDirection: 'row', flexGrow: 1,
}), }),
leftColumn: css({ row: css({
flexGrow: 1, display: 'flex',
textOverflow: 'ellipsis', flexDirection: 'row',
}), }),
rightColumn: css({ leftColumn: css({
justifyContent: 'flex-end', flexGrow: 1,
alignItems: 'center', textOverflow: 'ellipsis',
}), }),
wideRow: css({ rightColumn: css({
display: 'flex', justifyContent: 'flex-end',
flexDirection: 'row', alignItems: 'center',
alignItems: 'baseline', }),
}), wideRow: css({
spacer: css({ display: 'flex',
marginLeft: '6px', flexDirection: 'row',
}), alignItems: 'baseline',
}; }),
spacer: css({
marginLeft: '6px',
}),
}); });

View File

@@ -1,30 +1,34 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import React from 'react'; import React from 'react';
import { stylesFactory } from '../../themes'; import { useStyles2 } from '../../themes';
import { ErrorBoundaryApi } from './ErrorBoundary'; import { ErrorBoundaryApi } from './ErrorBoundary';
const getStyles = stylesFactory(() => {
return css({
width: '500px',
margin: '64px auto',
});
});
export interface Props extends ErrorBoundaryApi { export interface Props extends ErrorBoundaryApi {
title: string; title: string;
} }
export const ErrorWithStack = ({ error, errorInfo, title }: Props) => ( export const ErrorWithStack = ({ error, errorInfo, title }: Props) => {
<div className={getStyles()}> const style = useStyles2(getStyles);
<h2>{title}</h2>
<details style={{ whiteSpace: 'pre-wrap' }}> return (
{error && error.toString()} <div className={style}>
<br /> <h2>{title}</h2>
{errorInfo && errorInfo.componentStack} <details style={{ whiteSpace: 'pre-wrap' }}>
</details> {error && error.toString()}
</div> <br />
); {errorInfo && errorInfo.componentStack}
</details>
</div>
);
};
ErrorWithStack.displayName = 'ErrorWithStack'; ErrorWithStack.displayName = 'ErrorWithStack';
const getStyles = () => {
return css({
width: '500px',
margin: '64px auto',
});
};

View File

@@ -3,7 +3,7 @@ import React, { HTMLProps, useCallback } from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { useTheme2 } from '../../themes'; import { useStyles2 } from '../../themes';
import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins'; import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins';
import { getLabelStyles } from './Label'; import { getLabelStyles } from './Label';
@@ -36,8 +36,7 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
}, },
[onChange] [onChange]
); );
const theme = useTheme2(); const styles = useStyles2(getCheckboxStyles, invalid);
const styles = getCheckboxStyles(theme, invalid);
const ariaChecked = indeterminate ? 'mixed' : undefined; const ariaChecked = indeterminate ? 'mixed' : undefined;

View File

@@ -3,7 +3,7 @@ import React, { HTMLAttributes } from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../themes'; import { useStyles2 } from '../../themes';
import { getChildId } from '../../utils/reactUtils'; import { getChildId } from '../../utils/reactUtils';
import { FieldValidationMessage } from './FieldValidationMessage'; import { FieldValidationMessage } from './FieldValidationMessage';
@@ -40,35 +40,6 @@ export interface FieldProps extends HTMLAttributes<HTMLDivElement> {
htmlFor?: string; htmlFor?: string;
} }
export const getFieldStyles = stylesFactory((theme: GrafanaTheme2) => {
return {
field: css({
display: 'flex',
flexDirection: 'column',
marginBottom: theme.spacing(2),
}),
fieldHorizontal: css({
flexDirection: 'row',
justifyContent: 'space-between',
flexWrap: 'wrap',
}),
fieldValidationWrapper: css({
marginTop: theme.spacing(0.5),
}),
fieldValidationWrapperHorizontal: css({
flex: '1 1 100%',
}),
validationMessageHorizontalOverflow: css({
width: 0,
overflowX: 'visible',
'& > *': {
whiteSpace: 'nowrap',
},
}),
};
});
export const Field = React.forwardRef<HTMLDivElement, FieldProps>( export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
( (
{ {
@@ -88,8 +59,7 @@ export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
}: FieldProps, }: FieldProps,
ref ref
) => { ) => {
const theme = useTheme2(); const styles = useStyles2(getFieldStyles);
const styles = getFieldStyles(theme);
const inputId = htmlFor ?? getChildId(children); const inputId = htmlFor ?? getChildId(children);
const labelElement = const labelElement =
@@ -143,3 +113,30 @@ function deleteUndefinedProps<T extends Object>(obj: T): Partial<T> {
return obj; return obj;
} }
export const getFieldStyles = (theme: GrafanaTheme2) => ({
field: css({
display: 'flex',
flexDirection: 'column',
marginBottom: theme.spacing(2),
}),
fieldHorizontal: css({
flexDirection: 'row',
justifyContent: 'space-between',
flexWrap: 'wrap',
}),
fieldValidationWrapper: css({
marginTop: theme.spacing(0.5),
}),
fieldValidationWrapperHorizontal: css({
flex: '1 1 100%',
}),
validationMessageHorizontalOverflow: css({
width: 0,
overflowX: 'visible',
'& > *': {
whiteSpace: 'nowrap',
},
}),
});

View File

@@ -3,7 +3,7 @@ import React, { HTMLProps } from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../themes'; import { useStyles2 } from '../../themes';
import { Legend } from './Legend'; import { Legend } from './Legend';
@@ -14,8 +14,7 @@ export interface Props extends Omit<HTMLProps<HTMLFieldSetElement>, 'label'> {
} }
export const FieldSet = ({ label, children, className, ...rest }: Props) => { export const FieldSet = ({ label, children, className, ...rest }: Props) => {
const theme = useTheme2(); const styles = useStyles2(getStyles);
const styles = getStyles(theme);
return ( return (
<fieldset className={cx(styles.wrapper, className)} {...rest}> <fieldset className={cx(styles.wrapper, className)} {...rest}>
@@ -25,14 +24,12 @@ export const FieldSet = ({ label, children, className, ...rest }: Props) => {
); );
}; };
const getStyles = stylesFactory((theme: GrafanaTheme2) => { const getStyles = (theme: GrafanaTheme2) => ({
return { wrapper: css({
wrapper: css({ marginBottom: theme.spacing(4),
marginBottom: theme.spacing(4),
'&:last-child': { '&:last-child': {
marginBottom: 0, marginBottom: 0,
}, },
}), }),
};
}); });

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../themes'; import { useStyles2 } from '../../themes';
import { Icon } from '../Icon/Icon'; import { Icon } from '../Icon/Icon';
export interface FieldValidationMessageProps { export interface FieldValidationMessageProps {
@@ -12,7 +12,23 @@ export interface FieldValidationMessageProps {
horizontal?: boolean; horizontal?: boolean;
} }
export const getFieldValidationMessageStyles = stylesFactory((theme: GrafanaTheme2) => { export const FieldValidationMessage = ({
children,
horizontal,
className,
}: React.PropsWithChildren<FieldValidationMessageProps>) => {
const styles = useStyles2(getFieldValidationMessageStyles);
const cssName = cx(horizontal ? styles.horizontal : styles.vertical, className);
return (
<div role="alert" className={cssName}>
<Icon className={styles.fieldValidationMessageIcon} name="exclamation-triangle" />
{children}
</div>
);
};
export const getFieldValidationMessageStyles = (theme: GrafanaTheme2) => {
const baseStyle = ` const baseStyle = `
font-size: ${theme.typography.size.sm}; font-size: ${theme.typography.size.sm};
font-weight: ${theme.typography.fontWeightMedium}; font-weight: ${theme.typography.fontWeightMedium};
@@ -69,21 +85,4 @@ export const getFieldValidationMessageStyles = stylesFactory((theme: GrafanaThem
marginRight: theme.spacing(), marginRight: theme.spacing(),
}), }),
}; };
});
export const FieldValidationMessage = ({
children,
horizontal,
className,
}: React.PropsWithChildren<FieldValidationMessageProps>) => {
const theme = useTheme2();
const styles = getFieldValidationMessageStyles(theme);
const cssName = cx(horizontal ? styles.horizontal : styles.vertical, className);
return (
<div role="alert" className={cssName}>
<Icon className={styles.fieldValidationMessageIcon} name="exclamation-triangle" />
{children}
</div>
);
}; };

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { useTheme2, stylesFactory } from '../../themes'; import { useStyles2 } from '../../themes';
import { Icon } from '../Icon/Icon'; import { Icon } from '../Icon/Icon';
export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> { export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
@@ -12,43 +12,8 @@ export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement>
category?: React.ReactNode[]; category?: React.ReactNode[];
} }
export const getLabelStyles = stylesFactory((theme: GrafanaTheme2) => {
return {
label: css({
label: 'Label',
fontSize: theme.typography.size.sm,
fontWeight: theme.typography.fontWeightMedium,
lineHeight: 1.25,
marginBottom: theme.spacing(0.5),
color: theme.colors.text.primary,
maxWidth: '480px',
}),
labelContent: css({
display: 'flex',
alignItems: 'center',
}),
description: css({
label: 'Label-description',
color: theme.colors.text.secondary,
fontSize: theme.typography.size.sm,
fontWeight: theme.typography.fontWeightRegular,
marginTop: theme.spacing(0.25),
display: 'block',
}),
categories: css({
label: 'Label-categories',
display: 'inline-flex',
alignItems: 'center',
}),
chevron: css({
margin: theme.spacing(0, 0.25),
}),
};
});
export const Label = ({ children, description, className, category, ...labelProps }: LabelProps) => { export const Label = ({ children, description, className, category, ...labelProps }: LabelProps) => {
const theme = useTheme2(); const styles = useStyles2(getLabelStyles);
const styles = getLabelStyles(theme);
const categories = category?.map((c, i) => { const categories = category?.map((c, i) => {
return ( return (
<span className={styles.categories} key={`${c}/${i}`}> <span className={styles.categories} key={`${c}/${i}`}>
@@ -70,3 +35,35 @@ export const Label = ({ children, description, className, category, ...labelProp
</div> </div>
); );
}; };
export const getLabelStyles = (theme: GrafanaTheme2) => ({
label: css({
label: 'Label',
fontSize: theme.typography.size.sm,
fontWeight: theme.typography.fontWeightMedium,
lineHeight: 1.25,
marginBottom: theme.spacing(0.5),
color: theme.colors.text.primary,
maxWidth: '480px',
}),
labelContent: css({
display: 'flex',
alignItems: 'center',
}),
description: css({
label: 'Label-description',
color: theme.colors.text.secondary,
fontSize: theme.typography.size.sm,
fontWeight: theme.typography.fontWeightRegular,
marginTop: theme.spacing(0.25),
display: 'block',
}),
categories: css({
label: 'Label-categories',
display: 'inline-flex',
alignItems: 'center',
}),
chevron: css({
margin: theme.spacing(0, 0.25),
}),
});

View File

@@ -4,7 +4,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { StringSelector } from '@grafana/e2e-selectors'; import { StringSelector } from '@grafana/e2e-selectors';
import { useTheme2, stylesFactory } from '../../../themes'; import { useStyles2 } from '../../../themes';
import { getFocusStyles, getMouseFocusStyles } from '../../../themes/mixins'; import { getFocusStyles, getMouseFocusStyles } from '../../../themes/mixins';
import { getPropertiesForButtonSize } from '../commonStyles'; import { getPropertiesForButtonSize } from '../commonStyles';
@@ -41,8 +41,7 @@ export const RadioButton = React.forwardRef<HTMLInputElement, RadioButtonProps>(
}, },
ref ref
) => { ) => {
const theme = useTheme2(); const styles = useStyles2(getRadioButtonStyles, size, fullWidth);
const styles = getRadioButtonStyles(theme, size, fullWidth);
return ( return (
<div className={styles.radioOption}> <div className={styles.radioOption}>
@@ -68,7 +67,7 @@ export const RadioButton = React.forwardRef<HTMLInputElement, RadioButtonProps>(
RadioButton.displayName = 'RadioButton'; RadioButton.displayName = 'RadioButton';
const getRadioButtonStyles = stylesFactory((theme: GrafanaTheme2, size: RadioButtonSize, fullWidth?: boolean) => { const getRadioButtonStyles = (theme: GrafanaTheme2, size: RadioButtonSize, fullWidth?: boolean) => {
const { fontSize, height, padding } = getPropertiesForButtonSize(size, theme); const { fontSize, height, padding } = getPropertiesForButtonSize(size, theme);
const textColor = theme.colors.text.secondary; const textColor = theme.colors.text.secondary;
@@ -130,4 +129,4 @@ const getRadioButtonStyles = stylesFactory((theme: GrafanaTheme2, size: RadioBut
}, },
}), }),
}; };
}); };

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { GrafanaTheme2, colorManipulator, deprecationWarning } from '@grafana/data'; import { GrafanaTheme2, colorManipulator, deprecationWarning } from '@grafana/data';
import { useTheme2, stylesFactory } from '../../themes'; import { useStyles2 } from '../../themes';
import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins'; import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins';
import { ComponentSize } from '../../types'; import { ComponentSize } from '../../types';
import { IconName, IconSize, IconType } from '../../types/icon'; import { IconName, IconSize, IconType } from '../../types/icon';
@@ -44,8 +44,6 @@ export type Props = BasePropsWithTooltip | BasePropsWithAriaLabel;
export const IconButton = React.forwardRef<HTMLButtonElement, Props>((props, ref) => { export const IconButton = React.forwardRef<HTMLButtonElement, Props>((props, ref) => {
const { size = 'md', variant = 'secondary' } = props; const { size = 'md', variant = 'secondary' } = props;
const theme = useTheme2();
let limitedIconSize: LimitedIconSize; let limitedIconSize: LimitedIconSize;
// very large icons (xl to xxxl) are unified to size xl // very large icons (xl to xxxl) are unified to size xl
@@ -56,7 +54,7 @@ export const IconButton = React.forwardRef<HTMLButtonElement, Props>((props, ref
limitedIconSize = size; limitedIconSize = size;
} }
const styles = getStyles(theme, limitedIconSize, variant); const styles = useStyles2(getStyles, limitedIconSize, variant);
let ariaLabel: string | undefined; let ariaLabel: string | undefined;
let buttonRef: typeof ref | undefined; let buttonRef: typeof ref | undefined;
@@ -104,7 +102,7 @@ export const IconButton = React.forwardRef<HTMLButtonElement, Props>((props, ref
IconButton.displayName = 'IconButton'; IconButton.displayName = 'IconButton';
const getStyles = stylesFactory((theme: GrafanaTheme2, size, variant: IconButtonVariant) => { const getStyles = (theme: GrafanaTheme2, size: IconSize, variant: IconButtonVariant) => {
// overall size of the IconButton on hover // overall size of the IconButton on hover
// theme.spacing.gridSize originates from 2*4px for padding and letting the IconSize generally decide on the hoverSize // theme.spacing.gridSize originates from 2*4px for padding and letting the IconSize generally decide on the hoverSize
const hoverSize = getSvgSize(size) + theme.spacing.gridSize; const hoverSize = getSvgSize(size) + theme.spacing.gridSize;
@@ -166,4 +164,4 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, size, variant: IconButton
verticalAlign: 'baseline', verticalAlign: 'baseline',
}), }),
}; };
}); };

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useStyles2 } from '../../themes'; import { useStyles2 } from '../../themes';
import { Alert, AlertVariant } from '../Alert/Alert'; import { Alert, AlertVariant } from '../Alert/Alert';
import { Icon } from '../Icon/Icon'; import { Icon } from '../Icon/Icon';
@@ -45,11 +45,9 @@ export const InfoBox = React.memo(
InfoBox.displayName = 'InfoBox'; InfoBox.displayName = 'InfoBox';
const getStyles = stylesFactory((theme: GrafanaTheme2) => { const getStyles = (theme: GrafanaTheme2) => ({
return { docsLink: css({
docsLink: css({ display: 'inline-block',
display: 'inline-block', marginTop: theme.spacing(2),
marginTop: theme.spacing(2), }),
}),
};
}); });

View File

@@ -4,7 +4,7 @@ import { FocusScope } from '@react-aria/focus';
import { OverlayContainer, useOverlay } from '@react-aria/overlays'; import { OverlayContainer, useOverlay } from '@react-aria/overlays';
import React, { PropsWithChildren, useRef } from 'react'; import React, { PropsWithChildren, useRef } from 'react';
import { useTheme2 } from '../../themes'; import { useStyles2 } from '../../themes';
import { IconName } from '../../types'; import { IconName } from '../../types';
import { t } from '../../utils/i18n'; import { t } from '../../utils/i18n';
import { IconButton } from '../IconButton/IconButton'; import { IconButton } from '../IconButton/IconButton';
@@ -46,8 +46,7 @@ export function Modal(props: PropsWithChildren<Props>) {
onClickBackdrop, onClickBackdrop,
trapFocus = true, trapFocus = true,
} = props; } = props;
const theme = useTheme2(); const styles = useStyles2(getModalStyles);
const styles = getModalStyles(theme);
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
@@ -101,8 +100,7 @@ export function Modal(props: PropsWithChildren<Props>) {
} }
function ModalButtonRow({ leftItems, children }: { leftItems?: React.ReactNode; children: React.ReactNode }) { function ModalButtonRow({ leftItems, children }: { leftItems?: React.ReactNode; children: React.ReactNode }) {
const theme = useTheme2(); const styles = useStyles2(getModalStyles);
const styles = getModalStyles(theme);
if (leftItems) { if (leftItems) {
return ( return (

View File

@@ -2,9 +2,7 @@ import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory } from '../../themes'; export const getModalStyles = (theme: GrafanaTheme2) => {
export const getModalStyles = stylesFactory((theme: GrafanaTheme2) => {
const borderRadius = theme.shape.radius.default; const borderRadius = theme.shape.radius.default;
return { return {
@@ -80,4 +78,4 @@ export const getModalStyles = stylesFactory((theme: GrafanaTheme2) => {
paddingTop: theme.spacing(3), paddingTop: theme.spacing(3),
}), }),
}; };
}); };

View File

@@ -3,8 +3,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory } from '../../themes'; import { useStyles2 } from '../../themes/ThemeContext';
import { useTheme2 } from '../../themes/ThemeContext';
import { inputPadding } from '../Forms/commonStyles'; import { inputPadding } from '../Forms/commonStyles';
import { getInputStyles } from '../Input/Input'; import { getInputStyles } from '../Input/Input';
@@ -17,7 +16,20 @@ interface InputControlProps {
innerProps: JSX.IntrinsicElements['div']; innerProps: JSX.IntrinsicElements['div'];
} }
const getInputControlStyles = stylesFactory((theme: GrafanaTheme2, invalid: boolean, withPrefix: boolean) => { export const InputControl = React.forwardRef<HTMLDivElement, React.PropsWithChildren<InputControlProps>>(
function InputControl({ focused, invalid, disabled, children, innerProps, prefix, ...otherProps }, ref) {
const styles = useStyles2(getInputControlStyles, invalid, !!prefix);
return (
<div className={styles.input} {...innerProps} ref={ref}>
{prefix && <div className={cx(styles.prefix)}>{prefix}</div>}
{children}
</div>
);
}
);
const getInputControlStyles = (theme: GrafanaTheme2, invalid: boolean, withPrefix: boolean) => {
const styles = getInputStyles({ theme, invalid }); const styles = getInputStyles({ theme, invalid });
return { return {
@@ -47,17 +59,4 @@ const getInputControlStyles = stylesFactory((theme: GrafanaTheme2, invalid: bool
}) })
), ),
}; };
}); };
export const InputControl = React.forwardRef<HTMLDivElement, React.PropsWithChildren<InputControlProps>>(
function InputControl({ focused, invalid, disabled, children, innerProps, prefix, ...otherProps }, ref) {
const theme = useTheme2();
const styles = getInputControlStyles(theme, invalid, !!prefix);
return (
<div className={styles.input} {...innerProps} ref={ref}>
{prefix && <div className={cx(styles.prefix)}>{prefix}</div>}
{children}
</div>
);
}
);

View File

@@ -4,8 +4,7 @@ import { components, ContainerProps as BaseContainerProps, GroupBase } from 'rea
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory } from '../../themes'; import { useStyles2 } from '../../themes/ThemeContext';
import { useTheme2 } from '../../themes/ThemeContext';
import { getFocusStyles } from '../../themes/mixins'; import { getFocusStyles } from '../../themes/mixins';
import { sharedInputStyle } from '../Forms/commonStyles'; import { sharedInputStyle } from '../Forms/commonStyles';
import { getInputStyles } from '../Input/Input'; import { getInputStyles } from '../Input/Input';
@@ -26,8 +25,7 @@ export const SelectContainer = <Option, isMulti extends boolean, Group extends G
selectProps: { invalid = false }, selectProps: { invalid = false },
} = props; } = props;
const theme = useTheme2(); const styles = useStyles2(getSelectContainerStyles, isFocused, isDisabled, invalid);
const styles = getSelectContainerStyles(theme, isFocused, isDisabled, invalid);
return ( return (
<components.SelectContainer {...props} className={cx(styles.wrapper, props.className)}> <components.SelectContainer {...props} className={cx(styles.wrapper, props.className)}>
@@ -36,33 +34,31 @@ export const SelectContainer = <Option, isMulti extends boolean, Group extends G
); );
}; };
const getSelectContainerStyles = stylesFactory( const getSelectContainerStyles = (theme: GrafanaTheme2, focused: boolean, disabled: boolean, invalid: boolean) => {
(theme: GrafanaTheme2, focused: boolean, disabled: boolean, invalid: boolean) => { const styles = getInputStyles({ theme, invalid });
const styles = getInputStyles({ theme, invalid });
return { return {
wrapper: cx( wrapper: cx(
styles.wrapper, styles.wrapper,
sharedInputStyle(theme, invalid), sharedInputStyle(theme, invalid),
focused && css(getFocusStyles(theme)), focused && css(getFocusStyles(theme)),
disabled && styles.inputDisabled, disabled && styles.inputDisabled,
css({ css({
position: 'relative', position: 'relative',
boxSizing: 'border-box', boxSizing: 'border-box',
/* The display property is set by the styles prop in SelectBase because it's dependant on the width prop */ /* The display property is set by the styles prop in SelectBase because it's dependant on the width prop */
flexDirection: 'row', flexDirection: 'row',
flexWrap: 'wrap', flexWrap: 'wrap',
alignItems: 'stretch', alignItems: 'stretch',
justifyContent: 'space-between', justifyContent: 'space-between',
minHeight: '32px', minHeight: '32px',
height: 'auto', height: 'auto',
maxWidth: '100%', maxWidth: '100%',
/* Input padding is applied to the InputControl so the menu is aligned correctly */ /* Input padding is applied to the InputControl so the menu is aligned correctly */
padding: 0, padding: 0,
cursor: disabled ? 'not-allowed' : 'pointer', cursor: disabled ? 'not-allowed' : 'pointer',
}) })
), ),
}; };
} };
);

View File

@@ -3,7 +3,7 @@ import { Global } from '@emotion/react';
import Slider, { SliderProps } from 'rc-slider'; import Slider, { SliderProps } from 'rc-slider';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { useTheme2 } from '../../themes/ThemeContext'; import { useStyles2 } from '../../themes/ThemeContext';
import HandleTooltip from './HandleTooltip'; import HandleTooltip from './HandleTooltip';
import { getStyles } from './styles'; import { getStyles } from './styles';
@@ -43,8 +43,7 @@ export const RangeSlider = ({
); );
const isHorizontal = orientation === 'horizontal'; const isHorizontal = orientation === 'horizontal';
const theme = useTheme2(); const styles = useStyles2(getStyles, isHorizontal);
const styles = getStyles(theme, isHorizontal);
const tipHandleRender: SliderProps['handleRender'] = (node, handleProps) => { const tipHandleRender: SliderProps['handleRender'] = (node, handleProps) => {
return ( return (

View File

@@ -3,7 +3,7 @@ import { Global } from '@emotion/react';
import SliderComponent from 'rc-slider'; import SliderComponent from 'rc-slider';
import React, { useState, useCallback, ChangeEvent, FocusEvent } from 'react'; import React, { useState, useCallback, ChangeEvent, FocusEvent } from 'react';
import { useTheme2 } from '../../themes/ThemeContext'; import { useStyles2 } from '../../themes/ThemeContext';
import { Input } from '../Input/Input'; import { Input } from '../Input/Input';
import { getStyles } from './styles'; import { getStyles } from './styles';
@@ -26,8 +26,7 @@ export const Slider = ({
included, included,
}: SliderProps) => { }: SliderProps) => {
const isHorizontal = orientation === 'horizontal'; const isHorizontal = orientation === 'horizontal';
const theme = useTheme2(); const styles = useStyles2(getStyles, isHorizontal, Boolean(marks));
const styles = getStyles(theme, isHorizontal, Boolean(marks));
const SliderWithTooltip = SliderComponent; const SliderWithTooltip = SliderComponent;
const [sliderValue, setSliderValue] = useState<number>(value ?? min); const [sliderValue, setSliderValue] = useState<number>(value ?? min);

View File

@@ -3,9 +3,7 @@ import { css as cssCore } from '@emotion/react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory } from '../../themes'; export const getStyles = (theme: GrafanaTheme2, isHorizontal: boolean, hasMarks = false) => {
export const getStyles = stylesFactory((theme: GrafanaTheme2, isHorizontal: boolean, hasMarks = false) => {
const { spacing } = theme; const { spacing } = theme;
const railColor = theme.colors.border.strong; const railColor = theme.colors.border.strong;
const trackColor = theme.colors.primary.main; const trackColor = theme.colors.primary.main;
@@ -127,4 +125,4 @@ export const getStyles = stylesFactory((theme: GrafanaTheme2, isHorizontal: bool
order: 1, order: 1,
}), }),
}; };
}); };

View File

@@ -4,7 +4,7 @@ import React, { HTMLProps, useRef } from 'react';
import { GrafanaTheme2, deprecationWarning } from '@grafana/data'; import { GrafanaTheme2, deprecationWarning } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../themes'; import { useStyles2 } from '../../themes';
import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins'; import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins';
export interface Props extends Omit<HTMLProps<HTMLInputElement>, 'value'> { export interface Props extends Omit<HTMLProps<HTMLInputElement>, 'value'> {
@@ -21,8 +21,7 @@ export const Switch = React.forwardRef<HTMLInputElement, Props>(
deprecationWarning('Switch', 'checked prop', 'value'); deprecationWarning('Switch', 'checked prop', 'value');
} }
const theme = useTheme2(); const styles = useStyles2(getSwitchStyles);
const styles = getSwitchStyles(theme);
const switchIdRef = useRef(id ? id : uniqueId('switch-')); const switchIdRef = useRef(id ? id : uniqueId('switch-'));
return ( return (
@@ -52,8 +51,7 @@ export interface InlineSwitchProps extends Props {
export const InlineSwitch = React.forwardRef<HTMLInputElement, InlineSwitchProps>( export const InlineSwitch = React.forwardRef<HTMLInputElement, InlineSwitchProps>(
({ transparent, className, showLabel, label, value, id, invalid, ...props }, ref) => { ({ transparent, className, showLabel, label, value, id, invalid, ...props }, ref) => {
const theme = useTheme2(); const styles = useStyles2(getSwitchStyles, transparent);
const styles = getSwitchStyles(theme, transparent);
return ( return (
<div <div
@@ -75,110 +73,108 @@ export const InlineSwitch = React.forwardRef<HTMLInputElement, InlineSwitchProps
InlineSwitch.displayName = 'Switch'; InlineSwitch.displayName = 'Switch';
const getSwitchStyles = stylesFactory((theme: GrafanaTheme2, transparent?: boolean) => { const getSwitchStyles = (theme: GrafanaTheme2, transparent?: boolean) => ({
return { switch: css({
switch: css({ width: '32px',
width: '32px', height: '16px',
height: '16px', position: 'relative',
position: 'relative',
input: { input: {
opacity: 0, opacity: 0,
left: '-100vw', left: '-100vw',
zIndex: -1000, zIndex: -1000,
position: 'absolute', position: 'absolute',
'&:disabled + label': { '&:disabled + label': {
background: theme.colors.action.disabledBackground, background: theme.colors.action.disabledBackground,
cursor: 'not-allowed', cursor: 'not-allowed',
},
'&:checked + label': {
background: theme.colors.primary.main,
borderColor: theme.colors.primary.main,
'&:hover': {
background: theme.colors.primary.shade,
},
'&::after': {
transform: 'translate3d(18px, -50%, 0)',
background: theme.colors.primary.contrastText,
},
},
'&:focus + label, &:focus-visible + label': getFocusStyles(theme),
'&:focus:not(:focus-visible) + label': getMouseFocusStyles(theme),
}, },
label: { '&:checked + label': {
width: '100%', background: theme.colors.primary.main,
height: '100%', borderColor: theme.colors.primary.main,
cursor: 'pointer',
borderRadius: theme.shape.radius.pill,
background: theme.components.input.background,
border: `1px solid ${theme.components.input.borderColor}`,
transition: 'all 0.3s ease',
'&:hover': { '&:hover': {
borderColor: theme.components.input.borderHover, background: theme.colors.primary.shade,
}, },
'&::after': { '&::after': {
position: 'absolute', transform: 'translate3d(18px, -50%, 0)',
display: 'block', background: theme.colors.primary.contrastText,
content: '""',
width: '12px',
height: '12px',
borderRadius: theme.shape.radius.circle,
background: theme.colors.text.secondary,
boxShadow: theme.shadows.z1,
top: '50%',
transform: 'translate3d(2px, -50%, 0)',
transition: 'transform 0.2s cubic-bezier(0.19, 1, 0.22, 1)',
'@media (forced-colors: active)': {
border: '1px solid transparent',
},
}, },
}, },
}),
inlineContainer: css({ '&:focus + label, &:focus-visible + label': getFocusStyles(theme),
padding: theme.spacing(0, 1),
height: theme.spacing(theme.components.height.md), '&:focus:not(:focus-visible) + label': getMouseFocusStyles(theme),
display: 'inline-flex', },
alignItems: 'center',
background: transparent ? 'transparent' : theme.components.input.background, label: {
border: `1px solid ${transparent ? 'transparent' : theme.components.input.borderColor}`, width: '100%',
borderRadius: theme.shape.radius.default, height: '100%',
cursor: 'pointer',
borderRadius: theme.shape.radius.pill,
background: theme.components.input.background,
border: `1px solid ${theme.components.input.borderColor}`,
transition: 'all 0.3s ease',
'&:hover': { '&:hover': {
border: `1px solid ${transparent ? 'transparent' : theme.components.input.borderHover}`, borderColor: theme.components.input.borderHover,
},
'.inline-switch-label': { '&::after': {
color: theme.colors.text.primary, position: 'absolute',
display: 'block',
content: '""',
width: '12px',
height: '12px',
borderRadius: theme.shape.radius.circle,
background: theme.colors.text.secondary,
boxShadow: theme.shadows.z1,
top: '50%',
transform: 'translate3d(2px, -50%, 0)',
transition: 'transform 0.2s cubic-bezier(0.19, 1, 0.22, 1)',
'@media (forced-colors: active)': {
border: '1px solid transparent',
}, },
}, },
}), },
disabled: css({ }),
backgroundColor: 'rgba(204, 204, 220, 0.04)', inlineContainer: css({
color: 'rgba(204, 204, 220, 0.6)', padding: theme.spacing(0, 1),
border: '1px solid rgba(204, 204, 220, 0.04)', height: theme.spacing(theme.components.height.md),
}), display: 'inline-flex',
inlineLabel: css({ alignItems: 'center',
cursor: 'pointer', background: transparent ? 'transparent' : theme.components.input.background,
paddingRight: theme.spacing(1), border: `1px solid ${transparent ? 'transparent' : theme.components.input.borderColor}`,
color: theme.colors.text.secondary, borderRadius: theme.shape.radius.default,
whiteSpace: 'nowrap',
}), '&:hover': {
inlineLabelEnabled: css({ border: `1px solid ${transparent ? 'transparent' : theme.components.input.borderHover}`,
color: theme.colors.text.primary,
}), '.inline-switch-label': {
invalid: css({ color: theme.colors.text.primary,
'input + label, input:checked + label, input:hover + label': {
border: `1px solid ${theme.colors.error.border}`,
}, },
}), },
}; }),
disabled: css({
backgroundColor: 'rgba(204, 204, 220, 0.04)',
color: 'rgba(204, 204, 220, 0.6)',
border: '1px solid rgba(204, 204, 220, 0.04)',
}),
inlineLabel: css({
cursor: 'pointer',
paddingRight: theme.spacing(1),
color: theme.colors.text.secondary,
whiteSpace: 'nowrap',
}),
inlineLabelEnabled: css({
color: theme.colors.text.primary,
}),
invalid: css({
'input + label, input:checked + label, input:hover + label': {
border: `1px solid ${theme.colors.error.border}`,
},
}),
}); });

View File

@@ -5,7 +5,7 @@ import { SelectableValue, GrafanaTheme2 } from '@grafana/data';
import { IconButton } from '../../components/IconButton/IconButton'; import { IconButton } from '../../components/IconButton/IconButton';
import { TabsBar, Tab, TabContent } from '../../components/Tabs'; import { TabsBar, Tab, TabContent } from '../../components/Tabs';
import { stylesFactory, useTheme2 } from '../../themes'; import { useStyles2 } from '../../themes';
import { IconName } from '../../types/icon'; import { IconName } from '../../types/icon';
import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar'; import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar';
@@ -23,45 +23,14 @@ export interface TabbedContainerProps {
onClose: () => void; onClose: () => void;
} }
const getStyles = stylesFactory((theme: GrafanaTheme2) => { export function TabbedContainer({ tabs, defaultTab, closeIconTooltip, onClose }: TabbedContainerProps) {
return { const [activeTab, setActiveTab] = useState(tabs.some((tab) => tab.value === defaultTab) ? defaultTab : tabs[0].value);
container: css({
height: '100%',
}),
tabContent: css({
padding: theme.spacing(2),
backgroundColor: theme.colors.background.primary,
height: `calc(100% - ${theme.components.menuTabs.height}px)`,
}),
close: css({
position: 'absolute',
right: '16px',
top: '5px',
cursor: 'pointer',
fontSize: theme.typography.size.lg,
}),
tabs: css({
paddingTop: theme.spacing(1),
borderColor: theme.colors.border.weak,
ul: {
marginLeft: theme.spacing(2),
},
}),
};
});
export function TabbedContainer(props: TabbedContainerProps) {
const [activeTab, setActiveTab] = useState(
props.tabs.some((tab) => tab.value === props.defaultTab) ? props.defaultTab : props.tabs?.[0].value
);
const onSelectTab = (item: SelectableValue<string>) => { const onSelectTab = (item: SelectableValue<string>) => {
setActiveTab(item.value!); setActiveTab(item.value!);
}; };
const { tabs, onClose, closeIconTooltip } = props; const styles = useStyles2(getStyles);
const theme = useTheme2();
const styles = getStyles(theme);
return ( return (
<div className={styles.container}> <div className={styles.container}>
@@ -83,3 +52,28 @@ export function TabbedContainer(props: TabbedContainerProps) {
</div> </div>
); );
} }
const getStyles = (theme: GrafanaTheme2) => ({
container: css({
height: '100%',
}),
tabContent: css({
padding: theme.spacing(2),
backgroundColor: theme.colors.background.primary,
height: `calc(100% - ${theme.components.menuTabs.height}px)`,
}),
close: css({
position: 'absolute',
right: '16px',
top: '5px',
cursor: 'pointer',
fontSize: theme.typography.size.lg,
}),
tabs: css({
paddingTop: theme.spacing(1),
borderColor: theme.colors.border.weak,
ul: {
marginLeft: theme.spacing(2),
},
}),
});

View File

@@ -5,7 +5,7 @@ import { FixedSizeList as List } from 'react-window';
import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import { Checkbox, FilterInput, Label, VerticalGroup } from '..'; import { Checkbox, FilterInput, Label, VerticalGroup } from '..';
import { stylesFactory, useTheme2 } from '../../themes'; import { useStyles2, useTheme2 } from '../../themes';
interface Props { interface Props {
values: SelectableValue[]; values: SelectableValue[];
@@ -18,8 +18,6 @@ const ITEM_HEIGHT = 28;
const MIN_HEIGHT = ITEM_HEIGHT * 5; const MIN_HEIGHT = ITEM_HEIGHT * 5;
export const FilterList = ({ options, values, caseSensitive, onChange }: Props) => { export const FilterList = ({ options, values, caseSensitive, onChange }: Props) => {
const theme = useTheme2();
const styles = getStyles(theme);
const [searchFilter, setSearchFilter] = useState(''); const [searchFilter, setSearchFilter] = useState('');
const regex = useMemo(() => new RegExp(searchFilter, caseSensitive ? undefined : 'i'), [searchFilter, caseSensitive]); const regex = useMemo(() => new RegExp(searchFilter, caseSensitive ? undefined : 'i'), [searchFilter, caseSensitive]);
const items = useMemo( const items = useMemo(
@@ -32,16 +30,12 @@ export const FilterList = ({ options, values, caseSensitive, onChange }: Props)
}), }),
[options, regex] [options, regex]
); );
const styles = useStyles2(getStyles);
const theme = useTheme2();
const gutter = theme.spacing.gridSize; const gutter = theme.spacing.gridSize;
const height = useMemo(() => Math.min(items.length * ITEM_HEIGHT, MIN_HEIGHT) + gutter, [gutter, items.length]); const height = useMemo(() => Math.min(items.length * ITEM_HEIGHT, MIN_HEIGHT) + gutter, [gutter, items.length]);
const onInputChange = useCallback(
(v: string) => {
setSearchFilter(v);
},
[setSearchFilter]
);
const onCheckedChanged = useCallback( const onCheckedChanged = useCallback(
(option: SelectableValue) => (event: React.FormEvent<HTMLInputElement>) => { (option: SelectableValue) => (event: React.FormEvent<HTMLInputElement>) => {
const newValues = event.currentTarget.checked const newValues = event.currentTarget.checked
@@ -55,7 +49,7 @@ export const FilterList = ({ options, values, caseSensitive, onChange }: Props)
return ( return (
<VerticalGroup spacing="md"> <VerticalGroup spacing="md">
<FilterInput placeholder="Filter values" onChange={onInputChange} value={searchFilter} /> <FilterInput placeholder="Filter values" onChange={setSearchFilter} value={searchFilter} />
{!items.length && <Label>No values</Label>} {!items.length && <Label>No values</Label>}
{items.length && ( {items.length && (
<List <List
@@ -82,7 +76,7 @@ export const FilterList = ({ options, values, caseSensitive, onChange }: Props)
); );
}; };
const getStyles = stylesFactory((theme: GrafanaTheme2) => ({ const getStyles = (theme: GrafanaTheme2) => ({
filterList: css({ filterList: css({
label: 'filterList', label: 'filterList',
}), }),
@@ -98,4 +92,4 @@ const getStyles = stylesFactory((theme: GrafanaTheme2) => ({
backgroundColor: theme.colors.action.hover, backgroundColor: theme.colors.action.hover,
}, },
}), }),
})); });

View File

@@ -3,22 +3,7 @@ import React from 'react';
import { GrafanaTheme2, locale } from '@grafana/data'; import { GrafanaTheme2, locale } from '@grafana/data';
import { stylesFactory, useStyles2 } from '../../themes'; import { useStyles2 } from '../../themes';
const getStyles = stylesFactory((theme: GrafanaTheme2) => {
return {
counter: css({
label: 'counter',
marginLeft: theme.spacing(1),
borderRadius: theme.spacing(3),
backgroundColor: theme.colors.action.hover,
padding: theme.spacing(0.25, 1),
color: theme.colors.text.secondary,
fontWeight: theme.typography.fontWeightMedium,
fontSize: theme.typography.size.sm,
}),
};
});
export interface CounterProps { export interface CounterProps {
value: number; value: number;
@@ -29,3 +14,16 @@ export const Counter = ({ value }: CounterProps) => {
return <span className={styles.counter}>{locale(value, 0).text}</span>; return <span className={styles.counter}>{locale(value, 0).text}</span>;
}; };
const getStyles = (theme: GrafanaTheme2) => ({
counter: css({
label: 'counter',
marginLeft: theme.spacing(1),
borderRadius: theme.spacing(3),
backgroundColor: theme.colors.action.hover,
padding: theme.spacing(0.25, 1),
color: theme.colors.text.secondary,
fontWeight: theme.typography.fontWeightMedium,
fontSize: theme.typography.size.sm,
}),
});

View File

@@ -3,23 +3,14 @@ import React, { HTMLAttributes, ReactNode } from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../themes'; import { useStyles2 } from '../../themes';
interface Props extends HTMLAttributes<HTMLDivElement> { interface Props extends HTMLAttributes<HTMLDivElement> {
children: ReactNode; children: ReactNode;
} }
const getTabContentStyle = stylesFactory((theme: GrafanaTheme2) => {
return {
tabContent: css({
background: theme.colors.background.primary,
}),
};
});
export const TabContent = ({ children, className, ...restProps }: Props) => { export const TabContent = ({ children, className, ...restProps }: Props) => {
const theme = useTheme2(); const styles = useStyles2(getTabContentStyle);
const styles = getTabContentStyle(theme);
return ( return (
<div {...restProps} className={cx(styles.tabContent, className)}> <div {...restProps} className={cx(styles.tabContent, className)}>
@@ -27,3 +18,9 @@ export const TabContent = ({ children, className, ...restProps }: Props) => {
</div> </div>
); );
}; };
const getTabContentStyle = (theme: GrafanaTheme2) => ({
tabContent: css({
background: theme.colors.background.primary,
}),
});

View File

@@ -3,7 +3,7 @@ import React, { HTMLProps } from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../themes'; import { useStyles2 } from '../../themes';
import { getFocusStyle, sharedInputStyle } from '../Forms/commonStyles'; import { getFocusStyle, sharedInputStyle } from '../Forms/commonStyles';
export interface Props extends Omit<HTMLProps<HTMLTextAreaElement>, 'size'> { export interface Props extends Omit<HTMLProps<HTMLTextAreaElement>, 'size'> {
@@ -12,26 +12,23 @@ export interface Props extends Omit<HTMLProps<HTMLTextAreaElement>, 'size'> {
} }
export const TextArea = React.forwardRef<HTMLTextAreaElement, Props>(({ invalid, className, ...props }, ref) => { export const TextArea = React.forwardRef<HTMLTextAreaElement, Props>(({ invalid, className, ...props }, ref) => {
const theme = useTheme2(); const styles = useStyles2(getTextAreaStyle, invalid);
const styles = getTextAreaStyle(theme, invalid);
return <textarea {...props} className={cx(styles.textarea, className)} ref={ref} />; return <textarea {...props} className={cx(styles.textarea, className)} ref={ref} />;
}); });
const getTextAreaStyle = stylesFactory((theme: GrafanaTheme2, invalid = false) => { const getTextAreaStyle = (theme: GrafanaTheme2, invalid = false) => ({
return { textarea: cx(
textarea: cx( sharedInputStyle(theme),
sharedInputStyle(theme), getFocusStyle(theme),
getFocusStyle(theme), css({
css({ display: 'block',
display: 'block', borderRadius: theme.shape.radius.default,
borderRadius: theme.shape.radius.default, padding: `${theme.spacing.gridSize / 4}px ${theme.spacing.gridSize}px`,
padding: `${theme.spacing.gridSize / 4}px ${theme.spacing.gridSize}px`, width: '100%',
width: '100%', borderColor: invalid ? theme.colors.error.border : theme.components.input.borderColor,
borderColor: invalid ? theme.colors.error.border : theme.components.input.borderColor, })
}) ),
),
};
}); });
TextArea.displayName = 'TextArea'; TextArea.displayName = 'TextArea';

View File

@@ -2,30 +2,9 @@ import { css } from '@emotion/css';
import React from 'react'; import React from 'react';
import { CSSTransition } from 'react-transition-group'; import { CSSTransition } from 'react-transition-group';
import { stylesFactory } from '../../themes'; import { GrafanaTheme2 } from '@grafana/data';
const getStyles = stylesFactory((duration: number) => { import { useStyles2 } from '../../themes';
return {
enter: css({
label: 'enter',
opacity: 0,
}),
enterActive: css({
label: 'enterActive',
opacity: 1,
transition: `opacity ${duration}ms ease-out`,
}),
exit: css({
label: 'exit',
opacity: 1,
}),
exitActive: css({
label: 'exitActive',
opacity: 0,
transition: `opacity ${duration}ms ease-out`,
}),
};
});
type Props = { type Props = {
children: React.ReactNode; children: React.ReactNode;
@@ -35,10 +14,32 @@ type Props = {
export function FadeTransition(props: Props) { export function FadeTransition(props: Props) {
const { visible, children, duration = 250 } = props; const { visible, children, duration = 250 } = props;
const styles = getStyles(duration); const styles = useStyles2(getStyles, duration);
return ( return (
<CSSTransition in={visible} mountOnEnter={true} unmountOnExit={true} timeout={duration} classNames={styles}> <CSSTransition in={visible} mountOnEnter={true} unmountOnExit={true} timeout={duration} classNames={styles}>
{children} {children}
</CSSTransition> </CSSTransition>
); );
} }
const getStyles = (_theme: GrafanaTheme2, duration: number) => ({
enter: css({
label: 'enter',
opacity: 0,
}),
enterActive: css({
label: 'enterActive',
opacity: 1,
transition: `opacity ${duration}ms ease-out`,
}),
exit: css({
label: 'exit',
opacity: 1,
}),
exitActive: css({
label: 'exitActive',
opacity: 0,
transition: `opacity ${duration}ms ease-out`,
}),
});

View File

@@ -2,34 +2,9 @@ import { css } from '@emotion/css';
import React from 'react'; import React from 'react';
import { CSSTransition } from 'react-transition-group'; import { CSSTransition } from 'react-transition-group';
import { stylesFactory } from '../../themes'; import { GrafanaTheme2 } from '@grafana/data';
const getStyles = stylesFactory((duration: number, measurement: 'width' | 'height', size: number) => { import { useStyles2 } from '../../themes';
return {
enter: css({
label: 'enter',
[`${measurement}`]: 0,
opacity: 0,
}),
enterActive: css({
label: 'enterActive',
[`${measurement}`]: `${size}px`,
opacity: 1,
transition: `opacity ${duration}ms ease-out, ${measurement} ${duration}ms ease-out`,
}),
exit: css({
label: 'exit',
[`${measurement}`]: `${size}px`,
opacity: 1,
}),
exitActive: css({
label: 'exitActive',
opacity: 0,
[`${measurement}`]: 0,
transition: `opacity ${duration}ms ease-out, ${measurement} ${duration}ms ease-out`,
}),
};
});
type Props = { type Props = {
children: React.ReactNode; children: React.ReactNode;
@@ -42,10 +17,36 @@ type Props = {
export function SlideOutTransition(props: Props) { export function SlideOutTransition(props: Props) {
const { visible, children, duration = 250, horizontal, size } = props; const { visible, children, duration = 250, horizontal, size } = props;
const styles = getStyles(duration, horizontal ? 'width' : 'height', size); const styles = useStyles2(getStyles, duration, horizontal ? 'width' : 'height', size);
return ( return (
<CSSTransition in={visible} mountOnEnter={true} unmountOnExit={true} timeout={duration} classNames={styles}> <CSSTransition in={visible} mountOnEnter={true} unmountOnExit={true} timeout={duration} classNames={styles}>
{children} {children}
</CSSTransition> </CSSTransition>
); );
} }
const getStyles = (_theme: GrafanaTheme2, duration: number, measurement: 'width' | 'height', size: number) => ({
enter: css({
label: 'enter',
[`${measurement}`]: 0,
opacity: 0,
}),
enterActive: css({
label: 'enterActive',
[`${measurement}`]: `${size}px`,
opacity: 1,
transition: `opacity ${duration}ms ease-out, ${measurement} ${duration}ms ease-out`,
}),
exit: css({
label: 'exit',
[`${measurement}`]: `${size}px`,
opacity: 1,
}),
exitActive: css({
label: 'exitActive',
opacity: 0,
[`${measurement}`]: 0,
transition: `opacity ${duration}ms ease-out, ${measurement} ${duration}ms ease-out`,
}),
});

View File

@@ -3,13 +3,13 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import React from 'react'; import React from 'react';
import { GraphSeriesValue } from '@grafana/data'; import { GrafanaTheme2, GraphSeriesValue } from '@grafana/data';
import { LegendDisplayMode, LegendPlacement } from '@grafana/schema'; import { LegendDisplayMode, LegendPlacement } from '@grafana/schema';
import { CustomScrollbar } from '../../components/CustomScrollbar/CustomScrollbar'; import { CustomScrollbar } from '../../components/CustomScrollbar/CustomScrollbar';
import { VizLegend } from '../../components/VizLegend/VizLegend'; import { VizLegend } from '../../components/VizLegend/VizLegend';
import { VizLegendItem } from '../../components/VizLegend/types'; import { VizLegendItem } from '../../components/VizLegend/types';
import { stylesFactory } from '../../themes'; import { useStyles2 } from '../../themes';
import { Graph, GraphProps } from './Graph'; import { Graph, GraphProps } from './Graph';
@@ -25,21 +25,6 @@ export interface GraphWithLegendProps extends GraphProps {
onToggleSort: (sortBy: string) => void; onToggleSort: (sortBy: string) => void;
} }
const getGraphWithLegendStyles = stylesFactory(({ placement }: GraphWithLegendProps) => ({
wrapper: css({
display: 'flex',
flexDirection: placement === 'bottom' ? 'column' : 'row',
}),
graphContainer: css({
minHeight: '65%',
flexGrow: 1,
}),
legendContainer: css({
padding: '10px 0',
maxHeight: placement === 'bottom' ? '35%' : 'none',
}),
}));
const shouldHideLegendItem = (data: GraphSeriesValue[][], hideEmpty = false, hideZero = false) => { const shouldHideLegendItem = (data: GraphSeriesValue[][], hideEmpty = false, hideZero = false) => {
const isZeroOnlySeries = data.reduce((acc, current) => acc + (current[1] || 0), 0) === 0; const isZeroOnlySeries = data.reduce((acc, current) => acc + (current[1] || 0), 0) === 0;
const isNullOnlySeries = !data.reduce((acc, current) => acc && current[1] !== null, true); const isNullOnlySeries = !data.reduce((acc, current) => acc && current[1] !== null, true);
@@ -72,7 +57,7 @@ export const GraphWithLegend = (props: GraphWithLegendProps) => {
children, children,
ariaLabel, ariaLabel,
} = props; } = props;
const { graphContainer, wrapper, legendContainer } = getGraphWithLegendStyles(props); const { graphContainer, wrapper, legendContainer } = useStyles2(getGraphWithLegendStyles, props.placement);
const legendItems = series.reduce<VizLegendItem[]>((acc, s) => { const legendItems = series.reduce<VizLegendItem[]>((acc, s) => {
return shouldHideLegendItem(s.data, hideEmpty, hideZero) return shouldHideLegendItem(s.data, hideEmpty, hideZero)
@@ -130,3 +115,18 @@ export const GraphWithLegend = (props: GraphWithLegendProps) => {
</div> </div>
); );
}; };
const getGraphWithLegendStyles = (_theme: GrafanaTheme2, placement: LegendPlacement) => ({
wrapper: css({
display: 'flex',
flexDirection: placement === 'bottom' ? 'column' : 'row',
}),
graphContainer: css({
minHeight: '65%',
flexGrow: 1,
}),
legendContainer: css({
padding: '10px 0',
maxHeight: placement === 'bottom' ? '35%' : 'none',
}),
});

View File

@@ -3,7 +3,7 @@ import React, { ErrorInfo, useEffect } from 'react';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { locationUtil, PageLayoutType } from '@grafana/data'; import { locationUtil, PageLayoutType } from '@grafana/data';
import { Button, ErrorWithStack, stylesFactory } from '@grafana/ui'; import { Button, ErrorWithStack, useStyles2 } from '@grafana/ui';
import { Page } from '../components/Page/Page'; import { Page } from '../components/Page/Page';
@@ -15,6 +15,7 @@ interface Props {
export function GrafanaRouteError({ error, errorInfo }: Props) { export function GrafanaRouteError({ error, errorInfo }: Props) {
const location = useLocation(); const location = useLocation();
const isChunkLoadingError = error?.name === 'ChunkLoadError'; const isChunkLoadingError = error?.name === 'ChunkLoadError';
const style = useStyles2(getStyles);
useEffect(() => { useEffect(() => {
// Auto reload page 1 time if we have a chunk load error // Auto reload page 1 time if we have a chunk load error
@@ -27,7 +28,7 @@ export function GrafanaRouteError({ error, errorInfo }: Props) {
return ( return (
<Page navId="error" layout={PageLayoutType.Canvas}> <Page navId="error" layout={PageLayoutType.Canvas}>
<div className={getStyles()}> <div className={style}>
{isChunkLoadingError && ( {isChunkLoadingError && (
<div> <div>
<h2>Unable to find application file</h2> <h2>Unable to find application file</h2>
@@ -50,9 +51,7 @@ export function GrafanaRouteError({ error, errorInfo }: Props) {
); );
} }
const getStyles = stylesFactory(() => { const getStyles = () => css`
return css` width: 500px;
width: 500px; margin: 64px auto;
margin: 64px auto; `;
`;
});

View File

@@ -1,7 +1,7 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import React from 'react'; import React from 'react';
import { Modal, stylesFactory } from '@grafana/ui'; import { Modal, useStyles2 } from '@grafana/ui';
import { OnRowOptionsUpdate, RowOptionsForm } from './RowOptionsForm'; import { OnRowOptionsUpdate, RowOptionsForm } from './RowOptionsForm';
@@ -14,7 +14,8 @@ export interface RowOptionsModalProps {
} }
export const RowOptionsModal = ({ repeat, title, onDismiss, onUpdate, warning }: RowOptionsModalProps) => { export const RowOptionsModal = ({ repeat, title, onDismiss, onUpdate, warning }: RowOptionsModalProps) => {
const styles = getStyles(); const styles = useStyles2(getStyles);
return ( return (
<Modal isOpen={true} title="Row options" icon="copy" onDismiss={onDismiss} className={styles.modal}> <Modal isOpen={true} title="Row options" icon="copy" onDismiss={onDismiss} className={styles.modal}>
<RowOptionsForm repeat={repeat} title={title} onCancel={onDismiss} onUpdate={onUpdate} warning={warning} /> <RowOptionsForm repeat={repeat} title={title} onCancel={onDismiss} onUpdate={onUpdate} warning={warning} />
@@ -22,11 +23,9 @@ export const RowOptionsModal = ({ repeat, title, onDismiss, onUpdate, warning }:
); );
}; };
const getStyles = stylesFactory(() => { const getStyles = () => ({
return { modal: css`
modal: css` label: RowOptionsModal;
label: RowOptionsModal; width: 500px;
width: 500px; `,
`,
};
}); });

View File

@@ -4,7 +4,7 @@ import AutoSizer from 'react-virtualized-auto-sizer';
import { areEqual, FixedSizeGrid as Grid } from 'react-window'; import { areEqual, FixedSizeGrid as Grid } from 'react-window';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { useTheme2, stylesFactory } from '@grafana/ui'; import { useStyles2 } from '@grafana/ui';
import { SanitizedSVG } from 'app/core/components/SVG/SanitizedSVG'; import { SanitizedSVG } from 'app/core/components/SVG/SanitizedSVG';
import { ResourceItem } from './FolderPickerTab'; import { ResourceItem } from './FolderPickerTab';
@@ -26,8 +26,7 @@ const MemoizedCell = memo(function Cell(props: CellProps) {
const { cards, columnCount, onChange, selected } = data; const { cards, columnCount, onChange, selected } = data;
const singleColumnIndex = columnIndex + rowIndex * columnCount; const singleColumnIndex = columnIndex + rowIndex * columnCount;
const card = cards[singleColumnIndex]; const card = cards[singleColumnIndex];
const theme = useTheme2(); const styles = useStyles2(getStyles);
const styles = getStyles(theme);
return ( return (
<div style={style}> <div style={style}>
@@ -56,53 +55,6 @@ const MemoizedCell = memo(function Cell(props: CellProps) {
); );
}, areEqual); }, areEqual);
const getStyles = stylesFactory((theme: GrafanaTheme2) => {
return {
card: css`
display: inline-block;
width: 90px;
height: 90px;
margin: 0.75rem;
margin-left: 15px;
text-align: center;
cursor: pointer;
position: relative;
background-color: transparent;
border: 1px solid transparent;
border-radius: 8px;
padding-top: 6px;
:hover {
border-color: ${theme.colors.action.hover};
box-shadow: ${theme.shadows.z2};
}
`,
selected: css`
border: 2px solid ${theme.colors.primary.main};
:hover {
border-color: ${theme.colors.primary.main};
}
`,
img: css`
width: 40px;
height: 40px;
object-fit: cover;
vertical-align: middle;
fill: ${theme.colors.text.primary};
`,
text: css`
color: ${theme.colors.text.primary};
white-space: nowrap;
font-size: 12px;
text-overflow: ellipsis;
display: block;
overflow: hidden;
`,
grid: css`
border: 1px solid ${theme.colors.border.medium};
`,
};
});
interface CardProps { interface CardProps {
onChange: (value: string) => void; onChange: (value: string) => void;
cards: ResourceItem[]; cards: ResourceItem[];
@@ -111,8 +63,7 @@ interface CardProps {
export const ResourceCards = (props: CardProps) => { export const ResourceCards = (props: CardProps) => {
const { onChange, cards, value } = props; const { onChange, cards, value } = props;
const theme = useTheme2(); const styles = useStyles2(getStyles);
const styles = getStyles(theme);
return ( return (
<AutoSizer defaultWidth={680}> <AutoSizer defaultWidth={680}>
@@ -139,3 +90,48 @@ export const ResourceCards = (props: CardProps) => {
</AutoSizer> </AutoSizer>
); );
}; };
const getStyles = (theme: GrafanaTheme2) => ({
card: css`
display: inline-block;
width: 90px;
height: 90px;
margin: 0.75rem;
margin-left: 15px;
text-align: center;
cursor: pointer;
position: relative;
background-color: transparent;
border: 1px solid transparent;
border-radius: 8px;
padding-top: 6px;
:hover {
border-color: ${theme.colors.action.hover};
box-shadow: ${theme.shadows.z2};
}
`,
selected: css`
border: 2px solid ${theme.colors.primary.main};
:hover {
border-color: ${theme.colors.primary.main};
}
`,
img: css`
width: 40px;
height: 40px;
object-fit: cover;
vertical-align: middle;
fill: ${theme.colors.text.primary};
`,
text: css`
color: ${theme.colors.text.primary};
white-space: nowrap;
font-size: 12px;
text-overflow: ellipsis;
display: block;
overflow: hidden;
`,
grid: css`
border: 1px solid ${theme.colors.border.medium};
`,
});

View File

@@ -5,51 +5,7 @@ import React from 'react';
// Services & Utils // Services & Utils
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '@grafana/ui'; import { useStyles2, useTheme2 } from '@grafana/ui';
// Types
const drawerSlide = (theme: GrafanaTheme2) => keyframes`
0% {
transform: translateY(${theme.components.horizontalDrawer.defaultHeight}px);
}
100% {
transform: translateY(0px);
}
`;
const getStyles = stylesFactory((theme: GrafanaTheme2) => {
return {
container: css`
position: fixed !important;
bottom: 0;
background: ${theme.colors.background.primary};
border-top: 1px solid ${theme.colors.border.weak};
margin: ${theme.spacing(0, -2, 0, -2)};
box-shadow: ${theme.shadows.z3};
z-index: ${theme.zIndex.navbarFixed};
`,
drawerActive: css`
opacity: 1;
animation: 0.5s ease-out ${drawerSlide(theme)};
`,
rzHandle: css`
background: ${theme.colors.secondary.main};
transition: 0.3s background ease-in-out;
position: relative;
width: 200px !important;
height: 7px !important;
left: calc(50% - 100px) !important;
top: -4px !important;
cursor: grab;
border-radius: ${theme.shape.radius.pill};
&:hover {
background: ${theme.colors.secondary.shade};
}
`,
};
});
export interface Props { export interface Props {
width: number; width: number;
@@ -60,7 +16,7 @@ export interface Props {
export function ExploreDrawer(props: Props) { export function ExploreDrawer(props: Props) {
const { width, children, onResize } = props; const { width, children, onResize } = props;
const theme = useTheme2(); const theme = useTheme2();
const styles = getStyles(theme); const styles = useStyles2(getStyles);
const drawerWidth = `${width + 31.5}px`; const drawerWidth = `${width + 31.5}px`;
return ( return (
@@ -87,3 +43,43 @@ export function ExploreDrawer(props: Props) {
</Resizable> </Resizable>
); );
} }
const drawerSlide = (theme: GrafanaTheme2) => keyframes`
0% {
transform: translateY(${theme.components.horizontalDrawer.defaultHeight}px);
}
100% {
transform: translateY(0px);
}
`;
const getStyles = (theme: GrafanaTheme2) => ({
container: css`
position: fixed !important;
bottom: 0;
background: ${theme.colors.background.primary};
border-top: 1px solid ${theme.colors.border.weak};
margin: ${theme.spacing(0, -2, 0, -2)};
box-shadow: ${theme.shadows.z3};
z-index: ${theme.zIndex.navbarFixed};
`,
drawerActive: css`
opacity: 1;
animation: 0.5s ease-out ${drawerSlide(theme)};
`,
rzHandle: css`
background: ${theme.colors.secondary.main};
transition: 0.3s background ease-in-out;
position: relative;
width: 200px !important;
height: 7px !important;
left: calc(50% - 100px) !important;
top: -4px !important;
cursor: grab;
border-radius: ${theme.shape.radius.pill};
&:hover {
background: ${theme.colors.secondary.shade};
}
`,
});

View File

@@ -1,8 +1,8 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import React, { useMemo, useState, useEffect } from 'react'; import React, { useMemo, useState, useEffect } from 'react';
import { StandardEditorProps, SelectableValue, GrafanaTheme2 } from '@grafana/data'; import { StandardEditorProps, SelectableValue } from '@grafana/data';
import { Alert, Select, stylesFactory, useTheme2 } from '@grafana/ui'; import { Alert, Select, useStyles2 } from '@grafana/ui';
import { COUNTRIES_GAZETTEER_PATH, Gazetteer, getGazetteer } from '../gazetteer/gazetteer'; import { COUNTRIES_GAZETTEER_PATH, Gazetteer, getGazetteer } from '../gazetteer/gazetteer';
@@ -34,7 +34,7 @@ export const GazetteerPathEditor = ({
context, context,
item, item,
}: StandardEditorProps<string, GazetteerPathEditorConfigSettings>) => { }: StandardEditorProps<string, GazetteerPathEditorConfigSettings>) => {
const styles = getStyles(useTheme2()); const styles = useStyles2(getStyles);
const [gaz, setGaz] = useState<Gazetteer>(); const [gaz, setGaz] = useState<Gazetteer>();
const settings = item.settings; const settings = item.settings;
@@ -86,17 +86,15 @@ export const GazetteerPathEditor = ({
); );
}; };
const getStyles = stylesFactory((theme: GrafanaTheme2) => { const getStyles = () => ({
return { keys: css`
keys: css` margin-top: 4px;
margin-top: 4px; text-overflow: ellipsis;
text-overflow: ellipsis; overflow: hidden;
overflow: hidden; white-space: nowrap;
white-space: nowrap;
> span { > span {
margin-left: 4px; margin-left: 4px;
} }
`, `,
};
}); });

View File

@@ -9,7 +9,7 @@ import {
QueryResultMetaStat, QueryResultMetaStat,
TimeZone, TimeZone,
} from '@grafana/data'; } from '@grafana/data';
import { stylesFactory, useTheme2 } from '@grafana/ui'; import { useStyles2, useTheme2 } from '@grafana/ui';
interface InspectStatsTableProps { interface InspectStatsTableProps {
timeZone: TimeZone; timeZone: TimeZone;
@@ -19,7 +19,7 @@ interface InspectStatsTableProps {
export const InspectStatsTable = ({ timeZone, name, stats }: InspectStatsTableProps) => { export const InspectStatsTable = ({ timeZone, name, stats }: InspectStatsTableProps) => {
const theme = useTheme2(); const theme = useTheme2();
const styles = getStyles(theme); const styles = useStyles2(getStyles);
if (!stats || !stats.length) { if (!stats || !stats.length) {
return null; return null;
@@ -56,13 +56,11 @@ function formatStat(stat: QueryResultMetaStat, timeZone: TimeZone, theme: Grafan
return formattedValueToString(display(stat.value)); return formattedValueToString(display(stat.value));
} }
const getStyles = stylesFactory((theme: GrafanaTheme2) => { const getStyles = (theme: GrafanaTheme2) => ({
return { wrapper: css`
wrapper: css` padding-bottom: ${theme.spacing(2)};
padding-bottom: ${theme.spacing(2)}; `,
`, cell: css`
cell: css` text-align: right;
text-align: right; `,
`,
};
}); });

View File

@@ -2,16 +2,15 @@ import { css } from '@emotion/css';
import React from 'react'; import React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '@grafana/ui'; import { useStyles2 } from '@grafana/ui';
type Props = { interface Props {
name: string; name: string;
traceIds: string[]; traceIds: string[];
}; }
export const InspectStatsTraceIdsTable = ({ name, traceIds }: Props) => { export const InspectStatsTraceIdsTable = ({ name, traceIds }: Props) => {
const theme = useTheme2(); const styles = useStyles2(getStyles);
const styles = getStyles(theme);
if (traceIds.length === 0) { if (traceIds.length === 0) {
return null; return null;
@@ -35,13 +34,11 @@ export const InspectStatsTraceIdsTable = ({ name, traceIds }: Props) => {
); );
}; };
const getStyles = stylesFactory((theme: GrafanaTheme2) => { const getStyles = (theme: GrafanaTheme2) => ({
return { wrapper: css`
wrapper: css` padding-bottom: ${theme.spacing(2)};
padding-bottom: ${theme.spacing(2)}; `,
`, cell: css`
cell: css` text-align: right;
text-align: right; `,
`,
};
}); });

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { locationService } from '@grafana/runtime'; import { locationService } from '@grafana/runtime';
import { Button, Modal, stylesFactory, useStyles2 } from '@grafana/ui'; import { Button, Modal, useStyles2 } from '@grafana/ui';
import { dashboardWatcher } from './dashboardWatcher'; import { dashboardWatcher } from './dashboardWatcher';
import { DashboardEvent, DashboardEventAction } from './types'; import { DashboardEvent, DashboardEventAction } from './types';
@@ -50,12 +50,10 @@ export function DashboardChangedModal({ onDismiss, event }: Props) {
); );
} }
const getStyles = stylesFactory((theme: GrafanaTheme2) => { const getStyles = (theme: GrafanaTheme2) => ({
return { modal: css({ width: '600px' }),
modal: css({ width: '600px' }), description: css({
description: css({ color: theme.colors.text.secondary,
color: theme.colors.text.secondary, paddingBottom: theme.spacing(1),
paddingBottom: theme.spacing(1), }),
}),
};
}); });

View File

@@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css';
import React from 'react'; import React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { Button, InfoBox, Portal, stylesFactory, useTheme2 } from '@grafana/ui'; import { Button, InfoBox, Portal, useStyles2, useTheme2 } from '@grafana/ui';
import { getModalStyles } from '@grafana/ui/src/components/Modal/getModalStyles'; import { getModalStyles } from '@grafana/ui/src/components/Modal/getModalStyles';
interface Props { interface Props {
@@ -10,9 +10,8 @@ interface Props {
} }
export const TokenRevokedModal = (props: Props) => { export const TokenRevokedModal = (props: Props) => {
const styles = useStyles2(getStyles);
const theme = useTheme2(); const theme = useTheme2();
const styles = getStyles(theme);
const modalStyles = getModalStyles(theme); const modalStyles = getModalStyles(theme);
const showMaxConcurrentSessions = Boolean(props.maxConcurrentSessions); const showMaxConcurrentSessions = Boolean(props.maxConcurrentSessions);
@@ -51,17 +50,15 @@ export const TokenRevokedModal = (props: Props) => {
); );
}; };
const getStyles = stylesFactory((theme: GrafanaTheme2) => { const getStyles = (theme: GrafanaTheme2) => ({
return { infobox: css`
infobox: css` margin-bottom: 0;
margin-bottom: 0; `,
`, text: css`
text: css` margin: ${theme.spacing(1, 0, 2)};
margin: ${theme.spacing(1, 0, 2)}; `,
`, backdrop: css`
backdrop: css` background-color: ${theme.colors.background.canvas};
background-color: ${theme.colors.background.canvas}; opacity: 0.8;
opacity: 0.8; `,
`,
};
}); });

View File

@@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css';
import React from 'react'; import React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '@grafana/ui'; import { useStyles2 } from '@grafana/ui';
export interface SpaceProps { export interface SpaceProps {
v?: number; v?: number;
@@ -11,8 +11,7 @@ export interface SpaceProps {
} }
export const Space = (props: SpaceProps) => { export const Space = (props: SpaceProps) => {
const theme = useTheme2(); const styles = useStyles2(getStyles, props);
const styles = getStyles(theme, props);
return <span className={cx(styles.wrapper)} />; return <span className={cx(styles.wrapper)} />;
}; };
@@ -23,7 +22,7 @@ Space.defaultProps = {
layout: 'block', layout: 'block',
}; };
const getStyles = stylesFactory((theme: GrafanaTheme2, props: SpaceProps) => ({ const getStyles = (theme: GrafanaTheme2, props: SpaceProps) => ({
wrapper: css([ wrapper: css([
{ {
paddingRight: theme.spacing(props.h ?? 0), paddingRight: theme.spacing(props.h ?? 0),
@@ -36,4 +35,4 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, props: SpaceProps) => ({
display: 'block', display: 'block',
}, },
]), ]),
})); });

View File

@@ -3,7 +3,7 @@ import React, { useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { AccessoryButton, InputGroup } from '@grafana/experimental'; import { AccessoryButton, InputGroup } from '@grafana/experimental';
import { Input, stylesFactory, useTheme2 } from '@grafana/ui'; import { Input, useStyles2 } from '@grafana/ui';
import { MultiFilterCondition } from './MultiFilter'; import { MultiFilterCondition } from './MultiFilter';
@@ -17,8 +17,7 @@ export interface Props {
export const MultiFilterItem = ({ filter, onChange, onDelete, keyPlaceholder }: Props) => { export const MultiFilterItem = ({ filter, onChange, onDelete, keyPlaceholder }: Props) => {
const [localKey, setLocalKey] = useState(filter.key || ''); const [localKey, setLocalKey] = useState(filter.key || '');
const [localValue, setLocalValue] = useState(filter.value?.join(', ') || ''); const [localValue, setLocalValue] = useState(filter.value?.join(', ') || '');
const theme = useTheme2(); const styles = useStyles2(getOperatorStyles);
const styles = getOperatorStyles(theme);
return ( return (
<div data-testid="cloudwatch-multifilter-item"> <div data-testid="cloudwatch-multifilter-item">
@@ -59,9 +58,9 @@ export const MultiFilterItem = ({ filter, onChange, onDelete, keyPlaceholder }:
); );
}; };
const getOperatorStyles = stylesFactory((theme: GrafanaTheme2) => ({ const getOperatorStyles = (theme: GrafanaTheme2) => ({
root: css({ root: css({
padding: theme.spacing(0, 1), padding: theme.spacing(0, 1),
alignSelf: 'center', alignSelf: 'center',
}), }),
})); });

View File

@@ -4,7 +4,7 @@ import { useAsyncFn } from 'react-use';
import { GrafanaTheme2, SelectableValue, toOption } from '@grafana/data'; import { GrafanaTheme2, SelectableValue, toOption } from '@grafana/data';
import { AccessoryButton, InputGroup } from '@grafana/experimental'; import { AccessoryButton, InputGroup } from '@grafana/experimental';
import { Select, stylesFactory, useTheme2 } from '@grafana/ui'; import { Select, useStyles2 } from '@grafana/ui';
import { CloudWatchDatasource } from '../../../datasource'; import { CloudWatchDatasource } from '../../../datasource';
import { Dimensions, MetricStat } from '../../../types'; import { Dimensions, MetricStat } from '../../../types';
@@ -76,8 +76,7 @@ export const FilterItem = ({
metricName, metricName,
accountId, accountId,
]); ]);
const theme = useTheme2(); const styles = useStyles2(getOperatorStyles);
const styles = getOperatorStyles(theme);
return ( return (
<div data-testid="cloudwatch-dimensions-filter-item"> <div data-testid="cloudwatch-dimensions-filter-item">
@@ -119,9 +118,9 @@ export const FilterItem = ({
); );
}; };
const getOperatorStyles = stylesFactory((theme: GrafanaTheme2) => ({ const getOperatorStyles = (theme: GrafanaTheme2) => ({
root: css({ root: css({
padding: theme.spacing(0, 1), padding: theme.spacing(0, 1),
alignSelf: 'center', alignSelf: 'center',
}), }),
})); });

View File

@@ -6,50 +6,28 @@ import { DataSourceInstanceSettings, VariableSuggestion } from '@grafana/data';
import { import {
Button, Button,
DataLinkInput, DataLinkInput,
stylesFactory,
InlineField, InlineField,
InlineSwitch, InlineSwitch,
InlineFieldRow, InlineFieldRow,
InlineLabel, InlineLabel,
Input, Input,
useStyles2,
} from '@grafana/ui'; } from '@grafana/ui';
import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker';
import { DataLinkConfig } from '../types'; import { DataLinkConfig } from '../types';
const getStyles = stylesFactory(() => ({ interface Props {
firstRow: css`
display: flex;
`,
nameField: css`
flex: 2;
`,
regexField: css`
flex: 3;
`,
row: css`
display: flex;
align-items: baseline;
`,
urlField: css`
display: flex;
flex: 1;
`,
urlDisplayLabelField: css`
flex: 1;
`,
}));
type Props = {
value: DataLinkConfig; value: DataLinkConfig;
onChange: (value: DataLinkConfig) => void; onChange: (value: DataLinkConfig) => void;
onDelete: () => void; onDelete: () => void;
suggestions: VariableSuggestion[]; suggestions: VariableSuggestion[];
className?: string; className?: string;
}; }
export const DataLink = (props: Props) => { export const DataLink = (props: Props) => {
const { value, onChange, onDelete, suggestions, className } = props; const { value, onChange, onDelete, suggestions, className } = props;
const styles = getStyles(); const styles = useStyles2(getStyles);
const [showInternalLink, setShowInternalLink] = useInternalLink(value.datasourceUid); const [showInternalLink, setShowInternalLink] = useInternalLink(value.datasourceUid);
const handleChange = (field: keyof typeof value) => (event: React.ChangeEvent<HTMLInputElement>) => { const handleChange = (field: keyof typeof value) => (event: React.ChangeEvent<HTMLInputElement>) => {
@@ -173,3 +151,26 @@ function useInternalLink(datasourceUid?: string): [boolean, Dispatch<SetStateAct
return [showInternalLink, setShowInternalLink]; return [showInternalLink, setShowInternalLink];
} }
const getStyles = () => ({
firstRow: css`
display: flex;
`,
nameField: css`
flex: 2;
`,
regexField: css`
flex: 3;
`,
row: css`
display: flex;
align-items: baseline;
`,
urlField: css`
display: flex;
flex: 1;
`,
urlDisplayLabelField: css`
flex: 1;
`,
});

View File

@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
import React, { ComponentProps } from 'react'; import React, { ComponentProps } from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { Field, Icon, PopoverContent, ReactUtils, stylesFactory, Tooltip, useTheme2 } from '@grafana/ui'; import { Field, Icon, PopoverContent, ReactUtils, Tooltip, useStyles2 } from '@grafana/ui';
interface EditorFieldProps extends ComponentProps<typeof Field> { interface EditorFieldProps extends ComponentProps<typeof Field> {
label: string; label: string;
@@ -15,8 +15,7 @@ interface EditorFieldProps extends ComponentProps<typeof Field> {
export const EditorField = (props: EditorFieldProps) => { export const EditorField = (props: EditorFieldProps) => {
const { label, optional, tooltip, children, width, ...fieldProps } = props; const { label, optional, tooltip, children, width, ...fieldProps } = props;
const theme = useTheme2(); const styles = useStyles2(getStyles, width);
const styles = getStyles(theme, width);
// Null check for backward compatibility // Null check for backward compatibility
const childInputId = fieldProps?.htmlFor || ReactUtils?.getChildId(children); const childInputId = fieldProps?.htmlFor || ReactUtils?.getChildId(children);
@@ -45,32 +44,30 @@ export const EditorField = (props: EditorFieldProps) => {
); );
}; };
const getStyles = stylesFactory((theme: GrafanaTheme2, width?: number | string) => { const getStyles = (theme: GrafanaTheme2, width?: number | string) => ({
return { space: css({
space: css({ paddingRight: theme.spacing(0),
paddingRight: theme.spacing(0), paddingBottom: theme.spacing(0.5),
paddingBottom: theme.spacing(0.5), }),
}), root: css({
root: css({ minWidth: theme.spacing(width ?? 0),
minWidth: theme.spacing(width ?? 0), }),
}), label: css({
label: css({ fontSize: 12,
fontSize: 12, fontWeight: theme.typography.fontWeightMedium,
fontWeight: theme.typography.fontWeightMedium, }),
}), optional: css({
optional: css({ fontStyle: 'italic',
fontStyle: 'italic', color: theme.colors.text.secondary,
color: theme.colors.text.secondary, }),
}), field: css({
field: css({ marginBottom: 0, // GrafanaUI/Field has a bottom margin which we must remove
marginBottom: 0, // GrafanaUI/Field has a bottom margin which we must remove }),
}), icon: css({
icon: css({ color: theme.colors.text.secondary,
color: theme.colors.text.secondary, marginLeft: theme.spacing(1),
marginLeft: theme.spacing(1), ':hover': {
':hover': { color: theme.colors.text.primary,
color: theme.colors.text.primary, },
}, }),
}),
};
}); });