LCOV - code coverage report
Current view: top level - dashql-app/src/views/query_result - query_result_details.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 95.2 % 62 59
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : 
       3              : import type { QueryExecutionState } from '../../query/query_execution_state.js';
       4              : import { TabHeader, formatRowCountDetail } from '../../session/tab_header.js';
       5              : import { QueryResultView } from './query_result_view.js';
       6              : import { TableColumnHeader } from './data_table_cell.js';
       7              : import { classNames } from '../../utils/classnames.js';
       8              : import { SET_CROSS_FILTERS } from '../../compute/computation_state.js';
       9              : import { CrossFilters } from '../../compute/cross_filters.js';
      10              : import { useComputationRegistry } from '../../compute/computation_registry.js';
      11              : import { ButtonSize, ButtonVariant, IconButton } from '../../ui/foundations/button.js';
      12              : import { SymbolIcon } from '../../ui/foundations/symbol_icon.js';
      13              : import * as styles from './query_result_details.module.css';
      14              : import { QueryResultToolbar, useQueryResultRowCounts } from './query_result_toolbar.js';
      15              : import { QueryResultDownloadButton } from './query_result_download_button.js';
      16              : import { QueryResultActionMenu } from './query_result_action_menu.js';
      17              : import { formatResultDetail } from './query_result_header.js';
      18              : 
      19              : export interface QueryResultDetailsDependencies {
      20              :     QueryResultToolbar: typeof QueryResultToolbar;
      21              :     QueryResultView: typeof QueryResultView;
      22              :     useComputationRegistry(): [
      23              :         ReturnType<typeof useComputationRegistry>[0],
      24              :         ReturnType<typeof useComputationRegistry>[1],
      25              :     ];
      26              :     useQueryResultRowCounts: typeof useQueryResultRowCounts;
      27              :     QueryResultDownloadButton: typeof QueryResultDownloadButton;
      28              :     QueryResultActionMenu: typeof QueryResultActionMenu;
      29              : }
      30              : 
      31            1 : const DEFAULT_DEPENDENCIES: QueryResultDetailsDependencies = {
      32            1 :     QueryResultToolbar,
      33            1 :     QueryResultView,
      34            1 :     useComputationRegistry: () => {
      35            0 :         const [state, dispatch] = useComputationRegistry();
      36            0 :         return [state, dispatch];
      37            0 :     },
      38            1 :     useQueryResultRowCounts,
      39            1 :     QueryResultDownloadButton,
      40            1 :     QueryResultActionMenu,
      41            1 : };
      42              : 
      43              : interface Props {
      44              :     query: QueryExecutionState;
      45              :     debugMode: boolean;
      46              :     actions?: React.ReactNode;
      47              :     fitHeight?: boolean;
      48              :     maxHeight?: number;
      49              :     columnHeader?: TableColumnHeader;
      50              :     dependencies?: QueryResultDetailsDependencies;
      51              : }
      52              : 
      53            1 : const HEADER_HEIGHT = 32;
      54            1 : const ClearFiltersIcon = SymbolIcon('filter_remove_24');
      55              : 
      56            1 : export const QueryResultDetails: React.FC<Props> = ({ query, debugMode, actions, fitHeight, maxHeight, columnHeader, dependencies = DEFAULT_DEPENDENCIES }) => {
      57            1 :     const searchRows = dependencies.useQueryResultRowCounts(query);
      58            1 :     const [computationState, computationDispatch] = dependencies.useComputationRegistry();
      59            1 :     const QueryResultToolbarComponent = dependencies.QueryResultToolbar;
      60            1 :     const QueryResultViewComponent = dependencies.QueryResultView;
      61            1 :     const QueryResultDownloadButtonComponent = dependencies.QueryResultDownloadButton;
      62            1 :     const QueryResultActionMenuComponent = dependencies.QueryResultActionMenu;
      63            1 :     const tableComputation = computationState.tableComputations[query.queryId] ?? null;
      64            1 :     const hasCrossFilters = tableComputation != null
      65            1 :         && Object.keys(tableComputation.crossFilters.columnFilters).length > 0;
      66            1 :     const clearCrossFilters = React.useCallback(() => {
      67            1 :         computationDispatch({ type: SET_CROSS_FILTERS, value: [query.queryId, new CrossFilters()] });
      68            1 :     }, [computationDispatch, query.queryId]);
      69            1 :     return (
      70            1 :         <div className={classNames(styles.root, { [styles.root_fit_height]: fitHeight })}>
      71            1 :             <TabHeader
      72            1 :                 title="Query Results"
      73            1 :                 detail={formatResultDetail(searchRows.matchingRows == null
      74            1 :                     ? formatRowCountDetail(searchRows.totalRows)
      75            1 :                     : `${searchRows.matchingRows} of ${searchRows.currentRows ?? searchRows.totalRows ?? 0} rows`, query.resultTable)}
      76            1 :                 actions={(
      77            1 :                     <>
      78            1 :                         <QueryResultToolbarComponent query={query} />
      79            1 :                         <IconButton
      80            1 :                             variant={ButtonVariant.Invisible}
      81            1 :                             size={ButtonSize.Small}
      82            1 :                             aria-label="Clear all cross-filters"
      83            1 :                             data-testid="clear-cross-filters"
      84            1 :                             onClick={clearCrossFilters}
      85            1 :                             disabled={!hasCrossFilters}
      86              :                         >
      87            1 :                             <ClearFiltersIcon size={16} />
      88            1 :                         </IconButton>
      89            1 :                         <QueryResultDownloadButtonComponent query={query} />
      90            1 :                         <QueryResultActionMenuComponent query={query} />
      91            1 :                         {actions}
      92            1 :                     </>
      93              :                 )}
      94            1 :             />
      95            1 :             <div className={classNames(styles.body, { [styles.body_fit_height]: fitHeight })}>
      96            1 :                 <QueryResultViewComponent
      97            1 :                     query={query}
      98            1 :                     debugMode={debugMode}
      99            1 :                     compact
     100            1 :                     fitHeight={fitHeight}
     101            1 :                     maxHeight={maxHeight == null ? undefined : Math.max(0, maxHeight - HEADER_HEIGHT)}
     102            1 :                     columnHeader={columnHeader}
     103            1 :                 />
     104            1 :             </div>
     105            1 :         </div>
     106              :     );
     107            1 : };
        

Generated by: LCOV version 2.0-1