remove editable fields from page Header - name, default datasource switch
This commit is contained in:
@@ -18,7 +18,6 @@ export const Pages = {
|
||||
},
|
||||
DataSource: {
|
||||
name: 'data-testid Data source settings page name input field',
|
||||
nameEditIcon: 'data-testid Data source settings page name edit icon button',
|
||||
delete: 'Data source settings page Delete button',
|
||||
readOnly: 'Data source settings page read only message',
|
||||
saveAndTest: 'data-testid Data source settings page Save and Test button',
|
||||
|
||||
@@ -57,7 +57,6 @@ export const addDataSource = (config?: Partial<AddDataSourceConfig>) => {
|
||||
.should('be.visible') // prevents flakiness
|
||||
.click();
|
||||
|
||||
e2e.pages.DataSource.nameEditIcon().click();
|
||||
e2e.pages.DataSource.name().clear();
|
||||
e2e.pages.DataSource.name().type(name);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface PageProps extends HTMLAttributes<HTMLDivElement> {
|
||||
}
|
||||
|
||||
export interface PageInfoItem {
|
||||
label: string | React.ReactNode;
|
||||
label: string;
|
||||
value: React.ReactNode;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { DataSourceSettings, GrafanaTheme2 } from '@grafana/data';
|
||||
import { InlineSwitch, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
|
||||
interface Props {
|
||||
dataSource: DataSourceSettings;
|
||||
isDefault: boolean;
|
||||
readOnly: boolean;
|
||||
onUpdate: (dataSource: DataSourceSettings) => Promise<DataSourceSettings>;
|
||||
}
|
||||
|
||||
export function DataSourceDefaultSwitch({ dataSource, isDefault, readOnly, onUpdate }: Props) {
|
||||
const [initialDataSource, setInitialDataSource] = useState<DataSourceSettings>(dataSource);
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const handleDefaultDataSourceChange = async (checked: boolean) => {
|
||||
try {
|
||||
await onUpdate({ ...initialDataSource, isDefault: checked });
|
||||
} catch (err) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// update this to read only initial dataSource load
|
||||
// currently it picks up dataSource updates from form
|
||||
useEffect(() => {
|
||||
setInitialDataSource(dataSource);
|
||||
}, [dataSource]);
|
||||
|
||||
if (readOnly) {
|
||||
return (
|
||||
<Tooltip
|
||||
placement="top"
|
||||
content="This data source was added by config and cannot be modified using the UI."
|
||||
theme="info"
|
||||
>
|
||||
<div className={styles.div}>
|
||||
<InlineSwitch
|
||||
id="basic-settings-default"
|
||||
transparent={true}
|
||||
value={isDefault || false}
|
||||
className={styles.switch}
|
||||
disabled={true}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<InlineSwitch
|
||||
id="basic-settings-default"
|
||||
transparent={true}
|
||||
value={isDefault || false}
|
||||
onChange={(evt: React.FormEvent<HTMLInputElement>) => handleDefaultDataSourceChange(evt.currentTarget.checked)}
|
||||
className={styles.switch}
|
||||
disabled={readOnly}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
div: css({
|
||||
div: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
}),
|
||||
switch: css({
|
||||
padding: '0',
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -18,26 +18,15 @@ export function DataSourceTabPage({ uid, pageId, navId }: Props) {
|
||||
const nav = useDataSourceSettingsNav(uid, pageId);
|
||||
|
||||
const info = useDataSourceInfo({
|
||||
dataSource: nav.dataSource,
|
||||
dataSourcePluginName: nav.main.dataSourcePluginName,
|
||||
isDefault: nav.dataSource.isDefault,
|
||||
isReadOnly: nav.dataSource.readOnly,
|
||||
alertingSupported: nav.dataSourceHeader.alertingSupported,
|
||||
onUpdate: nav.dataSourceHeader.onUpdate,
|
||||
});
|
||||
|
||||
return (
|
||||
<Page
|
||||
navId={navId}
|
||||
pageNav={nav.main}
|
||||
renderTitle={(title) => (
|
||||
<EditDataSourceTitle
|
||||
dataSource={nav.dataSource}
|
||||
title={title}
|
||||
readOnly={nav.dataSource.readOnly}
|
||||
onUpdate={nav.dataSourceHeader.onUpdate}
|
||||
/>
|
||||
)}
|
||||
renderTitle={(title) => <EditDataSourceTitle title={title} />}
|
||||
info={info}
|
||||
actions={<EditDataSourceActions uid={uid} />}
|
||||
>
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
DataSourcePluginMeta,
|
||||
DataSourceSettings as DataSourceSettingsType,
|
||||
} from '@grafana/data';
|
||||
import { getDataSourceSrv, config } from '@grafana/runtime';
|
||||
import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import PageLoader from 'app/core/components/PageLoader/PageLoader';
|
||||
import { DataSourceSettingsState, useDispatch } from 'app/types';
|
||||
|
||||
@@ -116,7 +116,6 @@ export function EditDataSourceView({
|
||||
const hasAlertingEnabled = Boolean(dsi?.meta?.alerting ?? false);
|
||||
const isAlertManagerDatasource = dsi?.type === 'alertmanager';
|
||||
const alertingSupported = hasAlertingEnabled || isAlertManagerDatasource;
|
||||
const dataSourcePageHeader = config.featureToggles.dataSourcePageHeader;
|
||||
|
||||
const onSubmit = async (e: React.MouseEvent<HTMLButtonElement> | React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
@@ -158,16 +157,14 @@ export function EditDataSourceView({
|
||||
|
||||
<CloudInfoBox dataSource={dataSource} />
|
||||
|
||||
{!dataSourcePageHeader && (
|
||||
<BasicSettings
|
||||
dataSourceName={dataSource.name}
|
||||
isDefault={dataSource.isDefault}
|
||||
onDefaultChange={onDefaultChange}
|
||||
onNameChange={onNameChange}
|
||||
alertingSupported={alertingSupported}
|
||||
disabled={readOnly || !hasWriteRights}
|
||||
/>
|
||||
)}
|
||||
<BasicSettings
|
||||
dataSourceName={dataSource.name}
|
||||
isDefault={dataSource.isDefault}
|
||||
onDefaultChange={onDefaultChange}
|
||||
onNameChange={onNameChange}
|
||||
alertingSupported={alertingSupported}
|
||||
disabled={readOnly || !hasWriteRights}
|
||||
/>
|
||||
|
||||
{plugin && (
|
||||
<DataSourcePluginContextProvider instanceSettings={dsi}>
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import { screen, render } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { getMockDataSource } from 'app/features/datasources/__mocks__';
|
||||
|
||||
import { EditDataSourceTitle } from './EditDataSourceTitle';
|
||||
|
||||
const setup = () => {
|
||||
const props = {
|
||||
dataSource: getMockDataSource(),
|
||||
title: 'My Datasource',
|
||||
readOnly: false,
|
||||
onUpdate: jest.fn(),
|
||||
onNameChange: jest.fn(),
|
||||
};
|
||||
|
||||
return render(<EditDataSourceTitle {...props} />);
|
||||
};
|
||||
|
||||
describe('<EditDataSourceTitle>', () => {
|
||||
it('should render component', () => {
|
||||
setup();
|
||||
const editButton = screen.queryByTestId(selectors.pages.DataSource.nameEditIcon);
|
||||
const nameInput = screen.queryByTestId(selectors.pages.DataSource.name);
|
||||
expect(editButton).toBeInTheDocument();
|
||||
expect(nameInput).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render nameInput by clicking edit icon button', async () => {
|
||||
setup();
|
||||
const editButton = screen.getByTestId(selectors.pages.DataSource.nameEditIcon);
|
||||
await userEvent.click(editButton);
|
||||
const nameInput = screen.getByRole('textbox');
|
||||
|
||||
expect(nameInput).toBeInTheDocument();
|
||||
expect(editButton).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,87 +1,19 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { DataSourceSettings, GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { IconButton, useStyles2, Input, Tooltip, Field } from '@grafana/ui';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
|
||||
interface Props {
|
||||
dataSource: DataSourceSettings;
|
||||
title: string;
|
||||
readOnly: boolean;
|
||||
onUpdate: (dataSource: DataSourceSettings) => Promise<DataSourceSettings>;
|
||||
}
|
||||
|
||||
export function EditDataSourceTitle({ dataSource, title, readOnly, onUpdate }: Props) {
|
||||
const [isNameEditable, setIsNameEditable] = useState(false);
|
||||
const [name, setName] = useState<string>(title);
|
||||
const [initialDataSource, setInitialDataSource] = useState<DataSourceSettings>(dataSource);
|
||||
export function EditDataSourceTitle({ title }: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const toggleEditMode = () => {
|
||||
setIsNameEditable(!isNameEditable);
|
||||
};
|
||||
|
||||
const handleNameChange = async (name: string) => {
|
||||
toggleEditMode();
|
||||
if (name === '') {
|
||||
setName(title);
|
||||
return;
|
||||
}
|
||||
if (name !== title) {
|
||||
try {
|
||||
await onUpdate({ ...initialDataSource, name });
|
||||
} catch (err) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setName(title);
|
||||
}, [title]);
|
||||
|
||||
// update this to read only initial dataSource load
|
||||
// currently it picks up dataSource updates from form
|
||||
useEffect(() => {
|
||||
setInitialDataSource(dataSource);
|
||||
}, [dataSource]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{!isNameEditable ? (
|
||||
<div className={styles.titleContainer}>
|
||||
<h1 className={styles.title}>{name}</h1>
|
||||
{!readOnly && (
|
||||
<Tooltip placement="top" content="Edit data source name" theme="info">
|
||||
<IconButton
|
||||
name="pen"
|
||||
onClick={toggleEditMode}
|
||||
size="lg"
|
||||
title="Change data source name"
|
||||
className={styles.editIcon}
|
||||
data-testid={selectors.pages.DataSource.nameEditIcon}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.datasourceNameInput}>
|
||||
<Field invalid={name === ''} error={name === '' ? 'Please enter a data source name' : ''}>
|
||||
<Input
|
||||
id="edit-data-source-name"
|
||||
type="text"
|
||||
value={name}
|
||||
placeholder="Name"
|
||||
onChange={(evt: React.FormEvent<HTMLInputElement>) => setName(evt.currentTarget.value)}
|
||||
onBlur={(evt: React.FormEvent<HTMLInputElement>) => handleNameChange(evt.currentTarget.value)}
|
||||
required
|
||||
data-testid={selectors.pages.DataSource.name}
|
||||
autoFocus={isNameEditable}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
)}
|
||||
<h1 className={styles.title}>{title}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -90,33 +22,17 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
container: css({
|
||||
marginBottom: theme.spacing(2),
|
||||
}),
|
||||
titleContainer: css({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
h1: {
|
||||
display: 'inline-block',
|
||||
},
|
||||
}),
|
||||
title: css({
|
||||
display: 'inline-block',
|
||||
margin: '0 0 0 0',
|
||||
maxWidth: '40vw',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}),
|
||||
datasourceNameInput: css({
|
||||
input: {
|
||||
fontSize: theme.typography.h1.fontSize,
|
||||
width: '40vw',
|
||||
padding: '6px 8px',
|
||||
height: '40px',
|
||||
},
|
||||
}),
|
||||
editIcon: css({
|
||||
marginLeft: theme.spacing(1),
|
||||
color: theme.colors.text.secondary,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,64 +1,25 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Badge, Icon, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
import { Badge } from '@grafana/ui';
|
||||
import { PageInfoItem } from 'app/core/components/Page/types';
|
||||
|
||||
import { DataSourceInfo } from '../types';
|
||||
|
||||
import { DataSourceDefaultSwitch } from './DataSourceDefaultSwitch';
|
||||
|
||||
export const useDataSourceInfo = (dataSourceInfo: DataSourceInfo): PageInfoItem[] => {
|
||||
const info: PageInfoItem[] = [];
|
||||
const alertingEnabled = dataSourceInfo.alertingSupported;
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
info.push({
|
||||
label: 'Type',
|
||||
value: <span className={styles.pageInfoValue}>{dataSourceInfo.dataSourcePluginName}</span>,
|
||||
});
|
||||
|
||||
info.push({
|
||||
label: (
|
||||
<Tooltip placement="top" content="The default data source is preselected in Explore." theme="info">
|
||||
<div className="gf-form-help-icon gf-form-help-icon--right-normal">
|
||||
Default
|
||||
<Icon name="info-circle" size="xs" style={{ marginLeft: '10px' }} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
),
|
||||
value: (
|
||||
<DataSourceDefaultSwitch
|
||||
dataSource={dataSourceInfo.dataSource}
|
||||
isDefault={dataSourceInfo.isDefault}
|
||||
onUpdate={dataSourceInfo.onUpdate}
|
||||
readOnly={dataSourceInfo.isReadOnly}
|
||||
></DataSourceDefaultSwitch>
|
||||
),
|
||||
value: dataSourceInfo.dataSourcePluginName,
|
||||
});
|
||||
|
||||
info.push({
|
||||
label: 'Alerting',
|
||||
value: (
|
||||
<div className={styles.pageInfoValue}>
|
||||
<Badge color={alertingEnabled ? 'green' : 'red'} text={alertingEnabled ? 'Supported' : 'Not supported'}></Badge>
|
||||
</div>
|
||||
<Badge color={alertingEnabled ? 'green' : 'red'} text={alertingEnabled ? 'Supported' : 'Not supported'}></Badge>
|
||||
),
|
||||
});
|
||||
|
||||
return info;
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
tooltip: css({
|
||||
marginLeft: '4px',
|
||||
}),
|
||||
pageInfoValue: css({
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -181,10 +181,8 @@ export const useDataSourceSettingsNav = (dataSourceId: string, pageId: string |
|
||||
dataSourcePluginName: datasourcePlugin?.name || '',
|
||||
active: true,
|
||||
},
|
||||
dataSource: dataSource,
|
||||
dataSourceHeader: {
|
||||
alertingSupported,
|
||||
onUpdate: useUpdateDatasource(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DataQuery, DataSourceApi, DataSourceJsonData, DataSourcePlugin, DataSourceSettings } from '@grafana/data';
|
||||
import { DataQuery, DataSourceApi, DataSourceJsonData, DataSourcePlugin } from '@grafana/data';
|
||||
|
||||
export type GenericDataSourcePlugin = DataSourcePlugin<DataSourceApi<DataQuery, DataSourceJsonData>>;
|
||||
|
||||
@@ -18,10 +18,6 @@ export type DataSourcesRoutes = {
|
||||
export type DataSourceTestStatus = 'success' | 'warning' | 'error';
|
||||
|
||||
export type DataSourceInfo = {
|
||||
dataSource: DataSourceSettings;
|
||||
dataSourcePluginName: string;
|
||||
isDefault: boolean;
|
||||
isReadOnly: boolean;
|
||||
alertingSupported: boolean;
|
||||
onUpdate: (dataSource: DataSourceSettings) => Promise<DataSourceSettings>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user