From ac378790166f957e67baa745e0e3fb4f71fce208 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 16 Jan 2019 18:22:53 +0100 Subject: [PATCH 01/57] WIP Basics of named color picker --- .../ColorPicker/ColorPicker.story.tsx | 12 +- .../components/ColorPicker/ColorPicker.tsx | 3 + .../ColorPicker/NamedColorsPicker.story.tsx | 68 ++++++++++ .../ColorPicker/NamedColorsPicker.tsx | 121 +++++++++++++++++ packages/grafana-ui/src/utils/colors.ts | 1 - .../src/utils/colorsPalette.test.ts | 31 +++++ .../grafana-ui/src/utils/colorsPalette.ts | 125 ++++++++++++++++++ yarn.lock | 6 +- 8 files changed, 358 insertions(+), 9 deletions(-) create mode 100644 packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx create mode 100644 packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx create mode 100644 packages/grafana-ui/src/utils/colorsPalette.test.ts create mode 100644 packages/grafana-ui/src/utils/colorsPalette.ts diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx index 234b4fa3a2e..6e029f3cfa8 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx @@ -1,7 +1,6 @@ import React, { FunctionComponent } from 'react'; import { storiesOf } from '@storybook/react'; -import { ColorPicker } from '@grafana/ui'; -import { withInfo } from '@storybook/addon-info'; +import { ColorPickerPopover } from './ColorPickerPopover'; const CenteredStory: FunctionComponent<{}> = ({ children }) => { return ( @@ -20,6 +19,9 @@ const CenteredStory: FunctionComponent<{}> = ({ children }) => { storiesOf('UI/ColorPicker', module) .addDecorator(story => {story()}) - .add('default', withInfo({inline: true})(() => { - return {}} />; - })); + // .add('Color picker popover', () => { + // return {}} />; + // }) + .add('Named colors swatch', () => { + return {}} />; + }); diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 141d616d4c5..682d2f1c605 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import Drop from 'tether-drop'; import { ColorPickerPopover } from './ColorPickerPopover'; +import { Color } from '../../utils/colorsPalette'; interface Props { /** @@ -9,6 +10,7 @@ interface Props { * * @default " " **/ + name?: Color; color: string; onChange: (c: string) => void; } @@ -55,6 +57,7 @@ export class ColorPicker extends Component { }; render() { + return (
(this.pickerElem = element)}>
diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx new file mode 100644 index 00000000000..1cdf01a2dd3 --- /dev/null +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx @@ -0,0 +1,68 @@ +import React, { FunctionComponent } from 'react'; +import { storiesOf } from '@storybook/react'; +import NamedColorsPicker from './NamedColorsPicker'; +import { Color, getColorName } from '@grafana/ui/src/utils/colorsPalette'; + +const CenteredStory: FunctionComponent<{}> = ({ children }) => { + return ( +
+ {children} +
+ ); +}; + +interface StateHolderProps { + initialState: T; + children: (currentState: T, updateState: (nextState: T) => void) => JSX.Element; +} + +class UseState extends React.Component, {value: T}> { + constructor(props: StateHolderProps) { + super(props) + this.state = { + value: props.initialState + } + } + handleStateUpdate = (nextState: T) => { + this.setState({value: nextState}) + } + render() { + return this.props.children(this.state.value, this.handleStateUpdate) + } +} + +storiesOf('UI/ColorPicker', module) + .addDecorator(story => {story()}) + .add('Named colors swatch - support for named colors', () => { + return( + + {(selectedColor, updateSelectedColor) => { + return ( + { updateSelectedColor(color.name);}} + /> + ) + }} + ); + }) + .add('Named colors swatch - support for hex values', () => { + return( + + {(selectedColor, updateSelectedColor) => { + return ( + updateSelectedColor(color.variants.dark)} + /> + ) + }} + ); + }); diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx new file mode 100644 index 00000000000..a0732e4e21d --- /dev/null +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx @@ -0,0 +1,121 @@ +import React, { FunctionComponent } from 'react'; +import { find, upperFirst } from 'lodash'; +import { Color, ColorsPalete, ColorDefinition } from '../../utils/colorsPalette'; + +type ColorChangeHandler = (color: ColorDefinition) => void; + +enum ColorSwatchVariant { + Small = 'small', + Large = 'large', +} + +interface ColorSwatchProps extends React.DOMAttributes { + color: ColorDefinition; + variant?: ColorSwatchVariant; + isSelected?: boolean; +} + +const ColorSwatch: FunctionComponent = ({ + color, + variant = ColorSwatchVariant.Small, + isSelected, + ...otherProps +}) => { + const isSmall = variant === ColorSwatchVariant.Small; + const swatchSize = isSmall ? '16px' : '32px'; + const swatchStyles = { + width: swatchSize, + height: swatchSize, + borderRadius: '50%', + background: `${color.variants.dark}`, + marginRight: isSmall ? '0px' : '8px', + boxShadow: isSelected ? `inset 0 0 0 2px ${color.variants.dark}, inset 0 0 0 4px white` : 'none', + cursor: isSelected ? 'default' : 'pointer' + }; + + return ( +
+
+ {variant === ColorSwatchVariant.Large && {upperFirst(color.hue)}} +
+ ); +}; + +const ColorsGroup = ({ + colors, + selectedColor, + onColorSelect, +}: { + colors: ColorDefinition[]; + selectedColor?: Color; + onColorSelect: ColorChangeHandler +}) => { + const primaryColor = find(colors, color => !!color.isPrimary); + + return ( +
+ {primaryColor && ( + onColorSelect(primaryColor)} + /> + )} +
+ {colors.map(color => !color.isPrimary && ( +
+ onColorSelect(color)} + /> +
+ ))} +
+
+ ); +}; + + +interface NamedColorsPickerProps { + selectedColor?: Color; + onChange: ColorChangeHandler; +} +const NamedColorsPicker = ({ selectedColor, onChange }: NamedColorsPickerProps) => { + const swatches: JSX.Element[] = []; + + ColorsPalete.forEach((colors, hue) => { + swatches.push( + <> + + + ); + }); + + return ( +
+ {swatches} +
+ ); +}; + +export default NamedColorsPicker; diff --git a/packages/grafana-ui/src/utils/colors.ts b/packages/grafana-ui/src/utils/colors.ts index 263d128aec4..65ee605c33b 100644 --- a/packages/grafana-ui/src/utils/colors.ts +++ b/packages/grafana-ui/src/utils/colors.ts @@ -9,7 +9,6 @@ export const ALERTING_COLOR = 'rgba(237, 46, 24, 1)'; export const NO_DATA_COLOR = 'rgba(150, 150, 150, 1)'; export const PENDING_COLOR = 'rgba(247, 149, 32, 1)'; export const REGION_FILL_ALPHA = 0.09; - export const colors = [ '#7EB26D', // 0: pale green '#EAB839', // 1: mustard diff --git a/packages/grafana-ui/src/utils/colorsPalette.test.ts b/packages/grafana-ui/src/utils/colorsPalette.test.ts new file mode 100644 index 00000000000..41a615410ef --- /dev/null +++ b/packages/grafana-ui/src/utils/colorsPalette.test.ts @@ -0,0 +1,31 @@ +import { getColorName, getColorDefinition, ColorsPalete, buildColorDefinition } from './colorsPalette'; + +describe('colors', () => { + const FakeBlue = buildColorDefinition('blue', 'blue', ['#0000ff', '#00000ee']); + + beforeAll(() => { + ColorsPalete.set('blue', [FakeBlue]) + }); + + describe('getColorDefinition', () => { + it('returns undefined for unknown hex', () => { + expect(getColorDefinition('#ff0000')).toBeUndefined(); + }); + + it('returns definition for known hex', () => { + expect(getColorDefinition(FakeBlue.variants.light)).toEqual(FakeBlue); + expect(getColorDefinition(FakeBlue.variants.dark)).toEqual(FakeBlue); + }); + }); + + describe('getColorName', () => { + it('returns undefined for unknown hex', () => { + expect(getColorName('#ff0000')).toBeUndefined(); + }); + + it('returns name for known hex', () => { + expect(getColorName(FakeBlue.variants.light)).toEqual(FakeBlue.name); + expect(getColorName(FakeBlue.variants.dark)).toEqual(FakeBlue.name); + }); + }); +}); diff --git a/packages/grafana-ui/src/utils/colorsPalette.ts b/packages/grafana-ui/src/utils/colorsPalette.ts new file mode 100644 index 00000000000..2f6300b1e18 --- /dev/null +++ b/packages/grafana-ui/src/utils/colorsPalette.ts @@ -0,0 +1,125 @@ +import { flatten, some, values } from 'lodash'; + +type Hue = 'green' | 'yellow' | 'red' | 'blue' | 'orange' | 'purple'; + +export type Color = + | 'green' + | 'dark-green' + | 'semi-dark-green' + | 'light-green' + | 'super-light-green' + | 'yellow' + | 'dark-yellow' + | 'semi-dark-yellow' + | 'light-yellow' + | 'super-light-yellow' + | 'red' + | 'dark-red' + | 'semi-dark-red' + | 'light-red' + | 'super-light-red' + | 'blue' + | 'dark-blue' + | 'semi-dark-blue' + | 'light-blue' + | 'super-light-blue' + | 'orange' + | 'dark-orange' + | 'semi-dark-orange' + | 'light-orange' + | 'super-light-orange' + | 'purple' + | 'dark-purple' + | 'semi-dark-purple' + | 'light-purple' + | 'super-light-purple'; + +type ThemeVariants = { + dark: string; + light: string; +}; +export type ColorDefinition = { + hue: Hue; + isPrimary?: boolean; + name: Color; + variants: ThemeVariants; +}; + +export const ColorsPalete = new Map(); + +export const buildColorDefinition = ( + hue: Hue, + name: Color, + [light, dark]: string[], + isPrimary?: boolean +): ColorDefinition => ({ + hue, + name, + variants: { + light, + dark, + }, + isPrimary: !!isPrimary, +}); + +export const BasicGreen = buildColorDefinition('green', 'green', ['#53A642', '#53A642'], true); +export const DarkGreen = buildColorDefinition('green', 'dark-green', ['#106100', '#106100']); +export const SemiDarkGreen = buildColorDefinition('green', 'semi-dark-green', ['#388729', '#388729']); +export const LightGreen = buildColorDefinition('green', 'light-green', ['#72C462', '#72C462']); +export const SuperLightGreen = buildColorDefinition('green', 'super-light-green', ['#99E68A', '#99E68A']); + +export const BasicYellow = buildColorDefinition('yellow', 'yellow', ['#F2CA00', '#F2CA00'], true); +export const DarkYellow = buildColorDefinition('yellow', 'dark-yellow', ['#A68A00', '#A68A00']); +export const SemiDarkYellow = buildColorDefinition('yellow', 'semi-dark-yellow', ['#CCAA00', '#CCAA00']); +export const LightYellow = buildColorDefinition('yellow', 'light-yellow', ['#F7D636', '#F7D636']); +export const SuperLightYellow = buildColorDefinition('yellow', 'super-light-yellow', ['#FFEB8A', '#FFEB8A']); + +export const BasicRed = buildColorDefinition('red', 'red', ['#F94462', '#F94462'], true); +export const DarkRed = buildColorDefinition('red', 'dark-red', ['#B3001D', '#B3001D']); +export const SemiDarkRed = buildColorDefinition('red', 'semi-dark-red', ['#D93651', '#D93651']); +export const LightRed = buildColorDefinition('red', 'light-red', ['#F07387', '#F07387']); +export const SuperLightRed = buildColorDefinition('red', 'super-light-red', ['#E6A1AC', '#E6A1AC']); + +export const BasicBlue = buildColorDefinition('blue', 'blue', ['#408BFF', '#408BFF'], true); +export const DarkBlue = buildColorDefinition('blue', 'dark-blue', ['#2155A6', '#2155A6']); +export const SemiDarkBlue = buildColorDefinition('blue', 'semi-dark-blue', ['#3471CF', '#3471CF']); +export const LightBlue = buildColorDefinition('blue', 'light-blue', ['#7DAEFA', '#7DAEFA']); +export const SuperLightBlue = buildColorDefinition('blue', 'super-light-blue', ['#B8D0F5', '#B8D0F5']); + +export const BasicOrange = buildColorDefinition('orange', 'orange', ['#FA6400', '#FA6400'], true); +export const DarkOrange = buildColorDefinition('orange', 'dark-orange', ['#963C00', '#963C00']); +export const SemiDarkOrange = buildColorDefinition('orange', 'semi-dark-orange', ['#ED4B00', '#ED4B00']); +export const LightOrange = buildColorDefinition('orange', 'light-orange', ['#FC934C', '#FC934C']); +export const SuperLightOrange = buildColorDefinition('orange', 'super-light-orange', ['#FFC299', '#FFC299']); + +export const BasicPurple = buildColorDefinition('purple', 'purple', ['#BC67E6', '#BC67E6'], true); +export const DarkPurple = buildColorDefinition('purple', 'dark-purple', ['#701F99', '#701F99']); +export const SemiDarkPurple = buildColorDefinition('purple', 'semi-dark-purple', ['#9E43CC', '#9E43CC']); +export const LightPurple = buildColorDefinition('purple', 'light-purple', ['#D19AED', '#D19AED']); +export const SuperLightPurple = buildColorDefinition('purple', 'super-light-purple', ['#E6CEF2', '#E6CEF2']); + +const greens = [BasicGreen, DarkGreen, SemiDarkGreen, LightGreen, SuperLightGreen]; +const yellows = [BasicYellow, DarkYellow, SemiDarkYellow, LightYellow, SuperLightYellow]; +const reds = [BasicRed, DarkRed, SemiDarkRed, LightRed, SuperLightRed]; +const blues = [BasicBlue, DarkBlue, SemiDarkBlue, LightBlue, SuperLightBlue]; +const oranges = [BasicOrange, DarkOrange, SemiDarkOrange, LightOrange, SuperLightOrange]; +const purples = [BasicPurple, DarkPurple, SemiDarkPurple, LightPurple, SuperLightPurple]; + +ColorsPalete.set('green', greens); +ColorsPalete.set('yellow', yellows); +ColorsPalete.set('red', reds); +ColorsPalete.set('blue', blues); +ColorsPalete.set('orange', oranges); +ColorsPalete.set('purple', purples); + +export const getColorDefinition = (hex: string): ColorDefinition | undefined => { + return flatten(Array.from(ColorsPalete.values())).filter(definition => + some(values(definition.variants), color => color === hex) + )[0]; +}; + +export const getColorName = (hex: string): Color | undefined => { + const definition = getColorDefinition(hex); + + return definition ? definition.name : undefined; +}; diff --git a/yarn.lock b/yarn.lock index 1aa0d7f20c3..32881e10817 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1673,7 +1673,7 @@ resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-1.10.35.tgz#4e5c2b1e5b3bf0b863efb8c5e70081f52e6c9518" integrity sha512-SVtqEcudm7yjkTwoRA1gC6CNMhGDdMx4Pg8BPdiqI7bXXdCn1BPmtxgeWYQOgDxrq53/5YTlhq5ULxBEAlWIBg== -"@types/lodash@4.14.119", "@types/lodash@^4.14.119": +"@types/lodash@^4.14.119": version "4.14.119" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39" integrity sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw== @@ -1735,7 +1735,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.7.6": +"@types/react@*", "@types/react@16.7.6", "@types/react@^16.7.6": version "16.7.6" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040" integrity sha512-QBUfzftr/8eg/q3ZRgf/GaDP6rTYc7ZNem+g4oZM38C9vXyV8AWRWaTQuW5yCoZTsfHrN7b3DeEiUnqH9SrnpA== @@ -4429,7 +4429,7 @@ caniuse-api@^1.5.2: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: +caniuse-db@1.0.30000772, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000772" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b" integrity sha1-UarokXaChureSj2DGep21qAbUSs= From 4384eb2f0b1c4d0b3a3dfcd1344790f4ec10fa27 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 17 Jan 2019 10:25:38 +0100 Subject: [PATCH 02/57] Move tooltip themes to Tooltip component making Popper/PopperController theme agnostic --- .../src/components/Tooltip/Popper.tsx | 17 +++++------------ .../src/components/Tooltip/PopperController.tsx | 7 +------ .../src/components/Tooltip/Tooltip.tsx | 13 ++++++++++++- .../src/components/Tooltip/_Tooltip.scss | 8 ++++---- .../features/dashboard/dashgrid/DataPanel.tsx | 2 +- .../dashboard/panel_editor/PanelEditor.tsx | 2 +- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/packages/grafana-ui/src/components/Tooltip/Popper.tsx b/packages/grafana-ui/src/components/Tooltip/Popper.tsx index eb64df1cb6e..c4b43005038 100644 --- a/packages/grafana-ui/src/components/Tooltip/Popper.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Popper.tsx @@ -4,11 +4,6 @@ import { Manager, Popper as ReactPopper } from 'react-popper'; import { Portal } from '@grafana/ui'; import Transition from 'react-transition-group/Transition'; -export enum Themes { - Default = 'popper__background--default', - Error = 'popper__background--error', - Brand = 'popper__background--brand', -} const defaultTransitionStyles = { transition: 'opacity 200ms linear', @@ -22,22 +17,20 @@ const transitionStyles: {[key: string]: object} = { exiting: { opacity: 0 }, }; -interface Props extends React.DOMAttributes { +interface Props extends React.HTMLAttributes { renderContent: (content: any) => any; show: boolean; placement?: PopperJS.Placement; content: string | ((props: any) => JSX.Element); referenceElement: PopperJS.ReferenceObject; - theme?: Themes; + } class Popper extends PureComponent { render() { - const { renderContent, show, placement, onMouseEnter, onMouseLeave, theme } = this.props; + const { renderContent, show, placement, onMouseEnter, onMouseLeave, className } = this.props; const { content } = this.props; - const popperBackgroundClassName = 'popper__background' + (theme ? ' ' + theme : ''); - return ( @@ -56,9 +49,9 @@ class Popper extends PureComponent { ...transitionStyles[transitionState], }} data-placement={placement} - className="popper" + className={`popper`} > -
+
{renderContent(content)}
diff --git a/packages/grafana-ui/src/components/Tooltip/PopperController.tsx b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx index 5f4010ac58a..5eadda46188 100644 --- a/packages/grafana-ui/src/components/Tooltip/PopperController.tsx +++ b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx @@ -1,6 +1,5 @@ import React from 'react'; import * as PopperJS from 'popper.js'; -import { Themes } from './Popper'; type PopperContent = string | (() => JSX.Element); @@ -10,7 +9,6 @@ export interface UsingPopperProps { content: PopperContent; children: JSX.Element; renderContent?: (content: PopperContent) => JSX.Element; - theme?: Themes; } type PopperControllerRenderProp = ( @@ -21,7 +19,6 @@ type PopperControllerRenderProp = ( placement: PopperJS.Placement; content: string | ((props: any) => JSX.Element); renderContent: (content: any) => any; - theme?: Themes; } ) => JSX.Element; @@ -30,7 +27,6 @@ interface Props { content: PopperContent; className?: string; children: PopperControllerRenderProp; - theme?: Themes; } interface State { @@ -83,7 +79,7 @@ class PopperController extends React.Component { } render() { - const { children, content, theme } = this.props; + const { children, content } = this.props; const { show, placement } = this.state; return children(this.showPopper, this.hidePopper, { @@ -91,7 +87,6 @@ class PopperController extends React.Component { placement, content, renderContent: this.renderContent, - theme, }); } } diff --git a/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx index 9cffb151d83..96d29057837 100644 --- a/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx @@ -3,8 +3,18 @@ import * as PopperJS from 'popper.js'; import Popper from './Popper'; import PopperController, { UsingPopperProps } from './PopperController'; -export const Tooltip = ({ children, renderContent, ...controllerProps }: UsingPopperProps) => { +export enum Themes { + Default = 'popper__background--default', + Error = 'popper__background--error', + Brand = 'popper__background--brand', +} + +interface TooltipProps extends UsingPopperProps { + theme?: Themes; +} +export const Tooltip = ({ children, renderContent, theme, ...controllerProps }: TooltipProps) => { const tooltipTriggerRef = createRef(); + const popperBackgroundClassName = 'popper__background' + (theme ? ' ' + theme : ''); return ( @@ -17,6 +27,7 @@ export const Tooltip = ({ children, renderContent, ...controllerProps }: UsingPo onMouseEnter={showPopper} onMouseLeave={hidePopper} referenceElement={tooltipTriggerRef.current} + className={popperBackgroundClassName} /> )} {React.cloneElement(children, { diff --git a/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss b/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss index c8fa099cce6..ad56328d816 100644 --- a/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss +++ b/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss @@ -1,6 +1,5 @@ $popper-margin-from-ref: 5px; - @mixin popper-theme($backgroundColor, $arrowColor) { background: $backgroundColor; .popper__arrow { @@ -22,6 +21,10 @@ $popper-margin-from-ref: 5px; box-shadow: 0 0 2px rgba(0, 0, 0, 0.5); padding: 10px; + .popper__arrow { + border-color: $tooltipBackground; + } + // Themes &.popper__background--error { @include popper-theme($tooltipBackgroundError, $tooltipBackgroundError); @@ -41,9 +44,6 @@ $popper-margin-from-ref: 5px; margin: 0px; } -.popper__arrow { - border-color: $tooltipBackground; -} // Top .popper[data-placement^='top'] { diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index d4f6859f1b6..753ef2964cd 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -1,7 +1,7 @@ // Library import React, { Component } from 'react'; import { Tooltip } from '@grafana/ui'; -import { Themes } from '@grafana/ui/src/components/Tooltip/Popper'; +import { Themes } from '@grafana/ui/src/components/Tooltip/Tooltip'; import ErrorBoundary from 'app/core/components/ErrorBoundary/ErrorBoundary'; diff --git a/public/app/features/dashboard/panel_editor/PanelEditor.tsx b/public/app/features/dashboard/panel_editor/PanelEditor.tsx index a09ff66f114..3588f7534b7 100644 --- a/public/app/features/dashboard/panel_editor/PanelEditor.tsx +++ b/public/app/features/dashboard/panel_editor/PanelEditor.tsx @@ -16,7 +16,7 @@ import { DashboardModel } from '../dashboard_model'; import { PanelPlugin } from 'app/types/plugins'; import { Tooltip } from '@grafana/ui'; -import { Themes } from '@grafana/ui/src/components/Tooltip/Popper'; +import { Themes } from '@grafana/ui/src/components/Tooltip/Tooltip'; interface PanelEditorProps { panel: PanelModel; From c74b39696c68bffe8cc298284fa26c7330a1b4ab Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 09:43:47 +0100 Subject: [PATCH 03/57] Updates to Popper to be positions correctly within window --- .../src/components/Tooltip/Popper.tsx | 21 +++++++++++++------ .../components/Tooltip/PopperController.tsx | 1 + .../src/components/Tooltip/Tooltip.tsx | 1 + 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/grafana-ui/src/components/Tooltip/Popper.tsx b/packages/grafana-ui/src/components/Tooltip/Popper.tsx index c4b43005038..17955d346b5 100644 --- a/packages/grafana-ui/src/components/Tooltip/Popper.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Popper.tsx @@ -4,13 +4,12 @@ import { Manager, Popper as ReactPopper } from 'react-popper'; import { Portal } from '@grafana/ui'; import Transition from 'react-transition-group/Transition'; - const defaultTransitionStyles = { transition: 'opacity 200ms linear', opacity: 0, }; -const transitionStyles: {[key: string]: object} = { +const transitionStyles: { [key: string]: object } = { exited: { opacity: 0 }, entering: { opacity: 0 }, entered: { opacity: 1 }, @@ -23,12 +22,12 @@ interface Props extends React.HTMLAttributes { placement?: PopperJS.Placement; content: string | ((props: any) => JSX.Element); referenceElement: PopperJS.ReferenceObject; - + arrowClassName?: string; } class Popper extends PureComponent { render() { - const { renderContent, show, placement, onMouseEnter, onMouseLeave, className } = this.props; + const { renderContent, show, placement, onMouseEnter, onMouseLeave, className, arrowClassName } = this.props; const { content } = this.props; return ( @@ -36,7 +35,12 @@ class Popper extends PureComponent { {transitionState => ( - + {({ ref, style, placement, arrowProps }) => { return (
{ >
{renderContent(content)} -
+
); diff --git a/packages/grafana-ui/src/components/Tooltip/PopperController.tsx b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx index 5eadda46188..5cacb5b8cea 100644 --- a/packages/grafana-ui/src/components/Tooltip/PopperController.tsx +++ b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx @@ -70,6 +70,7 @@ class PopperController extends React.Component { }; renderContent(content: PopperContent) { + if (typeof content === 'function') { // If it's a function we assume it's a React component const ReactComponent = content; diff --git a/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx index 96d29057837..343672721ec 100644 --- a/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx @@ -28,6 +28,7 @@ export const Tooltip = ({ children, renderContent, theme, ...controllerProps }: onMouseLeave={hidePopper} referenceElement={tooltipTriggerRef.current} className={popperBackgroundClassName} + arrowClassName={'popper__arrow'} /> )} {React.cloneElement(children, { From 33fa40a1f36364ed1cd43c2f5f68790052ba9476 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 09:47:39 +0100 Subject: [PATCH 04/57] Migrating color pickers to Popper from drop.js pt1 --- .../components/ColorPicker/ColorPicker.tsx | 74 +++------ .../ColorPicker/ColorPickerPopover.tsx | 143 +++++++----------- .../ColorPicker/NamedColorsPicker.tsx | 22 +-- .../ColorPicker/SeriesColorPicker.tsx | 101 ++++++------- .../ColorPicker/SeriesColorPickerPopover.tsx | 11 +- .../components/ColorPicker/SpectrumPicker.tsx | 2 +- .../components/ColorPicker/_ColorPicker.scss | 51 +++++++ packages/grafana-ui/src/types/index.ts | 5 + public/app/core/time_series2.ts | 12 +- 9 files changed, 207 insertions(+), 214 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 682d2f1c605..c2697b13e7d 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -1,69 +1,45 @@ -import React, { Component } from 'react'; -import ReactDOM from 'react-dom'; -import Drop from 'tether-drop'; +import React, { Component, createRef } from 'react'; +import PopperController from '../Tooltip/PopperController'; +import Popper from '../Tooltip/Popper'; import { ColorPickerPopover } from './ColorPickerPopover'; -import { Color } from '../../utils/colorsPalette'; +import { ColorDefinition } from '../../utils/colorsPalette'; interface Props { - /** - * Value to display, either empty (" ") or "X" / "O". - * - * @default " " - **/ - name?: Color; color: string; onChange: (c: string) => void; } export class ColorPicker extends Component { + private pickerTriggerRef = createRef(); pickerElem: HTMLElement | null; colorPickerDrop: any; - openColorPicker = () => { - const dropContent = ; - - const dropContentElem = document.createElement('div'); - ReactDOM.render(dropContent, dropContentElem); - - const drop = new Drop({ - target: this.pickerElem as Element, - content: dropContentElem, - position: 'top center', - classes: 'drop-popover', - openOn: 'click', - hoverCloseDelay: 200, - tetherOptions: { - constraints: [{ to: 'scrollParent', attachment: 'none both' }], - attachment: 'bottom center', - }, - }); - - drop.on('close', this.closeColorPicker); - - this.colorPickerDrop = drop; - this.colorPickerDrop.open(); + onColorSelect = (color: ColorDefinition) => { + this.props.onChange(color.name); }; - closeColorPicker = () => { - setTimeout(() => { - if (this.colorPickerDrop && this.colorPickerDrop.tether) { - this.colorPickerDrop.destroy(); - } - }, 100); - }; - - onColorSelect = (color: string) => { - this.props.onChange(color); + renderPickerTabs = () => { + return {}} />; }; render() { - return ( -
(this.pickerElem = element)}> -
-
-
-
+ + {(showPopper, hidePopper, popperProps) => { + return ( + <> + {this.pickerTriggerRef.current && ( + + )} +
+
+
+
+
+ + ); + }} + ); } } diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index e8305c99319..70dca7fa667 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -1,111 +1,76 @@ -import React from 'react'; -import $ from 'jquery'; -import tinycolor from 'tinycolor2'; -import { ColorPalette } from './ColorPalette'; +import React, { Children } from 'react'; +import NamedColorsPicker from './NamedColorsPicker'; +import { Color } from 'csstype'; +import { ColorDefinition, getColorName } from '../..//utils/colorsPalette'; import { SpectrumPicker } from './SpectrumPicker'; +import { GrafanaTheme } from '../../types'; -const DEFAULT_COLOR = '#000000'; +// const DEFAULT_COLOR = '#000000'; export interface Props { - color: string; - onColorSelect: (c: string) => void; + color: Color | string; + theme?: GrafanaTheme; + onColorSelect: (color: string | ColorDefinition) => void; } -export class ColorPickerPopover extends React.Component { - pickerNavElem: any; +type PickerType = 'palette' | 'spectrum'; +interface State { + activePicker: PickerType; +} + +export class ColorPickerPopover extends React.Component { constructor(props: Props) { super(props); this.state = { - tab: 'palette', - color: this.props.color || DEFAULT_COLOR, - colorString: this.props.color || DEFAULT_COLOR, + activePicker: 'spectrum', }; } - setPickerNavElem(elem: any) { - this.pickerNavElem = $(elem); - } + handleSpectrumColorSelect = (color: any) => { + this.props.onColorSelect(color.toRgbString()); + }; - setColor(color: string) { - const newColor = tinycolor(color); - if (newColor.isValid()) { - this.setState({ color: newColor.toString(), colorString: newColor.toString() }); - this.props.onColorSelect(color); - } - } + renderPicker = () => { + const { activePicker } = this.state; + const { color } = this.props; - sampleColorSelected(color: string) { - this.setColor(color); - } - - spectrumColorSelected(color: any) { - const rgbColor = color.toRgbString(); - this.setColor(rgbColor); - } - - onColorStringChange(e: any) { - const colorString = e.target.value; - this.setState({ colorString: colorString }); - - const newColor = tinycolor(colorString); - if (newColor.isValid()) { - // Update only color state - const newColorString = newColor.toString(); - this.setState({ color: newColorString }); - this.props.onColorSelect(newColorString); - } - } - - onColorStringBlur(e: any) { - const colorString = e.target.value; - this.setColor(colorString); - } - - componentDidMount() { - this.pickerNavElem.find('li:first').addClass('active'); - this.pickerNavElem.on('show', (e: any) => { - // use href attr (#name => name) - const tab = e.target.hash.slice(1); - this.setState({ tab: tab }); - }); - } + return activePicker === 'spectrum' ? ( + + ) : ( + + ); + }; render() { - const paletteTab = ( -
- -
- ); - const spectrumTab = ( -
- -
- ); - const currentTab = this.state.tab === 'palette' ? paletteTab : spectrumTab; + const { activePicker } = this.state; + const { theme, children } = this.props; + const colorPickerTheme = theme || GrafanaTheme.Dark; return ( -
- -
{currentTab}
-
- +
+
+
{ + this.setState({ activePicker: 'palette' }); + }} + > + Default +
+
{ + this.setState({ activePicker: 'spectrum' }); + }} + > + Custom +
+
+ +
+ {this.renderPicker()} + {children}
); diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx index a0732e4e21d..f7cd229dca9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx @@ -47,19 +47,22 @@ const ColorSwatch: FunctionComponent = ({ ); }; -const ColorsGroup = ({ +interface ColorsGroupProps { + colors: ColorDefinition[]; + selectedColor?: Color; + onColorSelect: ColorChangeHandler; + key?: string; +} +const ColorsGroup: FunctionComponent = ({ colors, selectedColor, onColorSelect, -}: { - colors: ColorDefinition[]; - selectedColor?: Color; - onColorSelect: ColorChangeHandler + ...otherProps }) => { const primaryColor = find(colors, color => !!color.isPrimary); return ( -
+
{primaryColor && ( {colors.map(color => !color.isPrimary && ( -
+
{ swatches.push( - <> - - + ); }); diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx index 7c3848f6868..40487386a68 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx @@ -1,7 +1,9 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Drop from 'tether-drop'; +import React, { createRef } from 'react'; +import * as PopperJS from 'popper.js'; import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; +import PopperController from '../Tooltip/PopperController'; +import Popper from '../Tooltip/Popper'; +import { GrafanaTheme } from '../../types'; export interface SeriesColorPickerProps { color: string; @@ -9,10 +11,12 @@ export interface SeriesColorPickerProps { optionalClass?: string; onColorChange: (newColor: string) => void; onToggleAxis?: () => void; + children: JSX.Element; + theme?: GrafanaTheme; } export class SeriesColorPicker extends React.Component { - pickerElem: any; + private pickerTriggerRef = createRef(); colorPickerDrop: any; static defaultProps = { @@ -21,65 +25,46 @@ export class SeriesColorPicker extends React.Component { onToggleAxis: () => {}, }; - constructor(props: SeriesColorPickerProps) { - super(props); - } - - componentWillUnmount() { - this.destroyDrop(); - } - - onClickToOpen = () => { - if (this.colorPickerDrop) { - this.destroyDrop(); - } - - const { color, yaxis, onColorChange, onToggleAxis } = this.props; - const dropContent = ( - + renderPickerTabs = () => { + const { color, yaxis, onColorChange, onToggleAxis, theme } = this.props; + return ( + ); - const dropContentElem = document.createElement('div'); - ReactDOM.render(dropContent, dropContentElem); - - const drop = new Drop({ - target: this.pickerElem, - content: dropContentElem, - position: 'bottom center', - classes: 'drop-popover', - openOn: 'hover', - hoverCloseDelay: 200, - remove: true, - tetherOptions: { - constraints: [{ to: 'scrollParent', attachment: 'none both' }], - attachment: 'bottom center', - }, - }); - - drop.on('close', this.closeColorPicker.bind(this)); - - this.colorPickerDrop = drop; - this.colorPickerDrop.open(); }; - closeColorPicker() { - setTimeout(() => { - this.destroyDrop(); - }, 100); - } - - destroyDrop() { - if (this.colorPickerDrop && this.colorPickerDrop.tether) { - this.colorPickerDrop.destroy(); - this.colorPickerDrop = null; - } - } - render() { - const { optionalClass, children } = this.props; + const { children } = this.props; + return ( -
(this.pickerElem = e)} onClick={this.onClickToOpen}> - {children} -
+ + {(showPopper, hidePopper, popperProps) => { + return ( + <> + {this.pickerTriggerRef.current && ( + + )} + {React.cloneElement(children, { + ref: this.pickerTriggerRef, + onClick: showPopper, + onMouseLeave: hidePopper, + })} + + ); + }} + ); } } diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx index 541a77ddabc..3f4fcc122a3 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx @@ -1,19 +1,24 @@ import React from 'react'; import { ColorPickerPopover } from './ColorPickerPopover'; +import { GrafanaTheme } from '../../types'; export interface SeriesColorPickerPopoverProps { color: string; yaxis?: number; onColorChange: (color: string) => void; onToggleAxis?: () => void; + theme?: GrafanaTheme; } export class SeriesColorPickerPopover extends React.PureComponent { render() { return ( -
- {this.props.yaxis && } - +
+ +
+ {this.props.yaxis && } +
+
); } diff --git a/packages/grafana-ui/src/components/ColorPicker/SpectrumPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/SpectrumPicker.tsx index a225db09046..98b3fc2feec 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SpectrumPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SpectrumPicker.tsx @@ -6,7 +6,7 @@ import '../../vendor/spectrum'; export interface Props { color: string; options: object; - onColorSelect: (c: string) => void; + onColorSelect: (color: string) => void; } export class SpectrumPicker extends React.Component { diff --git a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss index c0643342307..1a9da6c602d 100644 --- a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss +++ b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss @@ -1,3 +1,54 @@ +.ColorPicker { + .popper__arrow { + border-color: #f7f8fa; + } +} + +.ColorPickerPopover { + border-radius: 3px; +} + +.ColorPickerPopover--light { + color: black; + background: linear-gradient(180deg, #ffffff 0%, #f7f8fa 104.25%); + box-shadow: 0px 2px 4px #dde4ed, 0px 0px 2px #dde4ed; +} + +.ColorPickerPopover--dark { + color: #d8d9da; + background: linear-gradient(180deg, #1e2028 0%, #161719 104.25%); + box-shadow: 0px 2px 4px #000000, 0px 0px 2px #000000; + + .ColorPickerPopover__tab { + background: #303133; + color: white; + } + .ColorPickerPopover__tab--active { + background: none; + } +} + +.ColorPickerPopover__content { + width: 360px; + padding: 32px; +} + +.ColorPickerPopover__tabs { + display: flex; + width: 100%; +} + +.ColorPickerPopover__tab { + width: 50%; + text-align: center; + padding: 8px 0; + background: #dde4ed; + border-radius: 3px; +} + +.ColorPickerPopover__tab--active { + background: white; +} .sp-replacer { background: inherit; border: none; diff --git a/packages/grafana-ui/src/types/index.ts b/packages/grafana-ui/src/types/index.ts index eaa39fb080b..1b1d764e304 100644 --- a/packages/grafana-ui/src/types/index.ts +++ b/packages/grafana-ui/src/types/index.ts @@ -3,3 +3,8 @@ export * from './time'; export * from './panel'; export * from './plugin'; export * from './datasource'; + +export enum GrafanaTheme { + Light = 'light', + Dark = 'dark', +} diff --git a/public/app/core/time_series2.ts b/public/app/core/time_series2.ts index 76af75d0935..9d586a0b4b5 100644 --- a/public/app/core/time_series2.ts +++ b/public/app/core/time_series2.ts @@ -1,6 +1,7 @@ import kbn from 'app/core/utils/kbn'; import { getFlotTickDecimals } from 'app/core/utils/ticks'; import _ from 'lodash'; +import { ColorDefinition } from '@grafana/ui/src/utils/colorsPalette'; function matchSeriesOverride(aliasOrRegex, seriesAlias) { if (!aliasOrRegex) { @@ -356,8 +357,13 @@ export default class TimeSeries { return false; } - setColor(color) { - this.color = color; - this.bars.fillColor = color; + setColor(color: string | ColorDefinition) { + if (typeof color === 'string') { + this.color = color; + this.bars.fillColor = color; + } else { + this.color = color.variants.dark; + this.bars.fillColor = color.variants.dark; + } } } From 4b9e933691dc23a983843e8aa17163fe56de4da3 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 09:50:29 +0100 Subject: [PATCH 05/57] Added config provider to be able to access config easily from react components --- public/app/core/utils/ConfigProvider.tsx | 28 ++++++++++++++++++++++++ public/app/core/utils/react2angular.ts | 3 ++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 public/app/core/utils/ConfigProvider.tsx diff --git a/public/app/core/utils/ConfigProvider.tsx b/public/app/core/utils/ConfigProvider.tsx new file mode 100644 index 00000000000..6883401ad27 --- /dev/null +++ b/public/app/core/utils/ConfigProvider.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import config, { Settings } from 'app/core/config'; +import { GrafanaTheme } from '@grafana/ui'; + +export const ConfigContext = React.createContext(config); +export const ConfigConsumer = ConfigContext.Consumer; + +export const provideConfig = (component: React.ComponentType) => { + const ConfigProvider = (props: any) => ( + {React.createElement(component, { ...props })} + ); + + return ConfigProvider; +}; + +interface ThemeProviderProps { + children: (theme: GrafanaTheme) => JSX.Element; +} + +export const ThemeProvider = ({ children }: ThemeProviderProps) => { + return ( + + {({ bootData }) => { + return children(bootData.user.lightTheme ? GrafanaTheme.Light : GrafanaTheme.Dark); + }} + + ); +}; diff --git a/public/app/core/utils/react2angular.ts b/public/app/core/utils/react2angular.ts index 6dc1803133a..1057f68fcda 100644 --- a/public/app/core/utils/react2angular.ts +++ b/public/app/core/utils/react2angular.ts @@ -1,10 +1,11 @@ import coreModule from 'app/core/core_module'; +import { provideConfig } from 'app/core/utils/ConfigProvider'; export function react2AngularDirective(name: string, component: any, options: any) { coreModule.directive(name, [ 'reactDirective', reactDirective => { - return reactDirective(component, options); + return reactDirective(provideConfig(component), options); }, ]); } From 5420a816e72d0d5a1035bd0610920d5fbb892521 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 09:50:52 +0100 Subject: [PATCH 06/57] Render series color picker with correct theme --- .../panel/graph/Legend/LegendSeriesItem.tsx | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx b/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx index d6df17d9699..4ef07d19212 100644 --- a/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx +++ b/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx @@ -2,6 +2,8 @@ import React, { PureComponent } from 'react'; import classNames from 'classnames'; import { TimeSeries } from 'app/core/core'; import { SeriesColorPicker } from '@grafana/ui'; +import { ThemeProvider } from 'app/core/utils/ConfigProvider'; +import { GrafanaTheme } from '@grafana/ui/'; export const LEGEND_STATS = ['min', 'max', 'avg', 'current', 'total']; @@ -154,8 +156,8 @@ interface LegendSeriesIconState { color: string; } -function SeriesIcon(props) { - return ; +function SeriesIcon({ color }) { + return ; } class LegendSeriesIcon extends PureComponent { @@ -167,15 +169,24 @@ class LegendSeriesIcon extends PureComponent - - + + {theme => { + return ( + + + + + + ); + }} + ); } } From 3d2955f24f17402423b267e8961a070d0ad13be8 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 09:51:08 +0100 Subject: [PATCH 07/57] Lint fix --- packages/grafana-ui/src/utils/colorsPalette.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/utils/colorsPalette.test.ts b/packages/grafana-ui/src/utils/colorsPalette.test.ts index 41a615410ef..c15a56ea192 100644 --- a/packages/grafana-ui/src/utils/colorsPalette.test.ts +++ b/packages/grafana-ui/src/utils/colorsPalette.test.ts @@ -4,7 +4,7 @@ describe('colors', () => { const FakeBlue = buildColorDefinition('blue', 'blue', ['#0000ff', '#00000ee']); beforeAll(() => { - ColorsPalete.set('blue', [FakeBlue]) + ColorsPalete.set('blue', [FakeBlue]); }); describe('getColorDefinition', () => { From 0505add7f6621f60a5b3cf3ad07e0b793d9b5975 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 09:51:34 +0100 Subject: [PATCH 08/57] Enabled knobs for storybook and implemented some stories --- packages/grafana-ui/.storybook/addons.ts | 1 + packages/grafana-ui/package.json | 4 + .../ColorPicker/ColorPicker.story.tsx | 18 +-- .../ColorPicker/ColorPickerPopover.story.tsx | 44 ++++++ .../ColorPicker/ColorPickerPopover.tsx | 2 +- .../ColorPicker/NamedColorsPicker.story.tsx | 86 ++++++++---- yarn.lock | 132 ++++++++++++++++-- 7 files changed, 236 insertions(+), 51 deletions(-) create mode 100644 packages/grafana-ui/.storybook/addons.ts create mode 100644 packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx diff --git a/packages/grafana-ui/.storybook/addons.ts b/packages/grafana-ui/.storybook/addons.ts new file mode 100644 index 00000000000..6622fe475d5 --- /dev/null +++ b/packages/grafana-ui/.storybook/addons.ts @@ -0,0 +1 @@ +import '@storybook/addon-knobs/register'; diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 59dabef86f7..6950a824206 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -12,11 +12,13 @@ "license": "ISC", "dependencies": { "@torkelo/react-select": "2.1.1", + "@types/react-color": "^2.14.0", "classnames": "^2.2.5", "jquery": "^3.2.1", "lodash": "^4.17.10", "moment": "^2.22.2", "react": "^16.6.3", + "react-color": "^2.17.0", "react-custom-scrollbars": "^4.2.1", "react-dom": "^16.6.3", "react-highlight-words": "0.11.0", @@ -29,6 +31,7 @@ }, "devDependencies": { "@storybook/addon-info": "^4.1.6", + "@storybook/addon-knobs": "^4.1.7", "@storybook/react": "^4.1.4", "@types/classnames": "^2.2.6", "@types/jest": "^23.3.2", @@ -40,6 +43,7 @@ "@types/react-test-renderer": "^16.0.3", "@types/react-transition-group": "^2.0.15", "@types/storybook__addon-info": "^3.4.2", + "@types/storybook__addon-knobs": "^4.0.0", "@types/storybook__react": "^4.0.0", "@types/tether-drop": "^1.4.8", "@types/tinycolor2": "^1.4.1", diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx index 6e029f3cfa8..1ee4b18d0b2 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx @@ -1,6 +1,7 @@ import React, { FunctionComponent } from 'react'; import { storiesOf } from '@storybook/react'; -import { ColorPickerPopover } from './ColorPickerPopover'; +import { ColorPicker } from './ColorPicker'; +import { withKnobs, text } from '@storybook/addon-knobs'; const CenteredStory: FunctionComponent<{}> = ({ children }) => { return ( @@ -17,11 +18,10 @@ const CenteredStory: FunctionComponent<{}> = ({ children }) => { ); }; -storiesOf('UI/ColorPicker', module) - .addDecorator(story => {story()}) - // .add('Color picker popover', () => { - // return {}} />; - // }) - .add('Named colors swatch', () => { - return {}} />; - }); +const ColorPickerStories = storiesOf('UI/ColorPicker', module); + +ColorPickerStories.addDecorator(story => {story()}); +ColorPickerStories.addDecorator(withKnobs); +ColorPickerStories.add('Color picker', () => { + return {}} />; +}); diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx new file mode 100644 index 00000000000..cfbbd6f645a --- /dev/null +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx @@ -0,0 +1,44 @@ +import React, { FunctionComponent } from 'react'; +import { storiesOf } from '@storybook/react'; +import { ColorPickerPopover } from './ColorPickerPopover'; +import { withKnobs, select } from '@storybook/addon-knobs'; +import { GrafanaTheme } from '../../types'; + +const CenteredStory: FunctionComponent<{}> = ({ children }) => { + return ( +
+ {children} +
+ ); +}; + +storiesOf('UI/ColorPickerPopover', module) + .addDecorator(story => {story()}) + .addDecorator(withKnobs) + .add('default', () => { + const selectedTheme = select( + 'Theme', + { + Default: '', + Light: GrafanaTheme.Light, + Dark: GrafanaTheme.Dark, + }, + GrafanaTheme.Light + ); + return ( + { + console.log(color); + }} + theme={selectedTheme || undefined} + /> + ); + }); diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index 70dca7fa667..9592d6c1ead 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -1,4 +1,4 @@ -import React, { Children } from 'react'; +import React from 'react'; import NamedColorsPicker from './NamedColorsPicker'; import { Color } from 'csstype'; import { ColorDefinition, getColorName } from '../..//utils/colorsPalette'; diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx index 1cdf01a2dd3..3d51f4b0347 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx @@ -1,7 +1,8 @@ import React, { FunctionComponent } from 'react'; import { storiesOf } from '@storybook/react'; -import NamedColorsPicker from './NamedColorsPicker'; +import NamedColorsPicker from './NamedColorsPicker'; import { Color, getColorName } from '@grafana/ui/src/utils/colorsPalette'; +import { withKnobs, select } from '@storybook/addon-knobs'; const CenteredStory: FunctionComponent<{}> = ({ children }) => { return ( @@ -23,46 +24,69 @@ interface StateHolderProps { children: (currentState: T, updateState: (nextState: T) => void) => JSX.Element; } -class UseState extends React.Component, {value: T}> { +class UseState extends React.Component, { value: T }> { constructor(props: StateHolderProps) { - super(props) + super(props); this.state = { - value: props.initialState - } + value: props.initialState, + }; } + static getDerivedStateFromProps(props: StateHolderProps<{}>, state: { value: {} }) { + return { + value: props.initialState, + }; + } + handleStateUpdate = (nextState: T) => { - this.setState({value: nextState}) - } + this.setState({ value: nextState }); + }; render() { - return this.props.children(this.state.value, this.handleStateUpdate) + return this.props.children(this.state.value, this.handleStateUpdate); } } -storiesOf('UI/ColorPicker', module) +storiesOf('UI/NamedColorPicker', module) + .addDecorator(withKnobs) .addDecorator(story => {story()}) .add('Named colors swatch - support for named colors', () => { - return( - - {(selectedColor, updateSelectedColor) => { - return ( - { updateSelectedColor(color.name);}} - /> - ) - }} - ); + const selectedColor = select( + 'Selected color', + { + Green: 'green', + Red: 'red', + 'Light blue': 'light-blue', + }, + 'green' + ); + + return ( + + {(selectedColor, updateSelectedColor) => { + console.log(selectedColor); + return ( + { + // @ts-ignore + updateSelectedColor((color).name); + }} + /> + ); + }} + + ); }) .add('Named colors swatch - support for hex values', () => { - return( - - {(selectedColor, updateSelectedColor) => { - return ( - updateSelectedColor(color.variants.dark)} - /> - ) - }} - ); + return ( + + {(selectedColor, updateSelectedColor) => { + return ( + updateSelectedColor(color.variants.dark)} + /> + ); + }} + + ); }); diff --git a/yarn.lock b/yarn.lock index 32881e10817..a8cc15572c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1031,6 +1031,11 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.1.3.tgz#b700d97385fa91affed60c71dfd51c67e9dad762" integrity sha512-QsYGKdhhuDFNq7bjm2r44y0mp5xW3uO3csuTPDWZc0OIiMQv+AIY5Cqwd4mJiC5N8estVl7qlvOx1hbtOuUWbw== +"@icons/material@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8" + integrity sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw== + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -1085,6 +1090,26 @@ react-lifecycles-compat "^3.0.4" util-deprecate "^1.0.2" +"@storybook/addon-knobs@^4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-4.1.7.tgz#298a1bd17f177e4653b073c9bca70c073746daea" + integrity sha512-h2YSyZvz+KQIggAKttdTZvRpsCVQUJtyY9mzVHEFDKTccAlC9WjZPD9UDSHYQZKm+0gHtFkxOidFTcjt/5r+8A== + dependencies: + "@emotion/styled" "^0.10.6" + "@storybook/addons" "4.1.7" + "@storybook/components" "4.1.7" + "@storybook/core-events" "4.1.7" + copy-to-clipboard "^3.0.8" + core-js "^2.5.7" + escape-html "^1.0.3" + fast-deep-equal "^2.0.1" + global "^4.3.2" + prop-types "^15.6.2" + qs "^6.5.2" + react-color "^2.14.1" + react-lifecycles-compat "^3.0.4" + util-deprecate "^1.0.2" + "@storybook/addons@4.1.6": version "4.1.6" resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-4.1.6.tgz#458c4c6baf8b2acaffb9ced705a8db224bd874b6" @@ -1095,6 +1120,16 @@ global "^4.3.2" util-deprecate "^1.0.2" +"@storybook/addons@4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-4.1.7.tgz#59ca76fff6594ff196bc58135689d1597cb90a1b" + integrity sha512-psKz/uMlImHkuUqdYaEq84Kyh3VIcSo00yHsnpWeq0xv3v6ONQKrFZ0bdmuZT30XQC0sYvXaP+YOKRq/hdgWaQ== + dependencies: + "@storybook/channels" "4.1.7" + "@storybook/components" "4.1.7" + global "^4.3.2" + util-deprecate "^1.0.2" + "@storybook/channel-postmessage@4.1.6": version "4.1.6" resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-4.1.6.tgz#ff7af1c0ee1ab72ee8f10ec2e29cd413c39fc14d" @@ -1109,6 +1144,11 @@ resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-4.1.6.tgz#52b56f5c94f58442aba2d9c5919bb00ff33475c5" integrity sha512-8MqGYypdaPmZR7eORXdxtJijGOz5UMHXoMskVtodvKi26tmltFKX+okXFNh/teKe3+8s0QWkpzM95VI+Z+0qFA== +"@storybook/channels@4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-4.1.7.tgz#bf3412859cca8cd3d221afcd68de348645dbb329" + integrity sha512-UVLrCcQ8f52PQYdWh97XcWBdqiXKBznePbd4L36//C6cIJkfwQ5nK9UDzzrCP/tlCnolVxnqRlorns4YeJcrSg== + "@storybook/client-logger@4.1.6": version "4.1.6" resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-4.1.6.tgz#3409c20467abe4f98751db9a8d6769c21d0c80e5" @@ -1130,11 +1170,32 @@ react-textarea-autosize "^7.0.4" render-fragment "^0.1.1" +"@storybook/components@4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-4.1.7.tgz#5e7f8247aebe0b79246c19f580e520cc59a39a1d" + integrity sha512-PgEREFw58tlyzzGbA7q1WlXgOLk6Dd+qkHZYJ+4JlTAl6kPdjirfRshg0uyvTVy5jGbtDa5mhN7Fo1TtuYiyDw== + dependencies: + "@emotion/core" "^0.13.1" + "@emotion/provider" "^0.11.2" + "@emotion/styled" "^0.10.6" + global "^4.3.2" + lodash "^4.17.11" + prop-types "^15.6.2" + react-inspector "^2.3.0" + react-split-pane "^0.1.84" + react-textarea-autosize "^7.0.4" + render-fragment "^0.1.1" + "@storybook/core-events@4.1.6": version "4.1.6" resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-4.1.6.tgz#6c147b171a04cee3ed66990ebdf45be45dc658ba" integrity sha512-07ki5+VuruWQv7B1ZBlsNYEVSC3dQwIZKjEFL4aKFO57ruaNijkZTF1QHkSGJapyBPa7+LLM2fXqnBkputoEZw== +"@storybook/core-events@4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-4.1.7.tgz#15803967783231dcb96432ad45b360d5e95188df" + integrity sha512-IpggH1Br51UAlLaAhNr6qY0IVT5mN1akfeVi1DpCj2ji7zqdfeDO3zmX6aWSjYVefg6OvNol71WKh+wjcYNvXg== + "@storybook/core@4.1.6": version "4.1.6" resolved "https://registry.yarnpkg.com/@storybook/core/-/core-4.1.6.tgz#818124a8d47c9432637e000e509b01d051b9603e" @@ -1673,7 +1734,7 @@ resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-1.10.35.tgz#4e5c2b1e5b3bf0b863efb8c5e70081f52e6c9518" integrity sha512-SVtqEcudm7yjkTwoRA1gC6CNMhGDdMx4Pg8BPdiqI7bXXdCn1BPmtxgeWYQOgDxrq53/5YTlhq5ULxBEAlWIBg== -"@types/lodash@^4.14.119": +"@types/lodash@4.14.119", "@types/lodash@^4.14.119": version "4.14.119" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39" integrity sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw== @@ -1698,6 +1759,13 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18" integrity sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA== +"@types/react-color@^2.14.0": + version "2.14.0" + resolved "https://registry.yarnpkg.com/@types/react-color/-/react-color-2.14.0.tgz#e01f6069902819fe9f6544375829a485f894206f" + integrity sha512-5UfGjUsu7bXop7K064nNrIGgS7wPjGEY7Or9tAE6BLslDtfhRnAlqWo9N2BIntEZ/3KpXlRnt0MBuc+hZJTevw== + dependencies: + "@types/react" "*" + "@types/react-custom-scrollbars@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/react-custom-scrollbars/-/react-custom-scrollbars-4.0.5.tgz#8629b4b3164914d27df7cb0ca0a086c0ad406389" @@ -1735,7 +1803,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@16.7.6", "@types/react@^16.7.6": +"@types/react@*", "@types/react@^16.7.6": version "16.7.6" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040" integrity sha512-QBUfzftr/8eg/q3ZRgf/GaDP6rTYc7ZNem+g4oZM38C9vXyV8AWRWaTQuW5yCoZTsfHrN7b3DeEiUnqH9SrnpA== @@ -1750,7 +1818,15 @@ dependencies: "@types/react" "*" -"@types/storybook__react@^4.0.0": +"@types/storybook__addon-knobs@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/storybook__addon-knobs/-/storybook__addon-knobs-4.0.0.tgz#b218f0d84888833cc8b8d7a7b524175e8bb3030f" + integrity sha512-x3GNz8f0fQv7USvDuVXdZ4p/7nofFHyH6iB/qwR84Yp97xZxOzlQ0SY+6K14tVbdi9P7Qm5DZ2kZr0a+Io8qEQ== + dependencies: + "@types/react" "*" + "@types/storybook__react" "*" + +"@types/storybook__react@*", "@types/storybook__react@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/storybook__react/-/storybook__react-4.0.0.tgz#52cc452fbab599568d595075a90142ef4a1233f6" integrity sha512-Iq3RX953fqZRwWN3jywm8pUx1/Atev+x/9tF7/2CNA+Ii55sGSJJRWMRthUKQXTa3zOexcvfksfVYdUaIZY91w== @@ -4429,7 +4505,7 @@ caniuse-api@^1.5.2: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-db@1.0.30000772, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: +caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000772" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b" integrity sha1-UarokXaChureSj2DGep21qAbUSs= @@ -5186,6 +5262,13 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +copy-to-clipboard@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz#f4e82f4a8830dce4666b7eb8ded0c9bcc313aba9" + integrity sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw== + dependencies: + toggle-selection "^1.0.3" + core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -6797,7 +6880,7 @@ es6-weak-map@^2.0.1: es6-iterator "^2.0.1" es6-symbol "^3.1.1" -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= @@ -10807,16 +10890,16 @@ lodash.without@~4.4.0: resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw= +lodash@>4.17.4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.1.1, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.7.0, lodash@^4.8.0, lodash@~4.17.10, lodash@~4.17.5: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== + lodash@^3.10.1, lodash@^3.6.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y= -lodash@^4.0.0, lodash@^4.1.1, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.7.0, lodash@^4.8.0, lodash@~4.17.10, lodash@~4.17.5: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== - lodash@~4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.3.0.tgz#efd9c4a6ec53f3b05412429915c3e4824e4d25a4" @@ -11041,6 +11124,11 @@ marksy@^6.1.0: he "^1.1.1" marked "^0.3.12" +material-colors@^1.2.1: + version "1.2.6" + resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46" + integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg== + math-expression-evaluator@^1.2.14: version "1.2.17" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" @@ -13847,6 +13935,18 @@ react-addons-create-fragment@^15.5.3: loose-envify "^1.3.1" object-assign "^4.1.0" +react-color@^2.14.1, react-color@^2.17.0: + version "2.17.0" + resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.17.0.tgz#e14b8a11f4e89163f65a34c8b43faf93f7f02aaa" + integrity sha512-kJfE5tSaFe6GzalXOHksVjqwCPAsTl+nzS9/BWfP7j3EXbQ4IiLAF9sZGNzk3uq7HfofGYgjmcUgh0JP7xAQ0w== + dependencies: + "@icons/material" "^0.2.4" + lodash ">4.17.4" + material-colors "^1.2.1" + prop-types "^15.5.10" + reactcss "^1.2.0" + tinycolor2 "^1.4.1" + react-custom-scrollbars@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/react-custom-scrollbars/-/react-custom-scrollbars-4.2.1.tgz#830fd9502927e97e8a78c2086813899b2a8b66db" @@ -14168,6 +14268,13 @@ react@^16.6.3, react@^16.7.0: prop-types "^15.6.2" scheduler "^0.12.0" +reactcss@^1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd" + integrity sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A== + dependencies: + lodash "^4.0.1" + read-chunk@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-2.1.0.tgz#6a04c0928005ed9d42e1a6ac5600e19cbc7ff655" @@ -16463,6 +16570,11 @@ to-space-case@^1.0.0: dependencies: to-no-case "^1.0.0" +toggle-selection@^1.0.3: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= + toposort@^1.0.0: version "1.0.7" resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" From 977d53c8c964111a152d169c518abc7d69c8d13d Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 11:13:07 +0100 Subject: [PATCH 09/57] Get rid of unused renderContent prop on PopperController --- .../src/components/Tooltip/Popper.tsx | 8 ++++---- .../src/components/Tooltip/PopperController.tsx | 17 ++--------------- .../src/components/Tooltip/Tooltip.tsx | 2 +- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/packages/grafana-ui/src/components/Tooltip/Popper.tsx b/packages/grafana-ui/src/components/Tooltip/Popper.tsx index 17955d346b5..9ee4ca41f5f 100644 --- a/packages/grafana-ui/src/components/Tooltip/Popper.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Popper.tsx @@ -3,6 +3,7 @@ import * as PopperJS from 'popper.js'; import { Manager, Popper as ReactPopper } from 'react-popper'; import { Portal } from '@grafana/ui'; import Transition from 'react-transition-group/Transition'; +import { PopperContent } from './PopperController'; const defaultTransitionStyles = { transition: 'opacity 200ms linear', @@ -17,17 +18,16 @@ const transitionStyles: { [key: string]: object } = { }; interface Props extends React.HTMLAttributes { - renderContent: (content: any) => any; show: boolean; placement?: PopperJS.Placement; - content: string | ((props: any) => JSX.Element); + content: PopperContent; referenceElement: PopperJS.ReferenceObject; arrowClassName?: string; } class Popper extends PureComponent { render() { - const { renderContent, show, placement, onMouseEnter, onMouseLeave, className, arrowClassName } = this.props; + const { show, placement, onMouseEnter, onMouseLeave, className, arrowClassName } = this.props; const { content } = this.props; return ( @@ -56,7 +56,7 @@ class Popper extends PureComponent { className={`popper`} >
- {renderContent(content)} + {content}
JSX.Element); +export type PopperContent = string | JSX.Element; export interface UsingPopperProps { show?: boolean; placement?: PopperJS.Placement; content: PopperContent; children: JSX.Element; - renderContent?: (content: PopperContent) => JSX.Element; } type PopperControllerRenderProp = ( @@ -17,8 +16,7 @@ type PopperControllerRenderProp = ( popperProps: { show: boolean; placement: PopperJS.Placement; - content: string | ((props: any) => JSX.Element); - renderContent: (content: any) => any; + content: PopperContent; } ) => JSX.Element; @@ -69,16 +67,6 @@ class PopperController extends React.Component { })); }; - renderContent(content: PopperContent) { - - if (typeof content === 'function') { - // If it's a function we assume it's a React component - const ReactComponent = content; - return ; - } - return content; - } - render() { const { children, content } = this.props; const { show, placement } = this.state; @@ -87,7 +75,6 @@ class PopperController extends React.Component { show, placement, content, - renderContent: this.renderContent, }); } } diff --git a/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx index 343672721ec..f03eb97f441 100644 --- a/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx @@ -12,7 +12,7 @@ export enum Themes { interface TooltipProps extends UsingPopperProps { theme?: Themes; } -export const Tooltip = ({ children, renderContent, theme, ...controllerProps }: TooltipProps) => { +export const Tooltip = ({ children, theme, ...controllerProps }: TooltipProps) => { const tooltipTriggerRef = createRef(); const popperBackgroundClassName = 'popper__background' + (theme ? ' ' + theme : ''); From e33614ade3c219263f6d98187f3efc8b4fca3a72 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 12:24:44 +0100 Subject: [PATCH 10/57] Unified color picker API, allowed for color specified for theme selection, updated code to changes in PopperController API --- .../components/ColorPicker/ColorPicker.tsx | 20 +++-------- .../ColorPicker/ColorPickerPopover.tsx | 20 +++++------ .../ColorPicker/SeriesColorPicker.tsx | 15 ++++---- .../ColorPicker/SeriesColorPickerPopover.tsx | 36 +++++++++---------- .../components/ColorPicker/_ColorPicker.scss | 4 +++ packages/grafana-ui/src/types/index.ts | 4 +++ .../grafana-ui/src/utils/colorsPalette.ts | 23 ++++++++++-- yarn.lock | 6 ++-- 8 files changed, 67 insertions(+), 61 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index c2697b13e7d..f09994f23f9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -2,29 +2,19 @@ import React, { Component, createRef } from 'react'; import PopperController from '../Tooltip/PopperController'; import Popper from '../Tooltip/Popper'; import { ColorPickerPopover } from './ColorPickerPopover'; -import { ColorDefinition } from '../../utils/colorsPalette'; +import { Themeable } from '../../types'; -interface Props { +export interface ColorPickerProps { color: string; - onChange: (c: string) => void; + onChange: (color: string) => void; } -export class ColorPicker extends Component { +export class ColorPicker extends Component { private pickerTriggerRef = createRef(); - pickerElem: HTMLElement | null; - colorPickerDrop: any; - - onColorSelect = (color: ColorDefinition) => { - this.props.onChange(color.name); - }; - - renderPickerTabs = () => { - return {}} />; - }; render() { return ( - + }> {(showPopper, hidePopper, popperProps) => { return ( <> diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index 9592d6c1ead..57fbcda26c9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -1,17 +1,13 @@ import React from 'react'; import NamedColorsPicker from './NamedColorsPicker'; -import { Color } from 'csstype'; -import { ColorDefinition, getColorName } from '../..//utils/colorsPalette'; +import { getColorName } from '../..//utils/colorsPalette'; import { SpectrumPicker } from './SpectrumPicker'; -import { GrafanaTheme } from '../../types'; +import { ColorPickerProps } from './ColorPicker'; +import { GrafanaTheme, Themeable } from '../../types'; // const DEFAULT_COLOR = '#000000'; -export interface Props { - color: Color | string; - theme?: GrafanaTheme; - onColorSelect: (color: string | ColorDefinition) => void; -} +export interface Props extends ColorPickerProps, Themeable {} type PickerType = 'palette' | 'spectrum'; @@ -23,22 +19,22 @@ export class ColorPickerPopover extends React.Component { constructor(props: Props) { super(props); this.state = { - activePicker: 'spectrum', + activePicker: 'palette', }; } handleSpectrumColorSelect = (color: any) => { - this.props.onColorSelect(color.toRgbString()); + this.props.onChange(color.toRgbString()); }; renderPicker = () => { const { activePicker } = this.state; - const { color } = this.props; + const { color, onChange, theme } = this.props; return activePicker === 'spectrum' ? ( ) : ( - + ); }; diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx index 40487386a68..6c50f3b2f53 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx @@ -3,16 +3,14 @@ import * as PopperJS from 'popper.js'; import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; import PopperController from '../Tooltip/PopperController'; import Popper from '../Tooltip/Popper'; -import { GrafanaTheme } from '../../types'; +import { Themeable } from '../../types'; +import { ColorPickerProps } from './ColorPicker'; -export interface SeriesColorPickerProps { - color: string; +export interface SeriesColorPickerProps extends ColorPickerProps, Themeable { yaxis?: number; optionalClass?: string; - onColorChange: (newColor: string) => void; onToggleAxis?: () => void; children: JSX.Element; - theme?: GrafanaTheme; } export class SeriesColorPicker extends React.Component { @@ -26,13 +24,13 @@ export class SeriesColorPicker extends React.Component { }; renderPickerTabs = () => { - const { color, yaxis, onColorChange, onToggleAxis, theme } = this.props; + const { color, yaxis, onChange, onToggleAxis, theme } = this.props; return ( ); @@ -40,9 +38,8 @@ export class SeriesColorPicker extends React.Component { render() { const { children } = this.props; - return ( - + {(showPopper, hidePopper, popperProps) => { return ( <> diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx index 3f4fcc122a3..e6cae077cdc 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx @@ -1,28 +1,26 @@ -import React from 'react'; +import React, { FunctionComponent } from 'react'; import { ColorPickerPopover } from './ColorPickerPopover'; -import { GrafanaTheme } from '../../types'; +import { Themeable } from '../../types'; +import { ColorPickerProps } from './ColorPicker'; -export interface SeriesColorPickerPopoverProps { - color: string; +export interface SeriesColorPickerPopoverProps extends ColorPickerProps, Themeable { yaxis?: number; - onColorChange: (color: string) => void; onToggleAxis?: () => void; - theme?: GrafanaTheme; } -export class SeriesColorPickerPopover extends React.PureComponent { - render() { - return ( -
- -
- {this.props.yaxis && } -
-
-
- ); - } -} +export const SeriesColorPickerPopover: FunctionComponent = ({ + onChange, + color, + theme, + yaxis, + onToggleAxis, +}) => { + return ( + +
{yaxis && }
+
+ ); +}; interface AxisSelectorProps { yaxis: number; diff --git a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss index 1a9da6c602d..e38df81d402 100644 --- a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss +++ b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss @@ -4,6 +4,9 @@ } } +.ColorPicker__arrow { + +} .ColorPickerPopover { border-radius: 3px; } @@ -22,6 +25,7 @@ .ColorPickerPopover__tab { background: #303133; color: white; + cursor: pointer; } .ColorPickerPopover__tab--active { background: none; diff --git a/packages/grafana-ui/src/types/index.ts b/packages/grafana-ui/src/types/index.ts index 1b1d764e304..575c749e07e 100644 --- a/packages/grafana-ui/src/types/index.ts +++ b/packages/grafana-ui/src/types/index.ts @@ -8,3 +8,7 @@ export enum GrafanaTheme { Light = 'light', Dark = 'dark', } + +export interface Themeable { + theme?: GrafanaTheme; +} diff --git a/packages/grafana-ui/src/utils/colorsPalette.ts b/packages/grafana-ui/src/utils/colorsPalette.ts index 2f6300b1e18..c4911abeaa4 100644 --- a/packages/grafana-ui/src/utils/colorsPalette.ts +++ b/packages/grafana-ui/src/utils/colorsPalette.ts @@ -1,4 +1,5 @@ import { flatten, some, values } from 'lodash'; +import { GrafanaTheme } from '../types'; type Hue = 'green' | 'yellow' | 'red' | 'blue' | 'orange' | 'purple'; @@ -38,6 +39,7 @@ type ThemeVariants = { dark: string; light: string; }; + export type ColorDefinition = { hue: Hue; isPrimary?: boolean; @@ -118,8 +120,23 @@ export const getColorDefinition = (hex: string): ColorDefinition | undefined => )[0]; }; -export const getColorName = (hex: string): Color | undefined => { - const definition = getColorDefinition(hex); +const isHex = (color: string) => { + const hexRegex = /^((0x){0,1}|#{0,1})([0-9A-F]{8}|[0-9A-F]{6})$/gi; + return hexRegex.test(color); +}; - return definition ? definition.name : undefined; +export const getColorName = (color: string): Color | undefined => { + if (color.indexOf('rgb') > -1) { + return undefined; + } + if (isHex(color)) { + const definition = getColorDefinition(color); + return definition ? definition.name : undefined; + } + + return color as Color; +}; + +export const getColorForTheme = (color: ColorDefinition, theme?: GrafanaTheme) => { + return theme ? color.variants[theme] : color.variants.dark; }; diff --git a/yarn.lock b/yarn.lock index a8cc15572c5..04678dd8685 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1734,7 +1734,7 @@ resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-1.10.35.tgz#4e5c2b1e5b3bf0b863efb8c5e70081f52e6c9518" integrity sha512-SVtqEcudm7yjkTwoRA1gC6CNMhGDdMx4Pg8BPdiqI7bXXdCn1BPmtxgeWYQOgDxrq53/5YTlhq5ULxBEAlWIBg== -"@types/lodash@4.14.119", "@types/lodash@^4.14.119": +"@types/lodash@^4.14.119": version "4.14.119" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39" integrity sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw== @@ -1803,7 +1803,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.7.6": +"@types/react@*", "@types/react@16.7.6", "@types/react@^16.7.6": version "16.7.6" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040" integrity sha512-QBUfzftr/8eg/q3ZRgf/GaDP6rTYc7ZNem+g4oZM38C9vXyV8AWRWaTQuW5yCoZTsfHrN7b3DeEiUnqH9SrnpA== @@ -4505,7 +4505,7 @@ caniuse-api@^1.5.2: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: +caniuse-db@1.0.30000772, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000772" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b" integrity sha1-UarokXaChureSj2DGep21qAbUSs= From b02c89c3bafb9616daca36bf9a56cf83c8b9db42 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 12:25:20 +0100 Subject: [PATCH 11/57] Stories updates --- .../ColorPicker/ColorPicker.story.tsx | 24 ++++++- .../ColorPicker/ColorPickerPopover.story.tsx | 2 +- .../ColorPicker/NamedColorsPicker.story.tsx | 18 +++--- .../ColorPicker/NamedColorsPicker.tsx | 63 ++++++++++++------- 4 files changed, 69 insertions(+), 38 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx index 1ee4b18d0b2..909d91c5d3f 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx @@ -1,7 +1,8 @@ import React, { FunctionComponent } from 'react'; import { storiesOf } from '@storybook/react'; import { ColorPicker } from './ColorPicker'; -import { withKnobs, text } from '@storybook/addon-knobs'; +import { SeriesColorPicker } from './SeriesColorPicker'; +import { UseState } from './NamedColorsPicker.story'; const CenteredStory: FunctionComponent<{}> = ({ children }) => { return ( @@ -21,7 +22,24 @@ const CenteredStory: FunctionComponent<{}> = ({ children }) => { const ColorPickerStories = storiesOf('UI/ColorPicker', module); ColorPickerStories.addDecorator(story => {story()}); -ColorPickerStories.addDecorator(withKnobs); ColorPickerStories.add('Color picker', () => { - return {}} />; + return {}} />; +}); +ColorPickerStories.add('Series color picker', () => { + return ( + + {(selectedColor, updateSelectedColor) => { + return ( + {}} + color={selectedColor} + onChange={color => updateSelectedColor(color)} + > +
Open color picker
+
+ ); + }} +
+ ); }); diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx index cfbbd6f645a..a74cbaec889 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx @@ -35,7 +35,7 @@ storiesOf('UI/ColorPickerPopover', module) return ( { + onChange={color => { console.log(color); }} theme={selectedTheme || undefined} diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx index 3d51f4b0347..3d90f88bd26 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx @@ -1,7 +1,7 @@ import React, { FunctionComponent } from 'react'; import { storiesOf } from '@storybook/react'; import NamedColorsPicker from './NamedColorsPicker'; -import { Color, getColorName } from '@grafana/ui/src/utils/colorsPalette'; +import { getColorName } from '@grafana/ui/src/utils/colorsPalette'; import { withKnobs, select } from '@storybook/addon-knobs'; const CenteredStory: FunctionComponent<{}> = ({ children }) => { @@ -24,16 +24,18 @@ interface StateHolderProps { children: (currentState: T, updateState: (nextState: T) => void) => JSX.Element; } -class UseState extends React.Component, { value: T }> { +export class UseState extends React.Component, { value: T }> { constructor(props: StateHolderProps) { super(props); this.state = { value: props.initialState, }; } + static getDerivedStateFromProps(props: StateHolderProps<{}>, state: { value: {} }) { return { value: props.initialState, + ...state }; } @@ -62,14 +64,10 @@ storiesOf('UI/NamedColorPicker', module) return ( {(selectedColor, updateSelectedColor) => { - console.log(selectedColor); return ( { - // @ts-ignore - updateSelectedColor((color).name); - }} + color={selectedColor} + onChange={updateSelectedColor} /> ); }} @@ -82,8 +80,8 @@ storiesOf('UI/NamedColorPicker', module) {(selectedColor, updateSelectedColor) => { return ( updateSelectedColor(color.variants.dark)} + color={getColorName(selectedColor)} + onChange={updateSelectedColor} /> ); }} diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx index f7cd229dca9..b91e7c901e7 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx @@ -1,6 +1,7 @@ import React, { FunctionComponent } from 'react'; import { find, upperFirst } from 'lodash'; -import { Color, ColorsPalete, ColorDefinition } from '../../utils/colorsPalette'; +import { Color, ColorsPalete, ColorDefinition, getColorForTheme } from '../../utils/colorsPalette'; +import { Themeable } from '../../types'; type ColorChangeHandler = (color: ColorDefinition) => void; @@ -10,13 +11,15 @@ enum ColorSwatchVariant { } interface ColorSwatchProps extends React.DOMAttributes { - color: ColorDefinition; + color: string; + label?: string; variant?: ColorSwatchVariant; isSelected?: boolean; } const ColorSwatch: FunctionComponent = ({ color, + label, variant = ColorSwatchVariant.Small, isSelected, ...otherProps @@ -27,10 +30,10 @@ const ColorSwatch: FunctionComponent = ({ width: swatchSize, height: swatchSize, borderRadius: '50%', - background: `${color.variants.dark}`, + background: `${color}`, marginRight: isSmall ? '0px' : '8px', - boxShadow: isSelected ? `inset 0 0 0 2px ${color.variants.dark}, inset 0 0 0 4px white` : 'none', - cursor: isSelected ? 'default' : 'pointer' + boxShadow: isSelected ? `inset 0 0 0 2px ${color}, inset 0 0 0 4px white` : 'none', + cursor: isSelected ? 'default' : 'pointer', }; return ( @@ -42,32 +45,35 @@ const ColorSwatch: FunctionComponent = ({ {...otherProps} >
- {variant === ColorSwatchVariant.Large && {upperFirst(color.hue)}} + {variant === ColorSwatchVariant.Large && {label}}
); }; -interface ColorsGroupProps { +interface ColorsGroupProps extends Themeable { colors: ColorDefinition[]; selectedColor?: Color; onColorSelect: ColorChangeHandler; key?: string; } + const ColorsGroup: FunctionComponent = ({ colors, selectedColor, onColorSelect, + theme, ...otherProps }) => { const primaryColor = find(colors, color => !!color.isPrimary); return ( -
+
{primaryColor && ( onColorSelect(primaryColor)} /> )} @@ -77,30 +83,39 @@ const ColorsGroup: FunctionComponent = ({ marginTop: '8px', }} > - {colors.map(color => !color.isPrimary && ( -
- onColorSelect(color)} - /> -
- ))} + {colors.map( + color => + !color.isPrimary && ( +
+ onColorSelect(color)} + /> +
+ ) + )}
); }; -interface NamedColorsPickerProps { - selectedColor?: Color; - onChange: ColorChangeHandler; +interface NamedColorsPickerProps extends Themeable { + color?: Color; + onChange: (colorName: string) => void; } -const NamedColorsPicker = ({ selectedColor, onChange }: NamedColorsPickerProps) => { - const swatches: JSX.Element[] = []; +const NamedColorsPicker = ({ color, onChange, theme }: NamedColorsPickerProps) => { + const swatches: JSX.Element[] = []; ColorsPalete.forEach((colors, hue) => { swatches.push( - + onChange(color.name)} + /> ); }); From e35e266c8160f1577b6228cf3cad8fa5c4ec6c0a Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 13:29:12 +0100 Subject: [PATCH 12/57] Fix TS errors --- .../dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx | 2 +- .../features/dashboard/permissions/DashboardPermissions.tsx | 2 +- public/app/features/folders/FolderPermissions.tsx | 2 +- public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx | 5 ++--- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx index 6b6f81fc579..dbf441adaa8 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx @@ -77,7 +77,7 @@ export class PanelHeaderCorner extends Component { <> {infoMode === InfoModes.Info || infoMode === InfoModes.Links ? (
{

Permissions

- + }>
diff --git a/public/app/features/folders/FolderPermissions.tsx b/public/app/features/folders/FolderPermissions.tsx index 62a479b9124..78cb2170785 100644 --- a/public/app/features/folders/FolderPermissions.tsx +++ b/public/app/features/folders/FolderPermissions.tsx @@ -84,7 +84,7 @@ export class FolderPermissions extends PureComponent {

Folder Permissions

- + }>
diff --git a/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx b/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx index 4ef07d19212..e54e27869d6 100644 --- a/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx +++ b/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx @@ -3,7 +3,6 @@ import classNames from 'classnames'; import { TimeSeries } from 'app/core/core'; import { SeriesColorPicker } from '@grafana/ui'; import { ThemeProvider } from 'app/core/utils/ConfigProvider'; -import { GrafanaTheme } from '@grafana/ui/'; export const LEGEND_STATS = ['min', 'max', 'avg', 'current', 'total']; @@ -176,9 +175,9 @@ class LegendSeriesIcon extends PureComponent From c8ac23f3c1535672ab5b8469f5306427383b26a5 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 16:42:05 +0100 Subject: [PATCH 13/57] Rendering arrows for color picker, applying color changes to time series --- .../components/ColorPicker/ColorPicker.tsx | 24 +++- .../ColorPicker/ColorPickerPopover.tsx | 6 +- ...dColorsPicker.tsx => NamedColorsGroup.tsx} | 46 ++------ .../ColorPicker/NamedColorsPalette.tsx | 42 +++++++ .../ColorPicker/SeriesColorPicker.tsx | 19 +++- ...SpectrumPicker.tsx => SpectrumPalette.tsx} | 2 +- .../components/ColorPicker/_ColorPicker.scss | 103 +++++++++++++++++- .../src/components/Tooltip/Popper.tsx | 25 +++-- .../src/components/Tooltip/Tooltip.tsx | 5 +- .../src/components/Tooltip/_Tooltip.scss | 1 - .../src/utils/colorsPalette.test.ts | 47 ++++++-- .../grafana-ui/src/utils/colorsPalette.ts | 35 ++++-- public/app/core/time_series2.ts | 12 +- .../app/plugins/panel/graph/data_processor.ts | 7 +- public/app/plugins/panel/graph/module.ts | 7 +- 15 files changed, 283 insertions(+), 98 deletions(-) rename packages/grafana-ui/src/components/ColorPicker/{NamedColorsPicker.tsx => NamedColorsGroup.tsx} (71%) create mode 100644 packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx rename packages/grafana-ui/src/components/ColorPicker/{SpectrumPicker.tsx => SpectrumPalette.tsx} (96%) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index f09994f23f9..025417b91b9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -2,9 +2,9 @@ import React, { Component, createRef } from 'react'; import PopperController from '../Tooltip/PopperController'; import Popper from '../Tooltip/Popper'; import { ColorPickerPopover } from './ColorPickerPopover'; -import { Themeable } from '../../types'; +import { Themeable, GrafanaTheme } from '../../types'; -export interface ColorPickerProps { +export interface ColorPickerProps extends Themeable { color: string; onChange: (color: string) => void; } @@ -13,13 +13,29 @@ export class ColorPicker extends Component { private pickerTriggerRef = createRef(); render() { + const { theme } = this.props; return ( - }> + }> {(showPopper, hidePopper, popperProps) => { return ( <> {this.pickerTriggerRef.current && ( - + { + return ( +
+ ); + }} + /> )}
diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index 57fbcda26c9..518baeb393a 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import NamedColorsPicker from './NamedColorsPicker'; +import NamedColorsPicker from './NamedColorsPalette'; import { getColorName } from '../..//utils/colorsPalette'; -import { SpectrumPicker } from './SpectrumPicker'; +import { SpectrumPalette } from './SpectrumPalette'; import { ColorPickerProps } from './ColorPicker'; import { GrafanaTheme, Themeable } from '../../types'; @@ -32,7 +32,7 @@ export class ColorPickerPopover extends React.Component { const { color, onChange, theme } = this.props; return activePicker === 'spectrum' ? ( - + ) : ( ); diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx similarity index 71% rename from packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx rename to packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx index b91e7c901e7..b0cccd092a1 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx @@ -1,11 +1,12 @@ import React, { FunctionComponent } from 'react'; -import { find, upperFirst } from 'lodash'; -import { Color, ColorsPalete, ColorDefinition, getColorForTheme } from '../../utils/colorsPalette'; import { Themeable } from '../../types'; +import { ColorDefinition, getColorForTheme } from '../../utils/colorsPalette'; +import { Color } from 'csstype'; +import { find, upperFirst } from 'lodash'; type ColorChangeHandler = (color: ColorDefinition) => void; -enum ColorSwatchVariant { +export enum ColorSwatchVariant { Small = 'small', Large = 'large', } @@ -50,14 +51,14 @@ const ColorSwatch: FunctionComponent = ({ ); }; -interface ColorsGroupProps extends Themeable { +interface NamedColorsGroupProps extends Themeable { colors: ColorDefinition[]; selectedColor?: Color; onColorSelect: ColorChangeHandler; key?: string; } -const ColorsGroup: FunctionComponent = ({ +const NamedColorsGroup: FunctionComponent = ({ colors, selectedColor, onColorSelect, @@ -100,37 +101,4 @@ const ColorsGroup: FunctionComponent = ({ ); }; -interface NamedColorsPickerProps extends Themeable { - color?: Color; - onChange: (colorName: string) => void; -} - -const NamedColorsPicker = ({ color, onChange, theme }: NamedColorsPickerProps) => { - const swatches: JSX.Element[] = []; - ColorsPalete.forEach((colors, hue) => { - swatches.push( - onChange(color.name)} - /> - ); - }); - - return ( -
- {swatches} -
- ); -}; - -export default NamedColorsPicker; +export default NamedColorsGroup; diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx new file mode 100644 index 00000000000..5c1c8ecab89 --- /dev/null +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Color, ColorsPalette } from '../../utils/colorsPalette'; +import { Themeable } from '../../types/index'; +import NamedColorsGroup from './NamedColorsGroup'; + +interface NamedColorsPaletteProps extends Themeable { + color?: Color; + onChange: (colorName: string) => void; +} + +const NamedColorsPalette = ({ color, onChange, theme }: NamedColorsPaletteProps) => { + const swatches: JSX.Element[] = []; + + ColorsPalette.forEach((colors, hue) => { + swatches.push( + { + onChange(color.name) + }} + /> + ); + }); + + return ( +
+ {swatches} +
+ ); +}; + +export default NamedColorsPalette; diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx index 6c50f3b2f53..cf6dea672c9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx @@ -3,7 +3,7 @@ import * as PopperJS from 'popper.js'; import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; import PopperController from '../Tooltip/PopperController'; import Popper from '../Tooltip/Popper'; -import { Themeable } from '../../types'; +import { Themeable, GrafanaTheme } from '../../types'; import { ColorPickerProps } from './ColorPicker'; export interface SeriesColorPickerProps extends ColorPickerProps, Themeable { @@ -37,7 +37,7 @@ export class SeriesColorPicker extends React.Component { }; render() { - const { children } = this.props; + const { children, theme } = this.props; return ( {(showPopper, hidePopper, popperProps) => { @@ -49,10 +49,21 @@ export class SeriesColorPicker extends React.Component { onMouseEnter={showPopper} onMouseLeave={hidePopper} referenceElement={this.pickerTriggerRef.current} - className="ColorPicker" - arrowClassName="popper__arrow" + wrapperClassName="ColorPicker" + renderArrow={({ arrowProps, placement }) => { + return ( +
+ ); + }} /> )} + {React.cloneElement(children, { ref: this.pickerTriggerRef, onClick: showPopper, diff --git a/packages/grafana-ui/src/components/ColorPicker/SpectrumPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx similarity index 96% rename from packages/grafana-ui/src/components/ColorPicker/SpectrumPicker.tsx rename to packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx index 98b3fc2feec..154ff4b5bab 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SpectrumPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx @@ -9,7 +9,7 @@ export interface Props { onColorSelect: (color: string) => void; } -export class SpectrumPicker extends React.Component { +export class SpectrumPalette extends React.Component { elem: any; isMoving: boolean; diff --git a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss index e38df81d402..b1b58f94257 100644 --- a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss +++ b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss @@ -1,12 +1,107 @@ +$arrowSize: 10px; .ColorPicker { - .popper__arrow { - border-color: #f7f8fa; - } + @extend .popper; } .ColorPicker__arrow { + width: 0; + height: 0; + border-style: solid; + position: absolute; + margin: 0px; + &[data-placement^='top'] { + border-width: $arrowSize $arrowSize 0 $arrowSize; + border-left-color: transparent; + border-right-color: transparent; + border-bottom-color: transparent; + bottom: -$arrowSize; + left: calc(50% - $arrowSize); + padding-top: $arrowSize; + } + + &[data-placement^='bottom'] { + border-width: 0 $arrowSize $arrowSize $arrowSize; + border-left-color: transparent; + border-right-color: transparent; + border-top-color: transparent; + top: 0; + left: calc(50% - $arrowSize); + } + + &[data-placement^='bottom-start'] { + border-width: 0 $arrowSize $arrowSize $arrowSize; + border-left-color: transparent; + border-right-color: transparent; + border-top-color: transparent; + top: 0; + left: $arrowSize; + } + + &[data-placement^='bottom-end'] & { + border-width: 0 $arrowSize $arrowSize $arrowSize; + border-left-color: transparent; + border-right-color: transparent; + border-top-color: transparent; + top: 0; + left: calc(100% - $arrowSize); + } + + &[data-placement^='right'] { + border-width: $arrowSize $arrowSize $arrowSize 0; + border-left-color: transparent; + border-top-color: transparent; + border-bottom-color: transparent; + left: 0; + top: calc(50% - $arrowSize); + } + + &[data-placement^='left'] { + border-width: $arrowSize 0 $arrowSize $arrowSize; + border-top-color: transparent; + border-right-color: transparent; + border-bottom-color: transparent; + right: -$arrowSize; + top: calc(50% - $arrowSize); + } } + +.ColorPicker__arrow--light { + border-color: #ffffff; +} + +.ColorPicker__arrow--dark { + border-color: #1e2028; +} + +// Top +.ColorPicker[data-placement^='top'] { + padding-bottom: $arrowSize; +} + +// Bottom +.ColorPicker[data-placement^='bottom'] { + padding-top: $arrowSize; +} + +.ColorPicker[data-placement^='bottom-start'] { + padding-top: $arrowSize; +} + +.ColorPicker[data-placement^='bottom-end'] { + padding-top: $arrowSize; +} + +// Right +.ColorPicker[data-placement^='right'] { + padding-left: $arrowSize; +} + +// Left +.ColorPicker[data-placement^='left'] { + padding-right: $arrowSize; +} + .ColorPickerPopover { border-radius: 3px; } @@ -93,7 +188,7 @@ } .gf-color-picker__body { - padding-bottom: 10px; + padding-bottom: $arrowSize; padding-left: 6px; } diff --git a/packages/grafana-ui/src/components/Tooltip/Popper.tsx b/packages/grafana-ui/src/components/Tooltip/Popper.tsx index 9ee4ca41f5f..770baab6fb4 100644 --- a/packages/grafana-ui/src/components/Tooltip/Popper.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Popper.tsx @@ -1,6 +1,6 @@ import React, { PureComponent } from 'react'; import * as PopperJS from 'popper.js'; -import { Manager, Popper as ReactPopper } from 'react-popper'; +import { Manager, Popper as ReactPopper, PopperArrowProps } from 'react-popper'; import { Portal } from '@grafana/ui'; import Transition from 'react-transition-group/Transition'; import { PopperContent } from './PopperController'; @@ -22,12 +22,18 @@ interface Props extends React.HTMLAttributes { placement?: PopperJS.Placement; content: PopperContent; referenceElement: PopperJS.ReferenceObject; - arrowClassName?: string; + wrapperClassName?: string; + renderArrow?: ( + props: { + arrowProps: PopperArrowProps; + placement: string; + } + ) => JSX.Element; } class Popper extends PureComponent { render() { - const { show, placement, onMouseEnter, onMouseLeave, className, arrowClassName } = this.props; + const { show, placement, onMouseEnter, onMouseLeave, className, wrapperClassName, renderArrow } = this.props; const { content } = this.props; return ( @@ -53,16 +59,15 @@ class Popper extends PureComponent { ...transitionStyles[transitionState], }} data-placement={placement} - className={`popper`} + className={`${wrapperClassName}`} >
{content} -
+ {renderArrow && + renderArrow({ + arrowProps, + placement, + })}
); diff --git a/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx index f03eb97f441..7a3a97c6fd1 100644 --- a/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx @@ -27,8 +27,11 @@ export const Tooltip = ({ children, theme, ...controllerProps }: TooltipProps) = onMouseEnter={showPopper} onMouseLeave={hidePopper} referenceElement={tooltipTriggerRef.current} + wrapperClassName='popper' className={popperBackgroundClassName} - arrowClassName={'popper__arrow'} + renderArrow={({ arrowProps, placement }) => ( +
+ )} /> )} {React.cloneElement(children, { diff --git a/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss b/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss index ad56328d816..a010e89b1d4 100644 --- a/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss +++ b/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss @@ -44,7 +44,6 @@ $popper-margin-from-ref: 5px; margin: 0px; } - // Top .popper[data-placement^='top'] { padding-bottom: $popper-margin-from-ref; diff --git a/packages/grafana-ui/src/utils/colorsPalette.test.ts b/packages/grafana-ui/src/utils/colorsPalette.test.ts index c15a56ea192..fc22ed0ae2e 100644 --- a/packages/grafana-ui/src/utils/colorsPalette.test.ts +++ b/packages/grafana-ui/src/utils/colorsPalette.test.ts @@ -1,11 +1,7 @@ -import { getColorName, getColorDefinition, ColorsPalete, buildColorDefinition } from './colorsPalette'; +import { getColorName, getColorDefinition, getColorByName, SemiDarkBlue, getColorFromHexRgbOrName } from './colorsPalette'; +import { GrafanaTheme } from '../types'; describe('colors', () => { - const FakeBlue = buildColorDefinition('blue', 'blue', ['#0000ff', '#00000ee']); - - beforeAll(() => { - ColorsPalete.set('blue', [FakeBlue]); - }); describe('getColorDefinition', () => { it('returns undefined for unknown hex', () => { @@ -13,8 +9,8 @@ describe('colors', () => { }); it('returns definition for known hex', () => { - expect(getColorDefinition(FakeBlue.variants.light)).toEqual(FakeBlue); - expect(getColorDefinition(FakeBlue.variants.dark)).toEqual(FakeBlue); + expect(getColorDefinition(SemiDarkBlue.variants.light)).toEqual(SemiDarkBlue); + expect(getColorDefinition(SemiDarkBlue.variants.dark)).toEqual(SemiDarkBlue); }); }); @@ -24,8 +20,39 @@ describe('colors', () => { }); it('returns name for known hex', () => { - expect(getColorName(FakeBlue.variants.light)).toEqual(FakeBlue.name); - expect(getColorName(FakeBlue.variants.dark)).toEqual(FakeBlue.name); + expect(getColorName(SemiDarkBlue.variants.light)).toEqual(SemiDarkBlue.name); + expect(getColorName(SemiDarkBlue.variants.dark)).toEqual(SemiDarkBlue.name); + }); + }); + + describe('getColorByName', () => { + it('returns undefined for unknown color', () => { + expect(getColorByName('aruba-sunshine')).toBeUndefined(); + }); + + it('returns color definiton for known color', () => { + expect(getColorByName(SemiDarkBlue.name)).toBe(SemiDarkBlue); + }); + + }); + describe('getColorFromHexRgbOrName', () => { + it('returns undefined for unknown color', () => { + expect(() => getColorFromHexRgbOrName('aruba-sunshine')).toThrow(); + }); + + it('returns dark hex variant for known color if theme not specified', () => { + expect(getColorFromHexRgbOrName(SemiDarkBlue.name)).toBe(SemiDarkBlue.variants.dark); + }); + + it('returns correct variant\'s hex for known color if theme specified', () => { + expect(getColorFromHexRgbOrName(SemiDarkBlue.name, GrafanaTheme.Light)).toBe(SemiDarkBlue.variants.light); + }); + + it('returns color if specified as hex or rgb/a', () => { + expect(getColorFromHexRgbOrName('ff0000')).toBe('ff0000'); + expect(getColorFromHexRgbOrName('#ff0000')).toBe('#ff0000'); + expect(getColorFromHexRgbOrName('rgb(0,0,0)')).toBe('rgb(0,0,0)'); + expect(getColorFromHexRgbOrName('rgba(0,0,0,1)')).toBe('rgba(0,0,0,1)'); }); }); }); diff --git a/packages/grafana-ui/src/utils/colorsPalette.ts b/packages/grafana-ui/src/utils/colorsPalette.ts index c4911abeaa4..4238f50b410 100644 --- a/packages/grafana-ui/src/utils/colorsPalette.ts +++ b/packages/grafana-ui/src/utils/colorsPalette.ts @@ -47,7 +47,7 @@ export type ColorDefinition = { variants: ThemeVariants; }; -export const ColorsPalete = new Map(); +export const ColorsPalette = new Map(); export const buildColorDefinition = ( hue: Hue, @@ -107,15 +107,15 @@ const blues = [BasicBlue, DarkBlue, SemiDarkBlue, LightBlue, SuperLightBlue]; const oranges = [BasicOrange, DarkOrange, SemiDarkOrange, LightOrange, SuperLightOrange]; const purples = [BasicPurple, DarkPurple, SemiDarkPurple, LightPurple, SuperLightPurple]; -ColorsPalete.set('green', greens); -ColorsPalete.set('yellow', yellows); -ColorsPalete.set('red', reds); -ColorsPalete.set('blue', blues); -ColorsPalete.set('orange', oranges); -ColorsPalete.set('purple', purples); +ColorsPalette.set('green', greens); +ColorsPalette.set('yellow', yellows); +ColorsPalette.set('red', reds); +ColorsPalette.set('blue', blues); +ColorsPalette.set('orange', oranges); +ColorsPalette.set('purple', purples); export const getColorDefinition = (hex: string): ColorDefinition | undefined => { - return flatten(Array.from(ColorsPalete.values())).filter(definition => + return flatten(Array.from(ColorsPalette.values())).filter(definition => some(values(definition.variants), color => color === hex) )[0]; }; @@ -137,6 +137,25 @@ export const getColorName = (color: string): Color | undefined => { return color as Color; }; +export const getColorByName = (colorName: string) => { + const definition = flatten(Array.from(ColorsPalette.values())).filter(definition => definition.name === colorName); + return definition.length > 0 ? definition[0] : undefined; +}; + +export const getColorFromHexRgbOrName = (color: string, theme?: GrafanaTheme): string => { + if (color.indexOf('rgb') > -1 || isHex(color)) { + return color; + } + + const colorDefinition = getColorByName(color); + + if (!colorDefinition) { + throw new Error('Unknown color'); + } + + return theme ? colorDefinition.variants[theme] : colorDefinition.variants.dark; +}; + export const getColorForTheme = (color: ColorDefinition, theme?: GrafanaTheme) => { return theme ? color.variants[theme] : color.variants.dark; }; diff --git a/public/app/core/time_series2.ts b/public/app/core/time_series2.ts index 9d586a0b4b5..9872c0bc912 100644 --- a/public/app/core/time_series2.ts +++ b/public/app/core/time_series2.ts @@ -1,7 +1,6 @@ import kbn from 'app/core/utils/kbn'; import { getFlotTickDecimals } from 'app/core/utils/ticks'; import _ from 'lodash'; -import { ColorDefinition } from '@grafana/ui/src/utils/colorsPalette'; function matchSeriesOverride(aliasOrRegex, seriesAlias) { if (!aliasOrRegex) { @@ -357,13 +356,8 @@ export default class TimeSeries { return false; } - setColor(color: string | ColorDefinition) { - if (typeof color === 'string') { - this.color = color; - this.bars.fillColor = color; - } else { - this.color = color.variants.dark; - this.bars.fillColor = color.variants.dark; - } + setColor(color: string) { + this.color = color; + this.bars.fillColor = color; } } diff --git a/public/app/plugins/panel/graph/data_processor.ts b/public/app/plugins/panel/graph/data_processor.ts index 4fe47b70129..434070c2fe2 100644 --- a/public/app/plugins/panel/graph/data_processor.ts +++ b/public/app/plugins/panel/graph/data_processor.ts @@ -1,7 +1,9 @@ import _ from 'lodash'; -import { colors } from '@grafana/ui'; +import { colors, GrafanaTheme } from '@grafana/ui'; import TimeSeries from 'app/core/time_series2'; +import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; +import config from 'app/core/config'; export class DataProcessor { constructor(private panel) {} @@ -107,12 +109,13 @@ export class DataProcessor { const alias = seriesData.target; const colorIndex = index % colors.length; + const color = this.panel.aliasColors[alias] || colors[colorIndex]; const series = new TimeSeries({ datapoints: datapoints, alias: alias, - color: color, + color: getColorFromHexRgbOrName(color, config.bootData.user.lightTheme ? GrafanaTheme.Light : GrafanaTheme.Dark), unit: seriesData.unit, }); diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 75e82115ca1..362cc67c0ae 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -9,6 +9,9 @@ import _ from 'lodash'; import { MetricsPanelCtrl } from 'app/plugins/sdk'; import { DataProcessor } from './data_processor'; import { axesEditorComponent } from './axes_editor'; +import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; +import config from 'app/core/config'; +import { GrafanaTheme } from '@grafana/ui'; class GraphCtrl extends MetricsPanelCtrl { static template = template; @@ -242,8 +245,8 @@ class GraphCtrl extends MetricsPanelCtrl { } onColorChange = (series, color) => { - series.setColor(color); - this.panel.aliasColors[series.alias] = series.color; + series.setColor(getColorFromHexRgbOrName(color, config.bootData.user.lightTheme ? GrafanaTheme.Light : GrafanaTheme.Dark)); + this.panel.aliasColors[series.alias] = color; this.render(); }; From 25e60565ad796c75e98aa0156672ca03e51263ff Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 16:42:22 +0100 Subject: [PATCH 14/57] Updated stories --- .../ColorPicker/ColorPicker.story.tsx | 41 +++++++++++++++++-- ...story.tsx => NamedColorsPalette.story.tsx} | 8 ++-- 2 files changed, 41 insertions(+), 8 deletions(-) rename packages/grafana-ui/src/components/ColorPicker/{NamedColorsPicker.story.tsx => NamedColorsPalette.story.tsx} (92%) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx index 909d91c5d3f..6555e7b6b74 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx @@ -2,9 +2,12 @@ import React, { FunctionComponent } from 'react'; import { storiesOf } from '@storybook/react'; import { ColorPicker } from './ColorPicker'; import { SeriesColorPicker } from './SeriesColorPicker'; -import { UseState } from './NamedColorsPicker.story'; +import { UseState } from './NamedColorsPalette.story'; +import { withKnobs, select } from '@storybook/addon-knobs'; +import { GrafanaTheme } from '../../types'; -const CenteredStory: FunctionComponent<{}> = ({ children }) => { +// TODO: extract to decorators +export const CenteredStory: FunctionComponent<{}> = ({ children }) => { return (
= ({ children }) => { const ColorPickerStories = storiesOf('UI/ColorPicker', module); ColorPickerStories.addDecorator(story => {story()}); +ColorPickerStories.addDecorator(withKnobs); + ColorPickerStories.add('Color picker', () => { - return {}} />; + const selectedTheme = select( + 'Theme', + { + Default: '', + Light: GrafanaTheme.Light, + Dark: GrafanaTheme.Dark, + }, + GrafanaTheme.Light + ); + + return ( + + {(selectedColor, updateSelectedColor) => { + return ; + }} + + ); }); + ColorPickerStories.add('Series color picker', () => { + const selectedTheme = select( + 'Theme', + { + Default: '', + Light: GrafanaTheme.Light, + Dark: GrafanaTheme.Dark, + }, + GrafanaTheme.Light + ); + return ( {(selectedColor, updateSelectedColor) => { @@ -35,8 +67,9 @@ ColorPickerStories.add('Series color picker', () => { onToggleAxis={() => {}} color={selectedColor} onChange={color => updateSelectedColor(color)} + theme={selectedTheme || undefined} > -
Open color picker
+
Open color picker
); }} diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx similarity index 92% rename from packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx rename to packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx index 3d90f88bd26..fbab24579ab 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPicker.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx @@ -1,6 +1,6 @@ import React, { FunctionComponent } from 'react'; import { storiesOf } from '@storybook/react'; -import NamedColorsPicker from './NamedColorsPicker'; +import NamedColorsPalette from './NamedColorsPalette'; import { getColorName } from '@grafana/ui/src/utils/colorsPalette'; import { withKnobs, select } from '@storybook/addon-knobs'; @@ -47,7 +47,7 @@ export class UseState extends React.Component, { value: T } } -storiesOf('UI/NamedColorPicker', module) +storiesOf('UI/NamedColorsPalette', module) .addDecorator(withKnobs) .addDecorator(story => {story()}) .add('Named colors swatch - support for named colors', () => { @@ -65,7 +65,7 @@ storiesOf('UI/NamedColorPicker', module) {(selectedColor, updateSelectedColor) => { return ( - @@ -79,7 +79,7 @@ storiesOf('UI/NamedColorPicker', module) {(selectedColor, updateSelectedColor) => { return ( - From 4f516faa826a854ec1b940bccbe29202555c8d28 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 18 Jan 2019 16:44:58 +0100 Subject: [PATCH 15/57] Fix lint --- .../src/components/ColorPicker/NamedColorsPalette.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx index 5c1c8ecab89..c107cd91635 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx @@ -19,7 +19,7 @@ const NamedColorsPalette = ({ color, onChange, theme }: NamedColorsPaletteProps) selectedColor={color} colors={colors} onColorSelect={color => { - onChange(color.name) + onChange(color.name); }} /> ); From a214b5748e15eafdf6117af7a42f7b0dfba3f997 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 21 Jan 2019 12:15:42 +0100 Subject: [PATCH 16/57] Refactor color picker to remove code duplicartion (introduced colorPickerFactory). Allow popver position update on content change --- .../components/ColorPicker/ColorPicker.tsx | 106 +++++++++++------- .../ColorPicker/ColorPickerPopover.tsx | 17 ++- .../ColorPicker/SeriesColorPicker.tsx | 73 +----------- .../ColorPicker/SeriesColorPickerPopover.tsx | 6 +- .../components/ColorPicker/_ColorPicker.scss | 14 ++- .../ThresholdsEditor/ThresholdsEditor.tsx | 2 +- .../src/components/Tooltip/Popper.tsx | 24 ++-- .../components/Tooltip/PopperController.tsx | 12 +- .../grafana-ui/src/utils/colorsPalette.ts | 6 +- .../panel/graph/Legend/LegendSeriesItem.tsx | 3 +- 10 files changed, 125 insertions(+), 138 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 025417b91b9..a412c0c9d39 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -1,53 +1,81 @@ import React, { Component, createRef } from 'react'; import PopperController from '../Tooltip/PopperController'; -import Popper from '../Tooltip/Popper'; +import Popper, { RenderPopperArrowFn } from '../Tooltip/Popper'; import { ColorPickerPopover } from './ColorPickerPopover'; import { Themeable, GrafanaTheme } from '../../types'; +import { getColorFromHexRgbOrName } from '../../utils/colorsPalette'; export interface ColorPickerProps extends Themeable { color: string; onChange: (color: string) => void; + withArrow?: boolean; + children?: JSX.Element; } -export class ColorPicker extends Component { - private pickerTriggerRef = createRef(); +export const colorPickerFactory = ( + popover: React.ComponentType, + displayName?: string, + renderPopoverArrowFunction?: RenderPopperArrowFn +) => { + return class ColorPicker extends Component { + static displayName = displayName || 'ColorPicker'; + pickerTriggerRef = createRef(); - render() { - const { theme } = this.props; - return ( - }> - {(showPopper, hidePopper, popperProps) => { - return ( - <> - {this.pickerTriggerRef.current && ( - { - return ( + render() { + const popoverElement = React.createElement(popover, this.props); + const { theme, withArrow, children } = this.props; + + const renderArrow: RenderPopperArrowFn = ({ arrowProps, placement }) => { + return ( +
+ ); + }; + + return ( + + {(showPopper, hidePopper, popperProps) => { + return ( + <> + {this.pickerTriggerRef.current && ( + + )} + + {children ? ( + React.cloneElement(children as JSX.Element, { + ref: this.pickerTriggerRef, + onClick: showPopper, + onMouseLeave: hidePopper, + }) + ) : ( +
+
- ); - }} - /> - )} -
-
-
-
-
- - ); - }} - - ); - } -} +
+
+ )} + + ); + }} + + ); + } + }; +}; -export default ColorPicker; +export default colorPickerFactory(ColorPickerPopover, 'ColorPicker'); diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index 518baeb393a..c9242f72ebf 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -4,10 +4,11 @@ import { getColorName } from '../..//utils/colorsPalette'; import { SpectrumPalette } from './SpectrumPalette'; import { ColorPickerProps } from './ColorPicker'; import { GrafanaTheme, Themeable } from '../../types'; +import { PopperContentProps } from '../Tooltip/PopperController'; // const DEFAULT_COLOR = '#000000'; -export interface Props extends ColorPickerProps, Themeable {} +export interface Props extends ColorPickerProps, Themeable, PopperContentProps {} type PickerType = 'palette' | 'spectrum'; @@ -40,7 +41,7 @@ export class ColorPickerPopover extends React.Component { render() { const { activePicker } = this.state; - const { theme, children } = this.props; + const { theme, children, updatePopperPosition } = this.props; const colorPickerTheme = theme || GrafanaTheme.Dark; return ( @@ -49,7 +50,11 @@ export class ColorPickerPopover extends React.Component {
{ - this.setState({ activePicker: 'palette' }); + this.setState({ activePicker: 'palette' }, () => { + if (updatePopperPosition) { + updatePopperPosition(); + } + }); }} > Default @@ -57,7 +62,11 @@ export class ColorPickerPopover extends React.Component {
{ - this.setState({ activePicker: 'spectrum' }); + this.setState({ activePicker: 'spectrum' }, () => { + if (updatePopperPosition) { + updatePopperPosition(); + } + }); }} > Custom diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx index cf6dea672c9..c81e9ca8e86 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx @@ -1,78 +1,11 @@ -import React, { createRef } from 'react'; -import * as PopperJS from 'popper.js'; import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; -import PopperController from '../Tooltip/PopperController'; -import Popper from '../Tooltip/Popper'; -import { Themeable, GrafanaTheme } from '../../types'; -import { ColorPickerProps } from './ColorPicker'; +import { ColorPickerProps, colorPickerFactory } from './ColorPicker'; -export interface SeriesColorPickerProps extends ColorPickerProps, Themeable { +export interface SeriesColorPickerProps extends ColorPickerProps { yaxis?: number; optionalClass?: string; onToggleAxis?: () => void; children: JSX.Element; } -export class SeriesColorPicker extends React.Component { - private pickerTriggerRef = createRef(); - colorPickerDrop: any; - - static defaultProps = { - optionalClass: '', - yaxis: undefined, - onToggleAxis: () => {}, - }; - - renderPickerTabs = () => { - const { color, yaxis, onChange, onToggleAxis, theme } = this.props; - return ( - - ); - }; - - render() { - const { children, theme } = this.props; - return ( - - {(showPopper, hidePopper, popperProps) => { - return ( - <> - {this.pickerTriggerRef.current && ( - { - return ( -
- ); - }} - /> - )} - - {React.cloneElement(children, { - ref: this.pickerTriggerRef, - onClick: showPopper, - onMouseLeave: hidePopper, - })} - - ); - }} - - ); - } -} +export default colorPickerFactory(SeriesColorPickerPopover ,'SeriesColorPicker') diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx index e6cae077cdc..f019dc1634b 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx @@ -2,8 +2,9 @@ import React, { FunctionComponent } from 'react'; import { ColorPickerPopover } from './ColorPickerPopover'; import { Themeable } from '../../types'; import { ColorPickerProps } from './ColorPicker'; +import { PopperContentProps } from '../Tooltip/PopperController'; -export interface SeriesColorPickerPopoverProps extends ColorPickerProps, Themeable { +export interface SeriesColorPickerPopoverProps extends ColorPickerProps, Themeable, PopperContentProps { yaxis?: number; onToggleAxis?: () => void; } @@ -14,9 +15,10 @@ export const SeriesColorPickerPopover: FunctionComponent { return ( - +
{yaxis && }
); diff --git a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss index b1b58f94257..f35e47f126a 100644 --- a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss +++ b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss @@ -1,4 +1,4 @@ -$arrowSize: 10px; +$arrowSize: 15px; .ColorPicker { @extend .popper; } @@ -16,7 +16,7 @@ $arrowSize: 10px; border-right-color: transparent; border-bottom-color: transparent; bottom: -$arrowSize; - left: calc(50% - $arrowSize); + left: calc(50%-#{$arrowSize}); padding-top: $arrowSize; } @@ -26,7 +26,7 @@ $arrowSize: 10px; border-right-color: transparent; border-top-color: transparent; top: 0; - left: calc(50% - $arrowSize); + left: calc(50%-#{$arrowSize}); } &[data-placement^='bottom-start'] { @@ -44,7 +44,7 @@ $arrowSize: 10px; border-right-color: transparent; border-top-color: transparent; top: 0; - left: calc(100% - $arrowSize); + left: calc(100% -$arrowSize); } &[data-placement^='right'] { @@ -53,7 +53,7 @@ $arrowSize: 10px; border-top-color: transparent; border-bottom-color: transparent; left: 0; - top: calc(50% - $arrowSize); + top: calc(50%-#{$arrowSize}); } &[data-placement^='left'] { @@ -62,7 +62,7 @@ $arrowSize: 10px; border-right-color: transparent; border-bottom-color: transparent; right: -$arrowSize; - top: calc(50% - $arrowSize); + top: calc(50%-#{$arrowSize}); } } @@ -148,11 +148,13 @@ $arrowSize: 10px; .ColorPickerPopover__tab--active { background: white; } + .sp-replacer { background: inherit; border: none; color: inherit; padding: 0; + border-radius: 10px; } .sp-replacer:hover, diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index 590aca5c7a1..a1677e58e91 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; // import tinycolor, { ColorInput } from 'tinycolor2'; import { Threshold } from '../../types'; -import { ColorPicker } from '../ColorPicker/ColorPicker'; +import ColorPicker from '../ColorPicker/ColorPicker'; import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup'; import { colors } from '../../utils'; diff --git a/packages/grafana-ui/src/components/Tooltip/Popper.tsx b/packages/grafana-ui/src/components/Tooltip/Popper.tsx index 770baab6fb4..fa49596755c 100644 --- a/packages/grafana-ui/src/components/Tooltip/Popper.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Popper.tsx @@ -17,18 +17,20 @@ const transitionStyles: { [key: string]: object } = { exiting: { opacity: 0 }, }; +export type RenderPopperArrowFn = ( + props: { + arrowProps: PopperArrowProps; + placement: string; + } +) => JSX.Element; + interface Props extends React.HTMLAttributes { show: boolean; placement?: PopperJS.Placement; - content: PopperContent; + content: PopperContent; referenceElement: PopperJS.ReferenceObject; wrapperClassName?: string; - renderArrow?: ( - props: { - arrowProps: PopperArrowProps; - placement: string; - } - ) => JSX.Element; + renderArrow?: RenderPopperArrowFn; } class Popper extends PureComponent { @@ -47,7 +49,7 @@ class Popper extends PureComponent { // TODO: move modifiers config to popper controller modifiers={{ preventOverflow: { enabled: true, boundariesElement: 'window' } }} > - {({ ref, style, placement, arrowProps }) => { + {({ ref, style, placement, arrowProps, scheduleUpdate }) => { return (
{ className={`${wrapperClassName}`} >
- {content} + {typeof content === 'string' + ? content + : React.cloneElement(content, { + updatePopperPosition: scheduleUpdate, + })} {renderArrow && renderArrow({ arrowProps, diff --git a/packages/grafana-ui/src/components/Tooltip/PopperController.tsx b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx index aa2777f1c3d..a4bc555a4db 100644 --- a/packages/grafana-ui/src/components/Tooltip/PopperController.tsx +++ b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx @@ -1,12 +1,16 @@ import React from 'react'; import * as PopperJS from 'popper.js'; -export type PopperContent = string | JSX.Element; +// This API allows popovers to update Popper's position when e.g. popover content chaanges +// updatePopperPosition is delivered to content by react-popper +export interface PopperContentProps { updatePopperPosition?: () => void; } + +export type PopperContent = string | React.ReactElement; export interface UsingPopperProps { show?: boolean; placement?: PopperJS.Placement; - content: PopperContent; + content: PopperContent; children: JSX.Element; } @@ -16,13 +20,13 @@ type PopperControllerRenderProp = ( popperProps: { show: boolean; placement: PopperJS.Placement; - content: PopperContent; + content: PopperContent; } ) => JSX.Element; interface Props { placement?: PopperJS.Placement; - content: PopperContent; + content: PopperContent; className?: string; children: PopperControllerRenderProp; } diff --git a/packages/grafana-ui/src/utils/colorsPalette.ts b/packages/grafana-ui/src/utils/colorsPalette.ts index 4238f50b410..881519d6ff5 100644 --- a/packages/grafana-ui/src/utils/colorsPalette.ts +++ b/packages/grafana-ui/src/utils/colorsPalette.ts @@ -125,7 +125,11 @@ const isHex = (color: string) => { return hexRegex.test(color); }; -export const getColorName = (color: string): Color | undefined => { +export const getColorName = (color?: string): Color | undefined => { + if (!color) { + return undefined; + } + if (color.indexOf('rgb') > -1) { return undefined; } diff --git a/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx b/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx index e54e27869d6..568e38bab2e 100644 --- a/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx +++ b/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx @@ -172,14 +172,13 @@ class LegendSeriesIcon extends PureComponent { return ( - + From b560dcac3d5458a054f04b93227edc2d30b392aa Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 21 Jan 2019 12:16:05 +0100 Subject: [PATCH 17/57] Update grafana/ui exports --- packages/grafana-ui/src/components/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 584992f8803..68f8a2c0467 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -14,9 +14,9 @@ export { FormLabel } from './FormLabel/FormLabel'; export { FormField } from './FormField/FormField'; export { LoadingPlaceholder } from './LoadingPlaceholder/LoadingPlaceholder'; -export { ColorPicker } from './ColorPicker/ColorPicker'; +export { default as ColorPicker } from './ColorPicker/ColorPicker'; export { SeriesColorPickerPopover } from './ColorPicker/SeriesColorPickerPopover'; -export { SeriesColorPicker } from './ColorPicker/SeriesColorPicker'; +export { default as SeriesColorPicker } from './ColorPicker/SeriesColorPicker'; export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor'; export { Graph } from './Graph/Graph'; export { PanelOptionsGroup } from './PanelOptionsGroup/PanelOptionsGroup'; From 597cbb5b9b4afc0b1744c7d56c01355662a6d931 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 21 Jan 2019 12:16:32 +0100 Subject: [PATCH 18/57] Story updates --- .../src/components/ColorPicker/ColorPicker.story.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx index 6555e7b6b74..4b48e8503b4 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx @@ -1,7 +1,7 @@ import React, { FunctionComponent } from 'react'; import { storiesOf } from '@storybook/react'; -import { ColorPicker } from './ColorPicker'; -import { SeriesColorPicker } from './SeriesColorPicker'; +import SeriesColorPicker from './SeriesColorPicker'; +import ColorPicker from './ColorPicker'; import { UseState } from './NamedColorsPalette.story'; import { withKnobs, select } from '@storybook/addon-knobs'; import { GrafanaTheme } from '../../types'; @@ -69,7 +69,7 @@ ColorPickerStories.add('Series color picker', () => { onChange={color => updateSelectedColor(color)} theme={selectedTheme || undefined} > -
Open color picker
+
Open color picker
); }} From 84caf0bc9d63922a3a5c88e5a4c00d0695a58929 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 21 Jan 2019 12:17:23 +0100 Subject: [PATCH 19/57] Make named colors usable in angular code pt 1 --- public/app/plugins/panel/graph/graph.ts | 6 +++- .../plugins/panel/graph/threshold_manager.ts | 9 +++--- .../panel/graph/time_region_manager.ts | 28 +++++++++++++------ .../plugins/panel/graph/time_regions_form.ts | 3 ++ .../app/plugins/panel/heatmap/color_legend.ts | 7 ++++- .../app/plugins/panel/heatmap/heatmap_ctrl.ts | 1 + public/app/plugins/panel/heatmap/rendering.ts | 10 +++++-- public/app/plugins/panel/singlestat/module.ts | 20 +++++++++++-- public/app/plugins/panel/table/module.ts | 5 +++- public/app/plugins/panel/table/renderer.ts | 15 ++++++++-- 10 files changed, 80 insertions(+), 24 deletions(-) diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 86a6fd1dfd2..78f9d09520b 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -26,6 +26,7 @@ import ReactDOM from 'react-dom'; import { Legend, GraphLegendProps } from './Legend/Legend'; import { GraphCtrl } from './module'; +import { GrafanaTheme } from '@grafana/ui'; class GraphElement { ctrl: GraphCtrl; @@ -51,7 +52,10 @@ class GraphElement { this.panelWidth = 0; this.eventManager = new EventManager(this.ctrl); this.thresholdManager = new ThresholdManager(this.ctrl); - this.timeRegionManager = new TimeRegionManager(this.ctrl); + this.timeRegionManager = new TimeRegionManager( + this.ctrl, + config.bootData.user.lightTheme ? GrafanaTheme.Light : GrafanaTheme.Dark + ); this.tooltip = new GraphTooltip(this.elem, this.ctrl.dashboard, this.scope, () => { return this.sortedSeries; }); diff --git a/public/app/plugins/panel/graph/threshold_manager.ts b/public/app/plugins/panel/graph/threshold_manager.ts index e7d874e7451..1c782a99d3a 100644 --- a/public/app/plugins/panel/graph/threshold_manager.ts +++ b/public/app/plugins/panel/graph/threshold_manager.ts @@ -1,6 +1,7 @@ import 'vendor/flot/jquery.flot'; import $ from 'jquery'; import _ from 'lodash'; +import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; export class ThresholdManager { plot: any; @@ -225,12 +226,12 @@ export class ThresholdManager { if (threshold.yaxis === 'right' && this.hasSecondYAxis) { options.grid.markings.push({ y2axis: { from: threshold.value, to: limit }, - color: fillColor, + color: getColorFromHexRgbOrName(fillColor), }); } else { options.grid.markings.push({ yaxis: { from: threshold.value, to: limit }, - color: fillColor, + color: getColorFromHexRgbOrName(fillColor), }); } } @@ -238,12 +239,12 @@ export class ThresholdManager { if (threshold.yaxis === 'right' && this.hasSecondYAxis) { options.grid.markings.push({ y2axis: { from: threshold.value, to: threshold.value }, - color: lineColor, + color: getColorFromHexRgbOrName(lineColor), }); } else { options.grid.markings.push({ yaxis: { from: threshold.value, to: threshold.value }, - color: lineColor, + color: getColorFromHexRgbOrName(lineColor), }); } } diff --git a/public/app/plugins/panel/graph/time_region_manager.ts b/public/app/plugins/panel/graph/time_region_manager.ts index e5ec27ee77a..6d13e37d5c5 100644 --- a/public/app/plugins/panel/graph/time_region_manager.ts +++ b/public/app/plugins/panel/graph/time_region_manager.ts @@ -1,7 +1,13 @@ import 'vendor/flot/jquery.flot'; import _ from 'lodash'; import moment from 'moment'; -import config from 'app/core/config'; +import { GrafanaTheme } from '@grafana/ui'; +import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; + +type TimeRegionColorDefinition = { + fill: string; + line: string; +}; export const colorModes = { gray: { @@ -38,31 +44,35 @@ export function getColorModes() { }); } -function getColor(timeRegion) { +function getColor(timeRegion, theme: GrafanaTheme): TimeRegionColorDefinition { if (Object.keys(colorModes).indexOf(timeRegion.colorMode) === -1) { timeRegion.colorMode = 'red'; } if (timeRegion.colorMode === 'custom') { return { - fill: timeRegion.fillColor, - line: timeRegion.lineColor, + fill: getColorFromHexRgbOrName(timeRegion.fillColor, theme), + line: getColorFromHexRgbOrName(timeRegion.lineColor, theme), }; } const colorMode = colorModes[timeRegion.colorMode]; + if (colorMode.themeDependent === true) { - return config.bootData.user.lightTheme ? colorMode.lightColor : colorMode.darkColor; + return theme === GrafanaTheme.Light ? colorMode.lightColor : colorMode.darkColor; } - return colorMode.color; + return { + fill: getColorFromHexRgbOrName(colorMode.color.fill, theme), + line: getColorFromHexRgbOrName(colorMode.color.line, theme), + }; } export class TimeRegionManager { plot: any; timeRegions: any; - constructor(private panelCtrl) {} + constructor(private panelCtrl, private theme: GrafanaTheme = GrafanaTheme.Dark) {} draw(plot) { this.timeRegions = this.panelCtrl.panel.timeRegions; @@ -76,7 +86,7 @@ export class TimeRegionManager { const tRange = { from: moment(this.panelCtrl.range.from).utc(), to: moment(this.panelCtrl.range.to).utc() }; - let i, hRange, timeRegion, regions, fromStart, fromEnd, timeRegionColor; + let i, hRange, timeRegion, regions, fromStart, fromEnd, timeRegionColor: TimeRegionColorDefinition; const timeRegionsCopy = panel.timeRegions.map(a => ({ ...a })); @@ -200,7 +210,7 @@ export class TimeRegionManager { } } - timeRegionColor = getColor(timeRegion); + timeRegionColor = getColor(timeRegion, this.theme); for (let j = 0; j < regions.length; j++) { const r = regions[j]; diff --git a/public/app/plugins/panel/graph/time_regions_form.ts b/public/app/plugins/panel/graph/time_regions_form.ts index 5dc9c4016eb..aa7c94e3cf1 100644 --- a/public/app/plugins/panel/graph/time_regions_form.ts +++ b/public/app/plugins/panel/graph/time_regions_form.ts @@ -35,6 +35,9 @@ export class TimeRegionFormCtrl { colorMode: 'background6', fill: true, line: false, + // Default colors for new + fillColor: 'rgba(234, 112, 112, 0.12)', + lineColor: 'rgba(237, 46, 24, 0.60)' }); this.panelCtrl.render(); } diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts index 0e011e59439..4ba98890a31 100644 --- a/public/app/plugins/panel/heatmap/color_legend.ts +++ b/public/app/plugins/panel/heatmap/color_legend.ts @@ -5,6 +5,8 @@ import { contextSrv } from 'app/core/core'; import { tickStep } from 'app/core/utils/ticks'; import { getColorScale, getOpacityScale } from './color_scale'; import coreModule from 'app/core/core_module'; +import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; +import { GrafanaTheme } from '@grafana/ui'; const LEGEND_HEIGHT_PX = 6; const LEGEND_WIDTH_PX = 100; @@ -247,7 +249,10 @@ function drawSimpleOpacityLegend(elem, options) { .attr('width', rangeStep) .attr('height', legendHeight) .attr('stroke-width', 0) - .attr('fill', options.cardColor) + .attr( + 'fill', + getColorFromHexRgbOrName(options.cardColor, contextSrv.user.lightTheme ? GrafanaTheme.Light : GrafanaTheme.Dark) + ) .style('opacity', d => legendOpacityScale(d)); } } diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 71e059a5750..6e2dd148b90 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -304,6 +304,7 @@ export class HeatmapCtrl extends MetricsPanelCtrl { } onCardColorChange(newColor) { + console.log(newColor) this.panel.color.cardColor = newColor; this.render(); } diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts index 5d44231e362..68ee65547e2 100644 --- a/public/app/plugins/panel/heatmap/rendering.ts +++ b/public/app/plugins/panel/heatmap/rendering.ts @@ -8,6 +8,8 @@ import * as ticksUtils from 'app/core/utils/ticks'; import { HeatmapTooltip } from './heatmap_tooltip'; import { mergeZeroBuckets } from './heatmap_data_converter'; import { getColorScale, getOpacityScale } from './color_scale'; +import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; +import { GrafanaTheme } from '@grafana/ui'; const MIN_CARD_SIZE = 1, CARD_PADDING = 1, @@ -521,7 +523,6 @@ export class HeatmapRenderer { const maxValueAuto = this.data.cardStats.max; const maxValue = this.panel.color.max || maxValueAuto; const minValue = this.panel.color.min || 0; - const colorScheme = _.find(this.ctrl.colorSchemes, { value: this.panel.color.colorScheme, }); @@ -662,7 +663,12 @@ export class HeatmapRenderer { getCardColor(d) { if (this.panel.color.mode === 'opacity') { - return this.panel.color.cardColor; + + return getColorFromHexRgbOrName( + this.panel.color.cardColor, + contextSrv.user.lightTheme ? GrafanaTheme.Light : GrafanaTheme.Dark + ); + } else { return this.colorScale(d.count); } diff --git a/public/app/plugins/panel/singlestat/module.ts b/public/app/plugins/panel/singlestat/module.ts index b8e24616f0a..25d544d4922 100644 --- a/public/app/plugins/panel/singlestat/module.ts +++ b/public/app/plugins/panel/singlestat/module.ts @@ -8,6 +8,8 @@ import kbn from 'app/core/utils/kbn'; import config from 'app/core/config'; import TimeSeries from 'app/core/time_series2'; import { MetricsPanelCtrl } from 'app/plugins/sdk'; +import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; +import { GrafanaTheme } from '@grafana/ui'; class SingleStatCtrl extends MetricsPanelCtrl { static templateUrl = 'module.html'; @@ -479,6 +481,7 @@ class SingleStatCtrl extends MetricsPanelCtrl { plotCanvas.css(plotCss); const thresholds = []; + for (let i = 0; i < data.thresholds.length; i++) { thresholds.push({ value: data.thresholds[i], @@ -586,7 +589,10 @@ class SingleStatCtrl extends MetricsPanelCtrl { fill: 1, zero: false, lineWidth: 1, - fillColor: panel.sparkline.fillColor, + fillColor: getColorFromHexRgbOrName( + panel.sparkline.fillColor, + config.bootData.user.lightTheme ? GrafanaTheme.Light : GrafanaTheme.Dark + ), }, }, yaxes: { show: false }, @@ -603,7 +609,10 @@ class SingleStatCtrl extends MetricsPanelCtrl { const plotSeries = { data: data.flotpairs, - color: panel.sparkline.lineColor, + color: getColorFromHexRgbOrName( + panel.sparkline.lineColor, + config.bootData.user.lightTheme ? GrafanaTheme.Light : GrafanaTheme.Dark + ), }; $.plot(plotCanvas, [plotSeries], options); @@ -619,12 +628,17 @@ class SingleStatCtrl extends MetricsPanelCtrl { data.thresholds = panel.thresholds.split(',').map(strVale => { return Number(strVale.trim()); }); - data.colorMap = panel.colors; + + // Map panel colors to hex or rgb/a values + data.colorMap = panel.colors.map(color => + getColorFromHexRgbOrName(color, config.bootData.user.lightTheme ? GrafanaTheme.Light : GrafanaTheme.Dark) + ); const body = panel.gauge.show ? '' : getBigValueHtml(); if (panel.colorBackground) { const color = getColorForValue(data, data.value); + console.log(color); if (color) { $panelContainer.css('background-color', color); if (scope.fullscreen) { diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index a1366662955..ebc654f8be9 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -1,10 +1,12 @@ import _ from 'lodash'; import $ from 'jquery'; import { MetricsPanelCtrl } from 'app/plugins/sdk'; +import config from 'app/core/config'; import { transformDataToTable } from './transformers'; import { tablePanelEditor } from './editor'; import { columnOptionsTab } from './column_options'; import { TableRenderer } from './renderer'; +import { GrafanaTheme } from '@grafana/ui'; class TablePanelCtrl extends MetricsPanelCtrl { static templateUrl = 'module.html'; @@ -129,7 +131,8 @@ class TablePanelCtrl extends MetricsPanelCtrl { this.table, this.dashboard.isTimezoneUtc(), this.$sanitize, - this.templateSrv + this.templateSrv, + config.bootData.user.lightTheme ? GrafanaTheme.Light : GrafanaTheme.Dark, ); return super.render(this.table); diff --git a/public/app/plugins/panel/table/renderer.ts b/public/app/plugins/panel/table/renderer.ts index e4cc54e9c40..9f0354a6843 100644 --- a/public/app/plugins/panel/table/renderer.ts +++ b/public/app/plugins/panel/table/renderer.ts @@ -1,12 +1,21 @@ import _ from 'lodash'; import moment from 'moment'; import kbn from 'app/core/utils/kbn'; +import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; +import { GrafanaTheme } from '@grafana/ui'; export class TableRenderer { formatters: any[]; colorState: any; - constructor(private panel, private table, private isUtc, private sanitize, private templateSrv) { + constructor( + private panel, + private table, + private isUtc, + private sanitize, + private templateSrv, + private theme?: GrafanaTheme + ) { this.initColumns(); } @@ -49,10 +58,10 @@ export class TableRenderer { } for (let i = style.thresholds.length; i > 0; i--) { if (value >= style.thresholds[i - 1]) { - return style.colors[i]; + return getColorFromHexRgbOrName(style.colors[i], this.theme); } } - return _.first(style.colors); + return getColorFromHexRgbOrName(_.first(style.colors), this.theme); } defaultCellFormatter(v, style) { From ed48ecfe1d0852bbb29b704cfa8994c36866a1f8 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 21 Jan 2019 15:16:07 +0100 Subject: [PATCH 20/57] Updated table tests to new behavior for colors (values are always rendered as hex/rgb) --- .../components/ColorPicker/ColorPicker.tsx | 4 +- .../ColorPicker/SeriesColorPicker.tsx | 11 ------ .../ThresholdsEditor/ThresholdsEditor.tsx | 4 +- packages/grafana-ui/src/components/index.ts | 3 +- .../app/plugins/panel/heatmap/heatmap_ctrl.ts | 1 - .../panel/table/specs/renderer.test.ts | 37 ++++++++++--------- 6 files changed, 24 insertions(+), 36 deletions(-) delete mode 100644 packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index a412c0c9d39..eac97548157 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -4,6 +4,7 @@ import Popper, { RenderPopperArrowFn } from '../Tooltip/Popper'; import { ColorPickerPopover } from './ColorPickerPopover'; import { Themeable, GrafanaTheme } from '../../types'; import { getColorFromHexRgbOrName } from '../../utils/colorsPalette'; +import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; export interface ColorPickerProps extends Themeable { color: string; @@ -78,4 +79,5 @@ export const colorPickerFactory = ( }; }; -export default colorPickerFactory(ColorPickerPopover, 'ColorPicker'); +export const ColorPicker = colorPickerFactory(ColorPickerPopover, 'ColorPicker'); +export const SeriesColorPicker = colorPickerFactory(SeriesColorPickerPopover, 'SeriesColorPicker'); diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx deleted file mode 100644 index c81e9ca8e86..00000000000 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; -import { ColorPickerProps, colorPickerFactory } from './ColorPicker'; - -export interface SeriesColorPickerProps extends ColorPickerProps { - yaxis?: number; - optionalClass?: string; - onToggleAxis?: () => void; - children: JSX.Element; -} - -export default colorPickerFactory(SeriesColorPickerPopover ,'SeriesColorPicker') diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index a1677e58e91..520866ad7c8 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -1,8 +1,6 @@ import React, { PureComponent } from 'react'; -// import tinycolor, { ColorInput } from 'tinycolor2'; - import { Threshold } from '../../types'; -import ColorPicker from '../ColorPicker/ColorPicker'; +import { ColorPicker } from '../ColorPicker/ColorPicker'; import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup'; import { colors } from '../../utils'; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 68f8a2c0467..6e8f55df05b 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -14,9 +14,8 @@ export { FormLabel } from './FormLabel/FormLabel'; export { FormField } from './FormField/FormField'; export { LoadingPlaceholder } from './LoadingPlaceholder/LoadingPlaceholder'; -export { default as ColorPicker } from './ColorPicker/ColorPicker'; +export { ColorPicker, SeriesColorPicker } from './ColorPicker/ColorPicker'; export { SeriesColorPickerPopover } from './ColorPicker/SeriesColorPickerPopover'; -export { default as SeriesColorPicker } from './ColorPicker/SeriesColorPicker'; export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor'; export { Graph } from './Graph/Graph'; export { PanelOptionsGroup } from './PanelOptionsGroup/PanelOptionsGroup'; diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 6e2dd148b90..71e059a5750 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -304,7 +304,6 @@ export class HeatmapCtrl extends MetricsPanelCtrl { } onCardColorChange(newColor) { - console.log(newColor) this.panel.color.cardColor = newColor; this.render(); } diff --git a/public/app/plugins/panel/table/specs/renderer.test.ts b/public/app/plugins/panel/table/specs/renderer.test.ts index f9ccc574c91..017dc6f0538 100644 --- a/public/app/plugins/panel/table/specs/renderer.test.ts +++ b/public/app/plugins/panel/table/specs/renderer.test.ts @@ -1,6 +1,7 @@ import _ from 'lodash'; import TableModel from 'app/core/table_model'; import { TableRenderer } from '../renderer'; +import { SemiDarkOrange } from '@grafana/ui/src/utils/colorsPalette'; describe('when rendering table', () => { describe('given 13 columns', () => { @@ -47,7 +48,7 @@ describe('when rendering table', () => { decimals: 1, colorMode: 'value', thresholds: [50, 80], - colors: ['green', 'orange', 'red'], + colors: ['#00ff00', SemiDarkOrange.name, 'rgb(1,0,0)'], }, { pattern: 'String', @@ -138,7 +139,7 @@ describe('when rendering table', () => { ], colorMode: 'value', thresholds: [1, 2], - colors: ['green', 'orange', 'red'], + colors: ['#00ff00', SemiDarkOrange.name, 'rgb(1,0,0)'], }, { pattern: 'RangeMappingColored', @@ -158,7 +159,7 @@ describe('when rendering table', () => { ], colorMode: 'value', thresholds: [2, 5], - colors: ['green', 'orange', 'red'], + colors: ['#00ff00', SemiDarkOrange.name, 'rgb(1,0,0)'], }, ], }; @@ -226,19 +227,19 @@ describe('when rendering table', () => { expect(html).toBe('asd'); }); - it('colored cell should have style', () => { + it('colored cell should have style (handles HEX color values)', () => { const html = renderer.renderCell(2, 0, 40); - expect(html).toBe('40.0'); + expect(html).toBe('40.0'); }); - it('colored cell should have style', () => { + it('colored cell should have style (handles named color values', () => { const html = renderer.renderCell(2, 0, 55); - expect(html).toBe('55.0'); + expect(html).toBe(`55.0`); }); - it('colored cell should have style', () => { + it('colored cell should have style handles(rgb color values)', () => { const html = renderer.renderCell(2, 0, 85); - expect(html).toBe('85.0'); + expect(html).toBe('85.0'); }); it('unformated undefined should be rendered as string', () => { @@ -333,47 +334,47 @@ describe('when rendering table', () => { it('value should be mapped to text and colored cell should have style', () => { const html = renderer.renderCell(11, 0, 1); - expect(html).toBe('on'); + expect(html).toBe(`on`); }); it('value should be mapped to text and colored cell should have style', () => { const html = renderer.renderCell(11, 0, '1'); - expect(html).toBe('on'); + expect(html).toBe(`on`); }); it('value should be mapped to text and colored cell should have style', () => { const html = renderer.renderCell(11, 0, 0); - expect(html).toBe('off'); + expect(html).toBe('off'); }); it('value should be mapped to text and colored cell should have style', () => { const html = renderer.renderCell(11, 0, '0'); - expect(html).toBe('off'); + expect(html).toBe('off'); }); it('value should be mapped to text and colored cell should have style', () => { const html = renderer.renderCell(11, 0, '2.1'); - expect(html).toBe('2.1'); + expect(html).toBe('2.1'); }); it('value should be mapped to text (range) and colored cell should have style', () => { const html = renderer.renderCell(12, 0, 0); - expect(html).toBe('0'); + expect(html).toBe('0'); }); it('value should be mapped to text (range) and colored cell should have style', () => { const html = renderer.renderCell(12, 0, 1); - expect(html).toBe('on'); + expect(html).toBe('on'); }); it('value should be mapped to text (range) and colored cell should have style', () => { const html = renderer.renderCell(12, 0, 4); - expect(html).toBe('off'); + expect(html).toBe(`off`); }); it('value should be mapped to text (range) and colored cell should have style', () => { const html = renderer.renderCell(12, 0, '7.1'); - expect(html).toBe('7.1'); + expect(html).toBe('7.1'); }); }); }); From 93bb30a561f29e788794b7dee0632d512e3d5bb7 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 21 Jan 2019 16:00:09 +0100 Subject: [PATCH 21/57] Enable new color picker in Gauge and Thresholds editor, use ThemeProvider instead of ContextSrv --- .../grafana-ui/src/components/Gauge/Gauge.tsx | 26 +++++++++---------- .../ThresholdsEditor/ThresholdsEditor.tsx | 11 +++++--- packages/grafana-ui/src/types/panel.ts | 9 +------ public/app/core/services/context_srv.ts | 5 ---- public/app/plugins/panel/gauge/GaugePanel.tsx | 25 ++++++++++-------- .../plugins/panel/gauge/GaugePanelOptions.tsx | 25 ++++++++++++------ 6 files changed, 53 insertions(+), 48 deletions(-) diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.tsx index 63d875e9cd5..6f1dd009c7d 100644 --- a/packages/grafana-ui/src/components/Gauge/Gauge.tsx +++ b/packages/grafana-ui/src/components/Gauge/Gauge.tsx @@ -4,15 +4,15 @@ import $ from 'jquery'; import { ValueMapping, Threshold, - ThemeName, MappingType, BasicGaugeColor, - ThemeNames, ValueMap, RangeMap, } from '../../types/panel'; import { TimeSeriesVMs } from '../../types/series'; import { getValueFormat } from '../../utils/valueFormats/valueFormats'; +import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; +import { GrafanaTheme } from '@grafana/ui/src/types'; type TimeSeriesValue = string | number | null; @@ -31,7 +31,7 @@ export interface Props { suffix: string; unit: string; width: number; - theme?: ThemeName; + theme?: GrafanaTheme; } export class Gauge extends PureComponent { @@ -48,7 +48,7 @@ export class Gauge extends PureComponent { thresholds: [], unit: 'none', stat: 'avg', - theme: ThemeNames.Dark, + theme: GrafanaTheme.Dark, }; componentDidMount() { @@ -144,29 +144,29 @@ export class Gauge extends PureComponent { } getFontColor(value: TimeSeriesValue) { - const { thresholds } = this.props; + const { thresholds, theme } = this.props; if (thresholds.length === 1) { - return thresholds[0].color; + return getColorFromHexRgbOrName(thresholds[0].color, theme); } const atThreshold = thresholds.filter(threshold => (value as number) === threshold.value)[0]; if (atThreshold) { - return atThreshold.color; + return getColorFromHexRgbOrName(atThreshold.color, theme); } const belowThreshold = thresholds.filter(threshold => (value as number) > threshold.value); if (belowThreshold.length > 0) { const nearestThreshold = belowThreshold.sort((t1, t2) => t2.value - t1.value)[0]; - return nearestThreshold.color; + return getColorFromHexRgbOrName(nearestThreshold.color, theme); } return BasicGaugeColor.Red; } getFormattedThresholds() { - const { maxValue, minValue, thresholds } = this.props; + const { maxValue, minValue, thresholds, theme } = this.props; const thresholdsSortedByIndex = [...thresholds].sort((t1, t2) => t1.index - t2.index); const lastThreshold = thresholdsSortedByIndex[thresholdsSortedByIndex.length - 1]; @@ -174,13 +174,13 @@ export class Gauge extends PureComponent { const formattedThresholds = [ ...thresholdsSortedByIndex.map(threshold => { if (threshold.index === 0) { - return { value: minValue, color: threshold.color }; + return { value: minValue, color: getColorFromHexRgbOrName(threshold.color, theme) }; } const previousThreshold = thresholdsSortedByIndex[threshold.index - 1]; - return { value: threshold.value, color: previousThreshold.color }; + return { value: threshold.value, color: getColorFromHexRgbOrName(previousThreshold.color, theme) }; }), - { value: maxValue, color: lastThreshold.color }, + { value: maxValue, color: getColorFromHexRgbOrName(lastThreshold.color, theme) }, ]; return formattedThresholds; @@ -208,7 +208,7 @@ export class Gauge extends PureComponent { } const dimension = Math.min(width, height * 1.3); - const backgroundColor = theme === ThemeNames.Light ? 'rgb(230,230,230)' : 'rgb(38,38,38)'; + const backgroundColor = theme === GrafanaTheme.Light ? 'rgb(230,230,230)' : 'rgb(38,38,38)'; const fontScale = parseInt('80', 10) / 100; const fontSize = Math.min(dimension / 5, 100) * fontScale; const gaugeWidthReduceRatio = showThresholdLabels ? 1.5 : 1; diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index 520866ad7c8..78979e5e3e0 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -1,10 +1,11 @@ import React, { PureComponent } from 'react'; -import { Threshold } from '../../types'; +import { Threshold, Themeable } from '../../types'; import { ColorPicker } from '../ColorPicker/ColorPicker'; import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup'; import { colors } from '../../utils'; +import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; -export interface Props { +export interface Props extends Themeable { thresholds: Threshold[]; onChange: (thresholds: Threshold[]) => void; } @@ -187,6 +188,7 @@ export class ThresholdsEditor extends PureComponent { render() { const { thresholds } = this.state; + const { theme } = this.props; return ( @@ -197,7 +199,10 @@ export class ThresholdsEditor extends PureComponent {
this.onAddThreshold(threshold.index + 1)}>
-
+
{this.renderInput(threshold)}
); diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index 881bf920c27..f2a699839b8 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -36,7 +36,7 @@ export interface PanelMenuItem { export interface Threshold { index: number; value: number; - color?: string; + color: string; } export enum BasicGaugeColor { @@ -66,10 +66,3 @@ export interface RangeMap extends BaseMap { from: string; to: string; } - -export type ThemeName = 'dark' | 'light'; - -export enum ThemeNames { - Dark = 'dark', - Light = 'light', -} diff --git a/public/app/core/services/context_srv.ts b/public/app/core/services/context_srv.ts index 05985aae999..7bb753e6f71 100644 --- a/public/app/core/services/context_srv.ts +++ b/public/app/core/services/context_srv.ts @@ -2,7 +2,6 @@ import config from 'app/core/config'; import _ from 'lodash'; import coreModule from 'app/core/core_module'; import store from 'app/core/store'; -import { ThemeNames, ThemeName } from '@grafana/ui'; export class User { isGrafanaAdmin: any; @@ -64,10 +63,6 @@ export class ContextSrv { hasAccessToExplore() { return (this.isEditor || config.viewersCanEdit) && config.exploreEnabled; } - - getTheme(): ThemeName { - return this.user.lightTheme ? ThemeNames.Light : ThemeNames.Dark; - } } const contextSrv = new ContextSrv(); diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index cd92f697ced..a525886b809 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -2,7 +2,6 @@ import React, { PureComponent } from 'react'; // Services & Utils -import { contextSrv } from 'app/core/core'; import { processTimeSeries } from '@grafana/ui'; // Components @@ -11,11 +10,11 @@ import { Gauge } from '@grafana/ui'; // Types import { GaugeOptions } from './types'; import { PanelProps, NullValueMode } from '@grafana/ui/src/types'; +import { ThemeProvider } from 'app/core/utils/ConfigProvider'; interface Props extends PanelProps {} export class GaugePanel extends PureComponent { - render() { const { timeSeries, width, height, onInterpolate, options } = this.props; @@ -28,15 +27,19 @@ export class GaugePanel extends PureComponent { }); return ( - + + {(theme) => ( + + )} + ); } } diff --git a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx index 18a445d840d..655c596ce84 100644 --- a/public/app/plugins/panel/gauge/GaugePanelOptions.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelOptions.tsx @@ -11,6 +11,7 @@ import { import ValueOptions from 'app/plugins/panel/gauge/ValueOptions'; import GaugeOptionsEditor from './GaugeOptionsEditor'; import { GaugeOptions } from './types'; +import { ThemeProvider } from 'app/core/utils/ConfigProvider'; export const defaultProps = { options: { @@ -46,15 +47,23 @@ export default class GaugePanelOptions extends PureComponent - - - - - + + {(theme) => ( + <> + + + + + - - + + + )} + ); } } From 2b34933554f36e3156e3fafd9bd790402c7e9563 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 21 Jan 2019 16:18:28 +0100 Subject: [PATCH 22/57] Stories - fix import --- .../src/components/ColorPicker/ColorPicker.story.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx index 4b48e8503b4..484b82cc59a 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx @@ -1,7 +1,7 @@ import React, { FunctionComponent } from 'react'; import { storiesOf } from '@storybook/react'; -import SeriesColorPicker from './SeriesColorPicker'; -import ColorPicker from './ColorPicker'; +import { SeriesColorPicker } from './ColorPicker'; +import { ColorPicker } from './ColorPicker'; import { UseState } from './NamedColorsPalette.story'; import { withKnobs, select } from '@storybook/addon-knobs'; import { GrafanaTheme } from '../../types'; @@ -69,7 +69,7 @@ ColorPickerStories.add('Series color picker', () => { onChange={color => updateSelectedColor(color)} theme={selectedTheme || undefined} > -
Open color picker
+
Open color picker
); }} From d86d0639001114abe61715c8560a23a1e27717af Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 22 Jan 2019 09:10:47 +0100 Subject: [PATCH 23/57] Make popover hide delay configurable to enable better UX --- .../components/ColorPicker/ColorPicker.tsx | 4 +- .../ColorPicker/ColorPickerPopover.tsx | 1 - .../src/components/Tooltip/Popper.tsx | 82 ++++++++++--------- .../components/Tooltip/PopperController.tsx | 22 ++++- 4 files changed, 64 insertions(+), 45 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index eac97548157..68edf8abcd1 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -37,7 +37,7 @@ export const colorPickerFactory = ( }; return ( - + {(showPopper, hidePopper, popperProps) => { return ( <> @@ -79,5 +79,5 @@ export const colorPickerFactory = ( }; }; -export const ColorPicker = colorPickerFactory(ColorPickerPopover, 'ColorPicker'); +export const ColorPicker = colorPickerFactory(ColorPickerPopover, 'ColorPicker'); export const SeriesColorPicker = colorPickerFactory(SeriesColorPickerPopover, 'SeriesColorPicker'); diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index c9242f72ebf..5ac0d38a40a 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -6,7 +6,6 @@ import { ColorPickerProps } from './ColorPicker'; import { GrafanaTheme, Themeable } from '../../types'; import { PopperContentProps } from '../Tooltip/PopperController'; -// const DEFAULT_COLOR = '#000000'; export interface Props extends ColorPickerProps, Themeable, PopperContentProps {} diff --git a/packages/grafana-ui/src/components/Tooltip/Popper.tsx b/packages/grafana-ui/src/components/Tooltip/Popper.tsx index fa49596755c..93a896d97ed 100644 --- a/packages/grafana-ui/src/components/Tooltip/Popper.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Popper.tsx @@ -13,8 +13,8 @@ const defaultTransitionStyles = { const transitionStyles: { [key: string]: object } = { exited: { opacity: 0 }, entering: { opacity: 0 }, - entered: { opacity: 1 }, - exiting: { opacity: 0 }, + entered: { opacity: 1, transitionDelay: '0s' }, + exiting: { opacity: 0, transitionDelay: '500ms' }, }; export type RenderPopperArrowFn = ( @@ -41,46 +41,48 @@ class Popper extends PureComponent { return ( - {transitionState => ( - - - {({ ref, style, placement, arrowProps, scheduleUpdate }) => { - return ( -
-
- {typeof content === 'string' - ? content - : React.cloneElement(content, { - updatePopperPosition: scheduleUpdate, + {transitionState => { + return ( + + + {({ ref, style, placement, arrowProps, scheduleUpdate }) => { + return ( +
+
+ {typeof content === 'string' + ? content + : React.cloneElement(content, { + updatePopperPosition: scheduleUpdate, + })} + {renderArrow && + renderArrow({ + arrowProps, + placement, })} - {renderArrow && - renderArrow({ - arrowProps, - placement, - })} +
-
- ); - }} - - - )} + ); + }} + + + ); + }} ); diff --git a/packages/grafana-ui/src/components/Tooltip/PopperController.tsx b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx index a4bc555a4db..770d1ce9f37 100644 --- a/packages/grafana-ui/src/components/Tooltip/PopperController.tsx +++ b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx @@ -1,9 +1,11 @@ import React from 'react'; import * as PopperJS from 'popper.js'; -// This API allows popovers to update Popper's position when e.g. popover content chaanges +// This API allows popovers to update Popper's position when e.g. popover content changes // updatePopperPosition is delivered to content by react-popper -export interface PopperContentProps { updatePopperPosition?: () => void; } +export interface PopperContentProps { + updatePopperPosition?: () => void; +} export type PopperContent = string | React.ReactElement; @@ -29,6 +31,7 @@ interface Props { content: PopperContent; className?: string; children: PopperControllerRenderProp; + hideAfter?: number; } interface State { @@ -37,6 +40,8 @@ interface State { } class PopperController extends React.Component { + private hideTimeout: any; + constructor(props: Props) { super(props); @@ -58,6 +63,10 @@ class PopperController extends React.Component { } showPopper = () => { + if (this.hideTimeout) { + clearTimeout(this.hideTimeout); + } + this.setState(prevState => ({ ...prevState, show: true, @@ -65,6 +74,15 @@ class PopperController extends React.Component { }; hidePopper = () => { + if (this.props.hideAfter !== 0) { + this.hideTimeout = setTimeout(() => { + this.setState(prevState => ({ + ...prevState, + show: false, + })); + }, this.props.hideAfter); + return; + } this.setState(prevState => ({ ...prevState, show: false, From dd8f7aa7a9c7f7240b92c347006ff8df7e91bdd5 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 22 Jan 2019 10:58:31 +0100 Subject: [PATCH 24/57] CustomScrollbar - expose underlying's react-custom-scrollbars API to allow scroll tracks config. Updated config for Graph legend not to display horizontal scroll track which caused legend being not accessible in ceertain situations (e.g. when native OS scroll overlay disabled) --- .../components/CustomScrollbar/CustomScrollbar.tsx | 12 +++++++----- public/app/plugins/panel/graph/Legend/Legend.tsx | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx index eb50944ad35..434ee03c7a1 100644 --- a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx +++ b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx @@ -9,6 +9,8 @@ interface Props { autoHideDuration?: number; autoMaxHeight?: string; hideTracksWhenNotNeeded?: boolean; + renderTrackHorizontal?: React.FunctionComponent; + renderTrackVertical?: React.FunctionComponent; scrollTop?: number; setScrollTop: (value: React.MouseEvent) => void; autoHeightMin?: number | string; @@ -26,7 +28,7 @@ export class CustomScrollbar extends PureComponent { autoMaxHeight: '100%', hideTracksWhenNotNeeded: false, setScrollTop: () => {}, - autoHeightMin: '0' + autoHeightMin: '0', }; private ref: React.RefObject; @@ -45,7 +47,7 @@ export class CustomScrollbar extends PureComponent { } else { ref.scrollTop(this.props.scrollTop); } - } + } } componentDidMount() { @@ -57,7 +59,7 @@ export class CustomScrollbar extends PureComponent { } render() { - const { customClassName, children, autoMaxHeight } = this.props; + const { customClassName, children, autoMaxHeight, renderTrackHorizontal, renderTrackVertical } = this.props; return ( { // These autoHeightMin & autoHeightMax options affect firefox and chrome differently. // Before these where set to inhert but that caused problems with cut of legends in firefox autoHeightMax={autoMaxHeight} - renderTrackHorizontal={props =>
} - renderTrackVertical={props =>
} + renderTrackHorizontal={renderTrackHorizontal || (props =>
)} + renderTrackVertical={renderTrackVertical || (props =>
)} renderThumbHorizontal={props =>
} renderThumbVertical={props =>
} renderView={props =>
} diff --git a/public/app/plugins/panel/graph/Legend/Legend.tsx b/public/app/plugins/panel/graph/Legend/Legend.tsx index b83cd7bd88c..47a84622947 100644 --- a/public/app/plugins/panel/graph/Legend/Legend.tsx +++ b/public/app/plugins/panel/graph/Legend/Legend.tsx @@ -311,7 +311,7 @@ class LegendTableHeaderItem extends PureComponent { render() { return ( - +
}> ); From 5b63ee4bfbfb928b8e3c5318df8acf5f6f1b7b63 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 22 Jan 2019 13:19:47 +0100 Subject: [PATCH 25/57] Storybook - add actions addon --- packages/grafana-ui/.storybook/addons.ts | 1 + packages/grafana-ui/package.json | 2 ++ yarn.lock | 33 +++++++++++++++++++++--- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/grafana-ui/.storybook/addons.ts b/packages/grafana-ui/.storybook/addons.ts index 6622fe475d5..2efe80ebffa 100644 --- a/packages/grafana-ui/.storybook/addons.ts +++ b/packages/grafana-ui/.storybook/addons.ts @@ -1 +1,2 @@ import '@storybook/addon-knobs/register'; +import '@storybook/addon-actions/register'; diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 6950a824206..0d1b14a7150 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -30,6 +30,7 @@ "tinycolor2": "^1.4.1" }, "devDependencies": { + "@storybook/addon-actions": "^4.1.7", "@storybook/addon-info": "^4.1.6", "@storybook/addon-knobs": "^4.1.7", "@storybook/react": "^4.1.4", @@ -42,6 +43,7 @@ "@types/react-custom-scrollbars": "^4.0.5", "@types/react-test-renderer": "^16.0.3", "@types/react-transition-group": "^2.0.15", + "@types/storybook__addon-actions": "^3.4.1", "@types/storybook__addon-info": "^3.4.2", "@types/storybook__addon-knobs": "^4.0.0", "@types/storybook__react": "^4.0.0", diff --git a/yarn.lock b/yarn.lock index 04678dd8685..3cf75250712 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1073,6 +1073,26 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@storybook/addon-actions@^4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-4.1.7.tgz#7c2625ea45b0c51d907d97d24d8dc56a701967b9" + integrity sha512-xhZCkehXZl3FM/2Dw1YOla0KcXyDgPVJbv6fRt0ebbmzaVbeB+cdknZ10SImPbKUWLCdBEKKIAR/CNF29hAH5A== + dependencies: + "@emotion/core" "^0.13.1" + "@emotion/provider" "^0.11.2" + "@emotion/styled" "^0.10.6" + "@storybook/addons" "4.1.7" + "@storybook/components" "4.1.7" + "@storybook/core-events" "4.1.7" + core-js "^2.5.7" + deep-equal "^1.0.1" + global "^4.3.2" + lodash "^4.17.11" + make-error "^1.3.5" + prop-types "^15.6.2" + react-inspector "^2.3.0" + uuid "^3.3.2" + "@storybook/addon-info@^4.1.6": version "4.1.6" resolved "https://registry.yarnpkg.com/@storybook/addon-info/-/addon-info-4.1.6.tgz#48479487cd13d674807b77dbd42500cd770c954f" @@ -1734,7 +1754,7 @@ resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-1.10.35.tgz#4e5c2b1e5b3bf0b863efb8c5e70081f52e6c9518" integrity sha512-SVtqEcudm7yjkTwoRA1gC6CNMhGDdMx4Pg8BPdiqI7bXXdCn1BPmtxgeWYQOgDxrq53/5YTlhq5ULxBEAlWIBg== -"@types/lodash@^4.14.119": +"@types/lodash@4.14.119", "@types/lodash@^4.14.119": version "4.14.119" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39" integrity sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw== @@ -1803,7 +1823,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@16.7.6", "@types/react@^16.7.6": +"@types/react@*", "@types/react@^16.7.6": version "16.7.6" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040" integrity sha512-QBUfzftr/8eg/q3ZRgf/GaDP6rTYc7ZNem+g4oZM38C9vXyV8AWRWaTQuW5yCoZTsfHrN7b3DeEiUnqH9SrnpA== @@ -1811,6 +1831,11 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/storybook__addon-actions@^3.4.1": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@types/storybook__addon-actions/-/storybook__addon-actions-3.4.1.tgz#8f90d76b023b58ee794170f2fe774a3fddda2c1d" + integrity sha512-An8pNb1/7QhkdOT8Ht5WjJsSxAh2mWti/J4eILwUHpXVZ1j3xlVaOzwTbg8twN4DjgOAggjEDOj6Bx8YOWh9Pg== + "@types/storybook__addon-info@^3.4.2": version "3.4.2" resolved "https://registry.yarnpkg.com/@types/storybook__addon-info/-/storybook__addon-info-3.4.2.tgz#31a27e66867f02e593579cc58f54d22e726b0925" @@ -4505,7 +4530,7 @@ caniuse-api@^1.5.2: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-db@1.0.30000772, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: +caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000772" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b" integrity sha1-UarokXaChureSj2DGep21qAbUSs= @@ -11018,7 +11043,7 @@ make-dir@^1.0.0, make-dir@^1.1.0: dependencies: pify "^3.0.0" -make-error@1.x: +make-error@1.x, make-error@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== From c3e5737fb87d153efd48aa31d15c65e7d4adc477 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 22 Jan 2019 13:35:57 +0100 Subject: [PATCH 26/57] Make named colors optional in color picker, enable named colors in graph legend series picker --- .../src/components/ColorPicker/ColorPicker.tsx | 17 ++++++++++++++++- .../panel/graph/Legend/LegendSeriesItem.tsx | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 68edf8abcd1..12ee4977137 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -9,6 +9,7 @@ import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; export interface ColorPickerProps extends Themeable { color: string; onChange: (color: string) => void; + enableNamedColors?: boolean; withArrow?: boolean; children?: JSX.Element; } @@ -22,10 +23,24 @@ export const colorPickerFactory = ( static displayName = displayName || 'ColorPicker'; pickerTriggerRef = createRef(); + handleColorChange = (color: string) => { + const { enableNamedColors, onChange } = this.props; + + if (enableNamedColors) { + return onChange(color); + } + + return onChange(getColorFromHexRgbOrName(color)); + + }; render() { - const popoverElement = React.createElement(popover, this.props); + const popoverElement = React.createElement(popover, { + ...this.props, + onChange: this.handleColorChange, + }); const { theme, withArrow, children } = this.props; + // TODO: hoist that this shit const renderArrow: RenderPopperArrowFn = ({ arrowProps, placement }) => { return (
From ba4b774c96efeb09450f01cec63f5327f189f148 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 22 Jan 2019 13:36:21 +0100 Subject: [PATCH 27/57] Stories update for color picker --- .../ColorPicker/ColorPicker.story.tsx | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx index 484b82cc59a..d4a0734fd2f 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx @@ -3,7 +3,8 @@ import { storiesOf } from '@storybook/react'; import { SeriesColorPicker } from './ColorPicker'; import { ColorPicker } from './ColorPicker'; import { UseState } from './NamedColorsPalette.story'; -import { withKnobs, select } from '@storybook/addon-knobs'; +import { withKnobs, select, boolean } from '@storybook/addon-knobs'; +import { action } from '@storybook/addon-actions'; import { GrafanaTheme } from '../../types'; // TODO: extract to decorators @@ -22,47 +23,55 @@ export const CenteredStory: FunctionComponent<{}> = ({ children }) => { ); }; -const ColorPickerStories = storiesOf('UI/ColorPicker', module); +const getColorPickerKnobs = (defaultTheme: GrafanaTheme = GrafanaTheme.Light, enableNamedColors?: boolean) => { + return { + selectedTheme: select( + 'Theme', + { + Default: '', + Light: GrafanaTheme.Light, + Dark: GrafanaTheme.Dark, + }, + defaultTheme + ), + enableNamedColors: boolean('Enable named colors', !!enableNamedColors), + }; +}; +const ColorPickerStories = storiesOf('UI/ColorPicker', module); ColorPickerStories.addDecorator(story => {story()}); ColorPickerStories.addDecorator(withKnobs); ColorPickerStories.add('Color picker', () => { - const selectedTheme = select( - 'Theme', - { - Default: '', - Light: GrafanaTheme.Light, - Dark: GrafanaTheme.Dark, - }, - GrafanaTheme.Light - ); - + const { selectedTheme, enableNamedColors } = getColorPickerKnobs(); return ( {(selectedColor, updateSelectedColor) => { - return ; + return ( + { + action('Color changed')(color); + updateSelectedColor(color); + }} + theme={selectedTheme || undefined} + /> + ); }} ); }); ColorPickerStories.add('Series color picker', () => { - const selectedTheme = select( - 'Theme', - { - Default: '', - Light: GrafanaTheme.Light, - Dark: GrafanaTheme.Dark, - }, - GrafanaTheme.Light - ); + const { selectedTheme, enableNamedColors } = getColorPickerKnobs(); return ( {(selectedColor, updateSelectedColor) => { return ( {}} color={selectedColor} From 9fd9bba1f42ceb3bcb724c2170ba9cd6b928a79b Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 22 Jan 2019 20:00:22 +0100 Subject: [PATCH 28/57] Implemented new spectrum palette --- .../src/components/ColorPicker/ColorInput.tsx | 94 +++++++++++ .../components/ColorPicker/ColorPicker.tsx | 4 +- .../ColorPicker/ColorPickerPopover.tsx | 7 +- .../ColorPicker/SpectrumPalette.story.tsx | 57 +++++++ .../ColorPicker/SpectrumPalette.tsx | 149 ++++++++++-------- 5 files changed, 240 insertions(+), 71 deletions(-) create mode 100644 packages/grafana-ui/src/components/ColorPicker/ColorInput.tsx create mode 100644 packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.story.tsx diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorInput.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorInput.tsx new file mode 100644 index 00000000000..4c5df5314b8 --- /dev/null +++ b/packages/grafana-ui/src/components/ColorPicker/ColorInput.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { ColorPickerProps } from './ColorPicker'; +import tinycolor from 'tinycolor2'; +import { debounce } from 'lodash'; + +interface ColorInputState { + previousColor: string; + value: string; +} + +interface ColorInputProps extends ColorPickerProps { + style?: React.CSSProperties; +} + +class ColorInput extends React.PureComponent { + constructor(props: ColorInputProps) { + super(props); + this.state = { + previousColor: props.color, + value: props.color, + }; + + this.updateColor = debounce(this.updateColor, 100); + } + + static getDerivedStateFromProps(props: ColorPickerProps, state: ColorInputState) { + const newColor = tinycolor(props.color); + if (newColor.isValid() && props.color !== state.previousColor) { + return { + ...state, + previousColor: props.color, + value: newColor.toString(), + }; + } + + return state; + } + updateColor = (color: string) => { + this.props.onChange(color); + }; + + handleChange = (event: React.SyntheticEvent) => { + const newColor = tinycolor(event.currentTarget.value); + + this.setState({ + value: event.currentTarget.value, + }); + + if (newColor.isValid()) { + this.updateColor(newColor.toString()); + } + }; + + handleBlur = () => { + const newColor = tinycolor(this.state.value); + + if (!newColor.isValid()) { + this.setState({ + value: this.props.color, + }); + } + }; + + render() { + const { value } = this.state; + return ( +
+
+
+ +
+
+ ); + } +} + +export default ColorInput; diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 12ee4977137..f807b09bd93 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -29,10 +29,9 @@ export const colorPickerFactory = ( if (enableNamedColors) { return onChange(color); } - return onChange(getColorFromHexRgbOrName(color)); - }; + render() { const popoverElement = React.createElement(popover, { ...this.props, @@ -40,7 +39,6 @@ export const colorPickerFactory = ( }); const { theme, withArrow, children } = this.props; - // TODO: hoist that this shit const renderArrow: RenderPopperArrowFn = ({ arrowProps, placement }) => { return (
{ } handleSpectrumColorSelect = (color: any) => { - this.props.onChange(color.toRgbString()); + this.props.onChange(color); }; renderPicker = () => { @@ -32,7 +31,7 @@ export class ColorPickerPopover extends React.Component { const { color, onChange, theme } = this.props; return activePicker === 'spectrum' ? ( - + ) : ( ); diff --git a/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.story.tsx b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.story.tsx new file mode 100644 index 00000000000..843359d0168 --- /dev/null +++ b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.story.tsx @@ -0,0 +1,57 @@ +import React, { FunctionComponent } from 'react'; +import { storiesOf } from '@storybook/react'; +import SpectrumPalette from './SpectrumPalette'; + +const CenteredStory: FunctionComponent<{}> = ({ children }) => { + return ( +
+ {children} +
+ ); +}; + +interface StateHolderProps { + initialState: T; + children: (currentState: T, updateState: (nextState: T) => void) => JSX.Element; +} + +export class UseState extends React.Component, { value: T }> { + constructor(props: StateHolderProps) { + super(props); + this.state = { + value: props.initialState, + }; + } + + handleStateUpdate = (nextState: T) => { + this.setState({ value: nextState }); + }; + render() { + return this.props.children(this.state.value, this.handleStateUpdate); + } +} + +storiesOf('UI/SpectrumPalette', module) + .addDecorator(story => {story()}) + .add('Named colors swatch - support for named colors', () => { + + return ( + + {(selectedColor, updateSelectedColor) => { + return ( + + ); + }} + + ); + }); diff --git a/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx index 154ff4b5bab..b38d2f3031a 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx @@ -1,72 +1,93 @@ import React from 'react'; -import _ from 'lodash'; -import $ from 'jquery'; -import '../../vendor/spectrum'; +import { CustomPicker, ColorResult } from 'react-color'; -export interface Props { +import { Saturation, Hue, Alpha } from 'react-color/lib/components/common'; +import { getColorFromHexRgbOrName } from '../../utils/colorsPalette'; +import tinycolor from 'tinycolor2'; +import ColorInput from './ColorInput'; + +export interface SpectrumPaletteProps { color: string; - options: object; - onColorSelect: (color: string) => void; + onChange: (color: string) => void; } -export class SpectrumPalette extends React.Component { - elem: any; - isMoving: boolean; +// @ts-ignore +const SpectrumPicker = CustomPicker(({ rgb, hsl, onChange, renderers }) => { + return ( +
+
+
+
+ {/* + // @ts-ignore */} + +
+
+ {/* + // @ts-ignore */} + +
+
- constructor(props: Props) { - super(props); - this.onSpectrumMove = this.onSpectrumMove.bind(this); - this.setComponentElem = this.setComponentElem.bind(this); - } +
+ {/* + // @ts-ignore */} + +
+
+
+ ); +}); - setComponentElem(elem: any) { - this.elem = $(elem); - } +const SpectrumPalette: React.FunctionComponent = ({ color, onChange }) => { + return ( +
+ { + onChange(tinycolor(a.rgb).toString()); + }} + /> + +
+ ); +}; - onSpectrumMove(color: any) { - this.isMoving = true; - this.props.onColorSelect(color); - } - - componentDidMount() { - const spectrumOptions = _.assignIn( - { - flat: true, - showAlpha: true, - showButtons: false, - color: this.props.color, - appendTo: this.elem, - move: this.onSpectrumMove, - }, - this.props.options - ); - - this.elem.spectrum(spectrumOptions); - this.elem.spectrum('show'); - this.elem.spectrum('set', this.props.color); - } - - componentWillUpdate(nextProps: any) { - // If user move pointer over spectrum field this produce 'move' event and component - // may update props.color. We don't want to update spectrum color in this case, so we can use - // isMoving flag for tracking moving state. Flag should be cleared in componentDidUpdate() which - // is called after updating occurs (when user finished moving). - if (!this.isMoving) { - this.elem.spectrum('set', nextProps.color); - } - } - - componentDidUpdate() { - if (this.isMoving) { - this.isMoving = false; - } - } - - componentWillUnmount() { - this.elem.spectrum('destroy'); - } - - render() { - return
; - } -} +export default SpectrumPalette; From 5c5265829a65b61447ca7227bd8a2ccd39de1902 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 23 Jan 2019 09:39:59 +0100 Subject: [PATCH 29/57] Reduce padding in color picker popover --- .../grafana-ui/src/components/ColorPicker/_ColorPicker.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss index f35e47f126a..3ee97a2a357 100644 --- a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss +++ b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss @@ -129,7 +129,7 @@ $arrowSize: 15px; .ColorPickerPopover__content { width: 360px; - padding: 32px; + padding: 24px; } .ColorPickerPopover__tabs { From 53f52a67e1f200c5ba014cb928d29415c3269926 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 23 Jan 2019 13:29:56 +0100 Subject: [PATCH 30/57] Added deprecation warning to old color picker API props. Moved named color support handling to color popovers --- .../components/ColorPicker/ColorPicker.tsx | 29 +++++++++++++------ .../ColorPicker/ColorPickerPopover.tsx | 21 +++++++++----- .../ColorPicker/SeriesColorPickerPopover.tsx | 23 ++++++--------- .../src/utils/propDeprecationWarning.ts | 6 ++++ 4 files changed, 49 insertions(+), 30 deletions(-) create mode 100644 packages/grafana-ui/src/utils/propDeprecationWarning.ts diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index f807b09bd93..a08fd27b23e 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -5,10 +5,23 @@ import { ColorPickerPopover } from './ColorPickerPopover'; import { Themeable, GrafanaTheme } from '../../types'; import { getColorFromHexRgbOrName } from '../../utils/colorsPalette'; import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; +import propDeprecationWarning from '../../utils/propDeprecationWarning'; +type ColorPickerChangeHandler = (color: string) => void; + +export const handleColorPickerPropsDeprecation = (componentName: string, props: ColorPickerProps) => { + const { onColorChange } = props; + if (onColorChange) { + propDeprecationWarning(componentName, 'onColorChange', 'onChange'); + } +}; export interface ColorPickerProps extends Themeable { color: string; - onChange: (color: string) => void; + onChange: ColorPickerChangeHandler; + /** + * @deprecated Use onChange instead + */ + onColorChange?: ColorPickerChangeHandler; enableNamedColors?: boolean; withArrow?: boolean; children?: JSX.Element; @@ -16,20 +29,18 @@ export interface ColorPickerProps extends Themeable { export const colorPickerFactory = ( popover: React.ComponentType, - displayName?: string, + displayName = 'ColorPicker', renderPopoverArrowFunction?: RenderPopperArrowFn ) => { return class ColorPicker extends Component { - static displayName = displayName || 'ColorPicker'; + static displayName = displayName; pickerTriggerRef = createRef(); handleColorChange = (color: string) => { - const { enableNamedColors, onChange } = this.props; + const { onColorChange, onChange } = this.props; + const changeHandler = (onColorChange || onChange) as ColorPickerChangeHandler; - if (enableNamedColors) { - return onChange(color); - } - return onChange(getColorFromHexRgbOrName(color)); + return changeHandler(color); }; render() { @@ -77,7 +88,7 @@ export const colorPickerFactory = (
diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index fe12082cb5e..bb82e5b389f 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -1,7 +1,7 @@ import React from 'react'; import NamedColorsPicker from './NamedColorsPalette'; -import { getColorName } from '../..//utils/colorsPalette'; -import { ColorPickerProps } from './ColorPicker'; +import { getColorName, getColorFromHexRgbOrName } from '../..//utils/colorsPalette'; +import { ColorPickerProps, handleColorPickerPropsDeprecation } from './ColorPicker'; import { GrafanaTheme, Themeable } from '../../types'; import { PopperContentProps } from '../Tooltip/PopperController'; import SpectrumPalette from './SpectrumPalette'; @@ -20,20 +20,27 @@ export class ColorPickerPopover extends React.Component { this.state = { activePicker: 'palette', }; + handleColorPickerPropsDeprecation('ColorPickerPopover', props); } - handleSpectrumColorSelect = (color: any) => { - this.props.onChange(color); + handleChange = (color: any) => { + const { onColorChange, onChange, enableNamedColors } = this.props; + const changeHandler = onColorChange || onChange; + + if (enableNamedColors) { + return changeHandler(color); + } + changeHandler(getColorFromHexRgbOrName(color)); }; renderPicker = () => { const { activePicker } = this.state; - const { color, onChange, theme } = this.props; + const { color, theme } = this.props; return activePicker === 'spectrum' ? ( - + ) : ( - + ); }; diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx index f019dc1634b..559b3f4a550 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx @@ -1,24 +1,19 @@ import React, { FunctionComponent } from 'react'; -import { ColorPickerPopover } from './ColorPickerPopover'; -import { Themeable } from '../../types'; -import { ColorPickerProps } from './ColorPicker'; -import { PopperContentProps } from '../Tooltip/PopperController'; -export interface SeriesColorPickerPopoverProps extends ColorPickerProps, Themeable, PopperContentProps { +import { ColorPickerPopover } from './ColorPickerPopover'; +import { ColorPickerProps } from './ColorPicker'; +import { PopperContentProps } from '../Tooltip/PopperController'; + +export interface SeriesColorPickerPopoverProps extends ColorPickerProps, PopperContentProps { yaxis?: number; onToggleAxis?: () => void; } -export const SeriesColorPickerPopover: FunctionComponent = ({ - onChange, - color, - theme, - yaxis, - onToggleAxis, - updatePopperPosition -}) => { +export const SeriesColorPickerPopover: FunctionComponent = props => { + const { yaxis, onToggleAxis, color, ...colorPickerProps } = props; + return ( - +
{yaxis && }
); diff --git a/packages/grafana-ui/src/utils/propDeprecationWarning.ts b/packages/grafana-ui/src/utils/propDeprecationWarning.ts new file mode 100644 index 00000000000..a277395c090 --- /dev/null +++ b/packages/grafana-ui/src/utils/propDeprecationWarning.ts @@ -0,0 +1,6 @@ +const propDeprecationWarning = (componentName: string, propName: string, newPropName: string) => { + const message = `[Deprecation warning] ${componentName}: ${propName} is deprecated. Use ${newPropName} instead`; + console.warn(message); +}; + +export default propDeprecationWarning; From ab7756ecb4838eeb0d2fee6f2b7006c927669e1f Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 23 Jan 2019 13:46:13 +0100 Subject: [PATCH 31/57] Make default color picker close on trigger mouse leave --- packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index a08fd27b23e..ee7ea241f06 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -83,7 +83,7 @@ export const colorPickerFactory = ( onMouseLeave: hidePopper, }) ) : ( -
+
Date: Wed, 23 Jan 2019 14:46:38 +0100 Subject: [PATCH 32/57] Implement pointer component for spectrum palette sliders --- .../ColorPicker/ColorPickerPopover.tsx | 2 +- .../ColorPicker/SpectrumPalette.story.tsx | 19 +++-- .../ColorPicker/SpectrumPalette.tsx | 17 ++-- .../ColorPicker/SpectrumPalettePointer.tsx | 80 +++++++++++++++++++ 4 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 packages/grafana-ui/src/components/ColorPicker/SpectrumPalettePointer.tsx diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index bb82e5b389f..a1e1185983d 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -38,7 +38,7 @@ export class ColorPickerPopover extends React.Component { const { color, theme } = this.props; return activePicker === 'spectrum' ? ( - + ) : ( ); diff --git a/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.story.tsx b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.story.tsx index 843359d0168..0425f12d6ec 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.story.tsx @@ -1,6 +1,8 @@ import React, { FunctionComponent } from 'react'; import { storiesOf } from '@storybook/react'; import SpectrumPalette from './SpectrumPalette'; +import { withKnobs, select } from '@storybook/addon-knobs'; +import { GrafanaTheme } from '../../types'; const CenteredStory: FunctionComponent<{}> = ({ children }) => { return ( @@ -40,17 +42,20 @@ export class UseState extends React.Component, { value: T storiesOf('UI/SpectrumPalette', module) .addDecorator(story => {story()}) + .addDecorator(withKnobs) .add('Named colors swatch - support for named colors', () => { - + const selectedTheme = select( + 'Theme', + { + Light: GrafanaTheme.Light, + Dark: GrafanaTheme.Dark, + }, + GrafanaTheme.Light + ); return ( {(selectedColor, updateSelectedColor) => { - return ( - - ); + return ; }} ); diff --git a/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx index b38d2f3031a..7c890bca0e9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx @@ -5,14 +5,20 @@ import { Saturation, Hue, Alpha } from 'react-color/lib/components/common'; import { getColorFromHexRgbOrName } from '../../utils/colorsPalette'; import tinycolor from 'tinycolor2'; import ColorInput from './ColorInput'; +import { Themeable, GrafanaTheme } from '../../types'; +import SpectrumPalettePointer, { SpectrumPalettePointerProps } from './SpectrumPalettePointer'; -export interface SpectrumPaletteProps { +export interface SpectrumPaletteProps extends Themeable { color: string; onChange: (color: string) => void; } +const renderPointer = (theme?: GrafanaTheme) => (props: SpectrumPalettePointerProps) => ( + +); + // @ts-ignore -const SpectrumPicker = CustomPicker(({ rgb, hsl, onChange, renderers }) => { +const SpectrumPicker = CustomPicker(({ rgb, hsl, onChange, theme }) => { return (
{ > {/* // @ts-ignore */} - +
@@ -69,14 +75,14 @@ const SpectrumPicker = CustomPicker(({ rgb, hsl, onChange, renderers }) => { > {/* // @ts-ignore */} - +
); }); -const SpectrumPalette: React.FunctionComponent = ({ color, onChange }) => { +const SpectrumPalette: React.FunctionComponent = ({ color, onChange, theme }) => { return (
= ({ color, onChange={(a: ColorResult) => { onChange(tinycolor(a.rgb).toString()); }} + theme={theme} />
diff --git a/packages/grafana-ui/src/components/ColorPicker/SpectrumPalettePointer.tsx b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalettePointer.tsx new file mode 100644 index 00000000000..d0b2cbc4bff --- /dev/null +++ b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalettePointer.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { GrafanaTheme, Themeable } from '../../types'; + +export interface SpectrumPalettePointerProps extends Themeable { + direction?: string; +} + +const SpectrumPalettePointer: React.FunctionComponent = ({ + theme, + direction, +}) => { + const styles = { + picker: { + width: '16px', + height: '16px', + transform: direction === 'vertical' ? 'translate(0, -8px)' : 'translate(-8px, 0)', + }, + }; + + const pointerColor = theme === GrafanaTheme.Light ? '#3F444D' : '#8E8E8E'; + + let pointerStyles: React.CSSProperties = { + position: 'absolute', + left: '6px', + width: '0', + height: '0', + borderStyle: 'solid', + background: 'none', + }; + + let topArrowStyles: React.CSSProperties = { + top: '-7px', + borderWidth: '6px 3px 0px 3px', + borderColor: `${pointerColor} transparent transparent transparent`, + }; + + let bottomArrowStyles: React.CSSProperties = { + bottom: '-7px', + borderWidth: '0px 3px 6px 3px', + borderColor: ` transparent transparent ${pointerColor} transparent`, + }; + + if (direction === 'vertical') { + pointerStyles = { + ...pointerStyles, + left: 'auto', + }; + topArrowStyles = { + borderWidth: '3px 0px 3px 6px', + borderColor: `transparent transparent transparent ${pointerColor}`, + left: '-7px', + top: '7px', + }; + bottomArrowStyles = { + borderWidth: '3px 6px 3px 0px', + borderColor: `transparent ${pointerColor} transparent transparent`, + right: '-7px', + top: '7px', + }; + } + + return ( +
+
+
+
+ ); +}; + +export default SpectrumPalettePointer; From 3c7a5bdf7b49de026793d27dc46355f9b11b3805 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 23 Jan 2019 14:59:43 +0100 Subject: [PATCH 33/57] Update styles of selected named color swatch --- .../src/components/ColorPicker/NamedColorsGroup.tsx | 9 ++++++--- .../src/components/ColorPicker/NamedColorsPalette.tsx | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx index b0cccd092a1..2e015b92284 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx @@ -1,5 +1,5 @@ import React, { FunctionComponent } from 'react'; -import { Themeable } from '../../types'; +import { Themeable, GrafanaTheme } from '../../types'; import { ColorDefinition, getColorForTheme } from '../../utils/colorsPalette'; import { Color } from 'csstype'; import { find, upperFirst } from 'lodash'; @@ -11,7 +11,7 @@ export enum ColorSwatchVariant { Large = 'large', } -interface ColorSwatchProps extends React.DOMAttributes { +interface ColorSwatchProps extends Themeable, React.DOMAttributes { color: string; label?: string; variant?: ColorSwatchVariant; @@ -23,17 +23,19 @@ const ColorSwatch: FunctionComponent = ({ label, variant = ColorSwatchVariant.Small, isSelected, + theme, ...otherProps }) => { const isSmall = variant === ColorSwatchVariant.Small; const swatchSize = isSmall ? '16px' : '32px'; + const selectedSwatchBorder = theme === GrafanaTheme.Light ? '#ffffff' : '#1A1B1F'; const swatchStyles = { width: swatchSize, height: swatchSize, borderRadius: '50%', background: `${color}`, marginRight: isSmall ? '0px' : '8px', - boxShadow: isSelected ? `inset 0 0 0 2px ${color}, inset 0 0 0 4px white` : 'none', + boxShadow: isSelected ? `inset 0 0 0 2px ${color}, inset 0 0 0 4px ${selectedSwatchBorder}` : 'none', cursor: isSelected ? 'default' : 'pointer', }; @@ -76,6 +78,7 @@ const NamedColorsGroup: FunctionComponent = ({ color={getColorForTheme(primaryColor, theme)} label={upperFirst(primaryColor.hue)} onClick={() => onColorSelect(primaryColor)} + theme={theme} /> )}
{ const swatches: JSX.Element[] = []; - ColorsPalette.forEach((colors, hue) => { swatches.push( Date: Wed, 23 Jan 2019 15:30:33 +0100 Subject: [PATCH 34/57] Make small swatches react to theme changes --- .../grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx index 2e015b92284..326a76b9fa9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx @@ -95,6 +95,7 @@ const NamedColorsGroup: FunctionComponent = ({ isSelected={color.name === selectedColor} color={getColorForTheme(color, theme)} onClick={() => onColorSelect(color)} + theme={theme} />
) From e5f74fdf93d045ce57bed062cd6bab009acdc505 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 23 Jan 2019 15:31:41 +0100 Subject: [PATCH 35/57] Rename colorsPalette util to namedColorsPalette --- .../src/components/ColorPicker/ColorPicker.tsx | 11 ++++++++--- .../components/ColorPicker/ColorPickerPopover.tsx | 2 +- .../components/ColorPicker/NamedColorsGroup.tsx | 2 +- .../ColorPicker/NamedColorsPalette.story.tsx | 2 +- .../components/ColorPicker/NamedColorsPalette.tsx | 2 +- .../src/components/ColorPicker/SpectrumPalette.tsx | 2 +- packages/grafana-ui/src/components/Gauge/Gauge.tsx | 13 +++---------- .../ThresholdsEditor/ThresholdsEditor.tsx | 2 +- packages/grafana-ui/src/utils/index.ts | 1 + ...sPalette.test.ts => namedColorsPalette.test.ts} | 14 +++++++++----- .../{colorsPalette.ts => namedColorsPalette.ts} | 0 public/app/plugins/panel/graph/data_processor.ts | 4 +--- public/app/plugins/panel/graph/module.ts | 3 +-- .../app/plugins/panel/graph/threshold_manager.ts | 2 +- .../app/plugins/panel/graph/time_region_manager.ts | 3 +-- public/app/plugins/panel/heatmap/color_legend.ts | 3 +-- public/app/plugins/panel/heatmap/rendering.ts | 5 +---- public/app/plugins/panel/singlestat/module.ts | 2 +- public/app/plugins/panel/table/renderer.ts | 2 +- .../app/plugins/panel/table/specs/renderer.test.ts | 2 +- 20 files changed, 36 insertions(+), 41 deletions(-) rename packages/grafana-ui/src/utils/{colorsPalette.test.ts => namedColorsPalette.test.ts} (87%) rename packages/grafana-ui/src/utils/{colorsPalette.ts => namedColorsPalette.ts} (100%) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index ee7ea241f06..b50e177833a 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -3,7 +3,7 @@ import PopperController from '../Tooltip/PopperController'; import Popper, { RenderPopperArrowFn } from '../Tooltip/Popper'; import { ColorPickerPopover } from './ColorPickerPopover'; import { Themeable, GrafanaTheme } from '../../types'; -import { getColorFromHexRgbOrName } from '../../utils/colorsPalette'; +import { getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; import propDeprecationWarning from '../../utils/propDeprecationWarning'; @@ -61,7 +61,7 @@ export const colorPickerFactory = ( }; return ( - + {(showPopper, hidePopper, popperProps) => { return ( <> @@ -83,7 +83,12 @@ export const colorPickerFactory = ( onMouseLeave: hidePopper, }) ) : ( -
+
= ({ children }) => { diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx index 0932b5bde76..9ae216aa8b0 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Color, ColorsPalette } from '../../utils/colorsPalette'; +import { Color, ColorsPalette } from '../../utils/namedColorsPalette'; import { Themeable } from '../../types/index'; import NamedColorsGroup from './NamedColorsGroup'; diff --git a/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx index 7c890bca0e9..cf001cf5629 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { CustomPicker, ColorResult } from 'react-color'; import { Saturation, Hue, Alpha } from 'react-color/lib/components/common'; -import { getColorFromHexRgbOrName } from '../../utils/colorsPalette'; +import { getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; import tinycolor from 'tinycolor2'; import ColorInput from './ColorInput'; import { Themeable, GrafanaTheme } from '../../types'; diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.tsx index 6f1dd009c7d..c0f23f17bc3 100644 --- a/packages/grafana-ui/src/components/Gauge/Gauge.tsx +++ b/packages/grafana-ui/src/components/Gauge/Gauge.tsx @@ -1,18 +1,11 @@ import React, { PureComponent } from 'react'; import $ from 'jquery'; -import { - ValueMapping, - Threshold, - MappingType, - BasicGaugeColor, - ValueMap, - RangeMap, -} from '../../types/panel'; +import { ValueMapping, Threshold, MappingType, BasicGaugeColor, ValueMap, RangeMap } from '../../types/panel'; import { TimeSeriesVMs } from '../../types/series'; import { getValueFormat } from '../../utils/valueFormats/valueFormats'; -import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; -import { GrafanaTheme } from '@grafana/ui/src/types'; +import { GrafanaTheme } from '../../types'; +import { getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; type TimeSeriesValue = string | number | null; diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index 78979e5e3e0..c15f66cca54 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -3,7 +3,7 @@ import { Threshold, Themeable } from '../../types'; import { ColorPicker } from '../ColorPicker/ColorPicker'; import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup'; import { colors } from '../../utils'; -import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; +import { getColorFromHexRgbOrName } from '@grafana/ui'; export interface Props extends Themeable { thresholds: Threshold[]; diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index aeb65032067..52a8535f9e1 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -1,3 +1,4 @@ export * from './processTimeSeries'; export * from './valueFormats/valueFormats'; export * from './colors'; +export * from './namedColorsPalette'; diff --git a/packages/grafana-ui/src/utils/colorsPalette.test.ts b/packages/grafana-ui/src/utils/namedColorsPalette.test.ts similarity index 87% rename from packages/grafana-ui/src/utils/colorsPalette.test.ts rename to packages/grafana-ui/src/utils/namedColorsPalette.test.ts index fc22ed0ae2e..ad5822fbaaa 100644 --- a/packages/grafana-ui/src/utils/colorsPalette.test.ts +++ b/packages/grafana-ui/src/utils/namedColorsPalette.test.ts @@ -1,8 +1,13 @@ -import { getColorName, getColorDefinition, getColorByName, SemiDarkBlue, getColorFromHexRgbOrName } from './colorsPalette'; -import { GrafanaTheme } from '../types'; +import { + getColorName, + getColorDefinition, + getColorByName, + SemiDarkBlue, + getColorFromHexRgbOrName, +} from './namedColorsPalette'; +import { GrafanaTheme } from '../types/index'; describe('colors', () => { - describe('getColorDefinition', () => { it('returns undefined for unknown hex', () => { expect(getColorDefinition('#ff0000')).toBeUndefined(); @@ -33,7 +38,6 @@ describe('colors', () => { it('returns color definiton for known color', () => { expect(getColorByName(SemiDarkBlue.name)).toBe(SemiDarkBlue); }); - }); describe('getColorFromHexRgbOrName', () => { it('returns undefined for unknown color', () => { @@ -44,7 +48,7 @@ describe('colors', () => { expect(getColorFromHexRgbOrName(SemiDarkBlue.name)).toBe(SemiDarkBlue.variants.dark); }); - it('returns correct variant\'s hex for known color if theme specified', () => { + it("returns correct variant's hex for known color if theme specified", () => { expect(getColorFromHexRgbOrName(SemiDarkBlue.name, GrafanaTheme.Light)).toBe(SemiDarkBlue.variants.light); }); diff --git a/packages/grafana-ui/src/utils/colorsPalette.ts b/packages/grafana-ui/src/utils/namedColorsPalette.ts similarity index 100% rename from packages/grafana-ui/src/utils/colorsPalette.ts rename to packages/grafana-ui/src/utils/namedColorsPalette.ts diff --git a/public/app/plugins/panel/graph/data_processor.ts b/public/app/plugins/panel/graph/data_processor.ts index 434070c2fe2..4141d36e273 100644 --- a/public/app/plugins/panel/graph/data_processor.ts +++ b/public/app/plugins/panel/graph/data_processor.ts @@ -1,8 +1,6 @@ import _ from 'lodash'; -import { colors, GrafanaTheme } from '@grafana/ui'; - +import { colors, GrafanaTheme, getColorFromHexRgbOrName } from '@grafana/ui'; import TimeSeries from 'app/core/time_series2'; -import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; import config from 'app/core/config'; export class DataProcessor { diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 362cc67c0ae..f72b441b67b 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -9,9 +9,8 @@ import _ from 'lodash'; import { MetricsPanelCtrl } from 'app/plugins/sdk'; import { DataProcessor } from './data_processor'; import { axesEditorComponent } from './axes_editor'; -import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; import config from 'app/core/config'; -import { GrafanaTheme } from '@grafana/ui'; +import { GrafanaTheme, getColorFromHexRgbOrName } from '@grafana/ui'; class GraphCtrl extends MetricsPanelCtrl { static template = template; diff --git a/public/app/plugins/panel/graph/threshold_manager.ts b/public/app/plugins/panel/graph/threshold_manager.ts index 1c782a99d3a..ef9feb80a5b 100644 --- a/public/app/plugins/panel/graph/threshold_manager.ts +++ b/public/app/plugins/panel/graph/threshold_manager.ts @@ -1,7 +1,7 @@ import 'vendor/flot/jquery.flot'; import $ from 'jquery'; import _ from 'lodash'; -import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; +import { getColorFromHexRgbOrName } from '@grafana/ui'; export class ThresholdManager { plot: any; diff --git a/public/app/plugins/panel/graph/time_region_manager.ts b/public/app/plugins/panel/graph/time_region_manager.ts index 6d13e37d5c5..be5de722fe2 100644 --- a/public/app/plugins/panel/graph/time_region_manager.ts +++ b/public/app/plugins/panel/graph/time_region_manager.ts @@ -1,8 +1,7 @@ import 'vendor/flot/jquery.flot'; import _ from 'lodash'; import moment from 'moment'; -import { GrafanaTheme } from '@grafana/ui'; -import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; +import { GrafanaTheme, getColorFromHexRgbOrName } from '@grafana/ui'; type TimeRegionColorDefinition = { fill: string; diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts index 4ba98890a31..81329fe297b 100644 --- a/public/app/plugins/panel/heatmap/color_legend.ts +++ b/public/app/plugins/panel/heatmap/color_legend.ts @@ -5,8 +5,7 @@ import { contextSrv } from 'app/core/core'; import { tickStep } from 'app/core/utils/ticks'; import { getColorScale, getOpacityScale } from './color_scale'; import coreModule from 'app/core/core_module'; -import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; -import { GrafanaTheme } from '@grafana/ui'; +import { GrafanaTheme, getColorFromHexRgbOrName } from '@grafana/ui'; const LEGEND_HEIGHT_PX = 6; const LEGEND_WIDTH_PX = 100; diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts index 68ee65547e2..6333a985819 100644 --- a/public/app/plugins/panel/heatmap/rendering.ts +++ b/public/app/plugins/panel/heatmap/rendering.ts @@ -8,8 +8,7 @@ import * as ticksUtils from 'app/core/utils/ticks'; import { HeatmapTooltip } from './heatmap_tooltip'; import { mergeZeroBuckets } from './heatmap_data_converter'; import { getColorScale, getOpacityScale } from './color_scale'; -import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; -import { GrafanaTheme } from '@grafana/ui'; +import { GrafanaTheme, getColorFromHexRgbOrName } from '@grafana/ui'; const MIN_CARD_SIZE = 1, CARD_PADDING = 1, @@ -663,12 +662,10 @@ export class HeatmapRenderer { getCardColor(d) { if (this.panel.color.mode === 'opacity') { - return getColorFromHexRgbOrName( this.panel.color.cardColor, contextSrv.user.lightTheme ? GrafanaTheme.Light : GrafanaTheme.Dark ); - } else { return this.colorScale(d.count); } diff --git a/public/app/plugins/panel/singlestat/module.ts b/public/app/plugins/panel/singlestat/module.ts index 25d544d4922..bb62109e0ad 100644 --- a/public/app/plugins/panel/singlestat/module.ts +++ b/public/app/plugins/panel/singlestat/module.ts @@ -8,7 +8,7 @@ import kbn from 'app/core/utils/kbn'; import config from 'app/core/config'; import TimeSeries from 'app/core/time_series2'; import { MetricsPanelCtrl } from 'app/plugins/sdk'; -import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; +import { getColorFromHexRgbOrName } from '@grafana/ui'; import { GrafanaTheme } from '@grafana/ui'; class SingleStatCtrl extends MetricsPanelCtrl { diff --git a/public/app/plugins/panel/table/renderer.ts b/public/app/plugins/panel/table/renderer.ts index 9f0354a6843..ccaf3ddf423 100644 --- a/public/app/plugins/panel/table/renderer.ts +++ b/public/app/plugins/panel/table/renderer.ts @@ -1,7 +1,7 @@ import _ from 'lodash'; import moment from 'moment'; import kbn from 'app/core/utils/kbn'; -import { getColorFromHexRgbOrName } from '@grafana/ui/src/utils/colorsPalette'; +import { getColorFromHexRgbOrName } from '@grafana/ui'; import { GrafanaTheme } from '@grafana/ui'; export class TableRenderer { diff --git a/public/app/plugins/panel/table/specs/renderer.test.ts b/public/app/plugins/panel/table/specs/renderer.test.ts index 017dc6f0538..a6e03417085 100644 --- a/public/app/plugins/panel/table/specs/renderer.test.ts +++ b/public/app/plugins/panel/table/specs/renderer.test.ts @@ -1,7 +1,7 @@ import _ from 'lodash'; import TableModel from 'app/core/table_model'; import { TableRenderer } from '../renderer'; -import { SemiDarkOrange } from '@grafana/ui/src/utils/colorsPalette'; +import { SemiDarkOrange } from '@grafana/ui'; describe('when rendering table', () => { describe('given 13 columns', () => { From 580b919424bf85f482ebe7b22e81a078b3c52bca Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 23 Jan 2019 15:34:39 +0100 Subject: [PATCH 36/57] Fix hide timeout for color picker --- packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index b50e177833a..2f088b32327 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -61,7 +61,7 @@ export const colorPickerFactory = ( }; return ( - + {(showPopper, hidePopper, popperProps) => { return ( <> From 573e64ef114cff47076a61818b95ab4690b7f2af Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 23 Jan 2019 16:02:21 +0100 Subject: [PATCH 37/57] Remove spectrum.js vendor dependency from grafana/ui --- packages/grafana-ui/src/vendor/spectrum.css | 509 ---- packages/grafana-ui/src/vendor/spectrum.js | 2317 ------------------- 2 files changed, 2826 deletions(-) delete mode 100644 packages/grafana-ui/src/vendor/spectrum.css delete mode 100644 packages/grafana-ui/src/vendor/spectrum.js diff --git a/packages/grafana-ui/src/vendor/spectrum.css b/packages/grafana-ui/src/vendor/spectrum.css deleted file mode 100644 index d17af2db4f7..00000000000 --- a/packages/grafana-ui/src/vendor/spectrum.css +++ /dev/null @@ -1,509 +0,0 @@ -/*** -Spectrum Colorpicker v1.3.0 -https://github.com/bgrins/spectrum -Author: Brian Grinstead -License: MIT -***/ - -.sp-container { - position:absolute; - top:0; - left:0; - display:inline-block; - *display: inline; - *zoom: 1; - /* https://github.com/bgrins/spectrum/issues/40 */ - z-index: 9999994; - overflow: hidden; -} -.sp-container.sp-flat { - position: relative; -} - -/* Fix for * { box-sizing: border-box; } */ -.sp-container, -.sp-container * { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} - -/* http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */ -.sp-top { - position:relative; - width: 100%; - display:inline-block; -} -.sp-top-inner { - position:absolute; - top:0; - left:0; - bottom:0; - right:0; -} -.sp-color { - position: absolute; - top:0; - left:0; - bottom:0; - right:20%; -} -.sp-hue { - position: absolute; - top:0; - right:0; - bottom:0; - left:84%; - height: 100%; -} - -.sp-clear-enabled .sp-hue { - top:33px; - height: 77.5%; -} - -.sp-fill { - padding-top: 80%; -} -.sp-sat, .sp-val { - position: absolute; - top:0; - left:0; - right:0; - bottom:0; -} - -.sp-alpha-enabled .sp-top { - margin-bottom: 18px; -} -.sp-alpha-enabled .sp-alpha { - display: block; -} -.sp-alpha-handle { - position:absolute; - top:-4px; - bottom: -4px; - width: 6px; - left: 50%; - cursor: pointer; - border: 1px solid black; - background: white; - opacity: .8; -} -.sp-alpha { - display: none; - position: absolute; - bottom: -14px; - right: 0; - left: 0; - height: 8px; -} -.sp-alpha-inner { - border: solid 1px #333; -} - -.sp-clear { - display: none; -} - -.sp-clear.sp-clear-display { - background-position: center; -} - -.sp-clear-enabled .sp-clear { - display: block; - position:absolute; - top:0px; - right:0; - bottom:0; - left:84%; - height: 28px; -} - -/* Don't allow text selection */ -.sp-container, .sp-replacer, .sp-preview, .sp-dragger, .sp-slider, .sp-alpha, .sp-clear, .sp-alpha-handle, .sp-container.sp-dragging .sp-input, .sp-container button { - -webkit-user-select:none; - -moz-user-select: -moz-none; - -o-user-select:none; - user-select: none; -} - -.sp-container.sp-input-disabled .sp-input-container { - display: none; -} -.sp-container.sp-buttons-disabled .sp-button-container { - display: none; -} -.sp-palette-only .sp-picker-container { - display: none; -} -.sp-palette-disabled .sp-palette-container { - display: none; -} - -.sp-initial-disabled .sp-initial { - display: none; -} - - -/* Gradients for hue, saturation and value instead of images. Not pretty... but it works */ -.sp-sat { - background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0)); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)"; - filter : progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81'); -} -.sp-val { - background-image: linear-gradient(to top, #000, rgba(204, 154, 129, 0)); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)"; - filter : progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000'); -} - -.sp-hue { - background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); - background: -ms-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); - background: -o-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); - background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), color-stop(0.17, #ffff00), color-stop(0.33, #00ff00), color-stop(0.5, #00ffff), color-stop(0.67, #0000ff), color-stop(0.83, #ff00ff), to(#ff0000)); - background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); -} - -/* IE filters do not support multiple color stops. - Generate 6 divs, line them up, and do two color gradients for each. - Yes, really. - */ -.sp-1 { - height:17%; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00'); -} -.sp-2 { - height:16%; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00'); -} -.sp-3 { - height:17%; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff'); -} -.sp-4 { - height:17%; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff'); -} -.sp-5 { - height:16%; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff'); -} -.sp-6 { - height:17%; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000'); -} - -.sp-hidden { - display: none !important; -} - -/* Clearfix hack */ -.sp-cf:before, .sp-cf:after { content: ""; display: table; } -.sp-cf:after { clear: both; } -.sp-cf { *zoom: 1; } - -/* Mobile devices, make hue slider bigger so it is easier to slide */ -@media (max-device-width: 480px) { - .sp-color { right: 40%; } - .sp-hue { left: 63%; } - .sp-fill { padding-top: 60%; } -} -.sp-dragger { - border-radius: 5px; - height: 5px; - width: 5px; - border: 1px solid #fff; - background: #000; - cursor: pointer; - position:absolute; - top:0; - left: 0; -} -.sp-slider { - position: absolute; - top:0; - cursor:pointer; - height: 3px; - left: -1px; - right: -1px; - border: 1px solid #000; - background: white; - opacity: .8; -} - -/* -Theme authors: -Here are the basic themeable display options (colors, fonts, global widths). -See http://bgrins.github.io/spectrum/themes/ for instructions. -*/ - -.sp-container { - border-radius: 0; - background-color: #ECECEC; - border: solid 1px #f0c49B; - padding: 0; -} -.sp-container, .sp-container button, .sp-container input, .sp-color, .sp-hue, .sp-clear -{ - font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - -ms-box-sizing: border-box; - box-sizing: border-box; -} -.sp-top -{ - margin-bottom: 3px; -} -.sp-color, .sp-hue, .sp-clear -{ - border: solid 1px #666; -} - -/* Input */ -.sp-input-container { - float:right; - width: 100px; - margin-bottom: 4px; -} -.sp-initial-disabled .sp-input-container { - width: 100%; -} -.sp-input { - font-size: 12px !important; - border: 1px inset; - padding: 4px 5px; - margin: 0; - width: 100%; - background:transparent; - border-radius: 3px; - color: #222; -} -.sp-input:focus { - border: 1px solid orange; -} -.sp-input.sp-validation-error -{ - border: 1px solid red; - background: #fdd; -} -.sp-picker-container , .sp-palette-container -{ - float:left; - position: relative; - padding: 10px; - padding-bottom: 300px; - margin-bottom: -290px; -} -.sp-picker-container -{ - width: 172px; - border-left: solid 1px #fff; -} - -/* Palettes */ -.sp-palette-container -{ - border-right: solid 1px #ccc; -} - -.sp-palette .sp-thumb-el { - display: block; - position:relative; - float:left; - width: 24px; - height: 15px; - margin: 3px; - cursor: pointer; - border:solid 2px transparent; -} -.sp-palette .sp-thumb-el:hover, .sp-palette .sp-thumb-el.sp-thumb-active { - border-color: orange; -} -.sp-thumb-el -{ - position:relative; -} - -/* Initial */ -.sp-initial -{ - float: left; - border: solid 1px #333; -} -.sp-initial span { - width: 30px; - height: 25px; - border:none; - display:block; - float:left; - margin:0; -} - -.sp-initial .sp-clear-display { - background-position: center; -} - -/* Buttons */ -.sp-button-container { - float: right; -} - -/* Replacer (the little preview div that shows up instead of the ) */ -.sp-replacer { - margin:0; - overflow:hidden; - cursor:pointer; - padding: 4px; - display:inline-block; - *zoom: 1; - *display: inline; - border: solid 1px #91765d; - background: #eee; - color: #333; - vertical-align: middle; -} -.sp-replacer:hover, .sp-replacer.sp-active { - border-color: #F0C49B; - color: #111; -} -.sp-replacer.sp-disabled { - cursor:default; - border-color: silver; - color: silver; -} -.sp-dd { - padding: 2px 0; - height: 16px; - line-height: 16px; - float:left; - font-size:10px; -} -.sp-preview -{ - position:relative; - width:25px; - height: 20px; - border: solid 1px #222; - margin-right: 5px; - float:left; - z-index: 0; -} - -.sp-palette -{ - *width: 220px; - max-width: 220px; -} -.sp-palette .sp-thumb-el -{ - width:16px; - height: 16px; - margin:2px 1px; - border: solid 1px #d0d0d0; -} - -.sp-container -{ - padding-bottom:0; -} - - -/* Buttons: http://hellohappy.org/css3-buttons/ */ -.sp-container button { - background-color: #eeeeee; - background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc); - background-image: -moz-linear-gradient(top, #eeeeee, #cccccc); - background-image: -ms-linear-gradient(top, #eeeeee, #cccccc); - background-image: -o-linear-gradient(top, #eeeeee, #cccccc); - background-image: linear-gradient(to bottom, #eeeeee, #cccccc); - border: 1px solid #ccc; - border-bottom: 1px solid #bbb; - border-radius: 3px; - color: #333; - font-size: 14px; - line-height: 1; - padding: 5px 4px; - text-align: center; - text-shadow: 0 1px 0 #eee; - vertical-align: middle; -} -.sp-container button:hover { - background-color: #dddddd; - background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb); - background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb); - background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb); - background-image: -o-linear-gradient(top, #dddddd, #bbbbbb); - background-image: linear-gradient(to bottom, #dddddd, #bbbbbb); - border: 1px solid #bbb; - border-bottom: 1px solid #999; - cursor: pointer; - text-shadow: 0 1px 0 #ddd; -} -.sp-container button:active { - border: 1px solid #aaa; - border-bottom: 1px solid #888; - -webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; - -moz-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; - -ms-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; - -o-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; - box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; -} -.sp-cancel -{ - font-size: 11px; - color: #d93f3f !important; - margin:0; - padding:2px; - margin-right: 5px; - vertical-align: middle; - text-decoration:none; - -} -.sp-cancel:hover -{ - color: #d93f3f !important; - text-decoration: underline; -} - - -.sp-palette span:hover, .sp-palette span.sp-thumb-active -{ - border-color: #000; -} - -.sp-preview, .sp-alpha, .sp-thumb-el -{ - position:relative; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==); -} -.sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner -{ - display:block; - position:absolute; - top:0;left:0;bottom:0;right:0; -} - -.sp-palette .sp-thumb-inner -{ - background-position: 50% 50%; - background-repeat: no-repeat; -} - -.sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner -{ - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=); -} - -.sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner -{ - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=); -} - -.sp-clear-display { - background-repeat:no-repeat; - background-position: center; - background-image: url(data:image/gif;base64,R0lGODlhFAAUAPcAAAAAAJmZmZ2dnZ6enqKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq/Hx8fLy8vT09PX19ff39/j4+Pn5+fr6+vv7+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAUABQAAAihAP9FoPCvoMGDBy08+EdhQAIJCCMybCDAAYUEARBAlFiQQoMABQhKUJBxY0SPICEYHBnggEmDKAuoPMjS5cGYMxHW3IiT478JJA8M/CjTZ0GgLRekNGpwAsYABHIypcAgQMsITDtWJYBR6NSqMico9cqR6tKfY7GeBCuVwlipDNmefAtTrkSzB1RaIAoXodsABiZAEFB06gIBWC1mLVgBa0AAOw==); -} \ No newline at end of file diff --git a/packages/grafana-ui/src/vendor/spectrum.js b/packages/grafana-ui/src/vendor/spectrum.js deleted file mode 100644 index acdf4a0b015..00000000000 --- a/packages/grafana-ui/src/vendor/spectrum.js +++ /dev/null @@ -1,2317 +0,0 @@ -// Spectrum Colorpicker v1.7.0 -// https://github.com/bgrins/spectrum -// Author: Brian Grinstead -// License: MIT - -(function (factory) { - "use strict"; - - if (typeof define === 'function' && define.amd) { // AMD - define(['jquery'], factory); - } - else if (typeof exports == "object" && typeof module == "object") { // CommonJS - module.exports = factory; - } - else { // Browser - factory(jQuery); - } -})(function($, undefined) { - "use strict"; - - var defaultOpts = { - - // Callbacks - beforeShow: noop, - move: noop, - change: noop, - show: noop, - hide: noop, - - // Options - color: false, - flat: false, - showInput: false, - allowEmpty: false, - showButtons: true, - clickoutFiresChange: true, - showInitial: false, - showPalette: false, - showPaletteOnly: false, - hideAfterPaletteSelect: false, - togglePaletteOnly: false, - showSelectionPalette: true, - localStorageKey: false, - appendTo: "body", - maxSelectionSize: 7, - cancelText: "cancel", - chooseText: "choose", - togglePaletteMoreText: "more", - togglePaletteLessText: "less", - clearText: "Clear Color Selection", - noColorSelectedText: "No Color Selected", - preferredFormat: false, - className: "", // Deprecated - use containerClassName and replacerClassName instead. - containerClassName: "", - replacerClassName: "", - showAlpha: false, - theme: "sp-light", - palette: [["#ffffff", "#000000", "#ff0000", "#ff8000", "#ffff00", "#008000", "#0000ff", "#4b0082", "#9400d3"]], - selectionPalette: [], - disabled: false, - offset: null - }, - spectrums = [], - IE = !!/msie/i.exec( window.navigator.userAgent ), - rgbaSupport = (function() { - function contains( str, substr ) { - return !!~('' + str).indexOf(substr); - } - - var elem = document.createElement('div'); - var style = elem.style; - style.cssText = 'background-color:rgba(0,0,0,.5)'; - return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla'); - })(), - replaceInput = [ - "
", - "
", - "
▼
", - "
" - ].join(''), - markup = (function () { - - // IE does not support gradients with multiple stops, so we need to simulate - // that for the rainbow slider with 8 divs that each have a single gradient - var gradientFix = ""; - if (IE) { - for (var i = 1; i <= 6; i++) { - gradientFix += "
"; - } - } - - return [ - "
", - "
", - "
", - "
", - "", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - gradientFix, - "
", - "
", - "
", - "
", - "
", - "", - "
", - "
", - "
", - "", - "", - "
", - "
", - "
" - ].join(""); - })(); - - function paletteTemplate (p, color, className, opts) { - var html = []; - for (var i = 0; i < p.length; i++) { - var current = p[i]; - if(current) { - var tiny = tinycolor(current); - var c = tiny.toHsl().l < 0.5 ? "sp-thumb-el sp-thumb-dark" : "sp-thumb-el sp-thumb-light"; - c += (tinycolor.equals(color, current)) ? " sp-thumb-active" : ""; - var formattedString = tiny.toString(opts.preferredFormat || "rgb"); - var swatchStyle = rgbaSupport ? ("background-color:" + tiny.toRgbString()) : "filter:" + tiny.toFilter(); - html.push(''); - } else { - var cls = 'sp-clear-display'; - html.push($('
') - .append($('') - .attr('title', opts.noColorSelectedText) - ) - .html() - ); - } - } - return "
" + html.join('') + "
"; - } - - function hideAll() { - for (var i = 0; i < spectrums.length; i++) { - if (spectrums[i]) { - spectrums[i].hide(); - } - } - } - - function instanceOptions(o, callbackContext) { - var opts = $.extend({}, defaultOpts, o); - opts.callbacks = { - 'move': bind(opts.move, callbackContext), - 'change': bind(opts.change, callbackContext), - 'show': bind(opts.show, callbackContext), - 'hide': bind(opts.hide, callbackContext), - 'beforeShow': bind(opts.beforeShow, callbackContext) - }; - - return opts; - } - - function spectrum(element, o) { - - var opts = instanceOptions(o, element), - flat = opts.flat, - showSelectionPalette = opts.showSelectionPalette, - localStorageKey = opts.localStorageKey, - theme = opts.theme, - callbacks = opts.callbacks, - resize = throttle(reflow, 10), - visible = false, - isDragging = false, - dragWidth = 0, - dragHeight = 0, - dragHelperHeight = 0, - slideHeight = 0, - slideWidth = 0, - alphaWidth = 0, - alphaSlideHelperWidth = 0, - slideHelperHeight = 0, - currentHue = 0, - currentSaturation = 0, - currentValue = 0, - currentAlpha = 1, - palette = [], - paletteArray = [], - paletteLookup = {}, - selectionPalette = opts.selectionPalette.slice(0), - maxSelectionSize = opts.maxSelectionSize, - draggingClass = "sp-dragging", - shiftMovementDirection = null; - - var doc = element.ownerDocument, - body = doc.body, - boundElement = $(element), - disabled = false, - container = $(markup, doc).addClass(theme), - pickerContainer = container.find(".sp-picker-container"), - dragger = container.find(".sp-color"), - dragHelper = container.find(".sp-dragger"), - slider = container.find(".sp-hue"), - slideHelper = container.find(".sp-slider"), - alphaSliderInner = container.find(".sp-alpha-inner"), - alphaSlider = container.find(".sp-alpha"), - alphaSlideHelper = container.find(".sp-alpha-handle"), - textInput = container.find(".sp-input"), - paletteContainer = container.find(".sp-palette"), - initialColorContainer = container.find(".sp-initial"), - cancelButton = container.find(".sp-cancel"), - clearButton = container.find(".sp-clear"), - chooseButton = container.find(".sp-choose"), - toggleButton = container.find(".sp-palette-toggle"), - isInput = boundElement.is("input"), - isInputTypeColor = isInput && boundElement.attr("type") === "color" && inputTypeColorSupport(), - shouldReplace = isInput && !flat, - replacer = (shouldReplace) ? $(replaceInput).addClass(theme).addClass(opts.className).addClass(opts.replacerClassName) : $([]), - offsetElement = (shouldReplace) ? replacer : boundElement, - previewElement = replacer.find(".sp-preview-inner"), - initialColor = opts.color || (isInput && boundElement.val()), - colorOnShow = false, - preferredFormat = opts.preferredFormat, - currentPreferredFormat = preferredFormat, - clickoutFiresChange = !opts.showButtons || opts.clickoutFiresChange, - isEmpty = !initialColor, - allowEmpty = opts.allowEmpty && !isInputTypeColor; - - function applyOptions() { - - if (opts.showPaletteOnly) { - opts.showPalette = true; - } - - toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText); - - if (opts.palette) { - palette = opts.palette.slice(0); - paletteArray = $.isArray(palette[0]) ? palette : [palette]; - paletteLookup = {}; - for (var i = 0; i < paletteArray.length; i++) { - for (var j = 0; j < paletteArray[i].length; j++) { - var rgb = tinycolor(paletteArray[i][j]).toRgbString(); - paletteLookup[rgb] = true; - } - } - } - - container.toggleClass("sp-flat", flat); - container.toggleClass("sp-input-disabled", !opts.showInput); - container.toggleClass("sp-alpha-enabled", opts.showAlpha); - container.toggleClass("sp-clear-enabled", allowEmpty); - container.toggleClass("sp-buttons-disabled", !opts.showButtons); - container.toggleClass("sp-palette-buttons-disabled", !opts.togglePaletteOnly); - container.toggleClass("sp-palette-disabled", !opts.showPalette); - container.toggleClass("sp-palette-only", opts.showPaletteOnly); - container.toggleClass("sp-initial-disabled", !opts.showInitial); - container.addClass(opts.className).addClass(opts.containerClassName); - - reflow(); - } - - function initialize() { - - if (IE) { - container.find("*:not(input)").attr("unselectable", "on"); - } - - applyOptions(); - - if (shouldReplace) { - boundElement.after(replacer).hide(); - } - - if (!allowEmpty) { - clearButton.hide(); - } - - if (flat) { - boundElement.after(container).hide(); - } - else { - - var appendTo = opts.appendTo === "parent" ? boundElement.parent() : $(opts.appendTo); - if (appendTo.length !== 1) { - appendTo = $("body"); - } - - appendTo.append(container); - } - - updateSelectionPaletteFromStorage(); - - offsetElement.bind("click.spectrum touchstart.spectrum", function (e) { - if (!disabled) { - toggle(); - } - - e.stopPropagation(); - - if (!$(e.target).is("input")) { - e.preventDefault(); - } - }); - - if(boundElement.is(":disabled") || (opts.disabled === true)) { - disable(); - } - - // Prevent clicks from bubbling up to document. This would cause it to be hidden. - container.click(stopPropagation); - - // Handle user typed input - textInput.change(setFromTextInput); - textInput.bind("paste", function () { - setTimeout(setFromTextInput, 1); - }); - textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } }); - - cancelButton.text(opts.cancelText); - cancelButton.bind("click.spectrum", function (e) { - e.stopPropagation(); - e.preventDefault(); - revert(); - hide(); - }); - - clearButton.attr("title", opts.clearText); - clearButton.bind("click.spectrum", function (e) { - e.stopPropagation(); - e.preventDefault(); - isEmpty = true; - move(); - - if(flat) { - //for the flat style, this is a change event - updateOriginalInput(true); - } - }); - - chooseButton.text(opts.chooseText); - chooseButton.bind("click.spectrum", function (e) { - e.stopPropagation(); - e.preventDefault(); - - if (IE && textInput.is(":focus")) { - textInput.trigger('change'); - } - - if (isValid()) { - updateOriginalInput(true); - hide(); - } - }); - - toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText); - toggleButton.bind("click.spectrum", function (e) { - e.stopPropagation(); - e.preventDefault(); - - opts.showPaletteOnly = !opts.showPaletteOnly; - - // To make sure the Picker area is drawn on the right, next to the - // Palette area (and not below the palette), first move the Palette - // to the left to make space for the picker, plus 5px extra. - // The 'applyOptions' function puts the whole container back into place - // and takes care of the button-text and the sp-palette-only CSS class. - if (!opts.showPaletteOnly && !flat) { - container.css('left', '-=' + (pickerContainer.outerWidth(true) + 5)); - } - applyOptions(); - }); - - draggable(alphaSlider, function (dragX, dragY, e) { - currentAlpha = (dragX / alphaWidth); - isEmpty = false; - if (e.shiftKey) { - currentAlpha = Math.round(currentAlpha * 10) / 10; - } - - move(); - }, dragStart, dragStop); - - draggable(slider, function (dragX, dragY) { - currentHue = parseFloat(dragY / slideHeight); - isEmpty = false; - if (!opts.showAlpha) { - currentAlpha = 1; - } - move(); - }, dragStart, dragStop); - - draggable(dragger, function (dragX, dragY, e) { - - // shift+drag should snap the movement to either the x or y axis. - if (!e.shiftKey) { - shiftMovementDirection = null; - } - else if (!shiftMovementDirection) { - var oldDragX = currentSaturation * dragWidth; - var oldDragY = dragHeight - (currentValue * dragHeight); - var furtherFromX = Math.abs(dragX - oldDragX) > Math.abs(dragY - oldDragY); - - shiftMovementDirection = furtherFromX ? "x" : "y"; - } - - var setSaturation = !shiftMovementDirection || shiftMovementDirection === "x"; - var setValue = !shiftMovementDirection || shiftMovementDirection === "y"; - - if (setSaturation) { - currentSaturation = parseFloat(dragX / dragWidth); - } - if (setValue) { - currentValue = parseFloat((dragHeight - dragY) / dragHeight); - } - - isEmpty = false; - if (!opts.showAlpha) { - currentAlpha = 1; - } - - move(); - - }, dragStart, dragStop); - - if (!!initialColor) { - set(initialColor); - - // In case color was black - update the preview UI and set the format - // since the set function will not run (default color is black). - updateUI(); - currentPreferredFormat = preferredFormat || tinycolor(initialColor).format; - - addColorToSelectionPalette(initialColor); - } - else { - updateUI(); - } - - if (flat) { - show(); - } - - function paletteElementClick(e) { - if (e.data && e.data.ignore) { - set($(e.target).closest(".sp-thumb-el").data("color")); - move(); - } - else { - set($(e.target).closest(".sp-thumb-el").data("color")); - move(); - updateOriginalInput(true); - if (opts.hideAfterPaletteSelect) { - hide(); - } - } - - return false; - } - - var paletteEvent = IE ? "mousedown.spectrum" : "click.spectrum touchstart.spectrum"; - paletteContainer.delegate(".sp-thumb-el", paletteEvent, paletteElementClick); - initialColorContainer.delegate(".sp-thumb-el:nth-child(1)", paletteEvent, { ignore: true }, paletteElementClick); - } - - function updateSelectionPaletteFromStorage() { - - if (localStorageKey && window.localStorage) { - - // Migrate old palettes over to new format. May want to remove this eventually. - try { - var oldPalette = window.localStorage[localStorageKey].split(",#"); - if (oldPalette.length > 1) { - delete window.localStorage[localStorageKey]; - $.each(oldPalette, function(i, c) { - addColorToSelectionPalette(c); - }); - } - } - catch(e) { } - - try { - selectionPalette = window.localStorage[localStorageKey].split(";"); - } - catch (e) { } - } - } - - function addColorToSelectionPalette(color) { - if (showSelectionPalette) { - var rgb = tinycolor(color).toRgbString(); - if (!paletteLookup[rgb] && $.inArray(rgb, selectionPalette) === -1) { - selectionPalette.push(rgb); - while(selectionPalette.length > maxSelectionSize) { - selectionPalette.shift(); - } - } - - if (localStorageKey && window.localStorage) { - try { - window.localStorage[localStorageKey] = selectionPalette.join(";"); - } - catch(e) { } - } - } - } - - function getUniqueSelectionPalette() { - var unique = []; - if (opts.showPalette) { - for (var i = 0; i < selectionPalette.length; i++) { - var rgb = tinycolor(selectionPalette[i]).toRgbString(); - - if (!paletteLookup[rgb]) { - unique.push(selectionPalette[i]); - } - } - } - - return unique.reverse().slice(0, opts.maxSelectionSize); - } - - function drawPalette() { - - var currentColor = get(); - - var html = $.map(paletteArray, function (palette, i) { - return paletteTemplate(palette, currentColor, "sp-palette-row sp-palette-row-" + i, opts); - }); - - updateSelectionPaletteFromStorage(); - - if (selectionPalette) { - html.push(paletteTemplate(getUniqueSelectionPalette(), currentColor, "sp-palette-row sp-palette-row-selection", opts)); - } - - paletteContainer.html(html.join("")); - } - - function drawInitial() { - if (opts.showInitial) { - var initial = colorOnShow; - var current = get(); - initialColorContainer.html(paletteTemplate([initial, current], current, "sp-palette-row-initial", opts)); - } - } - - function dragStart() { - if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0) { - reflow(); - } - isDragging = true; - container.addClass(draggingClass); - shiftMovementDirection = null; - boundElement.trigger('dragstart.spectrum', [ get() ]); - } - - function dragStop() { - isDragging = false; - container.removeClass(draggingClass); - boundElement.trigger('dragstop.spectrum', [ get() ]); - } - - function setFromTextInput() { - - var value = textInput.val(); - - if ((value === null || value === "") && allowEmpty) { - set(null); - updateOriginalInput(true); - } - else { - var tiny = tinycolor(value); - if (tiny.isValid()) { - set(tiny); - updateOriginalInput(true); - } - else { - textInput.addClass("sp-validation-error"); - } - } - } - - function toggle() { - if (visible) { - hide(); - } - else { - show(); - } - } - - function show() { - var event = $.Event('beforeShow.spectrum'); - - if (visible) { - reflow(); - return; - } - - boundElement.trigger(event, [ get() ]); - - if (callbacks.beforeShow(get()) === false || event.isDefaultPrevented()) { - return; - } - - hideAll(); - visible = true; - - $(doc).bind("keydown.spectrum", onkeydown); - $(doc).bind("click.spectrum", clickout); - $(window).bind("resize.spectrum", resize); - replacer.addClass("sp-active"); - container.removeClass("sp-hidden"); - - reflow(); - updateUI(); - - colorOnShow = get(); - - drawInitial(); - callbacks.show(colorOnShow); - boundElement.trigger('show.spectrum', [ colorOnShow ]); - } - - function onkeydown(e) { - // Close on ESC - if (e.keyCode === 27) { - hide(); - } - } - - function clickout(e) { - // Return on right click. - if (e.button == 2) { return; } - - // If a drag event was happening during the mouseup, don't hide - // on click. - if (isDragging) { return; } - - if (clickoutFiresChange) { - updateOriginalInput(true); - } - else { - revert(); - } - hide(); - } - - function hide() { - // Return if hiding is unnecessary - if (!visible || flat) { return; } - visible = false; - - $(doc).unbind("keydown.spectrum", onkeydown); - $(doc).unbind("click.spectrum", clickout); - $(window).unbind("resize.spectrum", resize); - - replacer.removeClass("sp-active"); - container.addClass("sp-hidden"); - - callbacks.hide(get()); - boundElement.trigger('hide.spectrum', [ get() ]); - } - - function revert() { - set(colorOnShow, true); - } - - function set(color, ignoreFormatChange) { - if (tinycolor.equals(color, get())) { - // Update UI just in case a validation error needs - // to be cleared. - updateUI(); - return; - } - - var newColor, newHsv; - if (!color && allowEmpty) { - isEmpty = true; - } else { - isEmpty = false; - newColor = tinycolor(color); - newHsv = newColor.toHsv(); - - currentHue = (newHsv.h % 360) / 360; - currentSaturation = newHsv.s; - currentValue = newHsv.v; - currentAlpha = newHsv.a; - } - updateUI(); - - if (newColor && newColor.isValid() && !ignoreFormatChange) { - currentPreferredFormat = preferredFormat || newColor.getFormat(); - } - } - - function get(opts) { - opts = opts || { }; - - if (allowEmpty && isEmpty) { - return null; - } - - return tinycolor.fromRatio({ - h: currentHue, - s: currentSaturation, - v: currentValue, - a: Math.round(currentAlpha * 100) / 100 - }, { format: opts.format || currentPreferredFormat }); - } - - function isValid() { - return !textInput.hasClass("sp-validation-error"); - } - - function move() { - updateUI(); - - callbacks.move(get()); - boundElement.trigger('move.spectrum', [ get() ]); - } - - function updateUI() { - - textInput.removeClass("sp-validation-error"); - - updateHelperLocations(); - - // Update dragger background color (gradients take care of saturation and value). - var flatColor = tinycolor.fromRatio({ h: currentHue, s: 1, v: 1 }); - dragger.css("background-color", flatColor.toHexString()); - - // Get a format that alpha will be included in (hex and names ignore alpha) - var format = currentPreferredFormat; - if (currentAlpha < 1 && !(currentAlpha === 0 && format === "name")) { - if (format === "hex" || format === "hex3" || format === "hex6" || format === "name") { - format = "rgb"; - } - } - - var realColor = get({ format: format }), - displayColor = ''; - - //reset background info for preview element - previewElement.removeClass("sp-clear-display"); - previewElement.css('background-color', 'transparent'); - - if (!realColor && allowEmpty) { - // Update the replaced elements background with icon indicating no color selection - previewElement.addClass("sp-clear-display"); - } - else { - var realHex = realColor.toHexString(), - realRgb = realColor.toRgbString(); - - // Update the replaced elements background color (with actual selected color) - if (rgbaSupport || realColor.alpha === 1) { - previewElement.css("background-color", realRgb); - } - else { - previewElement.css("background-color", "transparent"); - previewElement.css("filter", realColor.toFilter()); - } - - if (opts.showAlpha) { - var rgb = realColor.toRgb(); - rgb.a = 0; - var realAlpha = tinycolor(rgb).toRgbString(); - var gradient = "linear-gradient(left, " + realAlpha + ", " + realHex + ")"; - - if (IE) { - alphaSliderInner.css("filter", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex)); - } - else { - alphaSliderInner.css("background", "-webkit-" + gradient); - alphaSliderInner.css("background", "-moz-" + gradient); - alphaSliderInner.css("background", "-ms-" + gradient); - // Use current syntax gradient on unprefixed property. - alphaSliderInner.css("background", - "linear-gradient(to right, " + realAlpha + ", " + realHex + ")"); - } - } - - displayColor = realColor.toString(format); - } - - // Update the text entry input as it changes happen - if (opts.showInput) { - textInput.val(displayColor); - } - - if (opts.showPalette) { - drawPalette(); - } - - drawInitial(); - } - - function updateHelperLocations() { - var s = currentSaturation; - var v = currentValue; - - if(allowEmpty && isEmpty) { - //if selected color is empty, hide the helpers - alphaSlideHelper.hide(); - slideHelper.hide(); - dragHelper.hide(); - } - else { - //make sure helpers are visible - alphaSlideHelper.show(); - slideHelper.show(); - dragHelper.show(); - - // Where to show the little circle in that displays your current selected color - var dragX = s * dragWidth; - var dragY = dragHeight - (v * dragHeight); - dragX = Math.max( - -dragHelperHeight, - Math.min(dragWidth - dragHelperHeight, dragX - dragHelperHeight) - ); - dragY = Math.max( - -dragHelperHeight, - Math.min(dragHeight - dragHelperHeight, dragY - dragHelperHeight) - ); - dragHelper.css({ - "top": dragY + "px", - "left": dragX + "px" - }); - - var alphaX = currentAlpha * alphaWidth; - alphaSlideHelper.css({ - "left": (alphaX - (alphaSlideHelperWidth / 2)) + "px" - }); - - // Where to show the bar that displays your current selected hue - var slideY = (currentHue) * slideHeight; - slideHelper.css({ - "top": (slideY - slideHelperHeight) + "px" - }); - } - } - - function updateOriginalInput(fireCallback) { - var color = get(), - displayColor = '', - hasChanged = !tinycolor.equals(color, colorOnShow); - - if (color) { - displayColor = color.toString(currentPreferredFormat); - // Update the selection palette with the current color - addColorToSelectionPalette(color); - } - - if (isInput) { - boundElement.val(displayColor); - } - - if (fireCallback && hasChanged) { - callbacks.change(color); - boundElement.trigger('change', [ color ]); - } - } - - function reflow() { - dragWidth = dragger.width(); - dragHeight = dragger.height(); - dragHelperHeight = dragHelper.height(); - slideWidth = slider.width(); - slideHeight = slider.height(); - slideHelperHeight = slideHelper.height(); - alphaWidth = alphaSlider.width(); - alphaSlideHelperWidth = alphaSlideHelper.width(); - - if (!flat) { - container.css("position", "absolute"); - if (opts.offset) { - container.offset(opts.offset); - } else { - container.offset(getOffset(container, offsetElement)); - } - } - - updateHelperLocations(); - - if (opts.showPalette) { - drawPalette(); - } - - boundElement.trigger('reflow.spectrum'); - } - - function destroy() { - boundElement.show(); - offsetElement.unbind("click.spectrum touchstart.spectrum"); - container.remove(); - replacer.remove(); - spectrums[spect.id] = null; - } - - function option(optionName, optionValue) { - if (optionName === undefined) { - return $.extend({}, opts); - } - if (optionValue === undefined) { - return opts[optionName]; - } - - opts[optionName] = optionValue; - applyOptions(); - } - - function enable() { - disabled = false; - boundElement.attr("disabled", false); - offsetElement.removeClass("sp-disabled"); - } - - function disable() { - hide(); - disabled = true; - boundElement.attr("disabled", true); - offsetElement.addClass("sp-disabled"); - } - - function setOffset(coord) { - opts.offset = coord; - reflow(); - } - - initialize(); - - var spect = { - show: show, - hide: hide, - toggle: toggle, - reflow: reflow, - option: option, - enable: enable, - disable: disable, - offset: setOffset, - set: function (c) { - set(c); - updateOriginalInput(); - }, - get: get, - destroy: destroy, - container: container - }; - - spect.id = spectrums.push(spect) - 1; - - return spect; - } - - /** - * checkOffset - get the offset below/above and left/right element depending on screen position - * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js - */ - function getOffset(picker, input) { - var extraY = 0; - var dpWidth = picker.outerWidth(); - var dpHeight = picker.outerHeight(); - var inputHeight = input.outerHeight(); - var doc = picker[0].ownerDocument; - var docElem = doc.documentElement; - var viewWidth = docElem.clientWidth + $(doc).scrollLeft(); - var viewHeight = docElem.clientHeight + $(doc).scrollTop(); - var offset = input.offset(); - offset.top += inputHeight; - - offset.left -= - Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? - Math.abs(offset.left + dpWidth - viewWidth) : 0); - - offset.top -= - Math.min(offset.top, ((offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? - Math.abs(dpHeight + inputHeight - extraY) : extraY)); - - return offset; - } - - /** - * noop - do nothing - */ - function noop() { - - } - - /** - * stopPropagation - makes the code only doing this a little easier to read in line - */ - function stopPropagation(e) { - e.stopPropagation(); - } - - /** - * Create a function bound to a given object - * Thanks to underscore.js - */ - function bind(func, obj) { - var slice = Array.prototype.slice; - var args = slice.call(arguments, 2); - return function () { - return func.apply(obj, args.concat(slice.call(arguments))); - }; - } - - /** - * Lightweight drag helper. Handles containment within the element, so that - * when dragging, the x is within [0,element.width] and y is within [0,element.height] - */ - function draggable(element, onmove, onstart, onstop) { - onmove = onmove || function () { }; - onstart = onstart || function () { }; - onstop = onstop || function () { }; - var doc = document; - var dragging = false; - var offset = {}; - var maxHeight = 0; - var maxWidth = 0; - var hasTouch = ('ontouchstart' in window); - - var duringDragEvents = {}; - duringDragEvents["selectstart"] = prevent; - duringDragEvents["dragstart"] = prevent; - duringDragEvents["touchmove mousemove"] = move; - duringDragEvents["touchend mouseup"] = stop; - - function prevent(e) { - if (e.stopPropagation) { - e.stopPropagation(); - } - if (e.preventDefault) { - e.preventDefault(); - } - e.returnValue = false; - } - - function move(e) { - if (dragging) { - // Mouseup happened outside of window - if (IE && doc.documentMode < 9 && !e.button) { - return stop(); - } - - var t0 = e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0]; - var pageX = t0 && t0.pageX || e.pageX; - var pageY = t0 && t0.pageY || e.pageY; - - var dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth)); - var dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight)); - - if (hasTouch) { - // Stop scrolling in iOS - prevent(e); - } - - onmove.apply(element, [dragX, dragY, e]); - } - } - - function start(e) { - var rightclick = (e.which) ? (e.which == 3) : (e.button == 2); - - if (!rightclick && !dragging) { - if (onstart.apply(element, arguments) !== false) { - dragging = true; - maxHeight = $(element).height(); - maxWidth = $(element).width(); - offset = $(element).offset(); - - $(doc).bind(duringDragEvents); - $(doc.body).addClass("sp-dragging"); - - move(e); - - prevent(e); - } - } - } - - function stop() { - if (dragging) { - $(doc).unbind(duringDragEvents); - $(doc.body).removeClass("sp-dragging"); - - // Wait a tick before notifying observers to allow the click event - // to fire in Chrome. - setTimeout(function() { - onstop.apply(element, arguments); - }, 0); - } - dragging = false; - } - - $(element).bind("touchstart mousedown", start); - } - - function throttle(func, wait, debounce) { - var timeout; - return function () { - var context = this, args = arguments; - var throttler = function () { - timeout = null; - func.apply(context, args); - }; - if (debounce) clearTimeout(timeout); - if (debounce || !timeout) timeout = setTimeout(throttler, wait); - }; - } - - function inputTypeColorSupport() { - return $.fn.spectrum.inputTypeColorSupport(); - } - - /** - * Define a jQuery plugin - */ - var dataID = "spectrum.id"; - $.fn.spectrum = function (opts, extra) { - - if (typeof opts == "string") { - - var returnValue = this; - var args = Array.prototype.slice.call( arguments, 1 ); - - this.each(function () { - var spect = spectrums[$(this).data(dataID)]; - if (spect) { - var method = spect[opts]; - if (!method) { - throw new Error( "Spectrum: no such method: '" + opts + "'" ); - } - - if (opts == "get") { - returnValue = spect.get(); - } - else if (opts == "container") { - returnValue = spect.container; - } - else if (opts == "option") { - returnValue = spect.option.apply(spect, args); - } - else if (opts == "destroy") { - spect.destroy(); - $(this).removeData(dataID); - } - else { - method.apply(spect, args); - } - } - }); - - return returnValue; - } - - // Initializing a new instance of spectrum - return this.spectrum("destroy").each(function () { - var options = $.extend({}, opts, $(this).data()); - var spect = spectrum(this, options); - $(this).data(dataID, spect.id); - }); - }; - - $.fn.spectrum.load = true; - $.fn.spectrum.loadOpts = {}; - $.fn.spectrum.draggable = draggable; - $.fn.spectrum.defaults = defaultOpts; - $.fn.spectrum.inputTypeColorSupport = function inputTypeColorSupport() { - if (typeof inputTypeColorSupport._cachedResult === "undefined") { - var colorInput = $("")[0]; - inputTypeColorSupport._cachedResult = colorInput.type === "color" && colorInput.value !== "!"; - } - return inputTypeColorSupport._cachedResult; - }; - - $.spectrum = { }; - $.spectrum.localization = { }; - $.spectrum.palettes = { }; - - $.fn.spectrum.processNativeColorInputs = function () { - var colorInputs = $("input[type=color]"); - if (colorInputs.length && !inputTypeColorSupport()) { - colorInputs.spectrum({ - preferredFormat: "hex6" - }); - } - }; - - // TinyColor v1.1.2 - // https://github.com/bgrins/TinyColor - // Brian Grinstead, MIT License - - (function() { - - var trimLeft = /^[\s,#]+/, - trimRight = /\s+$/, - tinyCounter = 0, - math = Math, - mathRound = math.round, - mathMin = math.min, - mathMax = math.max, - mathRandom = math.random; - - var tinycolor = function(color, opts) { - - color = (color) ? color : ''; - opts = opts || { }; - - // If input is already a tinycolor, return itself - if (color instanceof tinycolor) { - return color; - } - // If we are called as a function, call using new instead - if (!(this instanceof tinycolor)) { - return new tinycolor(color, opts); - } - - var rgb = inputToRGB(color); - this._originalInput = color, - this._r = rgb.r, - this._g = rgb.g, - this._b = rgb.b, - this._a = rgb.a, - this._roundA = mathRound(100*this._a) / 100, - this._format = opts.format || rgb.format; - this._gradientType = opts.gradientType; - - // Don't let the range of [0,255] come back in [0,1]. - // Potentially lose a little bit of precision here, but will fix issues where - // .5 gets interpreted as half of the total, instead of half of 1 - // If it was supposed to be 128, this was already taken care of by `inputToRgb` - if (this._r < 1) { this._r = mathRound(this._r); } - if (this._g < 1) { this._g = mathRound(this._g); } - if (this._b < 1) { this._b = mathRound(this._b); } - - this._ok = rgb.ok; - this._tc_id = tinyCounter++; - }; - - tinycolor.prototype = { - isDark: function() { - return this.getBrightness() < 128; - }, - isLight: function() { - return !this.isDark(); - }, - isValid: function() { - return this._ok; - }, - getOriginalInput: function() { - return this._originalInput; - }, - getFormat: function() { - return this._format; - }, - getAlpha: function() { - return this._a; - }, - getBrightness: function() { - var rgb = this.toRgb(); - return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000; - }, - setAlpha: function(value) { - this._a = boundAlpha(value); - this._roundA = mathRound(100*this._a) / 100; - return this; - }, - toHsv: function() { - var hsv = rgbToHsv(this._r, this._g, this._b); - return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a }; - }, - toHsvString: function() { - var hsv = rgbToHsv(this._r, this._g, this._b); - var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100); - return (this._a == 1) ? - "hsv(" + h + ", " + s + "%, " + v + "%)" : - "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")"; - }, - toHsl: function() { - var hsl = rgbToHsl(this._r, this._g, this._b); - return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a }; - }, - toHslString: function() { - var hsl = rgbToHsl(this._r, this._g, this._b); - var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100); - return (this._a == 1) ? - "hsl(" + h + ", " + s + "%, " + l + "%)" : - "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")"; - }, - toHex: function(allow3Char) { - return rgbToHex(this._r, this._g, this._b, allow3Char); - }, - toHexString: function(allow3Char) { - return '#' + this.toHex(allow3Char); - }, - toHex8: function() { - return rgbaToHex(this._r, this._g, this._b, this._a); - }, - toHex8String: function() { - return '#' + this.toHex8(); - }, - toRgb: function() { - return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a }; - }, - toRgbString: function() { - return (this._a == 1) ? - "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" : - "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")"; - }, - toPercentageRgb: function() { - return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a }; - }, - toPercentageRgbString: function() { - return (this._a == 1) ? - "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" : - "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")"; - }, - toName: function() { - if (this._a === 0) { - return "transparent"; - } - - if (this._a < 1) { - return false; - } - - return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false; - }, - toFilter: function(secondColor) { - var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a); - var secondHex8String = hex8String; - var gradientType = this._gradientType ? "GradientType = 1, " : ""; - - if (secondColor) { - var s = tinycolor(secondColor); - secondHex8String = s.toHex8String(); - } - - return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")"; - }, - toString: function(format) { - var formatSet = !!format; - format = format || this._format; - - var formattedString = false; - var hasAlpha = this._a < 1 && this._a >= 0; - var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name"); - - if (needsAlphaFormat) { - // Special case for "transparent", all other non-alpha formats - // will return rgba when there is transparency. - if (format === "name" && this._a === 0) { - return this.toName(); - } - return this.toRgbString(); - } - if (format === "rgb") { - formattedString = this.toRgbString(); - } - if (format === "prgb") { - formattedString = this.toPercentageRgbString(); - } - if (format === "hex" || format === "hex6") { - formattedString = this.toHexString(); - } - if (format === "hex3") { - formattedString = this.toHexString(true); - } - if (format === "hex8") { - formattedString = this.toHex8String(); - } - if (format === "name") { - formattedString = this.toName(); - } - if (format === "hsl") { - formattedString = this.toHslString(); - } - if (format === "hsv") { - formattedString = this.toHsvString(); - } - - return formattedString || this.toHexString(); - }, - - _applyModification: function(fn, args) { - var color = fn.apply(null, [this].concat([].slice.call(args))); - this._r = color._r; - this._g = color._g; - this._b = color._b; - this.setAlpha(color._a); - return this; - }, - lighten: function() { - return this._applyModification(lighten, arguments); - }, - brighten: function() { - return this._applyModification(brighten, arguments); - }, - darken: function() { - return this._applyModification(darken, arguments); - }, - desaturate: function() { - return this._applyModification(desaturate, arguments); - }, - saturate: function() { - return this._applyModification(saturate, arguments); - }, - greyscale: function() { - return this._applyModification(greyscale, arguments); - }, - spin: function() { - return this._applyModification(spin, arguments); - }, - - _applyCombination: function(fn, args) { - return fn.apply(null, [this].concat([].slice.call(args))); - }, - analogous: function() { - return this._applyCombination(analogous, arguments); - }, - complement: function() { - return this._applyCombination(complement, arguments); - }, - monochromatic: function() { - return this._applyCombination(monochromatic, arguments); - }, - splitcomplement: function() { - return this._applyCombination(splitcomplement, arguments); - }, - triad: function() { - return this._applyCombination(triad, arguments); - }, - tetrad: function() { - return this._applyCombination(tetrad, arguments); - } - }; - - // If input is an object, force 1 into "1.0" to handle ratios properly - // String input requires "1.0" as input, so 1 will be treated as 1 - tinycolor.fromRatio = function(color, opts) { - if (typeof color == "object") { - var newColor = {}; - for (var i in color) { - if (color.hasOwnProperty(i)) { - if (i === "a") { - newColor[i] = color[i]; - } - else { - newColor[i] = convertToPercentage(color[i]); - } - } - } - color = newColor; - } - - return tinycolor(color, opts); - }; - - // Given a string or object, convert that input to RGB - // Possible string inputs: - // - // "red" - // "#f00" or "f00" - // "#ff0000" or "ff0000" - // "#ff000000" or "ff000000" - // "rgb 255 0 0" or "rgb (255, 0, 0)" - // "rgb 1.0 0 0" or "rgb (1, 0, 0)" - // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" - // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" - // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" - // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" - // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" - // - function inputToRGB(color) { - - var rgb = { r: 0, g: 0, b: 0 }; - var a = 1; - var ok = false; - var format = false; - - if (typeof color == "string") { - color = stringInputToObject(color); - } - - if (typeof color == "object") { - if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) { - rgb = rgbToRgb(color.r, color.g, color.b); - ok = true; - format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb"; - } - else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) { - color.s = convertToPercentage(color.s); - color.v = convertToPercentage(color.v); - rgb = hsvToRgb(color.h, color.s, color.v); - ok = true; - format = "hsv"; - } - else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) { - color.s = convertToPercentage(color.s); - color.l = convertToPercentage(color.l); - rgb = hslToRgb(color.h, color.s, color.l); - ok = true; - format = "hsl"; - } - - if (color.hasOwnProperty("a")) { - a = color.a; - } - } - - a = boundAlpha(a); - - return { - ok: ok, - format: color.format || format, - r: mathMin(255, mathMax(rgb.r, 0)), - g: mathMin(255, mathMax(rgb.g, 0)), - b: mathMin(255, mathMax(rgb.b, 0)), - a: a - }; - } - - - // Conversion Functions - // -------------------- - - // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from: - // - - // `rgbToRgb` - // Handle bounds / percentage checking to conform to CSS color spec - // - // *Assumes:* r, g, b in [0, 255] or [0, 1] - // *Returns:* { r, g, b } in [0, 255] - function rgbToRgb(r, g, b){ - return { - r: bound01(r, 255) * 255, - g: bound01(g, 255) * 255, - b: bound01(b, 255) * 255 - }; - } - - // `rgbToHsl` - // Converts an RGB color value to HSL. - // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] - // *Returns:* { h, s, l } in [0,1] - function rgbToHsl(r, g, b) { - - r = bound01(r, 255); - g = bound01(g, 255); - b = bound01(b, 255); - - var max = mathMax(r, g, b), min = mathMin(r, g, b); - var h, s, l = (max + min) / 2; - - if(max == min) { - h = s = 0; // achromatic - } - else { - var d = max - min; - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - switch(max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - - h /= 6; - } - - return { h: h, s: s, l: l }; - } - - // `hslToRgb` - // Converts an HSL color value to RGB. - // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] - // *Returns:* { r, g, b } in the set [0, 255] - function hslToRgb(h, s, l) { - var r, g, b; - - h = bound01(h, 360); - s = bound01(s, 100); - l = bound01(l, 100); - - function hue2rgb(p, q, t) { - if(t < 0) t += 1; - if(t > 1) t -= 1; - if(t < 1/6) return p + (q - p) * 6 * t; - if(t < 1/2) return q; - if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; - return p; - } - - if(s === 0) { - r = g = b = l; // achromatic - } - else { - var q = l < 0.5 ? l * (1 + s) : l + s - l * s; - var p = 2 * l - q; - r = hue2rgb(p, q, h + 1/3); - g = hue2rgb(p, q, h); - b = hue2rgb(p, q, h - 1/3); - } - - return { r: r * 255, g: g * 255, b: b * 255 }; - } - - // `rgbToHsv` - // Converts an RGB color value to HSV - // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] - // *Returns:* { h, s, v } in [0,1] - function rgbToHsv(r, g, b) { - - r = bound01(r, 255); - g = bound01(g, 255); - b = bound01(b, 255); - - var max = mathMax(r, g, b), min = mathMin(r, g, b); - var h, s, v = max; - - var d = max - min; - s = max === 0 ? 0 : d / max; - - if(max == min) { - h = 0; // achromatic - } - else { - switch(max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h, s: s, v: v }; - } - - // `hsvToRgb` - // Converts an HSV color value to RGB. - // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] - // *Returns:* { r, g, b } in the set [0, 255] - function hsvToRgb(h, s, v) { - - h = bound01(h, 360) * 6; - s = bound01(s, 100); - v = bound01(v, 100); - - var i = math.floor(h), - f = h - i, - p = v * (1 - s), - q = v * (1 - f * s), - t = v * (1 - (1 - f) * s), - mod = i % 6, - r = [v, q, p, p, t, v][mod], - g = [t, v, v, q, p, p][mod], - b = [p, p, t, v, v, q][mod]; - - return { r: r * 255, g: g * 255, b: b * 255 }; - } - - // `rgbToHex` - // Converts an RGB color to hex - // Assumes r, g, and b are contained in the set [0, 255] - // Returns a 3 or 6 character hex - function rgbToHex(r, g, b, allow3Char) { - - var hex = [ - pad2(mathRound(r).toString(16)), - pad2(mathRound(g).toString(16)), - pad2(mathRound(b).toString(16)) - ]; - - // Return a 3 character hex if possible - if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) { - return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); - } - - return hex.join(""); - } - // `rgbaToHex` - // Converts an RGBA color plus alpha transparency to hex - // Assumes r, g, b and a are contained in the set [0, 255] - // Returns an 8 character hex - function rgbaToHex(r, g, b, a) { - - var hex = [ - pad2(convertDecimalToHex(a)), - pad2(mathRound(r).toString(16)), - pad2(mathRound(g).toString(16)), - pad2(mathRound(b).toString(16)) - ]; - - return hex.join(""); - } - - // `equals` - // Can be called with any tinycolor input - tinycolor.equals = function (color1, color2) { - if (!color1 || !color2) { return false; } - return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); - }; - tinycolor.random = function() { - return tinycolor.fromRatio({ - r: mathRandom(), - g: mathRandom(), - b: mathRandom() - }); - }; - - - // Modification Functions - // ---------------------- - // Thanks to less.js for some of the basics here - // - - function desaturate(color, amount) { - amount = (amount === 0) ? 0 : (amount || 10); - var hsl = tinycolor(color).toHsl(); - hsl.s -= amount / 100; - hsl.s = clamp01(hsl.s); - return tinycolor(hsl); - } - - function saturate(color, amount) { - amount = (amount === 0) ? 0 : (amount || 10); - var hsl = tinycolor(color).toHsl(); - hsl.s += amount / 100; - hsl.s = clamp01(hsl.s); - return tinycolor(hsl); - } - - function greyscale(color) { - return tinycolor(color).desaturate(100); - } - - function lighten (color, amount) { - amount = (amount === 0) ? 0 : (amount || 10); - var hsl = tinycolor(color).toHsl(); - hsl.l += amount / 100; - hsl.l = clamp01(hsl.l); - return tinycolor(hsl); - } - - function brighten(color, amount) { - amount = (amount === 0) ? 0 : (amount || 10); - var rgb = tinycolor(color).toRgb(); - rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100)))); - rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100)))); - rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100)))); - return tinycolor(rgb); - } - - function darken (color, amount) { - amount = (amount === 0) ? 0 : (amount || 10); - var hsl = tinycolor(color).toHsl(); - hsl.l -= amount / 100; - hsl.l = clamp01(hsl.l); - return tinycolor(hsl); - } - - // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue. - // Values outside of this range will be wrapped into this range. - function spin(color, amount) { - var hsl = tinycolor(color).toHsl(); - var hue = (mathRound(hsl.h) + amount) % 360; - hsl.h = hue < 0 ? 360 + hue : hue; - return tinycolor(hsl); - } - - // Combination Functions - // --------------------- - // Thanks to jQuery xColor for some of the ideas behind these - // - - function complement(color) { - var hsl = tinycolor(color).toHsl(); - hsl.h = (hsl.h + 180) % 360; - return tinycolor(hsl); - } - - function triad(color) { - var hsl = tinycolor(color).toHsl(); - var h = hsl.h; - return [ - tinycolor(color), - tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }), - tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l }) - ]; - } - - function tetrad(color) { - var hsl = tinycolor(color).toHsl(); - var h = hsl.h; - return [ - tinycolor(color), - tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }), - tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }), - tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l }) - ]; - } - - function splitcomplement(color) { - var hsl = tinycolor(color).toHsl(); - var h = hsl.h; - return [ - tinycolor(color), - tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}), - tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l}) - ]; - } - - function analogous(color, results, slices) { - results = results || 6; - slices = slices || 30; - - var hsl = tinycolor(color).toHsl(); - var part = 360 / slices; - var ret = [tinycolor(color)]; - - for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) { - hsl.h = (hsl.h + part) % 360; - ret.push(tinycolor(hsl)); - } - return ret; - } - - function monochromatic(color, results) { - results = results || 6; - var hsv = tinycolor(color).toHsv(); - var h = hsv.h, s = hsv.s, v = hsv.v; - var ret = []; - var modification = 1 / results; - - while (results--) { - ret.push(tinycolor({ h: h, s: s, v: v})); - v = (v + modification) % 1; - } - - return ret; - } - - // Utility Functions - // --------------------- - - tinycolor.mix = function(color1, color2, amount) { - amount = (amount === 0) ? 0 : (amount || 50); - - var rgb1 = tinycolor(color1).toRgb(); - var rgb2 = tinycolor(color2).toRgb(); - - var p = amount / 100; - var w = p * 2 - 1; - var a = rgb2.a - rgb1.a; - - var w1; - - if (w * a == -1) { - w1 = w; - } else { - w1 = (w + a) / (1 + w * a); - } - - w1 = (w1 + 1) / 2; - - var w2 = 1 - w1; - - var rgba = { - r: rgb2.r * w1 + rgb1.r * w2, - g: rgb2.g * w1 + rgb1.g * w2, - b: rgb2.b * w1 + rgb1.b * w2, - a: rgb2.a * p + rgb1.a * (1 - p) - }; - - return tinycolor(rgba); - }; - - - // Readability Functions - // --------------------- - // - - // `readability` - // Analyze the 2 colors and returns an object with the following properties: - // `brightness`: difference in brightness between the two colors - // `color`: difference in color/hue between the two colors - tinycolor.readability = function(color1, color2) { - var c1 = tinycolor(color1); - var c2 = tinycolor(color2); - var rgb1 = c1.toRgb(); - var rgb2 = c2.toRgb(); - var brightnessA = c1.getBrightness(); - var brightnessB = c2.getBrightness(); - var colorDiff = ( - Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) + - Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) + - Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b) - ); - - return { - brightness: Math.abs(brightnessA - brightnessB), - color: colorDiff - }; - }; - - // `readable` - // http://www.w3.org/TR/AERT#color-contrast - // Ensure that foreground and background color combinations provide sufficient contrast. - // *Example* - // tinycolor.isReadable("#000", "#111") => false - tinycolor.isReadable = function(color1, color2) { - var readability = tinycolor.readability(color1, color2); - return readability.brightness > 125 && readability.color > 500; - }; - - // `mostReadable` - // Given a base color and a list of possible foreground or background - // colors for that base, returns the most readable color. - // *Example* - // tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000" - tinycolor.mostReadable = function(baseColor, colorList) { - var bestColor = null; - var bestScore = 0; - var bestIsReadable = false; - for (var i=0; i < colorList.length; i++) { - - // We normalize both around the "acceptable" breaking point, - // but rank brightness constrast higher than hue. - - var readability = tinycolor.readability(baseColor, colorList[i]); - var readable = readability.brightness > 125 && readability.color > 500; - var score = 3 * (readability.brightness / 125) + (readability.color / 500); - - if ((readable && ! bestIsReadable) || - (readable && bestIsReadable && score > bestScore) || - ((! readable) && (! bestIsReadable) && score > bestScore)) { - bestIsReadable = readable; - bestScore = score; - bestColor = tinycolor(colorList[i]); - } - } - return bestColor; - }; - - - // Big List of Colors - // ------------------ - // - var names = tinycolor.names = { - aliceblue: "f0f8ff", - antiquewhite: "faebd7", - aqua: "0ff", - aquamarine: "7fffd4", - azure: "f0ffff", - beige: "f5f5dc", - bisque: "ffe4c4", - black: "000", - blanchedalmond: "ffebcd", - blue: "00f", - blueviolet: "8a2be2", - brown: "a52a2a", - burlywood: "deb887", - burntsienna: "ea7e5d", - cadetblue: "5f9ea0", - chartreuse: "7fff00", - chocolate: "d2691e", - coral: "ff7f50", - cornflowerblue: "6495ed", - cornsilk: "fff8dc", - crimson: "dc143c", - cyan: "0ff", - darkblue: "00008b", - darkcyan: "008b8b", - darkgoldenrod: "b8860b", - darkgray: "a9a9a9", - darkgreen: "006400", - darkgrey: "a9a9a9", - darkkhaki: "bdb76b", - darkmagenta: "8b008b", - darkolivegreen: "556b2f", - darkorange: "ff8c00", - darkorchid: "9932cc", - darkred: "8b0000", - darksalmon: "e9967a", - darkseagreen: "8fbc8f", - darkslateblue: "483d8b", - darkslategray: "2f4f4f", - darkslategrey: "2f4f4f", - darkturquoise: "00ced1", - darkviolet: "9400d3", - deeppink: "ff1493", - deepskyblue: "00bfff", - dimgray: "696969", - dimgrey: "696969", - dodgerblue: "1e90ff", - firebrick: "b22222", - floralwhite: "fffaf0", - forestgreen: "228b22", - fuchsia: "f0f", - gainsboro: "dcdcdc", - ghostwhite: "f8f8ff", - gold: "ffd700", - goldenrod: "daa520", - gray: "808080", - green: "008000", - greenyellow: "adff2f", - grey: "808080", - honeydew: "f0fff0", - hotpink: "ff69b4", - indianred: "cd5c5c", - indigo: "4b0082", - ivory: "fffff0", - khaki: "f0e68c", - lavender: "e6e6fa", - lavenderblush: "fff0f5", - lawngreen: "7cfc00", - lemonchiffon: "fffacd", - lightblue: "add8e6", - lightcoral: "f08080", - lightcyan: "e0ffff", - lightgoldenrodyellow: "fafad2", - lightgray: "d3d3d3", - lightgreen: "90ee90", - lightgrey: "d3d3d3", - lightpink: "ffb6c1", - lightsalmon: "ffa07a", - lightseagreen: "20b2aa", - lightskyblue: "87cefa", - lightslategray: "789", - lightslategrey: "789", - lightsteelblue: "b0c4de", - lightyellow: "ffffe0", - lime: "0f0", - limegreen: "32cd32", - linen: "faf0e6", - magenta: "f0f", - maroon: "800000", - mediumaquamarine: "66cdaa", - mediumblue: "0000cd", - mediumorchid: "ba55d3", - mediumpurple: "9370db", - mediumseagreen: "3cb371", - mediumslateblue: "7b68ee", - mediumspringgreen: "00fa9a", - mediumturquoise: "48d1cc", - mediumvioletred: "c71585", - midnightblue: "191970", - mintcream: "f5fffa", - mistyrose: "ffe4e1", - moccasin: "ffe4b5", - navajowhite: "ffdead", - navy: "000080", - oldlace: "fdf5e6", - olive: "808000", - olivedrab: "6b8e23", - orange: "ffa500", - orangered: "ff4500", - orchid: "da70d6", - palegoldenrod: "eee8aa", - palegreen: "98fb98", - paleturquoise: "afeeee", - palevioletred: "db7093", - papayawhip: "ffefd5", - peachpuff: "ffdab9", - peru: "cd853f", - pink: "ffc0cb", - plum: "dda0dd", - powderblue: "b0e0e6", - purple: "800080", - rebeccapurple: "663399", - red: "f00", - rosybrown: "bc8f8f", - royalblue: "4169e1", - saddlebrown: "8b4513", - salmon: "fa8072", - sandybrown: "f4a460", - seagreen: "2e8b57", - seashell: "fff5ee", - sienna: "a0522d", - silver: "c0c0c0", - skyblue: "87ceeb", - slateblue: "6a5acd", - slategray: "708090", - slategrey: "708090", - snow: "fffafa", - springgreen: "00ff7f", - steelblue: "4682b4", - tan: "d2b48c", - teal: "008080", - thistle: "d8bfd8", - tomato: "ff6347", - turquoise: "40e0d0", - violet: "ee82ee", - wheat: "f5deb3", - white: "fff", - whitesmoke: "f5f5f5", - yellow: "ff0", - yellowgreen: "9acd32" - }; - - // Make it easy to access colors via `hexNames[hex]` - var hexNames = tinycolor.hexNames = flip(names); - - - // Utilities - // --------- - - // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }` - function flip(o) { - var flipped = { }; - for (var i in o) { - if (o.hasOwnProperty(i)) { - flipped[o[i]] = i; - } - } - return flipped; - } - - // Return a valid alpha value [0,1] with all invalid values being set to 1 - function boundAlpha(a) { - a = parseFloat(a); - - if (isNaN(a) || a < 0 || a > 1) { - a = 1; - } - - return a; - } - - // Take input from [0, n] and return it as [0, 1] - function bound01(n, max) { - if (isOnePointZero(n)) { n = "100%"; } - - var processPercent = isPercentage(n); - n = mathMin(max, mathMax(0, parseFloat(n))); - - // Automatically convert percentage into number - if (processPercent) { - n = parseInt(n * max, 10) / 100; - } - - // Handle floating point rounding errors - if ((math.abs(n - max) < 0.000001)) { - return 1; - } - - // Convert into [0, 1] range if it isn't already - return (n % max) / parseFloat(max); - } - - // Force a number between 0 and 1 - function clamp01(val) { - return mathMin(1, mathMax(0, val)); - } - - // Parse a base-16 hex value into a base-10 integer - function parseIntFromHex(val) { - return parseInt(val, 16); - } - - // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 - // - function isOnePointZero(n) { - return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1; - } - - // Check to see if string passed in is a percentage - function isPercentage(n) { - return typeof n === "string" && n.indexOf('%') != -1; - } - - // Force a hex value to have 2 characters - function pad2(c) { - return c.length == 1 ? '0' + c : '' + c; - } - - // Replace a decimal with it's percentage value - function convertToPercentage(n) { - if (n <= 1) { - n = (n * 100) + "%"; - } - - return n; - } - - // Converts a decimal to a hex value - function convertDecimalToHex(d) { - return Math.round(parseFloat(d) * 255).toString(16); - } - // Converts a hex value to a decimal - function convertHexToDecimal(h) { - return (parseIntFromHex(h) / 255); - } - - var matchers = (function() { - - // - var CSS_INTEGER = "[-\\+]?\\d+%?"; - - // - var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; - - // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. - var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; - - // Actual matching. - // Parentheses and commas are optional, but not required. - // Whitespace can take the place of commas or opening paren - var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; - var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; - - return { - rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), - rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), - hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), - hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), - hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), - hsva: new RegExp("hsva" + PERMISSIVE_MATCH4), - hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, - hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, - hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ - }; - })(); - - // `stringInputToObject` - // Permissive string parsing. Take in a number of formats, and output an object - // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` - function stringInputToObject(color) { - - color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase(); - var named = false; - if (names[color]) { - color = names[color]; - named = true; - } - else if (color == 'transparent') { - return { r: 0, g: 0, b: 0, a: 0, format: "name" }; - } - - // Try to match string input using regular expressions. - // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] - // Just return an object and let the conversion functions handle that. - // This way the result will be the same whether the tinycolor is initialized with string or object. - var match; - if ((match = matchers.rgb.exec(color))) { - return { r: match[1], g: match[2], b: match[3] }; - } - if ((match = matchers.rgba.exec(color))) { - return { r: match[1], g: match[2], b: match[3], a: match[4] }; - } - if ((match = matchers.hsl.exec(color))) { - return { h: match[1], s: match[2], l: match[3] }; - } - if ((match = matchers.hsla.exec(color))) { - return { h: match[1], s: match[2], l: match[3], a: match[4] }; - } - if ((match = matchers.hsv.exec(color))) { - return { h: match[1], s: match[2], v: match[3] }; - } - if ((match = matchers.hsva.exec(color))) { - return { h: match[1], s: match[2], v: match[3], a: match[4] }; - } - if ((match = matchers.hex8.exec(color))) { - return { - a: convertHexToDecimal(match[1]), - r: parseIntFromHex(match[2]), - g: parseIntFromHex(match[3]), - b: parseIntFromHex(match[4]), - format: named ? "name" : "hex8" - }; - } - if ((match = matchers.hex6.exec(color))) { - return { - r: parseIntFromHex(match[1]), - g: parseIntFromHex(match[2]), - b: parseIntFromHex(match[3]), - format: named ? "name" : "hex" - }; - } - if ((match = matchers.hex3.exec(color))) { - return { - r: parseIntFromHex(match[1] + '' + match[1]), - g: parseIntFromHex(match[2] + '' + match[2]), - b: parseIntFromHex(match[3] + '' + match[3]), - format: named ? "name" : "hex" - }; - } - - return false; - } - - window.tinycolor = tinycolor; - })(); - - $(function () { - if ($.fn.spectrum.load) { - $.fn.spectrum.processNativeColorInputs(); - } - }); - -}); From 387c40c6d20de6bb0dd4e46d423291a48196e488 Mon Sep 17 00:00:00 2001 From: ijin08 Date: Wed, 23 Jan 2019 16:23:48 +0100 Subject: [PATCH 38/57] replaced palette colors with current palette adjusted for dark and light theme --- .../src/utils/namedColorsPalette.ts | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/grafana-ui/src/utils/namedColorsPalette.ts b/packages/grafana-ui/src/utils/namedColorsPalette.ts index 881519d6ff5..691abc6fc8f 100644 --- a/packages/grafana-ui/src/utils/namedColorsPalette.ts +++ b/packages/grafana-ui/src/utils/namedColorsPalette.ts @@ -64,41 +64,41 @@ export const buildColorDefinition = ( isPrimary: !!isPrimary, }); -export const BasicGreen = buildColorDefinition('green', 'green', ['#53A642', '#53A642'], true); -export const DarkGreen = buildColorDefinition('green', 'dark-green', ['#106100', '#106100']); -export const SemiDarkGreen = buildColorDefinition('green', 'semi-dark-green', ['#388729', '#388729']); -export const LightGreen = buildColorDefinition('green', 'light-green', ['#72C462', '#72C462']); -export const SuperLightGreen = buildColorDefinition('green', 'super-light-green', ['#99E68A', '#99E68A']); +export const BasicGreen = buildColorDefinition('green', 'green', ['#5AA64B', '#77BF69'], true); +export const DarkGreen = buildColorDefinition('green', 'dark-green', ['#1E6910', '#388729']); +export const SemiDarkGreen = buildColorDefinition('green', 'semi-dark-green', ['#388729', '#5AA64B']); +export const LightGreen = buildColorDefinition('green', 'light-green', ['#77BF69', '#99D98D']); +export const SuperLightGreen = buildColorDefinition('green', 'super-light-green', ['#99D98D', '#CAF2C2']); -export const BasicYellow = buildColorDefinition('yellow', 'yellow', ['#F2CA00', '#F2CA00'], true); -export const DarkYellow = buildColorDefinition('yellow', 'dark-yellow', ['#A68A00', '#A68A00']); -export const SemiDarkYellow = buildColorDefinition('yellow', 'semi-dark-yellow', ['#CCAA00', '#CCAA00']); -export const LightYellow = buildColorDefinition('yellow', 'light-yellow', ['#F7D636', '#F7D636']); -export const SuperLightYellow = buildColorDefinition('yellow', 'super-light-yellow', ['#FFEB8A', '#FFEB8A']); +export const BasicYellow = buildColorDefinition('yellow', 'yellow', ['#F2CC0C', '#FADE2A'], true); +export const DarkYellow = buildColorDefinition('yellow', 'dark-yellow', ['#CC9D00', '#E0B400']); +export const SemiDarkYellow = buildColorDefinition('yellow', 'semi-dark-yellow', ['#E0B400', '#F2CC0C']); +export const LightYellow = buildColorDefinition('yellow', 'light-yellow', ['#FADE2A', '#FFEE52']); +export const SuperLightYellow = buildColorDefinition('yellow', 'super-light-yellow', ['#FFEE52', '#FFF899']); -export const BasicRed = buildColorDefinition('red', 'red', ['#F94462', '#F94462'], true); -export const DarkRed = buildColorDefinition('red', 'dark-red', ['#B3001D', '#B3001D']); -export const SemiDarkRed = buildColorDefinition('red', 'semi-dark-red', ['#D93651', '#D93651']); -export const LightRed = buildColorDefinition('red', 'light-red', ['#F07387', '#F07387']); -export const SuperLightRed = buildColorDefinition('red', 'super-light-red', ['#E6A1AC', '#E6A1AC']); +export const BasicRed = buildColorDefinition('red', 'red', ['#DE314D', '#F24965'], true); +export const DarkRed = buildColorDefinition('red', 'dark-red', ['#AB031F', '#C41834']); +export const SemiDarkRed = buildColorDefinition('red', 'semi-dark-red', ['#C41834', '#DE314D']); +export const LightRed = buildColorDefinition('red', 'light-red', ['#F24965', '#FF7389']); +export const SuperLightRed = buildColorDefinition('red', 'super-light-red', ['#FF7389', '#FFA6B4']); -export const BasicBlue = buildColorDefinition('blue', 'blue', ['#408BFF', '#408BFF'], true); -export const DarkBlue = buildColorDefinition('blue', 'dark-blue', ['#2155A6', '#2155A6']); -export const SemiDarkBlue = buildColorDefinition('blue', 'semi-dark-blue', ['#3471CF', '#3471CF']); -export const LightBlue = buildColorDefinition('blue', 'light-blue', ['#7DAEFA', '#7DAEFA']); -export const SuperLightBlue = buildColorDefinition('blue', 'super-light-blue', ['#B8D0F5', '#B8D0F5']); +export const BasicBlue = buildColorDefinition('blue', 'blue', ['#3274D9', '#5794F2'], true); +export const DarkBlue = buildColorDefinition('blue', 'dark-blue', ['#144796', '#1857B8']); +export const SemiDarkBlue = buildColorDefinition('blue', 'semi-dark-blue', ['#1857B8', '#3274D9']); +export const LightBlue = buildColorDefinition('blue', 'light-blue', ['#5794F2', '#8AB8FF']); +export const SuperLightBlue = buildColorDefinition('blue', 'super-light-blue', ['#8AB8FF', '#C0D8FF']); -export const BasicOrange = buildColorDefinition('orange', 'orange', ['#FA6400', '#FA6400'], true); -export const DarkOrange = buildColorDefinition('orange', 'dark-orange', ['#963C00', '#963C00']); -export const SemiDarkOrange = buildColorDefinition('orange', 'semi-dark-orange', ['#ED4B00', '#ED4B00']); -export const LightOrange = buildColorDefinition('orange', 'light-orange', ['#FC934C', '#FC934C']); -export const SuperLightOrange = buildColorDefinition('orange', 'super-light-orange', ['#FFC299', '#FFC299']); +export const BasicOrange = buildColorDefinition('orange', 'orange', ['#FF780A', '#FF9830'], true); +export const DarkOrange = buildColorDefinition('orange', 'dark-orange', ['#E55400', '#FA6400']); +export const SemiDarkOrange = buildColorDefinition('orange', 'semi-dark-orange', ['#FA6400', '#FF780A']); +export const LightOrange = buildColorDefinition('orange', 'light-orange', ['#FF9830', '#FFB357']); +export const SuperLightOrange = buildColorDefinition('orange', 'super-light-orange', ['#FFB357', '#FFCB7D']); -export const BasicPurple = buildColorDefinition('purple', 'purple', ['#BC67E6', '#BC67E6'], true); -export const DarkPurple = buildColorDefinition('purple', 'dark-purple', ['#701F99', '#701F99']); -export const SemiDarkPurple = buildColorDefinition('purple', 'semi-dark-purple', ['#9E43CC', '#9E43CC']); -export const LightPurple = buildColorDefinition('purple', 'light-purple', ['#D19AED', '#D19AED']); -export const SuperLightPurple = buildColorDefinition('purple', 'super-light-purple', ['#E6CEF2', '#E6CEF2']); +export const BasicPurple = buildColorDefinition('purple', 'purple', ['#A352CC', '#B877D9'], true); +export const DarkPurple = buildColorDefinition('purple', 'dark-purple', ['#732699', '#8936B2']); +export const SemiDarkPurple = buildColorDefinition('purple', 'semi-dark-purple', ['#8936B2', '#A352CC']); +export const LightPurple = buildColorDefinition('purple', 'light-purple', ['#B877D9', '#CA95E5']); +export const SuperLightPurple = buildColorDefinition('purple', 'super-light-purple', ['#CA95E5', '#DEB6F2']); const greens = [BasicGreen, DarkGreen, SemiDarkGreen, LightGreen, SuperLightGreen]; const yellows = [BasicYellow, DarkYellow, SemiDarkYellow, LightYellow, SuperLightYellow]; From d13efb337265a11ce9beb3911cfa27ea776ba040 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 23 Jan 2019 16:37:23 +0100 Subject: [PATCH 39/57] Removed sass import of spectrum.scss --- packages/grafana-ui/src/index.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/grafana-ui/src/index.scss b/packages/grafana-ui/src/index.scss index 74fdcde3072..841415620d6 100644 --- a/packages/grafana-ui/src/index.scss +++ b/packages/grafana-ui/src/index.scss @@ -1,3 +1 @@ -@import 'vendor/spectrum'; @import 'components/index'; - From 071b1f8478e2454b387667fea4acb4dd47581015 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 24 Jan 2019 09:56:52 +0100 Subject: [PATCH 40/57] Fixed issue with color name retrieval not being aware of current theme --- .../components/ColorPicker/ColorPickerPopover.tsx | 2 +- .../grafana-ui/src/utils/namedColorsPalette.test.ts | 12 +++++++----- packages/grafana-ui/src/utils/namedColorsPalette.ts | 12 +++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index 854b0b8bbfa..4bbeb790b6d 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -40,7 +40,7 @@ export class ColorPickerPopover extends React.Component { return activePicker === 'spectrum' ? ( ) : ( - + ); }; diff --git a/packages/grafana-ui/src/utils/namedColorsPalette.test.ts b/packages/grafana-ui/src/utils/namedColorsPalette.test.ts index ad5822fbaaa..dafca2de141 100644 --- a/packages/grafana-ui/src/utils/namedColorsPalette.test.ts +++ b/packages/grafana-ui/src/utils/namedColorsPalette.test.ts @@ -10,12 +10,13 @@ import { GrafanaTheme } from '../types/index'; describe('colors', () => { describe('getColorDefinition', () => { it('returns undefined for unknown hex', () => { - expect(getColorDefinition('#ff0000')).toBeUndefined(); + expect(getColorDefinition('#ff0000', GrafanaTheme.Light)).toBeUndefined(); + expect(getColorDefinition('#ff0000', GrafanaTheme.Dark)).toBeUndefined(); }); it('returns definition for known hex', () => { - expect(getColorDefinition(SemiDarkBlue.variants.light)).toEqual(SemiDarkBlue); - expect(getColorDefinition(SemiDarkBlue.variants.dark)).toEqual(SemiDarkBlue); + expect(getColorDefinition(SemiDarkBlue.variants.light, GrafanaTheme.Light)).toEqual(SemiDarkBlue); + expect(getColorDefinition(SemiDarkBlue.variants.dark, GrafanaTheme.Dark)).toEqual(SemiDarkBlue); }); }); @@ -25,8 +26,8 @@ describe('colors', () => { }); it('returns name for known hex', () => { - expect(getColorName(SemiDarkBlue.variants.light)).toEqual(SemiDarkBlue.name); - expect(getColorName(SemiDarkBlue.variants.dark)).toEqual(SemiDarkBlue.name); + expect(getColorName(SemiDarkBlue.variants.light, GrafanaTheme.Light)).toEqual(SemiDarkBlue.name); + expect(getColorName(SemiDarkBlue.variants.dark, GrafanaTheme.Dark)).toEqual(SemiDarkBlue.name); }); }); @@ -39,6 +40,7 @@ describe('colors', () => { expect(getColorByName(SemiDarkBlue.name)).toBe(SemiDarkBlue); }); }); + describe('getColorFromHexRgbOrName', () => { it('returns undefined for unknown color', () => { expect(() => getColorFromHexRgbOrName('aruba-sunshine')).toThrow(); diff --git a/packages/grafana-ui/src/utils/namedColorsPalette.ts b/packages/grafana-ui/src/utils/namedColorsPalette.ts index 691abc6fc8f..4112927ffd1 100644 --- a/packages/grafana-ui/src/utils/namedColorsPalette.ts +++ b/packages/grafana-ui/src/utils/namedColorsPalette.ts @@ -1,4 +1,4 @@ -import { flatten, some, values } from 'lodash'; +import { flatten } from 'lodash'; import { GrafanaTheme } from '../types'; type Hue = 'green' | 'yellow' | 'red' | 'blue' | 'orange' | 'purple'; @@ -114,10 +114,8 @@ ColorsPalette.set('blue', blues); ColorsPalette.set('orange', oranges); ColorsPalette.set('purple', purples); -export const getColorDefinition = (hex: string): ColorDefinition | undefined => { - return flatten(Array.from(ColorsPalette.values())).filter(definition => - some(values(definition.variants), color => color === hex) - )[0]; +export const getColorDefinition = (hex: string, theme: GrafanaTheme): ColorDefinition | undefined => { + return flatten(Array.from(ColorsPalette.values())).filter(definition => definition.variants[theme] === hex)[0]; }; const isHex = (color: string) => { @@ -125,7 +123,7 @@ const isHex = (color: string) => { return hexRegex.test(color); }; -export const getColorName = (color?: string): Color | undefined => { +export const getColorName = (color?: string, theme?: GrafanaTheme): Color | undefined => { if (!color) { return undefined; } @@ -134,7 +132,7 @@ export const getColorName = (color?: string): Color | undefined => { return undefined; } if (isHex(color)) { - const definition = getColorDefinition(color); + const definition = getColorDefinition(color, theme || GrafanaTheme.Dark); return definition ? definition.name : undefined; } From 549e0993c473d0c64c25d18b9ef62efb396421a1 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 24 Jan 2019 10:28:48 +0100 Subject: [PATCH 41/57] Restore missing styles --- .../src/components/ColorPicker/_ColorPicker.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss index 3ee97a2a357..8d6403602e9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss +++ b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss @@ -189,6 +189,14 @@ $arrowSize: 15px; z-index: 0; } +.sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner +{ + display:block; + position:absolute; + top:0;left:0;bottom:0;right:0; +} + + .gf-color-picker__body { padding-bottom: $arrowSize; padding-left: 6px; From d414e463bd791e3f73cac2ec19677778c225a728 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 24 Jan 2019 10:37:34 +0100 Subject: [PATCH 42/57] Update imports of NamedColorsPalette --- .../components/ColorPicker/ColorPickerPopover.tsx | 6 +++--- .../components/ColorPicker/NamedColorsPalette.tsx | 3 +-- .../src/components/ColorPicker/_ColorPicker.scss | 15 +++++++++------ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index 4bbeb790b6d..396488c8cad 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import NamedColorsPicker from './NamedColorsPalette'; +import { NamedColorsPalette } from './NamedColorsPalette'; import { getColorName, getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; import { ColorPickerProps, handleColorPickerPropsDeprecation } from './ColorPicker'; import { GrafanaTheme, Themeable } from '../../types'; @@ -38,9 +38,9 @@ export class ColorPickerPopover extends React.Component { const { color, theme } = this.props; return activePicker === 'spectrum' ? ( - + ) : ( - + ); }; diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx index 9ae216aa8b0..06e8f90ef4f 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx @@ -8,7 +8,7 @@ interface NamedColorsPaletteProps extends Themeable { onChange: (colorName: string) => void; } -const NamedColorsPalette = ({ color, onChange, theme }: NamedColorsPaletteProps) => { +export const NamedColorsPalette = ({ color, onChange, theme }: NamedColorsPaletteProps) => { const swatches: JSX.Element[] = []; ColorsPalette.forEach((colors, hue) => { swatches.push( @@ -38,4 +38,3 @@ const NamedColorsPalette = ({ color, onChange, theme }: NamedColorsPaletteProps) ); }; -export default NamedColorsPalette; diff --git a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss index 8d6403602e9..fc26a649c6c 100644 --- a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss +++ b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss @@ -189,14 +189,17 @@ $arrowSize: 15px; z-index: 0; } -.sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner -{ - display:block; - position:absolute; - top:0;left:0;bottom:0;right:0; +.sp-preview-inner, +.sp-alpha-inner, +.sp-thumb-inner { + display: block; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; } - .gf-color-picker__body { padding-bottom: $arrowSize; padding-left: 6px; From 1e69a7b8b719df94cf536d91b8c2d2e67de8841a Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 24 Jan 2019 10:37:53 +0100 Subject: [PATCH 43/57] Update story for NamedColorsPalette --- .../ColorPicker/NamedColorsPalette.story.tsx | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx index 58b524d5570..d6ac287c7fc 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx @@ -1,6 +1,6 @@ import React, { FunctionComponent } from 'react'; import { storiesOf } from '@storybook/react'; -import NamedColorsPalette from './NamedColorsPalette'; +import { NamedColorsPalette } from './NamedColorsPalette'; import { getColorName } from '../../utils/namedColorsPalette'; import { withKnobs, select } from '@storybook/addon-knobs'; @@ -35,7 +35,7 @@ export class UseState extends React.Component, { value: T static getDerivedStateFromProps(props: StateHolderProps<{}>, state: { value: {} }) { return { value: props.initialState, - ...state + ...state, }; } @@ -64,12 +64,7 @@ storiesOf('UI/NamedColorsPalette', module) return ( {(selectedColor, updateSelectedColor) => { - return ( - - ); + return ; }} ); @@ -78,12 +73,7 @@ storiesOf('UI/NamedColorsPalette', module) return ( {(selectedColor, updateSelectedColor) => { - return ( - - ); + return ; }} ); From 40dc29d13506ebf05151c978cff6f056fa941a87 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 24 Jan 2019 12:01:15 +0100 Subject: [PATCH 44/57] Make series overrides color picker display correctly --- .../src/components/ColorPicker/_ColorPicker.scss | 15 +++++++++++++++ public/app/core/angular_wrappers.ts | 1 + .../plugins/panel/graph/series_overrides_ctrl.ts | 4 +++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss index fc26a649c6c..6b8a0e491b2 100644 --- a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss +++ b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss @@ -210,3 +210,18 @@ $arrowSize: 15px; width: 210px; } } + +// TODO: Remove. This is a temporary solution until color picker popovers are used +// with Drop.js. +.drop-popover.drop-popover--transparent { + .drop-content { + border: none; + background: none; + padding: 0; + max-width: none; + + &:before { + display: none; + } + } +} diff --git a/public/app/core/angular_wrappers.ts b/public/app/core/angular_wrappers.ts index 65eb68966ff..4806275e87d 100644 --- a/public/app/core/angular_wrappers.ts +++ b/public/app/core/angular_wrappers.ts @@ -28,6 +28,7 @@ export function registerAngularDirectives() { ['onChange', { watchDepth: 'reference', wrapApply: true }], ]); react2AngularDirective('seriesColorPickerPopover', SeriesColorPickerPopover, [ + 'color', 'series', 'onColorChange', 'onToggleAxis', diff --git a/public/app/plugins/panel/graph/series_overrides_ctrl.ts b/public/app/plugins/panel/graph/series_overrides_ctrl.ts index 934fd835347..90d61a362ab 100644 --- a/public/app/plugins/panel/graph/series_overrides_ctrl.ts +++ b/public/app/plugins/panel/graph/series_overrides_ctrl.ts @@ -53,11 +53,13 @@ export function SeriesOverridesCtrl($scope, $element, popoverSrv) { element: $element.find('.dropdown')[0], position: 'top center', openOn: 'click', - template: '', + template: '', + classNames: 'drop-popover drop-popover--transparent', model: { autoClose: true, colorSelected: $scope.colorSelected, series: fakeSeries, + color, }, onClose: () => { $scope.ctrl.render(); From 8d8f944a1f6ac1d689b390f79cd39bcbc0857417 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 24 Jan 2019 13:18:21 +0100 Subject: [PATCH 45/57] Enable custom picers on color color picker popovers (for y-axis support in legend picker) --- .../components/ColorPicker/ColorPicker.tsx | 14 ++- .../ColorPicker/ColorPickerPopover.tsx | 113 +++++++++++++----- .../ColorPicker/SeriesColorPickerPopover.tsx | 13 +- 3 files changed, 98 insertions(+), 42 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 2f088b32327..51abd5e9d58 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -9,15 +9,10 @@ import propDeprecationWarning from '../../utils/propDeprecationWarning'; type ColorPickerChangeHandler = (color: string) => void; -export const handleColorPickerPropsDeprecation = (componentName: string, props: ColorPickerProps) => { - const { onColorChange } = props; - if (onColorChange) { - propDeprecationWarning(componentName, 'onColorChange', 'onChange'); - } -}; export interface ColorPickerProps extends Themeable { color: string; onChange: ColorPickerChangeHandler; + /** * @deprecated Use onChange instead */ @@ -27,6 +22,13 @@ export interface ColorPickerProps extends Themeable { children?: JSX.Element; } +export const handleColorPickerPropsDeprecation = (componentName: string, props: ColorPickerProps) => { + const { onColorChange } = props; + if (onColorChange) { + propDeprecationWarning(componentName, 'onColorChange', 'onChange'); + } +}; + export const colorPickerFactory = ( popover: React.ComponentType, displayName = 'ColorPicker', diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index 396488c8cad..03b6086b8dc 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -2,20 +2,28 @@ import React from 'react'; import { NamedColorsPalette } from './NamedColorsPalette'; import { getColorName, getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; import { ColorPickerProps, handleColorPickerPropsDeprecation } from './ColorPicker'; -import { GrafanaTheme, Themeable } from '../../types'; +import { GrafanaTheme } from '../../types'; import { PopperContentProps } from '../Tooltip/PopperController'; import SpectrumPalette from './SpectrumPalette'; -export interface Props extends ColorPickerProps, Themeable, PopperContentProps {} +export interface Props extends ColorPickerProps, PopperContentProps { + customPickers?: T; +} type PickerType = 'palette' | 'spectrum'; -interface State { - activePicker: PickerType; +interface CustomPickersDescriptor { + [key: string]: { + tabComponent: React.ComponentType; + name: string; + }; +} +interface State { + activePicker: PickerType | keyof T; } -export class ColorPickerPopover extends React.Component { - constructor(props: Props) { +export class ColorPickerPopover extends React.Component, State> { + constructor(props: Props) { super(props); this.state = { activePicker: 'palette', @@ -23,6 +31,11 @@ export class ColorPickerPopover extends React.Component { handleColorPickerPropsDeprecation('ColorPickerPopover', props); } + getTabClassName = (tabName: PickerType | keyof T) => { + const { activePicker } = this.state; + return `ColorPickerPopover__tab ${activePicker === tabName && 'ColorPickerPopover__tab--active'}`; + }; + handleChange = (color: any) => { const { onColorChange, onChange, enableNamedColors } = this.props; const changeHandler = onColorChange || onChange; @@ -33,55 +46,89 @@ export class ColorPickerPopover extends React.Component { changeHandler(getColorFromHexRgbOrName(color)); }; + handleTabChange = (tab: PickerType | keyof T, updatePopperPosition?: () => void) => { + return () => + this.setState({ activePicker: tab }, () => { + if (updatePopperPosition) { + updatePopperPosition(); + } + }); + }; + renderPicker = () => { const { activePicker } = this.state; const { color, theme } = this.props; - return activePicker === 'spectrum' ? ( - - ) : ( - + switch (activePicker) { + case 'spectrum': + return ; + case 'palette': + return ; + default: + return this.renderCustomPicker(activePicker); + } + }; + + renderCustomPicker = (tabKey: keyof T) => { + const { customPickers, color, theme } = this.props; + if (!customPickers) { + return null; + } + + return React.createElement(customPickers[tabKey].tabComponent, { + color, + theme, + onChange: this.handleChange, + }); + }; + + renderCustomPickerTabs = (updatePopperPosition?: () => void) => { + const { customPickers } = this.props; + + if (!customPickers) { + return null; + } + + return ( + <> + {Object.keys(customPickers).map(key => { + return ( +
+ {customPickers[key].name} +
+ ); + })} + ); }; render() { - const { activePicker } = this.state; - const { theme, children, updatePopperPosition } = this.props; + const { theme, updatePopperPosition } = this.props; const colorPickerTheme = theme || GrafanaTheme.Dark; return (
{ - this.setState({ activePicker: 'palette' }, () => { - if (updatePopperPosition) { - updatePopperPosition(); - } - }); - }} + className={this.getTabClassName('palette')} + onClick={this.handleTabChange('palette', updatePopperPosition)} > - Default + Colors
{ - this.setState({ activePicker: 'spectrum' }, () => { - if (updatePopperPosition) { - updatePopperPosition(); - } - }); - }} + className={this.getTabClassName('spectrum')} + onClick={this.handleTabChange('spectrum', updatePopperPosition)} > Custom
+ {this.renderCustomPickerTabs(updatePopperPosition)}
-
- {this.renderPicker()} - {children} -
+
{this.renderPicker()}
); } diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx index 559b3f4a550..0bc89c893cd 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx @@ -13,9 +13,16 @@ export const SeriesColorPickerPopover: FunctionComponent -
{yaxis && }
- +
{yaxis && }
+ }, + }} + /> ); }; From ce440b85fc269dcb3569f6cd9d635f16c85a4153 Mon Sep 17 00:00:00 2001 From: ijin08 Date: Thu, 24 Jan 2019 14:39:52 +0100 Subject: [PATCH 46/57] minor styling changes to gaps, font-size and width --- .../src/components/ColorPicker/NamedColorsPalette.tsx | 4 ++-- .../grafana-ui/src/components/ColorPicker/_ColorPicker.scss | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx index 06e8f90ef4f..8d09ec0cdec 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx @@ -29,8 +29,8 @@ export const NamedColorsPalette = ({ color, onChange, theme }: NamedColorsPalett style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', - gridRowGap: '32px', - gridColumnGap: '32px', + gridRowGap: '24px', + gridColumnGap: '24px', }} > {swatches} diff --git a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss index 6b8a0e491b2..95cf5141bf3 100644 --- a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss +++ b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss @@ -1,6 +1,7 @@ $arrowSize: 15px; .ColorPicker { @extend .popper; + font-size: 12px; } .ColorPicker__arrow { @@ -128,7 +129,7 @@ $arrowSize: 15px; } .ColorPickerPopover__content { - width: 360px; + width: 336px; padding: 24px; } From 1d3122632fb2925a181d33393a09751cb11b4cf1 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 24 Jan 2019 15:35:50 +0100 Subject: [PATCH 47/57] Move Switch component to grafana-ui --- .../grafana-ui/src}/components/Switch/Switch.tsx | 12 +++++++----- packages/grafana-ui/src/components/index.ts | 1 + .../features/dashboard/panel_editor/QueryOptions.tsx | 2 +- .../features/datasources/settings/BasicSettings.tsx | 5 +++-- public/app/features/explore/Logs.tsx | 3 +-- .../app/plugins/panel/gauge/GaugeOptionsEditor.tsx | 3 +-- .../app/plugins/panel/graph2/GraphPanelOptions.tsx | 4 +--- 7 files changed, 15 insertions(+), 15 deletions(-) rename {public/app/core => packages/grafana-ui/src}/components/Switch/Switch.tsx (76%) diff --git a/public/app/core/components/Switch/Switch.tsx b/packages/grafana-ui/src/components/Switch/Switch.tsx similarity index 76% rename from public/app/core/components/Switch/Switch.tsx rename to packages/grafana-ui/src/components/Switch/Switch.tsx index d53a3d68878..36475e55acb 100644 --- a/public/app/core/components/Switch/Switch.tsx +++ b/packages/grafana-ui/src/components/Switch/Switch.tsx @@ -4,10 +4,11 @@ import _ from 'lodash'; export interface Props { label: string; checked: boolean; + className?: string; labelClass?: string; switchClass?: string; transparent?: boolean; - onChange: (event) => any; + onChange: (event?: React.SyntheticEvent) => void; } export interface State { @@ -19,20 +20,21 @@ export class Switch extends PureComponent { id: _.uniqueId(), }; - internalOnChange = event => { + internalOnChange = (event: React.FormEvent) => { event.stopPropagation(); - this.props.onChange(event); + + this.props.onChange(); }; render() { - const { labelClass = '', switchClass = '', label, checked, transparent } = this.props; + const { labelClass = '', switchClass = '', label, checked, transparent, className } = this.props; const labelId = `check-${this.state.id}`; const labelClassName = `gf-form-label ${labelClass} ${transparent ? 'gf-form-label--transparent' : ''} pointer`; const switchClassName = `gf-form-switch ${switchClass} ${transparent ? 'gf-form-switch--transparent' : ''}`; return ( -
diff --git a/public/app/features/explore/Logs.tsx b/public/app/features/explore/Logs.tsx index d07b31e2ff1..650cddd035e 100644 --- a/public/app/features/explore/Logs.tsx +++ b/public/app/features/explore/Logs.tsx @@ -4,7 +4,7 @@ import Highlighter from 'react-highlight-words'; import classnames from 'classnames'; import * as rangeUtil from 'app/core/utils/rangeutil'; -import { RawTimeRange } from '@grafana/ui'; +import { RawTimeRange, Switch } from '@grafana/ui'; import { LogsDedupDescription, LogsDedupStrategy, @@ -20,7 +20,6 @@ import { calculateFieldStats, } from 'app/core/logs_model'; import { findHighlightChunksInText } from 'app/core/utils/text'; -import { Switch } from 'app/core/components/Switch/Switch'; import ToggleButtonGroup, { ToggleButton } from 'app/core/components/ToggleButtonGroup/ToggleButtonGroup'; import Graph from './Graph'; diff --git a/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx b/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx index 09e2c3c3772..50e2a344a9b 100644 --- a/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx +++ b/public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx @@ -1,7 +1,6 @@ import React, { PureComponent } from 'react'; -import { FormField, PanelOptionsProps, PanelOptionsGroup } from '@grafana/ui'; +import { FormField, PanelOptionsProps, PanelOptionsGroup, Switch } from '@grafana/ui'; -import { Switch } from 'app/core/components/Switch/Switch'; import { GaugeOptions } from './types'; export default class GaugeOptionsEditor extends PureComponent> { diff --git a/public/app/plugins/panel/graph2/GraphPanelOptions.tsx b/public/app/plugins/panel/graph2/GraphPanelOptions.tsx index 32e68b7a1d4..467a83b68af 100644 --- a/public/app/plugins/panel/graph2/GraphPanelOptions.tsx +++ b/public/app/plugins/panel/graph2/GraphPanelOptions.tsx @@ -2,11 +2,9 @@ import _ from 'lodash'; import React, { PureComponent } from 'react'; -// Components -import { Switch } from 'app/core/components/Switch/Switch'; // Types -import { PanelOptionsProps } from '@grafana/ui'; +import { PanelOptionsProps, Switch } from '@grafana/ui'; import { Options } from './types'; export class GraphPanelOptions extends PureComponent> { From 5032c74b79f8fcfeaeb1631105eb23aee11ad79e Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 24 Jan 2019 15:36:28 +0100 Subject: [PATCH 48/57] Use Switch to control y-axis in series color picker --- .../src/components/ColorPicker/ColorPicker.tsx | 2 +- .../ColorPicker/SeriesColorPickerPopover.tsx | 16 +++++++++++++++- .../src/components/ColorPicker/_ColorPicker.scss | 13 ++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 51abd5e9d58..0b3eb10c7ed 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -63,7 +63,7 @@ export const colorPickerFactory = ( }; return ( - + {(showPopper, hidePopper, popperProps) => { return ( <> diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx index 0bc89c893cd..3fa7a1f4a45 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx @@ -3,6 +3,7 @@ import React, { FunctionComponent } from 'react'; import { ColorPickerPopover } from './ColorPickerPopover'; import { ColorPickerProps } from './ColorPicker'; import { PopperContentProps } from '../Tooltip/PopperController'; +import { Switch } from '../Switch/Switch'; export interface SeriesColorPickerPopoverProps extends ColorPickerProps, PopperContentProps { yaxis?: number; @@ -19,7 +20,20 @@ export const SeriesColorPickerPopover: FunctionComponent
{yaxis && }
+ tabComponent: () => ( + { + if (onToggleAxis) { + onToggleAxis(); + } + }} + /> + ), }, }} /> diff --git a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss index 95cf5141bf3..2d2dbb6896d 100644 --- a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss +++ b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss @@ -130,12 +130,15 @@ $arrowSize: 15px; .ColorPickerPopover__content { width: 336px; + min-height: 184px; padding: 24px; } .ColorPickerPopover__tabs { display: flex; width: 100%; + border-radius: 3px 3px 0 0; + overflow: hidden; } .ColorPickerPopover__tab { @@ -143,13 +146,21 @@ $arrowSize: 15px; text-align: center; padding: 8px 0; background: #dde4ed; - border-radius: 3px; } .ColorPickerPopover__tab--active { background: white; } +.ColorPicker__axisSwitch { + width: 100%; +} + +.ColorPicker__axisSwitchLabel { + display: flex; + flex-grow: 1; +} + .sp-replacer { background: inherit; border: none; From f01441f4a794a42e5494c5f90ba810ffa3dc5918 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 25 Jan 2019 12:03:42 +0100 Subject: [PATCH 49/57] Stories cleanup --- .../ColorPicker/ColorPicker.story.tsx | 50 ++------ .../ColorPicker/ColorPickerPopover.story.tsx | 70 +++++------ .../ColorPicker/NamedColorsPalette.story.tsx | 116 +++++++----------- .../ColorPicker/SpectrumPalette.story.tsx | 73 +++-------- .../src/utils/storybook/UseState.tsx | 38 ++++++ .../src/utils/storybook/themeKnob.ts | 14 +++ .../src/utils/storybook/withCenteredStory.tsx | 19 +++ 7 files changed, 176 insertions(+), 204 deletions(-) create mode 100644 packages/grafana-ui/src/utils/storybook/UseState.tsx create mode 100644 packages/grafana-ui/src/utils/storybook/themeKnob.ts create mode 100644 packages/grafana-ui/src/utils/storybook/withCenteredStory.tsx diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx index d4a0734fd2f..19ae2fda978 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx @@ -1,48 +1,24 @@ -import React, { FunctionComponent } from 'react'; +import React from 'react'; import { storiesOf } from '@storybook/react'; -import { SeriesColorPicker } from './ColorPicker'; -import { ColorPicker } from './ColorPicker'; -import { UseState } from './NamedColorsPalette.story'; -import { withKnobs, select, boolean } from '@storybook/addon-knobs'; +import { withKnobs, boolean } from '@storybook/addon-knobs'; +import { SeriesColorPicker, ColorPicker } from './ColorPicker'; import { action } from '@storybook/addon-actions'; -import { GrafanaTheme } from '../../types'; +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; +import { UseState } from '../../utils/storybook/UseState'; +import { getThemeKnob } from '../../utils/storybook/themeKnob'; -// TODO: extract to decorators -export const CenteredStory: FunctionComponent<{}> = ({ children }) => { - return ( -
- {children} -
- ); -}; - -const getColorPickerKnobs = (defaultTheme: GrafanaTheme = GrafanaTheme.Light, enableNamedColors?: boolean) => { +const getColorPickerKnobs = () => { return { - selectedTheme: select( - 'Theme', - { - Default: '', - Light: GrafanaTheme.Light, - Dark: GrafanaTheme.Dark, - }, - defaultTheme - ), - enableNamedColors: boolean('Enable named colors', !!enableNamedColors), + selectedTheme: getThemeKnob(), + enableNamedColors: boolean('Enable named colors', false), }; }; -const ColorPickerStories = storiesOf('UI/ColorPicker', module); -ColorPickerStories.addDecorator(story => {story()}); -ColorPickerStories.addDecorator(withKnobs); +const ColorPickerStories = storiesOf('UI/ColorPicker/Pickers', module); -ColorPickerStories.add('Color picker', () => { +ColorPickerStories.addDecorator(withCenteredStory).addDecorator(withKnobs); + +ColorPickerStories.add('default', () => { const { selectedTheme, enableNamedColors } = getColorPickerKnobs(); return ( diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx index a74cbaec889..dc51819a413 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx @@ -1,44 +1,40 @@ -import React, { FunctionComponent } from 'react'; +import React from 'react'; import { storiesOf } from '@storybook/react'; import { ColorPickerPopover } from './ColorPickerPopover'; -import { withKnobs, select } from '@storybook/addon-knobs'; -import { GrafanaTheme } from '../../types'; +import { withKnobs } from '@storybook/addon-knobs'; + +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; +import { getThemeKnob } from '../../utils/storybook/themeKnob'; +import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; + +const ColorPickerPopoverStories = storiesOf('UI/ColorPicker/Popovers', module); + +ColorPickerPopoverStories.addDecorator(withCenteredStory).addDecorator(withKnobs); + +ColorPickerPopoverStories.add('default', () => { + const selectedTheme = getThemeKnob(); -const CenteredStory: FunctionComponent<{}> = ({ children }) => { return ( -
{ + console.log(color); }} - > - {children} -
+ theme={selectedTheme || undefined} + /> ); -}; +}); -storiesOf('UI/ColorPickerPopover', module) - .addDecorator(story => {story()}) - .addDecorator(withKnobs) - .add('default', () => { - const selectedTheme = select( - 'Theme', - { - Default: '', - Light: GrafanaTheme.Light, - Dark: GrafanaTheme.Dark, - }, - GrafanaTheme.Light - ); - return ( - { - console.log(color); - }} - theme={selectedTheme || undefined} - /> - ); - }); +ColorPickerPopoverStories.add('SeriesColorPickerPopover', () => { + const selectedTheme = getThemeKnob(); + + return ( + { + console.log(color); + }} + theme={selectedTheme || undefined} + /> + ); +}); diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx index d6ac287c7fc..a530318d6c9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx @@ -1,80 +1,48 @@ -import React, { FunctionComponent } from 'react'; +import React from 'react'; import { storiesOf } from '@storybook/react'; import { NamedColorsPalette } from './NamedColorsPalette'; -import { getColorName } from '../../utils/namedColorsPalette'; +import { getColorName, BasicGreen, BasicBlue, LightBlue } from '../../utils/namedColorsPalette'; import { withKnobs, select } from '@storybook/addon-knobs'; +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; +import { UseState } from '../../utils/storybook/UseState'; -const CenteredStory: FunctionComponent<{}> = ({ children }) => { - return ( -
- {children} -
+const NamedColorsPaletteStories = storiesOf('UI/ColorPicker/Palettes/NamedColorsPalette', module); + +NamedColorsPaletteStories.addDecorator(withKnobs).addDecorator(withCenteredStory); + +NamedColorsPaletteStories.add('Named colors swatch - support for named colors', () => { + const selectedColor = select( + 'Selected color', + { + Green: 'green', + Red: 'red', + 'Light blue': 'light-blue', + }, + 'red' ); -}; -interface StateHolderProps { - initialState: T; - children: (currentState: T, updateState: (nextState: T) => void) => JSX.Element; -} - -export class UseState extends React.Component, { value: T }> { - constructor(props: StateHolderProps) { - super(props); - this.state = { - value: props.initialState, - }; - } - - static getDerivedStateFromProps(props: StateHolderProps<{}>, state: { value: {} }) { - return { - value: props.initialState, - ...state, - }; - } - - handleStateUpdate = (nextState: T) => { - this.setState({ value: nextState }); - }; - render() { - return this.props.children(this.state.value, this.handleStateUpdate); - } -} - -storiesOf('UI/NamedColorsPalette', module) - .addDecorator(withKnobs) - .addDecorator(story => {story()}) - .add('Named colors swatch - support for named colors', () => { - const selectedColor = select( - 'Selected color', - { - Green: 'green', - Red: 'red', - 'Light blue': 'light-blue', - }, - 'green' - ); - - return ( - - {(selectedColor, updateSelectedColor) => { - return ; - }} - - ); - }) - .add('Named colors swatch - support for hex values', () => { - return ( - - {(selectedColor, updateSelectedColor) => { - return ; - }} - - ); - }); + return ( + + {(selectedColor, updateSelectedColor) => { + return ; + }} + + ); +}).add('Named colors swatch - support for hex values', () => { + const selectedColor = select( + 'Selected color', + { + Green: BasicGreen.variants.dark, + Red: BasicBlue.variants.dark, + 'Light blue': LightBlue.variants.dark, + }, + 'red' + ); + return ( + + {(selectedColor, updateSelectedColor) => { + return ; + }} + + ); +}); diff --git a/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.story.tsx b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.story.tsx index 0425f12d6ec..407564cdfb2 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SpectrumPalette.story.tsx @@ -1,62 +1,23 @@ -import React, { FunctionComponent } from 'react'; +import React from 'react'; import { storiesOf } from '@storybook/react'; +import { withKnobs } from '@storybook/addon-knobs'; + import SpectrumPalette from './SpectrumPalette'; -import { withKnobs, select } from '@storybook/addon-knobs'; -import { GrafanaTheme } from '../../types'; +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; +import { UseState } from '../../utils/storybook/UseState'; +import { getThemeKnob } from '../../utils/storybook/themeKnob'; -const CenteredStory: FunctionComponent<{}> = ({ children }) => { +const SpectrumPaletteStories = storiesOf('UI/ColorPicker/Palettes/SpectrumPalette', module); + +SpectrumPaletteStories.addDecorator(withCenteredStory).addDecorator(withKnobs); + +SpectrumPaletteStories.add('Named colors swatch - support for named colors', () => { + const selectedTheme = getThemeKnob(); return ( -
+ {(selectedColor, updateSelectedColor) => { + return ; }} - > - {children} -
+
); -}; - -interface StateHolderProps { - initialState: T; - children: (currentState: T, updateState: (nextState: T) => void) => JSX.Element; -} - -export class UseState extends React.Component, { value: T }> { - constructor(props: StateHolderProps) { - super(props); - this.state = { - value: props.initialState, - }; - } - - handleStateUpdate = (nextState: T) => { - this.setState({ value: nextState }); - }; - render() { - return this.props.children(this.state.value, this.handleStateUpdate); - } -} - -storiesOf('UI/SpectrumPalette', module) - .addDecorator(story => {story()}) - .addDecorator(withKnobs) - .add('Named colors swatch - support for named colors', () => { - const selectedTheme = select( - 'Theme', - { - Light: GrafanaTheme.Light, - Dark: GrafanaTheme.Dark, - }, - GrafanaTheme.Light - ); - return ( - - {(selectedColor, updateSelectedColor) => { - return ; - }} - - ); - }); +}); diff --git a/packages/grafana-ui/src/utils/storybook/UseState.tsx b/packages/grafana-ui/src/utils/storybook/UseState.tsx new file mode 100644 index 00000000000..cc263b9a456 --- /dev/null +++ b/packages/grafana-ui/src/utils/storybook/UseState.tsx @@ -0,0 +1,38 @@ +import React from 'react'; + +interface StateHolderProps { + initialState: T; + children: (currentState: T, updateState: (nextState: T) => void) => JSX.Element; +} + +export class UseState extends React.Component, { value: T; initialState: T }> { + constructor(props: StateHolderProps) { + super(props); + this.state = { + value: props.initialState, + initialState: props.initialState, // To enable control from knobs + }; + } + // @ts-ignore + static getDerivedStateFromProps(props: StateHolderProps<{}>, state: { value: any; initialState: any }) { + if (props.initialState !== state.initialState) { + return { + initialState: props.initialState, + value: props.initialState, + }; + } + return { + ...state, + value: state.value, + }; + } + + handleStateUpdate = (nextState: T) => { + console.log(nextState); + this.setState({ value: nextState }); + }; + + render() { + return this.props.children(this.state.value, this.handleStateUpdate); + } +} diff --git a/packages/grafana-ui/src/utils/storybook/themeKnob.ts b/packages/grafana-ui/src/utils/storybook/themeKnob.ts new file mode 100644 index 00000000000..a3733462bea --- /dev/null +++ b/packages/grafana-ui/src/utils/storybook/themeKnob.ts @@ -0,0 +1,14 @@ +import { select } from '@storybook/addon-knobs'; +import { GrafanaTheme } from '../../types'; + +export const getThemeKnob = (defaultTheme: GrafanaTheme = GrafanaTheme.Dark) => { + return select( + 'Theme', + { + Default: defaultTheme, + Light: GrafanaTheme.Light, + Dark: GrafanaTheme.Dark, + }, + defaultTheme + ); +}; diff --git a/packages/grafana-ui/src/utils/storybook/withCenteredStory.tsx b/packages/grafana-ui/src/utils/storybook/withCenteredStory.tsx new file mode 100644 index 00000000000..af564616f5a --- /dev/null +++ b/packages/grafana-ui/src/utils/storybook/withCenteredStory.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { RenderFunction } from '@storybook/react'; + +const CenteredStory: React.FunctionComponent<{}> = ({ children }) => { + return ( +
+ {children} +
+ ); +}; + +export const withCenteredStory = (story: RenderFunction) => {story()}; From e75b0a3c70211e790c24a7f4e776b866b728688e Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 25 Jan 2019 12:13:36 +0100 Subject: [PATCH 50/57] Fix light theme issues with named colors disabled --- .../src/components/ColorPicker/ColorPickerPopover.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index 03b6086b8dc..fc2d70e3f8f 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -37,13 +37,13 @@ export class ColorPickerPopover extends React }; handleChange = (color: any) => { - const { onColorChange, onChange, enableNamedColors } = this.props; + const { onColorChange, onChange, enableNamedColors, theme } = this.props; const changeHandler = onColorChange || onChange; if (enableNamedColors) { return changeHandler(color); } - changeHandler(getColorFromHexRgbOrName(color)); + changeHandler(getColorFromHexRgbOrName(color, theme)); }; handleTabChange = (tab: PickerType | keyof T, updatePopperPosition?: () => void) => { From 3c60f98d1390c3e3805c939ca5c33fc86c5de14d Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 25 Jan 2019 13:24:20 +0100 Subject: [PATCH 51/57] Implemented tests for ColorPickerPopover and NamedColorsPalette --- .../ColorPicker/ColorPickerPopover.test.tsx | 72 +++++++++++++++++++ .../ColorPicker/NamedColorsGroup.tsx | 4 +- .../ColorPicker/NamedColorsPalette.test.tsx | 33 +++++++++ 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx create mode 100644 packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.test.tsx diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx new file mode 100644 index 00000000000..7438a206ee9 --- /dev/null +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { mount, ReactWrapper } from 'enzyme'; +import { ColorPickerPopover } from './ColorPickerPopover'; +import { ColorsPalette, BasicGreen, BasicBlue } from '../../utils/namedColorsPalette'; +import { ColorSwatch } from './NamedColorsGroup'; +import { flatten } from 'lodash'; +import { GrafanaTheme } from '../../types'; + +const allColors = flatten(Array.from(ColorsPalette.values())); + +describe('ColorPickerPopover', () => { + describe('rendering', () => { + it('should render provided color as selected if color provided by name', () => { + const wrapper = mount( {}} />); + const selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name); + const notSelectedSwatches = wrapper.find(ColorSwatch).filterWhere(node => node.prop('isSelected') === false); + + expect(selectedSwatch.length).toBe(1); + expect(notSelectedSwatches.length).toBe(allColors.length - 1); + expect(selectedSwatch.prop('isSelected')).toBe(true); + }); + + it('should render provided color as selected if color provided by hex', () => { + const wrapper = mount( {}} />); + const selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name); + const notSelectedSwatches = wrapper.find(ColorSwatch).filterWhere(node => node.prop('isSelected') === false); + + expect(selectedSwatch.length).toBe(1); + expect(notSelectedSwatches.length).toBe(allColors.length - 1); + expect(selectedSwatch.prop('isSelected')).toBe(true); + }); + }); + + describe('named colors support', () => { + const onChangeSpy = jest.fn(); + let wrapper: ReactWrapper; + + afterEach(() => { + wrapper.unmount(); + onChangeSpy.mockClear(); + }); + + it('should pass hex color value to onChange prop by default', () => { + wrapper = mount( + + ); + const basicBlueSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicBlue.name); + + basicBlueSwatch.simulate('click'); + + expect(onChangeSpy).toBeCalledTimes(1); + expect(onChangeSpy).toBeCalledWith(BasicBlue.variants.light); + }); + + it('should pass color name to onChange prop when named colors enabled', () => { + wrapper = mount( + + ); + const basicBlueSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicBlue.name); + + basicBlueSwatch.simulate('click'); + + expect(onChangeSpy).toBeCalledTimes(1); + expect(onChangeSpy).toBeCalledWith(BasicBlue.name); + }); + }); +}); diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx index f2bcdeec66d..7aa6aeb25dd 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx @@ -18,7 +18,7 @@ interface ColorSwatchProps extends Themeable, React.DOMAttributes = ({ +export const ColorSwatch: FunctionComponent = ({ color, label, variant = ColorSwatchVariant.Small, @@ -73,6 +73,7 @@ const NamedColorsGroup: FunctionComponent = ({
{primaryColor && ( = ({ !color.isPrimary && (
onColorSelect(color)} diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.test.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.test.tsx new file mode 100644 index 00000000000..a0015e2a557 --- /dev/null +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.test.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { mount, ReactWrapper } from 'enzyme'; +import { NamedColorsPalette } from './NamedColorsPalette'; +import { ColorSwatch } from './NamedColorsGroup'; +import { BasicGreen } from '../../utils'; +import { GrafanaTheme } from '../../types'; + +describe('NamedColorsPalette', () => { + describe('theme support for named colors', () => { + let wrapper: ReactWrapper, selectedSwatch; + + afterEach(() => { + wrapper.unmount(); + }); + + it('should render provided color variant specific for theme', () => { + wrapper = mount( {}} />); + selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name); + expect(selectedSwatch.prop('color')).toBe(BasicGreen.variants.dark); + + wrapper.unmount(); + wrapper = mount( {}} />); + selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name); + expect(selectedSwatch.prop('color')).toBe(BasicGreen.variants.light); + }); + + it('should render dar variant of provided color when theme not provided', () => { + wrapper = mount( {}} />); + selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name); + expect(selectedSwatch.prop('color')).toBe(BasicGreen.variants.dark); + }); + }); +}); From 5f41c5ca5e9769969c94fd7796729a3d4b67dec0 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 25 Jan 2019 13:30:02 +0100 Subject: [PATCH 52/57] Rename deprecation warning helper --- .../grafana-ui/src/components/ColorPicker/ColorPicker.tsx | 2 +- .../src/components/ColorPicker/ColorPickerPopover.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 0b3eb10c7ed..b6cf176a24b 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -22,7 +22,7 @@ export interface ColorPickerProps extends Themeable { children?: JSX.Element; } -export const handleColorPickerPropsDeprecation = (componentName: string, props: ColorPickerProps) => { +export const warnAboutColorPickerPropsDeprecation = (componentName: string, props: ColorPickerProps) => { const { onColorChange } = props; if (onColorChange) { propDeprecationWarning(componentName, 'onColorChange', 'onChange'); diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index fc2d70e3f8f..06b90e6ebc4 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { NamedColorsPalette } from './NamedColorsPalette'; import { getColorName, getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; -import { ColorPickerProps, handleColorPickerPropsDeprecation } from './ColorPicker'; +import { ColorPickerProps, warnAboutColorPickerPropsDeprecation } from './ColorPicker'; import { GrafanaTheme } from '../../types'; import { PopperContentProps } from '../Tooltip/PopperController'; import SpectrumPalette from './SpectrumPalette'; @@ -28,7 +28,7 @@ export class ColorPickerPopover extends React this.state = { activePicker: 'palette', }; - handleColorPickerPropsDeprecation('ColorPickerPopover', props); + warnAboutColorPickerPropsDeprecation('ColorPickerPopover', props); } getTabClassName = (tabName: PickerType | keyof T) => { From 07b9735263c08fee944b1122c3d8a533c17d2c8e Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 25 Jan 2019 14:07:22 +0100 Subject: [PATCH 53/57] Do not update color picker popover position on tab change Position update caused tabs to move up and down. This led to confusing user experience, as tabs position was dependent on tab's content. --- .../ColorPicker/ColorPickerPopover.tsx | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index 06b90e6ebc4..d2937a1caba 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -46,13 +46,8 @@ export class ColorPickerPopover extends React changeHandler(getColorFromHexRgbOrName(color, theme)); }; - handleTabChange = (tab: PickerType | keyof T, updatePopperPosition?: () => void) => { - return () => - this.setState({ activePicker: tab }, () => { - if (updatePopperPosition) { - updatePopperPosition(); - } - }); + handleTabChange = (tab: PickerType | keyof T) => { + return () => this.setState({ activePicker: tab }); }; renderPicker = () => { @@ -82,7 +77,7 @@ export class ColorPickerPopover extends React }); }; - renderCustomPickerTabs = (updatePopperPosition?: () => void) => { + renderCustomPickerTabs = () => { const { customPickers } = this.props; if (!customPickers) { @@ -95,7 +90,7 @@ export class ColorPickerPopover extends React return (
{customPickers[key].name} @@ -107,7 +102,7 @@ export class ColorPickerPopover extends React }; render() { - const { theme, updatePopperPosition } = this.props; + const { theme } = this.props; const colorPickerTheme = theme || GrafanaTheme.Dark; return ( @@ -115,17 +110,17 @@ export class ColorPickerPopover extends React
Colors
Custom
- {this.renderCustomPickerTabs(updatePopperPosition)} + {this.renderCustomPickerTabs()}
{this.renderPicker()}
From 778ed912b888220ceb4b6888688812a33364d089 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 28 Jan 2019 09:25:35 +0100 Subject: [PATCH 54/57] Add cursor pointer to color swatches --- .../grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx index 7aa6aeb25dd..91c4f21642a 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx @@ -36,7 +36,6 @@ export const ColorSwatch: FunctionComponent = ({ background: `${color}`, marginRight: isSmall ? '0px' : '8px', boxShadow: isSelected ? `inset 0 0 0 2px ${color}, inset 0 0 0 4px ${selectedSwatchBorder}` : 'none', - cursor: isSelected ? 'default' : 'pointer', }; return ( @@ -44,6 +43,7 @@ export const ColorSwatch: FunctionComponent = ({ style={{ display: 'flex', alignItems: 'center', + cursor: 'pointer', }} {...otherProps} > From b28e9b01fd1c69dad7a45de94c7d497d4aa5bb3e Mon Sep 17 00:00:00 2001 From: ijin08 Date: Mon, 28 Jan 2019 11:52:36 +0100 Subject: [PATCH 55/57] updated the color palette --- .../src/utils/namedColorsPalette.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/grafana-ui/src/utils/namedColorsPalette.ts b/packages/grafana-ui/src/utils/namedColorsPalette.ts index 4112927ffd1..893b6cb1926 100644 --- a/packages/grafana-ui/src/utils/namedColorsPalette.ts +++ b/packages/grafana-ui/src/utils/namedColorsPalette.ts @@ -64,11 +64,11 @@ export const buildColorDefinition = ( isPrimary: !!isPrimary, }); -export const BasicGreen = buildColorDefinition('green', 'green', ['#5AA64B', '#77BF69'], true); -export const DarkGreen = buildColorDefinition('green', 'dark-green', ['#1E6910', '#388729']); -export const SemiDarkGreen = buildColorDefinition('green', 'semi-dark-green', ['#388729', '#5AA64B']); -export const LightGreen = buildColorDefinition('green', 'light-green', ['#77BF69', '#99D98D']); -export const SuperLightGreen = buildColorDefinition('green', 'super-light-green', ['#99D98D', '#CAF2C2']); +export const BasicGreen = buildColorDefinition('green', 'green', ['#56A64B', '#73BF69'], true); +export const DarkGreen = buildColorDefinition('green', 'dark-green', ['#19730E', '#37872D']); +export const SemiDarkGreen = buildColorDefinition('green', 'semi-dark-green', ['#37872D', '#56A64B']); +export const LightGreen = buildColorDefinition('green', 'light-green', ['#73BF69', '#96D98D']); +export const SuperLightGreen = buildColorDefinition('green', 'super-light-green', ['#96D98D', '#C8F2C2']); export const BasicYellow = buildColorDefinition('yellow', 'yellow', ['#F2CC0C', '#FADE2A'], true); export const DarkYellow = buildColorDefinition('yellow', 'dark-yellow', ['#CC9D00', '#E0B400']); @@ -76,15 +76,15 @@ export const SemiDarkYellow = buildColorDefinition('yellow', 'semi-dark-yellow', export const LightYellow = buildColorDefinition('yellow', 'light-yellow', ['#FADE2A', '#FFEE52']); export const SuperLightYellow = buildColorDefinition('yellow', 'super-light-yellow', ['#FFEE52', '#FFF899']); -export const BasicRed = buildColorDefinition('red', 'red', ['#DE314D', '#F24965'], true); -export const DarkRed = buildColorDefinition('red', 'dark-red', ['#AB031F', '#C41834']); -export const SemiDarkRed = buildColorDefinition('red', 'semi-dark-red', ['#C41834', '#DE314D']); -export const LightRed = buildColorDefinition('red', 'light-red', ['#F24965', '#FF7389']); -export const SuperLightRed = buildColorDefinition('red', 'super-light-red', ['#FF7389', '#FFA6B4']); +export const BasicRed = buildColorDefinition('red', 'red', ['#E02F44', '#F2495C'], true); +export const DarkRed = buildColorDefinition('red', 'dark-red', ['#AD0317', '#C4162A']); +export const SemiDarkRed = buildColorDefinition('red', 'semi-dark-red', ['#C4162A', '#E02F44']); +export const LightRed = buildColorDefinition('red', 'light-red', ['#F2495C', '#FF7383']); +export const SuperLightRed = buildColorDefinition('red', 'super-light-red', ['#FF7383', '#FFA6B0']); export const BasicBlue = buildColorDefinition('blue', 'blue', ['#3274D9', '#5794F2'], true); -export const DarkBlue = buildColorDefinition('blue', 'dark-blue', ['#144796', '#1857B8']); -export const SemiDarkBlue = buildColorDefinition('blue', 'semi-dark-blue', ['#1857B8', '#3274D9']); +export const DarkBlue = buildColorDefinition('blue', 'dark-blue', ['#1250B0', '#1F60C4']); +export const SemiDarkBlue = buildColorDefinition('blue', 'semi-dark-blue', ['#1F60C4', '#3274D9']); export const LightBlue = buildColorDefinition('blue', 'light-blue', ['#5794F2', '#8AB8FF']); export const SuperLightBlue = buildColorDefinition('blue', 'super-light-blue', ['#8AB8FF', '#C0D8FF']); @@ -95,8 +95,8 @@ export const LightOrange = buildColorDefinition('orange', 'light-orange', ['#FF9 export const SuperLightOrange = buildColorDefinition('orange', 'super-light-orange', ['#FFB357', '#FFCB7D']); export const BasicPurple = buildColorDefinition('purple', 'purple', ['#A352CC', '#B877D9'], true); -export const DarkPurple = buildColorDefinition('purple', 'dark-purple', ['#732699', '#8936B2']); -export const SemiDarkPurple = buildColorDefinition('purple', 'semi-dark-purple', ['#8936B2', '#A352CC']); +export const DarkPurple = buildColorDefinition('purple', 'dark-purple', ['#7C2EA3', '#8F3BB8']); +export const SemiDarkPurple = buildColorDefinition('purple', 'semi-dark-purple', ['#8F3BB8', '#A352CC']); export const LightPurple = buildColorDefinition('purple', 'light-purple', ['#B877D9', '#CA95E5']); export const SuperLightPurple = buildColorDefinition('purple', 'super-light-purple', ['#CA95E5', '#DEB6F2']); From 053d4060e895ba3318274afa7cfae5f40da47a79 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 28 Jan 2019 15:48:54 +0100 Subject: [PATCH 56/57] Fix thresholds default colors not being applied --- .../src/components/ColorPicker/_ColorPicker.scss | 1 + public/app/plugins/panel/graph/threshold_manager.ts | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss index 2d2dbb6896d..46eed5f7ff1 100644 --- a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss +++ b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss @@ -199,6 +199,7 @@ $arrowSize: 15px; margin: 0; float: left; z-index: 0; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==); } .sp-preview-inner, diff --git a/public/app/plugins/panel/graph/threshold_manager.ts b/public/app/plugins/panel/graph/threshold_manager.ts index ef9feb80a5b..f0bc7d62499 100644 --- a/public/app/plugins/panel/graph/threshold_manager.ts +++ b/public/app/plugins/panel/graph/threshold_manager.ts @@ -198,6 +198,7 @@ export class ThresholdManager { } let fillColor, lineColor; + switch (threshold.colorMode) { case 'critical': { fillColor = 'rgba(234, 112, 112, 0.12)'; @@ -215,6 +216,12 @@ export class ThresholdManager { break; } case 'custom': { + if (!threshold.fillColor) { + threshold.fillColor = 'rgba(255, 255, 255, 1)'; + } + if (!threshold.lineColor) { + threshold.lineColor = 'rgba(255, 255, 255, 0)'; + } fillColor = threshold.fillColor; lineColor = threshold.lineColor; break; From 9a08a994f4fc080ea40e26cdd58e38c8c8c562c7 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 28 Jan 2019 15:49:59 +0100 Subject: [PATCH 57/57] Initialize named colors palete lazily --- .../ColorPicker/ColorPickerPopover.test.tsx | 7 +- .../ColorPicker/NamedColorsPalette.story.tsx | 6 +- .../ColorPicker/NamedColorsPalette.test.tsx | 5 +- .../ColorPicker/NamedColorsPalette.tsx | 5 +- .../src/utils/namedColorsPalette.test.ts | 4 +- .../src/utils/namedColorsPalette.ts | 125 ++++++++++-------- .../panel/table/specs/renderer.test.ts | 4 +- 7 files changed, 94 insertions(+), 62 deletions(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx index 7438a206ee9..28d66e7af86 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx @@ -1,14 +1,17 @@ import React from 'react'; import { mount, ReactWrapper } from 'enzyme'; import { ColorPickerPopover } from './ColorPickerPopover'; -import { ColorsPalette, BasicGreen, BasicBlue } from '../../utils/namedColorsPalette'; +import { getColorDefinitionByName, getNamedColorPalette } from '../../utils/namedColorsPalette'; import { ColorSwatch } from './NamedColorsGroup'; import { flatten } from 'lodash'; import { GrafanaTheme } from '../../types'; -const allColors = flatten(Array.from(ColorsPalette.values())); +const allColors = flatten(Array.from(getNamedColorPalette().values())); describe('ColorPickerPopover', () => { + const BasicGreen = getColorDefinitionByName('green'); + const BasicBlue = getColorDefinitionByName('blue'); + describe('rendering', () => { it('should render provided color as selected if color provided by name', () => { const wrapper = mount( {}} />); diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx index a530318d6c9..af5de3b2a2d 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx @@ -1,11 +1,15 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import { NamedColorsPalette } from './NamedColorsPalette'; -import { getColorName, BasicGreen, BasicBlue, LightBlue } from '../../utils/namedColorsPalette'; +import { getColorName, getColorDefinitionByName } from '../../utils/namedColorsPalette'; import { withKnobs, select } from '@storybook/addon-knobs'; import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; import { UseState } from '../../utils/storybook/UseState'; +const BasicGreen = getColorDefinitionByName('green'); +const BasicBlue = getColorDefinitionByName('blue'); +const LightBlue = getColorDefinitionByName('light-blue'); + const NamedColorsPaletteStories = storiesOf('UI/ColorPicker/Palettes/NamedColorsPalette', module); NamedColorsPaletteStories.addDecorator(withKnobs).addDecorator(withCenteredStory); diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.test.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.test.tsx index a0015e2a557..171d26f5c56 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.test.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.test.tsx @@ -2,10 +2,13 @@ import React from 'react'; import { mount, ReactWrapper } from 'enzyme'; import { NamedColorsPalette } from './NamedColorsPalette'; import { ColorSwatch } from './NamedColorsGroup'; -import { BasicGreen } from '../../utils'; +import { getColorDefinitionByName } from '../../utils'; import { GrafanaTheme } from '../../types'; describe('NamedColorsPalette', () => { + + const BasicGreen = getColorDefinitionByName('green'); + describe('theme support for named colors', () => { let wrapper: ReactWrapper, selectedSwatch; diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx index 8d09ec0cdec..7bdd3c690b9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Color, ColorsPalette } from '../../utils/namedColorsPalette'; +import { Color, getNamedColorPalette } from '../../utils/namedColorsPalette'; import { Themeable } from '../../types/index'; import NamedColorsGroup from './NamedColorsGroup'; @@ -10,7 +10,7 @@ interface NamedColorsPaletteProps extends Themeable { export const NamedColorsPalette = ({ color, onChange, theme }: NamedColorsPaletteProps) => { const swatches: JSX.Element[] = []; - ColorsPalette.forEach((colors, hue) => { + getNamedColorPalette().forEach((colors, hue) => { swatches.push( ); }; - diff --git a/packages/grafana-ui/src/utils/namedColorsPalette.test.ts b/packages/grafana-ui/src/utils/namedColorsPalette.test.ts index dafca2de141..c6a1aaf0dd0 100644 --- a/packages/grafana-ui/src/utils/namedColorsPalette.test.ts +++ b/packages/grafana-ui/src/utils/namedColorsPalette.test.ts @@ -2,12 +2,14 @@ import { getColorName, getColorDefinition, getColorByName, - SemiDarkBlue, getColorFromHexRgbOrName, + getColorDefinitionByName, } from './namedColorsPalette'; import { GrafanaTheme } from '../types/index'; describe('colors', () => { + const SemiDarkBlue = getColorDefinitionByName('semi-dark-blue'); + describe('getColorDefinition', () => { it('returns undefined for unknown hex', () => { expect(getColorDefinition('#ff0000', GrafanaTheme.Light)).toBeUndefined(); diff --git a/packages/grafana-ui/src/utils/namedColorsPalette.ts b/packages/grafana-ui/src/utils/namedColorsPalette.ts index 4112927ffd1..299f5585c70 100644 --- a/packages/grafana-ui/src/utils/namedColorsPalette.ts +++ b/packages/grafana-ui/src/utils/namedColorsPalette.ts @@ -47,9 +47,9 @@ export type ColorDefinition = { variants: ThemeVariants; }; -export const ColorsPalette = new Map(); +let colorsPaletteInstance: Map; -export const buildColorDefinition = ( +const buildColorDefinition = ( hue: Hue, name: Color, [light, dark]: string[], @@ -64,58 +64,12 @@ export const buildColorDefinition = ( isPrimary: !!isPrimary, }); -export const BasicGreen = buildColorDefinition('green', 'green', ['#5AA64B', '#77BF69'], true); -export const DarkGreen = buildColorDefinition('green', 'dark-green', ['#1E6910', '#388729']); -export const SemiDarkGreen = buildColorDefinition('green', 'semi-dark-green', ['#388729', '#5AA64B']); -export const LightGreen = buildColorDefinition('green', 'light-green', ['#77BF69', '#99D98D']); -export const SuperLightGreen = buildColorDefinition('green', 'super-light-green', ['#99D98D', '#CAF2C2']); - -export const BasicYellow = buildColorDefinition('yellow', 'yellow', ['#F2CC0C', '#FADE2A'], true); -export const DarkYellow = buildColorDefinition('yellow', 'dark-yellow', ['#CC9D00', '#E0B400']); -export const SemiDarkYellow = buildColorDefinition('yellow', 'semi-dark-yellow', ['#E0B400', '#F2CC0C']); -export const LightYellow = buildColorDefinition('yellow', 'light-yellow', ['#FADE2A', '#FFEE52']); -export const SuperLightYellow = buildColorDefinition('yellow', 'super-light-yellow', ['#FFEE52', '#FFF899']); - -export const BasicRed = buildColorDefinition('red', 'red', ['#DE314D', '#F24965'], true); -export const DarkRed = buildColorDefinition('red', 'dark-red', ['#AB031F', '#C41834']); -export const SemiDarkRed = buildColorDefinition('red', 'semi-dark-red', ['#C41834', '#DE314D']); -export const LightRed = buildColorDefinition('red', 'light-red', ['#F24965', '#FF7389']); -export const SuperLightRed = buildColorDefinition('red', 'super-light-red', ['#FF7389', '#FFA6B4']); - -export const BasicBlue = buildColorDefinition('blue', 'blue', ['#3274D9', '#5794F2'], true); -export const DarkBlue = buildColorDefinition('blue', 'dark-blue', ['#144796', '#1857B8']); -export const SemiDarkBlue = buildColorDefinition('blue', 'semi-dark-blue', ['#1857B8', '#3274D9']); -export const LightBlue = buildColorDefinition('blue', 'light-blue', ['#5794F2', '#8AB8FF']); -export const SuperLightBlue = buildColorDefinition('blue', 'super-light-blue', ['#8AB8FF', '#C0D8FF']); - -export const BasicOrange = buildColorDefinition('orange', 'orange', ['#FF780A', '#FF9830'], true); -export const DarkOrange = buildColorDefinition('orange', 'dark-orange', ['#E55400', '#FA6400']); -export const SemiDarkOrange = buildColorDefinition('orange', 'semi-dark-orange', ['#FA6400', '#FF780A']); -export const LightOrange = buildColorDefinition('orange', 'light-orange', ['#FF9830', '#FFB357']); -export const SuperLightOrange = buildColorDefinition('orange', 'super-light-orange', ['#FFB357', '#FFCB7D']); - -export const BasicPurple = buildColorDefinition('purple', 'purple', ['#A352CC', '#B877D9'], true); -export const DarkPurple = buildColorDefinition('purple', 'dark-purple', ['#732699', '#8936B2']); -export const SemiDarkPurple = buildColorDefinition('purple', 'semi-dark-purple', ['#8936B2', '#A352CC']); -export const LightPurple = buildColorDefinition('purple', 'light-purple', ['#B877D9', '#CA95E5']); -export const SuperLightPurple = buildColorDefinition('purple', 'super-light-purple', ['#CA95E5', '#DEB6F2']); - -const greens = [BasicGreen, DarkGreen, SemiDarkGreen, LightGreen, SuperLightGreen]; -const yellows = [BasicYellow, DarkYellow, SemiDarkYellow, LightYellow, SuperLightYellow]; -const reds = [BasicRed, DarkRed, SemiDarkRed, LightRed, SuperLightRed]; -const blues = [BasicBlue, DarkBlue, SemiDarkBlue, LightBlue, SuperLightBlue]; -const oranges = [BasicOrange, DarkOrange, SemiDarkOrange, LightOrange, SuperLightOrange]; -const purples = [BasicPurple, DarkPurple, SemiDarkPurple, LightPurple, SuperLightPurple]; - -ColorsPalette.set('green', greens); -ColorsPalette.set('yellow', yellows); -ColorsPalette.set('red', reds); -ColorsPalette.set('blue', blues); -ColorsPalette.set('orange', oranges); -ColorsPalette.set('purple', purples); +export const getColorDefinitionByName = (name: Color): ColorDefinition => { + return flatten(Array.from(getNamedColorPalette().values())).filter(definition => definition.name === name)[0]; +}; export const getColorDefinition = (hex: string, theme: GrafanaTheme): ColorDefinition | undefined => { - return flatten(Array.from(ColorsPalette.values())).filter(definition => definition.variants[theme] === hex)[0]; + return flatten(Array.from(getNamedColorPalette().values())).filter(definition => definition.variants[theme] === hex)[0]; }; const isHex = (color: string) => { @@ -140,7 +94,7 @@ export const getColorName = (color?: string, theme?: GrafanaTheme): Color | unde }; export const getColorByName = (colorName: string) => { - const definition = flatten(Array.from(ColorsPalette.values())).filter(definition => definition.name === colorName); + const definition = flatten(Array.from(getNamedColorPalette().values())).filter(definition => definition.name === colorName); return definition.length > 0 ? definition[0] : undefined; }; @@ -161,3 +115,68 @@ export const getColorFromHexRgbOrName = (color: string, theme?: GrafanaTheme): s export const getColorForTheme = (color: ColorDefinition, theme?: GrafanaTheme) => { return theme ? color.variants[theme] : color.variants.dark; }; + +const buildNamedColorsPalette = () => { + const palette = new Map(); + + const BasicGreen = buildColorDefinition('green', 'green', ['#5AA64B', '#77BF69'], true); + const DarkGreen = buildColorDefinition('green', 'dark-green', ['#1E6910', '#388729']); + const SemiDarkGreen = buildColorDefinition('green', 'semi-dark-green', ['#388729', '#5AA64B']); + const LightGreen = buildColorDefinition('green', 'light-green', ['#77BF69', '#99D98D']); + const SuperLightGreen = buildColorDefinition('green', 'super-light-green', ['#99D98D', '#CAF2C2']); + + const BasicYellow = buildColorDefinition('yellow', 'yellow', ['#F2CC0C', '#FADE2A'], true); + const DarkYellow = buildColorDefinition('yellow', 'dark-yellow', ['#CC9D00', '#E0B400']); + const SemiDarkYellow = buildColorDefinition('yellow', 'semi-dark-yellow', ['#E0B400', '#F2CC0C']); + const LightYellow = buildColorDefinition('yellow', 'light-yellow', ['#FADE2A', '#FFEE52']); + const SuperLightYellow = buildColorDefinition('yellow', 'super-light-yellow', ['#FFEE52', '#FFF899']); + + const BasicRed = buildColorDefinition('red', 'red', ['#DE314D', '#F24965'], true); + const DarkRed = buildColorDefinition('red', 'dark-red', ['#AB031F', '#C41834']); + const SemiDarkRed = buildColorDefinition('red', 'semi-dark-red', ['#C41834', '#DE314D']); + const LightRed = buildColorDefinition('red', 'light-red', ['#F24965', '#FF7389']); + const SuperLightRed = buildColorDefinition('red', 'super-light-red', ['#FF7389', '#FFA6B4']); + + const BasicBlue = buildColorDefinition('blue', 'blue', ['#3274D9', '#5794F2'], true); + const DarkBlue = buildColorDefinition('blue', 'dark-blue', ['#144796', '#1857B8']); + const SemiDarkBlue = buildColorDefinition('blue', 'semi-dark-blue', ['#1857B8', '#3274D9']); + const LightBlue = buildColorDefinition('blue', 'light-blue', ['#5794F2', '#8AB8FF']); + const SuperLightBlue = buildColorDefinition('blue', 'super-light-blue', ['#8AB8FF', '#C0D8FF']); + + const BasicOrange = buildColorDefinition('orange', 'orange', ['#FF780A', '#FF9830'], true); + const DarkOrange = buildColorDefinition('orange', 'dark-orange', ['#E55400', '#FA6400']); + const SemiDarkOrange = buildColorDefinition('orange', 'semi-dark-orange', ['#FA6400', '#FF780A']); + const LightOrange = buildColorDefinition('orange', 'light-orange', ['#FF9830', '#FFB357']); + const SuperLightOrange = buildColorDefinition('orange', 'super-light-orange', ['#FFB357', '#FFCB7D']); + + const BasicPurple = buildColorDefinition('purple', 'purple', ['#A352CC', '#B877D9'], true); + const DarkPurple = buildColorDefinition('purple', 'dark-purple', ['#732699', '#8936B2']); + const SemiDarkPurple = buildColorDefinition('purple', 'semi-dark-purple', ['#8936B2', '#A352CC']); + const LightPurple = buildColorDefinition('purple', 'light-purple', ['#B877D9', '#CA95E5']); + const SuperLightPurple = buildColorDefinition('purple', 'super-light-purple', ['#CA95E5', '#DEB6F2']); + + const greens = [BasicGreen, DarkGreen, SemiDarkGreen, LightGreen, SuperLightGreen]; + const yellows = [BasicYellow, DarkYellow, SemiDarkYellow, LightYellow, SuperLightYellow]; + const reds = [BasicRed, DarkRed, SemiDarkRed, LightRed, SuperLightRed]; + const blues = [BasicBlue, DarkBlue, SemiDarkBlue, LightBlue, SuperLightBlue]; + const oranges = [BasicOrange, DarkOrange, SemiDarkOrange, LightOrange, SuperLightOrange]; + const purples = [BasicPurple, DarkPurple, SemiDarkPurple, LightPurple, SuperLightPurple]; + + palette.set('green', greens); + palette.set('yellow', yellows); + palette.set('red', reds); + palette.set('blue', blues); + palette.set('orange', oranges); + palette.set('purple', purples); + + return palette; +}; + +export const getNamedColorPalette = () => { + if (colorsPaletteInstance) { + return colorsPaletteInstance; + } + + colorsPaletteInstance = buildNamedColorsPalette(); + return colorsPaletteInstance; +}; diff --git a/public/app/plugins/panel/table/specs/renderer.test.ts b/public/app/plugins/panel/table/specs/renderer.test.ts index a6e03417085..ca5a036d98f 100644 --- a/public/app/plugins/panel/table/specs/renderer.test.ts +++ b/public/app/plugins/panel/table/specs/renderer.test.ts @@ -1,9 +1,11 @@ import _ from 'lodash'; import TableModel from 'app/core/table_model'; import { TableRenderer } from '../renderer'; -import { SemiDarkOrange } from '@grafana/ui'; +import { getColorDefinitionByName } from '@grafana/ui'; describe('when rendering table', () => { + const SemiDarkOrange = getColorDefinitionByName('semi-dark-orange'); + describe('given 13 columns', () => { const table = new TableModel(); table.columns = [