From a420e8439c165b14e71d154bc4b4b409d032c4af Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 27 Jul 2018 16:35:31 -0700 Subject: [PATCH] Introduce family of Panel components --- .../reusable_ui/components/panel/Panel.scss | 123 ++++++++++++++++++ ui/src/reusable_ui/components/panel/Panel.tsx | 75 +++++++++++ .../components/panel/PanelBody.tsx | 19 +++ .../components/panel/PanelFooter.tsx | 19 +++ .../components/panel/PanelHeader.tsx | 25 ++++ 5 files changed, 261 insertions(+) create mode 100644 ui/src/reusable_ui/components/panel/Panel.scss create mode 100644 ui/src/reusable_ui/components/panel/Panel.tsx create mode 100644 ui/src/reusable_ui/components/panel/PanelBody.tsx create mode 100644 ui/src/reusable_ui/components/panel/PanelFooter.tsx create mode 100644 ui/src/reusable_ui/components/panel/PanelHeader.tsx diff --git a/ui/src/reusable_ui/components/panel/Panel.scss b/ui/src/reusable_ui/components/panel/Panel.scss new file mode 100644 index 000000000..cd5f3c010 --- /dev/null +++ b/ui/src/reusable_ui/components/panel/Panel.scss @@ -0,0 +1,123 @@ +/* + Panels + ----------------------------------------------------------------------------- +*/ + +@import 'src/style/modules/influx-colors'; +@import 'src/style/modules/variables'; +@import 'src/style/modules/mixins'; + +$panel-gutter: 30px; +$panel-background: $g3-castle; + +.panel { + display: flex; + flex-direction: column; + align-items: stretch; + margin-bottom: $panel-gutter; +} + +.panel-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: $panel-gutter 0; +} + +.panel-title { + font-weight: 400; + font-size: 19px; + color: $g12-forge; + letter-spacing: 0.015em; + margin: 0; + line-height: 1em; + @extend %no-user-select; +} + +.panel-controls { + display: flex; + align-items: center; + + &:nth-child(1) { + justify-content: flex-start; + > * { + margin-right: $ix-marg-b; + } + } + + &:nth-child(2) { + justify-content: flex-end; + > * { + margin-left: $ix-marg-b; + } + } +} + +.panel-body { + background-color: $panel-background; + padding: $panel-gutter; + + .panel-header + &, + &:first-child { + border-top-left-radius: $ix-radius; + border-top-right-radius: $ix-radius; + } + &:last-child { + border-bottom-left-radius: $ix-radius; + border-bottom-right-radius: $ix-radius; + } + + > *:first-child { + margin-top: 0; + } + > *:last-child { + margin-bottom: 0; + } +} + +.panel-footer { + padding: $ix-marg-c $panel-gutter; + border-radius: 0 0 $ix-radius $ix-radius; + @include gradient-v($g2-kevlar, $panel-background); + color: $g9-mountain; +} + +// Tables directly inside Panels +// ---------------------------------------------------------------------------- +.panel > .table { + border-top: $ix-border; + * { + border-color: $g19-ghost; + } +} +.panel-header + .table { + border: none; +} +.panel > .table td:first-child, +.panel > .table th:first-child { + padding-left: $panel-gutter; +} +.panel > .table td:last-child, +.panel > .table th:last-child { + padding-right: $panel-gutter; +} + +// Solid Panels +// ---------------------------------------------------------------------------- +.panel.panel-solid { + background-color: $panel-background; + border-radius: $ix-radius; + + .panel-header { + padding: $panel-gutter; + } + .panel-body { + background-color: transparent; + } +} + +// Horizontal Rules directly inside Panels +// ---------------------------------------------------------------------------- +.panel-body hr { + margin: $ix-marg-c 0; +} diff --git a/ui/src/reusable_ui/components/panel/Panel.tsx b/ui/src/reusable_ui/components/panel/Panel.tsx new file mode 100644 index 000000000..97d41fe73 --- /dev/null +++ b/ui/src/reusable_ui/components/panel/Panel.tsx @@ -0,0 +1,75 @@ +// Libraries +import React, {Component, Children} from 'react' +import classnames from 'classnames' + +// Components +import PanelHeader from 'src/reusable_ui/components/panel/PanelHeader' +import PanelBody from 'src/reusable_ui/components/panel/PanelBody' +import PanelFooter from 'src/reusable_ui/components/panel/PanelFooter' + +// Styles +import 'src/reusable_ui/components/panel/Panel.scss' + +import {ErrorHandling} from 'src/shared/decorators/errors' + +export enum PanelType { + Default = '', + Solid = 'solid', +} + +interface Props { + children: JSX.Element[] + type?: PanelType +} + +@ErrorHandling +class Panel extends Component { + public static defaultProps: Partial = { + type: PanelType.Default, + } + + public static Header = PanelHeader + public static Body = PanelBody + public static Footer = PanelFooter + + public render() { + const {children} = this.props + + this.validateChildren() + + return
{children}
+ } + + private get className(): string { + const {type} = this.props + + return classnames('panel', {'panel-solid': type === PanelType.Solid}) + } + + private validateChildren = (): void => { + const {children} = this.props + + let invalidCount = 0 + + Children.forEach(children, (child: JSX.Element) => { + if ( + child.type === PanelHeader || + child.type === PanelBody || + child.type === PanelFooter + ) { + return + } + + invalidCount += 1 + return + }) + + if (invalidCount > 0) { + throw new Error( + 'Panel expected children of type , , or ' + ) + } + } +} + +export default Panel diff --git a/ui/src/reusable_ui/components/panel/PanelBody.tsx b/ui/src/reusable_ui/components/panel/PanelBody.tsx new file mode 100644 index 000000000..f9a0f431b --- /dev/null +++ b/ui/src/reusable_ui/components/panel/PanelBody.tsx @@ -0,0 +1,19 @@ +// Libraries +import React, {Component} from 'react' + +import {ErrorHandling} from 'src/shared/decorators/errors' + +interface Props { + children: JSX.Element[] | JSX.Element +} + +@ErrorHandling +class PanelBody extends Component { + public render() { + const {children} = this.props + + return
{children}
+ } +} + +export default PanelBody diff --git a/ui/src/reusable_ui/components/panel/PanelFooter.tsx b/ui/src/reusable_ui/components/panel/PanelFooter.tsx new file mode 100644 index 000000000..b3e771d54 --- /dev/null +++ b/ui/src/reusable_ui/components/panel/PanelFooter.tsx @@ -0,0 +1,19 @@ +// Libraries +import React, {Component} from 'react' + +import {ErrorHandling} from 'src/shared/decorators/errors' + +interface Props { + children: JSX.Element[] +} + +@ErrorHandling +class PanelFooter extends Component { + public render() { + const {children} = this.props + + return
{children}
+ } +} + +export default PanelFooter diff --git a/ui/src/reusable_ui/components/panel/PanelHeader.tsx b/ui/src/reusable_ui/components/panel/PanelHeader.tsx new file mode 100644 index 000000000..6219169ba --- /dev/null +++ b/ui/src/reusable_ui/components/panel/PanelHeader.tsx @@ -0,0 +1,25 @@ +// Libraries +import React, {Component} from 'react' + +import {ErrorHandling} from 'src/shared/decorators/errors' + +interface Props { + children?: JSX.Element[] + title: string +} + +@ErrorHandling +class PanelHeader extends Component { + public render() { + const {children, title} = this.props + + return ( +
+
{title}
+
{children}
+
+ ) + } +} + +export default PanelHeader