Merge pull request #1475 from influxdata/toggle-tempvars-controls
Toggle Visibility of Template Control Barpull/1541/head
commit
c7d74122b6
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -20,6 +20,7 @@ const Dashboard = ({
|
|||
templatesIncludingDashTime,
|
||||
onSummonOverlayTechnologies,
|
||||
onSelectTemplate,
|
||||
showTemplateControlBar,
|
||||
}) => {
|
||||
if (dashboard.id === 0) {
|
||||
return null
|
||||
|
@ -41,16 +42,20 @@ const Dashboard = ({
|
|||
})
|
||||
|
||||
return (
|
||||
<FancyScrollbar className={classnames(
|
||||
'page-contents',
|
||||
{'presentation-mode': inPresentationMode}
|
||||
)}>
|
||||
<FancyScrollbar
|
||||
className={classnames('page-contents', {
|
||||
'presentation-mode': inPresentationMode,
|
||||
})}
|
||||
>
|
||||
<div className="dashboard container-fluid full-width">
|
||||
<TemplateControlBar
|
||||
templates={dashboard.templates}
|
||||
onSelectTemplate={onSelectTemplate}
|
||||
onOpenTemplateManager={onOpenTemplateManager}
|
||||
/>
|
||||
{inPresentationMode
|
||||
? null
|
||||
: <TemplateControlBar
|
||||
templates={dashboard.templates}
|
||||
onSelectTemplate={onSelectTemplate}
|
||||
onOpenTemplateManager={onOpenTemplateManager}
|
||||
isOpen={showTemplateControlBar}
|
||||
/>}
|
||||
{cells.length
|
||||
? <LayoutRenderer
|
||||
templates={templatesIncludingDashTime}
|
||||
|
@ -116,6 +121,7 @@ Dashboard.propTypes = {
|
|||
timeRange: shape({}).isRequired,
|
||||
onOpenTemplateManager: func.isRequired,
|
||||
onSelectTemplate: func.isRequired,
|
||||
showTemplateControlBar: bool,
|
||||
}
|
||||
|
||||
export default Dashboard
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import AutoRefreshDropdown from 'shared/components/AutoRefreshDropdown'
|
||||
import TimeRangeDropdown from 'shared/components/TimeRangeDropdown'
|
||||
|
@ -19,8 +20,10 @@ const DashboardHeader = ({
|
|||
source,
|
||||
onAddCell,
|
||||
onEditDashboard,
|
||||
onToggleTempVarControls,
|
||||
showTemplateControlBar,
|
||||
}) =>
|
||||
(isHidden
|
||||
isHidden
|
||||
? null
|
||||
: <div className="page-header full-width">
|
||||
<div className="page-header__container">
|
||||
|
@ -59,6 +62,16 @@ const DashboardHeader = ({
|
|||
Rename
|
||||
</button>
|
||||
: null}
|
||||
{dashboard
|
||||
? <div
|
||||
className={classnames('btn btn-info btn-sm', {
|
||||
active: showTemplateControlBar,
|
||||
})}
|
||||
onClick={onToggleTempVarControls}
|
||||
>
|
||||
<span className="icon cube" />Template Variables
|
||||
</div>
|
||||
: null}
|
||||
<AutoRefreshDropdown
|
||||
onChoose={handleChooseAutoRefresh}
|
||||
selected={autoRefresh}
|
||||
|
@ -76,7 +89,7 @@ const DashboardHeader = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>)
|
||||
</div>
|
||||
|
||||
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
|
||||
|
|
|
@ -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,
|
||||
}) => (
|
||||
<div className="template-control-bar">
|
||||
<div className="template-control--controls">
|
||||
{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
|
||||
<div className={classnames('template-control-bar', {show: isOpen})}>
|
||||
<div className="template-control--container">
|
||||
<div className="template-control--controls">
|
||||
{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 (
|
||||
<div key={id} className="template-control--dropdown">
|
||||
<Dropdown
|
||||
items={items}
|
||||
buttonSize="btn-xs"
|
||||
useAutoComplete={true}
|
||||
selected={selectedText || '(No values)'}
|
||||
onChoose={item =>
|
||||
onSelectTemplate(id, [item].map(x => omit(x, 'text')))}
|
||||
/>
|
||||
<label className="template-control--label">
|
||||
{tempVar}
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
// 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 (
|
||||
<div key={id} className="template-control--dropdown">
|
||||
<Dropdown
|
||||
items={items}
|
||||
buttonSize="btn-xs"
|
||||
useAutoComplete={true}
|
||||
selected={selectedText || '(No values)'}
|
||||
onChoose={item =>
|
||||
onSelectTemplate(id, [item].map(x => omit(x, 'text')))}
|
||||
/>
|
||||
<label className="template-control--label">
|
||||
{tempVar}
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
: <div className="template-control--empty">
|
||||
This dashboard does not have any Template Variables
|
||||
</div>}
|
||||
</div>
|
||||
<button
|
||||
className="btn btn-primary btn-sm template-control--manage"
|
||||
onClick={onOpenTemplateManager}
|
||||
>
|
||||
<span className="icon cog-thick" />
|
||||
Manage
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
className="btn btn-primary btn-sm template-control--manage"
|
||||
onClick={onOpenTemplateManager}
|
||||
>
|
||||
<span className="icon cog-thick" />
|
||||
Templates
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
|
||||
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
|
||||
|
|
|
@ -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}
|
||||
</div>
|
||||
|
@ -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),
|
||||
|
|
|
@ -24,6 +24,10 @@ export const setAutoRefresh = milliseconds => ({
|
|||
},
|
||||
})
|
||||
|
||||
export const templateControlBarVisibilityToggled = () => ({
|
||||
type: 'TEMPLATE_CONTROL_BAR_VISIBILITY_TOGGLED',
|
||||
})
|
||||
|
||||
export const noop = () => ({
|
||||
type: 'NOOP',
|
||||
payload: {},
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue