Commit 3dcfe54d by Hugo Häggmark Committed by GitHub

Variables: Fixes Textbox current value persistence (#29481)

* Variables: Fixes savequery for Constant and TextBox variables

* Refactor: reverts textbox changes

* Refactor: Fixes dashboard export and tests

* Refactor: hides or migrates Constant variables

* Tests: fixes snapshots

* Variables: Fixes Textbox current value persistance

* Refactor: fixes PR comments and adds e2e tests
parent 15f8dd44
{
"__inputs": [
{
"name": "DS_GDEV-TESTDATA",
"label": "gdev-testdata",
"description": "",
"type": "datasource",
"pluginId": "testdata",
"pluginName": "TestData DB"
}
],
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "7.4.0-pre"
},
{
"type": "datasource",
"id": "testdata",
"name": "TestData DB",
"version": "1.0.0"
},
{
"type": "panel",
"id": "text",
"name": "Text",
"version": ""
}
],
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": null,
"iteration": 1606804991052,
"links": [],
"panels": [
{
"datasource": "${DS_GDEV-TESTDATA}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 0
},
"id": 2,
"options": {
"content": "# variable: ${text}\n ",
"mode": "markdown"
},
"pluginVersion": "7.4.0-pre",
"timeFrom": null,
"timeShift": null,
"title": "Panel Title",
"type": "text"
}
],
"schemaVersion": 27,
"style": "dark",
"tags": [],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "default value",
"value": "default value"
},
"description": null,
"error": null,
"hide": 0,
"label": null,
"name": "text",
"options": [
{
"selected": true,
"text": "default value",
"value": "default value"
}
],
"query": "default value",
"skipUrlSync": false,
"type": "textbox"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Templating - Textbox e2e scenarios",
"uid": "AejrN1AMz",
"version": 1
}
import { e2e } from '@grafana/e2e';
const PAGE_UNDER_TEST = 'AejrN1AMz';
describe('TextBox - load options scenarios', function() {
it('default options should be correct', function() {
e2e.flows.login('admin', 'admin');
e2e.flows.openDashboard({ uid: PAGE_UNDER_TEST });
e2e().server();
e2e()
.route({
method: 'GET',
url: `/api/dashboards/uid/${PAGE_UNDER_TEST}`,
})
.as('dash');
e2e().wait('@dash');
validateTextboxAndMarkup('default value');
});
it('loading variable from url should be correct', function() {
e2e.flows.login('admin', 'admin');
e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?var-text=not default value` });
e2e().server();
e2e()
.route({
method: 'GET',
url: `/api/dashboards/uid/${PAGE_UNDER_TEST}`,
})
.as('dash');
e2e().wait('@dash');
validateTextboxAndMarkup('not default value');
});
});
describe('TextBox - change query scenarios', function() {
it('when changing the query value and not saving current as default should revert query value', function() {
copyExistingDashboard();
changeQueryInput();
e2e.components.BackButton.backArrow()
.should('be.visible')
.click({ force: true });
validateTextboxAndMarkup('changed value');
saveDashboard(false);
e2e()
.get('@dashuid')
.then((dashuid: any) => {
expect(dashuid).not.to.eq(PAGE_UNDER_TEST);
e2e.flows.openDashboard({ uid: dashuid });
e2e().wait('@load-dash');
validateTextboxAndMarkup('default value');
validateVariable('changed value');
});
});
it('when changing the query value and saving current as default should change query value', function() {
copyExistingDashboard();
changeQueryInput();
e2e.components.BackButton.backArrow()
.should('be.visible')
.click({ force: true });
validateTextboxAndMarkup('changed value');
saveDashboard(true);
e2e()
.get('@dashuid')
.then((dashuid: any) => {
expect(dashuid).not.to.eq(PAGE_UNDER_TEST);
e2e.flows.openDashboard({ uid: dashuid });
e2e().wait('@load-dash');
validateTextboxAndMarkup('changed value');
validateVariable('changed value');
});
});
});
describe('TextBox - change picker value scenarios', function() {
it('when changing the input value and not saving current as default should revert query value', function() {
copyExistingDashboard();
changeTextBoxInput();
validateTextboxAndMarkup('changed value');
saveDashboard(false);
e2e()
.get('@dashuid')
.then((dashuid: any) => {
expect(dashuid).not.to.eq(PAGE_UNDER_TEST);
e2e.flows.openDashboard({ uid: dashuid });
e2e().wait('@load-dash');
validateTextboxAndMarkup('default value');
validateVariable('default value');
});
});
it('when changing the input value and saving current as default should change query value', function() {
copyExistingDashboard();
changeTextBoxInput();
validateTextboxAndMarkup('changed value');
saveDashboard(true);
e2e()
.get('@dashuid')
.then((dashuid: any) => {
expect(dashuid).not.to.eq(PAGE_UNDER_TEST);
e2e.flows.openDashboard({ uid: dashuid });
e2e().wait('@load-dash');
validateTextboxAndMarkup('changed value');
validateVariable('changed value');
});
});
});
function copyExistingDashboard() {
e2e.flows.login('admin', 'admin');
e2e().server();
e2e()
.route({
method: 'GET',
url: '/api/search?query=&type=dash-folder&permission=Edit',
})
.as('dash-settings');
e2e()
.route({
method: 'POST',
url: '/api/dashboards/db/',
})
.as('save-dash');
e2e()
.route({
method: 'GET',
url: /\/api\/dashboards\/uid\/(?!AejrN1AMz)\w+/,
})
.as('load-dash');
e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?editview=settings&orgId=1` });
e2e().wait('@dash-settings');
e2e.pages.Dashboard.Settings.General.saveAsDashBoard()
.should('be.visible')
.click();
e2e.pages.SaveDashboardAsModal.newName()
.should('be.visible')
.type(`${Date.now()}`);
e2e.pages.SaveDashboardAsModal.save()
.should('be.visible')
.click();
e2e().wait('@save-dash');
e2e().wait('@load-dash');
e2e.pages.Dashboard.SubMenu.submenuItem().should('be.visible');
e2e()
.location()
.then(loc => {
const dashuid = /\/d\/(\w+)\//.exec(loc.href)![1];
e2e()
.wrap(dashuid)
.as('dashuid');
});
e2e().wait(500);
}
function saveDashboard(saveVariables: boolean) {
e2e.pages.Dashboard.Toolbar.toolbarItems('Save dashboard')
.should('be.visible')
.click();
if (saveVariables) {
e2e.pages.SaveDashboardModal.saveVariables()
.should('exist')
.click({ force: true });
}
e2e.pages.SaveDashboardModal.save()
.should('be.visible')
.click();
e2e().wait('@save-dash');
}
function validateTextboxAndMarkup(value: string) {
e2e.pages.Dashboard.SubMenu.submenuItem()
.should('be.visible')
.within(() => {
e2e.pages.Dashboard.SubMenu.submenuItemLabels('text').should('be.visible');
e2e()
.get('input')
.should('be.visible')
.should('have.value', value);
});
e2e.components.Panels.Visualization.Text.container()
.should('be.visible')
.within(() => {
e2e()
.get('h1')
.should('be.visible')
.should('have.text', `variable: ${value}`);
});
}
function validateVariable(value: string) {
e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings')
.should('be.visible')
.click();
e2e.pages.Dashboard.Settings.General.sectionItems('Variables')
.should('be.visible')
.click();
e2e.pages.Dashboard.Settings.Variables.List.tableRowNameFields('text')
.should('be.visible')
.click();
e2e.pages.Dashboard.Settings.Variables.Edit.TextBoxVariable.textBoxOptionsQueryInput()
.should('be.visible')
.should('have.value', value);
}
function changeTextBoxInput() {
e2e.pages.Dashboard.SubMenu.submenuItemLabels('text').should('be.visible');
e2e.pages.Dashboard.SubMenu.submenuItem()
.should('be.visible')
.within(() => {
e2e()
.get('input')
.should('be.visible')
.should('have.value', 'default value')
.clear()
.type('changed value')
.type('{enter}');
});
e2e()
.location()
.should(loc => {
expect(loc.search).to.contain('var-text=changed%20value');
});
}
function changeQueryInput() {
e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings')
.should('be.visible')
.click();
e2e.pages.Dashboard.Settings.General.sectionItems('Variables')
.should('be.visible')
.click();
e2e.pages.Dashboard.Settings.Variables.List.tableRowNameFields('text')
.should('be.visible')
.click();
e2e.pages.Dashboard.Settings.Variables.Edit.TextBoxVariable.textBoxOptionsQueryInput()
.should('be.visible')
.clear()
.type('changed value')
.blur();
e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption()
.should('have.length', 1)
.should('have.text', 'changed value');
}
...@@ -37,6 +37,9 @@ export const Components = { ...@@ -37,6 +37,9 @@ export const Components = {
BarGauge: { BarGauge: {
value: 'Bar gauge value', value: 'Bar gauge value',
}, },
Text: {
container: () => '.markdown-html',
},
}, },
}, },
Drawer: { Drawer: {
......
...@@ -99,6 +99,9 @@ export const Pages = { ...@@ -99,6 +99,9 @@ export const Pages = {
ConstantVariable: { ConstantVariable: {
constantOptionsQueryInput: 'Variable editor Form Constant Query field', constantOptionsQueryInput: 'Variable editor Form Constant Query field',
}, },
TextBoxVariable: {
textBoxOptionsQueryInput: 'Variable editor Form TextBox Query field',
},
}, },
}, },
}, },
......
...@@ -10,10 +10,10 @@ ...@@ -10,10 +10,10 @@
<div class="dashboard-settings__aside-actions"> <div class="dashboard-settings__aside-actions">
<div ng-show="ctrl.canSave"> <div ng-show="ctrl.canSave">
<save-dashboard-button getDashboard="ctrl.getDashboard" aria-label="Dashboard settings aside actions Save button" /> <save-dashboard-button getDashboard="ctrl.getDashboard" />
</div> </div>
<div ng-show="ctrl.canSaveAs"> <div ng-show="ctrl.canSaveAs">
<save-dashboard-as-button getDashboard="ctrl.getDashboard" aria-label="Dashboard settings aside actions Save as button" variant="'secondary'" /> <save-dashboard-as-button getDashboard="ctrl.getDashboard" variant="'secondary'" />
</div> </div>
</div> </div>
</aside> </aside>
......
...@@ -5,6 +5,7 @@ import { connectWithProvider } from 'app/core/utils/connectWithReduxStore'; ...@@ -5,6 +5,7 @@ import { connectWithProvider } from 'app/core/utils/connectWithReduxStore';
import { provideModalsContext } from 'app/routes/ReactContainer'; import { provideModalsContext } from 'app/routes/ReactContainer';
import { SaveDashboardAsModal } from './SaveDashboardAsModal'; import { SaveDashboardAsModal } from './SaveDashboardAsModal';
import { SaveDashboardModalProxy } from './SaveDashboardModalProxy'; import { SaveDashboardModalProxy } from './SaveDashboardModalProxy';
import { selectors } from '@grafana/e2e-selectors';
interface SaveDashboardButtonProps { interface SaveDashboardButtonProps {
dashboard: DashboardModel; dashboard: DashboardModel;
...@@ -30,6 +31,7 @@ export const SaveDashboardButton: React.FC<SaveDashboardButtonProps> = ({ dashbo ...@@ -30,6 +31,7 @@ export const SaveDashboardButton: React.FC<SaveDashboardButtonProps> = ({ dashbo
onDismiss: hideModal, onDismiss: hideModal,
}); });
}} }}
aria-label={selectors.pages.Dashboard.Settings.General.saveDashBoard}
> >
Save dashboard Save dashboard
</Button> </Button>
...@@ -63,6 +65,7 @@ export const SaveDashboardAsButton: React.FC<SaveDashboardButtonProps & { varian ...@@ -63,6 +65,7 @@ export const SaveDashboardAsButton: React.FC<SaveDashboardButtonProps & { varian
// In Dashboard Settings in sidebar we need to use new form but with inverse variant to make it look like it should // In Dashboard Settings in sidebar we need to use new form but with inverse variant to make it look like it should
// Everywhere else we use old button component :( // Everywhere else we use old button component :(
variant={variant as ButtonVariant} variant={variant as ButtonVariant}
aria-label={selectors.pages.Dashboard.Settings.General.saveAsDashBoard}
> >
Save As... Save As...
</Button> </Button>
......
...@@ -857,7 +857,7 @@ describe('DashboardModel', () => { ...@@ -857,7 +857,7 @@ describe('DashboardModel', () => {
}); });
}); });
it('should have three variables after migration', () => { it('should have six variables after migration', () => {
expect(model.templating.list.length).toBe(6); expect(model.templating.list.length).toBe(6);
}); });
......
...@@ -14,12 +14,12 @@ import { ...@@ -14,12 +14,12 @@ import {
dateTimeFormat, dateTimeFormat,
dateTimeFormatTimeAgo, dateTimeFormatTimeAgo,
DateTimeInput, DateTimeInput,
EventBusExtended,
EventBusSrv,
PanelEvents, PanelEvents,
TimeRange, TimeRange,
TimeZone, TimeZone,
UrlQueryValue, UrlQueryValue,
EventBusSrv,
EventBusExtended,
} from '@grafana/data'; } from '@grafana/data';
import { CoreEvents, DashboardMeta, KIOSK_MODE_TV } from 'app/types'; import { CoreEvents, DashboardMeta, KIOSK_MODE_TV } from 'app/types';
import { GetVariables, getVariables } from 'app/features/variables/state/selectors'; import { GetVariables, getVariables } from 'app/features/variables/state/selectors';
...@@ -236,7 +236,9 @@ export class DashboardModel { ...@@ -236,7 +236,9 @@ export class DashboardModel {
const currentVariables = this.getVariablesFromState(); const currentVariables = this.getVariablesFromState();
copy.templating = { copy.templating = {
list: currentVariables.map(variable => variableAdapters.get(variable.type).getSaveModel(variable)), list: currentVariables.map(variable =>
variableAdapters.get(variable.type).getSaveModel(variable, defaults.saveVariables)
),
}; };
if (!defaults.saveVariables) { if (!defaults.saveVariables) {
......
...@@ -34,7 +34,7 @@ export interface VariableAdapter<Model extends VariableModel> { ...@@ -34,7 +34,7 @@ export interface VariableAdapter<Model extends VariableModel> {
setValue: (variable: Model, option: VariableOption, emitChanges?: boolean) => Promise<void>; setValue: (variable: Model, option: VariableOption, emitChanges?: boolean) => Promise<void>;
setValueFromUrl: (variable: Model, urlValue: UrlQueryValue) => Promise<void>; setValueFromUrl: (variable: Model, urlValue: UrlQueryValue) => Promise<void>;
updateOptions: (variable: Model, searchFilter?: string) => Promise<void>; updateOptions: (variable: Model, searchFilter?: string) => Promise<void>;
getSaveModel: (variable: Model) => Partial<Model>; getSaveModel: (variable: Model, saveCurrentAsDefault?: boolean) => Partial<Model>;
getValueForUrl: (variable: Model) => string | string[]; getValueForUrl: (variable: Model) => string | string[];
picker: ComponentType<VariablePickerProps>; picker: ComponentType<VariablePickerProps>;
editor: ComponentType<VariableEditorProps>; editor: ComponentType<VariableEditorProps>;
......
...@@ -11,11 +11,12 @@ import { initialCustomVariableModelState } from '../../custom/reducer'; ...@@ -11,11 +11,12 @@ import { initialCustomVariableModelState } from '../../custom/reducer';
import { MultiVariableBuilder } from './multiVariableBuilder'; import { MultiVariableBuilder } from './multiVariableBuilder';
import { initialConstantVariableModelState } from '../../constant/reducer'; import { initialConstantVariableModelState } from '../../constant/reducer';
import { QueryVariableBuilder } from './queryVariableBuilder'; import { QueryVariableBuilder } from './queryVariableBuilder';
import { TextBoxVariableBuilder } from './textboxVariableBuilder';
export const adHocBuilder = () => new AdHocVariableBuilder(initialAdHocVariableModelState); export const adHocBuilder = () => new AdHocVariableBuilder(initialAdHocVariableModelState);
export const intervalBuilder = () => new IntervalVariableBuilder(initialIntervalVariableModelState); export const intervalBuilder = () => new IntervalVariableBuilder(initialIntervalVariableModelState);
export const datasourceBuilder = () => new DatasourceVariableBuilder(initialDataSourceVariableModelState); export const datasourceBuilder = () => new DatasourceVariableBuilder(initialDataSourceVariableModelState);
export const queryBuilder = () => new QueryVariableBuilder(initialQueryVariableModelState); export const queryBuilder = () => new QueryVariableBuilder(initialQueryVariableModelState);
export const textboxBuilder = () => new OptionsVariableBuilder(initialTextBoxVariableModelState); export const textboxBuilder = () => new TextBoxVariableBuilder(initialTextBoxVariableModelState);
export const customBuilder = () => new MultiVariableBuilder(initialCustomVariableModelState); export const customBuilder = () => new MultiVariableBuilder(initialCustomVariableModelState);
export const constantBuilder = () => new OptionsVariableBuilder(initialConstantVariableModelState); export const constantBuilder = () => new OptionsVariableBuilder(initialConstantVariableModelState);
import { TextBoxVariableModel } from 'app/features/variables/types';
import { OptionsVariableBuilder } from './optionsVariableBuilder';
export class TextBoxVariableBuilder<T extends TextBoxVariableModel> extends OptionsVariableBuilder<T> {
withOriginalQuery(original: string) {
this.variable.originalQuery = original;
return this;
}
}
import React, { ChangeEvent, PureComponent } from 'react'; import React, { ChangeEvent, ReactElement, useCallback } from 'react';
import { VerticalGroup } from '@grafana/ui'; import { VerticalGroup } from '@grafana/ui';
import { TextBoxVariableModel } from '../types'; import { TextBoxVariableModel } from '../types';
import { VariableEditorProps } from '../editor/types'; import { VariableEditorProps } from '../editor/types';
import { VariableSectionHeader } from '../editor/VariableSectionHeader'; import { VariableSectionHeader } from '../editor/VariableSectionHeader';
import { VariableTextField } from '../editor/VariableTextField'; import { VariableTextField } from '../editor/VariableTextField';
import { selectors } from '@grafana/e2e-selectors';
export interface Props extends VariableEditorProps<TextBoxVariableModel> {} export interface Props extends VariableEditorProps<TextBoxVariableModel> {}
export class TextBoxVariableEditor extends PureComponent<Props> {
onQueryChange = (event: ChangeEvent<HTMLInputElement>) => { export function TextBoxVariableEditor({ onPropChange, variable: { query } }: Props): ReactElement {
event.preventDefault(); const updateVariable = useCallback(
this.props.onPropChange({ propName: 'query', propValue: event.target.value, updateOptions: false }); (event: ChangeEvent<HTMLInputElement>, updateOptions: boolean) => {
};
onQueryBlur = (event: ChangeEvent<HTMLInputElement>) => {
event.preventDefault(); event.preventDefault();
this.props.onPropChange({ propName: 'query', propValue: event.target.value, updateOptions: true }); onPropChange({ propName: 'originalQuery', propValue: event.target.value, updateOptions: false });
}; onPropChange({ propName: 'query', propValue: event.target.value, updateOptions });
render() { },
const { query } = this.props.variable; [onPropChange]
);
const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => updateVariable(e, false), [updateVariable]);
const onBlur = useCallback((e: ChangeEvent<HTMLInputElement>) => updateVariable(e, true), [updateVariable]);
return ( return (
<VerticalGroup spacing="xs"> <VerticalGroup spacing="xs">
<VariableSectionHeader name="Text Options" /> <VariableSectionHeader name="Text Options" />
...@@ -25,12 +29,12 @@ export class TextBoxVariableEditor extends PureComponent<Props> { ...@@ -25,12 +29,12 @@ export class TextBoxVariableEditor extends PureComponent<Props> {
value={query} value={query}
name="Default value" name="Default value"
placeholder="default value, if any" placeholder="default value, if any"
onChange={this.onQueryChange} onChange={onChange}
onBlur={this.onQueryBlur} onBlur={onBlur}
labelWidth={20} labelWidth={20}
grow grow
ariaLabel={selectors.pages.Dashboard.Settings.Variables.Edit.TextBoxVariable.textBoxOptionsQueryInput}
/> />
</VerticalGroup> </VerticalGroup>
); );
}
} }
import React, { ChangeEvent, FocusEvent, KeyboardEvent, PureComponent } from 'react'; import React, { ChangeEvent, FocusEvent, KeyboardEvent, ReactElement, useCallback, useEffect, useState } from 'react';
import { TextBoxVariableModel } from '../types'; import { TextBoxVariableModel } from '../types';
import { toVariableIdentifier, toVariablePayload } from '../state/types'; import { toVariablePayload } from '../state/types';
import { dispatch } from '../../../store/store';
import { changeVariableProp } from '../state/sharedReducer'; import { changeVariableProp } from '../state/sharedReducer';
import { VariablePickerProps } from '../pickers/types'; import { VariablePickerProps } from '../pickers/types';
import { updateOptions } from '../state/actions'; import { Input } from '@grafana/ui';
import { variableAdapters } from '../adapters';
import { useDispatch } from 'react-redux';
export interface Props extends VariablePickerProps<TextBoxVariableModel> {} export interface Props extends VariablePickerProps<TextBoxVariableModel> {}
export class TextBoxVariablePicker extends PureComponent<Props> { export function TextBoxVariablePicker({ variable }: Props): ReactElement {
onQueryChange = (event: ChangeEvent<HTMLInputElement>) => { const dispatch = useDispatch();
const [updatedValue, setUpdatedValue] = useState(variable.current.value);
useEffect(() => {
setUpdatedValue(variable.current.value);
}, [variable]);
const updateVariable = useCallback(() => {
if (variable.current.value === updatedValue) {
return;
}
dispatch( dispatch(
changeVariableProp(toVariablePayload(this.props.variable, { propName: 'query', propValue: event.target.value })) changeVariableProp(
toVariablePayload({ id: variable.id, type: variable.type }, { propName: 'query', propValue: updatedValue })
)
); );
}; variableAdapters.get(variable.type).updateOptions(variable);
}, [dispatch, variable, updatedValue]);
onQueryBlur = (event: FocusEvent<HTMLInputElement>) => { const onChange = useCallback((event: ChangeEvent<HTMLInputElement>) => setUpdatedValue(event.target.value), [
if (this.props.variable.current.value !== this.props.variable.query) { setUpdatedValue,
dispatch(updateOptions(toVariableIdentifier(this.props.variable))); ]);
}
};
onQueryKeyDown = (event: KeyboardEvent<HTMLInputElement>) => { const onBlur = (e: FocusEvent<HTMLInputElement>) => updateVariable();
if (event.keyCode === 13 && this.props.variable.current.value !== this.props.variable.query) { const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
dispatch(updateOptions(toVariableIdentifier(this.props.variable))); if (event.keyCode === 13) {
updateVariable();
} }
}; };
render() { return <Input type="text" value={updatedValue} onChange={onChange} onBlur={onBlur} onKeyDown={onKeyDown} />;
return (
<input
type="text"
value={this.props.variable.query}
className="gf-form-input width-12"
onChange={this.onQueryChange}
onBlur={this.onQueryBlur}
onKeyDown={this.onQueryKeyDown}
/>
);
}
} }
import { variableAdapters } from '../adapters';
import { createTextBoxVariableAdapter } from './adapter';
import { textboxBuilder } from '../shared/testing/builders';
import { VariableHide } from '../types';
variableAdapters.setInit(() => [createTextBoxVariableAdapter()]);
describe('createTextBoxVariableAdapter', () => {
describe('getSaveModel', () => {
describe('when called and query differs from the original query and not saving current as default', () => {
it('then the model should be correct', () => {
const text = textboxBuilder()
.withId('text')
.withName('text')
.withQuery('query')
.withOriginalQuery('original')
.withCurrent('query')
.withOptions('query')
.build();
const adapter = variableAdapters.get('textbox');
const result = adapter.getSaveModel(text, false);
expect(result).toEqual({
name: 'text',
query: 'original',
current: { selected: false, text: 'original', value: 'original' },
options: [{ selected: false, text: 'original', value: 'original' }],
type: 'textbox',
label: null,
hide: VariableHide.dontHide,
skipUrlSync: false,
error: null,
description: null,
});
});
});
describe('when called and query differs from the original query and saving current as default', () => {
it('then the model should be correct', () => {
const text = textboxBuilder()
.withId('text')
.withName('text')
.withQuery('query')
.withOriginalQuery('original')
.withCurrent('query')
.withOptions('query')
.build();
const adapter = variableAdapters.get('textbox');
const result = adapter.getSaveModel(text, true);
expect(result).toEqual({
name: 'text',
query: 'query',
current: { selected: true, text: 'query', value: 'query' },
options: [{ selected: false, text: 'query', value: 'query' }],
type: 'textbox',
label: null,
hide: VariableHide.dontHide,
skipUrlSync: false,
error: null,
description: null,
});
});
});
});
describe('beforeAdding', () => {
describe('when called', () => {
it('then originalQuery should be same added', () => {
const model = { name: 'text', query: 'a query' };
const adapter = variableAdapters.get('textbox');
const result = adapter.beforeAdding!(model);
expect(result).toEqual({ name: 'text', query: 'a query', originalQuery: 'a query' });
});
});
});
});
...@@ -31,12 +31,22 @@ export const createTextBoxVariableAdapter = (): VariableAdapter<TextBoxVariableM ...@@ -31,12 +31,22 @@ export const createTextBoxVariableAdapter = (): VariableAdapter<TextBoxVariableM
updateOptions: async variable => { updateOptions: async variable => {
await dispatch(updateTextBoxVariableOptions(toVariableIdentifier(variable))); await dispatch(updateTextBoxVariableOptions(toVariableIdentifier(variable)));
}, },
getSaveModel: variable => { getSaveModel: (variable, saveCurrentAsDefault) => {
const { index, id, state, global, ...rest } = cloneDeep(variable); const { index, id, state, global, originalQuery, ...rest } = cloneDeep(variable);
if (variable.query !== originalQuery && !saveCurrentAsDefault) {
const origQuery = originalQuery ?? '';
const current = { selected: false, text: origQuery, value: origQuery };
return { ...rest, query: origQuery, current, options: [current] };
}
return rest; return rest;
}, },
getValueForUrl: variable => { getValueForUrl: variable => {
return variable.current.value; return variable.current.value;
}, },
beforeAdding: model => {
return { ...cloneDeep(model), originalQuery: model.query };
},
}; };
}; };
...@@ -10,6 +10,7 @@ export const initialTextBoxVariableModelState: TextBoxVariableModel = { ...@@ -10,6 +10,7 @@ export const initialTextBoxVariableModelState: TextBoxVariableModel = {
query: '', query: '',
current: {} as VariableOption, current: {} as VariableOption,
options: [], options: [],
originalQuery: null,
}; };
export const textBoxVariableSlice = createSlice({ export const textBoxVariableSlice = createSlice({
......
...@@ -86,7 +86,9 @@ export interface QueryVariableModel extends DataSourceVariableModel { ...@@ -86,7 +86,9 @@ export interface QueryVariableModel extends DataSourceVariableModel {
query: any; query: any;
} }
export interface TextBoxVariableModel extends VariableWithOptions {} export interface TextBoxVariableModel extends VariableWithOptions {
originalQuery: string | null;
}
export interface ConstantVariableModel extends VariableWithOptions {} export interface ConstantVariableModel extends VariableWithOptions {}
......
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