+ )
+ }
+
+ private get className(): string {
+ const {className} = this.props
+
+ return classnames('resource-list', {[`${className}`]: className})
+ }
+}
diff --git a/src/Components/ResourceList/List/ResourceListBody.md b/src/Components/ResourceList/List/ResourceListBody.md
new file mode 100644
index 00000000..4facf5e9
--- /dev/null
+++ b/src/Components/ResourceList/List/ResourceListBody.md
@@ -0,0 +1,24 @@
+# ResourceList
+
+`ResourceListBody` is intended to be the second child of `ResourceList`, similar to `` in a plain HTML table. It can be accessed via the single `ResourceList` import as a subclass.
+
+### Usage
+```js
+import {ResourceList} from '@influxdata/clockface'
+```
+```js
+
+ // Children
+
+```
+
+We recommend using `ResourceCard` as the child component type.
+
+### Example
+
+
+
+
+
+
+
diff --git a/src/Components/ResourceList/List/ResourceListBody.tsx b/src/Components/ResourceList/List/ResourceListBody.tsx
new file mode 100644
index 00000000..8007afa8
--- /dev/null
+++ b/src/Components/ResourceList/List/ResourceListBody.tsx
@@ -0,0 +1,49 @@
+// Libraries
+import React, {PureComponent, ReactNode} from 'react'
+import classnames from 'classnames'
+
+// Types
+import {StandardProps} from '../../../Types'
+
+interface Props extends StandardProps {
+ /** Element to show when no children are passed in, useful for implementing filtering */
+ emptyState: JSX.Element
+}
+
+export class ResourceListBody extends PureComponent {
+ public static readonly displayName = 'ResourceListBody'
+
+ public static defaultProps = {
+ testID: 'resource-list--body',
+ }
+
+ public render() {
+ const {testID} = this.props
+
+ return (
+
+ {this.children}
+
+ )
+ }
+
+ private get children(): JSX.Element | ReactNode {
+ const {children, emptyState} = this.props
+
+ if (
+ React.Children.count(children) === 0 ||
+ children === undefined ||
+ children === null
+ ) {
+ return emptyState
+ }
+
+ return children
+ }
+
+ private get className(): string {
+ const {className} = this.props
+
+ return classnames('resource-list--body', {[`${className}`]: className})
+ }
+}
diff --git a/src/Components/ResourceList/List/ResourceListHeader.md b/src/Components/ResourceList/List/ResourceListHeader.md
new file mode 100644
index 00000000..e791e147
--- /dev/null
+++ b/src/Components/ResourceList/List/ResourceListHeader.md
@@ -0,0 +1,24 @@
+# ResourceListHeader
+
+`ResourceListHeader` is intended to be the first child of `ResourceList`, similar to `` in a plain HTML table. It can be accessed via the single `ResourceList` import as a subclass.
+
+### Usage
+```js
+import {ResourceList} from '@influxdata/clockface'
+```
+```js
+
+ // Children
+
+```
+
+We recommend using `ResourceList.Sorter` as the child component type.
+
+### Example
+
+
+
+
+
+
+
diff --git a/src/Components/ResourceList/List/ResourceListHeader.tsx b/src/Components/ResourceList/List/ResourceListHeader.tsx
new file mode 100644
index 00000000..e6774823
--- /dev/null
+++ b/src/Components/ResourceList/List/ResourceListHeader.tsx
@@ -0,0 +1,46 @@
+// Libraries
+import React, {PureComponent} from 'react'
+import classnames from 'classnames'
+
+// Types
+import {StandardProps} from '../../../Types'
+
+interface Props extends StandardProps {
+ /** Used for rendering a filter input above the list, opposite the sort headers */
+ filterComponent?: JSX.Element
+}
+
+export class ResourceListHeader extends PureComponent {
+ public static readonly displayName = 'ResourceListHeader'
+
+ public static defaultProps = {
+ testID: 'resource-list--header',
+ }
+
+ public render() {
+ const {children, testID} = this.props
+
+ return (
+
+ }
+
+ return
+ }
+
+ private get className(): string {
+ const {className} = this.props
+
+ return classnames('resource-list--header', {[`${className}`]: className})
+ }
+}
diff --git a/src/Components/ResourceList/List/ResourceListSorter.md b/src/Components/ResourceList/List/ResourceListSorter.md
new file mode 100644
index 00000000..75ff4d04
--- /dev/null
+++ b/src/Components/ResourceList/List/ResourceListSorter.md
@@ -0,0 +1,61 @@
+# ResourceListSorter
+
+`ResourceListSorter` is a sortable header intended for use as a child of `ResourceListHeader`. It can be accessed via the single `ResourceList` import as a subclass.
+
+### Usage
+```js
+import {ResourceList} from '@influxdata/clockface'
+```
+```js
+
+```
+
+### Example
+
+
+### Enabling Sorting
+
+`ResourceListSorter`s have some handy features that making sorting easier. First you will need a stateful component that wraps `ResourceList`. This stateful component should have at least 3 pieces of state:
+
+| State Key | Type | |
+|:-----------------|:--------|----------------------------------------------------------------------------------|
+| `sortKey` | `string` | The identifier for the currently sorted column, can be `null` if no sort applied |
+| `sortDirection` | `Sort` | Keeps track of which direction sorting is happening in |
+| `items` | `[]` | List of items; enables sorting and/or filtering |
+
+Next, pass a handler function into each `` you want to be sortable:
+
+```js
+private handleSort = (nextSort: Sort, sortKey: string): void => {
+ this.setState({
+ sortDirection: nextSort,
+ sortKey,
+ })
+}
+```
+```js
+
+```
+
+When a sorter is clicked it cycles to the next available sort state and passes that back. This ensures that sort states are cycled through in a consistent manner.
+
+Make sure each each `` receives state:
+
+```js
+const {sortKey, sortDirection, items} = this.state
+```
+```js
+items.map(item => (
+
+))
+```
+
+
+
+
+
+
diff --git a/src/Components/ResourceList/List/ResourceListSorter.tsx b/src/Components/ResourceList/List/ResourceListSorter.tsx
new file mode 100644
index 00000000..fe1f89df
--- /dev/null
+++ b/src/Components/ResourceList/List/ResourceListSorter.tsx
@@ -0,0 +1,93 @@
+// Libraries
+import React, {PureComponent} from 'react'
+import classnames from 'classnames'
+
+// Types
+import {Sort, StandardProps} from '../../../Types'
+
+interface Props extends StandardProps {
+ /** Controls appearance of sort indicator (arrow) */
+ sort: Sort
+ /** Unique identifier for use in managing sort state */
+ sortKey: string
+ /** Name of attribute this element sorts on */
+ name: string
+ /** Useful for triggering a change in sort state */
+ onClick?: (nextSort: Sort, sortKey: string) => void
+}
+
+export class ResourceListSorter extends PureComponent {
+ public static readonly displayName = 'ResourceListSorter'
+
+ public static defaultProps = {
+ testID: 'resource-list--sorter',
+ }
+
+ public render() {
+ const {name, testID} = this.props
+
+ return (
+
+ {name}
+ {this.sortIndicator}
+
+ )
+ }
+
+ 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 title(): string | undefined {
+ const {sort, name} = this.props
+
+ if (sort === Sort.None) {
+ return `Sort ${name} in ${Sort.Ascending} order`
+ } else if (sort === Sort.Ascending) {
+ return `Sort ${name} in ${Sort.Descending} order`
+ }
+
+ return
+ }
+
+ private get sortIndicator(): JSX.Element | undefined {
+ const {onClick} = this.props
+
+ if (onClick) {
+ return (
+
+
+
+ )
+ }
+
+ return
+ }
+
+ private get className(): string {
+ const {sort, className} = this.props
+
+ return classnames('resource-list--sorter', {
+ 'resource-list--sort-descending': sort === Sort.Descending,
+ 'resource-list--sort-ascending': sort === Sort.Ascending,
+ [`${className}`]: className,
+ })
+ }
+}
diff --git a/src/Components/ResourceList/ResourceCardExample.md b/src/Components/ResourceList/ResourceCardExample.md
new file mode 100644
index 00000000..ca187885
--- /dev/null
+++ b/src/Components/ResourceList/ResourceCardExample.md
@@ -0,0 +1,17 @@
+# Toggleable Card
+
+This example shows what a `ResourceCard` looks like when a component is passed into the `toggle`, `labels` and `contextMenu` props.
+
+### Usage
+```js
+import {ResourceCard, SquareButton, SlideToggle, Label} from '@influxdata/clockface'
+```
+
+### Example
+
+
+
+
+
+
+
diff --git a/src/Components/ResourceList/ResourceList.stories.tsx b/src/Components/ResourceList/ResourceList.stories.tsx
new file mode 100644
index 00000000..0bd383c3
--- /dev/null
+++ b/src/Components/ResourceList/ResourceList.stories.tsx
@@ -0,0 +1,474 @@
+// Libraries
+import * as React from 'react'
+import marked from 'marked'
+
+// Storybook
+import {storiesOf} from '@storybook/react'
+import {jsxDecorator} from 'storybook-addon-jsx'
+import {
+ withKnobs,
+ text,
+ boolean,
+ array,
+ select,
+ object,
+ number,
+} from '@storybook/addon-knobs'
+import {mapEnumKeys} from '../../../.storybook/utils'
+
+// Components
+import {ResourceList} from './List/ResourceList'
+import {ResourceListHeader} from './List/ResourceListHeader'
+import {ResourceListBody} from './List/ResourceListBody'
+import {ResourceListSorter} from './List/ResourceListSorter'
+import {ResourceCard} from './Card/ResourceCard'
+import {ResourceCardName} from './Card/ResourceCardName'
+import {ResourceCardEditableName} from './Card/ResourceCardEditableName'
+import {ResourceCardDescription} from './Card/ResourceCardDescription'
+import {Input} from '../Inputs/Input'
+import {EmptyState} from '../EmptyState/EmptyState'
+import {SlideToggle} from '../SlideToggle/SlideToggle'
+import {SquareButton} from '../Button/Composed/SquareButton'
+import {Label} from '../Label/Label'
+import {ComponentSpacer} from '../ComponentSpacer/ComponentSpacer'
+
+// Types
+import {
+ Sort,
+ IconFont,
+ ComponentSize,
+ ComponentColor,
+ FlexDirection,
+} from '../../Types'
+
+// Notes
+const ResourceListReadme = marked(require('./List/ResourceList.md'))
+const ResourceListHeaderReadme = marked(require('./List/ResourceListHeader.md'))
+const ResourceListBodyReadme = marked(require('./List/ResourceListBody.md'))
+const ResourceListSorterReadme = marked(require('./List/ResourceListSorter.md'))
+const ResourceCardReadme = marked(require('./Card/ResourceCard.md'))
+const ResourceCardDescriptionReadme = marked(
+ require('./Card/ResourceCardDescription.md')
+)
+const ResourceCardNameReadme = marked(require('./Card/ResourceCardName.md'))
+const ResourceCardEditableNameReadme = marked(
+ require('./Card/ResourceCardEditableName.md')
+)
+const ResourceListExampleReadme = marked(require('./ResourceListExample.md'))
+const ResourceCardExampleReadme = marked(require('./ResourceCardExample.md'))
+
+const indexListStories = storiesOf(
+ 'Components|ResourceList/List Family',
+ module
+)
+ .addDecorator(withKnobs)
+ .addDecorator(jsxDecorator)
+
+const indexListCardStories = storiesOf(
+ 'Components|ResourceList/Card Family',
+ module
+)
+ .addDecorator(withKnobs)
+ .addDecorator(jsxDecorator)
+
+const indexListExampleStories = storiesOf(
+ 'Components|ResourceList/Examples',
+ module
+)
+ .addDecorator(withKnobs)
+ .addDecorator(jsxDecorator)
+
+indexListStories.add(
+ 'ResourceList',
+ () => (
+