Skip to content

Data Table with Sparklines

Overview

Displays tabular data with embedded mini-charts (sparklines) in cells. Perfect for showing detailed data alongside visual trends, combining the precision of tables with the insight of charts.

Sample Preview

Data Table Sample

Best Use Cases

  • Store Performance Dashboard - Show store metrics with trend sparklines
  • Customer Segment Analysis - Display segment data with satisfaction trends
  • Monthly Reports - Present KPIs with historical trend visualization

Sample Data Structure

AskRITA UniversalChartData

from askrita.sqlagent.formatters.DataFormatter import UniversalChartData

table_data = UniversalChartData(
    type="table",
    title="Store Performance Dashboard",
    datasets=[],  # Empty for table charts
    table_data=[
        {
            "store_id": "Store #1234",
            "location": "Boston, MA",
            "current_nps": 78,
            "nps_trend": [65, 68, 71, 74, 76, 78],
            "monthly_responses": 1250,
            "response_trend": [980, 1050, 1120, 1180, 1220, 1250],
            "satisfaction": 8.4,
            "satisfaction_trend": [8.0, 8.1, 8.2, 8.3, 8.3, 8.4]
        },
        {
            "store_id": "Store #5678",
            "location": "Austin, TX",
            "current_nps": 82,
            "nps_trend": [70, 73, 76, 78, 80, 82],
            "monthly_responses": 1450,
            "response_trend": [1200, 1280, 1320, 1380, 1420, 1450],
            "satisfaction": 8.7,
            "satisfaction_trend": [8.2, 8.3, 8.4, 8.5, 8.6, 8.7]
        }
    ]
)

Google Charts Implementation

HTML Structure

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
</head>
<body>
    <div id="table_chart" style="width: 1200px; height: 500px;"></div>
</body>
</html>

JavaScript Code

google.charts.load('current', {'packages':['table']});
google.charts.setOnLoadCallback(drawTableChart);

function drawTableChart() {
    var data = new google.visualization.DataTable();

    // Define columns
    data.addColumn('string', 'Store ID');
    data.addColumn('string', 'Location');
    data.addColumn('number', 'Current NPS');
    data.addColumn('string', 'NPS Trend');
    data.addColumn('number', 'Monthly Responses');
    data.addColumn('string', 'Response Trend');
    data.addColumn('number', 'Satisfaction');
    data.addColumn('string', 'Satisfaction Trend');

    // Add rows with sparkline data
    data.addRows([
        [
            'Store #1234', 
            'Boston, MA', 
            78, 
            createSparklineHTML([65, 68, 71, 74, 76, 78], 'line'),
            1250,
            createSparklineHTML([980, 1050, 1120, 1180, 1220, 1250], 'column'),
            8.4,
            createSparklineHTML([8.0, 8.1, 8.2, 8.3, 8.3, 8.4], 'line')
        ],
        [
            'Store #5678', 
            'Austin, TX', 
            82, 
            createSparklineHTML([70, 73, 76, 78, 80, 82], 'line'),
            1450,
            createSparklineHTML([1200, 1280, 1320, 1380, 1420, 1450], 'column'),
            8.7,
            createSparklineHTML([8.2, 8.3, 8.4, 8.5, 8.6, 8.7], 'line')
        ],
        [
            'Store #9012', 
            'Seattle, WA', 
            75, 
            createSparklineHTML([68, 70, 72, 73, 74, 75], 'line'),
            1180,
            createSparklineHTML([950, 1020, 1080, 1120, 1150, 1180], 'column'),
            8.1,
            createSparklineHTML([7.8, 7.9, 8.0, 8.0, 8.1, 8.1], 'line')
        ],
        [
            'Store #3456', 
            'Denver, CO', 
            85, 
            createSparklineHTML([75, 78, 80, 82, 84, 85], 'line'),
            890,
            createSparklineHTML([720, 780, 820, 850, 870, 890], 'column'),
            8.9,
            createSparklineHTML([8.3, 8.4, 8.6, 8.7, 8.8, 8.9], 'line')
        ]
    ]);

    var options = {
        title: 'Store Performance Dashboard',
        titleTextStyle: {
            fontSize: 18,
            bold: true
        },
        width: 1200,
        height: 500,
        allowHtml: true, // Enable HTML content in cells
        alternatingRowStyle: false,
        cssClassNames: {
            'headerRow': 'table-header',
            'tableRow': 'table-row',
            'evenTableRow': 'table-row-even'
        },
        sort: 'enable'
    };

    var chart = new google.visualization.Table(document.getElementById('table_chart'));
    chart.draw(data, options);
}

function createSparklineHTML(values, type = 'line') {
    const width = 100;
    const height = 30;
    const max = Math.max(...values);
    const min = Math.min(...values);
    const range = max - min || 1;

    if (type === 'line') {
        // Create SVG line sparkline
        const points = values.map((value, index) => {
            const x = (index / (values.length - 1)) * width;
            const y = height - ((value - min) / range) * height;
            return `${x},${y}`;
        }).join(' ');

        return `<svg width="${width}" height="${height}" style="display: block;">
                    <polyline points="${points}" 
                              fill="none" 
                              stroke="#4285f4" 
                              stroke-width="2"/>
                </svg>`;
    } else if (type === 'column') {
        // Create SVG column sparkline
        const barWidth = width / values.length - 1;
        const bars = values.map((value, index) => {
            const x = index * (barWidth + 1);
            const barHeight = ((value - min) / range) * height;
            const y = height - barHeight;
            return `<rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}" fill="#34a853"/>`;
        }).join('');

        return `<svg width="${width}" height="${height}" style="display: block;">
                    ${bars}
                </svg>`;
    }
}

React Implementation

import React, { useEffect, useRef } from 'react';

interface TableRow {
    [key: string]: any;
    sparklines?: { [column: string]: number[] };
}

interface TableChartProps {
    data: TableRow[];
    columns: Array<{
        id: string;
        label: string;
        type: 'string' | 'number' | 'sparkline';
        sparklineType?: 'line' | 'column';
    }>;
    title?: string;
    width?: number;
    height?: number;
    sortable?: boolean;
}

const TableChart: React.FC<TableChartProps> = ({
    data,
    columns,
    title = "Data Table",
    width = 1200,
    height = 500,
    sortable = true
}) => {
    const chartRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
        if (!window.google || !chartRef.current) return;

        const dataTable = new google.visualization.DataTable();

        // Add columns
        columns.forEach(col => {
            if (col.type === 'sparkline') {
                dataTable.addColumn('string', col.label);
            } else {
                dataTable.addColumn(col.type, col.label);
            }
        });

        // Add rows
        const rows = data.map(row => {
            return columns.map(col => {
                if (col.type === 'sparkline' && row.sparklines && row.sparklines[col.id]) {
                    return createSparklineHTML(row.sparklines[col.id], col.sparklineType || 'line');
                }
                return row[col.id];
            });
        });
        dataTable.addRows(rows);

        const options = {
            title: title,
            width: width,
            height: height,
            allowHtml: true,
            sort: sortable ? 'enable' : 'disable',
            alternatingRowStyle: false
        };

        const chart = new google.visualization.Table(chartRef.current);
        chart.draw(dataTable, options);
    }, [data, columns, title, width, height, sortable]);

    const createSparklineHTML = (values: number[], type: 'line' | 'column') => {
        const width = 100;
        const height = 30;
        const max = Math.max(...values);
        const min = Math.min(...values);
        const range = max - min || 1;

        if (type === 'line') {
            const points = values.map((value, index) => {
                const x = (index / (values.length - 1)) * width;
                const y = height - ((value - min) / range) * height;
                return `${x},${y}`;
            }).join(' ');

            return `<svg width="${width}" height="${height}">
                        <polyline points="${points}" fill="none" stroke="#4285f4" stroke-width="2"/>
                    </svg>`;
        } else {
            const barWidth = width / values.length - 1;
            const bars = values.map((value, index) => {
                const x = index * (barWidth + 1);
                const barHeight = ((value - min) / range) * height;
                const y = height - barHeight;
                return `<rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}" fill="#34a853"/>`;
            }).join('');

            return `<svg width="${width}" height="${height}">${bars}</svg>`;
        }
    };

    return <div ref={chartRef} style={{ width: `${width}px`, height: `${height}px` }} />;
};

export default TableChart;

Survey Data Examples

Customer Segment Performance

// Customer segment analysis with trends
function drawSegmentTable() {
    var data = new google.visualization.DataTable();

    data.addColumn('string', 'Segment');
    data.addColumn('number', 'Customers');
    data.addColumn('string', 'Growth Trend');
    data.addColumn('number', 'Avg NPS');
    data.addColumn('string', 'NPS Trend');
    data.addColumn('number', 'Satisfaction');
    data.addColumn('string', 'Satisfaction Trend');
    data.addColumn('number', 'Response Rate %');

    data.addRows([
        [
            'Premium Members',
            12450,
            createSparklineHTML([10200, 10800, 11300, 11800, 12100, 12450], 'column'),
            85,
            createSparklineHTML([78, 80, 82, 83, 84, 85], 'line'),
            9.1,
            createSparklineHTML([8.6, 8.7, 8.8, 8.9, 9.0, 9.1], 'line'),
            78
        ],
        [
            'Regular Customers',
            45890,
            createSparklineHTML([43200, 44100, 44800, 45200, 45600, 45890], 'column'),
            72,
            createSparklineHTML([68, 69, 70, 71, 71, 72], 'line'),
            8.3,
            createSparklineHTML([8.0, 8.1, 8.1, 8.2, 8.2, 8.3], 'line'),
            65
        ],
        [
            'New Customers',
            8920,
            createSparklineHTML([6500, 7200, 7800, 8200, 8600, 8920], 'column'),
            68,
            createSparklineHTML([62, 64, 65, 66, 67, 68], 'line'),
            7.8,
            createSparklineHTML([7.2, 7.3, 7.5, 7.6, 7.7, 7.8], 'line'),
            52
        ]
    ]);

    var options = {
        title: 'Customer Segment Performance Analysis',
        width: 1200,
        height: 400,
        allowHtml: true,
        sort: 'enable'
    };

    var chart = new google.visualization.Table(document.getElementById('table_chart'));
    chart.draw(data, options);
}

Regional Performance Dashboard

// Regional performance with multiple metrics
function drawRegionalTable() {
    var data = new google.visualization.DataTable();

    data.addColumn('string', 'Region');
    data.addColumn('string', 'Top Store');
    data.addColumn('number', 'Stores Count');
    data.addColumn('number', 'Total Responses');
    data.addColumn('string', 'Response Trend');
    data.addColumn('number', 'Avg NPS');
    data.addColumn('string', 'NPS Trend');
    data.addColumn('number', 'CSAT Score');
    data.addColumn('string', 'CSAT Trend');

    data.addRows([
        [
            'Northeast',
            'Boston Downtown #1234',
            145,
            18450,
            createSparklineHTML([16200, 16800, 17300, 17800, 18100, 18450], 'column'),
            74,
            createSparklineHTML([70, 71, 72, 73, 73, 74], 'line'),
            8.4,
            createSparklineHTML([8.0, 8.1, 8.2, 8.3, 8.3, 8.4], 'line')
        ],
        [
            'Southeast',
            'Atlanta Midtown #5678',
            189,
            22890,
            createSparklineHTML([20100, 20900, 21500, 22000, 22400, 22890], 'column'),
            71,
            createSparklineHTML([67, 68, 69, 70, 70, 71], 'line'),
            8.1,
            createSparklineHTML([7.8, 7.9, 8.0, 8.0, 8.1, 8.1], 'line')
        ],
        [
            'West',
            'Seattle Capitol Hill #9012',
            167,
            19780,
            createSparklineHTML([17800, 18400, 18900, 19300, 19600, 19780], 'column'),
            76,
            createSparklineHTML([72, 73, 74, 75, 75, 76], 'line'),
            8.6,
            createSparklineHTML([8.2, 8.3, 8.4, 8.5, 8.5, 8.6], 'line')
        ]
    ]);

    var options = {
        title: 'Regional Performance Dashboard',
        width: 1200,
        height: 350,
        allowHtml: true
    };

    var chart = new google.visualization.Table(document.getElementById('table_chart'));
    chart.draw(data, options);
}

Campaign Performance Tracker

// Survey campaign performance with trends
function drawCampaignTable() {
    var data = new google.visualization.DataTable();

    data.addColumn('string', 'Campaign');
    data.addColumn('string', 'Channel');
    data.addColumn('date', 'Start Date');
    data.addColumn('date', 'End Date');
    data.addColumn('number', 'Total Sent');
    data.addColumn('number', 'Responses');
    data.addColumn('string', 'Daily Response Trend');
    data.addColumn('number', 'Response Rate %');
    data.addColumn('number', 'Avg Score');
    data.addColumn('string', 'Score Trend');

    data.addRows([
        [
            'Q1 Customer Satisfaction',
            'Email',
            new Date(2024, 0, 15),
            new Date(2024, 1, 15),
            25000,
            3450,
            createSparklineHTML([45, 120, 180, 210, 195, 165, 140, 120, 95, 80], 'column'),
            13.8,
            8.2,
            createSparklineHTML([7.8, 8.0, 8.1, 8.2, 8.3, 8.2, 8.1, 8.2, 8.3, 8.2], 'line')
        ],
        [
            'Post-Visit NPS',
            'SMS',
            new Date(2024, 1, 1),
            new Date(2024, 11, 31),
            180000,
            28900,
            createSparklineHTML([78, 82, 85, 88, 92, 89, 85, 87, 90, 88], 'column'),
            16.1,
            7.9,
            createSparklineHTML([7.5, 7.6, 7.7, 7.8, 7.9, 8.0, 7.9, 7.8, 7.9, 7.9], 'line')
        ],
        [
            'Annual Health Survey',
            'Phone',
            new Date(2024, 8, 1),
            new Date(2024, 9, 30),
            5000,
            1890,
            createSparklineHTML([25, 45, 65, 85, 95, 88, 82, 78, 65, 45], 'column'),
            37.8,
            8.7,
            createSparklineHTML([8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.7, 8.6, 8.7, 8.7], 'line')
        ]
    ]);

    var options = {
        title: 'Survey Campaign Performance Tracker',
        width: 1400,
        height: 400,
        allowHtml: true
    };

    var chart = new google.visualization.Table(document.getElementById('table_chart'));
    chart.draw(data, options);
}

Advanced Features

Interactive Table with Drill-Down

function drawInteractiveTable() {
    var chart = new google.visualization.Table(document.getElementById('table_chart'));

    google.visualization.events.addListener(chart, 'select', function() {
        var selection = chart.getSelection();
        if (selection.length > 0) {
            var row = selection[0].row;
            var storeId = data.getValue(row, 0);
            showStoreDetails(storeId);
        }
    });

    chart.draw(data, options);
}

function showStoreDetails(storeId) {
    // Load detailed store data
    const detailPanel = document.getElementById('store-details');
    detailPanel.innerHTML = `
        <div class="store-detail">
            <h4>${storeId} - Detailed Analysis</h4>
            <div id="store-detail-charts"></div>
            <button onclick="loadFullReport('${storeId}')">Full Report</button>
        </div>
    `;
    detailPanel.style.display = 'block';

    // Load additional charts for the selected store
    loadStoreCharts(storeId);
}

Custom Formatting and Styling

// Enhanced sparkline with custom styling
function createAdvancedSparkline(values, type, options = {}) {
    const {
        width = 100,
        height = 30,
        color = '#4285f4',
        fillColor = 'rgba(66, 133, 244, 0.1)',
        showPoints = false,
        lineWidth = 2
    } = options;

    const max = Math.max(...values);
    const min = Math.min(...values);
    const range = max - min || 1;

    if (type === 'area') {
        const points = values.map((value, index) => {
            const x = (index / (values.length - 1)) * width;
            const y = height - ((value - min) / range) * height;
            return `${x},${y}`;
        }).join(' ');

        const areaPoints = `0,${height} ${points} ${width},${height}`;

        return `<svg width="${width}" height="${height}">
                    <polygon points="${areaPoints}" fill="${fillColor}" stroke="none"/>
                    <polyline points="${points}" fill="none" stroke="${color}" stroke-width="${lineWidth}"/>
                    ${showPoints ? createSparklinePoints(values, width, height, min, range, color) : ''}
                </svg>`;
    }

    return createSparklineHTML(values, type);
}

function createSparklinePoints(values, width, height, min, range, color) {
    return values.map((value, index) => {
        const x = (index / (values.length - 1)) * width;
        const y = height - ((value - min) / range) * height;
        return `<circle cx="${x}" cy="${y}" r="2" fill="${color}"/>`;
    }).join('');
}

Conditional Formatting

function applyConditionalFormatting(data) {
    // Apply color coding based on values
    for (let row = 0; row < data.getNumberOfRows(); row++) {
        const npsValue = data.getValue(row, 2); // NPS column
        const satisfactionValue = data.getValue(row, 6); // Satisfaction column

        // Color code NPS values
        if (npsValue >= 80) {
            data.setProperty(row, 2, 'style', 'background-color: #d4edda; color: #155724;');
        } else if (npsValue < 60) {
            data.setProperty(row, 2, 'style', 'background-color: #f8d7da; color: #721c24;');
        }

        // Color code satisfaction values
        if (satisfactionValue >= 8.5) {
            data.setProperty(row, 6, 'style', 'background-color: #d4edda; color: #155724;');
        } else if (satisfactionValue < 7.5) {
            data.setProperty(row, 6, 'style', 'background-color: #f8d7da; color: #721c24;');
        }
    }
}

Key Features

  • Tabular Precision - Exact values alongside visual trends
  • Embedded Charts - Sparklines provide context without clutter
  • Sortable Columns - Interactive sorting for data exploration
  • Custom Formatting - Conditional formatting and styling
  • Mixed Data Types - Numbers, text, dates, and visualizations

When to Use

Perfect for: - Executive dashboards - Performance scorecards - Detailed data reports - Comparative analysis - KPI tracking with trends

Avoid when: - Simple data visualization - Large datasets (>100 rows) - Mobile-first interfaces - Print-friendly reports

CSS Styling

.google-visualization-table-table {
    font-family: Arial, sans-serif;
    border-collapse: collapse;
    width: 100%;
}

.table-header {
    background-color: #f8f9fa;
    font-weight: bold;
    border-bottom: 2px solid #dee2e6;
}

.table-row {
    border-bottom: 1px solid #dee2e6;
}

.table-row-even {
    background-color: #f8f9fa;
}

.table-row:hover {
    background-color: #e9ecef;
}

Documentation