diff --git a/.changeset/table-tile-alternate-row-external-api.md b/.changeset/table-tile-alternate-row-external-api.md new file mode 100644 index 0000000000..a50e32c332 --- /dev/null +++ b/.changeset/table-tile-alternate-row-external-api.md @@ -0,0 +1,7 @@ +--- +"@hyperdx/api": patch +--- + +feat(api): expose table tile alternate row background in the external Dashboards API + +Add the optional `alternateRowBackground` boolean to the external REST Dashboards API for both builder and raw SQL table charts, matching the display setting available in the app. When true, the table tile renders alternating row background colors (zebra striping) for easier scanning on wide tables; it defaults to false. diff --git a/packages/api/openapi.json b/packages/api/openapi.json index 4c076f92fa..8a4343133b 100644 --- a/packages/api/openapi.json +++ b/packages/api/openapi.json @@ -1767,6 +1767,12 @@ "default": false, "example": false }, + "alternateRowBackground": { + "type": "boolean", + "description": "When true, apply alternating row background colors (zebra striping) to improve readability of wide tables. Defaults to false.\n", + "default": false, + "example": false + }, "onClick": { "$ref": "#/components/schemas/OnClick", "description": "Optional link-out configuration applied when a user clicks a row." @@ -2259,6 +2265,12 @@ "description": "Display as a table chart.", "example": "table" }, + "alternateRowBackground": { + "type": "boolean", + "description": "When true, apply alternating row background colors (zebra striping) to improve readability of wide tables. Defaults to false.\n", + "default": false, + "example": false + }, "onClick": { "$ref": "#/components/schemas/OnClick", "description": "Optional link-out configuration applied when a user clicks a row." diff --git a/packages/api/src/routers/external-api/__tests__/dashboards.int.test.ts b/packages/api/src/routers/external-api/__tests__/dashboards.int.test.ts index d1b7323eb4..9cf3178aa8 100644 --- a/packages/api/src/routers/external-api/__tests__/dashboards.int.test.ts +++ b/packages/api/src/routers/external-api/__tests__/dashboards.int.test.ts @@ -2355,6 +2355,7 @@ describe('External API v2 Dashboards - new format', () => { average: true, }, groupByColumnsOnLeft: true, + alternateRowBackground: true, onClick: { type: 'search', target: { @@ -2625,6 +2626,65 @@ describe('External API v2 Dashboards - new format', () => { expect(response.body.data.tiles[0].config).not.toHaveProperty('orderBy'); }); + it('omits alternateRowBackground on a table tile when not provided, and persists explicit false', async () => { + const tableNoStripe: ExternalDashboardTile = { + name: 'Table without stripe setting', + x: 0, + y: 0, + w: 6, + h: 3, + config: { + displayType: 'table', + sourceId: traceSource._id.toString(), + select: [ + { + aggFn: 'count', + alias: 'Count', + where: '', + whereLanguage: 'sql', + }, + ], + groupBy: 'ServiceName', + }, + }; + + const tableStripeOff: ExternalDashboardTile = { + name: 'Table with stripe explicitly off', + x: 6, + y: 0, + w: 6, + h: 3, + config: { + displayType: 'table', + sourceId: traceSource._id.toString(), + select: [ + { + aggFn: 'count', + alias: 'Count', + where: '', + whereLanguage: 'sql', + }, + ], + groupBy: 'ServiceName', + alternateRowBackground: false, + }, + }; + + const response = await authRequest('post', BASE_URL) + .send({ + name: 'Dashboard table stripe defaults', + tiles: [tableNoStripe, tableStripeOff], + }) + .expect(200); + + expect(response.body.data.tiles[0].config).not.toHaveProperty( + 'alternateRowBackground', + ); + expect(response.body.data.tiles[1].config.alternateRowBackground).toBe( + false, + ); + }); + // Schema-level rejections that exercise pure Zod constraints // (discriminated-union absence, `min(1)` on valueExpression, and // `length(1)` on the select array). The non-Trace-source case @@ -2945,6 +3005,7 @@ describe('External API v2 Dashboards - new format', () => { sqlTemplate, sourceId, numberFormat: { output: 'percent', mantissa: 1 }, + alternateRowBackground: true, onClick: { type: 'search', target: { @@ -4080,6 +4141,7 @@ describe('External API v2 Dashboards - new format', () => { average: true, }, groupByColumnsOnLeft: true, + alternateRowBackground: true, onClick: { type: 'search', target: { @@ -4308,6 +4370,7 @@ describe('External API v2 Dashboards - new format', () => { sqlTemplate, sourceId, numberFormat: { output: 'percent', mantissa: 1 }, + alternateRowBackground: true, onClick: { type: 'dashboard', target: { diff --git a/packages/api/src/routers/external-api/v2/dashboards.ts b/packages/api/src/routers/external-api/v2/dashboards.ts index 3bb6749daf..aad46efb38 100644 --- a/packages/api/src/routers/external-api/v2/dashboards.ts +++ b/packages/api/src/routers/external-api/v2/dashboards.ts @@ -732,6 +732,13 @@ const EXTERNAL_DASHBOARD_PROJECTION = { * in the table. Defaults to false (Group By columns on the right). * default: false * example: false + * alternateRowBackground: + * type: boolean + * description: > + * When true, apply alternating row background colors (zebra striping) + * to improve readability of wide tables. Defaults to false. + * default: false + * example: false * onClick: * $ref: '#/components/schemas/OnClick' * description: Optional link-out configuration applied when a user clicks a row. @@ -1139,6 +1146,13 @@ const EXTERNAL_DASHBOARD_PROJECTION = { * enum: [table] * description: Display as a table chart. * example: "table" + * alternateRowBackground: + * type: boolean + * description: > + * When true, apply alternating row background colors (zebra + * striping) to improve readability of wide tables. Defaults to false. + * default: false + * example: false * onClick: * $ref: '#/components/schemas/OnClick' * description: Optional link-out configuration applied when a user clicks a row. diff --git a/packages/api/src/routers/external-api/v2/utils/dashboards.ts b/packages/api/src/routers/external-api/v2/utils/dashboards.ts index 638efbcb1b..5185a8362d 100644 --- a/packages/api/src/routers/external-api/v2/utils/dashboards.ts +++ b/packages/api/src/routers/external-api/v2/utils/dashboards.ts @@ -220,6 +220,7 @@ const convertToExternalTileChartConfig = ( sqlTemplate: config.sqlTemplate, sourceId: config.source, numberFormat: config.numberFormat, + alternateRowBackground: config.alternateRowBackground, onClick: config.onClick, }; case DisplayType.Number: @@ -372,6 +373,7 @@ const convertToExternalTileChartConfig = ( 'having', 'numberFormat', 'groupByColumnsOnLeft', + 'alternateRowBackground', 'onClick', ]), displayType: config.displayType, @@ -666,6 +668,13 @@ export function convertToInternalTileConfig( externalConfig.displayType === 'table' ? externalConfig.onClick : undefined, + // Zebra striping is a table-only presentational flag that lives on + // the shared raw SQL config; only table tiles honor it, mirroring + // onClick above. `_.omitBy(_.isNil)` below drops it for other types. + alternateRowBackground: + externalConfig.displayType === 'table' + ? externalConfig.alternateRowBackground + : undefined, // Only the raw SQL number variant carries `color`; table and pie // do not expose it. `_.omitBy(_.isNil)` below drops it when absent. color: @@ -714,6 +723,7 @@ export function convertToInternalTileConfig( 'having', 'orderBy', 'groupByColumnsOnLeft', + 'alternateRowBackground', 'onClick', ]), displayType: DisplayType.Table, diff --git a/packages/api/src/utils/zod.ts b/packages/api/src/utils/zod.ts index 42f11afeab..865577b48d 100644 --- a/packages/api/src/utils/zod.ts +++ b/packages/api/src/utils/zod.ts @@ -301,12 +301,14 @@ const externalDashboardTableChartConfigSchema = z.object({ asRatio: z.boolean().optional(), numberFormat: NumberFormatSchema.optional(), groupByColumnsOnLeft: z.boolean().optional(), + alternateRowBackground: z.boolean().optional(), onClick: externalOnClickSchema.optional(), }); const externalDashboardTableRawSqlChartConfigSchema = externalDashboardRawSqlChartConfigBaseSchema.extend({ displayType: z.literal('table'), + alternateRowBackground: z.boolean().optional(), onClick: externalOnClickSchema.optional(), });