diff --git a/ui/src/kapacitor/components/KapacitorRules.tsx b/ui/src/kapacitor/components/KapacitorRules.tsx index 92d3cf563..cbad8476a 100644 --- a/ui/src/kapacitor/components/KapacitorRules.tsx +++ b/ui/src/kapacitor/components/KapacitorRules.tsx @@ -1,17 +1,15 @@ import React, {SFC} from 'react' import {Link} from 'react-router' -import NoKapacitorError from 'src/shared/components/NoKapacitorError' import KapacitorRulesTable from 'src/kapacitor/components/KapacitorRulesTable' import TasksTable from 'src/kapacitor/components/TasksTable' -import {Source, AlertRule} from 'src/types' +import {Source, AlertRule, Kapacitor} from 'src/types' interface KapacitorRulesProps { source: Source rules: AlertRule[] - hasKapacitor: boolean - loading: boolean + kapacitor: Kapacitor onDelete: (rule: AlertRule) => void onChangeRuleStatus: (rule: AlertRule) => void } @@ -19,35 +17,10 @@ interface KapacitorRulesProps { const KapacitorRules: SFC = ({ source, rules, - hasKapacitor, - loading, onDelete, onChangeRuleStatus, }) => { - if (loading || !hasKapacitor) { - return ( -
-
-

Alert Rules

- -
-
-
- {!hasKapacitor ? ( - - ) : ( -

Loading Rules...

- )} -
-
-
- ) - } - const builderRules = rules.filter((r: AlertRule) => r.query) - const builderHeader = `${builderRules.length} Alert Rule${ builderRules.length === 1 ? '' : 's' }` diff --git a/ui/src/kapacitor/containers/KapacitorRulePage.tsx b/ui/src/kapacitor/containers/KapacitorRulePage.tsx index 607b80d76..2a3fb068e 100644 --- a/ui/src/kapacitor/containers/KapacitorRulePage.tsx +++ b/ui/src/kapacitor/containers/KapacitorRulePage.tsx @@ -103,6 +103,7 @@ class KapacitorRulePage extends Component { if (!query) { return
} + return ( { constructor(props) { super(props) this.state = { - hasKapacitor: false, - loading: true, + kapacitor: null, + loading: RemoteDataState.NotStarted, } } public async componentDidMount() { const {source, actions} = this.props + this.setState({loading: RemoteDataState.Loading}) const kapacitor: Kapacitor = await getActiveKapacitor(source) + if (!kapacitor) { - return + return this.setState({loading: RemoteDataState.Done, kapacitor: null}) } await actions.fetchRules(kapacitor) - this.setState({loading: false, hasKapacitor: !!kapacitor}) + this.setState({loading: RemoteDataState.Done, kapacitor}) } public render() { - const {source, rules} = this.props - const {hasKapacitor, loading} = this.state return ( - + - + { - + {this.rules} ) } + private get rules(): JSX.Element { + const {kapacitor} = this.state + const {source, rules} = this.props + + if (!kapacitor) { + return + } + + return ( + + ) + } + + private get headerTitle(): string { + const {kapacitor} = this.state + + if (!kapacitor) { + return 'Manage Tasks' + } + + return `Manage Tasks on "${kapacitor.name}" @ ${kapacitor.url}` + } + + private get className(): string { + const {kapacitor} = this.state + + if (!kapacitor) { + return 'empty-tasks-page' + } + + return '' + } + private handleDeleteRule = (rule: AlertRule) => { const {actions} = this.props diff --git a/ui/src/reusable_ui/components/spinners/Spinner.tsx b/ui/src/reusable_ui/components/spinners/Spinner.tsx new file mode 100644 index 000000000..325bbc96d --- /dev/null +++ b/ui/src/reusable_ui/components/spinners/Spinner.tsx @@ -0,0 +1,33 @@ +// Libraries +import React, {Component} from 'react' + +// Types +import {RemoteDataState} from 'src/types' + +// Decorators +import {ErrorHandling} from 'src/shared/decorators/errors' + +interface Props { + loading: RemoteDataState + children: JSX.Element[] | JSX.Element +} + +@ErrorHandling +export default class Spinner extends Component { + public render() { + return this.children + } + + private get children(): JSX.Element | JSX.Element[] { + const {loading, children} = this.props + + if ( + loading === RemoteDataState.Loading || + loading === RemoteDataState.NotStarted + ) { + return
+ } + + return children + } +} diff --git a/ui/src/reusable_ui/index.ts b/ui/src/reusable_ui/index.ts index 58dd5df0f..fdf138969 100644 --- a/ui/src/reusable_ui/index.ts +++ b/ui/src/reusable_ui/index.ts @@ -12,6 +12,7 @@ import Page from './components/page_layout/Page' import Panel, {PanelType} from './components/panel/Panel' import Radio from './components/radio_buttons/RadioButtons' import SlideToggle from './components/slide_toggle/SlideToggle' +import Spinner from './components/spinners/Spinner' // Import Types import { @@ -53,4 +54,5 @@ export { Greys, IconFont, Columns, + Spinner, } diff --git a/ui/src/style/components/tables.scss b/ui/src/style/components/tables.scss index c9a24260f..7d357196b 100644 --- a/ui/src/style/components/tables.scss +++ b/ui/src/style/components/tables.scss @@ -51,9 +51,7 @@ ---------------------------------------------- */ .sortable-header { - transition: - color 0.25s ease, - background-color 0.25s ease; + transition: color 0.25s ease, background-color 0.25s ease; position: relative; > .icon { @@ -64,10 +62,7 @@ color: $g20-white; opacity: 0; transform: translateY(-50%); - transition: - opacity 0.25s ease, - color 0.25s ease, - transform 0.35s ease; + transition: opacity 0.25s ease, color 0.25s ease, transform 0.35s ease; } &:hover { @@ -80,7 +75,9 @@ background-color: $g5-pepper; color: $g19-ghost; - > .icon {opacity: 1;} + > .icon { + opacity: 1; + } } &.sorting-ascending > .icon { transform: translateY(-50%) rotate(180deg); @@ -140,9 +137,7 @@ $table-tab-scrollbar-height: 6px; background-color: $g4-onyx; color: $g11-sidewalk; margin-right: 2px; - transition: - color 0.25s ease, - background-color 0.25s ease; + transition: color 0.25s ease, background-color 0.25s ease; &:hover { background-color: $g5-pepper; @@ -169,7 +164,6 @@ $table-tab-scrollbar-height: 6px; border-radius: 0 $radius-small $radius-small $radius-small; } - .table > tbody > tr.highlight, .table.table-highlight > tbody > tr.highlight { background-color: $g4-onyx; @@ -180,6 +174,7 @@ $table-tab-scrollbar-height: 6px; ---------------------------------------------- */ .alert-history-page, +.empty-tasks-page, .hosts-list-page { .container-fluid, .row, diff --git a/ui/test/kapacitor/components/KapacitorRules.test.tsx b/ui/test/kapacitor/components/KapacitorRules.test.tsx index 94aa5b624..cd45ba993 100644 --- a/ui/test/kapacitor/components/KapacitorRules.test.tsx +++ b/ui/test/kapacitor/components/KapacitorRules.test.tsx @@ -8,13 +8,13 @@ import NoKapacitorError from 'src/shared/components/NoKapacitorError' import KapacitorRulesTable from 'src/kapacitor/components/KapacitorRulesTable' import TasksTable from 'src/kapacitor/components/TasksTable' -import {source, kapacitorRules} from 'test/resources' +import {source, kapacitorRules, kapacitor} from 'test/resources' describe('Kapacitor.Containers.KapacitorRules', () => { const props = { source, rules: kapacitorRules, - hasKapacitor: true, + kapacitor, loading: false, onDelete: () => {}, onChangeRuleStatus: () => {}, @@ -52,7 +52,11 @@ describe('Kapacitor.Containers.KapacitorRules', () => { }) it('renders NoKapacitorError if not loading and no kapacitor configured', () => { - const noKapacitorProps = {...props, loading: false, hasKapacitor: false} + const noKapacitorProps = { + ...props, + loading: false, + hasKapacitor: false, + } const wrapper = shallow() const noKapacitorError = wrapper.find(NoKapacitorError) expect(noKapacitorError.length).toEqual(1)