refactor(clockface): replace local index list with Clockface index list (#14491)

* refactor(clockface): replace index lists with clockface index lists

* refactor(clockface): replace local index list import with clockface index list import

* refactor(clockface): remove local index list components
pull/14496/head
alexpaxton 2019-07-29 08:13:20 -10:00 committed by GitHub
parent 13d2b8df66
commit a2a44067be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 17 additions and 988 deletions

View File

@ -4,8 +4,7 @@ import _ from 'lodash'
import memoizeOne from 'memoize-one'
// Components
import {EmptyState, Overlay} from '@influxdata/clockface'
import {IndexList} from 'src/clockface'
import {EmptyState, Overlay, IndexList} from '@influxdata/clockface'
import TokenRow from 'src/authorizations/components/TokenRow'
import ViewTokenOverlay from 'src/authorizations/components/ViewTokenOverlay'
@ -59,12 +58,14 @@ export default class TokenList extends PureComponent<Props, State> {
sort={sortKey === this.headerKeys[0] ? sortDirection : Sort.None}
columnName="Description"
onClick={onClickColumn}
width="50%"
/>
<IndexList.HeaderCell
sortKey={this.headerKeys[0]}
sort={sortKey === this.headerKeys[0] ? sortDirection : Sort.None}
columnName="Status"
onClick={onClickColumn}
width="50%"
/>
</IndexList.Header>
<IndexList.Body emptyState={this.emptyState} columnCount={2}>

View File

@ -9,8 +9,14 @@ import {
} from 'src/authorizations/actions'
// Components
import {ComponentSize, SlideToggle, ComponentColor} from '@influxdata/clockface'
import {IndexList, ConfirmationButton, Alignment} from 'src/clockface'
import {
ComponentSize,
SlideToggle,
ComponentColor,
IndexList,
Alignment,
} from '@influxdata/clockface'
import {ConfirmationButton} from 'src/clockface'
import EditableName from 'src/shared/components/EditableName'
// Types

View File

@ -1,215 +0,0 @@
/*
Index List Styles
------------------------------------------------------------------------------
*/
.index-list {
border: 0;
border-collapse: collapse;
width: 100%;
}
.index-list--header-cell {
@include no-user-select();
text-transform: uppercase;
letter-spacing: 0.03em;
padding: 0 13px;
font-size: $form-md-font;
font-weight: 600;
color: $g11-sidewalk;
white-space: nowrap;
height: 52px;
}
.index-list--cell {
height: calc(100% - #{$ix-border});
padding: $ix-marg-a + $ix-marg-b;
background-color: $g3-castle;
font-size: $form-md-font;
font-weight: 500;
color: $g13-mist;
display: flex;
align-items: center;
transition: background-color 0.25s ease, color 0.25s ease;
}
// Alignment modifiers
.index-list--align-left {
text-align: left;
}
.index-list--align-left .index-list--cell {
justify-content: flex-start;
}
.index-list--align-right {
text-align: right;
}
.index-list--align-right .index-list--cell {
justify-content: flex-end;
}
.index-list--align-center {
text-align: center;
}
.index-list--align-center .index-list--cell {
justify-content: center;
}
// TD Style
.index-list--row-cell {
position: relative;
height: 52px;
&:first-child .index-list--cell {
border-radius: $radius 0 0 $radius;
}
&:last-child .index-list--cell {
border-radius: 0 $radius $radius 0;
}
}
// Row hover State
.index-list--row:hover .index-list--cell {
background-color: $g4-onyx;
color: $g18-cloud;
}
// Show cell contents on row hover
.index-list--show-hover .index-list--cell > * {
transition: opacity 0.25s ease;
opacity: 0;
}
.index-list--row:hover .index-list--show-hover .index-list--cell > * {
opacity: 1;
}
// Row Disabled State
.index-list--row.index-list--row-disabled
.index-list--row-cell
.index-list--cell {
background-color: rgba($g3-castle, 0.5);
color: $g9-mountain;
font-style: italic;
a:link,
a:visited,
a:hover,
a:active {
transition: color 0.25s ease, opacity 0.25s ease;
opacity: 0.7;
}
}
// Row Disabled Hover State
.index-list--row.index-list--row-disabled:hover
.index-list--row-cell
.index-list--cell {
background-color: rgba($g3-castle, 0.7);
color: $g15-platinum;
a:link,
a:visited,
a:hover,
a:active {
opacity: 0.9;
}
}
// Empty state
.index-list--empty-cell {
background-color: rgba($g3-castle, 0.5);
border-radius: $radius;
display: flex;
align-content: center;
justify-content: center;
color: $empty-state-text;
}
/*
Depth Styling
------------------------------------------------------------------------------
*/
.tabs--contents,
// TODO: Remove this .tabbed-page rule when the component is phased out
.tabbed-page {
.index-list--cell {
background-color: $g4-onyx;
}
.index-list--row:hover .index-list--cell {
background-color: $g5-pepper;
}
.index-list--row-cell.index-list--row-disabled .index-list--cell {
background-color: rgba($g4-onyx, 0.5);
}
.index-list--row:hover
.index-list--row-cell.index-list--row-disabled
.index-list--cell {
background-color: rgba($g4-onyx, 0.7);
}
.index-list--empty-cell {
background-color: rgba($g4-onyx, 0.5);
}
}
/*
Sortable Header Styling
------------------------------------------------------------------------------
*/
.index-list--sort-arrow {
display: inline-block;
vertical-align: middle;
width: 20px;
height: 20px;
margin-left: $ix-marg-a;
opacity: 0;
position: relative;
top: -1px;
transition: opacity 0.25s ease, transform 0.25s ease;
> span.icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.index-list--header-cell.index-list--sortable {
transition: color 0.25s ease;
&:hover {
cursor: pointer;
color: $c-pool;
}
}
.index-list--header-cell.index-list--sort-ascending,
.index-list--header-cell.index-list--sort-descending {
color: $g17-whisper;
}
.index-list--sort-ascending .index-list--sort-arrow {
opacity: 1;
transform: rotate(180deg);
}
.index-list--sort-descending .index-list--sort-arrow {
opacity: 1;
transform: rotate(00deg);
}
/*
Margin for "Name" column in tables
------------------------------------------------------------------------------
*/
.index-list--labels {
margin-left: $ix-marg-b;
}
.index-list--row-cell .index-list--cell a {
white-space: nowrap;
}

View File

@ -1,33 +0,0 @@
// Libraries
import React, {Component} from 'react'
// Components
import Body from 'src/clockface/components/index_views/IndexListBody'
import Header from 'src/clockface/components/index_views/IndexListHeader'
import HeaderCell from 'src/clockface/components/index_views/IndexListHeaderCell'
import Row from 'src/clockface/components/index_views/IndexListRow'
import Cell from 'src/clockface/components/index_views/IndexListRowCell'
// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
children: JSX.Element[] | JSX.Element
}
@ErrorHandling
class IndexList extends Component<Props> {
public static Body = Body
public static Header = Header
public static HeaderCell = HeaderCell
public static Row = Row
public static Cell = Cell
public render() {
const {children} = this.props
return <table className="index-list">{children}</table>
}
}
export default IndexList

View File

@ -1,36 +0,0 @@
// Libraries
import React, {Component} from 'react'
// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
children?: JSX.Element[] | JSX.Element
emptyState: JSX.Element
columnCount: number
}
@ErrorHandling
class IndexListBody extends Component<Props> {
public render() {
const {children, columnCount, emptyState} = this.props
if (React.Children.count(children)) {
return <tbody className="index-list--body">{children}</tbody>
}
return (
<tbody className="index-list--empty">
<tr className="index-list--empty-row">
<td colSpan={columnCount}>
<div className="index-list--empty-cell" data-testid="empty-state">
{emptyState}
</div>
</td>
</tr>
</tbody>
)
}
}
export default IndexListBody

View File

@ -1,24 +0,0 @@
// Libraries
import React, {Component} from 'react'
// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
children: JSX.Element[]
}
@ErrorHandling
class IndexListHeader extends Component<Props> {
public render() {
const {children} = this.props
return (
<thead className="index-list--header">
<tr>{children}</tr>
</thead>
)
}
}
export default IndexListHeader

View File

@ -1,92 +0,0 @@
// Libraries
import React, {Component} from 'react'
import classnames from 'classnames'
// Types
import {Alignment, Sort} from 'src/clockface/types'
// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors'
export interface IndexHeaderCellProps {
width: string
columnName?: string
alignment?: Alignment
sort?: Sort
sortKey?: string
onClick?: (nextSort: Sort, sortKey: string) => void
}
@ErrorHandling
class IndexListHeaderCell extends Component<IndexHeaderCellProps> {
public static defaultProps: Partial<IndexHeaderCellProps> = {
columnName: '',
alignment: Alignment.Left,
}
public render() {
const {columnName, width} = this.props
return (
<th className={this.className} style={{width}} onClick={this.handleClick}>
{columnName}
{this.sortIndicator}
</th>
)
}
private handleClick = (): void => {
const {onClick, sort, sortKey} = this.props
if (!onClick || !sort) {
return
}
if (sort === Sort.None) {
onClick(Sort.Ascending, sortKey)
} else if (sort === Sort.Ascending) {
onClick(Sort.Descending, sortKey)
} else if (sort === Sort.Descending) {
onClick(Sort.None, sortKey)
}
}
private get sortIndicator(): JSX.Element {
if (this.isSortable) {
return (
<span className="index-list--sort-arrow">
<span className="icon caret-down" />
</span>
)
}
}
private get isSortable(): boolean {
const {sort} = this.props
if (
sort === Sort.None ||
sort === Sort.Ascending ||
sort === Sort.Descending
) {
return true
}
return false
}
private get className(): string {
const {alignment, sort} = this.props
return classnames('index-list--header-cell', {
'index-list--align-left': alignment === Alignment.Left,
'index-list--align-center': alignment === Alignment.Center,
'index-list--align-right': alignment === Alignment.Right,
'index-list--sortable': this.isSortable,
'index-list--sort-descending': sort === Sort.Descending,
'index-list--sort-ascending': sort === Sort.Ascending,
})
}
}
export default IndexListHeaderCell

View File

@ -1,42 +0,0 @@
// Libraries
import React, {Component} from 'react'
import classnames from 'classnames'
// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
disabled: boolean
children: JSX.Element[] | JSX.Element
customClass?: string
testID: string
}
@ErrorHandling
class IndexListRow extends Component<Props> {
public static defaultProps = {
disabled: false,
testID: 'table-row',
}
public render() {
const {children, testID} = this.props
return (
<tr data-testid={testID} className={this.className}>
{children}
</tr>
)
}
private get className(): string {
const {disabled, customClass} = this.props
return classnames('index-list--row', {
'index-list--row-disabled': disabled,
[customClass]: !!customClass,
})
}
}
export default IndexListRow

View File

@ -1,50 +0,0 @@
// Libraries
import React, {Component} from 'react'
import classnames from 'classnames'
// Types
import {Alignment} from 'src/clockface/types'
// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
children: any
alignment: Alignment
revealOnHover: boolean
testID: string
}
@ErrorHandling
class IndexListRowCell extends Component<Props> {
public static defaultProps = {
alignment: Alignment.Left,
revealOnHover: false,
testID: 'table-cell',
}
public render() {
const {children, testID} = this.props
return (
<td className={this.className}>
<div className="index-list--cell" data-testid={testID}>
{children}
</div>
</td>
)
}
private get className(): string {
const {alignment, revealOnHover} = this.props
return classnames('index-list--row-cell', {
'index-list--show-hover': revealOnHover,
'index-list--align-left': alignment === Alignment.Left,
'index-list--align-center': alignment === Alignment.Center,
'index-list--align-right': alignment === Alignment.Right,
})
}
}
export default IndexListRowCell

View File

@ -1,118 +0,0 @@
# Index View Components
These components are purely presentational and intended for use in index view pages (such as Dashboards index)
There are two types of index views:
1. Index List
2. Index Grid (Coming soon)
## How to Use Index List
`IndexList` is essentially a configurable table.
Import the component and associated types:
```
import {Alignment} from 'src/clockface'
import {
IndexListColumn,
IndexListRow,
} from 'src/clockface/components/index_views/IndexListTypes'
import IndexList from 'src/clockface/components/index_views/IndexList'
```
#### Define Your Columns
First define the columns present in your table. Each column has a few options for customization:
| Option | Type | |
|-------------|-----------|---------------------------------------------------------------------------------------------------------------------------------------|
| key | string | Unique identifier |
| title | string | Text that appears in column header |
| size | number | Use as a proportion not a unit of measurement. `IndexList` totals the sizes of all columns and calculates each column as a percentage |
| showOnHover | boolean | if `true` the column's contents will be hidden until a row is hovered |
| align | Alignment | Controls text alignment `Left | Center | Right` |
Example columns:
```
const columns = [
{
key: 'users--name',
title: 'Name',
size: 500,
showOnHover: false,
align: Alignment.Left,
},
{
key: 'users--email',
title: 'Email',
size: 100,
showOnHover: false,
align: Alignment.Left,
},
{
key: 'users--last-login',
title: 'Last Login',
size: 90,
showOnHover: false,
align: Alignment.Left,
},
{
key: 'users--actions',
title: '',
size: 200,
showOnHover: true,
align: Alignment.Right,
},
]
```
#### Define Your Rows
Next step is to define your rows. It is crucial that the `key` of each column has a matching `key` within each row or else the component won't render properly.
The `contents` field accepts any type of value. Setting `disabled` to true is just a visual style -- any interactive elements in that row should be individually disabled as well.
```
const rows = users.map(user => ({
disabled: false,
columns: [
{
key: 'users--name',
contents: <p>{user.name}</p>,
},
{
key: 'users--email',
contents: <p>{user.email}</p>,
},
{
key: 'users--login',
contents: this.userLastLogin(user),
},
{
key: 'users--actions',
contents: (
<ComponentSpacer align={Alignment.Left}>
<Button>Edit User</Button>
<Button>Remove from Org</Button>
</ComponentSpacer>
),
},
],
}))
```
#### Define Your Empty State
`IndexList` can be passed a `JSX.Element` that will appear when the list of `rows` is empty. This can be utilized a few different ways. For example, if you are allowing users to sort using a filter then you would pass the filtered list of rows into `IndexList`. The empty state you pass in would then depend on the presence of a `searchTerm` state to show either `No users exist` or `No users match your query`.
#### Render
Now that everything is defined and ready:
```
<IndexList columns={columns} rows={rows} emptyState={this.emptyList}>
```

View File

@ -1,81 +0,0 @@
import React from 'react'
import {mount} from 'enzyme'
import IndexList from 'src/clockface/components/index_views/IndexList'
describe('IndexList', () => {
let wrapper
const wrapperSetup = (empty: boolean) => {
const emptyState = <div>Empty</div>
const header = (
<IndexList.Header key="index-header">
<IndexList.HeaderCell columnName="Fruit" width="50%" />
<IndexList.HeaderCell columnName="Calories" width="50%" />
</IndexList.Header>
)
const body = (
<IndexList.Body key="index-body" columnCount={2} emptyState={emptyState}>
<IndexList.Row>
<IndexList.Cell>Apple</IndexList.Cell>
<IndexList.Cell>500</IndexList.Cell>
</IndexList.Row>
<IndexList.Row>
<IndexList.Cell>Pear</IndexList.Cell>
<IndexList.Cell>1000</IndexList.Cell>
</IndexList.Row>
<IndexList.Row>
<IndexList.Cell>Banana</IndexList.Cell>
<IndexList.Cell>100</IndexList.Cell>
</IndexList.Row>
</IndexList.Body>
)
const emptyBody = (
<IndexList.Body
key="index-body"
columnCount={2}
emptyState={emptyState}
/>
)
let children = [header, body]
if (empty) {
children = [header, emptyBody]
}
const props = {
children,
}
return mount(<IndexList {...props} />)
}
it('mounts without exploding', () => {
wrapper = wrapperSetup(false)
expect(wrapper).toHaveLength(1)
})
it('matches snapshot with minimal props', () => {
wrapper = wrapperSetup(false)
expect(wrapper).toMatchSnapshot()
})
it('renders empty state when 0 rows exist', () => {
wrapper = wrapperSetup(true)
const emptyDiv = wrapper
.find('div')
.filterWhere(div => div.prop('data-testid'))
expect(emptyDiv.prop('data-testid')).toBe('empty-state')
})
it('matches snapshot when 0 rows exist', () => {
wrapper = wrapperSetup(true)
expect(wrapper).toMatchSnapshot()
})
})

View File

@ -1,277 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`IndexList matches snapshot when 0 rows exist 1`] = `
<IndexList>
<table
className="index-list"
>
<IndexListHeader
key="index-header"
>
<thead
className="index-list--header"
>
<tr>
<IndexListHeaderCell
alignment="left"
columnName="Fruit"
width="50%"
>
<th
className="index-list--header-cell index-list--align-left"
onClick={[Function]}
style={
Object {
"width": "50%",
}
}
>
Fruit
</th>
</IndexListHeaderCell>
<IndexListHeaderCell
alignment="left"
columnName="Calories"
width="50%"
>
<th
className="index-list--header-cell index-list--align-left"
onClick={[Function]}
style={
Object {
"width": "50%",
}
}
>
Calories
</th>
</IndexListHeaderCell>
</tr>
</thead>
</IndexListHeader>
<IndexListBody
columnCount={2}
emptyState={
<div>
Empty
</div>
}
key="index-body"
>
<tbody
className="index-list--empty"
>
<tr
className="index-list--empty-row"
>
<td
colSpan={2}
>
<div
className="index-list--empty-cell"
data-testid="empty-state"
>
<div>
Empty
</div>
</div>
</td>
</tr>
</tbody>
</IndexListBody>
</table>
</IndexList>
`;
exports[`IndexList matches snapshot with minimal props 1`] = `
<IndexList>
<table
className="index-list"
>
<IndexListHeader
key="index-header"
>
<thead
className="index-list--header"
>
<tr>
<IndexListHeaderCell
alignment="left"
columnName="Fruit"
width="50%"
>
<th
className="index-list--header-cell index-list--align-left"
onClick={[Function]}
style={
Object {
"width": "50%",
}
}
>
Fruit
</th>
</IndexListHeaderCell>
<IndexListHeaderCell
alignment="left"
columnName="Calories"
width="50%"
>
<th
className="index-list--header-cell index-list--align-left"
onClick={[Function]}
style={
Object {
"width": "50%",
}
}
>
Calories
</th>
</IndexListHeaderCell>
</tr>
</thead>
</IndexListHeader>
<IndexListBody
columnCount={2}
emptyState={
<div>
Empty
</div>
}
key="index-body"
>
<tbody
className="index-list--body"
>
<IndexListRow
disabled={false}
testID="table-row"
>
<tr
className="index-list--row"
data-testid="table-row"
>
<IndexListRowCell
alignment="left"
revealOnHover={false}
testID="table-cell"
>
<td
className="index-list--row-cell index-list--align-left"
>
<div
className="index-list--cell"
data-testid="table-cell"
>
Apple
</div>
</td>
</IndexListRowCell>
<IndexListRowCell
alignment="left"
revealOnHover={false}
testID="table-cell"
>
<td
className="index-list--row-cell index-list--align-left"
>
<div
className="index-list--cell"
data-testid="table-cell"
>
500
</div>
</td>
</IndexListRowCell>
</tr>
</IndexListRow>
<IndexListRow
disabled={false}
testID="table-row"
>
<tr
className="index-list--row"
data-testid="table-row"
>
<IndexListRowCell
alignment="left"
revealOnHover={false}
testID="table-cell"
>
<td
className="index-list--row-cell index-list--align-left"
>
<div
className="index-list--cell"
data-testid="table-cell"
>
Pear
</div>
</td>
</IndexListRowCell>
<IndexListRowCell
alignment="left"
revealOnHover={false}
testID="table-cell"
>
<td
className="index-list--row-cell index-list--align-left"
>
<div
className="index-list--cell"
data-testid="table-cell"
>
1000
</div>
</td>
</IndexListRowCell>
</tr>
</IndexListRow>
<IndexListRow
disabled={false}
testID="table-row"
>
<tr
className="index-list--row"
data-testid="table-row"
>
<IndexListRowCell
alignment="left"
revealOnHover={false}
testID="table-cell"
>
<td
className="index-list--row-cell index-list--align-left"
>
<div
className="index-list--cell"
data-testid="table-cell"
>
Banana
</div>
</td>
</IndexListRowCell>
<IndexListRowCell
alignment="left"
revealOnHover={false}
testID="table-cell"
>
<td
className="index-list--row-cell index-list--align-left"
>
<div
className="index-list--cell"
data-testid="table-cell"
>
100
</div>
</td>
</IndexListRowCell>
</tr>
</IndexListRow>
</tbody>
</IndexListBody>
</table>
</IndexList>
`;

View File

@ -4,12 +4,8 @@ import uuid from 'uuid'
import {ErrorHandling} from 'src/shared/decorators/errors'
// Components
import {
IndexList,
ConfirmationButton,
ComponentSize,
Alignment,
} from 'src/clockface'
import {ConfirmationButton} from 'src/clockface'
import {IndexList, ComponentSize, Alignment} from '@influxdata/clockface'
import EditableDescription from 'src/shared/components/editable_description/EditableDescription'
interface Item {

View File

@ -9,7 +9,6 @@ import WizardFullScreen from './components/wizard/WizardFullScreen'
import WizardOverlay from './components/wizard/WizardOverlay'
import WizardProgressHeader from './components/wizard/WizardProgressHeader'
import ProgressBar from './components/wizard/ProgressBar'
import IndexList from './components/index_views/IndexList'
import ResourceList from './components/resource_list/ResourceList'
import Context from './components/context_menu/Context'
import FormElement from 'src/clockface/components/form_layout/FormElement'
@ -58,7 +57,6 @@ export {
Greys,
GridSizer,
IconFont,
IndexList,
MultiInputType,
MultipleInput,
NavMenuType,

View File

@ -17,7 +17,6 @@
@import 'components/auto_input/AutoInput.scss';
@import 'components/confirmation_button/ConfirmationButton.scss';
@import 'components/context_menu/ContextMenu.scss';
@import 'components/index_views/IndexList.scss';
@import 'components/resource_list/ResourceList.scss';
@import 'components/resource_list/ResourceDescription.scss';
@import 'components/resource_list/ResourceEditableName.scss';

View File

@ -4,7 +4,7 @@ import _ from 'lodash'
import moment from 'moment'
// Components
import {IndexList} from 'src/clockface'
import {IndexList} from '@influxdata/clockface'
// Types
import {LogEvent} from '@influxdata/influx'

View File

@ -3,8 +3,7 @@ import React, {PureComponent} from 'react'
import _ from 'lodash'
//Components
import {Overlay} from '@influxdata/clockface'
import {IndexList} from 'src/clockface'
import {Overlay, IndexList} from '@influxdata/clockface'
import RunLogRow from 'src/tasks/components/RunLogRow'
import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar'

View File

@ -3,8 +3,7 @@ import React, {PureComponent} from 'react'
import memoizeOne from 'memoize-one'
// Components
import {EmptyState} from '@influxdata/clockface'
import {IndexList} from 'src/clockface'
import {EmptyState, IndexList} from '@influxdata/clockface'
import TaskRunsRow from 'src/tasks/components/TaskRunsRow'
// Types

View File

@ -4,8 +4,7 @@ import {connect} from 'react-redux'
import moment from 'moment'
// Components
import {Overlay} from '@influxdata/clockface'
import {IndexList} from 'src/clockface'
import {Overlay, IndexList} from '@influxdata/clockface'
import RunLogsOverlay from 'src/tasks/components/RunLogsList'
// Actions