diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a732e08e..c459a8fc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,8 @@ In versions 1.3.1+, installing a new version of Chronograf automatically clears ### UI Improvements 1. [#1451](https://github.com/influxdata/chronograf/pull/1451): Refactor scrollbars to support non-webkit browsers - 1. [#1453](https://github.com/influxdata/chronograf/pull/1453): Increase the query builder's default height in cell editor mode and in the data explorer + 1. [#1453](https://github.com/influxdata/chronograf/pull/1453): Give QueryMaker a greater initial height than Visualization + 1. [#1475](https://github.com/influxdata/chronograf/pull/1475): Add ability to toggle visibility of the Template Control Bar 1. [#1464](https://github.com/influxdata/chronograf/pull/1464): Make the [template variables](https://docs.influxdata.com/chronograf/v1.3/guides/dashboard-template-variables/) manager more space efficient 1. [#1464](https://github.com/influxdata/chronograf/pull/1464): Add page spinners to pages that did not have them 1. [#1464](https://github.com/influxdata/chronograf/pull/1464): Denote which source is connected in the sources table diff --git a/ui/spec/shared/reducers/appSpec.js b/ui/spec/shared/reducers/appSpec.js index 5d1190c77..94935ae1f 100644 --- a/ui/spec/shared/reducers/appSpec.js +++ b/ui/spec/shared/reducers/appSpec.js @@ -4,6 +4,7 @@ import { disablePresentationMode, // delayEnablePresentationMode, setAutoRefresh, + templateControlBarVisibilityToggled, } from 'src/shared/actions/app' describe('Shared.Reducers.appReducer', () => { @@ -13,6 +14,7 @@ describe('Shared.Reducers.appReducer', () => { }, persisted: { autoRefresh: 0, + showTemplateControlBar: false, }, } @@ -37,4 +39,17 @@ describe('Shared.Reducers.appReducer', () => { expect(reducedState.persisted.autoRefresh).to.equal(expectedMs) }) + + it('should handle TEMPLATE_CONTROL_BAR_VISIBILITY_TOGGLED', () => { + const reducedState = appReducer( + initialState, + templateControlBarVisibilityToggled() + ) + + const expectedTestState = !reducedState.persisted.showTemplateControlBar + + expect(initialState.persisted.showTemplateControlBar).to.equal( + expectedTestState + ) + }) }) diff --git a/ui/src/dashboards/components/Dashboard.js b/ui/src/dashboards/components/Dashboard.js index 6f97e0ced..7bb27e497 100644 --- a/ui/src/dashboards/components/Dashboard.js +++ b/ui/src/dashboards/components/Dashboard.js @@ -20,6 +20,7 @@ const Dashboard = ({ templatesIncludingDashTime, onSummonOverlayTechnologies, onSelectTemplate, + showTemplateControlBar, }) => { if (dashboard.id === 0) { return null @@ -41,16 +42,20 @@ const Dashboard = ({ }) return ( - +
- + {inPresentationMode + ? null + : } {cells.length ? - (isHidden + isHidden ? null :
@@ -59,6 +62,16 @@ const DashboardHeader = ({ Rename : null} + {dashboard + ?
+ Template Variables +
+ : null}
-
) + const {array, bool, func, number, shape, string} = PropTypes @@ -95,6 +108,8 @@ DashboardHeader.propTypes = { source: shape({}), onAddCell: func, onEditDashboard: func, + onToggleTempVarControls: func, + showTemplateControlBar: bool, } export default DashboardHeader diff --git a/ui/src/dashboards/components/TemplateControlBar.js b/ui/src/dashboards/components/TemplateControlBar.js index d5d7a69de..ed03a5f53 100644 --- a/ui/src/dashboards/components/TemplateControlBar.js +++ b/ui/src/dashboards/components/TemplateControlBar.js @@ -1,4 +1,5 @@ import React, {PropTypes} from 'react' +import classnames from 'classnames' import Dropdown from 'shared/components/Dropdown' @@ -8,45 +9,52 @@ const TemplateControlBar = ({ templates, onSelectTemplate, onOpenTemplateManager, + isOpen, }) => ( -
-
- {templates.map(({id, values, tempVar}) => { - const items = values.map(value => ({...value, text: value.value})) - const selectedItem = items.find(item => item.selected) || items[0] - const selectedText = selectedItem && selectedItem.text +
+
+
+ {templates.length + ? templates.map(({id, values, tempVar}) => { + const items = values.map(value => ({...value, text: value.value})) + const selectedItem = items.find(item => item.selected) || items[0] + const selectedText = selectedItem && selectedItem.text - // TODO: change Dropdown to a MultiSelectDropdown, `selected` to - // the full array, and [item] to all `selected` values when we update - // this component to support multiple values - return ( -
- - onSelectTemplate(id, [item].map(x => omit(x, 'text')))} - /> - -
- ) - })} + // TODO: change Dropdown to a MultiSelectDropdown, `selected` to + // the full array, and [item] to all `selected` values when we update + // this component to support multiple values + return ( +
+ + onSelectTemplate(id, [item].map(x => omit(x, 'text')))} + /> + +
+ ) + }) + :
+ This dashboard does not have any Template Variables +
} +
+
-
) -const {arrayOf, func, shape, string} = PropTypes +const {arrayOf, bool, func, shape, string} = PropTypes TemplateControlBar.propTypes = { templates: arrayOf( @@ -62,6 +70,7 @@ TemplateControlBar.propTypes = { ).isRequired, onSelectTemplate: func.isRequired, onOpenTemplateManager: func.isRequired, + isOpen: bool, } export default TemplateControlBar diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index 4535fcbbf..e54f3b087 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -15,7 +15,10 @@ import {errorThrown as errorThrownAction} from 'shared/actions/errors' import * as dashboardActionCreators from 'src/dashboards/actions' -import {setAutoRefresh} from 'shared/actions/app' +import { + setAutoRefresh, + templateControlBarVisibilityToggled as templateControlBarVisibilityToggledAction, +} from 'shared/actions/app' import {presentationButtonDispatcher} from 'shared/dispatchers' class DashboardPage extends Component { @@ -48,6 +51,7 @@ class DashboardPage extends Component { this.handleSelectTemplate = ::this.handleSelectTemplate this.handleEditTemplateVariables = ::this.handleEditTemplateVariables this.handleRunQueryFailure = ::this.handleRunQueryFailure + this.handleToggleTempVarControls = ::this.handleToggleTempVarControls } componentDidMount() { @@ -206,6 +210,10 @@ class DashboardPage extends Component { this.props.errorThrown(error) } + handleToggleTempVarControls() { + this.props.templateControlBarVisibilityToggled() + } + getActiveDashboard() { const {params: {dashboardID}, dashboards} = this.props return dashboards.find(d => d.id === +dashboardID) @@ -215,6 +223,7 @@ class DashboardPage extends Component { const { source, timeRange, + showTemplateControlBar, dashboards, autoRefresh, cellQueryStatus, @@ -289,6 +298,8 @@ class DashboardPage extends Component { source={source} onAddCell={this.handleAddCell} onEditDashboard={this.handleEditDashboard} + onToggleTempVarControls={this.handleToggleTempVarControls} + showTemplateControlBar={showTemplateControlBar} > {dashboards ? dashboards.map((d, i) => ( @@ -320,6 +331,7 @@ class DashboardPage extends Component { templatesIncludingDashTime={templatesIncludingDashTime} onSummonOverlayTechnologies={this.handleSummonOverlayTechnologies} onSelectTemplate={this.handleSelectTemplate} + showTemplateControlBar={showTemplateControlBar} /> : null}
@@ -377,7 +389,9 @@ DashboardPage.propTypes = { ), handleChooseAutoRefresh: func.isRequired, autoRefresh: number.isRequired, + templateControlBarVisibilityToggled: func.isRequired, timeRange: shape({}).isRequired, + showTemplateControlBar: bool.isRequired, inPresentationMode: bool.isRequired, handleClickPresentationButton: func, cellQueryStatus: shape({ @@ -389,7 +403,10 @@ DashboardPage.propTypes = { const mapStateToProps = state => { const { - app: {ephemeral: {inPresentationMode}, persisted: {autoRefresh}}, + app: { + ephemeral: {inPresentationMode}, + persisted: {autoRefresh, showTemplateControlBar}, + }, dashboardUI: {dashboards, timeRange, cellQueryStatus}, } = state @@ -397,6 +414,7 @@ const mapStateToProps = state => { dashboards, autoRefresh, timeRange, + showTemplateControlBar, inPresentationMode, cellQueryStatus, } @@ -404,6 +422,10 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => ({ handleChooseAutoRefresh: bindActionCreators(setAutoRefresh, dispatch), + templateControlBarVisibilityToggled: bindActionCreators( + templateControlBarVisibilityToggledAction, + dispatch + ), handleClickPresentationButton: presentationButtonDispatcher(dispatch), dashboardActions: bindActionCreators(dashboardActionCreators, dispatch), errorThrown: bindActionCreators(errorThrownAction, dispatch), diff --git a/ui/src/shared/actions/app.js b/ui/src/shared/actions/app.js index f2af1731a..df95c736d 100644 --- a/ui/src/shared/actions/app.js +++ b/ui/src/shared/actions/app.js @@ -24,6 +24,10 @@ export const setAutoRefresh = milliseconds => ({ }, }) +export const templateControlBarVisibilityToggled = () => ({ + type: 'TEMPLATE_CONTROL_BAR_VISIBILITY_TOGGLED', +}) + export const noop = () => ({ type: 'NOOP', payload: {}, diff --git a/ui/src/shared/reducers/app.js b/ui/src/shared/reducers/app.js index 366ac49ec..32c4959a7 100644 --- a/ui/src/shared/reducers/app.js +++ b/ui/src/shared/reducers/app.js @@ -8,6 +8,7 @@ const initialState = { }, persisted: { autoRefresh: AUTOREFRESH_DEFAULT, + showTemplateControlBar: false, }, } @@ -46,6 +47,12 @@ const appPersistedReducer = (state = initialAppPersistedState, action) => { } } + case 'TEMPLATE_CONTROL_BAR_VISIBILITY_TOGGLED': { + const {showTemplateControlBar} = state + + return {...state, showTemplateControlBar: !showTemplateControlBar} + } + default: return state } diff --git a/ui/src/style/components/template-control-bar.scss b/ui/src/style/components/template-control-bar.scss index b8ad33f61..c3d77c397 100644 --- a/ui/src/style/components/template-control-bar.scss +++ b/ui/src/style/components/template-control-bar.scss @@ -7,13 +7,22 @@ $template-control--margin: 2px; $template-control--min-height: 52px; +$template-control-dropdown-min-width: 146px; .template-control-bar { + display: none; + height: auto; + margin-bottom: 8px; + + &.show { + display: block; + } +} +.template-control--container { display: flex; - flex-wrap: wrap; + flex-wrap: nowrap; align-items: center; justify-content: space-between; - margin-bottom: 8px; padding: $template-control--margin; @extend .cell-shell; background-color: $g0-obsidian; @@ -35,9 +44,16 @@ $template-control--min-height: 52px; flex: 1 0 0; flex-wrap: wrap; } +.template-control--empty { + color: $g11-sidewalk; + font-size: 14px; + font-weight: 500; + margin-left: 18px; + @include no-user-select(); +} .template-control--dropdown { flex: 0 1 auto; - min-width: 150px; + min-width: $template-control-dropdown-min-width; display: flex; flex-direction: column; align-items: stretch; diff --git a/ui/src/style/theme/theme-dark.scss b/ui/src/style/theme/theme-dark.scss index 31509c31b..5124149a4 100644 --- a/ui/src/style/theme/theme-dark.scss +++ b/ui/src/style/theme/theme-dark.scss @@ -165,6 +165,14 @@ button.btn.btn-sm > span.icon { } } +/* Active state for buttons */ +.btn-info.active { + &, &:hover, &:active, &:focus { + background-color: $g7-graphite; + color: $g20-white; + } +} + /*