import {Fragment, useMemo} from 'react'; import {useSortable} from '@dnd-kit/sortable'; import {CSS} from '@dnd-kit/utilities'; import styled from '@emotion/styled'; import {Button} from 'sentry/components/button'; import type {SelectKey, SelectOption} from 'sentry/components/compactSelect'; import {CompactSelect} from 'sentry/components/compactSelect'; import {Tooltip} from 'sentry/components/tooltip'; import {IconAdd} from 'sentry/icons/iconAdd'; import {IconDelete} from 'sentry/icons/iconDelete'; import {IconGrabbable} from 'sentry/icons/iconGrabbable'; import {t} from 'sentry/locale'; import {defined} from 'sentry/utils'; import { useExploreGroupBys, useExploreMode, useSetExploreGroupBys, } from 'sentry/views/explore/contexts/pageParamsContext'; import {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode'; import {DragNDropContext} from '../contexts/dragNDropContext'; import {useSpanTags} from '../contexts/spanTagsContext'; import type {Column} from '../hooks/useDragNDropColumns'; import { ToolbarHeader, ToolbarHeaderButton, ToolbarLabel, ToolbarRow, ToolbarSection, } from './styles'; interface ToolbarGroupByProps { disabled?: boolean; } export function ToolbarGroupBy({disabled}: ToolbarGroupByProps) { const tags = useSpanTags(); const mode = useExploreMode(); const groupBys = useExploreGroupBys(); const setGroupBys = useSetExploreGroupBys(); const options: SelectOption[] = useMemo(() => { const potentialOptions = [ ...Object.keys(tags), // These options aren't known to exist on this project but it was inserted into // the group bys somehow so it should be a valid options in the group bys. // // One place this may come from is when switching projects/environment/date range, // a tag may disappear based on the selection. ...groupBys.filter(groupBy => groupBy && !tags.hasOwnProperty(groupBy)), ]; potentialOptions.sort(); return [ // hard code in an empty option {label: t('None'), value: '', textValue: t('none')}, ...potentialOptions.map(key => ({ label: key, value: key, textValue: key, })), ]; }, [groupBys, tags]); return ( {({editableColumns, insertColumn, updateColumnAtIndex, deleteColumnAtIndex}) => { let columnEditorRows = ( {editableColumns.map((column, i) => ( 1 || !['', undefined].includes(column.column) } column={column} options={options} onColumnChange={c => updateColumnAtIndex(i, c)} onColumnDelete={() => deleteColumnAtIndex(i)} /> ))} ); if (disabled) { columnEditorRows = ( {columnEditorRows} ); } return ( {t('Group By')} } /> {columnEditorRows} ); }} ); } const FullWidthTooltip = styled(Tooltip)` width: 100%; `; interface ColumnEditorRowProps { canDelete: boolean; column: Column; onColumnChange: (column: string) => void; onColumnDelete: () => void; options: SelectOption[]; disabled?: boolean; } function ColumnEditorRow({ canDelete, column, options, onColumnChange, onColumnDelete, disabled = false, }: ColumnEditorRowProps) { const {attributes, listeners, setNodeRef, transform, transition} = useSortable({ id: column.id, }); function handleColumnChange(option: SelectOption) { if (defined(option) && typeof option.value === 'string') { onColumnChange(option.value); } } const label = useMemo(() => { const tag = options.find(option => option.value === column.column); return {tag?.label ?? t('None')}; }, [column.column, options]); return (