Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dashboard): add ability to change order of variable dropdowns #12821

Merged
merged 1 commit into from
Mar 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
1. [12843](https://github.com/influxdata/influxdb/pull/12843): Add copy to clipboard button to export overlays
1. [12826](https://github.com/influxdata/influxdb/pull/12826): Enable copying error messages to the clipboard from dashboard cells
1. [12876](https://github.com/influxdata/influxdb/pull/12876): Add the ability to update token's status in Token list
1. [12821](https://github.com/influxdata/influxdb/pull/12821): Allow variables to be re-ordered within control bar on a dashboard.

### Bug Fixes

Expand All @@ -20,6 +21,7 @@
1. [12790](https://github.com/influxdata/influxdb/pull/12790): Fix bucket creation error when changing rentention rules types.
1. [12793](https://github.com/influxdata/influxdb/pull/12793): Fix task creation error when switching schedule types.
1. [12805](https://github.com/influxdata/influxdb/pull/12805): Fix hidden horizonal scrollbars in flux raw data view
1. [12827](https://github.com/influxdata/influxdb/pull/12827): Fix screen tearing bug in Raw Data View

### UI Improvements

Expand Down
118 changes: 118 additions & 0 deletions ui/src/dashboards/components/variablesControlBar/DraggableDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Libraries
import * as React from 'react'
import {
DragSource,
DropTarget,
ConnectDropTarget,
ConnectDragSource,
DropTargetConnector,
DragSourceConnector,
DragSourceMonitor,
} from 'react-dnd'
import classnames from 'classnames'

// Components
import VariableDropdown from './VariableDropdown'

// Constants
const dropdownType = 'dropdown'

const dropdownSource = {
beginDrag(props: Props) {
return {
id: props.id,
index: props.index,
}
},
}

interface Props {
id: string
index: number
name: string
moveDropdown: (dragIndex: number, hoverIndex: number) => void
dashboardID: string
}

interface DropdownSourceCollectedProps {
isDragging: boolean
connectDragSource: ConnectDragSource
}

interface DropdownTargetCollectedProps {
connectDropTarget?: ConnectDropTarget
}

const dropdownTarget = {
hover(props, monitor, component) {
if (!component) {
return null
}
const dragIndex = monitor.getItem().index
const hoverIndex = props.index

// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return
}

// Time to actually perform the action
props.moveDropdown(dragIndex, hoverIndex)

monitor.getItem().index = hoverIndex
},
}

class Dropdown extends React.Component<
Props & DropdownSourceCollectedProps & DropdownTargetCollectedProps
> {
public render() {
const {
name,
id,
dashboardID,
isDragging,
connectDragSource,
connectDropTarget,
} = this.props

const className = classnames('variable-dropdown', {
'variable-dropdown__dragging': isDragging,
})

return connectDragSource(
connectDropTarget(
<div style={{display: 'inline-block'}}>
<div className={className}>
{/* TODO: Add variable description to title attribute when it is ready */}
<div className="variable-dropdown--label">
<div className="customizable-field--drag">
<span className="hamburger" />
</div>
<span>{name}</span>
</div>
<div className="variable-dropdown--placeholder" />
<VariableDropdown variableID={id} dashboardID={dashboardID} />
</div>
</div>
)
)
}
}

export default DropTarget<Props & DropdownTargetCollectedProps>(
dropdownType,
dropdownTarget,
(connect: DropTargetConnector) => ({
connectDropTarget: connect.dropTarget(),
})
)(
DragSource<Props & DropdownSourceCollectedProps>(
dropdownType,
dropdownSource,
(connect: DragSourceConnector, monitor: DragSourceMonitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
})
)(Dropdown)
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
display: flex;
flex-direction: row;
height: $form-sm-height;
position: relative;
border-radius: $radius;
}

.variable-dropdown--dropdown {
Expand All @@ -21,14 +23,33 @@
color: $c-comet;
white-space: nowrap;
overflow: hidden;
padding: 0 $form-sm-padding;
padding: 0 $form-sm-padding 0 0;
border-radius: $radius 0 0 $radius;
background-color: $g3-castle;
background-attachment: fixed;
display: flex;

> span {
@include gradient-h($c-comet, $c-laser);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}

.variable-dropdown--placeholder {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border-radius: $radius;
@include gradient-h($c-comet, $c-pool);
opacity: 0;
pointer-events: none;
z-index: 2;
transition: opacity 0.25s ease;

.variable-dropdown__dragging & {
opacity: 1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ interface DispatchProps {
}

interface OwnProps {
name: string
variableID: string
dashboardID: string
}
Expand All @@ -38,15 +37,12 @@ type Props = StateProps & DispatchProps & OwnProps

class VariableDropdown extends PureComponent<Props> {
render() {
const {name, selectedValue} = this.props
const {selectedValue} = this.props
const dropdownValues = this.props.values || []

return (
<div className="variable-dropdown">
{/* TODO: Add variable description to title attribute when it is ready */}
<div className="variable-dropdown--label">
<span>{name}</span>
</div>
<Dropdown
selectedID={selectedValue}
onChange={this.handleSelect}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// Libraries
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
import _ from 'lodash'
import {isEmpty} from 'lodash'
import {DragDropContext} from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'

// Components
import VariableDropdown from 'src/dashboards/components/variablesControlBar/VariableDropdown'
import {EmptyState, ComponentSize} from 'src/clockface'
import {TechnoSpinner} from '@influxdata/clockface'

Expand All @@ -14,13 +15,20 @@ import {
getDashboardValuesStatus,
} from 'src/variables/selectors'

// Styles
import 'src/dashboards/components/variablesControlBar/VariablesControlBar.scss'

// Actions
import {moveVariable} from 'src/variables/actions'

// Types
import {AppState} from 'src/types/v2'
import {Variable} from '@influxdata/influx'

// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors'
import {RemoteDataState} from 'src/types'
import DraggableDropdown from 'src/dashboards/components/variablesControlBar/DraggableDropdown'

interface OwnProps {
dashboardID: string
Expand All @@ -31,14 +39,18 @@ interface StateProps {
valuesStatus: RemoteDataState
}

type Props = StateProps & OwnProps
interface DispatchProps {
moveVariable: typeof moveVariable
}

type Props = StateProps & DispatchProps & OwnProps

@ErrorHandling
class VariablesControlBar extends PureComponent<Props> {
render() {
const {dashboardID, variables, valuesStatus} = this.props

if (_.isEmpty(variables)) {
if (isEmpty(variables)) {
return (
<div className="variables-control-bar">
<EmptyState
Expand All @@ -53,12 +65,14 @@ class VariablesControlBar extends PureComponent<Props> {

return (
<div className="variables-control-bar">
{variables.map(v => (
<VariableDropdown
{variables.map((v, i) => (
<DraggableDropdown
key={v.id}
name={v.name}
variableID={v.id}
id={v.id}
index={i}
dashboardID={dashboardID}
moveDropdown={this.handleMoveDropdown}
/>
))}
{valuesStatus === RemoteDataState.Loading && (
Expand All @@ -67,6 +81,18 @@ class VariablesControlBar extends PureComponent<Props> {
</div>
)
}

private handleMoveDropdown = (
originalIndex: number,
newIndex: number
): void => {
const {dashboardID, moveVariable} = this.props
moveVariable(originalIndex, newIndex, dashboardID)
}
}

const mdtp = {
moveVariable,
}

const mstp = (state: AppState, props: OwnProps): StateProps => {
Expand All @@ -76,4 +102,9 @@ const mstp = (state: AppState, props: OwnProps): StateProps => {
return {variables, valuesStatus}
}

export default connect<StateProps, {}, OwnProps>(mstp)(VariablesControlBar)
export default DragDropContext(HTML5Backend)(
connect<StateProps, DispatchProps, OwnProps>(
mstp,
mdtp
)(VariablesControlBar)
)
2 changes: 2 additions & 0 deletions ui/src/localStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const loadLocalStorage = (): LocalStorage => {
export const saveToLocalStorage = ({
app: {persisted},
ranges,
variables,
}: LocalStorage): void => {
try {
const appPersisted = {app: {persisted}}
Expand All @@ -39,6 +40,7 @@ export const saveToLocalStorage = ({
...appPersisted,
VERSION,
ranges: normalizer(ranges),
variables,
})
)
} catch (err) {
Expand Down
2 changes: 2 additions & 0 deletions ui/src/mockState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Provider} from 'react-redux'
import {Router, createMemoryHistory} from 'react-router'

import {render} from 'react-testing-library'
import {initialState} from 'src/variables/reducers'
import configureStore from 'src/store/configureStore'

const localState = {
Expand All @@ -24,6 +25,7 @@ const localState = {
duration: '15m',
},
],
variables: initialState(),
}

const history = createMemoryHistory({entries: ['/']})
Expand Down
2 changes: 2 additions & 0 deletions ui/src/types/localStorage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {AppState} from 'src/shared/reducers/app'
import {VariablesState} from 'src/variables/reducers'

export interface LocalStorage {
VERSION: string
app: AppState
ranges: any[]
variables: VariablesState
}
15 changes: 15 additions & 0 deletions ui/src/variables/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type Action =
| SetVariables
| SetVariable
| RemoveVariable
| MoveVariable
| SetValues
| SelectValue

Expand Down Expand Up @@ -75,6 +76,20 @@ const removeVariable = (id: string): RemoveVariable => ({
payload: {id},
})

interface MoveVariable {
type: 'MOVE_VARIABLE'
payload: {originalIndex: number; newIndex: number; contextID: string}
}

export const moveVariable = (
originalIndex: number,
newIndex: number,
contextID: string
): MoveVariable => ({
type: 'MOVE_VARIABLE',
payload: {originalIndex, newIndex, contextID},
})

interface SetValues {
type: 'SET_VARIABLE_VALUES'
payload: {
Expand Down
Loading