Refactor dashboard naming flow and associated UI
parent
bf3ce3ed4c
commit
4395ef6e14
|
@ -1,49 +1,44 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
import AutoRefreshDropdown from 'shared/components/AutoRefreshDropdown'
|
import AutoRefreshDropdown from 'shared/components/AutoRefreshDropdown'
|
||||||
import TimeRangeDropdown from 'shared/components/TimeRangeDropdown'
|
import TimeRangeDropdown from 'shared/components/TimeRangeDropdown'
|
||||||
import SourceIndicator from 'shared/components/SourceIndicator'
|
import SourceIndicator from 'shared/components/SourceIndicator'
|
||||||
import GraphTips from 'shared/components/GraphTips'
|
import GraphTips from 'shared/components/GraphTips'
|
||||||
|
import DashboardHeaderEdit from 'src/dashboards/components/DashboardHeaderEdit'
|
||||||
|
|
||||||
const DashboardHeader = ({
|
const DashboardHeader = ({
|
||||||
|
onSave,
|
||||||
children,
|
children,
|
||||||
buttonText,
|
onCancel,
|
||||||
dashboard,
|
isEditMode,
|
||||||
timeRange: {upper, lower},
|
|
||||||
zoomedTimeRange: {zoomedLower, zoomedUpper},
|
|
||||||
autoRefresh,
|
|
||||||
isHidden,
|
isHidden,
|
||||||
|
dashboard,
|
||||||
|
onAddCell,
|
||||||
|
autoRefresh,
|
||||||
|
dashboardName,
|
||||||
|
onEditDashboard,
|
||||||
|
onManualRefresh,
|
||||||
handleChooseTimeRange,
|
handleChooseTimeRange,
|
||||||
handleChooseAutoRefresh,
|
handleChooseAutoRefresh,
|
||||||
onManualRefresh,
|
|
||||||
handleClickPresentationButton,
|
|
||||||
onAddCell,
|
|
||||||
onEditDashboard,
|
|
||||||
onToggleTempVarControls,
|
onToggleTempVarControls,
|
||||||
showTemplateControlBar,
|
showTemplateControlBar,
|
||||||
|
timeRange: {upper, lower},
|
||||||
|
handleClickPresentationButton,
|
||||||
|
zoomedTimeRange: {zoomedLower, zoomedUpper},
|
||||||
}) =>
|
}) =>
|
||||||
isHidden
|
isHidden
|
||||||
? null
|
? null
|
||||||
: <div className="page-header full-width">
|
: <div className="page-header full-width">
|
||||||
<div className="page-header__container">
|
<div className="page-header__container">
|
||||||
<div className="page-header__left">
|
<div
|
||||||
{buttonText &&
|
className={
|
||||||
<div className="dropdown page-header-dropdown">
|
dashboard
|
||||||
<button
|
? 'page-header__left page-header__dash-editable'
|
||||||
className="dropdown-toggle"
|
: 'page-header__left'
|
||||||
type="button"
|
}
|
||||||
data-toggle="dropdown"
|
>
|
||||||
>
|
|
||||||
<span>
|
|
||||||
{buttonText}
|
|
||||||
</span>
|
|
||||||
<span className="caret" />
|
|
||||||
</button>
|
|
||||||
<ul className="dropdown-menu">
|
|
||||||
{children}
|
|
||||||
</ul>
|
|
||||||
</div>}
|
|
||||||
{children.length > 1
|
{children.length > 1
|
||||||
? <div className="dropdown dashboard-switcher">
|
? <div className="dropdown dashboard-switcher">
|
||||||
<button
|
<button
|
||||||
|
@ -60,6 +55,17 @@ const DashboardHeader = ({
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
|
{dashboard
|
||||||
|
? <DashboardHeaderEdit
|
||||||
|
onSave={onSave}
|
||||||
|
onCancel={onCancel}
|
||||||
|
dashboardName={dashboardName}
|
||||||
|
onEditDashboard={onEditDashboard}
|
||||||
|
isEditMode={isEditMode}
|
||||||
|
/>
|
||||||
|
: <h1 className="page-header__title">
|
||||||
|
{dashboardName}
|
||||||
|
</h1>}
|
||||||
</div>
|
</div>
|
||||||
<div className="page-header__right">
|
<div className="page-header__right">
|
||||||
<GraphTips />
|
<GraphTips />
|
||||||
|
@ -70,15 +76,6 @@ const DashboardHeader = ({
|
||||||
Add Cell
|
Add Cell
|
||||||
</button>
|
</button>
|
||||||
: null}
|
: null}
|
||||||
{dashboard
|
|
||||||
? <button
|
|
||||||
className="btn btn-default btn-sm"
|
|
||||||
onClick={onEditDashboard}
|
|
||||||
>
|
|
||||||
<span className="icon pencil" />
|
|
||||||
Rename
|
|
||||||
</button>
|
|
||||||
: null}
|
|
||||||
{dashboard
|
{dashboard
|
||||||
? <div
|
? <div
|
||||||
className={classnames('btn btn-default btn-sm', {
|
className={classnames('btn btn-default btn-sm', {
|
||||||
|
@ -123,7 +120,8 @@ DashboardHeader.defaultProps = {
|
||||||
|
|
||||||
DashboardHeader.propTypes = {
|
DashboardHeader.propTypes = {
|
||||||
children: array,
|
children: array,
|
||||||
buttonText: string,
|
dashboardName: string.isRequired,
|
||||||
|
onEditDashboard: func.isRequired,
|
||||||
dashboard: shape({}),
|
dashboard: shape({}),
|
||||||
timeRange: shape({
|
timeRange: shape({
|
||||||
lower: string,
|
lower: string,
|
||||||
|
@ -131,15 +129,17 @@ DashboardHeader.propTypes = {
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
autoRefresh: number.isRequired,
|
autoRefresh: number.isRequired,
|
||||||
isHidden: bool.isRequired,
|
isHidden: bool.isRequired,
|
||||||
|
isEditMode: bool,
|
||||||
handleChooseTimeRange: func.isRequired,
|
handleChooseTimeRange: func.isRequired,
|
||||||
handleChooseAutoRefresh: func.isRequired,
|
handleChooseAutoRefresh: func.isRequired,
|
||||||
onManualRefresh: func.isRequired,
|
onManualRefresh: func.isRequired,
|
||||||
handleClickPresentationButton: func.isRequired,
|
handleClickPresentationButton: func.isRequired,
|
||||||
onAddCell: func,
|
onAddCell: func,
|
||||||
onEditDashboard: func,
|
|
||||||
onToggleTempVarControls: func,
|
onToggleTempVarControls: func,
|
||||||
showTemplateControlBar: bool,
|
showTemplateControlBar: bool,
|
||||||
zoomedTimeRange: shape({}),
|
zoomedTimeRange: shape({}),
|
||||||
|
onCancel: func.isRequired,
|
||||||
|
onSave: func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DashboardHeader
|
export default DashboardHeader
|
||||||
|
|
|
@ -1,70 +1,76 @@
|
||||||
import React, {PropTypes, Component} from 'react'
|
import React, {PropTypes, Component} from 'react'
|
||||||
import ConfirmButtons from 'shared/components/ConfirmButtons'
|
import {
|
||||||
|
DASHBOARD_NAME_MAX_LENGTH,
|
||||||
|
NEW_DASHBOARD,
|
||||||
|
} from 'src/dashboards/constants/index'
|
||||||
|
|
||||||
class DashboardEditHeader extends Component {
|
class DashboardEditHeader extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
const {dashboard: {name}} = props
|
|
||||||
this.state = {
|
this.state = {
|
||||||
name,
|
reset: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange = e => {
|
handleInputBlur = e => {
|
||||||
this.setState({name: e.target.value})
|
const {onSave, onCancel} = this.props
|
||||||
}
|
const {reset} = this.state
|
||||||
|
|
||||||
handleFormSubmit = e => {
|
if (reset) {
|
||||||
e.preventDefault()
|
|
||||||
const name = e.target.name.value
|
|
||||||
this.props.onSave(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyUp = e => {
|
|
||||||
const {onCancel} = this.props
|
|
||||||
if (e.key === 'Escape') {
|
|
||||||
onCancel()
|
onCancel()
|
||||||
|
} else {
|
||||||
|
const newName = e.target.value || NEW_DASHBOARD.name
|
||||||
|
onSave(newName)
|
||||||
|
}
|
||||||
|
this.setState({reset: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyDown = e => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.inputRef.blur()
|
||||||
|
}
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
this.inputRef.value = this.props.dashboardName
|
||||||
|
this.setState({reset: true}, () => this.inputRef.blur())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {onSave, onCancel} = this.props
|
const {onEditDashboard, isEditMode, dashboardName} = this.props
|
||||||
const {name} = this.state
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-header full-width">
|
<div className="dashboard-title">
|
||||||
<div className="page-header__container">
|
{isEditMode
|
||||||
<form
|
? <input
|
||||||
className="page-header__left"
|
maxLength={DASHBOARD_NAME_MAX_LENGTH}
|
||||||
style={{flex: '1 0 0%'}}
|
type="text"
|
||||||
onSubmit={this.handleFormSubmit}
|
className="dashboard-title--input form-control"
|
||||||
>
|
defaultValue={dashboardName}
|
||||||
<input
|
autoComplete="off"
|
||||||
className="page-header--editing"
|
|
||||||
name="name"
|
|
||||||
value={name}
|
|
||||||
placeholder="Name this Dashboard"
|
|
||||||
onKeyUp={this.handleKeyUp}
|
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
autoComplete="off"
|
onBlur={this.handleInputBlur}
|
||||||
onChange={this.handleChange}
|
onKeyDown={this.handleKeyDown}
|
||||||
|
placeholder="Name this Dashboard"
|
||||||
|
ref={r => (this.inputRef = r)}
|
||||||
/>
|
/>
|
||||||
</form>
|
: <h1 onClick={onEditDashboard}>
|
||||||
<ConfirmButtons item={name} onConfirm={onSave} onCancel={onCancel} />
|
{dashboardName}
|
||||||
</div>
|
</h1>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {shape, func} = PropTypes
|
const {bool, func, string} = PropTypes
|
||||||
|
|
||||||
DashboardEditHeader.propTypes = {
|
DashboardEditHeader.propTypes = {
|
||||||
dashboard: shape({}),
|
dashboardName: string.isRequired,
|
||||||
onCancel: func.isRequired,
|
|
||||||
onSave: func.isRequired,
|
onSave: func.isRequired,
|
||||||
|
onCancel: func.isRequired,
|
||||||
|
isEditMode: bool,
|
||||||
|
onEditDashboard: func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DashboardEditHeader
|
export default DashboardEditHeader
|
||||||
|
|
|
@ -109,3 +109,5 @@ export const TOOLTIP_CONTENT = {
|
||||||
|
|
||||||
export const TYPE_QUERY_CONFIG = 'queryConfig'
|
export const TYPE_QUERY_CONFIG = 'queryConfig'
|
||||||
export const TYPE_IFQL = 'ifql'
|
export const TYPE_IFQL = 'ifql'
|
||||||
|
|
||||||
|
export const DASHBOARD_NAME_MAX_LENGTH = 50
|
||||||
|
|
|
@ -8,7 +8,6 @@ import Dygraph from 'src/external/dygraph'
|
||||||
import OverlayTechnologies from 'shared/components/OverlayTechnologies'
|
import OverlayTechnologies from 'shared/components/OverlayTechnologies'
|
||||||
import CellEditorOverlay from 'src/dashboards/components/CellEditorOverlay'
|
import CellEditorOverlay from 'src/dashboards/components/CellEditorOverlay'
|
||||||
import DashboardHeader from 'src/dashboards/components/DashboardHeader'
|
import DashboardHeader from 'src/dashboards/components/DashboardHeader'
|
||||||
import DashboardHeaderEdit from 'src/dashboards/components/DashboardHeaderEdit'
|
|
||||||
import Dashboard from 'src/dashboards/components/Dashboard'
|
import Dashboard from 'src/dashboards/components/Dashboard'
|
||||||
import TemplateVariableManager from 'src/dashboards/components/template_variables/Manager'
|
import TemplateVariableManager from 'src/dashboards/components/template_variables/Manager'
|
||||||
import ManualRefresh from 'src/shared/components/ManualRefresh'
|
import ManualRefresh from 'src/shared/components/ManualRefresh'
|
||||||
|
@ -309,40 +308,36 @@ class DashboardPage extends Component {
|
||||||
editQueryStatus={dashboardActions.editCellQueryStatus}
|
editQueryStatus={dashboardActions.editCellQueryStatus}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
{isEditMode
|
<DashboardHeader
|
||||||
? <DashboardHeaderEdit
|
source={source}
|
||||||
dashboard={dashboard}
|
sourceID={sourceID}
|
||||||
onSave={this.handleRenameDashboard}
|
dashboard={dashboard}
|
||||||
onCancel={this.handleCancelEditDashboard}
|
timeRange={timeRange}
|
||||||
/>
|
isEditMode={isEditMode}
|
||||||
: <DashboardHeader
|
autoRefresh={autoRefresh}
|
||||||
source={source}
|
isHidden={inPresentationMode}
|
||||||
sourceID={sourceID}
|
onAddCell={this.handleAddCell}
|
||||||
dashboard={dashboard}
|
zoomedTimeRange={zoomedTimeRange}
|
||||||
timeRange={timeRange}
|
onSave={this.handleRenameDashboard}
|
||||||
zoomedTimeRange={zoomedTimeRange}
|
onCancel={this.handleCancelEditDashboard}
|
||||||
autoRefresh={autoRefresh}
|
onEditDashboard={this.handleEditDashboard}
|
||||||
isHidden={inPresentationMode}
|
dashboardName={dashboard ? dashboard.name : ''}
|
||||||
onAddCell={this.handleAddCell}
|
showTemplateControlBar={showTemplateControlBar}
|
||||||
onEditDashboard={this.handleEditDashboard}
|
handleChooseAutoRefresh={handleChooseAutoRefresh}
|
||||||
buttonText={dashboard ? dashboard.name : ''}
|
handleChooseTimeRange={this.handleChooseTimeRange}
|
||||||
showTemplateControlBar={showTemplateControlBar}
|
onToggleTempVarControls={this.handleToggleTempVarControls}
|
||||||
handleChooseAutoRefresh={handleChooseAutoRefresh}
|
handleClickPresentationButton={handleClickPresentationButton}
|
||||||
onManualRefresh={onManualRefresh}
|
>
|
||||||
handleChooseTimeRange={this.handleChooseTimeRange}
|
{dashboards
|
||||||
onToggleTempVarControls={this.handleToggleTempVarControls}
|
? dashboards.map((d, i) =>
|
||||||
handleClickPresentationButton={handleClickPresentationButton}
|
<li className="dropdown-item" key={i}>
|
||||||
>
|
<Link to={`/sources/${sourceID}/dashboards/${d.id}`}>
|
||||||
{dashboards
|
{d.name}
|
||||||
? dashboards.map((d, i) =>
|
</Link>
|
||||||
<li className="dropdown-item" key={i}>
|
</li>
|
||||||
<Link to={`/sources/${sourceID}/dashboards/${d.id}`}>
|
)
|
||||||
{d.name}
|
: null}
|
||||||
</Link>
|
</DashboardHeader>
|
||||||
</li>
|
|
||||||
)
|
|
||||||
: null}
|
|
||||||
</DashboardHeader>}
|
|
||||||
{dashboard
|
{dashboard
|
||||||
? <Dashboard
|
? <Dashboard
|
||||||
source={source}
|
source={source}
|
||||||
|
|
|
@ -182,7 +182,7 @@ class HostPage extends Component {
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<DashboardHeader
|
<DashboardHeader
|
||||||
source={source}
|
source={source}
|
||||||
buttonText={hostID}
|
dashboardName={hostID}
|
||||||
timeRange={timeRange}
|
timeRange={timeRange}
|
||||||
autoRefresh={autoRefresh}
|
autoRefresh={autoRefresh}
|
||||||
isHidden={inPresentationMode}
|
isHidden={inPresentationMode}
|
||||||
|
|
|
@ -340,8 +340,48 @@ $tick-script-overlay-margin: 30px;
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
.dropdown.dashboard-switcher {
|
.dropdown.dashboard-switcher {
|
||||||
margin-right: 4px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
.dropdown.dashboard-switcher .btn.dropdown-toggle {
|
.dropdown.dashboard-switcher .btn.dropdown-toggle {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Dashboard Name Editing
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
.page-header__left.page-header__dash-editable,
|
||||||
|
.dashboard-title,
|
||||||
|
.dashboard-title input[type="text"].form-control.dashboard-title--input {
|
||||||
|
flex: 1 0 0;
|
||||||
|
}
|
||||||
|
.dashboard-title {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
input[type="text"].form-control.dashboard-title--input,
|
||||||
|
input[type="text"].form-control.dashboard-title--input:focus {
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: $page-header-size;
|
||||||
|
font-weight: $page-header-weight;
|
||||||
|
color: $c-pool;
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 0;
|
||||||
|
letter-spacing: 0;
|
||||||
|
text-transform: none;
|
||||||
|
font-size: $page-header-size;
|
||||||
|
font-weight: $page-header-weight;
|
||||||
|
transition: color 0.25s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: text;
|
||||||
|
color: $c-pool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue