// Copyright 2022 The Parca Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import {useRef, useState} from 'react'; import {Switch} from '@headlessui/react'; import {RpcError} from '@protobuf-ts/runtime-rpc'; import {type SelectInstance} from 'react-select'; import {ProfileTypesResponse, QueryServiceClient} from '@parca/client'; import {Button, DateTimeRange, DateTimeRangePicker, useParcaContext} from '@parca/components'; import {ProfileType, Query} from '@parca/parser'; import {TEST_IDS, testId} from '@parca/test-utils'; import MatchersInput from '../MatchersInput'; import PreSelectedMatchers from '../PreSelectedMatchers'; import {QuerySelection} from '../ProfileSelector'; import ProfileTypeSelector from '../ProfileTypeSelector'; import {SelectWithRefresh} from '../SelectWithRefresh'; import SimpleMatchers from '../SimpleMatchers/'; import {useLabelNames} from '../hooks/useLabels'; export interface ExternalProfilerComponentProps { disableProfileTypesDropdown?: boolean; defaultProfileType?: string; configuredLabelNames?: string[]; disableExplorativeQuerying?: boolean; profileFilterDefaults?: unknown[]; } interface SelectOption { label: string; value: string; } interface QueryControlsProps { queryClient: QueryServiceClient; query: Query; profileType: string | ProfileType; timeRangeSelection: DateTimeRange; setTimeRangeSelection: (range: DateTimeRange) => void; setMatchersString: (matchers: string) => void; setQueryExpression: (updateTs?: boolean) => void; searchDisabled: boolean; showProfileTypeSelector?: boolean; showSumBySelector?: boolean; showAdvancedMode?: boolean; disableExplorativeQuerying?: boolean; profileTypesData?: ProfileTypesResponse; profileTypesLoading?: boolean; selectedProfileName?: string; setProfileName?: (name: string | undefined) => void; profileTypesError?: RpcError; externalProfilerComponent?: ExternalProfilerComponentProps; setQueryBrowserMode?: (mode: string) => void; advancedModeForQueryBrowser?: boolean; setAdvancedModeForQueryBrowser?: (mode: boolean) => void; queryBrowserRef?: React.RefObject; labels?: string[]; sumBySelection?: string[]; sumBySelectionLoading?: boolean; setUserSumBySelection?: (sumBy: string[]) => void; sumByRef?: React.RefObject; draftSelection: QuerySelection; setDraftMatchers: (selection: string) => void; draftParsedQuery: Query | null; commitDraft: () => void; } export function QueryControls({ profileType, timeRangeSelection, setTimeRangeSelection, setQueryExpression, searchDisabled, showProfileTypeSelector = false, showSumBySelector = false, showAdvancedMode = true, profileTypesData, profileTypesLoading = false, selectedProfileName, setProfileName, profileTypesError, externalProfilerComponent, setQueryBrowserMode, advancedModeForQueryBrowser = false, setAdvancedModeForQueryBrowser, queryBrowserRef, labels = [], sumBySelection = [], sumBySelectionLoading = false, setUserSumBySelection, sumByRef, queryClient, draftSelection, setDraftMatchers, draftParsedQuery, commitDraft, }: QueryControlsProps): JSX.Element { const {timezone} = useParcaContext(); const defaultQueryBrowserRef = useRef(null); const actualQueryBrowserRef = queryBrowserRef ?? defaultQueryBrowserRef; const [searchExecutedTimestamp, setSearchExecutedTimestamp] = useState(0); const {refetch: refetchLabelNames} = useLabelNames( queryClient, profileType as string, timeRangeSelection.getFromMs(), timeRangeSelection.getToMs() ); return (
{showProfileTypeSelector && (
{})} error={profileTypesError} disabled={externalProfilerComponent?.disableProfileTypesDropdown} />
)}
{showAdvancedMode && externalProfilerComponent?.disableExplorativeQuerying !== true && ( <> { setAdvancedModeForQueryBrowser?.(!advancedModeForQueryBrowser); setQueryBrowserMode?.(advancedModeForQueryBrowser ? 'simple' : 'advanced'); }} className={`${ advancedModeForQueryBrowser ? 'bg-indigo-600' : 'bg-gray-400 dark:bg-gray-800' } relative inline-flex h-[20px] w-[44px] shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75`} {...testId(TEST_IDS.ADVANCED_MODE_SWITCH)} > Use setting )}
{externalProfilerComponent?.configuredLabelNames !== undefined && externalProfilerComponent?.configuredLabelNames.length >= 1 ? ( ) : showAdvancedMode && advancedModeForQueryBrowser ? ( ) : ( )}
{showSumBySelector && (
id="h-sum-by-selector" data-testid={testId(TEST_IDS.SUM_BY_SELECT)['data-testid']} defaultValue={[]} isMulti isClearable={false} name="colors" options={labels.map(label => ({label, value: label}))} className="parca-select-container text-sm w-full max-w-80" classNamePrefix="parca-select" value={sumBySelection.map(sumBy => ({label: sumBy, value: sumBy}))} onChange={newValue => { setUserSumBySelection?.(newValue.map(option => option.value)); }} placeholder="Labels..." styles={{ indicatorSeparator: () => ({display: 'none'}), menu: provided => ({ ...provided, marginBottom: 0, boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', marginTop: 10, zIndex: 50, minWidth: '320px', position: 'absolute', }), }} isLoading={sumBySelectionLoading} isDisabled={!(profileType as ProfileType)?.delta} // @ts-expect-error ref={sumByRef} onKeyDown={e => { const currentRef = sumByRef?.current as unknown as SelectInstance | null; if (currentRef == null) { return; } const inputRef = currentRef.inputRef; if (inputRef == null) { return; } if ( e.key === 'Enter' && inputRef.value === '' && currentRef.state.focusedOptionId === null // menu is not open ) { setQueryExpression(true); currentRef.blur(); } }} onRefresh={refetchLabelNames} refreshTitle="Refresh label names" refreshTestId="sum-by-refresh-button" menuTestId={TEST_IDS.SUM_BY_SELECT_FLYOUT} />
)}
); }