LCOV - code coverage report
Current view: top level - dashql-app/src/session - tab_header.tsx Coverage Total Hit
Test: DashQL 6e74948 Lines: 100.0 % 44 44
Test Date: 2026-11-06 17:46:50

            Line data    Source code
       1              : import * as React from 'react';
       2              : import * as styles from './tab_header.module.css';
       3              : 
       4              : import { QueryExecutionState, QueryExecutionStatus } from '../query/query_execution_state.js';
       5              : import { ButtonSize, ButtonVariant, IconButton } from '../ui/foundations/button.js';
       6              : import { ScreenFullIcon } from '../ui/foundations/symbol_icon.js';
       7              : import { classNames } from '../utils/classnames.js';
       8              : import { useComputationRegistry } from '../compute/computation_registry.js';
       9              : 
      10              : interface TabHeaderProps {
      11              :     title: string;
      12              :     detail?: string | null;
      13              :     /// When set, the header becomes clickable (the feed footer uses this to open the full result table)
      14              :     /// and ends with an expand button doing the same. Omitted elsewhere, where the header is a plain,
      15              :     /// non-clickable label + count.
      16              :     onClick?: () => void;
      17              :     /// Render the count as a link, for previews that only show part of the rows (clickable variant).
      18              :     detailIsLink?: boolean;
      19              :     /// Optional right-aligned content (e.g. a "delete cached" button). Rendered on both variants; on
      20              :     /// the clickable variant the title/count sit in an inner button while the actions sit beside it,
      21              :     /// since nesting interactive controls inside a button is invalid.
      22              :     actions?: React.ReactNode;
      23              : }
      24              : 
      25              : /// The header bar shown atop a session tab body: a title with an optional greyish "N of M rows"
      26              : /// count. Shared by the feed footer (clickable, opens the full result table) and the full views (inert),
      27              : /// so the surfaces read identically.
      28            1 : export const TabHeader: React.FC<TabHeaderProps> = ({ title, detail, onClick, detailIsLink, actions }) => {
      29            1 :     if (onClick != null) {
      30            1 :         return (
      31            1 :             <div className={styles.tab_header}>
      32            1 :                 <button type="button" className={styles.tab_header_clickable_inner} onClick={onClick}>
      33            1 :                     <span className={styles.tab_header_title}>{title}</span>
      34            1 :                     {detail && (
      35            1 :                         <span className={classNames(styles.tab_header_detail, { [styles.tab_header_detail_link]: detailIsLink })}>
      36            1 :                             {detail}
      37            1 :                         </span>
      38              :                     )}
      39            1 :                 </button>
      40            1 :                 <div className={styles.tab_header_actions}>
      41            1 :                     {actions}
      42            1 :                     <IconButton
      43            1 :                         variant={ButtonVariant.Invisible}
      44            1 :                         size={ButtonSize.Small}
      45            1 :                         aria-label={`Expand ${title}`}
      46            1 :                         data-testid="tab-header-expand"
      47            1 :                         onClick={onClick}
      48              :                     >
      49            1 :                         <ScreenFullIcon size={16} />
      50            1 :                     </IconButton>
      51            1 :                 </div>
      52            1 :             </div>
      53              :         );
      54              :     }
      55            1 :     return (
      56            1 :         <div className={styles.tab_header}>
      57            1 :             <span className={styles.tab_header_title}>{title}</span>
      58            1 :             {detail && <span className={styles.tab_header_detail}>{detail}</span>}
      59            1 :             {actions && <div className={styles.tab_header_actions}>{actions}</div>}
      60            1 :         </div>
      61              :     );
      62            1 : };
      63              : 
      64              : /// Whether the query produced a materialized result, and its total row count. Shared so the footer's
      65              : /// preview and the Details tabs derive the "N rows" detail identically.
      66            1 : export function useResultRowCount(queryState: QueryExecutionState | null): { hasResult: boolean; totalRows: number | null } {
      67            1 :     const computationState = useComputationRegistry()?.[0];
      68            1 :     const hasResult = queryState != null
      69            1 :         && queryState.status === QueryExecutionStatus.SUCCEEDED
      70            1 :         && computationState?.tableComputations[queryState.queryId] != null;
      71            1 :     const totalRows = hasResult
      72            1 :         ? (computationState?.tableComputations[queryState!.queryId]?.dataTable.numRows ?? null)
      73            1 :         : null;
      74            1 :     return { hasResult, totalRows };
      75            1 : }
      76              : 
      77              : /// Format a plain "N rows" / "N row" detail string from a total (null when the total is unknown).
      78            1 : export function formatRowCountDetail(totalRows: number | null): string | null {
      79            1 :     if (totalRows == null) return null;
      80            1 :     return `${totalRows} ${totalRows === 1 ? 'row' : 'rows'}`;
      81            1 : }
        

Generated by: LCOV version 2.0-1