Cleanup: Remove CSV drag-and-drop snapshot query feature (#113645)
* Chore: Remove editPanelCSVDragAndDrop feature * update i18n * fix issues from PR
This commit is contained in:
@@ -84,10 +84,6 @@ export interface FeatureToggles {
|
||||
*/
|
||||
alertingBacktesting?: boolean;
|
||||
/**
|
||||
* Enables drag and drop for CSV and Excel files
|
||||
*/
|
||||
editPanelCSVDragAndDrop?: boolean;
|
||||
/**
|
||||
* Allow datasource to provide custom UI for context view
|
||||
* @default true
|
||||
*/
|
||||
|
||||
@@ -124,13 +124,6 @@ var (
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaAlertingSquad,
|
||||
},
|
||||
{
|
||||
Name: "editPanelCSVDragAndDrop",
|
||||
Description: "Enables drag and drop for CSV and Excel files",
|
||||
FrontendOnly: true,
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaDatavizSquad,
|
||||
},
|
||||
{
|
||||
Name: "logsContextDatasourceUi",
|
||||
Description: "Allow datasource to provide custom UI for context view",
|
||||
|
||||
@@ -14,7 +14,6 @@ cloudWatchCrossAccountQuerying,GA,@grafana/aws-datasources,false,false,false
|
||||
showDashboardValidationWarnings,experimental,@grafana/dashboards-squad,false,false,false
|
||||
mysqlAnsiQuotes,experimental,@grafana/search-and-storage,false,false,false
|
||||
alertingBacktesting,experimental,@grafana/alerting-squad,false,false,false
|
||||
editPanelCSVDragAndDrop,experimental,@grafana/dataviz-squad,false,false,true
|
||||
logsContextDatasourceUi,GA,@grafana/observability-logs,false,false,true
|
||||
lokiShardSplitting,experimental,@grafana/observability-logs,false,false,true
|
||||
lokiQuerySplitting,GA,@grafana/observability-logs,false,false,true
|
||||
|
||||
|
Generated
-4
@@ -67,10 +67,6 @@ const (
|
||||
// Rule backtesting API for alerting
|
||||
FlagAlertingBacktesting = "alertingBacktesting"
|
||||
|
||||
// FlagEditPanelCSVDragAndDrop
|
||||
// Enables drag and drop for CSV and Excel files
|
||||
FlagEditPanelCSVDragAndDrop = "editPanelCSVDragAndDrop"
|
||||
|
||||
// FlagLogsContextDatasourceUi
|
||||
// Allow datasource to provide custom UI for context view
|
||||
FlagLogsContextDatasourceUi = "logsContextDatasourceUi"
|
||||
|
||||
@@ -1357,6 +1357,7 @@
|
||||
"name": "editPanelCSVDragAndDrop",
|
||||
"resourceVersion": "1762783224740",
|
||||
"creationTimestamp": "2023-01-24T09:43:44Z",
|
||||
"deletionTimestamp": "2025-11-10T16:31:10Z",
|
||||
"annotations": {
|
||||
"grafana.app/updatedTimestamp": "2025-11-10 14:00:24.740459 +0000 UTC"
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Accept } from 'react-dropzone';
|
||||
|
||||
export const acceptedFiles: Accept = {
|
||||
'text/plain': ['.csv', '.txt'],
|
||||
'application/json': ['.json'],
|
||||
};
|
||||
|
||||
//This should probably set from grafana conf
|
||||
export const maxFileSize = 500000;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { queryByTestId, render, screen } from '@testing-library/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import {
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
PluginType,
|
||||
locationUtil,
|
||||
} from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { DataSourceModal, DataSourceModalProps } from './DataSourceModal';
|
||||
|
||||
@@ -106,44 +105,6 @@ describe('DataSourceDropdown', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('only displays the file drop area when the ff is enabled', async () => {
|
||||
const defaultValue = config.featureToggles.editPanelCSVDragAndDrop;
|
||||
config.featureToggles.editPanelCSVDragAndDrop = true;
|
||||
setup({ uploadFile: true });
|
||||
|
||||
expect(await screen.queryByTestId('file-drop-zone-default-children')).toBeInTheDocument();
|
||||
config.featureToggles.editPanelCSVDragAndDrop = defaultValue;
|
||||
});
|
||||
|
||||
it('does not show the file drop area when the ff is disabled', async () => {
|
||||
const defaultValue = config.featureToggles.editPanelCSVDragAndDrop;
|
||||
config.featureToggles.editPanelCSVDragAndDrop = false;
|
||||
|
||||
setup({ uploadFile: true });
|
||||
expect(await screen.queryByTestId('file-drop-zone-default-children')).toBeNull();
|
||||
|
||||
config.featureToggles.editPanelCSVDragAndDrop = defaultValue;
|
||||
});
|
||||
|
||||
it('should not display the drop zone by default', async () => {
|
||||
const defaultValue = config.featureToggles.editPanelCSVDragAndDrop;
|
||||
config.featureToggles.editPanelCSVDragAndDrop = true;
|
||||
|
||||
const component = setup();
|
||||
|
||||
expect(queryByTestId(component.container, 'file-drop-zone-default-children')).toBeNull();
|
||||
config.featureToggles.editPanelCSVDragAndDrop = defaultValue;
|
||||
});
|
||||
|
||||
it('should display the drop zone when uploadFile is enabled', async () => {
|
||||
const defaultValue = config.featureToggles.editPanelCSVDragAndDrop;
|
||||
config.featureToggles.editPanelCSVDragAndDrop = true;
|
||||
setup({ uploadFile: true });
|
||||
|
||||
expect(await screen.queryByTestId('file-drop-zone-default-children')).toBeInTheDocument();
|
||||
config.featureToggles.editPanelCSVDragAndDrop = defaultValue;
|
||||
});
|
||||
|
||||
it('should fetch the DS applying the correct filters consistently across lists', async () => {
|
||||
const filters = {
|
||||
mixed: true,
|
||||
@@ -197,29 +158,6 @@ describe('DataSourceDropdown', () => {
|
||||
expect(await screen.findByText('No data sources found')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
//Skipping this test as it's flaky on drone
|
||||
it.skip('calls the onChange with the default query containing the file', async () => {
|
||||
const user = userEvent.setup();
|
||||
config.featureToggles.editPanelCSVDragAndDrop = true;
|
||||
const onChange = jest.fn();
|
||||
setup({ onChange, uploadFile: true });
|
||||
|
||||
const fileInput = (
|
||||
await screen.queryByTestId('file-drop-zone-default-children')!
|
||||
).parentElement!.parentElement!.querySelector('input');
|
||||
const file = new File([''], 'test.csv', { type: 'text/plain' });
|
||||
expect(fileInput).toBeInTheDocument();
|
||||
await user.upload(fileInput!, file);
|
||||
const defaultQuery = onChange.mock.lastCall[1][0];
|
||||
expect(defaultQuery).toMatchObject({
|
||||
refId: 'A',
|
||||
datasource: { type: 'grafana', uid: 'grafana' },
|
||||
queryType: 'snapshot',
|
||||
file: { path: 'test.csv' },
|
||||
});
|
||||
config.featureToggles.editPanelCSVDragAndDrop = false;
|
||||
});
|
||||
|
||||
it('should call the onChange handler with the correct datasource', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onChange = jest.fn();
|
||||
|
||||
@@ -4,22 +4,12 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { DataSourceInstanceSettings, DataSourceRef, GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { config, reportInteraction, useFavoriteDatasources } from '@grafana/runtime';
|
||||
import { reportInteraction, useFavoriteDatasources } from '@grafana/runtime';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import {
|
||||
Modal,
|
||||
FileDropzone,
|
||||
FileDropzoneDefaultChildren,
|
||||
useStyles2,
|
||||
Input,
|
||||
Icon,
|
||||
ScrollContainer,
|
||||
} from '@grafana/ui';
|
||||
import { acceptedFiles, maxFileSize } from 'app/features/dataframe-import/constants';
|
||||
import { Modal, useStyles2, Input, Icon, ScrollContainer } from '@grafana/ui';
|
||||
import { GrafanaQuery } from 'app/plugins/datasource/grafana/types';
|
||||
import { getFileDropToQueryHandler } from 'app/plugins/datasource/grafana/utils';
|
||||
|
||||
import { useDatasource, useDatasources } from '../../hooks';
|
||||
import { useDatasources } from '../../hooks';
|
||||
|
||||
import { AddNewDataSourceButton } from './AddNewDataSourceButton';
|
||||
import { BuiltInDataSourceList } from './BuiltInDataSourceList';
|
||||
@@ -29,7 +19,6 @@ import { matchDataSourceWithSearch } from './utils';
|
||||
const INTERACTION_EVENT_NAME = 'dashboards_dspickermodal_clicked';
|
||||
const INTERACTION_ITEM = {
|
||||
SELECT_DS: 'select_ds',
|
||||
UPLOAD_FILE: 'upload_file',
|
||||
CONFIG_NEW_DS: 'config_new_ds',
|
||||
CONFIG_NEW_DS_EMPTY_STATE: 'config_new_ds_empty_state',
|
||||
SEARCH: 'search',
|
||||
@@ -56,7 +45,6 @@ export interface DataSourceModalProps {
|
||||
alerting?: boolean;
|
||||
pluginId?: string;
|
||||
logs?: boolean;
|
||||
uploadFile?: boolean;
|
||||
}
|
||||
|
||||
export function DataSourceModal({
|
||||
@@ -70,7 +58,6 @@ export function DataSourceModal({
|
||||
alerting,
|
||||
pluginId,
|
||||
logs,
|
||||
uploadFile,
|
||||
filter,
|
||||
onChange,
|
||||
current,
|
||||
@@ -96,8 +83,6 @@ export function DataSourceModal({
|
||||
});
|
||||
};
|
||||
|
||||
const grafanaDS = useDatasource('-- Grafana --');
|
||||
|
||||
// Get all datasources to report total_configured count
|
||||
const dataSources = useDatasources({
|
||||
tracing,
|
||||
@@ -134,22 +119,6 @@ export function DataSourceModal({
|
||||
[analyticsInteractionSrc]
|
||||
);
|
||||
|
||||
const onFileDrop = getFileDropToQueryHandler((query, fileRejections) => {
|
||||
if (!grafanaDS) {
|
||||
return;
|
||||
}
|
||||
onChange(grafanaDS, [query]);
|
||||
|
||||
reportInteraction(INTERACTION_EVENT_NAME, {
|
||||
item: INTERACTION_ITEM.UPLOAD_FILE,
|
||||
src: analyticsInteractionSrc,
|
||||
});
|
||||
|
||||
if (fileRejections.length < 1) {
|
||||
onDismiss();
|
||||
}
|
||||
});
|
||||
|
||||
// Built-in data sources used twice because of mobile layout adjustments
|
||||
// In movile the list is appended to the bottom of the DS list
|
||||
const BuiltInList = ({ className }: { className?: string }) => {
|
||||
@@ -231,20 +200,6 @@ export function DataSourceModal({
|
||||
<BuiltInList />
|
||||
</ScrollContainer>
|
||||
</div>
|
||||
{uploadFile && config.featureToggles.editPanelCSVDragAndDrop && (
|
||||
<FileDropzone
|
||||
readAs="readAsArrayBuffer"
|
||||
fileListRenderer={() => undefined}
|
||||
options={{
|
||||
maxSize: maxFileSize,
|
||||
multiple: false,
|
||||
accept: acceptedFiles,
|
||||
onDrop: onFileDrop,
|
||||
}}
|
||||
>
|
||||
<FileDropzoneDefaultChildren />
|
||||
</FileDropzone>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.newDSSection}>
|
||||
<span className={styles.newDSDescription}>
|
||||
|
||||
@@ -11,8 +11,6 @@ import {
|
||||
} from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { ModalRoot, ModalsProvider } from '@grafana/ui';
|
||||
import config from 'app/core/config';
|
||||
import { defaultFileUploadQuery } from 'app/plugins/datasource/grafana/types';
|
||||
|
||||
import { DataSourcePicker, DataSourcePickerProps } from './DataSourcePicker';
|
||||
import * as utils from './utils';
|
||||
@@ -290,18 +288,6 @@ describe('DataSourcePicker', () => {
|
||||
expect(screen.getByRole('link')).toHaveAttribute('href', '/my-sub-path/connections/datasources/new');
|
||||
});
|
||||
|
||||
it('should call onChange with the default query when add csv is clicked', async () => {
|
||||
config.featureToggles.editPanelCSVDragAndDrop = true;
|
||||
const onChange = jest.fn();
|
||||
await setupOpenDropdown(user, { onChange, uploadFile: true });
|
||||
|
||||
await user.click(await screen.findByText('Add csv or spreadsheet'));
|
||||
|
||||
expect(onChange.mock.lastCall[1]).toEqual([defaultFileUploadQuery]);
|
||||
expect(screen.queryByText('Open advanced data source picker')).toBeNull(); //Drop down is closed
|
||||
config.featureToggles.editPanelCSVDragAndDrop = false;
|
||||
});
|
||||
|
||||
it('should open the modal when open advanced is clicked', async () => {
|
||||
const props = { onChange: jest.fn(), current: mockDS1.name };
|
||||
render(
|
||||
|
||||
@@ -14,9 +14,8 @@ import { Trans, t } from '@grafana/i18n';
|
||||
import { FavoriteDatasources, reportInteraction, useFavoriteDatasources } from '@grafana/runtime';
|
||||
import { DataQuery, DataSourceJsonData, DataSourceRef } from '@grafana/schema';
|
||||
import { Button, floatingUtils, Icon, Input, ModalsController, Portal, ScrollContainer, useStyles2 } from '@grafana/ui';
|
||||
import config from 'app/core/config';
|
||||
import { useKeyNavigationListener } from 'app/features/search/hooks/useSearchKeyboardSelection';
|
||||
import { defaultFileUploadQuery, GrafanaQuery } from 'app/plugins/datasource/grafana/types';
|
||||
import { GrafanaQuery } from 'app/plugins/datasource/grafana/types';
|
||||
|
||||
import { useDatasource, useDatasources } from '../../hooks';
|
||||
|
||||
@@ -30,7 +29,6 @@ export const INTERACTION_ITEM = {
|
||||
SEARCH: 'search',
|
||||
OPEN_DROPDOWN: 'open_dspicker',
|
||||
SELECT_DS: 'select_ds',
|
||||
ADD_FILE: 'add_file',
|
||||
OPEN_ADVANCED_DS_PICKER: 'open_advanced_ds_picker',
|
||||
CONFIG_NEW_DS_EMPTY_STATE: 'config_new_ds_empty_state',
|
||||
TOGGLE_FAVORITE: 'toggle_favorite',
|
||||
@@ -58,7 +56,6 @@ export interface DataSourcePickerProps {
|
||||
alerting?: boolean;
|
||||
pluginId?: string;
|
||||
logs?: boolean;
|
||||
uploadFile?: boolean;
|
||||
filter?: (ds: DataSourceInstanceSettings) => boolean;
|
||||
}
|
||||
|
||||
@@ -99,7 +96,6 @@ export function DataSourcePicker(props: DataSourcePickerProps) {
|
||||
// Used to move the focus to the footer when tabbing from the input
|
||||
const [footerRef, setFooterRef] = useState<HTMLElement | null>();
|
||||
const currentDataSourceInstanceSettings = useDatasource(current);
|
||||
const grafanaDS = useDatasource('-- Grafana --');
|
||||
const currentValue = Boolean(!current && noDefault) ? undefined : currentDataSourceInstanceSettings;
|
||||
const prefixIcon =
|
||||
filterTerm && isOpen ? <DataSourceLogoPlaceHolder /> : <DataSourceLogo dataSource={currentValue} />;
|
||||
@@ -178,14 +174,6 @@ export function DataSourcePicker(props: DataSourcePickerProps) {
|
||||
markerElement?.focus();
|
||||
}
|
||||
|
||||
function onClickAddCSV() {
|
||||
if (!grafanaDS) {
|
||||
return;
|
||||
}
|
||||
|
||||
onChange(grafanaDS, [defaultFileUploadQuery]);
|
||||
}
|
||||
|
||||
function onKeyDownInput(keyEvent: React.KeyboardEvent<HTMLInputElement>) {
|
||||
// From the input, it navigates to the footer
|
||||
if (keyEvent.key === 'Tab' && !keyEvent.shiftKey && isOpen) {
|
||||
@@ -302,7 +290,6 @@ export function DataSourcePicker(props: DataSourcePickerProps) {
|
||||
}
|
||||
}}
|
||||
onClose={onClose}
|
||||
onClickAddCSV={onClickAddCSV}
|
||||
onDismiss={onClose}
|
||||
onNavigateOutsiteFooter={onNavigateOutsiteFooter}
|
||||
dataSources={dataSources}
|
||||
@@ -335,7 +322,6 @@ function getStylesDropdown(theme: GrafanaTheme2, props: DataSourcePickerProps) {
|
||||
}
|
||||
|
||||
export interface PickerContentProps extends DataSourcePickerProps {
|
||||
onClickAddCSV?: () => void;
|
||||
keyboardEvents: Observable<React.KeyboardEvent>;
|
||||
style: React.CSSProperties;
|
||||
filterTerm?: string;
|
||||
@@ -348,7 +334,7 @@ export interface PickerContentProps extends DataSourcePickerProps {
|
||||
}
|
||||
|
||||
const PickerContent = React.forwardRef<HTMLDivElement, PickerContentProps>((props, ref) => {
|
||||
const { filterTerm, onChange, onClose, onClickAddCSV, current, filter, dataSources, favoriteDataSources } = props;
|
||||
const { filterTerm, onChange, current, filter, dataSources, favoriteDataSources } = props;
|
||||
|
||||
const changeCallback = useCallback(
|
||||
(ds: DataSourceInstanceSettings) => {
|
||||
@@ -357,12 +343,6 @@ const PickerContent = React.forwardRef<HTMLDivElement, PickerContentProps>((prop
|
||||
[onChange]
|
||||
);
|
||||
|
||||
const clickAddCSVCallback = useCallback(() => {
|
||||
onClickAddCSV?.();
|
||||
onClose();
|
||||
reportInteraction(INTERACTION_EVENT_NAME, { item: INTERACTION_ITEM.ADD_FILE });
|
||||
}, [onClickAddCSV, onClose]);
|
||||
|
||||
const styles = useStyles2(getStylesPickerContent);
|
||||
|
||||
return (
|
||||
@@ -385,12 +365,7 @@ const PickerContent = React.forwardRef<HTMLDivElement, PickerContentProps>((prop
|
||||
></DataSourceList>
|
||||
</ScrollContainer>
|
||||
<FocusScope>
|
||||
<Footer
|
||||
{...props}
|
||||
onClickAddCSV={clickAddCSVCallback}
|
||||
onChange={changeCallback}
|
||||
onNavigateOutsiteFooter={props.onNavigateOutsiteFooter}
|
||||
/>
|
||||
<Footer {...props} onChange={changeCallback} onNavigateOutsiteFooter={props.onNavigateOutsiteFooter} />
|
||||
</FocusScope>
|
||||
</div>
|
||||
);
|
||||
@@ -427,20 +402,14 @@ function getStylesPickerContent(theme: GrafanaTheme2) {
|
||||
|
||||
export interface FooterProps extends PickerContentProps {}
|
||||
|
||||
function Footer({ onClose, onChange, onClickAddCSV, ...props }: FooterProps) {
|
||||
function Footer({ onClose, onChange, ...props }: FooterProps) {
|
||||
const styles = useStyles2(getStylesFooter);
|
||||
const isUploadFileEnabled = props.uploadFile && config.featureToggles.editPanelCSVDragAndDrop;
|
||||
|
||||
const onKeyDownLastButton = (e: React.KeyboardEvent<HTMLButtonElement>) => {
|
||||
if (e.key === 'Tab') {
|
||||
props.onNavigateOutsiteFooter(e);
|
||||
}
|
||||
};
|
||||
const onKeyDownFirstButton = (e: React.KeyboardEvent<HTMLButtonElement>) => {
|
||||
if (e.key === 'Tab' && e.shiftKey) {
|
||||
props.onNavigateOutsiteFooter(e);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.footer}>
|
||||
@@ -465,7 +434,6 @@ function Footer({ onClose, onChange, onClickAddCSV, ...props }: FooterProps) {
|
||||
pluginId: props.pluginId,
|
||||
logs: props.logs,
|
||||
filter: props.filter,
|
||||
uploadFile: props.uploadFile,
|
||||
current: props.current,
|
||||
onDismiss: hideModal,
|
||||
onChange: (ds, defaultQueries) => {
|
||||
@@ -477,18 +445,13 @@ function Footer({ onClose, onChange, onClickAddCSV, ...props }: FooterProps) {
|
||||
reportInteraction(INTERACTION_EVENT_NAME, { item: INTERACTION_ITEM.OPEN_ADVANCED_DS_PICKER });
|
||||
}}
|
||||
ref={props.footerRef}
|
||||
onKeyDown={isUploadFileEnabled ? onKeyDownFirstButton : onKeyDownLastButton}
|
||||
onKeyDown={onKeyDownLastButton}
|
||||
>
|
||||
<Trans i18nKey="data-source-picker.open-advanced-button">Open advanced data source picker</Trans>
|
||||
<Icon name="arrow-right" />
|
||||
</Button>
|
||||
)}
|
||||
</ModalsController>
|
||||
{isUploadFileEnabled && (
|
||||
<Button variant="secondary" size="sm" onClick={onClickAddCSV} onKeyDown={onKeyDownLastButton}>
|
||||
<Trans i18nKey="datasources.footer.add-csv-or-spreadsheet">Add csv or spreadsheet</Trans>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -499,7 +499,6 @@ function DataSourcePickerWithPrompt({ options, onChange, ...otherProps }: DataSo
|
||||
dashboard: true,
|
||||
variables: true,
|
||||
current: options.dataSource,
|
||||
uploadFile: true,
|
||||
onChange: async (ds: DataSourceInstanceSettings, defaultQueries?: DataQuery[] | GrafanaQuery[]) => {
|
||||
await onChange(ds, defaultQueries);
|
||||
setIsDataSourceModalOpen(false);
|
||||
|
||||
@@ -1,39 +1,20 @@
|
||||
import { css } from '@emotion/css';
|
||||
import pluralize from 'pluralize';
|
||||
import { PureComponent } from 'react';
|
||||
import * as React from 'react';
|
||||
import { DropEvent, FileRejection } from 'react-dropzone';
|
||||
|
||||
import {
|
||||
QueryEditorProps,
|
||||
SelectableValue,
|
||||
rangeUtil,
|
||||
DataQueryRequest,
|
||||
DataFrameJSON,
|
||||
dataFrameToJSON,
|
||||
GrafanaTheme2,
|
||||
getValueFormat,
|
||||
formattedValueToString,
|
||||
Field,
|
||||
} from '@grafana/data';
|
||||
import { config, getDataSourceSrv, reportInteraction } from '@grafana/runtime';
|
||||
import { QueryEditorProps, SelectableValue, rangeUtil, DataQueryRequest, Field } from '@grafana/data';
|
||||
import { config, getDataSourceSrv } from '@grafana/runtime';
|
||||
import {
|
||||
InlineField,
|
||||
Select,
|
||||
Alert,
|
||||
Input,
|
||||
InlineFieldRow,
|
||||
InlineLabel,
|
||||
FileDropzone,
|
||||
FileDropzoneDefaultChildren,
|
||||
DropzoneFile,
|
||||
Themeable2,
|
||||
withTheme2,
|
||||
Stack,
|
||||
InlineLabel,
|
||||
} from '@grafana/ui';
|
||||
import { hasAlphaPanels } from 'app/core/config';
|
||||
import { acceptedFiles, maxFileSize } from 'app/features/dataframe-import/constants';
|
||||
import { filesToDataframes } from 'app/features/dataframe-import/utils';
|
||||
import { getManagedChannelInfo } from 'app/features/live/info';
|
||||
import { SearchQuery } from 'app/features/search/service/types';
|
||||
|
||||
@@ -53,7 +34,7 @@ interface State {
|
||||
folders?: Array<SelectableValue<string>>;
|
||||
}
|
||||
|
||||
export class UnthemedQueryEditor extends PureComponent<Props, State> {
|
||||
export class UnthemedQueryEditor extends React.PureComponent<Props, State> {
|
||||
state: State = { channels: [], channelFields: {} };
|
||||
|
||||
queryTypes: Array<SelectableValue<GrafanaQueryType>> = [
|
||||
@@ -91,13 +72,6 @@ export class UnthemedQueryEditor extends PureComponent<Props, State> {
|
||||
description: 'Search for grafana resources',
|
||||
});
|
||||
}
|
||||
if (config.featureToggles.editPanelCSVDragAndDrop) {
|
||||
this.queryTypes.push({
|
||||
label: 'Spreadsheet or snapshot',
|
||||
value: GrafanaQueryType.Snapshot,
|
||||
description: 'Query an uploaded spreadsheet or a snapshot',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
loadChannelInfo() {
|
||||
@@ -356,43 +330,8 @@ export class UnthemedQueryEditor extends PureComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
// Skip rendering the file list as we're handling that in this component instead.
|
||||
fileListRenderer = (file: DropzoneFile, removeFile: (file: DropzoneFile) => void) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
onFileDrop = (acceptedFiles: File[], fileRejections: FileRejection[], event: DropEvent) => {
|
||||
filesToDataframes(acceptedFiles).subscribe((next) => {
|
||||
const snapshot: DataFrameJSON[] = [];
|
||||
next.dataFrames.forEach((df) => {
|
||||
const dataframeJson = dataFrameToJSON(df);
|
||||
snapshot.push(dataframeJson);
|
||||
});
|
||||
this.props.onChange({
|
||||
...this.props.query,
|
||||
file: { name: next.file.name, size: next.file.size },
|
||||
queryType: GrafanaQueryType.Snapshot,
|
||||
snapshot,
|
||||
});
|
||||
this.props.onRunQuery();
|
||||
|
||||
reportInteraction('grafana_datasource_drop_files', {
|
||||
number_of_files: fileRejections.length + acceptedFiles.length,
|
||||
accepted_files: acceptedFiles.map((a) => {
|
||||
return { type: a.type, size: a.size };
|
||||
}),
|
||||
rejected_files: fileRejections.map((r) => {
|
||||
return { type: r.file.type, size: r.file.size };
|
||||
}),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
renderSnapshotQuery() {
|
||||
const { query, theme } = this.props;
|
||||
const file = query.file;
|
||||
const styles = getStyles(theme);
|
||||
const fileSize = getValueFormat('decbytes')(file ? file.size : 0);
|
||||
const { query } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -401,32 +340,6 @@ export class UnthemedQueryEditor extends PureComponent<Props, State> {
|
||||
<InlineLabel>{pluralize('frame', query.snapshot?.length ?? 0, true)}</InlineLabel>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
{config.featureToggles.editPanelCSVDragAndDrop && (
|
||||
<>
|
||||
<FileDropzone
|
||||
readAs="readAsArrayBuffer"
|
||||
fileListRenderer={this.fileListRenderer}
|
||||
options={{
|
||||
onDrop: this.onFileDrop,
|
||||
maxSize: maxFileSize,
|
||||
multiple: false,
|
||||
accept: acceptedFiles,
|
||||
}}
|
||||
>
|
||||
<FileDropzoneDefaultChildren
|
||||
primaryText={this.props?.query?.file ? 'Replace file' : 'Drop file here or click to upload'}
|
||||
/>
|
||||
</FileDropzone>
|
||||
{file && (
|
||||
<div className={styles.file}>
|
||||
<span>{file?.name}</span>
|
||||
<span>
|
||||
<span>{formattedValueToString(fileSize)}</span>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -466,7 +379,7 @@ export class UnthemedQueryEditor extends PureComponent<Props, State> {
|
||||
|
||||
// Only show "snapshot" when it already exists
|
||||
let queryTypes = this.queryTypes;
|
||||
if (queryType === GrafanaQueryType.Snapshot && !config.featureToggles.editPanelCSVDragAndDrop) {
|
||||
if (queryType === GrafanaQueryType.Snapshot) {
|
||||
queryTypes = [
|
||||
...this.queryTypes,
|
||||
{
|
||||
@@ -511,19 +424,3 @@ export class UnthemedQueryEditor extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
export const QueryEditor = withTheme2(UnthemedQueryEditor);
|
||||
|
||||
function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
file: css({
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: theme.spacing(2),
|
||||
border: `1px dashed ${theme.colors.border.medium}`,
|
||||
backgroundColor: theme.colors.background.secondary,
|
||||
marginTop: theme.spacing(1),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,16 +53,6 @@ export const defaultQuery: GrafanaQuery = {
|
||||
queryType: GrafanaQueryType.RandomWalk,
|
||||
};
|
||||
|
||||
export const defaultFileUploadQuery: GrafanaQuery = {
|
||||
refId: 'A',
|
||||
datasource: {
|
||||
type: 'grafana',
|
||||
uid: 'grafana',
|
||||
},
|
||||
queryType: GrafanaQueryType.Snapshot,
|
||||
snapshot: [],
|
||||
};
|
||||
|
||||
//----------------------------------------------
|
||||
// Annotations
|
||||
//----------------------------------------------
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { DropEvent, FileRejection } from 'react-dropzone';
|
||||
|
||||
import { DataFrame, DataFrameJSON, dataFrameToJSON } from '@grafana/data';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { GRAFANA_DATASOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
|
||||
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
|
||||
import { filesToDataframes } from 'app/features/dataframe-import/utils';
|
||||
import { ShowConfirmModalEvent } from 'app/types/events';
|
||||
|
||||
import { defaultFileUploadQuery, GrafanaQuery, GrafanaQueryType } from './types';
|
||||
import { GrafanaQuery, GrafanaQueryType } from './types';
|
||||
|
||||
/**
|
||||
* Will show a confirm modal if the current panel does not have a snapshot query.
|
||||
@@ -57,18 +54,3 @@ function updateSnapshotData(frames: DataFrame[], panel: PanelModel) {
|
||||
|
||||
panel.refresh();
|
||||
}
|
||||
|
||||
export function getFileDropToQueryHandler(
|
||||
onFileLoaded: (query: GrafanaQuery, fileRejections: FileRejection[]) => void
|
||||
) {
|
||||
return (acceptedFiles: File[], fileRejections: FileRejection[], event: DropEvent) => {
|
||||
filesToDataframes(acceptedFiles).subscribe(async (next) => {
|
||||
const snapshot: DataFrameJSON[] = [];
|
||||
next.dataFrames.forEach((df: DataFrame) => {
|
||||
const dataframeJson = dataFrameToJSON(df);
|
||||
snapshot.push(dataframeJson);
|
||||
});
|
||||
onFileLoaded({ ...defaultFileUploadQuery, ...{ snapshot: snapshot, file: next.file } }, fileRejections);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6750,9 +6750,6 @@
|
||||
"error-details-link": {
|
||||
"aria-label-more-details-about-the-error": "More details about the error"
|
||||
},
|
||||
"footer": {
|
||||
"add-csv-or-spreadsheet": "Add csv or spreadsheet"
|
||||
},
|
||||
"get-enterprise-phantom-plugins": {
|
||||
"description": {
|
||||
"adobe-analytics-datasource": "Adobe Analytics datasource",
|
||||
|
||||
Reference in New Issue
Block a user