Commit d472c782 by Uchechukwu Obasi Committed by GitHub

CustomScrollbar: migrated styles from sass to emotion (#30506)

* CustomScrollbar: migrated styles from sass to emotion

* fixes frontend test

* updated changes

* fixed page props and applied changes to all occurences

* moved the getStyles function to the bottom of the file

* made some changes

* fixed snapshot test

* Revert "Merge branch 'refactor-customscrollbar-30354' of https://github.com/grafana/grafana into refactor-customscrollbar-30354"

This reverts commit c45ee30b6b297991a1628e9b21c4121dc78e376c, reversing
changes made to d2645534e39c4ffda30a948f4a892c1c68ec38ca.

* improved props name

* made use of theme variables for style consistency

* fixed snapshot test and updated some theme changes

* removed hover effects from customScrollbar style

* made some changes

* updated some changes

* added label to class names

* changed to a functional component and improved styling

* fixes snapshot test

* fixes small nit

* minor tweaks

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
parent 75cb6703
import React from 'react'; import React from 'react';
import renderer from 'react-test-renderer'; import renderer from 'react-test-renderer';
import CustomScrollbar from './CustomScrollbar'; import { CustomScrollbar } from './CustomScrollbar';
describe('CustomScrollbar', () => { describe('CustomScrollbar', () => {
it('renders correctly', () => { it('renders correctly', () => {
......
import React, { Component } from 'react'; import React, { FC, useCallback, useEffect, useRef } from 'react';
import isNil from 'lodash/isNil'; import isNil from 'lodash/isNil';
import classNames from 'classnames'; import classNames from 'classnames';
import { css } from 'emotion';
import Scrollbars from 'react-custom-scrollbars'; import Scrollbars from 'react-custom-scrollbars';
import { useStyles } from '../../themes';
import { GrafanaTheme } from '@grafana/data';
interface Props { interface Props {
className?: string; className?: string;
autoHide?: boolean; autoHide?: boolean;
autoHideTimeout?: number; autoHideTimeout?: number;
autoHideDuration?: number;
autoHeightMax?: string; autoHeightMax?: string;
hideTracksWhenNotNeeded?: boolean; hideTracksWhenNotNeeded?: boolean;
hideHorizontalTrack?: boolean; hideHorizontalTrack?: boolean;
hideVerticalTrack?: boolean; hideVerticalTrack?: boolean;
scrollTop?: number; scrollTop?: number;
setScrollTop: (event: any) => void; setScrollTop?: (event: any) => void;
autoHeightMin?: number | string; autoHeightMin?: number | string;
updateAfterMountMs?: number; updateAfterMountMs?: number;
} }
...@@ -21,103 +23,86 @@ interface Props { ...@@ -21,103 +23,86 @@ interface Props {
/** /**
* Wraps component into <Scrollbars> component from `react-custom-scrollbars` * Wraps component into <Scrollbars> component from `react-custom-scrollbars`
*/ */
export class CustomScrollbar extends Component<Props> { export const CustomScrollbar: FC<Props> = ({
static defaultProps: Partial<Props> = { autoHide = false,
autoHide: false, autoHideTimeout = 200,
autoHideTimeout: 200, setScrollTop,
autoHideDuration: 200, className,
setScrollTop: () => {}, autoHeightMin = '0',
hideTracksWhenNotNeeded: false, autoHeightMax = '100%',
autoHeightMin: '0', hideTracksWhenNotNeeded = false,
autoHeightMax: '100%', hideHorizontalTrack,
}; hideVerticalTrack,
updateAfterMountMs,
private ref: React.RefObject<Scrollbars>; scrollTop,
children,
constructor(props: Props) { }) => {
super(props); const ref = useRef<Scrollbars>(null);
this.ref = React.createRef<Scrollbars>(); const styles = useStyles(getStyles);
}
updateScroll() {
const ref = this.ref.current;
const { scrollTop } = this.props;
if (ref && !isNil(scrollTop)) { const updateScroll = () => {
ref.scrollTop(scrollTop); if (ref.current && !isNil(scrollTop)) {
} ref.current.scrollTop(scrollTop);
} }
};
componentDidMount() { useEffect(() => {
this.updateScroll(); updateScroll();
});
// this logic is to make scrollbar visible when content is added body after mount
if (this.props.updateAfterMountMs) {
setTimeout(() => this.updateAfterMount(), this.props.updateAfterMountMs);
}
}
updateAfterMount() { /**
if (this.ref && this.ref.current) { * Special logic for doing a update a few milliseconds after mount to check for
const scrollbar = this.ref.current as any; * updated height due to dynamic content
if (scrollbar.update) { */
if (updateAfterMountMs) {
useEffect(() => {
setTimeout(() => {
const scrollbar = ref.current as any;
if (scrollbar?.update) {
scrollbar.update(); scrollbar.update();
} }
} }, updateAfterMountMs);
} }, []);
componentDidUpdate() {
this.updateScroll();
} }
renderTrack = (track: 'track-vertical' | 'track-horizontal', hideTrack: boolean | undefined, passedProps: any) => { function renderTrack(className: string, hideTrack: boolean | undefined, passedProps: any) {
if (passedProps.style && hideTrack) { if (passedProps.style && hideTrack) {
passedProps.style.display = 'none'; passedProps.style.display = 'none';
} }
return <div {...passedProps} className={track} />; return <div {...passedProps} className={className} />;
}; }
renderThumb = (thumb: 'thumb-horizontal' | 'thumb-vertical', passedProps: any) => {
return <div {...passedProps} className={thumb} />;
};
renderTrackHorizontal = (passedProps: any) => {
return this.renderTrack('track-horizontal', this.props.hideHorizontalTrack, passedProps);
};
renderTrackVertical = (passedProps: any) => { const renderTrackHorizontal = useCallback(
return this.renderTrack('track-vertical', this.props.hideVerticalTrack, passedProps); (passedProps: any) => {
}; return renderTrack('track-horizontal', hideHorizontalTrack, passedProps);
},
[hideHorizontalTrack]
);
renderThumbHorizontal = (passedProps: any) => { const renderTrackVertical = useCallback(
return this.renderThumb('thumb-horizontal', passedProps); (passedProps: any) => {
}; return renderTrack('track-vertical', hideVerticalTrack, passedProps);
},
[hideVerticalTrack]
);
renderThumbVertical = (passedProps: any) => { const renderThumbHorizontal = useCallback((passedProps: any) => {
return this.renderThumb('thumb-vertical', passedProps); return <div {...passedProps} className="thumb-horizontal" />;
}; }, []);
renderView = (passedProps: any) => { const renderThumbVertical = useCallback((passedProps: any) => {
return <div {...passedProps} className="view" />; return <div {...passedProps} className="thumb-vertical" />;
}; }, []);
render() { const renderView = useCallback((passedProps: any) => {
const { return <div {...passedProps} className="scrollbar-view" />;
className, }, []);
children,
autoHeightMax,
autoHeightMin,
setScrollTop,
autoHide,
autoHideTimeout,
hideTracksWhenNotNeeded,
} = this.props;
return ( return (
<Scrollbars <Scrollbars
ref={this.ref} ref={ref}
className={classNames('custom-scrollbar', className)} className={classNames(styles.customScrollbar, className)}
onScroll={setScrollTop} onScroll={setScrollTop}
autoHeight={true} autoHeight={true}
autoHide={autoHide} autoHide={autoHide}
...@@ -127,16 +112,63 @@ export class CustomScrollbar extends Component<Props> { ...@@ -127,16 +112,63 @@ export class CustomScrollbar extends Component<Props> {
// Before these where set to inherit but that caused problems with cut of legends in firefox // Before these where set to inherit but that caused problems with cut of legends in firefox
autoHeightMax={autoHeightMax} autoHeightMax={autoHeightMax}
autoHeightMin={autoHeightMin} autoHeightMin={autoHeightMin}
renderTrackHorizontal={this.renderTrackHorizontal} renderTrackHorizontal={renderTrackHorizontal}
renderTrackVertical={this.renderTrackVertical} renderTrackVertical={renderTrackVertical}
renderThumbHorizontal={this.renderThumbHorizontal} renderThumbHorizontal={renderThumbHorizontal}
renderThumbVertical={this.renderThumbVertical} renderThumbVertical={renderThumbVertical}
renderView={this.renderView} renderView={renderView}
> >
{children} {children}
</Scrollbars> </Scrollbars>
); );
} };
}
export default CustomScrollbar; export default CustomScrollbar;
const getStyles = (theme: GrafanaTheme) => {
return {
customScrollbar: css`
// Fix for Firefox. For some reason sometimes .view container gets a height of its content, but in order to
// make scroll working it should fit outer container size (scroll appears only when inner container size is
// greater than outer one).
display: flex;
flex-grow: 1;
.scrollbar-view {
display: flex;
flex-grow: 1;
flex-direction: column;
}
.track-vertical {
border-radius: ${theme.border.radius.md};
width: ${theme.spacing.sm} !important;
right: 0px;
bottom: ${theme.spacing.xxs};
top: ${theme.spacing.xxs};
}
.track-horizontal {
border-radius: ${theme.border.radius.md};
height: ${theme.spacing.sm} !important;
right: ${theme.spacing.xxs};
bottom: ${theme.spacing.xxs};
left: ${theme.spacing.xxs};
}
.thumb-vertical {
background: ${theme.colors.bg3};
border-radius: ${theme.border.radius.md};
opacity: 0;
}
.thumb-horizontal {
background: ${theme.colors.bg3};
border-radius: ${theme.border.radius.md};
opacity: 0;
}
&:hover {
.thumb-vertical,
.thumb-horizontal {
opacity: 1;
transition: opacity 0.3s ease-in-out;
}
}
`,
};
};
.custom-scrollbar {
// Fix for Firefox. For some reason sometimes .view container gets a height of its content, but in order to
// make scroll working it should fit outer container size (scroll appears only when inner container size is
// greater than outer one).
display: flex;
flex-grow: 1;
.view {
display: flex;
flex-grow: 1;
flex-direction: column;
}
.track-vertical {
border-radius: 3px;
width: 8px !important;
right: 2px;
bottom: 2px;
top: 2px;
}
.track-horizontal {
border-radius: 3px;
height: 8px !important;
right: 2px;
bottom: 2px;
left: 2px;
}
.thumb-vertical {
@include gradient-vertical($scrollbarBackground, $scrollbarBackground2);
border-radius: 6px;
opacity: 0;
}
.thumb-horizontal {
@include gradient-horizontal($scrollbarBackground, $scrollbarBackground2);
border-radius: 6px;
opacity: 0;
}
&:hover {
.thumb-vertical,
.thumb-horizontal {
opacity: 0.8;
transition: opacity 0.3s ease-in-out;
}
}
// page scrollbar should stick to left side to aid hitting it
&--page {
.track-vertical {
right: 0;
}
}
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
exports[`CustomScrollbar renders correctly 1`] = ` exports[`CustomScrollbar renders correctly 1`] = `
<div <div
className="custom-scrollbar" className="css-1fb8j9z"
style={ style={
Object { Object {
"height": "auto", "height": "auto",
...@@ -15,7 +15,7 @@ exports[`CustomScrollbar renders correctly 1`] = ` ...@@ -15,7 +15,7 @@ exports[`CustomScrollbar renders correctly 1`] = `
} }
> >
<div <div
className="view" className="scrollbar-view"
style={ style={
Object { Object {
"WebkitOverflowScrolling": "touch", "WebkitOverflowScrolling": "touch",
......
...@@ -4,7 +4,7 @@ import RcDrawer from 'rc-drawer'; ...@@ -4,7 +4,7 @@ import RcDrawer from 'rc-drawer';
import { css } from 'emotion'; import { css } from 'emotion';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import CustomScrollbar from '../CustomScrollbar/CustomScrollbar'; import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar';
import { IconButton } from '../IconButton/IconButton'; import { IconButton } from '../IconButton/IconButton';
import { stylesFactory, useTheme } from '../../themes'; import { stylesFactory, useTheme } from '../../themes';
......
import React, { FC, CSSProperties, ComponentType } from 'react'; import React, { FC, CSSProperties, ComponentType } from 'react';
import { useMeasure } from 'react-use'; import { useMeasure } from 'react-use';
import CustomScrollbar from '../CustomScrollbar/CustomScrollbar'; import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar';
/** /**
* @beta * @beta
......
@import 'ButtonCascader/ButtonCascader'; @import 'ButtonCascader/ButtonCascader';
@import 'ColorPicker/ColorPicker'; @import 'ColorPicker/ColorPicker';
@import 'CustomScrollbar/CustomScrollbar';
@import 'Drawer/Drawer'; @import 'Drawer/Drawer';
@import 'FormField/FormField'; @import 'FormField/FormField';
@import 'RefreshPicker/RefreshPicker'; @import 'RefreshPicker/RefreshPicker';
......
...@@ -47,7 +47,7 @@ class Page extends Component<Props> { ...@@ -47,7 +47,7 @@ class Page extends Component<Props> {
const { navModel, children, ...otherProps } = this.props; const { navModel, children, ...otherProps } = this.props;
return ( return (
<div {...otherProps} className="page-scrollbar-wrapper"> <div {...otherProps} className="page-scrollbar-wrapper">
<CustomScrollbar autoHeightMin={'100%'} className="custom-scrollbar--page"> <CustomScrollbar autoHeightMin={'100%'}>
<div className="page-scrollbar-content"> <div className="page-scrollbar-content">
<PageHeader model={navModel} /> <PageHeader model={navModel} />
{children} {children}
......
...@@ -316,7 +316,6 @@ export class DashboardPage extends PureComponent<Props, State> { ...@@ -316,7 +316,6 @@ export class DashboardPage extends PureComponent<Props, State> {
scrollTop={updateScrollTop} scrollTop={updateScrollTop}
hideHorizontalTrack={true} hideHorizontalTrack={true}
updateAfterMountMs={500} updateAfterMountMs={500}
className="custom-scrollbar--page"
> >
<div className="dashboard-content"> <div className="dashboard-content">
{initError && this.renderInitFailedState()} {initError && this.renderInitFailedState()}
......
...@@ -106,14 +106,8 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1` ...@@ -106,14 +106,8 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
className="dashboard-scroll" className="dashboard-scroll"
> >
<CustomScrollbar <CustomScrollbar
autoHeightMax="100%"
autoHeightMin="100%" autoHeightMin="100%"
autoHide={false}
autoHideDuration={200}
autoHideTimeout={200}
className="custom-scrollbar--page"
hideHorizontalTrack={true} hideHorizontalTrack={true}
hideTracksWhenNotNeeded={false}
setScrollTop={[Function]} setScrollTop={[Function]}
updateAfterMountMs={500} updateAfterMountMs={500}
> >
...@@ -481,14 +475,8 @@ exports[`DashboardPage When dashboard has editview url state should render setti ...@@ -481,14 +475,8 @@ exports[`DashboardPage When dashboard has editview url state should render setti
className="dashboard-scroll" className="dashboard-scroll"
> >
<CustomScrollbar <CustomScrollbar
autoHeightMax="100%"
autoHeightMin="100%" autoHeightMin="100%"
autoHide={false}
autoHideDuration={200}
autoHideTimeout={200}
className="custom-scrollbar--page"
hideHorizontalTrack={true} hideHorizontalTrack={true}
hideTracksWhenNotNeeded={false}
setScrollTop={[Function]} setScrollTop={[Function]}
updateAfterMountMs={500} updateAfterMountMs={500}
> >
......
...@@ -31,7 +31,7 @@ export class Wrapper extends Component<WrapperProps> { ...@@ -31,7 +31,7 @@ export class Wrapper extends Component<WrapperProps> {
return ( return (
<div className="page-scrollbar-wrapper"> <div className="page-scrollbar-wrapper">
<CustomScrollbar autoHeightMin={'100%'} autoHeightMax={''} className="custom-scrollbar--page"> <CustomScrollbar autoHeightMin={'100%'}>
<div className="explore-wrapper"> <div className="explore-wrapper">
<ErrorBoundaryAlert style="page"> <ErrorBoundaryAlert style="page">
<Explore exploreId={ExploreId.left} /> <Explore exploreId={ExploreId.left} />
......
...@@ -337,13 +337,7 @@ export class QueryGroup extends PureComponent<Props, State> { ...@@ -337,13 +337,7 @@ export class QueryGroup extends PureComponent<Props, State> {
const styles = getStyles(); const styles = getStyles();
return ( return (
<CustomScrollbar <CustomScrollbar autoHeightMin="100%" scrollTop={scrollTop} setScrollTop={this.setScrollTop}>
autoHeightMin="100%"
autoHide={true}
updateAfterMountMs={300}
scrollTop={scrollTop}
setScrollTop={this.setScrollTop}
>
<div className={styles.innerWrapper}> <div className={styles.innerWrapper}>
{this.renderTopSection(styles)} {this.renderTopSection(styles)}
{dsSettings && ( {dsSettings && (
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment