Introduce component for individual filters

pull/10616/head
Alex P 2018-05-25 15:58:09 -07:00
parent f2fff2385b
commit 6bbed0cadd
4 changed files with 185 additions and 17 deletions

View File

@ -0,0 +1,94 @@
import React, {PureComponent} from 'react'
import classnames from 'classnames'
import {Filter} from 'src/logs/containers/LogsPage'
interface Props {
filter: Filter
onDelete: (id: string) => () => void
onToggleStatus: (id: string) => () => void
onToggleOperator: (id: string) => () => void
}
interface State {
expanded: boolean
}
class LogsFilter extends PureComponent<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
expanded: false,
}
}
public render() {
const {
filter: {id},
onDelete,
} = this.props
const {expanded} = this.state
return (
<li className={this.className} onMouseLeave={this.handleMouseLeave}>
{this.label}
<div className="logs-viewer--filter-remove" onClick={onDelete(id)} />
{expanded && this.renderTooltip}
</li>
)
}
private get label(): JSX.Element {
const {
filter: {key, operator, value},
} = this.props
return (
<span
onMouseEnter={this.handleMouseEnter}
>{`${key} ${operator} ${value}`}</span>
)
}
private get className(): string {
const {expanded} = this.state
const {
filter: {enabled},
} = this.props
return classnames('logs-viewer--filter', {
active: expanded,
disabled: !enabled,
})
}
private handleMouseEnter = (): void => {
this.setState({expanded: true})
}
private handleMouseLeave = (): void => {
this.setState({expanded: false})
}
private get renderTooltip(): JSX.Element {
const {
filter: {id, enabled, operator},
onDelete,
onToggleStatus,
onToggleOperator,
} = this.props
const toggleStatusText = enabled ? 'Disable' : 'Enable'
const toggleOperatorText = operator === '==' ? '!=' : '=='
return (
<ul className="logs-viewer--filter-tooltip">
<li onClick={onToggleStatus(id)}>{toggleStatusText}</li>
<li onClick={onToggleOperator(id)}>{toggleOperatorText}</li>
<li onClick={onDelete(id)}>Delete</li>
</ul>
)
}
}
export default LogsFilter

View File

@ -1,5 +1,6 @@
import React, {PureComponent} from 'react' import React, {PureComponent} from 'react'
import {Filter} from 'src/logs/containers/LogsPage' import {Filter} from 'src/logs/containers/LogsPage'
import FilterBlock from 'src/logs/components/LogsFilter'
interface Props { interface Props {
numResults: number numResults: number
@ -25,31 +26,25 @@ class LogsFilters extends PureComponent<Props> {
const {filters} = this.props const {filters} = this.props
return filters.map(filter => ( return filters.map(filter => (
<li className="logs-viewer--filter"> <FilterBlock
<span> key={filter.id}
{filter.key} filter={filter}
{filter.operator} onDelete={this.handleDeleteFilter}
{filter.value} onToggleStatus={this.handleToggleFilterStatus}
</span> onToggleOperator={this.handleToggleFilterOperator}
<button />
className="logs-viewer--filter-remove"
onClick={this.handleDeleteFilter(filter.id)}
/>
</li>
)) ))
} }
private handleDeleteFilter = (id: string) => (): void => { private handleDeleteFilter = (id: string) => (): void => {
const {filters, onUpdateFilters} = this.props const {filters, onUpdateFilters} = this.props
const filteredFilters = filters.map( const filteredFilters = filters.filter(filter => filter.id !== id)
filter => (filter.id === id ? null : filter)
)
onUpdateFilters(filteredFilters) onUpdateFilters(filteredFilters)
} }
private handleToggleFilter = (id: string) => (): void => { private handleToggleFilterStatus = (id: string) => (): void => {
const {filters, onUpdateFilters} = this.props const {filters, onUpdateFilters} = this.props
const filteredFilters = filters.map(filter => { const filteredFilters = filters.map(filter => {
@ -62,6 +57,28 @@ class LogsFilters extends PureComponent<Props> {
onUpdateFilters(filteredFilters) onUpdateFilters(filteredFilters)
} }
private handleToggleFilterOperator = (id: string) => (): void => {
const {filters, onUpdateFilters} = this.props
const filteredFilters = filters.map(filter => {
if (filter.id === id) {
return {...filter, operator: this.toggleOperator(filter.operator)}
}
return filter
})
onUpdateFilters(filteredFilters)
}
private toggleOperator = (op: string): string => {
if (op === '==') {
return '!='
}
return '=='
}
} }
export default LogsFilters export default LogsFilters

View File

@ -34,13 +34,23 @@ interface State {
filters: Filter[] filters: Filter[]
} }
const DUMMY_FILTERS = [
{
id: '0',
key: 'host',
value: 'prod1-rsavage.local',
operator: '==',
enabled: true,
},
]
class LogsPage extends PureComponent<Props, State> { class LogsPage extends PureComponent<Props, State> {
constructor(props: Props) { constructor(props: Props) {
super(props) super(props)
this.state = { this.state = {
searchString: '', searchString: '',
filters: [], filters: DUMMY_FILTERS,
} }
} }

View File

@ -94,6 +94,7 @@ $logs-viewer-gutter: 60px;
} }
.logs-viewer--filter { .logs-viewer--filter {
position: relative;
font-size: 12px; font-size: 12px;
display: flex; display: flex;
align-items: center; align-items: center;
@ -103,8 +104,20 @@ $logs-viewer-gutter: 60px;
border-radius: 4px; border-radius: 4px;
background-color: $g5-pepper; background-color: $g5-pepper;
color: $g13-mist; color: $g13-mist;
font-weight: 500; font-weight: 600;
margin: 2px; margin: 2px;
&.disabled {
background-color: $g4-onyx;
color: $g9-mountain;
font-style: italic;
}
&.active {
background-color: $g6-smoke;
color: $g15-platinum;
}
} }
.logs-viewer--filter-remove { .logs-viewer--filter-remove {
@ -144,4 +157,38 @@ $logs-viewer-gutter: 60px;
background-color: $c-dreamsicle; background-color: $c-dreamsicle;
} }
} }
}
.logs-viewer--filter-tooltip {
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: stretch;
border-radius: $radius;
z-index: 9999;
@extend %drop-shadow;
background-color: $g4-onyx;
margin: 0;
padding: 0;
list-style: none;
overflow: hidden;
> li {
height: 26px;
line-height: 26px;
padding: 0 8px;
font-size: 13px;
font-weight: 600;
color: $g11-sidewalk;
transition: background-color 0.25s ease, color 0.25s ease;
&:hover {
cursor: pointer;
background-color: $g5-pepper;
color: $g18-cloud;
}
}
} }