Commit 6d402064 by Jack Westbrook Committed by GitHub

Dashboard: remove usage of Legacyforms (#28707)

* refactor(dashboard): remove usage of legacyform components in sharemodal

* refactor(dashboard): replace legacyform components

* refactor(dashboard): remove ng-if and correct typo in content of sharesnapshot

* feat(grafana-ui): set displayName prop for Switch component

* refactor(dashboard): migrate TimePickerSettings legacyform components

* refactor(queryoptions): migrate switch and input to nextgen components

* refactor(sharesnapshot): prefer InlineFieldRow over gf-form-group

* refactor(shareembed): styling fixes

* refactor(timepickersettings): prefer double bang over nullish coalescing operator

* fix(grafana-ui): switch uses id prop if passed in

* feat: connect labels and switches with ids
parent f7a5150d
......@@ -70,20 +70,19 @@ export const getSwitchStyles = stylesFactory((theme: GrafanaTheme) => {
transition: transform 0.2s cubic-bezier(0.19, 1, 0.22, 1);
}
}
}
`,
};
});
export const Switch = React.forwardRef<HTMLInputElement, SwitchProps>(
({ value, checked, disabled = false, onChange, ...inputProps }, ref) => {
({ value, checked, disabled = false, onChange, id, ...inputProps }, ref) => {
if (checked) {
deprecationWarning('Switch', 'checked prop', 'value');
}
const theme = useTheme();
const styles = getSwitchStyles(theme);
const switchIdRef = useRef(uniqueId('switch-'));
const switchIdRef = useRef(id ? id : uniqueId('switch-'));
return (
<div className={cx(styles.switch)}>
......@@ -103,3 +102,5 @@ export const Switch = React.forwardRef<HTMLInputElement, SwitchProps>(
);
}
);
Switch.displayName = 'Switch';
import React, { PureComponent } from 'react';
import { Input, LegacyForms, TimeZonePicker, Tooltip } from '@grafana/ui';
import { InlineField, Input, Switch, TimeZonePicker, Tooltip } from '@grafana/ui';
import { rangeUtil, TimeZone } from '@grafana/data';
import isEmpty from 'lodash/isEmpty';
import { selectors } from '@grafana/e2e-selectors';
......@@ -87,12 +87,9 @@ export class TimePickerSettings extends PureComponent<Props, State> {
</div>
<div className="gf-form">
<LegacyForms.Switch
labelClass="width-7"
label="Hide time picker"
checked={this.props.timePickerHidden ?? false}
onChange={this.onHideTimePickerChange}
/>
<InlineField labelWidth={14} label="Hide time picker">
<Switch value={!!this.props.timePickerHidden} onChange={this.onHideTimePickerChange} />
</InlineField>
</div>
</div>
</div>
......
import React, { PureComponent } from 'react';
import { LegacyForms, Icon } from '@grafana/ui';
const { Select, Switch } = LegacyForms;
import { Select, Switch, Icon, InlineField } from '@grafana/ui';
import { SelectableValue } from '@grafana/data';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { buildIframeHtml } from './utils';
......@@ -82,24 +81,24 @@ export class ShareEmbed extends PureComponent<Props, State> {
<Icon name="link" className="share-modal-big-icon" size="xxl" />
<div className="share-modal-content">
<div className="gf-form-group">
<Switch
labelClass="width-12"
label="Current time range"
checked={useCurrentTimeRange}
onChange={this.onUseCurrentTimeRangeChange}
/>
<Switch
labelClass="width-12"
label="Template variables"
checked={includeTemplateVars}
onChange={this.onIncludeTemplateVarsChange}
/>
<div className="gf-form">
<label className="gf-form-label width-12">Theme</label>
<Select width={10} options={themeOptions} value={selectedTheme} onChange={this.onThemeChange} />
</div>
<InlineField labelWidth={24} label="Current time range">
<Switch
id="share-current-time-range"
value={useCurrentTimeRange}
onChange={this.onUseCurrentTimeRangeChange}
/>
</InlineField>
<InlineField labelWidth={24} label="Template variables">
<Switch
id="share-template-variables"
value={includeTemplateVars}
onChange={this.onIncludeTemplateVarsChange}
/>
</InlineField>
<InlineField labelWidth={24} label="Theme">
<Select width={20} options={themeOptions} value={selectedTheme} onChange={this.onThemeChange} />
</InlineField>
</div>
<p className="share-modal-info-text">
The html code below can be pasted and included in another web page. Unless anonymous access is enabled,
the user viewing that page need to be signed into grafana for the graph to load.
......
import React, { PureComponent } from 'react';
import { saveAs } from 'file-saver';
import { Button, LegacyForms, Icon } from '@grafana/ui';
const { Switch } = LegacyForms;
import { Button, InlineField, Switch, Icon } from '@grafana/ui';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { DashboardExporter } from 'app/features/dashboard/components/DashExportModal';
import { appEvents } from 'app/core/core';
......@@ -93,13 +92,9 @@ export class ShareExport extends PureComponent<Props, State> {
<div className="share-modal-header">
<Icon name="cloud-upload" size="xxl" className="share-modal-big-icon" />
<div className="share-modal-content">
<Switch
labelClass="width-16"
label="Export for sharing externally"
checked={shareExternally}
onChange={this.onShareExternallyChange}
/>
<InlineField labelWidth={32} label="Export for sharing externally">
<Switch value={shareExternally} onChange={this.onShareExternallyChange} />
</InlineField>
<div className="gf-form-button-row">
<Button variant="primary" onClick={this.onSaveAsFile}>
Save to file
......
import React, { PureComponent } from 'react';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
import { LegacyForms, ClipboardButton, Icon, InfoBox, Input } from '@grafana/ui';
const { Select, Switch } = LegacyForms;
import { InlineField, Select, Switch, ClipboardButton, Icon, InfoBox, Input } from '@grafana/ui';
import { SelectableValue, PanelModel, AppEvents } from '@grafana/data';
import { DashboardModel } from 'app/features/dashboard/state';
import { buildImageUrl, buildShareUrl } from './utils';
......@@ -111,23 +110,26 @@ export class ShareLink extends PureComponent<Props, State> {
Create a direct link to this dashboard or panel, customized with the options below.
</p>
<div className="gf-form-group">
<Switch
labelClass="width-12"
label="Current time range"
checked={useCurrentTimeRange}
onChange={this.onUseCurrentTimeRangeChange}
/>
<Switch
labelClass="width-12"
label="Template variables"
checked={includeTemplateVars}
onChange={this.onIncludeTemplateVarsChange}
/>
<div className="gf-form">
<label className="gf-form-label width-12">Theme</label>
<Select width={10} options={themeOptions} value={selectedTheme} onChange={this.onThemeChange} />
</div>
<Switch labelClass="width-12" label="Shorten URL" checked={useShortUrl} onChange={this.onUrlShorten} />
<InlineField labelWidth={24} label="Current time range">
<Switch
id="share-current-time-range"
value={useCurrentTimeRange}
onChange={this.onUseCurrentTimeRangeChange}
/>
</InlineField>
<InlineField labelWidth={24} label="Template variables">
<Switch
id="share-template-vars"
value={includeTemplateVars}
onChange={this.onIncludeTemplateVarsChange}
/>
</InlineField>
<InlineField labelWidth={24} label="Theme">
<Select width={20} options={themeOptions} value={selectedTheme} onChange={this.onThemeChange} />
</InlineField>
<InlineField labelWidth={24} label="Shorten URL">
<Switch id="share-shorten-url" value={useShortUrl} onChange={this.onUrlShorten} />
</InlineField>
</div>
<div>
<div className="gf-form-group">
......
import React, { PureComponent } from 'react';
import { Button, ClipboardButton, Icon, Spinner, LegacyForms, LinkButton } from '@grafana/ui';
import {
Button,
ClipboardButton,
Icon,
Spinner,
Select,
Input,
LinkButton,
InlineField,
InlineFieldRow,
} from '@grafana/ui';
import { AppEvents, SelectableValue } from '@grafana/data';
import { getBackendSrv } from '@grafana/runtime';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
......@@ -7,8 +17,6 @@ import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { appEvents } from 'app/core/core';
import { VariableRefresh } from '../../../variables/types';
const { Select, Input } = LegacyForms;
const snapshotApiUrl = '/api/snapshots';
const expireOptions: Array<SelectableValue<number>> = [
......@@ -221,27 +229,24 @@ export class ShareSnapshot extends PureComponent<Props, State> {
URL. Share wisely.
</p>
</div>
<div className="gf-form-group share-modal-options">
<div className="gf-form" ng-if="step === 1">
<label className="gf-form-label width-12">Snapshot name</label>
<Input width={15} value={snapshotName} onChange={this.onSnapshotNameChange} />
</div>
<div className="gf-form" ng-if="step === 1">
<label className="gf-form-label width-12">Expire</label>
<Select width={15} options={expireOptions} value={selectedExpireOption} onChange={this.onExpireChange} />
</div>
</div>
<InlineFieldRow className="share-modal-options">
<InlineField labelWidth={24} label="Snapshot name">
<Input width={30} value={snapshotName} onChange={this.onSnapshotNameChange} />
</InlineField>
<InlineField labelWidth={24} label="Expire">
<Select width={30} options={expireOptions} value={selectedExpireOption} onChange={this.onExpireChange} />
</InlineField>
</InlineFieldRow>
<p className="share-modal-info-text">
You may need to configure the timeout value if it takes a long time to collect your dashboard's metrics.
</p>
<div className="gf-form-group share-modal-options">
<div className="gf-form">
<span className="gf-form-label width-12">Timeout (seconds)</span>
<Input type="number" width={15} value={timeoutSeconds} onChange={this.onTimeoutChange} />
</div>
</div>
<InlineFieldRow className="share-modal-options">
<InlineField labelWidth={24} label="Timeout (seconds)">
<Input type="number" width={21} value={timeoutSeconds} onChange={this.onTimeoutChange} />
</InlineField>
</InlineFieldRow>
<div className="gf-form-button-row">
<Button className="width-10" variant="primary" disabled={isLoading} onClick={this.createSnapshot()}>
......@@ -277,7 +282,7 @@ export class ShareSnapshot extends PureComponent<Props, State> {
</div>
</div>
<div className="pull-right" ng-if="step === 2" style={{ padding: '5px' }}>
<div className="pull-right" style={{ padding: '5px' }}>
Did you make a mistake?{' '}
<LinkButton variant="link" target="_blank" onClick={this.deleteSnapshot}>
delete snapshot.
......@@ -291,8 +296,8 @@ export class ShareSnapshot extends PureComponent<Props, State> {
return (
<div className="share-modal-header">
<p className="share-modal-info-text">
The snapshot has now been deleted. If it you have already accessed it once, It might take up to an hour before
it is removed from browser caches or CDN caches.
The snapshot has now been deleted. If you have already accessed it once, it might take up to an hour before it
is removed from browser caches or CDN caches.
</p>
</div>
);
......
......@@ -5,15 +5,7 @@ import React, { PureComponent, ChangeEvent, FocusEvent } from 'react';
import { rangeUtil, PanelData, DataSourceApi } from '@grafana/data';
// Components
import {
EventsWithValidation,
LegacyInputStatus,
LegacyForms,
ValidationEvents,
InlineFormLabel,
stylesFactory,
} from '@grafana/ui';
const { Switch, Input } = LegacyForms;
import { Switch, Input, InlineField, InlineFormLabel, stylesFactory } from '@grafana/ui';
// Types
import { PanelModel } from '../state';
......@@ -21,18 +13,11 @@ import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOp
import { config } from 'app/core/config';
import { css } from 'emotion';
const timeRangeValidationEvents: ValidationEvents = {
[EventsWithValidation.onBlur]: [
{
rule: value => {
if (!value) {
return true;
}
return rangeUtil.isValidTimeSpan(value);
},
errorMessage: 'Not a valid timespan',
},
],
const timeRangeValidation = (value: string) => {
if (!value) {
return true;
}
return rangeUtil.isValidTimeSpan(value);
};
const emptyToNull = (value: string) => {
......@@ -53,6 +38,8 @@ interface State {
interval: string;
hideTimeOverride: boolean;
isOpen: boolean;
relativeTimeIsValid: boolean;
timeShiftIsValid: boolean;
}
export class QueryOptions extends PureComponent<Props, State> {
......@@ -67,6 +54,8 @@ export class QueryOptions extends PureComponent<Props, State> {
interval: props.panel.interval || '',
hideTimeOverride: props.panel.hideTimeOverride || false,
isOpen: false,
relativeTimeIsValid: true,
timeShiftIsValid: true,
};
}
......@@ -82,26 +71,30 @@ export class QueryOptions extends PureComponent<Props, State> {
});
};
onOverrideTime = (event: FocusEvent<HTMLInputElement>, status: LegacyInputStatus) => {
onOverrideTime = (event: FocusEvent<HTMLInputElement>) => {
const { value } = event.target;
const { panel } = this.props;
const emptyToNullValue = emptyToNull(value);
const isValid = timeRangeValidation(value);
if (status === LegacyInputStatus.Valid && panel.timeFrom !== emptyToNullValue) {
if (isValid && panel.timeFrom !== emptyToNullValue) {
panel.timeFrom = emptyToNullValue;
panel.refresh();
}
this.setState({ relativeTimeIsValid: isValid });
};
onTimeShift = (event: FocusEvent<HTMLInputElement>, status: LegacyInputStatus) => {
onTimeShift = (event: FocusEvent<HTMLInputElement>) => {
const { value } = event.target;
const { panel } = this.props;
const emptyToNullValue = emptyToNull(value);
const isValid = timeRangeValidation(value);
if (status === LegacyInputStatus.Valid && panel.timeShift !== emptyToNullValue) {
if (isValid && panel.timeShift !== emptyToNullValue) {
panel.timeShift = emptyToNullValue;
panel.refresh();
}
this.setState({ timeShiftIsValid: isValid });
};
onToggleTimeOverride = () => {
......@@ -301,7 +294,7 @@ export class QueryOptions extends PureComponent<Props, State> {
}
render() {
const { hideTimeOverride } = this.state;
const { hideTimeOverride, relativeTimeIsValid, timeShiftIsValid } = this.state;
const { relativeTime, timeShift, isOpen } = this.state;
const styles = getStyles();
......@@ -327,8 +320,7 @@ export class QueryOptions extends PureComponent<Props, State> {
placeholder="1h"
onChange={this.onRelativeTimeChange}
onBlur={this.onOverrideTime}
validationEvents={timeRangeValidationEvents}
hideErrorMessage={true}
invalid={!relativeTimeIsValid}
value={relativeTime}
/>
</div>
......@@ -341,19 +333,15 @@ export class QueryOptions extends PureComponent<Props, State> {
placeholder="1h"
onChange={this.onTimeShiftChange}
onBlur={this.onTimeShift}
validationEvents={timeRangeValidationEvents}
hideErrorMessage={true}
invalid={!timeShiftIsValid}
value={timeShift}
/>
</div>
{(timeShift || relativeTime) && (
<div className="gf-form-inline">
<Switch
label="Hide time info"
labelClass="width-9"
checked={hideTimeOverride}
onChange={this.onToggleTimeOverride}
/>
<InlineField label="Hide time info" labelWidth={18}>
<Switch value={hideTimeOverride} onChange={this.onToggleTimeOverride} />
</InlineField>
</div>
)}
</QueryOperationRow>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment