Skip to content

Commit

Permalink
feat(data-warehouse): Select color of series on a query visualization (
Browse files Browse the repository at this point in the history
…#25909)

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
rossgray and github-actions[bot] authored Oct 31, 2024
1 parent 99c8ae7 commit 44f28c3
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 77 deletions.
5 changes: 5 additions & 0 deletions frontend/src/lib/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ export function getSeriesColor(index: number = 0): string {
return getColorVar(`data-${dataColorVars[adjustedIndex]}`)
}

/** Returns all color options for series */
export function getSeriesColorPalette(): string[] {
return dataColorVars.map((colorVar) => getColorVar(`data-${colorVar}`))
}

/** Return the background color for the given series index. */
export function getSeriesBackgroundColor(index: number): string {
return `${getSeriesColor(index)}30`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export const LineGraph = (): JSX.Element => {
const data: ChartData = {
labels: xData.data,
datasets: yData.map(({ data, settings }, index) => {
const color = getSeriesColor(index)
const color = settings?.display?.color ?? getSeriesColor(index)
const backgroundColor = isAreaChart ? hexToRGBA(color, 0.5) : color

const graphType = getGraphType(visualizationType, settings)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { LemonButton, Popover } from '@posthog/lemon-ui'
import { useValues } from 'kea'
import { SeriesGlyph } from 'lib/components/SeriesGlyph'
import { hexToRGBA, lightenDarkenColor, RGBToHex, RGBToRGBA } from 'lib/utils'
import { useState } from 'react'
import { ColorResult, TwitterPicker } from 'react-color'

import { themeLogic } from '~/layout/navigation-3000/themeLogic'

const DEFAULT_PICKER_COLORS = [
'#FFADAD', // Current default
'#E8A598',
'#FFD6A5',
'#FFCFD2',
'#FDFFB6',
'#C1FBA4',
'#9BF6FF',
'#A0C4FF',
'#BDB2FF',
'#FFC6FF',
]

export const ColorPickerButton = ({
color,
onColorSelect: propOnColorSelect,
colorChoices = DEFAULT_PICKER_COLORS,
}: {
color: string
onColorSelect?: (color: string) => void
colorChoices?: string[]
}): JSX.Element => {
const [pickerOpen, setPickerOpen] = useState(false)
const { isDarkModeOn } = useValues(themeLogic)

const onColorSelect = (colorResult: ColorResult): void => {
if (propOnColorSelect) {
propOnColorSelect(colorResult.hex)
}

if (colorChoices.includes(colorResult.hex)) {
setPickerOpen(false)
}
}

const colors = isDarkModeOn ? colorChoices.map((n) => RGBToHex(lightenDarkenColor(n, -30))) : colorChoices

return (
<Popover
visible={pickerOpen}
overlay={<TwitterPicker color={color} colors={colors} onChangeComplete={onColorSelect} />}
onClickOutside={() => setPickerOpen(false)}
padded={false}
>
<LemonButton
type="secondary"
onClick={() => setPickerOpen(!pickerOpen)}
sideIcon={<></>}
className="ConditionalFormattingTab__ColorPicker"
>
<SeriesGlyph
style={{
borderColor: color,
color: color,
backgroundColor: isDarkModeOn
? RGBToRGBA(lightenDarkenColor(color, -20), 0.3)
: hexToRGBA(color, 0.5),
}}
>
<></>
</SeriesGlyph>
</LemonButton>
</Popover>
)
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import './ConditionalFormattingTab.scss'

import { IconPlusSmall, IconTrash } from '@posthog/icons'
import { LemonButton, LemonCollapse, LemonInput, LemonSelect, LemonTag, Popover } from '@posthog/lemon-ui'
import { LemonButton, LemonCollapse, LemonInput, LemonSelect, LemonTag } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { SeriesGlyph } from 'lib/components/SeriesGlyph'
import { hexToRGBA, lightenDarkenColor, RGBToHex, RGBToRGBA } from 'lib/utils'
import { useState } from 'react'
import { ColorResult, TwitterPicker } from 'react-color'
import { hexToRGBA, lightenDarkenColor, RGBToRGBA } from 'lib/utils'

import { themeLogic } from '~/layout/navigation-3000/themeLogic'
import { ColorPickerButton } from '~/queries/nodes/DataVisualization/Components/ColorPickerButton'
import { ConditionalFormattingRule } from '~/queries/schema'

import { dataVisualizationLogic } from '../../dataVisualizationLogic'
Expand Down Expand Up @@ -86,72 +85,6 @@ export const ConditionalFormattingTab = (): JSX.Element => {
)
}

const DEFAULT_PICKER_COLORS = [
'#FFADAD', // Current default
'#E8A598',
'#FFD6A5',
'#FFCFD2',
'#FDFFB6',
'#C1FBA4',
'#9BF6FF',
'#A0C4FF',
'#BDB2FF',
'#FFC6FF',
]

const ColourPickerButton = ({
color,
onColorSelect: propOnColorSelect,
}: {
color: string
onColorSelect?: (color: string) => void
}): JSX.Element => {
const [pickerOpen, setPickerOpen] = useState(false)
const { isDarkModeOn } = useValues(themeLogic)

const onColorSelect = (colorResult: ColorResult): void => {
if (propOnColorSelect) {
propOnColorSelect(colorResult.hex)
}

if (DEFAULT_PICKER_COLORS.includes(colorResult.hex)) {
setPickerOpen(false)
}
}

const colors = isDarkModeOn
? DEFAULT_PICKER_COLORS.map((n) => RGBToHex(lightenDarkenColor(n, -30)))
: DEFAULT_PICKER_COLORS

return (
<Popover
visible={pickerOpen}
overlay={<TwitterPicker color={color} colors={colors} onChangeComplete={onColorSelect} />}
onClickOutside={() => setPickerOpen(false)}
padded={false}
>
<LemonButton
type="secondary"
onClick={() => setPickerOpen(!pickerOpen)}
sideIcon={<></>}
className="ConditionalFormattingTab__ColorPicker"
>
<SeriesGlyph
style={{
borderColor: color,
color: color,
backgroundColor: isDarkModeOn
? RGBToRGBA(lightenDarkenColor(color, -20), 0.3)
: hexToRGBA(color, 0.5),
}}
>
<></>
</SeriesGlyph>
</LemonButton>
</Popover>
)
}

const RuleItem = ({ rule: propsRule }: { rule: ConditionalFormattingRule }): JSX.Element => {
const { columns, responseLoading, dataVisualizationProps } = useValues(dataVisualizationLogic)

Expand Down Expand Up @@ -197,7 +130,7 @@ const RuleItem = ({ rule: propsRule }: { rule: ConditionalFormattingRule }): JSX
/>

<div className="flex flex-1">
<ColourPickerButton color={rule.color} onColorSelect={selectColor} />
<ColorPickerButton color={rule.color} onColorSelect={selectColor} />
<LemonInput
placeholder="value"
className="ml-2 flex-1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import {
} from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { Form } from 'kea-forms'
import { getSeriesColor } from 'lib/colors'
import { getSeriesColor, getSeriesColorPalette } from 'lib/colors'
import { SeriesGlyph } from 'lib/components/SeriesGlyph'
import { LemonField } from 'lib/lemon-ui/LemonField'
import { hexToRGBA, lightenDarkenColor, RGBToRGBA } from 'lib/utils'

import { themeLogic } from '~/layout/navigation-3000/themeLogic'

import { AxisSeries, dataVisualizationLogic } from '../dataVisualizationLogic'
import { ColorPickerButton } from './ColorPickerButton'
import { ySeriesLogic, YSeriesLogicProps, YSeriesSettingsTab } from './ySeriesLogic'

export const SeriesTab = (): JSX.Element => {
Expand Down Expand Up @@ -98,7 +99,7 @@ const YSeries = ({ series, index }: { series: AxisSeries<number>; index: number
const { setSettingsOpen, submitFormatting, submitDisplay, setSettingsTab } = useActions(seriesLogic)

const { isDarkModeOn } = useValues(themeLogic)
const seriesColor = getSeriesColor(index)
const seriesColor = series.settings?.display?.color ?? getSeriesColor(index)

const columnsInOptions = showTableSettings ? columns : numericalColumns
const options = columnsInOptions.map(({ name, type }) => ({
Expand Down Expand Up @@ -219,9 +220,20 @@ const YSeriesDisplayTab = ({ ySeriesLogicProps }: { ySeriesLogicProps: YSeriesLo

return (
<Form logic={ySeriesLogic} props={ySeriesLogicProps} formKey="display" className="space-y-4">
<LemonField name="label" label="Label">
<LemonInput />
</LemonField>
<div className="flex gap-3">
<LemonField name="color" label="Color">
{({ value, onChange }) => (
<ColorPickerButton
color={value}
onColorSelect={onChange}
colorChoices={getSeriesColorPalette()}
/>
)}
</LemonField>
<LemonField name="label" label="Label">
<LemonInput />
</LemonField>
</div>
{!showTableSettings && (
<>
<LemonField name="trendLine" label="Trend line">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { actions, connect, kea, key, path, props, reducers, selectors } from 'kea'
import { forms } from 'kea-forms'
import { getSeriesColor } from 'lib/colors'

import {
AxisSeries,
Expand Down Expand Up @@ -75,6 +76,7 @@ export const ySeriesLogic = kea<ySeriesLogicType>([
},
display: {
defaults: {
color: props.series?.settings?.display?.color ?? getSeriesColor(props.seriesIndex),
label: props.series?.settings?.display?.label ?? '',
trendLine: props.series?.settings?.display?.trendLine ?? false,
yAxisPosition: props.series?.settings?.display?.yAxisPosition ?? 'left',
Expand All @@ -83,6 +85,7 @@ export const ySeriesLogic = kea<ySeriesLogicType>([
submit: async (display) => {
actions.updateSeriesIndex(props.seriesIndex, props.series.column.name, {
display: {
color: display.color,
label: display.label,
trendLine: display.trendLine,
yAxisPosition: display.yAxisPosition,
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/queries/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3006,6 +3006,9 @@
"ChartSettingsDisplay": {
"additionalProperties": false,
"properties": {
"color": {
"type": "string"
},
"displayType": {
"enum": ["auto", "line", "bar"],
"type": "string"
Expand Down
1 change: 1 addition & 0 deletions frontend/src/queries/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ export interface ChartSettingsFormatting {
}

export interface ChartSettingsDisplay {
color?: string
label?: string
trendLine?: boolean
yAxisPosition?: 'left' | 'right'
Expand Down
1 change: 1 addition & 0 deletions posthog/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ class ChartSettingsDisplay(BaseModel):
model_config = ConfigDict(
extra="forbid",
)
color: Optional[str] = None
displayType: Optional[DisplayType] = None
label: Optional[str] = None
trendLine: Optional[bool] = None
Expand Down

0 comments on commit 44f28c3

Please sign in to comment.