diff --git a/src/vis/sidebar/MultiSelect.tsx b/src/vis/sidebar/MultiSelect.tsx index a4707ce10..66ec7a957 100644 --- a/src/vis/sidebar/MultiSelect.tsx +++ b/src/vis/sidebar/MultiSelect.tsx @@ -1,4 +1,4 @@ -import { CloseButton, Combobox, Input, Pill, PillsInput, Stack, Tooltip, useCombobox, Text, Group } from '@mantine/core'; +import { CloseButton, Combobox, Input, Pill, PillsInput, Stack, Tooltip, useCombobox, Text, Group, ScrollArea } from '@mantine/core'; import * as React from 'react'; import { ColumnInfo, EColumnTypes, VisColumn } from '../interfaces'; @@ -121,7 +121,11 @@ export function MultiSelect({ - {options.length === 0 ? All options selected : options} + + + {options.length === 0 ? All options selected : options} + + ); diff --git a/src/vis/stories/Iris.stories.tsx b/src/vis/stories/Iris.stories.tsx index 66b8df58c..5f5fbf8dd 100644 --- a/src/vis/stories/Iris.stories.tsx +++ b/src/vis/stories/Iris.stories.tsx @@ -22,7 +22,7 @@ const Template: ComponentStory = (args) => { return (
- + {}} columns={columns} selected={selection} selectionCallback={setSelection} />
); diff --git a/src/vis/stories/Vis/Bar/BarRandom.stories.tsx b/src/vis/stories/Vis/Bar/BarRandom.stories.tsx index da050a9be..a7ca4d5b9 100644 --- a/src/vis/stories/Vis/Bar/BarRandom.stories.tsx +++ b/src/vis/stories/Vis/Bar/BarRandom.stories.tsx @@ -123,7 +123,7 @@ const Template: ComponentStory = (args) => {
- + {}} columns={columns} />
diff --git a/src/vis/stories/Vis/Correlation/CorrelationIris.stories.tsx b/src/vis/stories/Vis/Correlation/CorrelationIris.stories.tsx index 58a40691d..8b7799a0e 100644 --- a/src/vis/stories/Vis/Correlation/CorrelationIris.stories.tsx +++ b/src/vis/stories/Vis/Correlation/CorrelationIris.stories.tsx @@ -26,7 +26,7 @@ const Template: ComponentStory = (args) => {
- + {}} columns={columns} selected={selection} selectionCallback={setSelection} />
diff --git a/src/vis/stories/Vis/Heatmap/HeatmapRandom.stories.tsx b/src/vis/stories/Vis/Heatmap/HeatmapRandom.stories.tsx index 26d7dad43..7c33c3dfb 100644 --- a/src/vis/stories/Vis/Heatmap/HeatmapRandom.stories.tsx +++ b/src/vis/stories/Vis/Heatmap/HeatmapRandom.stories.tsx @@ -129,7 +129,7 @@ const Template: ComponentStory = (args) => {
- + {}} selected={selected} selectionCallback={setSelected} columns={columns} />
diff --git a/src/vis/stories/Vis/Hexbin/HexbinRandom.stories.tsx b/src/vis/stories/Vis/Hexbin/HexbinRandom.stories.tsx index 485167000..dddeb84f6 100644 --- a/src/vis/stories/Vis/Hexbin/HexbinRandom.stories.tsx +++ b/src/vis/stories/Vis/Hexbin/HexbinRandom.stories.tsx @@ -100,7 +100,7 @@ const Template: ComponentStory = (args) => {
- + {}} columns={columns} />
diff --git a/src/vis/stories/Vis/Raincloud/RaincloudRandom.stories.tsx b/src/vis/stories/Vis/Raincloud/RaincloudRandom.stories.tsx index 7eee5d333..9584e166c 100644 --- a/src/vis/stories/Vis/Raincloud/RaincloudRandom.stories.tsx +++ b/src/vis/stories/Vis/Raincloud/RaincloudRandom.stories.tsx @@ -132,7 +132,7 @@ const Template: ComponentStory = (args) => {
- + {}} columns={columns} selected={selected} selectionCallback={setSelected} />
diff --git a/src/vis/stories/Vis/Scatter/ScatterRandom.stories.tsx b/src/vis/stories/Vis/Scatter/ScatterRandom.stories.tsx index 91e59eedf..7ada2bf84 100644 --- a/src/vis/stories/Vis/Scatter/ScatterRandom.stories.tsx +++ b/src/vis/stories/Vis/Scatter/ScatterRandom.stories.tsx @@ -111,7 +111,7 @@ const Template: ComponentStory = (args) => {
- + {}} selected={selected} selectionCallback={setSelected} columns={columns} />
diff --git a/src/vis/stories/Vis/Violin/ViolinIris.stories.tsx b/src/vis/stories/Vis/Violin/ViolinIris.stories.tsx index 72329b34c..ba96d6654 100644 --- a/src/vis/stories/Vis/Violin/ViolinIris.stories.tsx +++ b/src/vis/stories/Vis/Violin/ViolinIris.stories.tsx @@ -21,7 +21,7 @@ const Template: ComponentStory = (args) => {
- + {}} columns={columns} />
diff --git a/src/vis/violin/ViolinVis.tsx b/src/vis/violin/ViolinVis.tsx index 458d02c11..f2011d388 100644 --- a/src/vis/violin/ViolinVis.tsx +++ b/src/vis/violin/ViolinVis.tsx @@ -8,43 +8,16 @@ import { Plotly } from '../../plotly/full'; import { InvalidCols } from '../general'; import { beautifyLayout } from '../general/layoutUtils'; import { ICommonVisProps } from '../interfaces'; -import { createViolinTraces } from './utils'; import { IViolinConfig } from './interfaces'; +import { createViolinTraces } from './utils'; export function ViolinVis({ config, columns, scales, dimensions, selectedList, selectedMap, selectionCallback }: ICommonVisProps) { const { value: traces, status: traceStatus, error: traceError } = useAsync(createViolinTraces, [columns, config, scales, selectedList, selectedMap]); - const [clearTimeoutValue, setClearTimeoutValue] = useState(null); const id = useMemo(() => uniqueId('ViolinVis'), []); const [layout, setLayout] = useState>(null); - // Filter out null values from traces as null values cause the tooltip to not show up - const filteredTraces = useMemo(() => { - if (!traces) return null; - const indexWithNull = traces.plots?.map( - (plot) => (plot?.data.y as PlotlyTypes.Datum[])?.reduce((acc: number[], curr, i) => (curr === null ? [...acc, i] : acc), []) as number[], - ); - const filtered = { - ...traces, - plots: traces?.plots?.map((p, p_index) => { - return { - ...p, - data: { - ...p.data, - y: (p.data?.y as PlotlyTypes.Datum[])?.filter((v, i) => !indexWithNull[p_index].includes(i)), - x: (p.data?.x as PlotlyTypes.Datum[])?.filter((v, i) => !indexWithNull[p_index].includes(i)), - ids: p.data?.ids?.filter((v, i) => !indexWithNull[p_index].includes(i)), - transforms: p.data?.transforms?.map( - (t) => (t.groups as unknown[])?.filter((v, i) => !indexWithNull[p_index].includes(i)) as Partial, - ), - }, - }; - }), - }; - return filtered; - }, [traces]); - const onClick = (e: (Readonly & { event: MouseEvent }) | null) => { if (!e || !e.points || !e.points[0]) { selectionCallback([]); @@ -87,22 +60,12 @@ export function ViolinVis({ config, columns, scales, dimensions, selectedList, s if (plotDiv) { // NOTE: @dv-usama-ansari: This is a hack to update the plotly plots on resize. // The `setTimeout` is used to pass the resize function to the next event loop, so that the plotly plots are rendered first. - const n = setTimeout(() => Plotly.Plots.resize(plotDiv)); - setClearTimeoutValue(n); + setTimeout(() => Plotly.Plots.resize(plotDiv)); } }, [id, dimensions, traces]); - // NOTE: @dv-usama-ansari: Clear the timeout on unmount. useEffect(() => { - return () => { - if (clearTimeoutValue) { - clearTimeout(clearTimeoutValue); - } - }; - }, [clearTimeoutValue]); - - useEffect(() => { - if (!filteredTraces) { + if (!traces) { return; } @@ -122,13 +85,14 @@ export function ViolinVis({ config, columns, scales, dimensions, selectedList, s family: 'Roboto, sans-serif', }, clickmode: 'event+select', + dragmode: false, // Disables zoom (makes no sense in violin plots) autosize: true, - grid: { rows: filteredTraces.rows, columns: filteredTraces.cols, xgap: 0.3, pattern: 'independent' }, + grid: { rows: traces.rows, columns: traces.cols, xgap: 0.3, pattern: 'independent' }, shapes: [], }; - setLayout((prev) => ({ ...prev, ...beautifyLayout(filteredTraces, innerLayout, prev, true) })); - }, [filteredTraces]); + setLayout((prev) => ({ ...prev, ...beautifyLayout(traces, innerLayout, prev, true) })); + }, [traces]); return ( - {traceStatus === 'success' && layout && filteredTraces?.plots.length > 0 ? ( + {traceStatus === 'success' && layout && traces?.plots.length > 0 ? ( p.data), ...filteredTraces.legendPlots.map((p) => p.data)]} + data={[...traces.plots.map((p) => p.data), ...traces.legendPlots.map((p) => p.data)]} layout={layout} config={{ responsive: true, displayModeBar: false }} useResizeHandler @@ -163,7 +127,7 @@ export function ViolinVis({ config, columns, scales, dimensions, selectedList, s }} /> ) : traceStatus !== 'pending' && traceStatus !== 'idle' && layout ? ( - + ) : null} ); diff --git a/src/vis/violin/utils.ts b/src/vis/violin/utils.ts index 4c6cb29e2..36ecfa35e 100644 --- a/src/vis/violin/utils.ts +++ b/src/vis/violin/utils.ts @@ -54,9 +54,10 @@ export async function createViolinTraces( // if we onl have numerical columns, add them individually. if (catColValues.length === 0) { for (const numCurr of numColValues) { + const y = numCurr.resolvedValues.map((v) => v.val); plots.push({ data: { - y: numCurr.resolvedValues.map((v) => v.val), + y, ids: numCurr.resolvedValues.map((v) => v.id), xaxis: plotCounter === 1 ? 'x' : `x${plotCounter}`, yaxis: plotCounter === 1 ? 'y' : `y${plotCounter}`, @@ -88,11 +89,15 @@ export async function createViolinTraces( for (const numCurr of numColValues) { for (const catCurr of catColValues) { + const y = numCurr.resolvedValues.map((v) => v.val); + // Null values in categorical columns would break the plot --> replace with 'missing' + const categoriesWithMissing = catCurr.resolvedValues?.map((v) => ({ ...v, val: v.val || 'missing' })); + const x = categoriesWithMissing.map((v) => v.val); plots.push({ data: { - x: catCurr.resolvedValues.map((v) => v.val), - ids: catCurr.resolvedValues.map((v) => v.id), - y: numCurr.resolvedValues.map((v) => v.val), + x, + y, + ids: categoriesWithMissing.map((v) => v.id), xaxis: plotCounter === 1 ? 'x' : `x${plotCounter}`, yaxis: plotCounter === 1 ? 'y' : `y${plotCounter}`, type: 'violin', @@ -112,14 +117,14 @@ export async function createViolinTraces( transforms: [ { type: 'groupby', - groups: catCurr.resolvedValues.map((v) => v.val) as string[], - styles: [...new Set(catCurr.resolvedValues.map((v) => v.val) as string[])].map((c) => { + groups: x as string[], + styles: [...new Set(x as string[])].map((c) => { return { target: c, value: { line: { color: - selectedList.length !== 0 && catCurr.resolvedValues.filter((val) => val.val === c).find((val) => selectedMap[val.id]) + selectedList.length !== 0 && categoriesWithMissing.filter((val) => val.val === c).find((val) => selectedMap[val.id]) ? SELECT_COLOR : '#878E95', },