Storybook fixes. Add stateful class MultiSelectDropdown shared component. Add Select Roles story for that component.
parent
22ba509ea7
commit
0a1f310deb
|
@ -1,28 +0,0 @@
|
||||||
const express = require('express');
|
|
||||||
const request = require('request');
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
app.use('/', (req, res) => {
|
|
||||||
console.log(`${req.method} ${req.url}`);
|
|
||||||
|
|
||||||
const headers = {};
|
|
||||||
headers['Access-Control-Allow-Origin'] = '*';
|
|
||||||
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS';
|
|
||||||
headers['Access-Control-Allow-Credentials'] = false;
|
|
||||||
headers['Access-Control-Max-Age'] = '86400'; // 24 hours
|
|
||||||
headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept';
|
|
||||||
res.writeHead(200, headers);
|
|
||||||
|
|
||||||
if (req.method === 'OPTIONS') {
|
|
||||||
res.end();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const url = 'http://localhost:8888' + req.url;
|
|
||||||
req.pipe(request(url)).pipe(res);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(3888, () => {
|
|
||||||
console.log('corsless proxy server now running')
|
|
||||||
});
|
|
|
@ -17,9 +17,7 @@
|
||||||
"test:lint": "npm run lint; npm run test",
|
"test:lint": "npm run lint; npm run test",
|
||||||
"test:dev": "nodemon --exec npm run test:lint",
|
"test:dev": "nodemon --exec npm run test:lint",
|
||||||
"clean": "rm -rf build",
|
"clean": "rm -rf build",
|
||||||
"storybook": "start-storybook -p 6006",
|
"storybook": "node ./storybook"
|
||||||
"build-storybook": "build-storybook",
|
|
||||||
"proxy": "node ./corsless"
|
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
import React, {Component, PropTypes} from 'react'
|
||||||
|
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
class MultiSelectDropdown extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isOpen: false,
|
||||||
|
localSelectedItems: this.props.selectedItems,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onSelect = ::this.onSelect
|
||||||
|
this.onApplyFunctions = ::this.onApplyFunctions
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (!_.isEqual(this.state.localSelectedItems, nextProps.selectedItems)) {
|
||||||
|
this.setState({
|
||||||
|
localSelectedItems: nextProps.selectedItems,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClickOutside() {
|
||||||
|
this.setState({isOpen: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleMenu(e) {
|
||||||
|
e.stopPropagation()
|
||||||
|
this.setState({isOpen: !this.state.isOpen})
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect(item, e) {
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
const {localSelectedItems} = this.state
|
||||||
|
|
||||||
|
let nextItems
|
||||||
|
if (this.isSelected(item)) {
|
||||||
|
nextItems = localSelectedItems.filter((i) => i !== item)
|
||||||
|
} else {
|
||||||
|
nextItems = localSelectedItems.concat(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({localSelectedItems: nextItems})
|
||||||
|
}
|
||||||
|
|
||||||
|
isSelected(item) {
|
||||||
|
return this.state.localSelectedItems.indexOf(item) > -1
|
||||||
|
}
|
||||||
|
|
||||||
|
onApplyFunctions(e) {
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
this.setState({isOpen: false})
|
||||||
|
this.props.onApply(this.state.localSelectedItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {localSelectedItems, isOpen} = this.state
|
||||||
|
const {label} = this.props
|
||||||
|
const labelText = isOpen ? "0 Selected" : "Apply Function"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames('dropdown multi-select-dropdown', {open: isOpen})}>
|
||||||
|
<div onClick={::this.toggleMenu} className="btn btn-xs btn-info dropdown-toggle" type="button">
|
||||||
|
<span className="multi-select-dropdown__label">
|
||||||
|
{
|
||||||
|
label ?
|
||||||
|
label :
|
||||||
|
localSelectedItems.length ? localSelectedItems.map((s) => s).join(', ') : labelText
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
<span className="caret"></span>
|
||||||
|
</div>
|
||||||
|
{this.renderMenu()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMenu() {
|
||||||
|
const {items} = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="dropdown-options">
|
||||||
|
<li className="multi-select-dropdown__apply" onClick={this.onApplyFunctions} style={{listStyle: 'none'}}>
|
||||||
|
<div className="btn btn-xs btn-info btn-block">Apply</div>
|
||||||
|
</li>
|
||||||
|
<ul className="dropdown-menu multi-select-dropdown__menu" aria-labelledby="dropdownMenu1">
|
||||||
|
{items.map((listItem, i) => {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
key={i}
|
||||||
|
className={classNames('multi-select-dropdown__item', {active: this.isSelected(listItem)})}
|
||||||
|
onClick={_.wrap(listItem, this.onSelect)}
|
||||||
|
>
|
||||||
|
<a href="#">{listItem}</a>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
arrayOf,
|
||||||
|
func,
|
||||||
|
string,
|
||||||
|
} = PropTypes
|
||||||
|
|
||||||
|
MultiSelectDropdown.propTypes = {
|
||||||
|
onApply: func.isRequired,
|
||||||
|
items: arrayOf(string.isRequired).isRequired,
|
||||||
|
selectedItems: arrayOf(string.isRequired).isRequired,
|
||||||
|
label: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OnClickOutside(MultiSelectDropdown)
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {storiesOf, action, linkTo} from '@kadira/storybook'
|
||||||
|
import MultiSelectDropdown from 'shared/components/MultiSelectDropdown'
|
||||||
|
|
||||||
|
storiesOf('MultiSelectDropdown', module)
|
||||||
|
.add('Select Roles', () => (
|
||||||
|
<MultiSelectDropdown
|
||||||
|
items={[
|
||||||
|
'Admin',
|
||||||
|
'User',
|
||||||
|
'Chrono Giraffe',
|
||||||
|
'Prophet',
|
||||||
|
'Susford',
|
||||||
|
]}
|
||||||
|
selectedItems={[
|
||||||
|
'User',
|
||||||
|
'Chrono Giraffe',
|
||||||
|
]}
|
||||||
|
label={'Select Roles'}
|
||||||
|
onApply={action('onApply')}
|
||||||
|
/>
|
||||||
|
))
|
|
@ -3,3 +3,4 @@ import 'src/style/chronograf.scss';
|
||||||
|
|
||||||
// Kapacitor Stories
|
// Kapacitor Stories
|
||||||
import './kapacitor'
|
import './kapacitor'
|
||||||
|
import './admin'
|
||||||
|
|
|
@ -12,7 +12,7 @@ import queryConfigs from './stubs/queryConfigs';
|
||||||
|
|
||||||
// Actions for Spies
|
// Actions for Spies
|
||||||
import * as kapacitorActions from 'src/kapacitor/actions/view'
|
import * as kapacitorActions from 'src/kapacitor/actions/view'
|
||||||
import * as queryActions from 'src/chronograf/actions/view';
|
import * as queryActions from 'src/data_explorer/actions/view';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import KapacitorRule from 'src/kapacitor/components/KapacitorRule';
|
import KapacitorRule from 'src/kapacitor/components/KapacitorRule';
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
const express = require('express')
|
||||||
|
const request = require('request')
|
||||||
|
const {default: storybook} = require('@kadira/storybook/dist/server/middleware')
|
||||||
|
|
||||||
|
const app = express()
|
||||||
|
|
||||||
|
const handler = (req, res) => {
|
||||||
|
console.log(`${req.method} ${req.url}`)
|
||||||
|
const url = 'http://localhost:8888' + req.url
|
||||||
|
req.pipe(request(url)).pipe(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use(storybook('./.storybook'))
|
||||||
|
app.get('/chronograf/v1/*', handler)
|
||||||
|
app.post('/chronograf/v1/*', handler)
|
||||||
|
|
||||||
|
app.listen(6006, () => {
|
||||||
|
console.log('storybook proxy server now running')
|
||||||
|
})
|
Loading…
Reference in New Issue