LCOV - code coverage report
Current view: top level - dashql-app/src/session/chart_builder - chart_select.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 100.0 % 50 50
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : 
       3              : import * as ActionList from '../../ui/foundations/action_list.js';
       4              : import { AnchorAlignment, AnchorSide } from '../../ui/foundations/anchored_position.js';
       5              : import { AnchoredOverlay } from '../../ui/foundations/anchored_overlay.js';
       6              : import { Button, ButtonSize, ButtonVariant } from '../../ui/foundations/button.js';
       7              : import { TriangleDownIcon } from '../../ui/foundations/symbol_icon.js';
       8              : import * as styles from './chart_builder.module.css';
       9              : 
      10              : export interface ChartSelectOption {
      11              :     value: string;
      12              :     label: string;
      13              :     description?: string;
      14              : }
      15              : 
      16              : interface Props {
      17              :     /// The accessible name of the trigger
      18              :     'aria-label': string;
      19              :     'data-testid'?: string;
      20              :     /// The picked value, null shows the placeholder
      21              :     value: string | null;
      22              :     /// The label of the trigger when nothing is picked
      23              :     placeholder: string;
      24              :     options: readonly ChartSelectOption[];
      25              :     onChange: (value: string) => void;
      26              :     /// The trigger shows a label that differs from the label of the option, e.g. `sum(revenue)`
      27              :     triggerLabel?: string;
      28              :     variant?: ButtonVariant;
      29              :     className?: string;
      30              :     disabled?: boolean;
      31              : }
      32              : 
      33              : /// A dropdown of the chart builder: a button that opens a list of options
      34            1 : export function ChartSelect(props: Props): React.ReactElement {
      35            1 :     const [open, setOpen] = React.useState(false);
      36            1 :     const triggerRef = React.useRef<HTMLButtonElement>(null);
      37            1 :     const picked = props.options.find(option => option.value === props.value);
      38            1 :     const label = props.triggerLabel ?? picked?.label ?? props.value ?? props.placeholder;
      39            1 :     return (
      40            1 :         <AnchoredOverlay
      41            1 :             open={open}
      42            1 :             onOpen={() => setOpen(true)}
      43            1 :             onClose={() => setOpen(false)}
      44            1 :             side={AnchorSide.OutsideBottom}
      45            1 :             align={AnchorAlignment.Start}
      46            1 :             anchorOffset={4}
      47            1 :             anchorRef={triggerRef}
      48            1 :             returnFocusRef={triggerRef}
      49            1 :             renderAnchor={anchorProps => (
      50            1 :                 <Button
      51            1 :                     {...anchorProps}
      52            1 :                     ref={triggerRef}
      53            1 :                     className={props.className}
      54            1 :                     variant={props.variant ?? ButtonVariant.Default}
      55            1 :                     size={ButtonSize.Small}
      56            1 :                     disabled={props.disabled}
      57            1 :                     trailingVisual={TriangleDownIcon}
      58            1 :                     aria-label={`${props['aria-label']}: ${label}`}
      59            1 :                     data-testid={props['data-testid']}
      60            1 :                     data-placeholder={props.value == null ? '' : undefined}
      61            1 :                     title={label}
      62              :                 >
      63            1 :                     <span className={styles.select_label}>{label}</span>
      64            1 :                 </Button>
      65              :             )}
      66              :         >
      67            1 :             <ActionList.List className={styles.select_list} aria-label={props['aria-label']} role="listbox">
      68            1 :                 {props.options.map(option => (
      69            1 :                     <ActionList.ListItem
      70            1 :                         key={option.value}
      71            1 :                         role="option"
      72            1 :                         selected={option.value === props.value}
      73            1 :                         data-item={option.value}
      74            1 :                         onClick={() => {
      75            1 :                             setOpen(false);
      76            1 :                             if (option.value !== props.value) props.onChange(option.value);
      77            1 :                         }}
      78              :                     >
      79            1 :                         <ActionList.ItemText>
      80            1 :                             <ActionList.ItemTextTitle>{option.label}</ActionList.ItemTextTitle>
      81            1 :                             {option.description != null ? <ActionList.ItemTextDescription>{option.description}</ActionList.ItemTextDescription> : undefined}
      82            1 :                         </ActionList.ItemText>
      83            1 :                     </ActionList.ListItem>
      84            1 :                 ))}
      85            1 :             </ActionList.List>
      86            1 :         </AnchoredOverlay>
      87              :     );
      88            1 : }
        

Generated by: LCOV version 2.0-1