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

pull/78476/head
kay delaney 2 years ago committed by GitHub
parent 165de515cd
commit 9e11779921
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      packages/grafana-ui/src/components/Button/FullWidthButtonContainer.tsx
  2. 39
      packages/grafana-ui/src/components/ButtonCascader/ButtonCascader.tsx
  3. 5
      packages/grafana-ui/src/components/Card/Card.tsx
  4. 123
      packages/grafana-ui/src/components/Card/CardContainer.tsx
  5. 15
      packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx
  6. 9
      packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksListItem.tsx
  7. 33
      packages/grafana-ui/src/components/DataSourceSettings/CustomHeadersSettings.tsx
  8. 2
      packages/grafana-ui/src/components/DateTimePickers/DateTimePicker/DateTimePicker.tsx
  9. 10
      packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.tsx
  10. 5
      packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx
  11. 4
      packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerCalendar.tsx
  12. 196
      packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerContent.tsx
  13. 81
      packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerFooter.tsx
  14. 42
      packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimeRangeList.tsx
  15. 95
      packages/grafana-ui/src/components/DateTimePickers/TimeZonePicker/TimeZoneOption.tsx
  16. 40
      packages/grafana-ui/src/components/ErrorBoundary/ErrorWithStack.tsx
  17. 5
      packages/grafana-ui/src/components/Forms/Checkbox.tsx
  18. 61
      packages/grafana-ui/src/components/Forms/Field.tsx
  19. 21
      packages/grafana-ui/src/components/Forms/FieldSet.tsx
  20. 37
      packages/grafana-ui/src/components/Forms/FieldValidationMessage.tsx
  21. 71
      packages/grafana-ui/src/components/Forms/Label.tsx
  22. 9
      packages/grafana-ui/src/components/Forms/RadioButtonGroup/RadioButton.tsx
  23. 10
      packages/grafana-ui/src/components/IconButton/IconButton.tsx
  24. 14
      packages/grafana-ui/src/components/InfoBox/InfoBox.tsx
  25. 8
      packages/grafana-ui/src/components/Modal/Modal.tsx
  26. 6
      packages/grafana-ui/src/components/Modal/getModalStyles.ts
  27. 33
      packages/grafana-ui/src/components/Select/InputControl.tsx
  28. 60
      packages/grafana-ui/src/components/Select/SelectContainer.tsx
  29. 5
      packages/grafana-ui/src/components/Slider/RangeSlider.tsx
  30. 5
      packages/grafana-ui/src/components/Slider/Slider.tsx
  31. 6
      packages/grafana-ui/src/components/Slider/styles.ts
  32. 188
      packages/grafana-ui/src/components/Switch/Switch.tsx
  33. 64
      packages/grafana-ui/src/components/TabbedContainer/TabbedContainer.tsx
  34. 20
      packages/grafana-ui/src/components/Table/FilterList.tsx
  35. 30
      packages/grafana-ui/src/components/Tabs/Counter.tsx
  36. 19
      packages/grafana-ui/src/components/Tabs/TabContent.tsx
  37. 31
      packages/grafana-ui/src/components/TextArea/TextArea.tsx
  38. 49
      packages/grafana-ui/src/components/transitions/FadeTransition.tsx
  39. 57
      packages/grafana-ui/src/components/transitions/SlideOutTransition.tsx
  40. 36
      packages/grafana-ui/src/graveyard/Graph/GraphWithLegend.tsx
  41. 15
      public/app/core/navigation/GrafanaRouteError.tsx
  42. 17
      public/app/features/dashboard/components/RowOptions/RowOptionsModal.tsx
  43. 100
      public/app/features/dimensions/editors/ResourceCards.tsx
  44. 88
      public/app/features/explore/ExploreDrawer.tsx
  45. 28
      public/app/features/geo/editor/GazetteerPathEditor.tsx
  46. 20
      public/app/features/inspector/InspectStatsTable.tsx
  47. 25
      public/app/features/inspector/InspectStatsTraceIdsTable.tsx
  48. 16
      public/app/features/live/dashboard/DashboardChangedModal.tsx
  49. 29
      public/app/features/users/TokenRevokedModal.tsx
  50. 9
      public/app/plugins/datasource/azuremonitor/components/Space.tsx
  51. 9
      public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/MultiFilterItem.tsx
  52. 9
      public/app/plugins/datasource/cloudwatch/components/shared/Dimensions/FilterItem.tsx
  53. 55
      public/app/plugins/datasource/elasticsearch/configuration/DataLink.tsx
  54. 59
      public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/EditorField.tsx

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

@ -4,7 +4,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../themes';
import { useStyles2 } from '../../themes';
import { IconName } from '../../types/icon';
import { Button, ButtonProps } from '../Button';
import { CascaderOption } from '../Cascader/Cascader';
@ -27,27 +27,9 @@ export interface ButtonCascaderProps {
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) => {
const { onChange, className, loadData, icon, buttonProps, hideDownIcon, variant, disabled, ...rest } = props;
const theme = useTheme2();
const styles = getStyles(theme);
const styles = useStyles2(getStyles);
// 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.
@ -72,3 +54,20 @@ export const ButtonCascader = (props: ButtonCascaderProps) => {
};
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',
}),
},
};
};

@ -3,7 +3,7 @@ import React, { memo, cloneElement, FC, useMemo, useContext, ReactNode } from 'r
import { GrafanaTheme2 } from '@grafana/data';
import { useStyles2, useTheme2 } from '../../themes';
import { useStyles2 } from '../../themes';
import { getFocusStyles } from '../../themes/mixins';
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 onCardClick = onClick && !disabled ? onClick : undefined;
const theme = useTheme2();
const styles = getCardContainerStyles(theme, disabled, disableHover, isSelected);
const styles = useStyles2(getCardContainerStyles, disabled, disableHover, isSelected);
return (
<CardContainer

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

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

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

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

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

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

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

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

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

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

@ -3,32 +3,12 @@ import React, { ReactNode } from 'react';
import { TimeOption } from '@grafana/data';
import { stylesFactory } from '../../../themes';
import { useStyles2 } from '../../../themes';
import { t } from '../../../utils/i18n';
import { TimePickerTitle } from './TimePickerTitle';
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 {
title?: string;
options: TimeOption[];
@ -38,7 +18,7 @@ interface Props {
}
export const TimeRangeList = (props: Props) => {
const styles = getStyles();
const styles = useStyles2(getStyles);
const { title, options, placeholderEmpty } = props;
if (typeof placeholderEmpty !== 'undefined' && options.length <= 0) {
@ -62,7 +42,7 @@ export const TimeRangeList = (props: Props) => {
};
const Options = ({ options, value, onChange, title }: Props) => {
const styles = getOptionsStyles();
const styles = useStyles2(getOptionsStyles);
return (
<>
@ -92,3 +72,19 @@ function isEqual(x: TimeOption, y?: TimeOption): boolean {
}
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',
}),
});

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

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

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

@ -3,7 +3,7 @@ import React, { HTMLAttributes } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../themes';
import { useStyles2 } from '../../themes';
import { getChildId } from '../../utils/reactUtils';
import { FieldValidationMessage } from './FieldValidationMessage';
@ -40,35 +40,6 @@ export interface FieldProps extends HTMLAttributes<HTMLDivElement> {
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>(
(
{
@ -88,8 +59,7 @@ export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
}: FieldProps,
ref
) => {
const theme = useTheme2();
const styles = getFieldStyles(theme);
const styles = useStyles2(getFieldStyles);
const inputId = htmlFor ?? getChildId(children);
const labelElement =
@ -143,3 +113,30 @@ function deleteUndefinedProps<T extends Object>(obj: T): Partial<T> {
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',
},
}),
});

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

@ -3,7 +3,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../themes';
import { useStyles2 } from '../../themes';
import { Icon } from '../Icon/Icon';
export interface FieldValidationMessageProps {
@ -12,7 +12,23 @@ export interface FieldValidationMessageProps {
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 = `
font-size: ${theme.typography.size.sm};
font-weight: ${theme.typography.fontWeightMedium};
@ -69,21 +85,4 @@ export const getFieldValidationMessageStyles = stylesFactory((theme: GrafanaThem
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>
);
};

@ -3,7 +3,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { useTheme2, stylesFactory } from '../../themes';
import { useStyles2 } from '../../themes';
import { Icon } from '../Icon/Icon';
export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
@ -12,43 +12,8 @@ export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement>
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) => {
const theme = useTheme2();
const styles = getLabelStyles(theme);
const styles = useStyles2(getLabelStyles);
const categories = category?.map((c, i) => {
return (
<span className={styles.categories} key={`${c}/${i}`}>
@ -70,3 +35,35 @@ export const Label = ({ children, description, className, category, ...labelProp
</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),
}),
});

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

@ -3,7 +3,7 @@ import React from 'react';
import { GrafanaTheme2, colorManipulator, deprecationWarning } from '@grafana/data';
import { useTheme2, stylesFactory } from '../../themes';
import { useStyles2 } from '../../themes';
import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins';
import { ComponentSize } from '../../types';
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) => {
const { size = 'md', variant = 'secondary' } = props;
const theme = useTheme2();
let limitedIconSize: LimitedIconSize;
// 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;
}
const styles = getStyles(theme, limitedIconSize, variant);
const styles = useStyles2(getStyles, limitedIconSize, variant);
let ariaLabel: string | undefined;
let buttonRef: typeof ref | undefined;
@ -104,7 +102,7 @@ export const IconButton = React.forwardRef<HTMLButtonElement, Props>((props, ref
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
// 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;
@ -166,4 +164,4 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, size, variant: IconButton
verticalAlign: 'baseline',
}),
};
});
};

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

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

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

@ -3,8 +3,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory } from '../../themes';
import { useTheme2 } from '../../themes/ThemeContext';
import { useStyles2 } from '../../themes/ThemeContext';
import { inputPadding } from '../Forms/commonStyles';
import { getInputStyles } from '../Input/Input';
@ -17,7 +16,20 @@ interface InputControlProps {
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 });
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>
);
}
);
};

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

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

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

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

@ -4,7 +4,7 @@ import React, { HTMLProps, useRef } from 'react';
import { GrafanaTheme2, deprecationWarning } from '@grafana/data';
import { stylesFactory, useTheme2 } from '../../themes';
import { useStyles2 } from '../../themes';
import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins';
export interface Props extends Omit<HTMLProps<HTMLInputElement>, 'value'> {
@ -21,8 +21,7 @@ export const Switch = React.forwardRef<HTMLInputElement, Props>(
deprecationWarning('Switch', 'checked prop', 'value');
}
const theme = useTheme2();
const styles = getSwitchStyles(theme);
const styles = useStyles2(getSwitchStyles);
const switchIdRef = useRef(id ? id : uniqueId('switch-'));
return (
@ -52,8 +51,7 @@ export interface InlineSwitchProps extends Props {
export const InlineSwitch = React.forwardRef<HTMLInputElement, InlineSwitchProps>(
({ transparent, className, showLabel, label, value, id, invalid, ...props }, ref) => {
const theme = useTheme2();
const styles = getSwitchStyles(theme, transparent);
const styles = useStyles2(getSwitchStyles, transparent);
return (
<div
@ -75,110 +73,108 @@ export const InlineSwitch = React.forwardRef<HTMLInputElement, InlineSwitchProps
InlineSwitch.displayName = 'Switch';
const getSwitchStyles = stylesFactory((theme: GrafanaTheme2, transparent?: boolean) => {
return {
switch: css({
width: '32px',
height: '16px',
position: 'relative',
input: {
opacity: 0,
left: '-100vw',
zIndex: -1000,
position: 'absolute',
'&:disabled + label': {
background: theme.colors.action.disabledBackground,
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),
const getSwitchStyles = (theme: GrafanaTheme2, transparent?: boolean) => ({
switch: css({
width: '32px',
height: '16px',
position: 'relative',
input: {
opacity: 0,
left: '-100vw',
zIndex: -1000,
position: 'absolute',
'&:disabled + label': {
background: theme.colors.action.disabledBackground,
cursor: 'not-allowed',
},
label: {
width: '100%',
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',
'&:checked + label': {
background: theme.colors.primary.main,
borderColor: theme.colors.primary.main,
'&:hover': {
borderColor: theme.components.input.borderHover,
background: theme.colors.primary.shade,
},
'&::after': {
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',
},
transform: 'translate3d(18px, -50%, 0)',
background: theme.colors.primary.contrastText,
},
},
}),
inlineContainer: css({
padding: theme.spacing(0, 1),
height: theme.spacing(theme.components.height.md),
display: 'inline-flex',
alignItems: 'center',
background: transparent ? 'transparent' : theme.components.input.background,
border: `1px solid ${transparent ? 'transparent' : theme.components.input.borderColor}`,
borderRadius: theme.shape.radius.default,
'&:focus + label, &:focus-visible + label': getFocusStyles(theme),
'&:focus:not(:focus-visible) + label': getMouseFocusStyles(theme),
},
label: {
width: '100%',
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': {
border: `1px solid ${transparent ? 'transparent' : theme.components.input.borderHover}`,
borderColor: theme.components.input.borderHover,
},
'.inline-switch-label': {
color: theme.colors.text.primary,
'&::after': {
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)',
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}`,
},
}),
inlineContainer: css({
padding: theme.spacing(0, 1),
height: theme.spacing(theme.components.height.md),
display: 'inline-flex',
alignItems: 'center',
background: transparent ? 'transparent' : theme.components.input.background,
border: `1px solid ${transparent ? 'transparent' : theme.components.input.borderColor}`,
borderRadius: theme.shape.radius.default,
'&:hover': {
border: `1px solid ${transparent ? 'transparent' : theme.components.input.borderHover}`,
'.inline-switch-label': {
color: theme.colors.text.primary,
},
}),
};
},
}),
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}`,
},
}),
});

@ -5,7 +5,7 @@ import { SelectableValue, GrafanaTheme2 } from '@grafana/data';
import { IconButton } from '../../components/IconButton/IconButton';
import { TabsBar, Tab, TabContent } from '../../components/Tabs';
import { stylesFactory, useTheme2 } from '../../themes';
import { useStyles2 } from '../../themes';
import { IconName } from '../../types/icon';
import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar';
@ -23,45 +23,14 @@ export interface TabbedContainerProps {
onClose: () => void;
}
const getStyles = stylesFactory((theme: GrafanaTheme2) => {
return {
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
);
export function TabbedContainer({ tabs, defaultTab, closeIconTooltip, onClose }: TabbedContainerProps) {
const [activeTab, setActiveTab] = useState(tabs.some((tab) => tab.value === defaultTab) ? defaultTab : tabs[0].value);
const onSelectTab = (item: SelectableValue<string>) => {
setActiveTab(item.value!);
};
const { tabs, onClose, closeIconTooltip } = props;
const theme = useTheme2();
const styles = getStyles(theme);
const styles = useStyles2(getStyles);
return (
<div className={styles.container}>
@ -83,3 +52,28 @@ export function TabbedContainer(props: TabbedContainerProps) {
</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),
},
}),
});

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

@ -3,22 +3,7 @@ import React from 'react';
import { GrafanaTheme2, locale } from '@grafana/data';
import { stylesFactory, 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,
}),
};
});
import { useStyles2 } from '../../themes';
export interface CounterProps {
value: number;
@ -29,3 +14,16 @@ export const Counter = ({ value }: CounterProps) => {
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,
}),
});

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

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

@ -2,30 +2,9 @@ import { css } from '@emotion/css';
import React from 'react';
import { CSSTransition } from 'react-transition-group';
import { stylesFactory } from '../../themes';
import { GrafanaTheme2 } from '@grafana/data';
const getStyles = stylesFactory((duration: number) => {
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`,
}),
};
});
import { useStyles2 } from '../../themes';
type Props = {
children: React.ReactNode;
@ -35,10 +14,32 @@ type Props = {
export function FadeTransition(props: Props) {
const { visible, children, duration = 250 } = props;
const styles = getStyles(duration);
const styles = useStyles2(getStyles, duration);
return (
<CSSTransition in={visible} mountOnEnter={true} unmountOnExit={true} timeout={duration} classNames={styles}>
{children}
</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`,
}),
});

@ -2,34 +2,9 @@ import { css } from '@emotion/css';
import React from 'react';
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) => {
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`,
}),
};
});
import { useStyles2 } from '../../themes';
type Props = {
children: React.ReactNode;
@ -42,10 +17,36 @@ type Props = {
export function SlideOutTransition(props: 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 (
<CSSTransition in={visible} mountOnEnter={true} unmountOnExit={true} timeout={duration} classNames={styles}>
{children}
</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`,
}),
});

@ -3,13 +3,13 @@
import { css } from '@emotion/css';
import React from 'react';
import { GraphSeriesValue } from '@grafana/data';
import { GrafanaTheme2, GraphSeriesValue } from '@grafana/data';
import { LegendDisplayMode, LegendPlacement } from '@grafana/schema';
import { CustomScrollbar } from '../../components/CustomScrollbar/CustomScrollbar';
import { VizLegend } from '../../components/VizLegend/VizLegend';
import { VizLegendItem } from '../../components/VizLegend/types';
import { stylesFactory } from '../../themes';
import { useStyles2 } from '../../themes';
import { Graph, GraphProps } from './Graph';
@ -25,21 +25,6 @@ export interface GraphWithLegendProps extends GraphProps {
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 isZeroOnlySeries = data.reduce((acc, current) => acc + (current[1] || 0), 0) === 0;
const isNullOnlySeries = !data.reduce((acc, current) => acc && current[1] !== null, true);
@ -72,7 +57,7 @@ export const GraphWithLegend = (props: GraphWithLegendProps) => {
children,
ariaLabel,
} = props;
const { graphContainer, wrapper, legendContainer } = getGraphWithLegendStyles(props);
const { graphContainer, wrapper, legendContainer } = useStyles2(getGraphWithLegendStyles, props.placement);
const legendItems = series.reduce<VizLegendItem[]>((acc, s) => {
return shouldHideLegendItem(s.data, hideEmpty, hideZero)
@ -130,3 +115,18 @@ export const GraphWithLegend = (props: GraphWithLegendProps) => {
</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',
}),
});

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

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

@ -4,7 +4,7 @@ import AutoSizer from 'react-virtualized-auto-sizer';
import { areEqual, FixedSizeGrid as Grid } from 'react-window';
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 { ResourceItem } from './FolderPickerTab';
@ -26,8 +26,7 @@ const MemoizedCell = memo(function Cell(props: CellProps) {
const { cards, columnCount, onChange, selected } = data;
const singleColumnIndex = columnIndex + rowIndex * columnCount;
const card = cards[singleColumnIndex];
const theme = useTheme2();
const styles = getStyles(theme);
const styles = useStyles2(getStyles);
return (
<div style={style}>
@ -56,53 +55,6 @@ const MemoizedCell = memo(function Cell(props: CellProps) {
);
}, 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 {
onChange: (value: string) => void;
cards: ResourceItem[];
@ -111,8 +63,7 @@ interface CardProps {
export const ResourceCards = (props: CardProps) => {
const { onChange, cards, value } = props;
const theme = useTheme2();
const styles = getStyles(theme);
const styles = useStyles2(getStyles);
return (
<AutoSizer defaultWidth={680}>
@ -139,3 +90,48 @@ export const ResourceCards = (props: CardProps) => {
</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};
`,
});

@ -5,51 +5,7 @@ import React from 'react';
// Services & Utils
import { GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, 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};
}
`,
};
});
import { useStyles2, useTheme2 } from '@grafana/ui';
export interface Props {
width: number;
@ -60,7 +16,7 @@ export interface Props {
export function ExploreDrawer(props: Props) {
const { width, children, onResize } = props;
const theme = useTheme2();
const styles = getStyles(theme);
const styles = useStyles2(getStyles);
const drawerWidth = `${width + 31.5}px`;
return (
@ -87,3 +43,43 @@ export function ExploreDrawer(props: Props) {
</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};
}
`,
});

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

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

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

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

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

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

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

@ -4,7 +4,7 @@ import { useAsyncFn } from 'react-use';
import { GrafanaTheme2, SelectableValue, toOption } from '@grafana/data';
import { AccessoryButton, InputGroup } from '@grafana/experimental';
import { Select, stylesFactory, useTheme2 } from '@grafana/ui';
import { Select, useStyles2 } from '@grafana/ui';
import { CloudWatchDatasource } from '../../../datasource';
import { Dimensions, MetricStat } from '../../../types';
@ -76,8 +76,7 @@ export const FilterItem = ({
metricName,
accountId,
]);
const theme = useTheme2();
const styles = getOperatorStyles(theme);
const styles = useStyles2(getOperatorStyles);
return (
<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({
padding: theme.spacing(0, 1),
alignSelf: 'center',
}),
}));
});

@ -6,50 +6,28 @@ import { DataSourceInstanceSettings, VariableSuggestion } from '@grafana/data';
import {
Button,
DataLinkInput,
stylesFactory,
InlineField,
InlineSwitch,
InlineFieldRow,
InlineLabel,
Input,
useStyles2,
} from '@grafana/ui';
import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker';
import { DataLinkConfig } from '../types';
const getStyles = stylesFactory(() => ({
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 = {
interface Props {
value: DataLinkConfig;
onChange: (value: DataLinkConfig) => void;
onDelete: () => void;
suggestions: VariableSuggestion[];
className?: string;
};
}
export const DataLink = (props: Props) => {
const { value, onChange, onDelete, suggestions, className } = props;
const styles = getStyles();
const styles = useStyles2(getStyles);
const [showInternalLink, setShowInternalLink] = useInternalLink(value.datasourceUid);
const handleChange = (field: keyof typeof value) => (event: React.ChangeEvent<HTMLInputElement>) => {
@ -173,3 +151,26 @@ function useInternalLink(datasourceUid?: string): [boolean, Dispatch<SetStateAct
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;
`,
});

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

Loading…
Cancel
Save