Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bi): Added the ability to customise table columns #24236

Merged
merged 28 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5c76b71
Added the ability to customise table columns
Gilbert09 Aug 7, 2024
d464bd5
Remove comment
Gilbert09 Aug 7, 2024
8cb15c4
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 7, 2024
0db5563
Merge branch 'master' into tom/table-settings
Gilbert09 Aug 7, 2024
ec1d980
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 7, 2024
c67610b
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 7, 2024
0242979
Fixed test
Gilbert09 Aug 8, 2024
2cb2e90
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 8, 2024
a838f90
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 8, 2024
a971f36
Update UI snapshots for `chromium` (2)
github-actions[bot] Aug 8, 2024
0f2c21b
Merge branch 'master' into tom/table-settings
Gilbert09 Aug 9, 2024
3278116
Update UI snapshots for `chromium` (2)
github-actions[bot] Aug 9, 2024
d47221e
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 9, 2024
a10689f
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 9, 2024
d5d80fe
Update UI snapshots for `chromium` (2)
github-actions[bot] Aug 9, 2024
178c10e
Merge branch 'master' into tom/table-settings
Gilbert09 Aug 9, 2024
9f4c243
Update UI snapshots for `chromium` (2)
github-actions[bot] Aug 9, 2024
d82e8ff
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 9, 2024
d94ee25
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 9, 2024
462c88a
Fixed tests, again
Gilbert09 Aug 9, 2024
aa01048
Fixed tests, again
Gilbert09 Aug 12, 2024
d827e1d
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 12, 2024
b32256e
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 12, 2024
c2be86e
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 12, 2024
f2558a6
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 12, 2024
5619dad
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 12, 2024
9ba13bd
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 12, 2024
6d6ca7a
Update UI snapshots for `chromium` (1)
github-actions[bot] Aug 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 0 additions & 29 deletions frontend/src/queries/nodes/DataVisualization/Components/Chart.tsx

This file was deleted.

194 changes: 112 additions & 82 deletions frontend/src/queries/nodes/DataVisualization/Components/SeriesTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,23 @@ import { AxisSeries, dataVisualizationLogic } from '../dataVisualizationLogic'
import { ySeriesLogic, YSeriesLogicProps, YSeriesSettingsTab } from './ySeriesLogic'

export const SeriesTab = (): JSX.Element => {
const { columns, numericalColumns, xData, yData, responseLoading } = useValues(dataVisualizationLogic)
const { columns, numericalColumns, xData, yData, responseLoading, isTableVisualization, tabularColumns } =
useValues(dataVisualizationLogic)
const { updateXSeries, addYSeries } = useActions(dataVisualizationLogic)

const hideAddYSeries = yData.length >= numericalColumns.length

if (isTableVisualization) {
return (
<div className="flex flex-col w-full">
<LemonLabel>Columns</LemonLabel>
{tabularColumns.map((series, index) => (
<YSeries series={series} index={index} key={`${series.column.name}-${index}`} />
))}
</div>
)
}

const options = columns.map(({ name, type }) => ({
value: name,
label: (
Expand Down Expand Up @@ -75,8 +87,9 @@ export const SeriesTab = (): JSX.Element => {
}

const YSeries = ({ series, index }: { series: AxisSeries<number>; index: number }): JSX.Element => {
const { columns, numericalColumns, responseLoading, dataVisualizationProps } = useValues(dataVisualizationLogic)
const { updateYSeries, deleteYSeries } = useActions(dataVisualizationLogic)
const { columns, numericalColumns, responseLoading, dataVisualizationProps, isTableVisualization } =
useValues(dataVisualizationLogic)
const { updateSeriesIndex, deleteYSeries } = useActions(dataVisualizationLogic)

const seriesLogicProps: YSeriesLogicProps = { series, seriesIndex: index, dataVisualizationProps }
const seriesLogic = ySeriesLogic(seriesLogicProps)
Expand All @@ -87,22 +100,25 @@ const YSeries = ({ series, index }: { series: AxisSeries<number>; index: number
const { isDarkModeOn } = useValues(themeLogic)
const seriesColor = getSeriesColor(index)

const options = numericalColumns.map(({ name, type }) => ({
const columnsInOptions = isTableVisualization ? columns : numericalColumns
const options = columnsInOptions.map(({ name, type }) => ({
value: name,
label: (
<div className="items-center flex flex-1">
<SeriesGlyph
style={{
borderColor: seriesColor,
color: seriesColor,
backgroundColor: isDarkModeOn
? RGBToRGBA(lightenDarkenColor(seriesColor, -20), 0.3)
: hexToRGBA(seriesColor, 0.2),
}}
className="mr-2"
>
<></>
</SeriesGlyph>
{!isTableVisualization && (
<SeriesGlyph
style={{
borderColor: seriesColor,
color: seriesColor,
backgroundColor: isDarkModeOn
? RGBToRGBA(lightenDarkenColor(seriesColor, -20), 0.3)
: hexToRGBA(seriesColor, 0.2),
}}
className="mr-2"
>
<></>
</SeriesGlyph>
)}
{series.settings?.display?.label && series.column.name === name ? series.settings.display.label : name}
<LemonTag className="ml-2" type="default">
{type.name}
Expand All @@ -121,7 +137,7 @@ const YSeries = ({ series, index }: { series: AxisSeries<number>; index: number
onChange={(value) => {
const column = columns.find((n) => n.name === value)
if (column) {
updateYSeries(index, column.name)
updateSeriesIndex(index, column.name)
}
}}
/>
Expand Down Expand Up @@ -156,94 +172,108 @@ const YSeries = ({ series, index }: { series: AxisSeries<number>; index: number
disabledReasonWrapperClass="flex"
/>
</Popover>
<LemonButton
key="delete"
icon={<IconTrash />}
status="danger"
title="Delete Y-series"
noPadding
onClick={() => deleteYSeries(index)}
/>
{!isTableVisualization && (
<LemonButton
key="delete"
icon={<IconTrash />}
status="danger"
title="Delete Y-series"
noPadding
onClick={() => deleteYSeries(index)}
/>
)}
</div>
)
}

const YSeriesFormattingTab = ({ ySeriesLogicProps }: { ySeriesLogicProps: YSeriesLogicProps }): JSX.Element => {
return (
<Form logic={ySeriesLogic} props={ySeriesLogicProps} formKey="formatting" className="space-y-4">
<LemonField name="style" label="Style" className="gap-1">
<LemonSelect
options={[
{ value: 'none', label: 'None' },
{ value: 'number', label: 'Number' },
{ value: 'percent', label: 'Percentage' },
]}
/>
</LemonField>
{ySeriesLogicProps.series.column.type.isNumerical && (
<LemonField name="style" label="Style" className="gap-1">
<LemonSelect
options={[
{ value: 'none', label: 'None' },
{ value: 'number', label: 'Number' },
{ value: 'percent', label: 'Percentage' },
]}
/>
</LemonField>
)}
<LemonField name="prefix" label="Prefix">
<LemonInput placeholder="$" />
</LemonField>
<LemonField name="suffix" label="Suffix">
<LemonInput placeholder="USD" />
</LemonField>
<LemonField name="decimalPlaces" label="Decimal places">
<LemonInput type="number" min={0} />
</LemonField>
{ySeriesLogicProps.series.column.type.isNumerical && (
<LemonField name="decimalPlaces" label="Decimal places">
<LemonInput type="number" min={0} />
</LemonField>
)}
</Form>
)
}

const YSeriesDisplayTab = ({ ySeriesLogicProps }: { ySeriesLogicProps: YSeriesLogicProps }): JSX.Element => {
const { isTableVisualization } = useValues(dataVisualizationLogic)

return (
<Form logic={ySeriesLogic} props={ySeriesLogicProps} formKey="display" className="space-y-4">
<LemonField name="label" label="Label">
<LemonInput />
</LemonField>
<LemonField name="trendLine" label="Trend line">
{({ value, onChange }) => <LemonSwitch checked={value} onChange={(newValue) => onChange(newValue)} />}
</LemonField>
<LemonField name="yAxisPosition" label="Y-axis position">
{({ value, onChange }) => (
<LemonSegmentedButton
value={value}
className="w-full"
options={[
{
label: 'Left',
value: 'left',
},
{
label: 'Right',
value: 'right',
},
]}
onChange={(newValue) => onChange(newValue)}
/>
)}
</LemonField>
<LemonField name="displayType" label="Display type">
{({ value, onChange }) => (
<LemonSegmentedButton
value={value}
className="w-full"
options={[
{
label: 'Auto',
value: 'auto',
},
{
label: 'Line',
value: 'line',
},
{
label: 'Bar',
value: 'bar',
},
]}
onChange={(newValue) => onChange(newValue)}
/>
)}
</LemonField>
{!isTableVisualization && (
<>
<LemonField name="trendLine" label="Trend line">
{({ value, onChange }) => (
<LemonSwitch checked={value} onChange={(newValue) => onChange(newValue)} />
)}
</LemonField>
<LemonField name="yAxisPosition" label="Y-axis position">
{({ value, onChange }) => (
<LemonSegmentedButton
value={value}
className="w-full"
options={[
{
label: 'Left',
value: 'left',
},
{
label: 'Right',
value: 'right',
},
]}
onChange={(newValue) => onChange(newValue)}
/>
)}
</LemonField>
<LemonField name="displayType" label="Display type">
{({ value, onChange }) => (
<LemonSegmentedButton
value={value}
className="w-full"
options={[
{
label: 'Auto',
value: 'auto',
},
{
label: 'Line',
value: 'line',
},
{
label: 'Bar',
value: 'bar',
},
]}
onChange={(newValue) => onChange(newValue)}
/>
)}
</LemonField>
</>
)}
</Form>
)
}
Expand Down
36 changes: 22 additions & 14 deletions frontend/src/queries/nodes/DataVisualization/Components/SideBar.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import './SideBar.scss'

import { LemonTabs } from '@posthog/lemon-ui'
import { LemonTab, LemonTabs } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'

import { ChartDisplayType } from '~/types'

import { dataVisualizationLogic, SideBarTab } from '../dataVisualizationLogic'
import { DisplayTab } from './DisplayTab'
import { SeriesTab } from './SeriesTab'

const TABS_TO_CONTENT = {
type TabContent = {
label: string
content: JSX.Element
shouldShow: (displayType: ChartDisplayType) => boolean
}

const TABS_TO_CONTENT: Record<SideBarTab, TabContent> = {
[SideBarTab.Series]: {
label: 'Series',
content: <SeriesTab />,
shouldShow: (): boolean => true,
},
[SideBarTab.Display]: {
label: 'Display',
content: <DisplayTab />,
shouldShow: (displayType: ChartDisplayType): boolean => displayType !== ChartDisplayType.ActionsTable,
},
}

Expand All @@ -23,18 +33,16 @@ const ContentWrapper = ({ children }: { children: JSX.Element }): JSX.Element =>
}

export const SideBar = (): JSX.Element => {
const { activeSideBarTab } = useValues(dataVisualizationLogic)
const { activeSideBarTab, visualizationType } = useValues(dataVisualizationLogic)
const { setSideBarTab } = useActions(dataVisualizationLogic)

return (
<LemonTabs
activeKey={activeSideBarTab}
onChange={(tab) => setSideBarTab(tab as SideBarTab)}
tabs={Object.values(TABS_TO_CONTENT).map((tab, index) => ({
label: tab.label,
key: Object.keys(TABS_TO_CONTENT)[index],
content: <ContentWrapper>{tab.content}</ContentWrapper>,
}))}
/>
)
const tabs: LemonTab<string>[] = Object.values(TABS_TO_CONTENT)
.filter((n) => n.shouldShow(visualizationType))
.map((tab, index) => ({
label: tab.label,
key: Object.keys(TABS_TO_CONTENT)[index],
content: <ContentWrapper>{tab.content}</ContentWrapper>,
}))

return <LemonTabs activeKey={activeSideBarTab} onChange={(tab) => setSideBarTab(tab as SideBarTab)} tabs={tabs} />
}
48 changes: 48 additions & 0 deletions frontend/src/queries/nodes/DataVisualization/Components/Table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { LemonTable, LemonTableColumn } from '@posthog/lemon-ui'
import { useValues } from 'kea'
import { InsightEmptyState } from 'scenes/insights/EmptyStates'

import { DataVisualizationNode, HogQLQueryResponse, NodeKind } from '~/queries/schema'
import { QueryContext } from '~/queries/types'

import { LoadNext } from '../../DataNode/LoadNext'
import { renderColumn } from '../../DataTable/renderColumn'
import { dataVisualizationLogic } from '../dataVisualizationLogic'

interface TableProps {
query: DataVisualizationNode
uniqueKey: string | number | undefined
context: QueryContext | undefined
cachedResults: HogQLQueryResponse | undefined
}

export const Table = (props: TableProps): JSX.Element => {
const { tabularData, tabularColumns, responseLoading } = useValues(dataVisualizationLogic)

const tableColumns: LemonTableColumn<any[], any>[] = tabularColumns.map(({ column, settings }, index) => ({
title: settings?.display?.label || column.name,
render: (_, data, recordIndex: number) => {
return renderColumn(column.name, data[index], data, recordIndex, {
kind: NodeKind.DataTableNode,
source: props.query.source,
})
},
}))

return (
<div className="relative w-full flex flex-col gap-4 flex-1 h-full">
<LemonTable
dataSource={tabularData}
columns={tableColumns}
loading={responseLoading}
emptyState={
<InsightEmptyState
heading={props.context?.emptyStateHeading}
detail={props.context?.emptyStateDetail}
/>
}
footer={<LoadNext query={props.query} />}
/>
</div>
)
}
Loading
Loading