remove editable fields from page Header - name, default datasource switch

This commit is contained in:
Taewoo Kim
2023-05-15 13:41:52 -04:00
parent da76dde23e
commit a44f57ec26
11 changed files with 21 additions and 281 deletions
@@ -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);
+1 -1
View File
@@ -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 -5
View File
@@ -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>;
};