From 74e48370740f9f6c605f58e00546e413a15fdcb2 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 24 May 2018 15:34:53 -0700 Subject: [PATCH 1/8] Condense page header --- ui/src/logs/components/LogViewerHeader.tsx | 44 ++++++++++++-------- ui/src/logs/components/TimeRangeDropdown.tsx | 23 +++++++--- ui/src/logs/containers/LogsPage.tsx | 9 +--- 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/ui/src/logs/components/LogViewerHeader.tsx b/ui/src/logs/components/LogViewerHeader.tsx index 33c72ef205..4ab7f5ebe9 100644 --- a/ui/src/logs/components/LogViewerHeader.tsx +++ b/ui/src/logs/components/LogViewerHeader.tsx @@ -26,24 +26,32 @@ class LogViewerHeader extends PureComponent { public render(): JSX.Element { const {timeRange} = this.props return ( - <> - - - - +
+
+
+

Log Viewer

+
+
+ + + +
+
+
) } diff --git a/ui/src/logs/components/TimeRangeDropdown.tsx b/ui/src/logs/components/TimeRangeDropdown.tsx index 7aa1b27d87..2d85f41da9 100644 --- a/ui/src/logs/components/TimeRangeDropdown.tsx +++ b/ui/src/logs/components/TimeRangeDropdown.tsx @@ -56,16 +56,12 @@ class TimeRangeDropdown extends Component { public render() { const {selected, preventCustomTimeRange, page} = this.props - const {customTimeRange, isCustomTimeRangeOpen, isOpen} = this.state + const {customTimeRange, isCustomTimeRangeOpen} = this.state return (
-
+
{ ) } + private get dropdownClassName(): string { + const { + isOpen, + customTimeRange: {lower, upper}, + } = this.state + + const absoluteTimeRange = !!lower || !!upper + + return classnames('dropdown', { + 'dropdown-290': absoluteTimeRange, + 'dropdown-120': !absoluteTimeRange, + open: isOpen, + }) + } + private findTimeRangeInputValue = ({upper, lower}: TimeRange) => { if (upper && lower) { if (upper === 'now()') { diff --git a/ui/src/logs/containers/LogsPage.tsx b/ui/src/logs/containers/LogsPage.tsx index b48a1843b6..fcba7a54cb 100644 --- a/ui/src/logs/containers/LogsPage.tsx +++ b/ui/src/logs/containers/LogsPage.tsx @@ -33,14 +33,7 @@ class LogsPage extends PureComponent { public render() { return (
-
-
-
-

Log Viewer

-
-
{this.header}
-
-
+ {this.header}
From abef7cd87aba34332512515991361ed3d03b3d28 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 24 May 2018 16:52:24 -0700 Subject: [PATCH 2/8] Add static markup & styles for Search and Filtering --- ui/src/logs/components/LogsSearchBar.tsx | 52 ++++++++ ui/src/logs/components/LogsTableContainer.tsx | 5 +- ui/src/style/pages/logs-viewer.scss | 119 +++++++++++++++++- 3 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 ui/src/logs/components/LogsSearchBar.tsx diff --git a/ui/src/logs/components/LogsSearchBar.tsx b/ui/src/logs/components/LogsSearchBar.tsx new file mode 100644 index 0000000000..5440b00956 --- /dev/null +++ b/ui/src/logs/components/LogsSearchBar.tsx @@ -0,0 +1,52 @@ +import React, {PureComponent} from 'react' + +interface Props { + thing: string +} + +class LogsSearchBar extends PureComponent { + public render() { + return ( +
+
+
+ + +
+ +
+
+ +
    +
  • + host='swoggle' +
  • +
  • + appname='plunger' +
  • +
  • + appname='bug-zapper' +
  • +
+
+
+ ) + } +} + +export default LogsSearchBar diff --git a/ui/src/logs/components/LogsTableContainer.tsx b/ui/src/logs/components/LogsTableContainer.tsx index 6388be7dba..88f993a711 100644 --- a/ui/src/logs/components/LogsTableContainer.tsx +++ b/ui/src/logs/components/LogsTableContainer.tsx @@ -1,4 +1,5 @@ import React, {PureComponent} from 'react' +import LogsSearchBar from 'src/logs/components/LogsSearchBar' interface Props { thing: string @@ -8,9 +9,7 @@ class LogsTableContainer extends PureComponent { public render() { return ( <> -
-

search

-
+

{this.props.thing}

diff --git a/ui/src/style/pages/logs-viewer.scss b/ui/src/style/pages/logs-viewer.scss index 59ee24d9a5..294543a847 100644 --- a/ui/src/style/pages/logs-viewer.scss +++ b/ui/src/style/pages/logs-viewer.scss @@ -11,7 +11,7 @@ $logs-viewer-gutter: 60px; display: flex; flex-direction: column; align-items: stretch; - flex-wrap: none; + flex-wrap: nowrap; } .logs-viewer--graph-container { @@ -21,6 +21,9 @@ $logs-viewer-gutter: 60px; } .logs-viewer--search-container { + display: flex; + flex-direction: column; + align-items: stretch; padding: 20px $logs-viewer-gutter; height: $logs-viewer-search-height; background-color: $g3-castle; @@ -30,4 +33,118 @@ $logs-viewer-gutter: 60px; padding: 12px $logs-viewer-gutter 30px $logs-viewer-gutter; height: calc(100% - #{$logs-viewer-graph-height + $logs-viewer-search-height}); background-color: $g3-castle; +} + +// Search Bar +.logs-viewer--search { + display: flex; + align-items: center; + flex-wrap: nowrap; +} + +.logs-viewer--search-input { + flex: 1 0 0; + margin-right: 8px; + position: relative; + + > span.icon.search { + font-size: 14px; + position: absolute; + top: 50%; + left: 12px; + transform: translateY(-50%); + color: $g8-storm; + transition: color 0.25s ease; + } + + > input.form-control.input-sm { + padding-left: 30px; + } + + > input.form-control.input-sm:focus + span.icon.search { + color: $c-pool; + } +} + +// Filters Bar +.logs-viewer--filters-container { + display: flex; + align-items: center; + @include no-user-select(); + margin-top: 8px; +} + +.logs-viewer--results-text { + margin: 0 12px 0 33px; + padding: 0; + font-size: 13px; + line-height: 13px; + font-weight: 500; + color: $g9-mountain; + + strong { + color: $g15-platinum; + font-weight: 700; + } +} + +.logs-viewer--filters { + flex: 1 0 0; + margin: 0; + padding: 0; + display: flex; + align-items: center; +} + +.logs-viewer--filter { + font-size: 12px; + display: flex; + align-items: center; + list-style: none; + padding: 0 2px 0 8px; + height: 26px; + border-radius: 4px; + background-color: $g5-pepper; + color: $g13-mist; + font-weight: 500; + margin: 2px; +} + +.logs-viewer--filter-remove { + outline: none; + width: 24px; + height: 24px; + background-color: transparent; + border: 0; + position: relative; + + &:before, + &:after { + position: absolute; + top: 50%; + left: 50%; + width: 12px; + height: 2px; + border-radius: 1px; + background-color: $g8-storm; + transition: background-color 0.25s ease; + content: ''; + } + + &:before { + transform: translate(-50%, -50%) rotate(-45deg); + } + + &:after { + transform: translate(-50%, -50%) rotate(45deg); + } + + &:hover { + cursor: pointer; + + &:before, + &:after { + background-color: $c-dreamsicle; + } + } } \ No newline at end of file From d61ffc906ca7846f581dab37833dd64c776cab8e Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 25 May 2018 10:54:46 -0700 Subject: [PATCH 3/8] Mock out search state and interactions --- ...sSearchBar.tsx => LogsSearchContainer.tsx} | 23 +++++++++--- ui/src/logs/components/LogsTableContainer.tsx | 10 ++---- ui/src/logs/containers/LogsPage.tsx | 35 +++++++++++++++++-- 3 files changed, 54 insertions(+), 14 deletions(-) rename ui/src/logs/components/{LogsSearchBar.tsx => LogsSearchContainer.tsx} (68%) diff --git a/ui/src/logs/components/LogsSearchBar.tsx b/ui/src/logs/components/LogsSearchContainer.tsx similarity index 68% rename from ui/src/logs/components/LogsSearchBar.tsx rename to ui/src/logs/components/LogsSearchContainer.tsx index 5440b00956..dda1aa4512 100644 --- a/ui/src/logs/components/LogsSearchBar.tsx +++ b/ui/src/logs/components/LogsSearchContainer.tsx @@ -1,11 +1,16 @@ -import React, {PureComponent} from 'react' +import React, {PureComponent, ChangeEvent, KeyboardEvent} from 'react' interface Props { - thing: string + numResults: number + searchString: string + onChange: (e: ChangeEvent) => void + onSearch: () => void } class LogsSearchBar extends PureComponent { public render() { + const {searchString, onSearch, onChange, numResults} = this.props + return (
@@ -13,21 +18,23 @@ class LogsSearchBar extends PureComponent {
-
  • @@ -47,6 +54,12 @@ class LogsSearchBar extends PureComponent {
) } + + private handleInputKeyDown = (e: KeyboardEvent): void => { + if (e.key === 'Enter') { + return this.props.onSearch() + } + } } export default LogsSearchBar diff --git a/ui/src/logs/components/LogsTableContainer.tsx b/ui/src/logs/components/LogsTableContainer.tsx index 88f993a711..ef6d52efe5 100644 --- a/ui/src/logs/components/LogsTableContainer.tsx +++ b/ui/src/logs/components/LogsTableContainer.tsx @@ -1,5 +1,4 @@ import React, {PureComponent} from 'react' -import LogsSearchBar from 'src/logs/components/LogsSearchBar' interface Props { thing: string @@ -8,12 +7,9 @@ interface Props { class LogsTableContainer extends PureComponent { public render() { return ( - <> - -
-

{this.props.thing}

-
- +
+

{this.props.thing}

+
) } } diff --git a/ui/src/logs/containers/LogsPage.tsx b/ui/src/logs/containers/LogsPage.tsx index fcba7a54cb..e6e633839b 100644 --- a/ui/src/logs/containers/LogsPage.tsx +++ b/ui/src/logs/containers/LogsPage.tsx @@ -1,4 +1,4 @@ -import React, {PureComponent} from 'react' +import React, {PureComponent, ChangeEvent} from 'react' import {connect} from 'react-redux' import {getSourceAsync, setTimeRange, setNamespace} from 'src/logs/actions' import {getSourcesAsync} from 'src/shared/actions/sources' @@ -6,6 +6,7 @@ import {Source, Namespace, TimeRange} from 'src/types' import LogViewerHeader from 'src/logs/components/LogViewerHeader' import GraphContainer from 'src/logs/components/LogsGraphContainer' import TableContainer from 'src/logs/components/LogsTableContainer' +import SearchContainer from 'src/logs/components/LogsSearchContainer' interface Props { sources: Source[] @@ -19,7 +20,19 @@ interface Props { timeRange: TimeRange } -class LogsPage extends PureComponent { +interface State { + searchString: string +} + +class LogsPage extends PureComponent { + constructor(props: Props) { + super(props) + + this.state = { + searchString: '', + } + } + public componentDidUpdate() { if (!this.props.currentSource) { this.props.getSource(this.props.sources[0].id) @@ -31,11 +44,19 @@ class LogsPage extends PureComponent { } public render() { + const {searchString} = this.state + return (
{this.header}
+
@@ -65,6 +86,16 @@ class LogsPage extends PureComponent { ) } + private handleSearchInputChange = ( + e: ChangeEvent + ): void => { + this.setState({searchString: e.target.value}) + } + + private handleSubmitSearch = (): void => { + // do the thing + } + private handleChooseTimerange = (timeRange: TimeRange) => { this.props.setTimeRange(timeRange) } From 28aeb076d708a9144ccb0654fcd1f5017b9c04ed Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 25 May 2018 11:59:49 -0700 Subject: [PATCH 4/8] Rename files and flesh out filter bar component --- ui/src/logs/components/LogsFilterBar.tsx | 67 +++++++++++++++++++ .../{LogsGraphContainer.tsx => LogsGraph.tsx} | 0 ui/src/logs/components/LogsSearchBar.tsx | 43 ++++++++++++ .../logs/components/LogsSearchContainer.tsx | 65 ------------------ .../{LogsTableContainer.tsx => LogsTable.tsx} | 0 ui/src/logs/containers/LogsPage.tsx | 35 +++++++--- ui/src/style/pages/logs-viewer.scss | 25 +++---- 7 files changed, 148 insertions(+), 87 deletions(-) create mode 100644 ui/src/logs/components/LogsFilterBar.tsx rename ui/src/logs/components/{LogsGraphContainer.tsx => LogsGraph.tsx} (100%) create mode 100644 ui/src/logs/components/LogsSearchBar.tsx delete mode 100644 ui/src/logs/components/LogsSearchContainer.tsx rename ui/src/logs/components/{LogsTableContainer.tsx => LogsTable.tsx} (100%) diff --git a/ui/src/logs/components/LogsFilterBar.tsx b/ui/src/logs/components/LogsFilterBar.tsx new file mode 100644 index 0000000000..c3f6aa8e79 --- /dev/null +++ b/ui/src/logs/components/LogsFilterBar.tsx @@ -0,0 +1,67 @@ +import React, {PureComponent} from 'react' +import {Filter} from 'src/logs/containers/LogsPage' + +interface Props { + numResults: number + filters: Filter[] + onUpdateFilters: (fitlers: Filter[]) => void +} + +class LogsFilters extends PureComponent { + public render() { + const {numResults} = this.props + + return ( +
+ +
    {this.renderFilters}
+
+ ) + } + + private get renderFilters(): JSX.Element[] { + const {filters} = this.props + + return filters.map(filter => ( +
  • + + {filter.key} + {filter.operator} + {filter.value} + +
  • + )) + } + + private handleDeleteFilter = (id: string) => (): void => { + const {filters, onUpdateFilters} = this.props + + const filteredFilters = filters.map( + filter => (filter.id === id ? null : filter) + ) + + onUpdateFilters(filteredFilters) + } + + private handleToggleFilter = (id: string) => (): void => { + const {filters, onUpdateFilters} = this.props + + const filteredFilters = filters.map(filter => { + if (filter.id === id) { + return {...filter, enabled: !filter.enabled} + } + + return filter + }) + + onUpdateFilters(filteredFilters) + } +} + +export default LogsFilters diff --git a/ui/src/logs/components/LogsGraphContainer.tsx b/ui/src/logs/components/LogsGraph.tsx similarity index 100% rename from ui/src/logs/components/LogsGraphContainer.tsx rename to ui/src/logs/components/LogsGraph.tsx diff --git a/ui/src/logs/components/LogsSearchBar.tsx b/ui/src/logs/components/LogsSearchBar.tsx new file mode 100644 index 0000000000..fdfa233357 --- /dev/null +++ b/ui/src/logs/components/LogsSearchBar.tsx @@ -0,0 +1,43 @@ +import React, {PureComponent, ChangeEvent, KeyboardEvent} from 'react' + +interface Props { + searchString: string + onChange: (e: ChangeEvent) => void + onSearch: () => void +} + +class LogsSearchBar extends PureComponent { + public render() { + const {searchString, onSearch, onChange} = this.props + + return ( +
    +
    + + +
    + +
    + ) + } + + private handleInputKeyDown = (e: KeyboardEvent): void => { + if (e.key === 'Enter') { + return this.props.onSearch() + } + } +} + +export default LogsSearchBar diff --git a/ui/src/logs/components/LogsSearchContainer.tsx b/ui/src/logs/components/LogsSearchContainer.tsx deleted file mode 100644 index dda1aa4512..0000000000 --- a/ui/src/logs/components/LogsSearchContainer.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, {PureComponent, ChangeEvent, KeyboardEvent} from 'react' - -interface Props { - numResults: number - searchString: string - onChange: (e: ChangeEvent) => void - onSearch: () => void -} - -class LogsSearchBar extends PureComponent { - public render() { - const {searchString, onSearch, onChange, numResults} = this.props - - return ( -
    -
    -
    - - -
    - -
    -
    - -
      -
    • - host='swoggle' -
    • -
    • - appname='plunger' -
    • -
    • - appname='bug-zapper' -
    • -
    -
    -
    - ) - } - - private handleInputKeyDown = (e: KeyboardEvent): void => { - if (e.key === 'Enter') { - return this.props.onSearch() - } - } -} - -export default LogsSearchBar diff --git a/ui/src/logs/components/LogsTableContainer.tsx b/ui/src/logs/components/LogsTable.tsx similarity index 100% rename from ui/src/logs/components/LogsTableContainer.tsx rename to ui/src/logs/components/LogsTable.tsx diff --git a/ui/src/logs/containers/LogsPage.tsx b/ui/src/logs/containers/LogsPage.tsx index e6e633839b..db4eff76eb 100644 --- a/ui/src/logs/containers/LogsPage.tsx +++ b/ui/src/logs/containers/LogsPage.tsx @@ -4,9 +4,18 @@ import {getSourceAsync, setTimeRange, setNamespace} from 'src/logs/actions' import {getSourcesAsync} from 'src/shared/actions/sources' import {Source, Namespace, TimeRange} from 'src/types' import LogViewerHeader from 'src/logs/components/LogViewerHeader' -import GraphContainer from 'src/logs/components/LogsGraphContainer' -import TableContainer from 'src/logs/components/LogsTableContainer' -import SearchContainer from 'src/logs/components/LogsSearchContainer' +import Graph from 'src/logs/components/LogsGraph' +import Table from 'src/logs/components/LogsTable' +import SearchBar from 'src/logs/components/LogsSearchBar' +import FilterBar from 'src/logs/components/LogsFilterBar' + +export interface Filter { + id: string + key: string + value: string + operator: string + enabled: boolean +} interface Props { sources: Source[] @@ -22,6 +31,7 @@ interface Props { interface State { searchString: string + filters: Filter[] } class LogsPage extends PureComponent { @@ -30,6 +40,7 @@ class LogsPage extends PureComponent { this.state = { searchString: '', + filters: [], } } @@ -44,20 +55,24 @@ class LogsPage extends PureComponent { } public render() { - const {searchString} = this.state + const {searchString, filters} = this.state return (
    {this.header}
    - - + - + + ) @@ -96,6 +111,10 @@ class LogsPage extends PureComponent { // do the thing } + private handleUpdateFilters = (filters: Filter[]): void => { + this.setState({filters}) + } + private handleChooseTimerange = (timeRange: TimeRange) => { this.props.setTimeRange(timeRange) } diff --git a/ui/src/style/pages/logs-viewer.scss b/ui/src/style/pages/logs-viewer.scss index 294543a847..dab491dbe8 100644 --- a/ui/src/style/pages/logs-viewer.scss +++ b/ui/src/style/pages/logs-viewer.scss @@ -4,7 +4,8 @@ */ $logs-viewer-graph-height: 240px; -$logs-viewer-search-height: 108px; +$logs-viewer-search-height: 46px; +$logs-viewer-filter-height: 42px; $logs-viewer-gutter: 60px; .logs-viewer { @@ -20,28 +21,22 @@ $logs-viewer-gutter: 60px; @include gradient-v($g2-kevlar, $g0-obsidian); } -.logs-viewer--search-container { +.logs-viewer--search-bar { display: flex; - flex-direction: column; - align-items: stretch; - padding: 20px $logs-viewer-gutter; + align-items: flex-end; + flex-wrap: nowrap; + padding: 0 $logs-viewer-gutter; height: $logs-viewer-search-height; background-color: $g3-castle; } .logs-viewer--table-container { padding: 12px $logs-viewer-gutter 30px $logs-viewer-gutter; - height: calc(100% - #{$logs-viewer-graph-height + $logs-viewer-search-height}); + height: calc(100% - #{$logs-viewer-graph-height + $logs-viewer-search-height + $logs-viewer-filter-height}); background-color: $g3-castle; } // Search Bar -.logs-viewer--search { - display: flex; - align-items: center; - flex-wrap: nowrap; -} - .logs-viewer--search-input { flex: 1 0 0; margin-right: 8px; @@ -67,11 +62,13 @@ $logs-viewer-gutter: 60px; } // Filters Bar -.logs-viewer--filters-container { +.logs-viewer--filter-bar { display: flex; align-items: center; @include no-user-select(); - margin-top: 8px; + padding: 0 $logs-viewer-gutter; + height: $logs-viewer-filter-height; + background-color: $g3-castle; } .logs-viewer--results-text { From f2fff2385b7211555693940f5686d9d9450c88e4 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 25 May 2018 15:57:38 -0700 Subject: [PATCH 5/8] Create SCSS shadow mixin this is used a bunch of places in the UI, going to work this in over time --- ui/src/style/components/dygraphs.scss | 2 +- ui/src/style/modules/mixins.scss | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/src/style/components/dygraphs.scss b/ui/src/style/components/dygraphs.scss index 5e0d8a6481..06e9138dd5 100644 --- a/ui/src/style/components/dygraphs.scss +++ b/ui/src/style/components/dygraphs.scss @@ -174,7 +174,7 @@ min-width: 350px; user-select: text; transform: translateX(-50%); - box-shadow: 0 0 10px 2px $g2-kevlar; + @extend %drop-shadow; &.hidden { display: none !important; diff --git a/ui/src/style/modules/mixins.scss b/ui/src/style/modules/mixins.scss index 1723b10db5..196ed1628a 100644 --- a/ui/src/style/modules/mixins.scss +++ b/ui/src/style/modules/mixins.scss @@ -125,3 +125,8 @@ $scrollbar-offset: 3px; cursor: default; } } + +// Shadows +%drop-shadow { + box-shadow: 0 0 10px 2px $g2-kevlar; +} From 6bbed0cadd88b287f9ac4f130d82f086193d56aa Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 25 May 2018 15:58:09 -0700 Subject: [PATCH 6/8] Introduce component for individual filters --- ui/src/logs/components/LogsFilter.tsx | 94 ++++++++++++++++++++++++ ui/src/logs/components/LogsFilterBar.tsx | 47 ++++++++---- ui/src/logs/containers/LogsPage.tsx | 12 ++- ui/src/style/pages/logs-viewer.scss | 49 +++++++++++- 4 files changed, 185 insertions(+), 17 deletions(-) create mode 100644 ui/src/logs/components/LogsFilter.tsx diff --git a/ui/src/logs/components/LogsFilter.tsx b/ui/src/logs/components/LogsFilter.tsx new file mode 100644 index 0000000000..686ad49667 --- /dev/null +++ b/ui/src/logs/components/LogsFilter.tsx @@ -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 { + constructor(props: Props) { + super(props) + + this.state = { + expanded: false, + } + } + + public render() { + const { + filter: {id}, + onDelete, + } = this.props + const {expanded} = this.state + + return ( +
  • + {this.label} +
    + {expanded && this.renderTooltip} +
  • + ) + } + + private get label(): JSX.Element { + const { + filter: {key, operator, value}, + } = this.props + + return ( + {`${key} ${operator} ${value}`} + ) + } + + 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 ( +
      +
    • {toggleStatusText}
    • +
    • {toggleOperatorText}
    • +
    • Delete
    • +
    + ) + } +} + +export default LogsFilter diff --git a/ui/src/logs/components/LogsFilterBar.tsx b/ui/src/logs/components/LogsFilterBar.tsx index c3f6aa8e79..64feb89acd 100644 --- a/ui/src/logs/components/LogsFilterBar.tsx +++ b/ui/src/logs/components/LogsFilterBar.tsx @@ -1,5 +1,6 @@ import React, {PureComponent} from 'react' import {Filter} from 'src/logs/containers/LogsPage' +import FilterBlock from 'src/logs/components/LogsFilter' interface Props { numResults: number @@ -25,31 +26,25 @@ class LogsFilters extends PureComponent { const {filters} = this.props return filters.map(filter => ( -
  • - - {filter.key} - {filter.operator} - {filter.value} - -
  • + )) } private handleDeleteFilter = (id: string) => (): void => { const {filters, onUpdateFilters} = this.props - const filteredFilters = filters.map( - filter => (filter.id === id ? null : filter) - ) + const filteredFilters = filters.filter(filter => filter.id !== id) onUpdateFilters(filteredFilters) } - private handleToggleFilter = (id: string) => (): void => { + private handleToggleFilterStatus = (id: string) => (): void => { const {filters, onUpdateFilters} = this.props const filteredFilters = filters.map(filter => { @@ -62,6 +57,28 @@ class LogsFilters extends PureComponent { 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 diff --git a/ui/src/logs/containers/LogsPage.tsx b/ui/src/logs/containers/LogsPage.tsx index db4eff76eb..e4f6c389da 100644 --- a/ui/src/logs/containers/LogsPage.tsx +++ b/ui/src/logs/containers/LogsPage.tsx @@ -34,13 +34,23 @@ interface State { filters: Filter[] } +const DUMMY_FILTERS = [ + { + id: '0', + key: 'host', + value: 'prod1-rsavage.local', + operator: '==', + enabled: true, + }, +] + class LogsPage extends PureComponent { constructor(props: Props) { super(props) this.state = { searchString: '', - filters: [], + filters: DUMMY_FILTERS, } } diff --git a/ui/src/style/pages/logs-viewer.scss b/ui/src/style/pages/logs-viewer.scss index dab491dbe8..152741f046 100644 --- a/ui/src/style/pages/logs-viewer.scss +++ b/ui/src/style/pages/logs-viewer.scss @@ -94,6 +94,7 @@ $logs-viewer-gutter: 60px; } .logs-viewer--filter { + position: relative; font-size: 12px; display: flex; align-items: center; @@ -103,8 +104,20 @@ $logs-viewer-gutter: 60px; border-radius: 4px; background-color: $g5-pepper; color: $g13-mist; - font-weight: 500; + font-weight: 600; 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 { @@ -144,4 +157,38 @@ $logs-viewer-gutter: 60px; 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; + } + } } \ No newline at end of file From 2ab4b4f6ac3916f1888be3be90068b3614421e01 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 25 May 2018 16:24:49 -0700 Subject: [PATCH 7/8] Improve readability of logic --- ui/src/logs/components/TimeRangeDropdown.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/logs/components/TimeRangeDropdown.tsx b/ui/src/logs/components/TimeRangeDropdown.tsx index 2d85f41da9..c3ad299f19 100644 --- a/ui/src/logs/components/TimeRangeDropdown.tsx +++ b/ui/src/logs/components/TimeRangeDropdown.tsx @@ -1,6 +1,7 @@ import React, {Component} from 'react' import classnames from 'classnames' import moment from 'moment' +import _ from 'lodash' import FancyScrollbar from 'src/shared/components/FancyScrollbar' import timeRanges from 'src/logs/data/timeRanges' @@ -134,7 +135,7 @@ class TimeRangeDropdown extends Component { customTimeRange: {lower, upper}, } = this.state - const absoluteTimeRange = !!lower || !!upper + const absoluteTimeRange = !_.isEmpty(lower) && !_.isEmpty(upper) return classnames('dropdown', { 'dropdown-290': absoluteTimeRange, From e1d90dbe0b628ad7ea53b085bc10cd17abb7d97e Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 25 May 2018 16:56:20 -0700 Subject: [PATCH 8/8] Remove margin in filter tooltip --- ui/src/style/pages/logs-viewer.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/style/pages/logs-viewer.scss b/ui/src/style/pages/logs-viewer.scss index 152741f046..133946ae3b 100644 --- a/ui/src/style/pages/logs-viewer.scss +++ b/ui/src/style/pages/logs-viewer.scss @@ -184,6 +184,7 @@ $logs-viewer-gutter: 60px; font-weight: 600; color: $g11-sidewalk; transition: background-color 0.25s ease, color 0.25s ease; + margin: 0; &:hover { cursor: pointer;