diff --git a/CHANGELOG.md b/CHANGELOG.md index ffd53a95dc..d9d9d1f256 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### Features 1. [#1863](https://github.com/influxdata/chronograf/pull/1863): Improve 'new-sources' server flag example by adding 'type' key 1. [#1898](https://github.com/influxdata/chronograf/pull/1898): Add an input and validation to custom time range calendar dropdowns +1. [#1904](https://github.com/influxdata/chronograf/pull/1904): Add support for selecting template variables with URL params ### UI Improvements 1. [#1862](https://github.com/influxdata/chronograf/pull/1862): Show "Add Graph" button on cells with no queries diff --git a/ui/spec/dashboards/reducers/uiSpec.js b/ui/spec/dashboards/reducers/uiSpec.js index 62ef3e54dd..4451c2611a 100644 --- a/ui/spec/dashboards/reducers/uiSpec.js +++ b/ui/spec/dashboards/reducers/uiSpec.js @@ -11,6 +11,7 @@ import { renameDashboardCell, syncDashboardCell, templateVariableSelected, + templateVariablesSelectedByName, cancelEditCell, } from 'src/dashboards/actions' @@ -20,7 +21,7 @@ const templates = [ id: '1', type: 'query', label: 'test query', - tempVar: '$REGION', + tempVar: ':region:', query: { db: 'db1', rp: 'rp1', @@ -37,7 +38,7 @@ const templates = [ id: '2', type: 'csv', label: 'test csv', - tempVar: '$TEMPERATURE', + tempVar: ':temperature:', values: [ {value: '98.7', type: 'measurement', selected: false}, {value: '99.1', type: 'measurement', selected: false}, @@ -167,9 +168,10 @@ describe('DataExplorer.Reducers.UI', () => { state = { dashboards: [dash], } + const value = dash.templates[0].values[2].value const actual = reducer( - {dashboards}, + state, templateVariableSelected(dash.id, dash.templates[0].id, [{value}]) ) @@ -178,6 +180,26 @@ describe('DataExplorer.Reducers.UI', () => { expect(actual.dashboards[0].templates[0].values[2].selected).to.equal(true) }) + it('can select template variable values by name', () => { + const dash = _.cloneDeep(d1) + state = { + dashboards: [dash], + } + + const selected = {region: 'us-west', temperature: '99.1'} + const actual = reducer( + state, + templateVariablesSelectedByName(dash.id, selected) + ) + + expect(actual.dashboards[0].templates[0].values[0].selected).to.equal(true) + expect(actual.dashboards[0].templates[0].values[1].selected).to.equal(false) + expect(actual.dashboards[0].templates[0].values[2].selected).to.equal(false) + expect(actual.dashboards[0].templates[1].values[0].selected).to.equal(false) + expect(actual.dashboards[0].templates[1].values[1].selected).to.equal(true) + expect(actual.dashboards[0].templates[1].values[2].selected).to.equal(false) + }) + it('can cancel cell editing', () => { const dash = _.cloneDeep(d1) dash.cells = [editingCell] diff --git a/ui/src/dashboards/actions/index.js b/ui/src/dashboards/actions/index.js index 205d017cb2..0675b0f8e5 100644 --- a/ui/src/dashboards/actions/index.js +++ b/ui/src/dashboards/actions/index.js @@ -13,7 +13,10 @@ import {errorThrown} from 'shared/actions/errors' import {NEW_DEFAULT_DASHBOARD_CELL} from 'src/dashboards/constants' -import {TEMPLATE_VARIABLE_SELECTED} from 'shared/constants/actionTypes' +import { + TEMPLATE_VARIABLE_SELECTED, + TEMPLATE_VARIABLES_SELECTED_BY_NAME, +} from 'shared/constants/actionTypes' import {makeQueryForTemplate} from 'src/dashboards/utils/templateVariableQueryGenerator' import parsers from 'shared/parsing' @@ -134,6 +137,14 @@ export const templateVariableSelected = (dashboardID, templateID, values) => ({ }, }) +export const templateVariablesSelectedByName = (dashboardID, query) => ({ + type: TEMPLATE_VARIABLES_SELECTED_BY_NAME, + payload: { + dashboardID, + query, + }, +}) + export const editTemplateVariableValues = ( dashboardID, templateID, @@ -234,7 +245,10 @@ export const deleteDashboardCellAsync = (dashboard, cell) => async dispatch => { export const updateTempVarValues = (source, dashboard) => async dispatch => { try { - const tempsWithQueries = dashboard.templates.filter(t => !!t.query.influxql) + const tempsWithQueries = dashboard.templates.filter( + ({query}) => !!query.influxql + ) + const asyncQueries = tempsWithQueries.map(({query}) => runTemplateVariableQuery(source, {query: makeQueryForTemplate(query)}) ) @@ -251,3 +265,7 @@ export const updateTempVarValues = (source, dashboard) => async dispatch => { dispatch(errorThrown(error)) } } + +export const selectTempVarsFromUrl = (dashboardID, query = {}) => dispatch => { + dispatch(templateVariablesSelectedByName(dashboardID, query)) +} diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index a20553e7d7..3a4d4a9b3f 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -40,9 +40,11 @@ class DashboardPage extends Component { dashboardActions: { getDashboardsAsync, updateTempVarValues, + selectTempVarsFromUrl, putDashboardByID, }, source, + location: {query}, } = this.props const dashboards = await getDashboardsAsync() @@ -50,6 +52,7 @@ class DashboardPage extends Component { // Refresh and persists influxql generated template variable values await updateTempVarValues(source, dashboard) + selectTempVarsFromUrl(+dashboardID, query) await putDashboardByID(dashboardID) } @@ -369,6 +372,7 @@ DashboardPage.propTypes = { }).isRequired, location: shape({ pathname: string.isRequired, + query: shape({}), }).isRequired, dashboardActions: shape({ putDashboard: func.isRequired, diff --git a/ui/src/dashboards/reducers/ui.js b/ui/src/dashboards/reducers/ui.js index 1c285bb3c9..3b3c7f39dd 100644 --- a/ui/src/dashboards/reducers/ui.js +++ b/ui/src/dashboards/reducers/ui.js @@ -10,7 +10,10 @@ const initialState = { cellQueryStatus: {queryID: null, status: null}, } -import {TEMPLATE_VARIABLE_SELECTED} from 'shared/constants/actionTypes' +import { + TEMPLATE_VARIABLE_SELECTED, + TEMPLATE_VARIABLES_SELECTED_BY_NAME, +} from 'shared/constants/actionTypes' import {TEMPLATE_VARIABLE_TYPES} from 'src/dashboards/constants' export default function ui(state = initialState, action) { @@ -230,6 +233,49 @@ export default function ui(state = initialState, action) { return {...state, dashboards: newDashboards} } + case TEMPLATE_VARIABLES_SELECTED_BY_NAME: { + const {dashboardID, query} = action.payload + + const selecteds = Object.keys(query).map(k => ({ + tempVar: `:${k}:`, + selectedValue: query[k], + })) + + const makeNewValue = (value, selected) => ({...value, selected}) + + const makeNewValues = template => ({ + ...template, + values: template.values.map( + value => + selecteds.find(({selectedValue}) => selectedValue === value.value) + ? makeNewValue(value, true) + : makeNewValue(value, false) + ), + }) + + const makeNewTemplates = templates => + templates.map( + template => + selecteds.find(({tempVar}) => tempVar === template.tempVar) + ? makeNewValues(template) + : template + ) + + const makeNewDashboard = dashboard => ({ + ...dashboard, + templates: makeNewTemplates(dashboard.templates), + }) + + const newDashboards = state.dashboards.map( + oldDashboard => + oldDashboard.id === dashboardID + ? makeNewDashboard(oldDashboard) + : oldDashboard + ) + + return {...state, dashboards: newDashboards} + } + case 'EDIT_TEMPLATE_VARIABLE_VALUES': { const {dashboardID, templateID, values} = action.payload diff --git a/ui/src/shared/constants/actionTypes.js b/ui/src/shared/constants/actionTypes.js index 763fc6841d..63f1554903 100644 --- a/ui/src/shared/constants/actionTypes.js +++ b/ui/src/shared/constants/actionTypes.js @@ -1,3 +1,6 @@ export const TEMPLATE_VARIABLE_SELECTED = 'TEMPLATE_VARIABLE_SELECTED' +export const TEMPLATE_VARIABLES_SELECTED_BY_NAME = + 'TEMPLATE_VARIABLES_SELECTED_BY_NAME' + export const LINKS_RECEIVED = 'LINKS_RECEIVED'