Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
76 changes: 76 additions & 0 deletions src/components/GraphPanel/GraphPanel.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ const GraphPanel = ({
aggregationType,
maxDataPoints,
calculatedValues,
secondaryYAxisType,
} = graphConfig;

// Limit data points for performance
Expand All @@ -154,6 +155,81 @@ const GraphPanel = ({
result = processBarLineData(limitedData, xColumn, valueColumn, groupByColumn, aggregationType, calculatedValues);
break;
}

// Apply secondary Y-axis chart type to datasets on secondary axis
if (result && result.datasets && secondaryYAxisType && (chartType === 'bar' || chartType === 'line')) {
result.datasets = result.datasets.map(dataset => {
if (dataset.yAxisID === 'y1') {
return {
...dataset,
type: secondaryYAxisType,
};
}
return dataset;
});
}

// Helper to compute the effective chart type for a dataset
// dataset.type overrides global defaults (set by secondary Y-axis type or other means)
const getEffectiveType = (dataset) => {
if (dataset.type) {
return dataset.type;
}
if (dataset.yAxisID === 'y1' && secondaryYAxisType) {
return secondaryYAxisType;
}
return chartType;
};

// Apply line styles to datasets (convert lineStyle to Chart.js borderDash)
if (result && result.datasets) {
const lineStyleToBorderDash = {
solid: [],
dashed: [8, 4],
dotted: [2, 2],
};

result.datasets = result.datasets.map(dataset => {
const effectiveType = getEffectiveType(dataset);
if (effectiveType === 'line' && dataset.lineStyle && lineStyleToBorderDash[dataset.lineStyle]) {
return {
...dataset,
borderDash: lineStyleToBorderDash[dataset.lineStyle],
};
}
return dataset;
});
}

// Apply bar styles to datasets
if (result && result.datasets) {
result.datasets = result.datasets.map(dataset => {
const effectiveType = getEffectiveType(dataset);
if (effectiveType === 'bar' && dataset.barStyle) {
switch (dataset.barStyle) {
case 'outlined':
// Outlined: transparent fill with thick border
return {
...dataset,
backgroundColor: 'transparent',
borderWidth: 2,
};
case 'striped':
// Striped: lighter fill with dashed border to indicate pattern
return {
...dataset,
backgroundColor: dataset.backgroundColor.replace('0.8', '0.3'),
borderWidth: 2,
borderDash: [4, 4],
};
default:
return dataset;
}
}
return dataset;
});
}

return { processedData: result, validationError: null };
} catch (err) {
console.error('Error processing graph data:', err);
Expand Down
74 changes: 74 additions & 0 deletions src/dashboard/Data/Browser/GraphDialog.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ const CALCULATED_VALUE_OPERATORS = [
{ value: 'formula', label: 'Formula' },
];

const LINE_STYLES = [
{ value: 'solid', label: 'Solid' },
{ value: 'dashed', label: 'Dashed' },
{ value: 'dotted', label: 'Dotted' },
];

const BAR_STYLES = [
{ value: 'solid', label: 'Solid' },
{ value: 'outlined', label: 'Outlined' },
{ value: 'striped', label: 'Striped' },
];

export default class GraphDialog extends React.Component {
constructor(props) {
super(props);
Expand Down Expand Up @@ -77,6 +89,7 @@ export default class GraphDialog extends React.Component {
title: initialConfig.title || '',
yAxisTitlePrimary: initialConfig.yAxisTitlePrimary || '',
yAxisTitleSecondary: initialConfig.yAxisTitleSecondary || '',
secondaryYAxisType: initialConfig.secondaryYAxisType || null,
showLegend: initialConfig.showLegend !== undefined ? initialConfig.showLegend : true,
showGrid: initialConfig.showGrid !== undefined ? initialConfig.showGrid : true,
showAxisLabels: initialConfig.showAxisLabels !== undefined ? initialConfig.showAxisLabels : true,
Expand Down Expand Up @@ -402,6 +415,10 @@ export default class GraphDialog extends React.Component {
const hasCircular = this.hasCircularReference(index);
// Check for formula errors
const formulaError = this.getFormulaError(index);
// Compute effective chart type for this calculated value
const effectiveType = calc.useSecondaryYAxis && this.state.secondaryYAxisType
? this.state.secondaryYAxisType
: this.state.chartType;

return (
<div key={index} style={{ paddingTop: '10px', paddingLeft: '10px', paddingRight: '10px', paddingBottom: isExpanded ? '0' : '10px', borderTop: '1px solid #e3e3e3', borderLeft: '1px solid #e3e3e3', borderRight: '1px solid #e3e3e3', borderBottom: index === this.state.calculatedValues.length - 1 ? '1px solid #e3e3e3' : 'none' }}>
Expand Down Expand Up @@ -547,6 +564,44 @@ export default class GraphDialog extends React.Component {
</div>
</div>
)}
{effectiveType === 'line' && (
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', borderTop: '1px solid #e3e3e3' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Label text="Line Style" />
</div>
<div>
<Dropdown
value={calc.lineStyle || 'solid'}
onChange={lineStyle => this.updateCalculatedValue(index, 'lineStyle', lineStyle)}
>
{LINE_STYLES.map(style => (
<Option key={style.value} value={style.value}>
{style.label}
</Option>
))}
</Dropdown>
</div>
</div>
)}
{effectiveType === 'bar' && (
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', borderTop: '1px solid #e3e3e3' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Label text="Bar Style" />
</div>
<div>
<Dropdown
value={calc.barStyle || 'solid'}
onChange={barStyle => this.updateCalculatedValue(index, 'barStyle', barStyle)}
>
{BAR_STYLES.map(style => (
<Option key={style.value} value={style.value}>
{style.label}
</Option>
))}
</Dropdown>
</div>
</div>
)}
{hasCircular && (
<div style={{ borderTop: '1px solid #e3e3e3', padding: '12px', background: '#fff3cd', color: '#856404' }}>
<strong>⚠ Circular Reference Detected</strong>
Expand Down Expand Up @@ -686,6 +741,25 @@ export default class GraphDialog extends React.Component {
} />
)}

{(this.state.chartType === 'bar' || this.state.chartType === 'line') && (
<Field label={
<Label
text="Secondary Y-Axis Chart Type"
description="Chart type for secondary axis series"
/>
} input={
<Dropdown
value={this.state.secondaryYAxisType || ''}
onChange={secondaryYAxisType => this.setState({ secondaryYAxisType: secondaryYAxisType || null })}
placeHolder="Same as chart type"
>
<Option value="">Same as chart type</Option>
<Option value="bar">Bar</Option>
<Option value="line">Line</Option>
</Dropdown>
} />
)}

<Field label={
<Label
text="Max Data Points"
Expand Down
26 changes: 24 additions & 2 deletions src/lib/GraphDataUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -720,10 +720,12 @@ export function processBarLineData(data, xColumn, valueColumn, groupByColumn, ag
const sortedXLabels = sortedXKeys.map(key => xValues.get(key));
const groupKeys = Object.keys(groups);

// Create maps for calculated value properties (operator, secondary Y axis)
// Create maps for calculated value properties (operator, secondary Y axis, line style, bar style)
// This handles both simple calc names and grouped calc names like "CalcName (GroupValue)"
const calcValueOperatorMap = new Map();
const calcValueSecondaryYAxisMap = new Map();
const calcValueLineStyleMap = new Map();
const calcValueBarStyleMap = new Map();
if (calculatedValues && Array.isArray(calculatedValues)) {
calculatedValues.forEach(calc => {
if (calc.name && calc.operator) {
Expand All @@ -736,6 +738,12 @@ export function processBarLineData(data, xColumn, valueColumn, groupByColumn, ag
if (calc.useSecondaryYAxis) {
calcValueSecondaryYAxisMap.set(groupKey, true);
}
if (calc.lineStyle) {
calcValueLineStyleMap.set(groupKey, calc.lineStyle);
}
if (calc.barStyle) {
calcValueBarStyleMap.set(groupKey, calc.barStyle);
}
}
});
}
Expand Down Expand Up @@ -770,14 +778,28 @@ export function processBarLineData(data, xColumn, valueColumn, groupByColumn, ag
}
});

return {
const dataset = {
label: groupKey,
data: values,
backgroundColor: colors[index],
borderColor: colors[index].replace('0.8', '1'),
borderWidth: 1,
yAxisID: calcValueSecondaryYAxisMap.get(groupKey) ? 'y1' : 'y',
};

// Add line style if specified
const lineStyle = calcValueLineStyleMap.get(groupKey);
if (lineStyle) {
dataset.lineStyle = lineStyle;
}

// Add bar style if specified
const barStyle = calcValueBarStyleMap.get(groupKey);
if (barStyle) {
dataset.barStyle = barStyle;
}

return dataset;
});

return {
Expand Down
Loading