Refactor dashboard naming flow and associated UI

pull/2104/head
Alex P 2017-10-10 15:02:19 -07:00 committed by Andrew Watkins
parent 1fe6d76f7e
commit 2c3ab050d7
6 changed files with 155 additions and 112 deletions

View File

@ -1,49 +1,44 @@
import React, {PropTypes} from 'react'
import classnames from 'classnames'
import _ from 'lodash'
import AutoRefreshDropdown from 'shared/components/AutoRefreshDropdown'
import TimeRangeDropdown from 'shared/components/TimeRangeDropdown'
import SourceIndicator from 'shared/components/SourceIndicator'
import GraphTips from 'shared/components/GraphTips'
import DashboardHeaderEdit from 'src/dashboards/components/DashboardHeaderEdit'
const DashboardHeader = ({
onSave,
children,
buttonText,
dashboard,
timeRange: {upper, lower},
zoomedTimeRange: {zoomedLower, zoomedUpper},
autoRefresh,
onCancel,
isEditMode,
isHidden,
dashboard,
onAddCell,
autoRefresh,
dashboardName,
onEditDashboard,
onManualRefresh,
handleChooseTimeRange,
handleChooseAutoRefresh,
onManualRefresh,
handleClickPresentationButton,
onAddCell,
onEditDashboard,
onToggleTempVarControls,
showTemplateControlBar,
timeRange: {upper, lower},
handleClickPresentationButton,
zoomedTimeRange: {zoomedLower, zoomedUpper},
}) =>
isHidden
? null
: <div className="page-header full-width">
<div className="page-header__container">
<div className="page-header__left">
{buttonText &&
<div className="dropdown page-header-dropdown">
<button
className="dropdown-toggle"
type="button"
data-toggle="dropdown"
>
<span>
{buttonText}
</span>
<span className="caret" />
</button>
<ul className="dropdown-menu">
{children}
</ul>
</div>}
<div
className={
dashboard
? 'page-header__left page-header__dash-editable'
: 'page-header__left'
}
>
{children.length > 1
? <div className="dropdown dashboard-switcher">
<button
@ -60,6 +55,17 @@ const DashboardHeader = ({
</ul>
</div>
: null}
{dashboard
? <DashboardHeaderEdit
onSave={onSave}
onCancel={onCancel}
dashboardName={dashboardName}
onEditDashboard={onEditDashboard}
isEditMode={isEditMode}
/>
: <h1 className="page-header__title">
{dashboardName}
</h1>}
</div>
<div className="page-header__right">
<GraphTips />
@ -70,15 +76,6 @@ const DashboardHeader = ({
Add Cell
</button>
: null}
{dashboard
? <button
className="btn btn-default btn-sm"
onClick={onEditDashboard}
>
<span className="icon pencil" />
Rename
</button>
: null}
{dashboard
? <div
className={classnames('btn btn-default btn-sm', {
@ -123,7 +120,8 @@ DashboardHeader.defaultProps = {
DashboardHeader.propTypes = {
children: array,
buttonText: string,
dashboardName: string.isRequired,
onEditDashboard: func.isRequired,
dashboard: shape({}),
timeRange: shape({
lower: string,
@ -131,15 +129,17 @@ DashboardHeader.propTypes = {
}).isRequired,
autoRefresh: number.isRequired,
isHidden: bool.isRequired,
isEditMode: bool,
handleChooseTimeRange: func.isRequired,
handleChooseAutoRefresh: func.isRequired,
onManualRefresh: func.isRequired,
handleClickPresentationButton: func.isRequired,
onAddCell: func,
onEditDashboard: func,
onToggleTempVarControls: func,
showTemplateControlBar: bool,
zoomedTimeRange: shape({}),
onCancel: func.isRequired,
onSave: func.isRequired,
}
export default DashboardHeader

View File

@ -1,70 +1,76 @@
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 {
constructor(props) {
super(props)
const {dashboard: {name}} = props
this.state = {
name,
reset: false,
}
}
handleChange = e => {
this.setState({name: e.target.value})
}
handleInputBlur = e => {
const {onSave, onCancel} = this.props
const {reset} = this.state
handleFormSubmit = e => {
e.preventDefault()
const name = e.target.name.value
this.props.onSave(name)
}
handleKeyUp = e => {
const {onCancel} = this.props
if (e.key === 'Escape') {
if (reset) {
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() {
const {onSave, onCancel} = this.props
const {name} = this.state
const {onEditDashboard, isEditMode, dashboardName} = this.props
return (
<div className="page-header full-width">
<div className="page-header__container">
<form
className="page-header__left"
style={{flex: '1 0 0%'}}
onSubmit={this.handleFormSubmit}
>
<input
className="page-header--editing"
name="name"
value={name}
placeholder="Name this Dashboard"
onKeyUp={this.handleKeyUp}
<div className="dashboard-title">
{isEditMode
? <input
maxLength={DASHBOARD_NAME_MAX_LENGTH}
type="text"
className="dashboard-title--input form-control"
defaultValue={dashboardName}
autoComplete="off"
autoFocus={true}
spellCheck={false}
autoComplete="off"
onChange={this.handleChange}
onBlur={this.handleInputBlur}
onKeyDown={this.handleKeyDown}
placeholder="Name this Dashboard"
ref={r => (this.inputRef = r)}
/>
</form>
<ConfirmButtons item={name} onConfirm={onSave} onCancel={onCancel} />
</div>
: <h1 onClick={onEditDashboard}>
{dashboardName}
</h1>}
</div>
)
}
}
const {shape, func} = PropTypes
const {bool, func, string} = PropTypes
DashboardEditHeader.propTypes = {
dashboard: shape({}),
onCancel: func.isRequired,
dashboardName: string.isRequired,
onSave: func.isRequired,
onCancel: func.isRequired,
isEditMode: bool,
onEditDashboard: func.isRequired,
}
export default DashboardEditHeader

View File

@ -109,3 +109,5 @@ export const TOOLTIP_CONTENT = {
export const TYPE_QUERY_CONFIG = 'queryConfig'
export const TYPE_IFQL = 'ifql'
export const DASHBOARD_NAME_MAX_LENGTH = 50

View File

@ -8,7 +8,6 @@ import Dygraph from 'src/external/dygraph'
import OverlayTechnologies from 'shared/components/OverlayTechnologies'
import CellEditorOverlay from 'src/dashboards/components/CellEditorOverlay'
import DashboardHeader from 'src/dashboards/components/DashboardHeader'
import DashboardHeaderEdit from 'src/dashboards/components/DashboardHeaderEdit'
import Dashboard from 'src/dashboards/components/Dashboard'
import TemplateVariableManager from 'src/dashboards/components/template_variables/Manager'
import ManualRefresh from 'src/shared/components/ManualRefresh'
@ -309,40 +308,36 @@ class DashboardPage extends Component {
editQueryStatus={dashboardActions.editCellQueryStatus}
/>
: null}
{isEditMode
? <DashboardHeaderEdit
dashboard={dashboard}
onSave={this.handleRenameDashboard}
onCancel={this.handleCancelEditDashboard}
/>
: <DashboardHeader
source={source}
sourceID={sourceID}
dashboard={dashboard}
timeRange={timeRange}
zoomedTimeRange={zoomedTimeRange}
autoRefresh={autoRefresh}
isHidden={inPresentationMode}
onAddCell={this.handleAddCell}
onEditDashboard={this.handleEditDashboard}
buttonText={dashboard ? dashboard.name : ''}
showTemplateControlBar={showTemplateControlBar}
handleChooseAutoRefresh={handleChooseAutoRefresh}
onManualRefresh={onManualRefresh}
handleChooseTimeRange={this.handleChooseTimeRange}
onToggleTempVarControls={this.handleToggleTempVarControls}
handleClickPresentationButton={handleClickPresentationButton}
>
{dashboards
? dashboards.map((d, i) =>
<li className="dropdown-item" key={i}>
<Link to={`/sources/${sourceID}/dashboards/${d.id}`}>
{d.name}
</Link>
</li>
)
: null}
</DashboardHeader>}
<DashboardHeader
source={source}
sourceID={sourceID}
dashboard={dashboard}
timeRange={timeRange}
isEditMode={isEditMode}
autoRefresh={autoRefresh}
isHidden={inPresentationMode}
onAddCell={this.handleAddCell}
zoomedTimeRange={zoomedTimeRange}
onSave={this.handleRenameDashboard}
onCancel={this.handleCancelEditDashboard}
onEditDashboard={this.handleEditDashboard}
dashboardName={dashboard ? dashboard.name : ''}
showTemplateControlBar={showTemplateControlBar}
handleChooseAutoRefresh={handleChooseAutoRefresh}
handleChooseTimeRange={this.handleChooseTimeRange}
onToggleTempVarControls={this.handleToggleTempVarControls}
handleClickPresentationButton={handleClickPresentationButton}
>
{dashboards
? dashboards.map((d, i) =>
<li className="dropdown-item" key={i}>
<Link to={`/sources/${sourceID}/dashboards/${d.id}`}>
{d.name}
</Link>
</li>
)
: null}
</DashboardHeader>
{dashboard
? <Dashboard
source={source}

View File

@ -182,7 +182,7 @@ class HostPage extends Component {
<div className="page">
<DashboardHeader
source={source}
buttonText={hostID}
dashboardName={hostID}
timeRange={timeRange}
autoRefresh={autoRefresh}
isHidden={inPresentationMode}

View File

@ -340,8 +340,48 @@ $tick-script-overlay-margin: 30px;
-----------------------------------------------------------------------------
*/
.dropdown.dashboard-switcher {
margin-right: 4px;
margin-right: 10px;
}
.dropdown.dashboard-switcher .btn.dropdown-toggle {
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;
}
}
}