From 133b1316172625fd3b678df829ae90ce65da8679 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Thu, 28 Mar 2019 15:05:26 -0700 Subject: [PATCH] Introduce DapperScrollbars (#12961) * Install new scrollbars library * First pass at new wrapper for scrollbars * Replace FancyScrollbars with DapperScrollbars in Page components * Polish DapperScrollbars * Update dropdown component to use DapperScrollbars * Update a couple dropdowns to conform to new constraints * Update legend to use Dapper Scrollbars * Fix linting error * Implement DapperScrollbars in overlays * Ensure proper functionality in page contents * Use fixed sizing for overlay scrollbars * Updoot --- CHANGELOG.md | 1 + ui/package-lock.json | 25 ++++ ui/package.json | 1 + .../components/dropdowns/Dropdown.scss | 41 +++---- .../components/dropdowns/Dropdown.tsx | 106 +++++++++++------ .../clockface/components/overlays/Overlay.tsx | 7 +- ui/src/pageLayout/components/Page.scss | 16 ++- ui/src/pageLayout/components/PageContents.tsx | 10 +- ui/src/shared/components/Legend.tsx | 6 +- .../shared/components/TimeRangeDropdown.tsx | 1 + .../dapperScrollbars/DapperScrollbars.scss | 59 +++++++++ .../dapperScrollbars/DapperScrollbars.tsx | 112 ++++++++++++++++++ .../AutoRefreshDropdown.tsx | 7 +- ui/src/style/chronograf.scss | 1 + 14 files changed, 317 insertions(+), 76 deletions(-) create mode 100644 ui/src/shared/components/dapperScrollbars/DapperScrollbars.scss create mode 100644 ui/src/shared/components/dapperScrollbars/DapperScrollbars.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index be9075b6d88..e68a01ed3f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ 1. [12793](https://github.com/influxdata/influxdb/pull/12793): Fix task creation error when switching schedule types. 1. [12805](https://github.com/influxdata/influxdb/pull/12805): Fix hidden horizonal scrollbars in flux raw data view 1. [12827](https://github.com/influxdata/influxdb/pull/12827): Fix screen tearing bug in Raw Data View +1. [12961](https://github.com/influxdata/influxdb/pull/12961): Fix scroll clipping in graph legends & dropdown menus 1. [12959](https://github.com/influxdata/influxdb/pull/12959): Fix routing loop ### UI Improvements diff --git a/ui/package-lock.json b/ui/package-lock.json index c8db54f49ea..937dd7b3813 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -3339,6 +3339,11 @@ "integrity": "sha512-FXDYw4TjR8wgPZYui2LeTqWh1BLpfQ8lB6upMtlpDF6WlOOxghmTTxWyngdKTgozqBgKnHbTVwTE+hOHqAykuQ==", "dev": true }, + "cnbuilder": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/cnbuilder/-/cnbuilder-1.0.8.tgz", + "integrity": "sha512-05l9Bhs0FhEFGJ6vFkqL9O9USCKT3zBfOoTAYXGDKA4nFBX1Qc780bvppG9av2U1sKpa27JT8brJtM6VQquRcQ==" + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -13104,6 +13109,26 @@ "resolved": "https://registry.npmjs.org/react-router-redux/-/react-router-redux-4.0.8.tgz", "integrity": "sha1-InQDWWtRUeGCN32rg1tdRfD4BU4=" }, + "react-scrollbars-custom": { + "version": "4.0.0-alpha.8", + "resolved": "https://registry.npmjs.org/react-scrollbars-custom/-/react-scrollbars-custom-4.0.0-alpha.8.tgz", + "integrity": "sha512-sj56pEY/0VV551B61yUzYy7YcR/h5ge51lqUGD9CbBmcJnyVDvZ90zaIIeERPsLxSR6Ddb1ueBAexKIe14zpNw==", + "requires": { + "cnbuilder": "^1.0.8", + "react-draggable": "^3.2.1" + }, + "dependencies": { + "react-draggable": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-3.2.1.tgz", + "integrity": "sha512-r+3Bs9InID2lyIEbR8UIRVtpn4jgu1ArFEZgIy8vibJjijLSdNLX7rH9U68BBVD4RD9v44RXbaK4EHLyKXzNQw==", + "requires": { + "classnames": "^2.2.5", + "prop-types": "^15.6.0" + } + } + } + }, "react-test-renderer": { "version": "16.5.2", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.5.2.tgz", diff --git a/ui/package.json b/ui/package.json index f622e41f28a..ce10cbbcd1d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -177,6 +177,7 @@ "react-resize-detector": "^2.3.0", "react-router": "^3.0.2", "react-router-redux": "^4.0.8", + "react-scrollbars-custom": "^4.0.0-alpha.8", "react-tooltip": "^3.2.1", "react-virtualized": "^9.18.5", "redux": "^4.0.0", diff --git a/ui/src/clockface/components/dropdowns/Dropdown.scss b/ui/src/clockface/components/dropdowns/Dropdown.scss index 1d963fbf982..7f4c872a9ea 100644 --- a/ui/src/clockface/components/dropdowns/Dropdown.scss +++ b/ui/src/clockface/components/dropdowns/Dropdown.scss @@ -46,7 +46,6 @@ line-height: 12px; font-weight: 600; color: fade-out($g20-white, 0.18); - white-space: nowrap; position: relative; text-align: left; @@ -64,13 +63,21 @@ cursor: pointer; } + .dropdown--action & { + padding-left: 11px; + } +} + +.dropdown-item--children { .dropdown-wrap & { word-break: break-all; white-space: pre-wrap; } - .dropdown--action & { - padding-left: 11px; + .dropdown-truncate & { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } } @@ -150,8 +157,7 @@ $dividerA, $dividerB, $dividerText, - $scrollA, - $scrollB + $checkbox ) { @include gradient-h($backgroundA, $backgroundB); @@ -167,13 +173,7 @@ background-color: $dividerA; } .dropdown-item--checkbox:after { - background-color: $scrollA; - } - .fancy-scroll--thumb-h { - @include gradient-h($scrollA, $scrollB); - } - .fancy-scroll--thumb-v { - @include gradient-v($scrollA, $scrollB); + background-color: $checkbox; } } @@ -186,8 +186,7 @@ $c-amethyst, $c-ocean, $c-potassium, - $c-neutrino, - $c-hydrogen + $c-neutrino ); } @@ -200,8 +199,7 @@ $c-sapphire, $c-ocean, $c-laser, - $c-neutrino, - $c-hydrogen + $c-neutrino ); } @@ -214,8 +212,7 @@ $c-ocean, $c-viridian, $c-krypton, - $c-neutrino, - $c-krypton + $c-neutrino ); } @@ -228,12 +225,6 @@ $g0-obsidian, $g2-kevlar, $g11-sidewalk, - $c-pool, - $c-comet + $c-pool ); } - -/* TODO: Make fancyscroll more customizable */ -.dropdown--menu-container .fancy-scroll--track-h { - display: none; -} diff --git a/ui/src/clockface/components/dropdowns/Dropdown.tsx b/ui/src/clockface/components/dropdowns/Dropdown.tsx index 670f3c095e1..c79dbbaa05b 100644 --- a/ui/src/clockface/components/dropdowns/Dropdown.tsx +++ b/ui/src/clockface/components/dropdowns/Dropdown.tsx @@ -1,5 +1,5 @@ // Libraries -import React, {Component, CSSProperties, MouseEvent} from 'react' +import React, {Component, MouseEvent} from 'react' import classnames from 'classnames' // Components @@ -7,7 +7,7 @@ import {ClickOutside} from 'src/shared/components/ClickOutside' import DropdownDivider from 'src/clockface/components/dropdowns/DropdownDivider' import DropdownItem from 'src/clockface/components/dropdowns/DropdownItem' import DropdownButton from 'src/clockface/components/dropdowns/DropdownButton' -import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar' +import DapperScrollbars from 'src/shared/components/dapperScrollbars/DapperScrollbars' import WaitingText from 'src/shared/components/WaitingText' // Types @@ -26,30 +26,36 @@ export enum DropdownMode { Radio = 'radio', } -interface OwnProps { +interface ThumbColors { + start: string + stop: string +} + +interface PassedProps { children: JSX.Element[] onChange: (value: any) => void + selectedID?: string + widthPixels?: number + menuWidthPixels?: number + menuHeader?: JSX.Element + icon?: IconFont + customClass?: string } -interface DefaultProps { - buttonTestID?: string - selectedID?: string +export interface DefaultProps { buttonColor?: ComponentColor buttonSize?: ComponentSize - menuColor?: DropdownMenuColors status?: ComponentStatus - widthPixels?: number - icon?: IconFont - wrapText?: boolean - customClass?: string maxMenuHeight?: number + menuColor?: DropdownMenuColors mode?: DropdownMode titleText?: string - menuHeader?: JSX.Element + wrapMenuText?: boolean testID?: string + buttonTestID?: string } -export type Props = OwnProps & DefaultProps +export type Props = PassedProps & DefaultProps interface State { expanded: boolean @@ -61,11 +67,11 @@ class Dropdown extends Component { buttonColor: ComponentColor.Default, buttonSize: ComponentSize.Small, status: ComponentStatus.Default, - wrapText: false, maxMenuHeight: 250, menuColor: DropdownMenuColors.Sapphire, mode: DropdownMode.Radio, titleText: '', + wrapMenuText: false, testID: 'dropdown', buttonTestID: 'dropdown-button', } @@ -110,16 +116,17 @@ class Dropdown extends Component { buttonColor, buttonSize, status, - wrapText, customClass, mode, + wrapMenuText, } = this.props return classnames( `dropdown dropdown-${buttonSize} dropdown-${buttonColor}`, { disabled: status === ComponentStatus.Disabled, - 'dropdown-wrap': wrapText, + 'dropdown-wrap': wrapMenuText, + 'dropdown-truncate': !wrapMenuText, [customClass]: customClass, [`dropdown--${mode}`]: mode, } @@ -178,6 +185,8 @@ class Dropdown extends Component { const { selectedID, maxMenuHeight, + widthPixels, + menuWidthPixels, menuHeader, menuColor, children, @@ -190,15 +199,32 @@ class Dropdown extends Component { return null } + let width = '100%' + + if (widthPixels) { + width = `${widthPixels}px` + } + + if (menuWidthPixels) { + width = `${menuWidthPixels}px` + } + + const {start, stop} = this.thumbColorsFromTheme + return (
-
{ } })}
-
+
) } - private get menuStyle(): CSSProperties { - const {wrapText, widthPixels} = this.props - - let containerWidth = '100%' - - if (widthPixels) { - containerWidth = `${widthPixels}px` - } - - if (wrapText && widthPixels) { - return { - width: containerWidth, - } - } - - return { - minWidth: containerWidth, + private get thumbColorsFromTheme(): ThumbColors { + const {menuColor} = this.props + + switch (menuColor) { + case DropdownMenuColors.Amethyst: + case DropdownMenuColors.Sapphire: + return { + start: '#BEF0FF', + stop: '#6BDFFF', + } + case DropdownMenuColors.Malachite: + return { + start: '#BEF0FF', + stop: '#A5F3B4', + } + default: + case DropdownMenuColors.Onyx: + return { + start: '#22ADF6', + stop: '#9394FF', + } } } diff --git a/ui/src/clockface/components/overlays/Overlay.tsx b/ui/src/clockface/components/overlays/Overlay.tsx index 32196055afc..170dcb504ee 100644 --- a/ui/src/clockface/components/overlays/Overlay.tsx +++ b/ui/src/clockface/components/overlays/Overlay.tsx @@ -7,7 +7,7 @@ import OverlayContainer from 'src/clockface/components/overlays/OverlayContainer import OverlayHeading from 'src/clockface/components/overlays/OverlayHeading' import OverlayBody from 'src/clockface/components/overlays/OverlayBody' import OverlayFooter from 'src/clockface/components/overlays/OverlayFooter' -import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar' +import DapperScrollbars from 'src/shared/components/dapperScrollbars/DapperScrollbars' // Styles import 'src/clockface/components/overlays/Overlay.scss' @@ -59,15 +59,16 @@ class Overlay extends Component { public render() { return ( - {this.childContainer}
- + ) } diff --git a/ui/src/pageLayout/components/Page.scss b/ui/src/pageLayout/components/Page.scss index ff05ca6e6db..be1c49bbb82 100644 --- a/ui/src/pageLayout/components/Page.scss +++ b/ui/src/pageLayout/components/Page.scss @@ -75,11 +75,21 @@ } .page-contents { + width: 100%; + position: relative; height: calc(100% - #{$page-header-size}) !important; &.full-width { padding: 0 $page-gutter; } + + & > .dapper-scrollbars--track-y { + background: linear-gradient( + to bottom, + $g6-smoke 0%, + $g3-castle 100% + ) !important; + } } .container-fluid { @@ -97,5 +107,7 @@ .page-contents.presentation-mode { height: 100% !important; - .container-fluid {padding: 8px} -} \ No newline at end of file + .container-fluid { + padding: 8px; + } +} diff --git a/ui/src/pageLayout/components/PageContents.tsx b/ui/src/pageLayout/components/PageContents.tsx index e156000f772..9c7b8c53837 100644 --- a/ui/src/pageLayout/components/PageContents.tsx +++ b/ui/src/pageLayout/components/PageContents.tsx @@ -3,7 +3,7 @@ import React, {Component, ReactNode} from 'react' import classnames from 'classnames' // Components -import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar' +import DapperScrollbars from 'src/shared/components/dapperScrollbars/DapperScrollbars' // Decorators import {ErrorHandling} from 'src/shared/decorators/errors' @@ -21,9 +21,13 @@ class PageContents extends Component { if (scrollable) { return ( - + {this.children} - + ) } diff --git a/ui/src/shared/components/Legend.tsx b/ui/src/shared/components/Legend.tsx index 3421b918def..b4705c486d6 100644 --- a/ui/src/shared/components/Legend.tsx +++ b/ui/src/shared/components/Legend.tsx @@ -5,7 +5,7 @@ import moment from 'moment' import {uniq, flatten, isNumber} from 'lodash' // Components -import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar' +import DapperScrollbars from 'src/shared/components/dapperScrollbars/DapperScrollbars' // Constants import {LEGEND_PORTAL_ID} from 'src/shared/components/LegendPortal' @@ -42,7 +42,7 @@ class Legend extends PureComponent { return createPortal(
{this.time}
- +
{this.columns.map(({name, isNumeric, rows}, i) => (
{
))}
-
+
, document.querySelector(`#${LEGEND_PORTAL_ID}`) ) diff --git a/ui/src/shared/components/TimeRangeDropdown.tsx b/ui/src/shared/components/TimeRangeDropdown.tsx index 18c7fde692c..fc034c15da1 100644 --- a/ui/src/shared/components/TimeRangeDropdown.tsx +++ b/ui/src/shared/components/TimeRangeDropdown.tsx @@ -55,6 +55,7 @@ class TimeRangeDropdown extends PureComponent { selectedID={timeRange.label} onChange={this.handleChange} widthPixels={this.dropdownWidth} + menuWidthPixels={this.dropdownWidth + 50} titleText={this.formattedCustomTimeRange} > {TIME_RANGES.map(({label}) => { diff --git a/ui/src/shared/components/dapperScrollbars/DapperScrollbars.scss b/ui/src/shared/components/dapperScrollbars/DapperScrollbars.scss new file mode 100644 index 00000000000..ffbd7bf5f03 --- /dev/null +++ b/ui/src/shared/components/dapperScrollbars/DapperScrollbars.scss @@ -0,0 +1,59 @@ +$dapper-scrollbars--size: 6px; + +.dapper-scrollbars--wrapper { + margin-right: 0 !important; + margin-bottom: 0 !important; + right: 0 !important; + bottom: 0 !important; +} + +.dapper-scrollbars--track-x, +.dapper-scrollbars--track-y { + border-radius: $dapper-scrollbars--size !important; + position: absolute !important; + background-color: rgba($g0-obsidian, 0.4) !important; + user-select: none !important; + overflow: hidden !important; + transition: opacity 0.25s ease !important; +} + +.dapper-scrollbars--track-x { + height: $dapper-scrollbars--size !important; + width: calc(100% - #{$dapper-scrollbars--size}) !important; + bottom: $dapper-scrollbars--size / 2 !important; + left: $dapper-scrollbars--size / 2 !important; +} + +.dapper-scrollbars--track-y { + width: $dapper-scrollbars--size !important; + height: calc(100% - #{$dapper-scrollbars--size}) !important; + right: $dapper-scrollbars--size / 2 !important; + top: $dapper-scrollbars--size / 2 !important; +} + +.dapper-scrollbars--thumb-x, +.dapper-scrollbars--thumb-y { + border-radius: $dapper-scrollbars--size / 2 !important; +} + +.dapper-scrollbars--thumb-x { + height: $dapper-scrollbars--size !important; +} + +.dapper-scrollbars--thumb-y { + width: $dapper-scrollbars--size !important; +} + +.dapper-scrollbars--autohide { + .dapper-scrollbars--track-x, + .dapper-scrollbars--track-y { + opacity: 0; + } + + &:hover { + .dapper-scrollbars--track-x, + .dapper-scrollbars--track-y { + opacity: 1; + } + } +} diff --git a/ui/src/shared/components/dapperScrollbars/DapperScrollbars.tsx b/ui/src/shared/components/dapperScrollbars/DapperScrollbars.tsx new file mode 100644 index 00000000000..1cb262bade8 --- /dev/null +++ b/ui/src/shared/components/dapperScrollbars/DapperScrollbars.tsx @@ -0,0 +1,112 @@ +// Libraries +import React, {Component, CSSProperties, ReactNode} from 'react' +import _ from 'lodash' +import classnames from 'classnames' +import Scrollbar from 'react-scrollbars-custom' + +// Decorators +import {ErrorHandling} from 'src/shared/decorators/errors' + +interface PassedProps { + children: JSX.Element | JSX.Element[] | ReactNode +} + +interface DefaultProps { + className?: string + removeTracksWhenNotUsed?: boolean + removeTrackYWhenNotUsed?: boolean + removeTrackXWhenNotUsed?: boolean + noScrollX?: boolean + noScrollY?: boolean + noScroll?: boolean + thumbStartColor?: string + thumbStopColor?: string + style?: CSSProperties + autoHide?: boolean + autoSize?: boolean +} + +type Props = PassedProps & DefaultProps + +@ErrorHandling +class DapperScrollbars extends Component { + public static defaultProps: DefaultProps = { + removeTracksWhenNotUsed: true, + removeTrackYWhenNotUsed: true, + removeTrackXWhenNotUsed: true, + noScrollX: false, + noScrollY: false, + noScroll: false, + thumbStartColor: '#00C9FF', + thumbStopColor: '#9394FF', + autoHide: false, + autoSize: true, + } + + public render() { + const { + removeTracksWhenNotUsed, + removeTrackYWhenNotUsed, + removeTrackXWhenNotUsed, + noScrollX, + noScrollY, + className, + autoHide, + autoSize, + noScroll, + children, + style, + } = this.props + + const classname = classnames('dapper-scrollbars', { + 'dapper-scrollbars--autohide': autoHide, + [`${className}`]: className, + }) + + return ( + + {children} + + ) + } + + private get thumbXStyle(): CSSProperties { + const {thumbStartColor, thumbStopColor} = this.props + + return { + background: `linear-gradient(to right, ${thumbStartColor} 0%,${thumbStopColor} 100%)`, + } + } + + private get thumbYStyle(): CSSProperties { + const {thumbStartColor, thumbStopColor} = this.props + + return { + background: `linear-gradient(to bottom, ${thumbStartColor} 0%,${thumbStopColor} 100%)`, + } + } +} + +export default DapperScrollbars diff --git a/ui/src/shared/components/dropdown_auto_refresh/AutoRefreshDropdown.tsx b/ui/src/shared/components/dropdown_auto_refresh/AutoRefreshDropdown.tsx index 469b917b214..0b283e888f0 100644 --- a/ui/src/shared/components/dropdown_auto_refresh/AutoRefreshDropdown.tsx +++ b/ui/src/shared/components/dropdown_auto_refresh/AutoRefreshDropdown.tsx @@ -11,6 +11,8 @@ import autoRefreshOptions, { AutoRefreshOption, AutoRefreshOptionType, } from 'src/shared/data/autoRefreshes' +const DROPDOWN_WIDTH_COLLAPSED = 50 +const DROPDOWN_WIDTH_FULL = 84 import {ErrorHandling} from 'src/shared/decorators/errors' @@ -41,6 +43,7 @@ class AutoRefreshDropdown extends Component { @@ -96,10 +99,10 @@ class AutoRefreshDropdown extends Component { private get dropdownWidthPixels(): number { if (this.isPaused) { - return 50 + return DROPDOWN_WIDTH_COLLAPSED } - return 84 + return DROPDOWN_WIDTH_FULL } private get selectedID(): string { diff --git a/ui/src/style/chronograf.scss b/ui/src/style/chronograf.scss index c38ea801bff..7fb605adaf6 100644 --- a/ui/src/style/chronograf.scss +++ b/ui/src/style/chronograf.scss @@ -106,6 +106,7 @@ @import 'src/dataLoaders/components/side_bar/SideBar.scss'; @import 'src/dataLoaders/components/DataLoadersOverlay.scss'; @import 'src/shared/components/EmptyGraphError.scss'; +@import 'src/shared/components/dapperScrollbars/DapperScrollbars.scss'; // External @import '../../node_modules/@influxdata/react-custom-scrollbars/dist/styles.css';