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

            Line data    Source code
       1              : import * as React from 'react';
       2              : import symbols from '@ankoh/dashql-svg-symbols';
       3              : 
       4              : import * as ActionList from '../ui/foundations/action_list.js';
       5              : import { Button, ButtonSize, ButtonVariant } from '../ui/foundations/button.js';
       6              : import { ConnectionHealth, type ConnectionState } from './connection_state.js';
       7              : import { CONNECTION_HEALTH_COLORS } from './connection_status.js';
       8              : import { connectionLabelText, type ConnectionLabelParts } from './connection_label.js';
       9              : import { ConnectionLabelView } from './connection_label_view.js';
      10              : import type { ConnectionReplaceCandidates } from './connection_replace.js';
      11              : import * as styles from './connection_replace_picker.module.css';
      12              : import { CONNECTOR_ICON_STYLE, useConnectorIcon } from './connector_icon.js';
      13              : 
      14              : interface Props {
      15              :     /// The connection that is replaced
      16              :     source: ConnectionState;
      17              :     /// The connections that can replace it
      18              :     candidates: ConnectionReplaceCandidates;
      19              :     /// Label a connection
      20              :     labelOf: (database: ConnectionState) => ConnectionLabelParts;
      21              :     /// Go back without replacing
      22              :     onCancel: () => void;
      23              :     /// Replace the connection with the picked one
      24              :     onConfirm: (targetId: string) => void;
      25              : }
      26              : 
      27              : /// Pick the connection that replaces a connection, the connections of the same connector first
      28            1 : export const ConnectionReplacePicker: React.FC<Props> = ({ source, candidates, labelOf, onCancel, onConfirm }) => {
      29            1 :     const connectorIcon = useConnectorIcon();
      30            1 :     const [selected, setSelected] = React.useState<string | null>(null);
      31            1 :     const sourceLabel = connectionLabelText(labelOf(source));
      32            1 :     const renderItem = (connection: ConnectionState) => (
      33            1 :         <ActionList.ListItem
      34            1 :             key={connection.connectionId}
      35            1 :             data-testid="connection-replace-option"
      36            1 :             data-connection-id={connection.connectionId}
      37            1 :             selected={connection.connectionId === selected}
      38            1 :             onClick={() => setSelected(connection.connectionId)}
      39            1 :             onDoubleClick={() => onConfirm(connection.connectionId)}
      40              :         >
      41            1 :             <ActionList.Leading>
      42            1 :                 <svg className={styles.connector_icon} width="16px" height="16px" aria-hidden="true">
      43            1 :                     <use xlinkHref={`${symbols}#${connectorIcon(connection.connectorInfo)}`} style={CONNECTOR_ICON_STYLE} />
      44            1 :                 </svg>
      45            1 :             </ActionList.Leading>
      46            1 :             <ActionList.ItemText>
      47            1 :                 <ActionList.ItemTextTitle>
      48            1 :                     <ConnectionLabelView parts={labelOf(connection)} />
      49            1 :                 </ActionList.ItemTextTitle>
      50            1 :             </ActionList.ItemText>
      51            1 :             <ActionList.Trailing>
      52            1 :                 <svg width="16px" height="16px" viewBox="0 0 16 16" aria-hidden="true">
      53            1 :                     <circle cx="8" cy="8" r="4" style={{ fill: CONNECTION_HEALTH_COLORS[connection.connectionHealth] ?? CONNECTION_HEALTH_COLORS[ConnectionHealth.NOT_STARTED] }} />
      54            1 :                 </svg>
      55            1 :             </ActionList.Trailing>
      56            1 :         </ActionList.ListItem>
      57              :     );
      58            1 :     return (
      59            1 :         <div className={styles.picker} role="dialog" aria-label={`Replace ${sourceLabel}`} data-testid="connection-replace-picker">
      60            1 :             <div className={styles.header}>
      61            1 :                 <span className={styles.title}>Replace with</span>
      62            1 :                 <span className={styles.subtitle}>{sourceLabel}</span>
      63            1 :             </div>
      64            1 :             <ActionList.List className={styles.list} aria-label={`Connections that replace ${sourceLabel}`}>
      65              :                 {/* The list takes elements only, the fragments wrap the optional groups */}
      66            1 :                 <>{candidates.sameConnector.length > 0 && (
      67            1 :                     <>
      68            1 :                         <ActionList.GroupHeading>{source.connectorInfo.names.displayShort}</ActionList.GroupHeading>
      69            1 :                         {candidates.sameConnector.map(renderItem)}
      70            1 :                     </>
      71            1 :                 )}</>
      72            1 :                 <>{candidates.otherConnectors.length > 0 && (
      73            1 :                     <>
      74            1 :                         <ActionList.GroupHeading>Other connectors</ActionList.GroupHeading>
      75            1 :                         {candidates.otherConnectors.map(renderItem)}
      76            1 :                     </>
      77            1 :                 )}</>
      78            1 :             </ActionList.List>
      79            1 :             <div className={styles.footer}>
      80            1 :                 <span className={styles.note}>The sessions move to the picked connection, then this one is removed.</span>
      81            1 :                 <div className={styles.actions}>
      82            1 :                     <Button size={ButtonSize.Small} onClick={onCancel} data-testid="connection-replace-cancel">Cancel</Button>
      83            1 :                     <Button
      84            1 :                         size={ButtonSize.Small}
      85            1 :                         variant={ButtonVariant.Primary}
      86            1 :                         disabled={selected == null}
      87            1 :                         onClick={() => { if (selected != null) onConfirm(selected); }}
      88            1 :                         data-testid="connection-replace-confirm"
      89            1 :                     >
      90              :                         Replace
      91            1 :                     </Button>
      92            1 :                 </div>
      93            1 :             </div>
      94            1 :         </div>
      95              :     );
      96            1 : };
        

Generated by: LCOV version 2.0-1