feat(ui): resource sorting dropdowns (#17321)
* feat(ui): replace sortable dashboard table headers with sort dropdown * refactor(ui): remove unecessary components * refactor(ui): add testIDs to dropdown * refactor(ui): move tasks filter to page control bar * fix(ui): remove unused import * feat(ui): replace tasks sortable headers with sort dropdown * fix(ui): make search widget more responsive * fix(ui): make dashboard sort dropdown maintain size * refactor(ui): consolidate resource sorting into a single component * refactor(ui): standardize tabbed page headers * fix: use correct import paths * refactor(ui): implement resource sorting dropdown for variables * refactor(ui): implement sorting dropdown on labels list * chore: delete unused stylesheet * refactor: implement sort dropdown for templates list * refactor: implement sort dropdown on buckets list * refactor: update design of "what is a bucket?" card * refactor: implement sort dropdowns in telegrafs list * fix: appease linter * refactor: implement sort dropdown on scrapers list * chore: add testIDs to resource sorter * fix: remove unused code * fix: update buckets and telegraf e2e tests * fix: update labels e2e test * fix: update variables e2e test * refactor: move dashboards list empty state into own component * fix: oops derp ayyyy * chore: changelog * refactor: use more resource specific types for sort keyspull/17728/head
parent
0d092d3dbb
commit
8130aa07a3
|
|
@ -1,4 +1,4 @@
|
|||
## v2.0.0-beta.9
|
||||
## v2.0.0-beta.9 [unreleased]
|
||||
|
||||
### Features
|
||||
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
### UI Improvements
|
||||
|
||||
1. [17714](https://github.com/influxdata/influxdb/pull/17714): Cloud environments no longer render markdown images, for security reasons.
|
||||
1. [17321](https://github.com/influxdata/influxdb/pull/17321): Improve UI for sorting resources
|
||||
|
||||
## v2.0.0-beta.8 [2020-04-10]
|
||||
|
||||
|
|
|
|||
|
|
@ -75,8 +75,11 @@ describe('Buckets', () => {
|
|||
describe('Searching and Sorting', () => {
|
||||
it('can sort by name and retention', () => {
|
||||
const buckets = ['defbuck', '_tasks', '_monitoring']
|
||||
cy.getByTestID('name-sorter')
|
||||
cy.getByTestID('resource-sorter--button')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.getByTestID('resource-sorter--name-desc').click()
|
||||
})
|
||||
.then(() => {
|
||||
cy.get('[data-testid*="bucket-card"]').each((val, index) => {
|
||||
const testID = val.attr('data-testid')
|
||||
|
|
@ -84,8 +87,13 @@ describe('Buckets', () => {
|
|||
})
|
||||
})
|
||||
|
||||
cy.getByTestID('name-sorter')
|
||||
cy.getByTestID('resource-sorter--button')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.getByTestID(
|
||||
'resource-sorter--retentionRules[0].everySeconds-desc'
|
||||
).click()
|
||||
})
|
||||
.then(() => {
|
||||
const asc_buckets = buckets.slice().sort()
|
||||
cy.get('[data-testid*="bucket-card"]').each((val, index) => {
|
||||
|
|
|
|||
|
|
@ -225,8 +225,11 @@ describe('Collectors', () => {
|
|||
})
|
||||
})
|
||||
|
||||
cy.getByTestID('name-sorter')
|
||||
cy.getByTestID('resource-sorter--button')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.getByTestID('resource-sorter--name-desc').click()
|
||||
})
|
||||
.then(() => {
|
||||
// NOTE: this then is just here to let me scope this variable (alex)
|
||||
const teletubbies = telegrafs
|
||||
|
|
|
|||
|
|
@ -284,7 +284,11 @@ describe('labels', () => {
|
|||
}
|
||||
})
|
||||
|
||||
cy.getByTestID('sorter--name').click()
|
||||
cy.getByTestID('resource-sorter--button')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.getByTestID('resource-sorter--name-desc').click()
|
||||
})
|
||||
|
||||
// check sort desc
|
||||
cy.getByTestIDSubStr('label--pill').then(labels => {
|
||||
|
|
@ -296,7 +300,11 @@ describe('labels', () => {
|
|||
})
|
||||
|
||||
// reset to asc
|
||||
cy.getByTestID('sorter--name').click()
|
||||
cy.getByTestID('resource-sorter--button')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.getByTestID('resource-sorter--name-asc').click()
|
||||
})
|
||||
|
||||
cy.getByTestIDSubStr('label--pill').then(labels => {
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
|
|
@ -332,27 +340,32 @@ describe('labels', () => {
|
|||
a.description < b.description ? -1 : a.description > b.description ? 1 : 0
|
||||
)
|
||||
// check sort asc
|
||||
cy.getByTestID('sorter--desc').click()
|
||||
cy.getByTestID('resource-sorter--button')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.getByTestID('resource-sorter--properties.description-asc').click()
|
||||
})
|
||||
|
||||
cy.getByTestIDSubStr('resource-card').then(labels => {
|
||||
cy.getByTestID('label-card').then(labels => {
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
cy.getByTestIDSubStr('resource-card')
|
||||
cy.getByTestID('label-card--description')
|
||||
.eq(i)
|
||||
.should('have.text', 'Description: ' + names[i].description)
|
||||
.should('have.text', names[i].description)
|
||||
}
|
||||
})
|
||||
|
||||
// check sort desc
|
||||
cy.getByTestID('sorter--desc').click()
|
||||
cy.getByTestID('resource-sorter--button')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.getByTestID('resource-sorter--properties.description-desc').click()
|
||||
})
|
||||
|
||||
cy.getByTestIDSubStr('resource-card').then(labels => {
|
||||
cy.getByTestID('label-card').then(labels => {
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
cy.getByTestIDSubStr('resource-card')
|
||||
cy.getByTestID('label-card--description')
|
||||
.eq(i)
|
||||
.should(
|
||||
'have.text',
|
||||
'Description: ' + names[labels.length - (i + 1)].description
|
||||
)
|
||||
.should('have.text', names[labels.length - (i + 1)].description)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ describe('Variables', () => {
|
|||
})
|
||||
|
||||
it('keeps user input in text area when attempting to import invalid JSON', () => {
|
||||
cy.get('.tabbed-page-section--header').within(() => {
|
||||
cy.getByTestID('tabbed-page--header').within(() => {
|
||||
cy.contains('Create').click()
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -55,20 +55,28 @@ class TokensTab extends PureComponent<Props, State> {
|
|||
const {searchTerm, sortKey, sortDirection, sortType} = this.state
|
||||
const {tokens} = this.props
|
||||
|
||||
const leftHeaderItems = (
|
||||
<SearchWidget
|
||||
searchTerm={searchTerm}
|
||||
placeholderText="Filter Tokens..."
|
||||
onSearch={this.handleChangeSearchTerm}
|
||||
testID="input-field--filter"
|
||||
/>
|
||||
)
|
||||
|
||||
const rightHeaderItems = (
|
||||
<GenerateTokenDropdown
|
||||
onSelectAllAccess={this.handleGenerateAllAccess}
|
||||
onSelectReadWrite={this.handleGenerateReadWrite}
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabbedPageHeader>
|
||||
<SearchWidget
|
||||
searchTerm={searchTerm}
|
||||
placeholderText="Filter Tokens..."
|
||||
onSearch={this.handleChangeSearchTerm}
|
||||
testID="input-field--filter"
|
||||
/>
|
||||
<GenerateTokenDropdown
|
||||
onSelectAllAccess={this.handleGenerateAllAccess}
|
||||
onSelectReadWrite={this.handleGenerateReadWrite}
|
||||
/>
|
||||
</TabbedPageHeader>
|
||||
<TabbedPageHeader
|
||||
childrenLeft={leftHeaderItems}
|
||||
childrenRight={rightHeaderItems}
|
||||
/>
|
||||
<FilterAuthorizations
|
||||
list={tokens}
|
||||
searchTerm={searchTerm}
|
||||
|
|
|
|||
|
|
@ -54,12 +54,3 @@
|
|||
.system-bucket {
|
||||
color: mix($c-honeydew, $g13-mist);
|
||||
}
|
||||
|
||||
.buckets-buttons-wrap {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
text-align: right;
|
||||
@media screen and (max-width: 680px) {
|
||||
margin: 8px auto 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
import React, {FunctionComponent} from 'react'
|
||||
|
||||
// Components
|
||||
import {Panel, InfluxColors} from '@influxdata/clockface'
|
||||
import {Panel, Gradients} from '@influxdata/clockface'
|
||||
|
||||
const BucketExplainer: FunctionComponent = () => (
|
||||
<Panel backgroundColor={InfluxColors.Smoke} style={{marginTop: '32px'}}>
|
||||
<Panel gradient={Gradients.PolarExpress} border={true}>
|
||||
<Panel.Header>
|
||||
<h5>What is a Bucket?</h5>
|
||||
</Panel.Header>
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ import {Sort} from '@influxdata/clockface'
|
|||
// Utils
|
||||
import {SortTypes} from 'src/shared/utils/sort'
|
||||
|
||||
type SortKey = keyof Bucket | 'retentionRules[0].everySeconds'
|
||||
|
||||
interface Props {
|
||||
buckets: Bucket[]
|
||||
emptyState: JSX.Element
|
||||
|
|
@ -29,9 +27,6 @@ interface Props {
|
|||
sortKey: string
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
onClickColumn: (
|
||||
sortType: SortTypes
|
||||
) => (nextSort: Sort, sortKey: SortKey) => void
|
||||
}
|
||||
|
||||
class BucketList extends PureComponent<Props & WithRouterProps> {
|
||||
|
|
@ -40,38 +35,15 @@ class BucketList extends PureComponent<Props & WithRouterProps> {
|
|||
)
|
||||
|
||||
public render() {
|
||||
const {sortKey, sortDirection, onClickColumn} = this.props
|
||||
return (
|
||||
<>
|
||||
<ResourceList>
|
||||
<ResourceList.Header>
|
||||
<ResourceList.Sorter
|
||||
name="Name"
|
||||
sortKey={this.headerKeys[0]}
|
||||
sort={sortKey === this.headerKeys[0] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn(SortTypes.String)}
|
||||
testID="name-sorter"
|
||||
/>
|
||||
<ResourceList.Sorter
|
||||
name="Retention"
|
||||
sortKey={this.headerKeys[1]}
|
||||
sort={sortKey === this.headerKeys[1] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn(SortTypes.Float)}
|
||||
testID="retention-sorter"
|
||||
/>
|
||||
</ResourceList.Header>
|
||||
<ResourceList.Body emptyState={this.props.emptyState}>
|
||||
{this.listBuckets}
|
||||
</ResourceList.Body>
|
||||
</ResourceList>
|
||||
</>
|
||||
<ResourceList>
|
||||
<ResourceList.Body emptyState={this.props.emptyState}>
|
||||
{this.listBuckets}
|
||||
</ResourceList.Body>
|
||||
</ResourceList>
|
||||
)
|
||||
}
|
||||
|
||||
private get headerKeys(): SortKey[] {
|
||||
return ['name', 'retentionRules[0].everySeconds']
|
||||
}
|
||||
|
||||
private get listBuckets(): JSX.Element[] {
|
||||
const {
|
||||
buckets,
|
||||
|
|
|
|||
|
|
@ -18,13 +18,15 @@ import {
|
|||
Overlay,
|
||||
} from '@influxdata/clockface'
|
||||
import SearchWidget from 'src/shared/components/search_widget/SearchWidget'
|
||||
import SettingsTabbedPageHeader from 'src/settings/components/SettingsTabbedPageHeader'
|
||||
import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader'
|
||||
import FilterList from 'src/shared/components/FilterList'
|
||||
import BucketList from 'src/buckets/components/BucketList'
|
||||
import CreateBucketOverlay from 'src/buckets/components/CreateBucketOverlay'
|
||||
import AssetLimitAlert from 'src/cloud/components/AssetLimitAlert'
|
||||
import BucketExplainer from 'src/buckets/components/BucketExplainer'
|
||||
import DemoDataDropdown from 'src/buckets/components/DemoDataDropdown'
|
||||
import {FeatureFlag} from 'src/shared/utils/featureFlag'
|
||||
import ResourceSortDropdown from 'src/shared/components/resource_sort_dropdown/ResourceSortDropdown'
|
||||
|
||||
// Actions
|
||||
import {
|
||||
|
|
@ -58,6 +60,7 @@ import {
|
|||
ResourceType,
|
||||
OwnBucket,
|
||||
} from 'src/types'
|
||||
import {BucketSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
|
||||
interface StateProps {
|
||||
org: Organization
|
||||
|
|
@ -78,15 +81,13 @@ interface DispatchProps {
|
|||
interface State {
|
||||
searchTerm: string
|
||||
overlayState: OverlayState
|
||||
sortKey: SortKey
|
||||
sortKey: BucketSortKey
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
}
|
||||
|
||||
type Props = DispatchProps & StateProps
|
||||
|
||||
type SortKey = keyof Bucket
|
||||
|
||||
const FilterBuckets = FilterList<Bucket>()
|
||||
|
||||
@ErrorHandling
|
||||
|
|
@ -126,6 +127,46 @@ class BucketsTab extends PureComponent<Props, State> {
|
|||
sortType,
|
||||
} = this.state
|
||||
|
||||
const leftHeaderItems = (
|
||||
<>
|
||||
<SearchWidget
|
||||
placeholderText="Filter buckets..."
|
||||
searchTerm={searchTerm}
|
||||
onSearch={this.handleFilterUpdate}
|
||||
/>
|
||||
<ResourceSortDropdown
|
||||
resourceType={ResourceType.Buckets}
|
||||
sortDirection={sortDirection}
|
||||
sortKey={sortKey}
|
||||
sortType={sortType}
|
||||
onSelect={this.handleSort}
|
||||
width={238}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
const rightHeaderItems = (
|
||||
<>
|
||||
<FeatureFlag name="demodata">
|
||||
{demoDataBuckets.length > 0 && (
|
||||
<DemoDataDropdown
|
||||
buckets={demoDataBuckets}
|
||||
getMembership={getDemoDataBucketMembership}
|
||||
/>
|
||||
)}
|
||||
</FeatureFlag>
|
||||
<Button
|
||||
text="Create Bucket"
|
||||
icon={IconFont.Plus}
|
||||
color={ComponentColor.Primary}
|
||||
onClick={this.handleOpenModal}
|
||||
testID="Create Bucket"
|
||||
status={this.createButtonStatus}
|
||||
titleText={this.createButtonTitleText}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<AssetLimitAlert
|
||||
|
|
@ -133,30 +174,10 @@ class BucketsTab extends PureComponent<Props, State> {
|
|||
limitStatus={limitStatus}
|
||||
className="load-data--asset-alert"
|
||||
/>
|
||||
<SettingsTabbedPageHeader>
|
||||
<SearchWidget
|
||||
placeholderText="Filter buckets..."
|
||||
searchTerm={searchTerm}
|
||||
onSearch={this.handleFilterUpdate}
|
||||
/>
|
||||
<div className="buckets-buttons-wrap">
|
||||
{isFlagEnabled('demodata') && demoDataBuckets.length > 0 && (
|
||||
<DemoDataDropdown
|
||||
buckets={demoDataBuckets}
|
||||
getMembership={getDemoDataBucketMembership}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
text="Create Bucket"
|
||||
icon={IconFont.Plus}
|
||||
color={ComponentColor.Primary}
|
||||
onClick={this.handleOpenModal}
|
||||
testID="Create Bucket"
|
||||
status={this.createButtonStatus}
|
||||
titleText={this.createButtonTitleText}
|
||||
/>
|
||||
</div>
|
||||
</SettingsTabbedPageHeader>
|
||||
<TabbedPageHeader
|
||||
childrenLeft={leftHeaderItems}
|
||||
childrenRight={rightHeaderItems}
|
||||
/>
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
<Grid.Column
|
||||
|
|
@ -179,7 +200,6 @@ class BucketsTab extends PureComponent<Props, State> {
|
|||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onClickColumn={this.handleClickColumn}
|
||||
/>
|
||||
)}
|
||||
</FilterBuckets>
|
||||
|
|
@ -204,11 +224,12 @@ class BucketsTab extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private handleClickColumn = (sortType: SortTypes) => (
|
||||
nextSort: Sort,
|
||||
sortKey: SortKey
|
||||
) => {
|
||||
this.setState({sortKey, sortDirection: nextSort, sortType})
|
||||
private handleSort = (
|
||||
sortKey: BucketSortKey,
|
||||
sortDirection: Sort,
|
||||
sortType: SortTypes
|
||||
): void => {
|
||||
this.setState({sortKey, sortDirection, sortType})
|
||||
}
|
||||
|
||||
private handleDeleteBucket = ({id, name}: OwnBucket) => {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import SearchWidget from 'src/shared/components/search_widget/SearchWidget'
|
|||
import AddResourceDropdown from 'src/shared/components/AddResourceDropdown'
|
||||
import GetAssetLimits from 'src/cloud/components/GetAssetLimits'
|
||||
import AssetLimitAlert from 'src/cloud/components/AssetLimitAlert'
|
||||
import ResourceSortDropdown from 'src/shared/components/resource_sort_dropdown/ResourceSortDropdown'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
// Utils
|
||||
|
|
@ -23,9 +24,11 @@ import {extractDashboardLimits} from 'src/cloud/utils/limits'
|
|||
import {createDashboard as createDashboardAction} from 'src/dashboards/actions/thunks'
|
||||
|
||||
// Types
|
||||
import {AppState} from 'src/types'
|
||||
import {AppState, ResourceType} from 'src/types'
|
||||
import {LimitStatus} from 'src/cloud/actions/limits'
|
||||
import {ComponentStatus} from '@influxdata/clockface'
|
||||
import {ComponentStatus, Sort} from '@influxdata/clockface'
|
||||
import {SortTypes} from 'src/shared/utils/sort'
|
||||
import {DashboardSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
|
||||
interface DispatchProps {
|
||||
createDashboard: typeof createDashboardAction
|
||||
|
|
@ -44,6 +47,9 @@ type Props = DispatchProps & StateProps & OwnProps
|
|||
|
||||
interface State {
|
||||
searchTerm: string
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
sortKey: DashboardSortKey
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
|
|
@ -53,12 +59,15 @@ class DashboardIndex extends PureComponent<Props, State> {
|
|||
|
||||
this.state = {
|
||||
searchTerm: '',
|
||||
sortDirection: Sort.Ascending,
|
||||
sortType: SortTypes.String,
|
||||
sortKey: 'name',
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {createDashboard, limitStatus} = this.props
|
||||
const {searchTerm} = this.state
|
||||
const {searchTerm, sortDirection, sortType, sortKey} = this.state
|
||||
return (
|
||||
<>
|
||||
<Page
|
||||
|
|
@ -76,6 +85,13 @@ class DashboardIndex extends PureComponent<Props, State> {
|
|||
onSearch={this.handleFilterDashboards}
|
||||
searchTerm={searchTerm}
|
||||
/>
|
||||
<ResourceSortDropdown
|
||||
resourceType={ResourceType.Dashboards}
|
||||
sortDirection={sortDirection}
|
||||
sortKey={sortKey}
|
||||
sortType={sortType}
|
||||
onSelect={this.handleSort}
|
||||
/>
|
||||
</Page.ControlBarLeft>
|
||||
<Page.ControlBarRight>
|
||||
<AddResourceDropdown
|
||||
|
|
@ -101,6 +117,9 @@ class DashboardIndex extends PureComponent<Props, State> {
|
|||
<DashboardsIndexContents
|
||||
searchTerm={searchTerm}
|
||||
onFilterChange={this.handleFilterDashboards}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
sortKey={sortKey}
|
||||
/>
|
||||
</GetAssetLimits>
|
||||
</Page.Contents>
|
||||
|
|
@ -110,6 +129,14 @@ class DashboardIndex extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private handleSort = (
|
||||
sortKey: DashboardSortKey,
|
||||
sortDirection: Sort,
|
||||
sortType: SortTypes
|
||||
): void => {
|
||||
this.setState({sortKey, sortDirection, sortType})
|
||||
}
|
||||
|
||||
private handleFilterDashboards = (searchTerm: string): void => {
|
||||
this.setState({searchTerm})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,18 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
|
|||
|
||||
// Types
|
||||
import {Dashboard, AppState, RemoteDataState, ResourceType} from 'src/types'
|
||||
import {Sort} from '@influxdata/clockface'
|
||||
import {getAll} from 'src/resources/selectors'
|
||||
import {SortTypes} from 'src/shared/utils/sort'
|
||||
import {DashboardSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
|
||||
interface OwnProps {
|
||||
onFilterChange: (searchTerm: string) => void
|
||||
searchTerm: string
|
||||
filterComponent?: JSX.Element
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
sortKey: DashboardSortKey
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
|
|
@ -48,7 +54,15 @@ class DashboardsIndexContents extends Component<Props> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const {searchTerm, dashboards, filterComponent, onFilterChange} = this.props
|
||||
const {
|
||||
searchTerm,
|
||||
dashboards,
|
||||
filterComponent,
|
||||
onFilterChange,
|
||||
sortDirection,
|
||||
sortType,
|
||||
sortKey,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<FilterDashboards
|
||||
|
|
@ -63,6 +77,9 @@ class DashboardsIndexContents extends Component<Props> {
|
|||
filterComponent={filterComponent}
|
||||
dashboards={filteredDashboards}
|
||||
onFilterChange={onFilterChange}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
sortKey={sortKey}
|
||||
/>
|
||||
)}
|
||||
</FilterDashboards>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
|
||||
// Components
|
||||
import {EmptyState, ComponentSize} from '@influxdata/clockface'
|
||||
import AddResourceDropdown from 'src/shared/components/AddResourceDropdown'
|
||||
|
||||
// Actions
|
||||
import {createDashboard} from 'src/dashboards/actions/thunks'
|
||||
|
||||
interface ComponentProps {
|
||||
searchTerm?: string
|
||||
onCreateDashboard: typeof createDashboard
|
||||
summonImportOverlay: () => void
|
||||
summonImportFromTemplateOverlay: () => void
|
||||
}
|
||||
|
||||
const DashboardsTableEmpty: FC<ComponentProps> = ({
|
||||
searchTerm,
|
||||
onCreateDashboard,
|
||||
summonImportOverlay,
|
||||
summonImportFromTemplateOverlay,
|
||||
}) => {
|
||||
if (searchTerm) {
|
||||
return (
|
||||
<EmptyState size={ComponentSize.Large} testID="empty-dashboards-list">
|
||||
<EmptyState.Text>No Dashboards match your search term</EmptyState.Text>
|
||||
</EmptyState>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<EmptyState size={ComponentSize.Large} testID="empty-dashboards-list">
|
||||
<EmptyState.Text>
|
||||
Looks like you don't have any <b>Dashboards</b>, why not create one?
|
||||
</EmptyState.Text>
|
||||
<AddResourceDropdown
|
||||
onSelectNew={onCreateDashboard}
|
||||
onSelectImport={summonImportOverlay}
|
||||
onSelectTemplate={summonImportFromTemplateOverlay}
|
||||
resourceName="Dashboard"
|
||||
canImportFromTemplate={true}
|
||||
/>
|
||||
</EmptyState>
|
||||
)
|
||||
}
|
||||
|
||||
export default DashboardsTableEmpty
|
||||
|
|
@ -5,15 +5,19 @@ import {withRouter, WithRouterProps} from 'react-router'
|
|||
import _ from 'lodash'
|
||||
|
||||
// Components
|
||||
import {EmptyState, ResourceList} from '@influxdata/clockface'
|
||||
import AddResourceDropdown from 'src/shared/components/AddResourceDropdown'
|
||||
import DashboardCards from 'src/dashboards/components/dashboard_index/DashboardCards'
|
||||
import {createDashboard, getDashboards} from 'src/dashboards/actions/thunks'
|
||||
import DashobardsTableEmpty from 'src/dashboards/components/dashboard_index/DashboardsTableEmpty'
|
||||
|
||||
// Utilities
|
||||
import {getLabels} from 'src/labels/actions/thunks'
|
||||
|
||||
// Actions
|
||||
import {createDashboard, getDashboards} from 'src/dashboards/actions/thunks'
|
||||
|
||||
// Types
|
||||
import {AppState, Dashboard, RemoteDataState} from 'src/types'
|
||||
import {Sort, ComponentSize} from '@influxdata/clockface'
|
||||
import {Sort} from '@influxdata/clockface'
|
||||
import {DashboardSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
import {SortTypes} from 'src/shared/utils/sort'
|
||||
|
||||
interface OwnProps {
|
||||
|
|
@ -21,11 +25,8 @@ interface OwnProps {
|
|||
onFilterChange: (searchTerm: string) => void
|
||||
filterComponent?: JSX.Element
|
||||
dashboards: Dashboard[]
|
||||
}
|
||||
|
||||
interface State {
|
||||
sortKey: SortKey
|
||||
sortDirection: Sort
|
||||
sortKey: DashboardSortKey
|
||||
sortType: SortTypes
|
||||
}
|
||||
|
||||
|
|
@ -39,79 +40,48 @@ interface DispatchProps {
|
|||
getLabels: typeof getLabels
|
||||
}
|
||||
|
||||
type SortKey = keyof Dashboard | 'meta.updatedAt'
|
||||
|
||||
type Props = OwnProps & StateProps & DispatchProps & WithRouterProps
|
||||
|
||||
class DashboardsTable extends PureComponent<Props, State> {
|
||||
state: State = {
|
||||
sortKey: 'name',
|
||||
sortDirection: Sort.Ascending,
|
||||
sortType: SortTypes.String,
|
||||
}
|
||||
|
||||
class DashboardsTable extends PureComponent<Props> {
|
||||
public componentDidMount() {
|
||||
this.props.getDashboards()
|
||||
this.props.getLabels()
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {status, dashboards, filterComponent, onFilterChange} = this.props
|
||||
|
||||
const {sortKey, sortDirection, sortType} = this.state
|
||||
|
||||
let body
|
||||
const {
|
||||
status,
|
||||
dashboards,
|
||||
onFilterChange,
|
||||
sortKey,
|
||||
sortDirection,
|
||||
sortType,
|
||||
onCreateDashboard,
|
||||
searchTerm,
|
||||
} = this.props
|
||||
|
||||
if (status === RemoteDataState.Done && !dashboards.length) {
|
||||
body = (
|
||||
<ResourceList.Body emptyState={null}>
|
||||
{this.emptyState}
|
||||
</ResourceList.Body>
|
||||
)
|
||||
} else {
|
||||
body = (
|
||||
<ResourceList.Body style={{height: '100%'}} emptyState={null}>
|
||||
<DashboardCards
|
||||
dashboards={dashboards}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
</ResourceList.Body>
|
||||
return (
|
||||
<DashobardsTableEmpty
|
||||
searchTerm={searchTerm}
|
||||
onCreateDashboard={onCreateDashboard}
|
||||
summonImportFromTemplateOverlay={this.summonImportFromTemplateOverlay}
|
||||
summonImportOverlay={this.summonImportOverlay}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ResourceList>
|
||||
<ResourceList.Header filterComponent={filterComponent}>
|
||||
<ResourceList.Sorter
|
||||
name="name"
|
||||
sortKey="name"
|
||||
sort={sortKey === 'name' ? sortDirection : Sort.None}
|
||||
onClick={this.handleClickColumn}
|
||||
/>
|
||||
<ResourceList.Sorter
|
||||
name="modified"
|
||||
sortKey="meta.updatedAt"
|
||||
sort={sortKey === 'meta.updatedAt' ? sortDirection : Sort.None}
|
||||
onClick={this.handleClickColumn}
|
||||
/>
|
||||
</ResourceList.Header>
|
||||
{body}
|
||||
</ResourceList>
|
||||
<DashboardCards
|
||||
dashboards={dashboards}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
private handleClickColumn = (nextSort: Sort, sortKey: SortKey) => {
|
||||
let sortType = SortTypes.String
|
||||
if (sortKey === 'meta.updatedAt') {
|
||||
sortType = SortTypes.Date
|
||||
}
|
||||
|
||||
this.setState({sortKey, sortDirection: nextSort, sortType})
|
||||
}
|
||||
|
||||
private summonImportOverlay = (): void => {
|
||||
const {
|
||||
router,
|
||||
|
|
@ -127,35 +97,6 @@ class DashboardsTable extends PureComponent<Props, State> {
|
|||
} = this.props
|
||||
router.push(`/orgs/${orgID}/dashboards/import/template`)
|
||||
}
|
||||
|
||||
private get emptyState(): JSX.Element {
|
||||
const {onCreateDashboard, searchTerm} = this.props
|
||||
|
||||
if (searchTerm) {
|
||||
return (
|
||||
<EmptyState size={ComponentSize.Large} testID="empty-dashboards-list">
|
||||
<EmptyState.Text>
|
||||
No Dashboards match your search term
|
||||
</EmptyState.Text>
|
||||
</EmptyState>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<EmptyState size={ComponentSize.Large} testID="empty-dashboards-list">
|
||||
<EmptyState.Text>
|
||||
Looks like you don't have any <b>Dashboards</b>, why not create one?
|
||||
</EmptyState.Text>
|
||||
<AddResourceDropdown
|
||||
onSelectNew={onCreateDashboard}
|
||||
onSelectImport={this.summonImportOverlay}
|
||||
onSelectTemplate={this.summonImportFromTemplateOverlay}
|
||||
resourceName="Dashboard"
|
||||
canImportFromTemplate={true}
|
||||
/>
|
||||
</EmptyState>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mstp = (state: AppState): StateProps => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
.label-card {
|
||||
.cf-resource-card--contents {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.cf-resource-card--row {
|
||||
margin-right: $cf-marg-c;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.label-card--description {
|
||||
font-weight: $cf-font-weight--medium;
|
||||
}
|
||||
|
||||
.label-card--description__untitled {
|
||||
color: $g8-storm;
|
||||
font-style: italic;
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Components
|
||||
import {
|
||||
|
|
@ -26,24 +27,39 @@ export default class LabelCard extends PureComponent<Props> {
|
|||
public render() {
|
||||
const {label, onDelete} = this.props
|
||||
|
||||
const labelHasDescription = !!label.properties.description
|
||||
|
||||
const descriptionClassName = classnames('label-card--description', {
|
||||
'label-card--description__untitled': !labelHasDescription,
|
||||
})
|
||||
|
||||
const description = labelHasDescription
|
||||
? label.properties.description
|
||||
: 'No description'
|
||||
|
||||
return (
|
||||
<>
|
||||
<ResourceCard
|
||||
testID="label-card"
|
||||
contextMenu={<LabelContextMenu label={label} onDelete={onDelete} />}
|
||||
name={
|
||||
<LabelComponent
|
||||
id={label.id}
|
||||
name={label.name}
|
||||
color={label.properties.color}
|
||||
description={label.properties.description}
|
||||
size={ComponentSize.Small}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
}
|
||||
metaData={[<>Description: {label.properties.description}</>]}
|
||||
/>
|
||||
</>
|
||||
<ResourceCard
|
||||
className="label-card"
|
||||
testID="label-card"
|
||||
contextMenu={<LabelContextMenu label={label} onDelete={onDelete} />}
|
||||
name={
|
||||
<LabelComponent
|
||||
id={label.id}
|
||||
name={label.name}
|
||||
color={label.properties.color}
|
||||
description={label.properties.description}
|
||||
size={ComponentSize.Small}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<p
|
||||
className={descriptionClassName}
|
||||
data-testid="label-card--description"
|
||||
>
|
||||
{description}
|
||||
</p>
|
||||
</ResourceCard>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ interface Props {
|
|||
sortKey: string
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
onClickColumn: (mextSort: Sort, sortKey: string) => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
|
@ -49,27 +48,9 @@ export default class LabelList extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const {sortKey, sortDirection, onClickColumn} = this.props
|
||||
const headerKeys = ['name', 'properties.description']
|
||||
return (
|
||||
<>
|
||||
<ResourceList>
|
||||
<ResourceList.Header>
|
||||
<ResourceList.Sorter
|
||||
name={headerKeys[0]}
|
||||
sortKey={headerKeys[0]}
|
||||
sort={sortKey === headerKeys[0] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
testID="sorter--name"
|
||||
/>
|
||||
<ResourceList.Sorter
|
||||
name="Description"
|
||||
sortKey={headerKeys[1]}
|
||||
sort={sortKey === headerKeys[1] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
testID="sorter--desc"
|
||||
/>
|
||||
</ResourceList.Header>
|
||||
<ResourceList.Body emptyState={this.props.emptyState}>
|
||||
{this.rows}
|
||||
</ResourceList.Body>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import CreateLabelOverlay from 'src/labels/components/CreateLabelOverlay'
|
|||
import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader'
|
||||
import LabelList from 'src/labels/components/LabelList'
|
||||
import FilterList from 'src/shared/components/FilterList'
|
||||
import ResourceSortDropdown from 'src/shared/components/resource_sort_dropdown/ResourceSortDropdown'
|
||||
|
||||
// Actions
|
||||
import {createLabel, updateLabel, deleteLabel} from 'src/labels/actions/thunks'
|
||||
|
|
@ -28,6 +29,7 @@ import {
|
|||
Sort,
|
||||
} from '@influxdata/clockface'
|
||||
import {SortTypes} from 'src/shared/utils/sort'
|
||||
import {LabelSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
|
@ -39,7 +41,7 @@ interface StateProps {
|
|||
interface State {
|
||||
searchTerm: string
|
||||
isOverlayVisible: boolean
|
||||
sortKey: SortKey
|
||||
sortKey: LabelSortKey
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
}
|
||||
|
|
@ -52,8 +54,6 @@ interface DispatchProps {
|
|||
|
||||
type Props = DispatchProps & StateProps
|
||||
|
||||
type SortKey = keyof Label
|
||||
|
||||
const FilterLabels = FilterList<Label>()
|
||||
@ErrorHandling
|
||||
class Labels extends PureComponent<Props, State> {
|
||||
|
|
@ -79,22 +79,39 @@ class Labels extends PureComponent<Props, State> {
|
|||
sortType,
|
||||
} = this.state
|
||||
|
||||
const leftHeaderItems = (
|
||||
<>
|
||||
<SearchWidget
|
||||
searchTerm={searchTerm}
|
||||
onSearch={this.handleFilterChange}
|
||||
placeholderText="Filter Labels..."
|
||||
/>
|
||||
<ResourceSortDropdown
|
||||
resourceType={ResourceType.Labels}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onSelect={this.handleSort}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
const rightHeaderItems = (
|
||||
<Button
|
||||
text="Create Label"
|
||||
color={ComponentColor.Primary}
|
||||
icon={IconFont.Plus}
|
||||
onClick={this.handleShowOverlay}
|
||||
testID="button-create"
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabbedPageHeader>
|
||||
<SearchWidget
|
||||
searchTerm={searchTerm}
|
||||
onSearch={this.handleFilterChange}
|
||||
placeholderText="Filter Labels..."
|
||||
/>
|
||||
<Button
|
||||
text="Create Label"
|
||||
color={ComponentColor.Primary}
|
||||
icon={IconFont.Plus}
|
||||
onClick={this.handleShowOverlay}
|
||||
testID="button-create"
|
||||
/>
|
||||
</TabbedPageHeader>
|
||||
<TabbedPageHeader
|
||||
childrenLeft={leftHeaderItems}
|
||||
childrenRight={rightHeaderItems}
|
||||
/>
|
||||
<FilterLabels
|
||||
list={labels}
|
||||
searchKeys={['name', 'properties.description']}
|
||||
|
|
@ -109,7 +126,6 @@ class Labels extends PureComponent<Props, State> {
|
|||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onClickColumn={this.handleClickColumn}
|
||||
/>
|
||||
)}
|
||||
</FilterLabels>
|
||||
|
|
@ -123,9 +139,12 @@ class Labels extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private handleClickColumn = (nextSort: Sort, sortKey: SortKey) => {
|
||||
const sortType = SortTypes.String
|
||||
this.setState({sortKey, sortDirection: nextSort, sortType})
|
||||
private handleSort = (
|
||||
sortKey: LabelSortKey,
|
||||
sortDirection: Sort,
|
||||
sortType: SortTypes
|
||||
): void => {
|
||||
this.setState({sortKey, sortDirection, sortType})
|
||||
}
|
||||
|
||||
private handleShowOverlay = (): void => {
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
RandomLabelColor Styles
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
.random-color--button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.label-colors--swatch {
|
||||
margin-right: $ix-marg-b;
|
||||
}
|
||||
|
||||
> span.button-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import {connect} from 'react-redux'
|
|||
import {withRouter, WithRouterProps} from 'react-router'
|
||||
|
||||
// Components
|
||||
import SettingsTabbedPageHeader from 'src/settings/components/SettingsTabbedPageHeader'
|
||||
import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader'
|
||||
import {EmptyState, Sort} from '@influxdata/clockface'
|
||||
import SearchWidget from 'src/shared/components/search_widget/SearchWidget'
|
||||
import MemberList from 'src/members/components/MemberList'
|
||||
|
|
@ -59,13 +59,15 @@ class Members extends PureComponent<Props & WithRouterProps, State> {
|
|||
|
||||
return (
|
||||
<>
|
||||
<SettingsTabbedPageHeader>
|
||||
<SearchWidget
|
||||
placeholderText="Filter members..."
|
||||
searchTerm={searchTerm}
|
||||
onSearch={this.handleFilterChange}
|
||||
/>
|
||||
</SettingsTabbedPageHeader>
|
||||
<TabbedPageHeader
|
||||
childrenLeft={
|
||||
<SearchWidget
|
||||
placeholderText="Filter members..."
|
||||
searchTerm={searchTerm}
|
||||
onSearch={this.handleFilterChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FilterMembers
|
||||
list={this.props.members}
|
||||
searchKeys={['name']}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
.tabbed-page--header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
margin-bottom: $cf-marg-d;
|
||||
}
|
||||
|
||||
.tabbed-page--header-left,
|
||||
.tabbed-page--header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
& > * {
|
||||
margin-right: $cf-marg-a;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tabbed-page--header-right {
|
||||
order: 1;
|
||||
margin-bottom: $cf-marg-b;
|
||||
|
||||
&:only-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tabbed-page--header-left {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
@media screen and (min-width: $cf-grid--breakpoint-sm) {
|
||||
.tabbed-page--header {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.tabbed-page--header-left {
|
||||
flex: 1 0 0;
|
||||
order: 1;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.tabbed-page--header-right {
|
||||
flex: 1 0 0;
|
||||
order: 2;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,8 +14,6 @@ import {Sort} from '@influxdata/clockface'
|
|||
// Selectors
|
||||
import {getSortedResources} from 'src/shared/utils/sort'
|
||||
|
||||
type SortKey = keyof Scraper
|
||||
|
||||
interface Props {
|
||||
scrapers: Scraper[]
|
||||
emptyState: JSX.Element
|
||||
|
|
@ -24,7 +22,6 @@ interface Props {
|
|||
sortKey: string
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
onClickColumn: (nextSort: Sort, sortKey: SortKey) => void
|
||||
}
|
||||
|
||||
export default class ScraperList extends PureComponent<Props> {
|
||||
|
|
@ -33,43 +30,17 @@ export default class ScraperList extends PureComponent<Props> {
|
|||
)
|
||||
|
||||
public render() {
|
||||
const {emptyState, sortKey, sortDirection, onClickColumn} = this.props
|
||||
const {emptyState} = this.props
|
||||
|
||||
return (
|
||||
<>
|
||||
<ResourceList>
|
||||
<ResourceList.Header>
|
||||
<ResourceList.Sorter
|
||||
name={this.headerKeys[0]}
|
||||
sortKey={this.headerKeys[0]}
|
||||
sort={sortKey === this.headerKeys[0] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
/>
|
||||
<ResourceList.Sorter
|
||||
name={this.headerKeys[1]}
|
||||
sortKey={this.headerKeys[1]}
|
||||
sort={sortKey === this.headerKeys[1] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
/>
|
||||
<ResourceList.Sorter
|
||||
name={this.headerKeys[2]}
|
||||
sortKey={this.headerKeys[2]}
|
||||
sort={sortKey === this.headerKeys[2] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
/>
|
||||
</ResourceList.Header>
|
||||
<ResourceList.Body emptyState={emptyState}>
|
||||
{this.scrapersList}
|
||||
</ResourceList.Body>
|
||||
</ResourceList>
|
||||
</>
|
||||
<ResourceList>
|
||||
<ResourceList.Body emptyState={emptyState}>
|
||||
{this.scrapersList}
|
||||
</ResourceList.Body>
|
||||
</ResourceList>
|
||||
)
|
||||
}
|
||||
|
||||
private get headerKeys(): SortKey[] {
|
||||
return ['name', 'url', 'bucket']
|
||||
}
|
||||
|
||||
public get scrapersList(): JSX.Element[] {
|
||||
const {
|
||||
scrapers,
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ import {isEmpty} from 'lodash'
|
|||
// Components
|
||||
import {Button, EmptyState, Sort} from '@influxdata/clockface'
|
||||
import SearchWidget from 'src/shared/components/search_widget/SearchWidget'
|
||||
import SettingsTabbedPageHeader from 'src/settings/components/SettingsTabbedPageHeader'
|
||||
import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader'
|
||||
import ScraperList from 'src/scrapers/components/ScraperList'
|
||||
import NoBucketsWarning from 'src/buckets/components/NoBucketsWarning'
|
||||
import FilterList from 'src/shared/components/FilterList'
|
||||
import ResourceSortDropdown from 'src/shared/components/resource_sort_dropdown/ResourceSortDropdown'
|
||||
|
||||
// Actions
|
||||
import {updateScraper, deleteScraper} from 'src/scrapers/actions/thunks'
|
||||
|
|
@ -27,6 +28,7 @@ import {
|
|||
ComponentStatus,
|
||||
} from '@influxdata/clockface'
|
||||
import {AppState, Bucket, Scraper, Organization, ResourceType} from 'src/types'
|
||||
import {ScraperSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
|
||||
// Selectors
|
||||
import {getOrg} from 'src/organizations/selectors'
|
||||
|
|
@ -47,13 +49,11 @@ type Props = StateProps & DispatchProps & WithRouterProps
|
|||
|
||||
interface State {
|
||||
searchTerm: string
|
||||
sortKey: SortKey
|
||||
sortKey: ScraperSortKey
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
}
|
||||
|
||||
type SortKey = keyof Scraper
|
||||
|
||||
const FilterScrapers = FilterList<Scraper>()
|
||||
|
||||
@ErrorHandling
|
||||
|
|
@ -73,16 +73,31 @@ class Scrapers extends PureComponent<Props, State> {
|
|||
const {searchTerm, sortKey, sortDirection, sortType} = this.state
|
||||
const {scrapers} = this.props
|
||||
|
||||
const leftHeaderItems = (
|
||||
<>
|
||||
<SearchWidget
|
||||
placeholderText="Filter scrapers..."
|
||||
searchTerm={searchTerm}
|
||||
onSearch={this.handleFilterChange}
|
||||
/>
|
||||
<ResourceSortDropdown
|
||||
resourceType={ResourceType.Scrapers}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onSelect={this.handleSort}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsTabbedPageHeader>
|
||||
<SearchWidget
|
||||
placeholderText="Filter scrapers..."
|
||||
searchTerm={searchTerm}
|
||||
onSearch={this.handleFilterChange}
|
||||
/>
|
||||
{this.createScraperButton('create-scraper-button-header')}
|
||||
</SettingsTabbedPageHeader>
|
||||
<TabbedPageHeader
|
||||
childrenLeft={leftHeaderItems}
|
||||
childrenRight={this.createScraperButton(
|
||||
'create-scraper-button-header'
|
||||
)}
|
||||
/>
|
||||
<NoBucketsWarning visible={this.hasNoBuckets} resourceName="Scrapers" />
|
||||
<FilterScrapers
|
||||
searchTerm={searchTerm}
|
||||
|
|
@ -98,7 +113,6 @@ class Scrapers extends PureComponent<Props, State> {
|
|||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onClickColumn={this.handleClickColumn}
|
||||
/>
|
||||
)}
|
||||
</FilterScrapers>
|
||||
|
|
@ -106,9 +120,12 @@ class Scrapers extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private handleClickColumn = (nextSort: Sort, sortKey: SortKey) => {
|
||||
const sortType = SortTypes.String
|
||||
this.setState({sortKey, sortDirection: nextSort, sortType})
|
||||
private handleSort = (
|
||||
sortKey: ScraperSortKey,
|
||||
sortDirection: Sort,
|
||||
sortType: SortTypes
|
||||
): void => {
|
||||
this.setState({sortKey, sortDirection, sortType})
|
||||
}
|
||||
|
||||
private get hasNoBuckets(): boolean {
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
// Libraries
|
||||
import React, {SFC} from 'react'
|
||||
|
||||
// Components
|
||||
import {FlexBox, FlexDirection, JustifyContent} from '@influxdata/clockface'
|
||||
|
||||
interface Props {
|
||||
children: JSX.Element[] | JSX.Element
|
||||
className?: string
|
||||
}
|
||||
|
||||
const SettingsTabbedPageHeader: SFC<Props> = ({children, className}) => (
|
||||
<FlexBox
|
||||
direction={FlexDirection.Row}
|
||||
justifyContent={JustifyContent.SpaceBetween}
|
||||
style={{marginBottom: '32px'}}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</FlexBox>
|
||||
)
|
||||
|
||||
export default SettingsTabbedPageHeader
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
// Libraries
|
||||
import React from 'react'
|
||||
|
||||
// Components
|
||||
import {Dropdown} from '@influxdata/clockface'
|
||||
|
||||
// Utilities
|
||||
import {generateSortItems} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
|
||||
// Types
|
||||
import {Sort} from '@influxdata/clockface'
|
||||
import {
|
||||
SortKey,
|
||||
SortDropdownItem,
|
||||
} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
import {SortTypes} from 'src/shared/utils/sort'
|
||||
import {ResourceType} from 'src/types'
|
||||
|
||||
interface ComponentProps {
|
||||
resourceType: ResourceType
|
||||
sortDirection: Sort
|
||||
sortKey: SortKey
|
||||
sortType: SortTypes
|
||||
onSelect: (sortKey: SortKey, sortDirection: Sort, sortType: SortTypes) => void
|
||||
width?: number
|
||||
}
|
||||
|
||||
function ResourceSortDropdown({
|
||||
sortDirection,
|
||||
sortKey,
|
||||
sortType,
|
||||
onSelect,
|
||||
resourceType,
|
||||
width = 210,
|
||||
}: ComponentProps) {
|
||||
const sortDropdownItems = generateSortItems(resourceType)
|
||||
|
||||
const {label} = sortDropdownItems.find(
|
||||
item =>
|
||||
item.sortKey === sortKey &&
|
||||
item.sortDirection === sortDirection &&
|
||||
item.sortType === sortType
|
||||
)
|
||||
|
||||
const handleItemClick = (item: SortDropdownItem): void => {
|
||||
const {sortKey, sortDirection, sortType} = item
|
||||
onSelect(sortKey, sortDirection, sortType)
|
||||
}
|
||||
|
||||
const button = (active, onClick) => (
|
||||
<Dropdown.Button
|
||||
onClick={onClick}
|
||||
active={active}
|
||||
testID="resource-sorter--button"
|
||||
>
|
||||
{`Sort by ${label}`}
|
||||
</Dropdown.Button>
|
||||
)
|
||||
|
||||
const menu = onCollapse => (
|
||||
<Dropdown.Menu onCollapse={onCollapse}>
|
||||
{sortDropdownItems.map(item => (
|
||||
<Dropdown.Item
|
||||
key={`${item.sortKey}${item.sortDirection}`}
|
||||
value={item}
|
||||
onClick={handleItemClick}
|
||||
testID={`resource-sorter--${item.sortKey}-${item.sortDirection}`}
|
||||
selected={
|
||||
item.sortKey === sortKey &&
|
||||
item.sortType === sortType &&
|
||||
item.sortDirection === sortDirection
|
||||
}
|
||||
>
|
||||
{item.label}
|
||||
</Dropdown.Item>
|
||||
))}
|
||||
</Dropdown.Menu>
|
||||
)
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
button={button}
|
||||
menu={menu}
|
||||
style={{flexBasis: `${width}px`, width: `${width}px`}}
|
||||
testID="resource-sorter"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default ResourceSortDropdown
|
||||
|
|
@ -0,0 +1,298 @@
|
|||
import {Sort} from '@influxdata/clockface'
|
||||
import {SortTypes} from 'src/shared/utils/sort'
|
||||
import {
|
||||
ResourceType,
|
||||
Dashboard,
|
||||
Task,
|
||||
Variable,
|
||||
Label,
|
||||
Template,
|
||||
Bucket,
|
||||
Telegraf,
|
||||
Scraper,
|
||||
} from 'src/types'
|
||||
|
||||
export type DashboardSortKey = keyof Dashboard | 'meta.updatedAt'
|
||||
export type TaskSortKey = keyof Task
|
||||
export type VariableSortKey = keyof Variable | 'arguments.type'
|
||||
export type LabelSortKey = keyof Label | 'properties.description'
|
||||
export type TemplateSortKey = keyof Template | 'meta.name' | 'meta.description'
|
||||
export type BucketSortKey = keyof Bucket | 'retentionRules[0].everySeconds'
|
||||
export type TelegrafSortKey = keyof Telegraf
|
||||
export type ScraperSortKey = keyof Scraper
|
||||
|
||||
export type SortKey =
|
||||
| DashboardSortKey
|
||||
| TaskSortKey
|
||||
| VariableSortKey
|
||||
| LabelSortKey
|
||||
| TemplateSortKey
|
||||
| BucketSortKey
|
||||
| TelegrafSortKey
|
||||
| ScraperSortKey
|
||||
|
||||
export interface SortDropdownItem {
|
||||
label: string
|
||||
sortKey: SortKey
|
||||
sortType: SortTypes
|
||||
sortDirection: Sort
|
||||
}
|
||||
|
||||
export const generateSortItems = (
|
||||
resourceType: ResourceType
|
||||
): SortDropdownItem[] => {
|
||||
switch (resourceType) {
|
||||
case ResourceType.Dashboards:
|
||||
return [
|
||||
{
|
||||
label: 'Name (A → Z)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Name (Z → A)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
{
|
||||
label: 'Modified (Oldest)',
|
||||
sortKey: 'meta.updatedAt',
|
||||
sortType: SortTypes.Date,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Modified (Newest)',
|
||||
sortKey: 'meta.updatedAt',
|
||||
sortType: SortTypes.Date,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
]
|
||||
case ResourceType.Tasks:
|
||||
return [
|
||||
{
|
||||
label: 'Name (A → Z)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Name (Z → A)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
{
|
||||
label: 'Active',
|
||||
sortKey: 'status',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Inactive',
|
||||
sortKey: 'status',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
{
|
||||
label: 'Completed (Oldest)',
|
||||
sortKey: 'latestCompleted',
|
||||
sortType: SortTypes.Date,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Completed (Newest)',
|
||||
sortKey: 'latestCompleted',
|
||||
sortType: SortTypes.Date,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
{
|
||||
label: 'Schedule (Most Often)',
|
||||
sortKey: 'every',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Schedule (Least Often)',
|
||||
sortKey: 'every',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
]
|
||||
case ResourceType.Variables:
|
||||
return [
|
||||
{
|
||||
label: 'Name (A → Z)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Name (Z → A)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
{
|
||||
label: 'Type (A → Z)',
|
||||
sortKey: 'arguments.type',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Type (Z → A)',
|
||||
sortKey: 'arguments.type',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
]
|
||||
case ResourceType.Labels:
|
||||
return [
|
||||
{
|
||||
label: 'Name (A → Z)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Name (Z → A)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
{
|
||||
label: 'Description (A → Z)',
|
||||
sortKey: 'properties.description',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Description (Z → A)',
|
||||
sortKey: 'properties.description',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
]
|
||||
case ResourceType.Templates:
|
||||
return [
|
||||
{
|
||||
label: 'Name (A → Z)',
|
||||
sortKey: 'meta.name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Name (Z → A)',
|
||||
sortKey: 'meta.name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
{
|
||||
label: 'Description (A → Z)',
|
||||
sortKey: 'meta.description',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Description (Z → A)',
|
||||
sortKey: 'meta.description',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
]
|
||||
case ResourceType.Buckets:
|
||||
return [
|
||||
{
|
||||
label: 'Name (A → Z)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Name (Z → A)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
{
|
||||
label: 'Retention (Ascending)',
|
||||
sortKey: 'retentionRules[0].everySeconds',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Retention (Descending)',
|
||||
sortKey: 'retentionRules[0].everySeconds',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
]
|
||||
case ResourceType.Telegrafs:
|
||||
return [
|
||||
{
|
||||
label: 'Name (A → Z)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Name (Z → A)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
{
|
||||
label: 'Description (Ascending)',
|
||||
sortKey: 'description',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Description (Descending)',
|
||||
sortKey: 'description',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
]
|
||||
case ResourceType.Scrapers:
|
||||
return [
|
||||
{
|
||||
label: 'Name (A → Z)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Name (Z → A)',
|
||||
sortKey: 'name',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
{
|
||||
label: 'URL (Ascending)',
|
||||
sortKey: 'url',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'URL (Descending)',
|
||||
sortKey: 'url',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
{
|
||||
label: 'Bucket (Ascending)',
|
||||
sortKey: 'bucket',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Ascending,
|
||||
},
|
||||
{
|
||||
label: 'Bucket (Descending)',
|
||||
sortKey: 'bucket',
|
||||
sortType: SortTypes.String,
|
||||
sortDirection: Sort.Descending,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.search-widget-input {
|
||||
flex: 1 0 80px;
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
|
|||
|
||||
interface Props {
|
||||
onSearch: (searchTerm: string) => void
|
||||
widthPixels: number
|
||||
placeholderText: string
|
||||
searchTerm: string
|
||||
testID: string
|
||||
|
|
@ -50,18 +49,18 @@ class SearchWidget extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const {placeholderText, widthPixels, testID} = this.props
|
||||
const {placeholderText, testID} = this.props
|
||||
const {searchTerm} = this.state
|
||||
|
||||
return (
|
||||
<Input
|
||||
icon={IconFont.Search}
|
||||
placeholder={placeholderText}
|
||||
style={{width: `${widthPixels}px`}}
|
||||
value={searchTerm}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.handleBlur}
|
||||
testID={testID}
|
||||
className="search-widget-input"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,30 @@
|
|||
import React, {SFC} from 'react'
|
||||
|
||||
interface Props {
|
||||
children: JSX.Element[] | JSX.Element
|
||||
childrenLeft?: JSX.Element[] | JSX.Element
|
||||
childrenRight?: JSX.Element[] | JSX.Element
|
||||
}
|
||||
|
||||
const TabbedPageHeader: SFC<Props> = ({children}) => (
|
||||
<div className="tabbed-page-section--header">{children}</div>
|
||||
)
|
||||
const TabbedPageHeader: SFC<Props> = ({childrenLeft, childrenRight}) => {
|
||||
let leftHeader = <></>
|
||||
let rightHeader = <></>
|
||||
|
||||
if (childrenLeft) {
|
||||
leftHeader = <div className="tabbed-page--header-left">{childrenLeft}</div>
|
||||
}
|
||||
|
||||
if (childrenRight) {
|
||||
rightHeader = (
|
||||
<div className="tabbed-page--header-right">{childrenRight}</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="tabbed-page--header" data-testid="tabbed-page--header">
|
||||
{leftHeader}
|
||||
{rightHeader}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TabbedPageHeader
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@
|
|||
@import 'src/buckets/components/Retention.scss';
|
||||
@import 'src/buckets/components/BucketAddDataButton.scss';
|
||||
@import 'src/buckets/components/NoBucketsWarning.scss';
|
||||
@import 'src/telegrafs/components/Collectors.scss';
|
||||
@import 'src/telegrafs/components/TelegrafConfigOverlay.scss';
|
||||
@import 'src/variables/components/CreateVariableOverlay.scss';
|
||||
@import 'src/variables/components/VariableDropdown.scss';
|
||||
|
|
@ -62,7 +61,6 @@
|
|||
@import 'src/tasks/components/TaskForm.scss';
|
||||
@import 'src/tasks/components/TasksPage.scss';
|
||||
@import 'src/labels/components/LabelOverlayForm.scss';
|
||||
@import 'src/labels/components/RandomLabelColor.scss';
|
||||
@import 'src/dataExplorer/components/SaveAsButton.scss';
|
||||
@import 'src/dataExplorer/components/DataExplorer.scss';
|
||||
@import 'src/dataExplorer/components/SaveAsButton.scss';
|
||||
|
|
@ -76,6 +74,7 @@
|
|||
@import 'src/me/graphics/CollectorGraphic.scss';
|
||||
@import 'src/pageLayout/components/RenamablePageTitle.scss';
|
||||
@import 'src/pageLayout/components/OrgSwitcherOverlay.scss';
|
||||
@import 'src/pageLayout/components/TabbedPage.scss';
|
||||
@import 'src/timeMachine/components/SelectorList.scss';
|
||||
@import 'src/timeMachine/components/Queries.scss';
|
||||
@import 'src/timeMachine/components/EditorShortcutsTooltip.scss';
|
||||
|
|
@ -98,6 +97,7 @@
|
|||
@import 'src/shared/components/AutoDomainInput.scss';
|
||||
@import 'src/shared/components/Checkbox.scss';
|
||||
@import 'src/shared/components/dapperScrollbars/DapperScrollbars.scss';
|
||||
@import 'src/shared/components/search_widget/SearchWidget.scss';
|
||||
@import 'src/templates/components/createFromTemplateOverlay/CreateFromTemplateOverlay.scss';
|
||||
@import 'src/onboarding/components/SigninForm.scss';
|
||||
@import 'src/onboarding/containers/LoginPage.scss';
|
||||
|
|
@ -118,6 +118,7 @@
|
|||
@import 'src/alerting/components/LevelTableField.scss';
|
||||
@import 'src/alerting/components/SentTableField.scss';
|
||||
@import 'src/notifications/rules/components/RuleOverlayFooter.scss';
|
||||
@import 'src/labels/components/LabelCard.scss';
|
||||
@import 'src/alerting/components/SearchBar.scss';
|
||||
@import 'src/clientLibraries/components/ClientLibraryOverlay.scss';
|
||||
@import 'src/dashboards/components/DashboardsCardGrid.scss';
|
||||
|
|
|
|||
|
|
@ -8,12 +8,21 @@ import {
|
|||
ComponentSize,
|
||||
ComponentStatus,
|
||||
Page,
|
||||
Sort,
|
||||
FlexBox,
|
||||
FlexDirection,
|
||||
} from '@influxdata/clockface'
|
||||
import AddResourceDropdown from 'src/shared/components/AddResourceDropdown'
|
||||
import SearchWidget from 'src/shared/components/search_widget/SearchWidget'
|
||||
import ResourceSortDropdown from 'src/shared/components/resource_sort_dropdown/ResourceSortDropdown'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
// Types
|
||||
import {LimitStatus} from 'src/cloud/actions/limits'
|
||||
import {setSearchTerm as setSearchTermAction} from 'src/tasks/actions/creators'
|
||||
import {TaskSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
import {SortTypes} from 'src/shared/utils/sort'
|
||||
import {ResourceType} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
onCreateTask: () => void
|
||||
|
|
@ -22,6 +31,16 @@ interface Props {
|
|||
onImportTask: () => void
|
||||
limitStatus: LimitStatus
|
||||
onImportFromTemplate: () => void
|
||||
searchTerm: string
|
||||
setSearchTerm: typeof setSearchTermAction
|
||||
sortKey: TaskSortKey
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
onSort: (
|
||||
sortKey: TaskSortKey,
|
||||
sortDirection: Sort,
|
||||
sortType: SortTypes
|
||||
) => void
|
||||
}
|
||||
|
||||
export default class TasksHeader extends PureComponent<Props> {
|
||||
|
|
@ -32,6 +51,12 @@ export default class TasksHeader extends PureComponent<Props> {
|
|||
showInactive,
|
||||
onImportTask,
|
||||
onImportFromTemplate,
|
||||
setSearchTerm,
|
||||
searchTerm,
|
||||
sortKey,
|
||||
sortType,
|
||||
sortDirection,
|
||||
onSort,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
|
|
@ -42,14 +67,31 @@ export default class TasksHeader extends PureComponent<Props> {
|
|||
</Page.Header>
|
||||
<Page.ControlBar fullWidth={false}>
|
||||
<Page.ControlBarLeft>
|
||||
<InputLabel>Show Inactive</InputLabel>
|
||||
<SlideToggle
|
||||
active={showInactive}
|
||||
size={ComponentSize.ExtraSmall}
|
||||
onChange={setShowInactive}
|
||||
<SearchWidget
|
||||
placeholderText="Filter tasks..."
|
||||
onSearch={setSearchTerm}
|
||||
searchTerm={searchTerm}
|
||||
/>
|
||||
<ResourceSortDropdown
|
||||
resourceType={ResourceType.Tasks}
|
||||
sortKey={sortKey}
|
||||
sortType={sortType}
|
||||
sortDirection={sortDirection}
|
||||
onSelect={onSort}
|
||||
/>
|
||||
</Page.ControlBarLeft>
|
||||
<Page.ControlBarRight>
|
||||
<FlexBox
|
||||
direction={FlexDirection.Row}
|
||||
margin={ComponentSize.Medium}
|
||||
>
|
||||
<InputLabel>Show Inactive</InputLabel>
|
||||
<SlideToggle
|
||||
active={showInactive}
|
||||
size={ComponentSize.ExtraSmall}
|
||||
onChange={setShowInactive}
|
||||
/>
|
||||
</FlexBox>
|
||||
<AddResourceDropdown
|
||||
canImportFromTemplate
|
||||
onSelectNew={onCreateTask}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ import EmptyTasksList from 'src/tasks/components/EmptyTasksList'
|
|||
import {Task} from 'src/types'
|
||||
import {SortTypes} from 'src/shared/utils/sort'
|
||||
import {Sort} from '@influxdata/clockface'
|
||||
|
||||
import {selectTask, addTaskLabel, runTask} from 'src/tasks/actions/thunks'
|
||||
import {checkTaskLimits as checkTaskLimitsAction} from 'src/cloud/actions/limits'
|
||||
import {TaskSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
|
||||
// Selectors
|
||||
import {getSortedResources} from 'src/shared/utils/sort'
|
||||
|
|
@ -33,16 +33,13 @@ interface Props {
|
|||
onUpdate: (name: string, taskID: string) => void
|
||||
filterComponent?: JSX.Element
|
||||
onImportTask: () => void
|
||||
sortKey: string
|
||||
sortKey: TaskSortKey
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
onClickColumn: (nextSort: Sort, sortKey: SortKey) => void
|
||||
checkTaskLimits: typeof checkTaskLimitsAction
|
||||
onImportFromTemplate: () => void
|
||||
}
|
||||
|
||||
type SortKey = keyof Task
|
||||
|
||||
interface State {
|
||||
taskLabelsEdit: Task
|
||||
isEditingTaskLabels: boolean
|
||||
|
|
@ -70,45 +67,13 @@ export default class TasksList extends PureComponent<Props, State> {
|
|||
searchTerm,
|
||||
onCreate,
|
||||
totalCount,
|
||||
filterComponent,
|
||||
onImportTask,
|
||||
sortKey,
|
||||
sortDirection,
|
||||
onClickColumn,
|
||||
onImportFromTemplate,
|
||||
} = this.props
|
||||
|
||||
const headerKeys: SortKey[] = ['name', 'status', 'every', 'latestCompleted']
|
||||
|
||||
return (
|
||||
<>
|
||||
<ResourceList>
|
||||
<ResourceList.Header filterComponent={filterComponent}>
|
||||
<ResourceList.Sorter
|
||||
name="Name"
|
||||
sortKey={headerKeys[0]}
|
||||
sort={sortKey === headerKeys[0] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
/>
|
||||
<ResourceList.Sorter
|
||||
name="Active"
|
||||
sortKey={headerKeys[1]}
|
||||
sort={sortKey === headerKeys[1] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
/>
|
||||
<ResourceList.Sorter
|
||||
name="Schedule"
|
||||
sortKey={headerKeys[2]}
|
||||
sort={sortKey === headerKeys[2] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
/>
|
||||
<ResourceList.Sorter
|
||||
name="Last Completed"
|
||||
sortKey={headerKeys[3]}
|
||||
sort={sortKey === headerKeys[3] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
/>
|
||||
</ResourceList.Header>
|
||||
<ResourceList.Body
|
||||
emptyState={
|
||||
<EmptyTasksList
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import TasksList from 'src/tasks/components/TasksList'
|
|||
import {Page} from '@influxdata/clockface'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import FilterList from 'src/shared/components/FilterList'
|
||||
import SearchWidget from 'src/shared/components/search_widget/SearchWidget'
|
||||
import GetResources from 'src/resources/components/GetResources'
|
||||
import GetAssetLimits from 'src/cloud/components/GetAssetLimits'
|
||||
import AssetLimitAlert from 'src/cloud/components/AssetLimitAlert'
|
||||
|
|
@ -43,6 +42,7 @@ import {InjectedRouter, WithRouterProps} from 'react-router'
|
|||
import {Sort} from '@influxdata/clockface'
|
||||
import {SortTypes} from 'src/shared/utils/sort'
|
||||
import {extractTaskLimits} from 'src/cloud/utils/limits'
|
||||
import {TaskSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
|
||||
// Selectors
|
||||
import {getAll} from 'src/resources/selectors'
|
||||
|
|
@ -80,13 +80,11 @@ type Props = ConnectedDispatchProps &
|
|||
interface State {
|
||||
isImporting: boolean
|
||||
taskLabelsEdit: Task
|
||||
sortKey: SortKey
|
||||
sortKey: TaskSortKey
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
}
|
||||
|
||||
type SortKey = keyof Task
|
||||
|
||||
const Filter = FilterList<Task>()
|
||||
|
||||
@ErrorHandling
|
||||
|
|
@ -134,6 +132,12 @@ class TasksPage extends PureComponent<Props, State> {
|
|||
onImportTask={this.summonImportOverlay}
|
||||
onImportFromTemplate={this.summonImportFromTemplateOverlay}
|
||||
limitStatus={limitStatus}
|
||||
searchTerm={searchTerm}
|
||||
setSearchTerm={setSearchTerm}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onSort={this.handleSort}
|
||||
/>
|
||||
<Page.Contents fullWidth={false} scrollable={true}>
|
||||
<GetResources resources={[ResourceType.Tasks, ResourceType.Labels]}>
|
||||
|
|
@ -160,7 +164,6 @@ class TasksPage extends PureComponent<Props, State> {
|
|||
onAddTaskLabel={onAddTaskLabel}
|
||||
onRunTask={onRunTask}
|
||||
onFilterChange={setSearchTerm}
|
||||
filterComponent={this.search}
|
||||
onUpdate={updateTaskName}
|
||||
onImportTask={this.summonImportOverlay}
|
||||
onImportFromTemplate={
|
||||
|
|
@ -169,7 +172,6 @@ class TasksPage extends PureComponent<Props, State> {
|
|||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onClickColumn={this.handleClickColumn}
|
||||
checkTaskLimits={checkTaskLimits}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -184,14 +186,12 @@ class TasksPage extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private handleClickColumn = (nextSort: Sort, sortKey: SortKey) => {
|
||||
let sortType = SortTypes.String
|
||||
|
||||
if (sortKey === 'latestCompleted') {
|
||||
sortType = SortTypes.Date
|
||||
}
|
||||
|
||||
this.setState({sortKey, sortDirection: nextSort, sortType})
|
||||
private handleSort = (
|
||||
sortKey: TaskSortKey,
|
||||
sortDirection: Sort,
|
||||
sortType: SortTypes
|
||||
) => {
|
||||
this.setState({sortKey, sortDirection, sortType})
|
||||
}
|
||||
|
||||
private handleActivate = (task: Task) => {
|
||||
|
|
@ -233,18 +233,6 @@ class TasksPage extends PureComponent<Props, State> {
|
|||
router.push(`/orgs/${orgID}/tasks/import`)
|
||||
}
|
||||
|
||||
private get search(): JSX.Element {
|
||||
const {setSearchTerm, searchTerm} = this.props
|
||||
|
||||
return (
|
||||
<SearchWidget
|
||||
placeholderText="Filter tasks..."
|
||||
onSearch={setSearchTerm}
|
||||
searchTerm={searchTerm}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
private get filteredTasks(): Task[] {
|
||||
const {tasks, showInactive} = this.props
|
||||
const matchingTasks = tasks.filter(t => {
|
||||
|
|
|
|||
|
|
@ -17,14 +17,11 @@ import {updateTelegraf, deleteTelegraf} from 'src/telegrafs/actions/thunks'
|
|||
// Selectors
|
||||
import {getAll} from 'src/resources/selectors'
|
||||
|
||||
type SortKey = keyof Telegraf
|
||||
|
||||
interface OwnProps {
|
||||
emptyState: JSX.Element
|
||||
sortKey: string
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
onClickColumn: (nextSort: Sort, sortKey: SortKey) => void
|
||||
onFilterChange: (searchTerm: string) => void
|
||||
}
|
||||
|
||||
|
|
@ -45,19 +42,10 @@ class CollectorList extends PureComponent<Props> {
|
|||
)
|
||||
|
||||
public render() {
|
||||
const {emptyState, sortKey, sortDirection, onClickColumn} = this.props
|
||||
const {emptyState} = this.props
|
||||
|
||||
return (
|
||||
<ResourceList>
|
||||
<ResourceList.Header>
|
||||
<ResourceList.Sorter
|
||||
sortKey="name"
|
||||
sort={sortKey === 'name' ? sortDirection : Sort.None}
|
||||
name="Name"
|
||||
onClick={onClickColumn}
|
||||
testID="name-sorter"
|
||||
/>
|
||||
</ResourceList.Header>
|
||||
<ResourceList.Body emptyState={emptyState}>
|
||||
{this.collectorsList}
|
||||
</ResourceList.Body>
|
||||
|
|
@ -130,7 +118,6 @@ class FilteredCollectorList extends PureComponent<FilteredProps> {
|
|||
sortKey,
|
||||
sortDirection,
|
||||
sortType,
|
||||
onClickColumn,
|
||||
onUpdateTelegraf,
|
||||
onDeleteTelegraf,
|
||||
} = this.props
|
||||
|
|
@ -148,7 +135,6 @@ class FilteredCollectorList extends PureComponent<FilteredProps> {
|
|||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onClickColumn={onClickColumn}
|
||||
onUpdateTelegraf={onUpdateTelegraf}
|
||||
onDeleteTelegraf={onDeleteTelegraf}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
.telegraf-collectors--header {
|
||||
min-width: 530px;
|
||||
@media screen and (max-width: 680px) {
|
||||
// willing to take suggestions about removing this
|
||||
display: block !important;
|
||||
|
||||
> .cf-input {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.telegraf-collectors-button-wrap {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
text-align: right;
|
||||
@media screen and (max-width: 680px) {
|
||||
margin: 8px auto 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,11 +17,12 @@ import {
|
|||
ComponentStatus,
|
||||
} from '@influxdata/clockface'
|
||||
import SearchWidget from 'src/shared/components/search_widget/SearchWidget'
|
||||
import SettingsTabbedPageHeader from 'src/settings/components/SettingsTabbedPageHeader'
|
||||
import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader'
|
||||
import {FilteredList} from 'src/telegrafs/components/CollectorList'
|
||||
import TelegrafExplainer from 'src/telegrafs/components/TelegrafExplainer'
|
||||
import NoBucketsWarning from 'src/buckets/components/NoBucketsWarning'
|
||||
import GetResources from 'src/resources/components/GetResources'
|
||||
import ResourceSortDropdown from 'src/shared/components/resource_sort_dropdown/ResourceSortDropdown'
|
||||
|
||||
// Actions
|
||||
import {updateTelegraf, deleteTelegraf} from 'src/telegrafs/actions/thunks'
|
||||
|
|
@ -30,13 +31,14 @@ import {updateTelegraf, deleteTelegraf} from 'src/telegrafs/actions/thunks'
|
|||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
// Types
|
||||
import {Telegraf, OverlayState, AppState, Bucket, ResourceType} from 'src/types'
|
||||
import {OverlayState, AppState, Bucket, ResourceType} from 'src/types'
|
||||
import {
|
||||
setTelegrafConfigID,
|
||||
setTelegrafConfigName,
|
||||
clearDataLoaders,
|
||||
} from 'src/dataLoaders/actions/dataLoaders'
|
||||
import {SortTypes} from 'src/shared/utils/sort'
|
||||
import {TelegrafSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
|
||||
// Selectors
|
||||
import {getOrg} from 'src/organizations/selectors'
|
||||
|
|
@ -63,13 +65,11 @@ interface State {
|
|||
searchTerm: string
|
||||
instructionsOverlay: OverlayState
|
||||
collectorID?: string
|
||||
sortKey: SortKey
|
||||
sortKey: TelegrafSortKey
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
}
|
||||
|
||||
type SortKey = keyof Telegraf
|
||||
|
||||
@ErrorHandling
|
||||
class Collectors extends PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
|
|
@ -89,32 +89,48 @@ class Collectors extends PureComponent<Props, State> {
|
|||
public render() {
|
||||
const {hasTelegrafs} = this.props
|
||||
const {searchTerm, sortKey, sortDirection, sortType} = this.state
|
||||
|
||||
const collecorsLeftHeaderItems = (
|
||||
<>
|
||||
<SearchWidget
|
||||
placeholderText="Filter telegraf configurations..."
|
||||
searchTerm={searchTerm}
|
||||
onSearch={this.handleFilterChange}
|
||||
/>
|
||||
<ResourceSortDropdown
|
||||
resourceType={ResourceType.Telegrafs}
|
||||
sortDirection={sortDirection}
|
||||
sortKey={sortKey}
|
||||
sortType={sortType}
|
||||
onSelect={this.handleSort}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
const collecorsRightHeaderItems = (
|
||||
<>
|
||||
<Button
|
||||
text="InfluxDB Output Plugin"
|
||||
icon={IconFont.Eye}
|
||||
color={ComponentColor.Secondary}
|
||||
onClick={this.handleJustTheOutput}
|
||||
titleText="Output section of telegraf.conf for V2"
|
||||
testID="button--output-only"
|
||||
/>
|
||||
{this.createButton}
|
||||
</>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<NoBucketsWarning
|
||||
visible={this.hasNoBuckets}
|
||||
resourceName="Telegraf Configurations"
|
||||
/>
|
||||
|
||||
<SettingsTabbedPageHeader className="telegraf-collectors--header">
|
||||
<SearchWidget
|
||||
placeholderText="Filter telegraf configurations..."
|
||||
searchTerm={searchTerm}
|
||||
onSearch={this.handleFilterChange}
|
||||
/>
|
||||
<div className="telegraf-collectors-button-wrap">
|
||||
<Button
|
||||
text="InfluxDB Output Plugin"
|
||||
icon={IconFont.Eye}
|
||||
color={ComponentColor.Secondary}
|
||||
style={{marginRight: '8px'}}
|
||||
onClick={this.handleJustTheOutput}
|
||||
titleText="Output section of telegraf.conf for V2"
|
||||
testID="button--output-only"
|
||||
/>
|
||||
{this.createButton}
|
||||
</div>
|
||||
</SettingsTabbedPageHeader>
|
||||
<TabbedPageHeader
|
||||
childrenLeft={collecorsLeftHeaderItems}
|
||||
childrenRight={collecorsRightHeaderItems}
|
||||
/>
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
<Grid.Column
|
||||
|
|
@ -130,7 +146,6 @@ class Collectors extends PureComponent<Props, State> {
|
|||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onClickColumn={this.handleClickColumn}
|
||||
/>
|
||||
</GetResources>
|
||||
</Grid.Column>
|
||||
|
|
@ -149,9 +164,12 @@ class Collectors extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private handleClickColumn = (nextSort: Sort, sortKey: SortKey) => {
|
||||
const sortType = SortTypes.String
|
||||
this.setState({sortKey, sortDirection: nextSort, sortType})
|
||||
private handleSort = (
|
||||
sortKey: TelegrafSortKey,
|
||||
sortDirection: Sort,
|
||||
sortType: SortTypes
|
||||
): void => {
|
||||
this.setState({sortKey, sortDirection, sortType})
|
||||
}
|
||||
|
||||
private get hasNoBuckets(): boolean {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
Panel,
|
||||
EmptyState,
|
||||
InfluxColors,
|
||||
Gradients,
|
||||
ComponentSize,
|
||||
} from '@influxdata/clockface'
|
||||
import {TextAlignProperty} from 'csstype'
|
||||
|
|
@ -21,10 +22,7 @@ const TelegrafExplainer: FunctionComponent<Props> = ({
|
|||
textAlign = 'inherit',
|
||||
bodySize,
|
||||
}) => (
|
||||
<Panel
|
||||
backgroundColor={InfluxColors.Smoke}
|
||||
style={{textAlign, marginTop: 32}}
|
||||
>
|
||||
<Panel gradient={Gradients.PolarExpress} border={true} style={{textAlign}}>
|
||||
{hasNoTelegrafs && (
|
||||
<EmptyState.Text style={{color: InfluxColors.Platinum, marginTop: 16}}>
|
||||
What is Telegraf?
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ import {Sort} from 'src/clockface'
|
|||
// Selectors
|
||||
import {getSortedResources} from 'src/shared/utils/sort'
|
||||
|
||||
type SortKey = 'meta.name'
|
||||
|
||||
export type TemplateOrSummary = Template | TemplateSummary
|
||||
|
||||
export interface StaticTemplate {
|
||||
|
|
@ -32,7 +30,6 @@ interface Props {
|
|||
sortKey: string
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
onClickColumn: (nextSort: Sort, sortKey: SortKey) => void
|
||||
}
|
||||
|
||||
export default class StaticTemplatesList extends PureComponent<Props> {
|
||||
|
|
@ -41,26 +38,10 @@ export default class StaticTemplatesList extends PureComponent<Props> {
|
|||
)
|
||||
|
||||
public render() {
|
||||
const {
|
||||
searchTerm,
|
||||
onImport,
|
||||
sortKey,
|
||||
sortDirection,
|
||||
onClickColumn,
|
||||
} = this.props
|
||||
|
||||
const headerKeys: SortKey[] = ['meta.name']
|
||||
const {searchTerm, onImport} = this.props
|
||||
|
||||
return (
|
||||
<ResourceList>
|
||||
<ResourceList.Header>
|
||||
<ResourceList.Sorter
|
||||
name="Name"
|
||||
sortKey={headerKeys[0]}
|
||||
sort={sortKey === headerKeys[0] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
/>
|
||||
</ResourceList.Header>
|
||||
<ResourceList.Body
|
||||
emptyState={
|
||||
<EmptyTemplatesList searchTerm={searchTerm} onImport={onImport} />
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ import {Sort} from 'src/clockface'
|
|||
// Selectors
|
||||
import {getSortedResources} from 'src/shared/utils/sort'
|
||||
|
||||
type SortKey = 'meta.name'
|
||||
|
||||
interface Props {
|
||||
templates: TemplateSummary[]
|
||||
searchTerm: string
|
||||
|
|
@ -26,7 +24,6 @@ interface Props {
|
|||
sortKey: string
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
onClickColumn: (nextSort: Sort, sortKey: SortKey) => void
|
||||
}
|
||||
|
||||
export default class TemplatesList extends PureComponent<Props> {
|
||||
|
|
@ -35,27 +32,11 @@ export default class TemplatesList extends PureComponent<Props> {
|
|||
)
|
||||
|
||||
public render() {
|
||||
const {
|
||||
searchTerm,
|
||||
onImport,
|
||||
sortKey,
|
||||
sortDirection,
|
||||
onClickColumn,
|
||||
} = this.props
|
||||
|
||||
const headerKeys: SortKey[] = ['meta.name']
|
||||
const {searchTerm, onImport} = this.props
|
||||
|
||||
return (
|
||||
<>
|
||||
<ResourceList>
|
||||
<ResourceList.Header>
|
||||
<ResourceList.Sorter
|
||||
name="Name"
|
||||
sortKey={headerKeys[0]}
|
||||
sort={sortKey === headerKeys[0] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
/>
|
||||
</ResourceList.Header>
|
||||
<ResourceList.Body
|
||||
emptyState={
|
||||
<EmptyTemplatesList searchTerm={searchTerm} onImport={onImport} />
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ import StaticTemplatesList, {
|
|||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import SearchWidget from 'src/shared/components/search_widget/SearchWidget'
|
||||
import GetResources from 'src/resources/components/GetResources'
|
||||
import SettingsTabbedPageHeader from 'src/settings/components/SettingsTabbedPageHeader'
|
||||
import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader'
|
||||
import ResourceSortDropdown from 'src/shared/components/resource_sort_dropdown/ResourceSortDropdown'
|
||||
|
||||
// Types
|
||||
import {AppState, ResourceType, TemplateSummary} from 'src/types'
|
||||
|
|
@ -24,16 +25,14 @@ import {
|
|||
ComponentColor,
|
||||
IconFont,
|
||||
SelectGroup,
|
||||
FlexBox,
|
||||
FlexDirection,
|
||||
ComponentSize,
|
||||
} from '@influxdata/clockface'
|
||||
|
||||
import {TemplateSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
import {staticTemplates as statics} from 'src/templates/constants/defaultTemplates'
|
||||
|
||||
// Selectors
|
||||
import {getAll} from 'src/resources/selectors/getAll'
|
||||
|
||||
// Constants
|
||||
const staticTemplates: StaticTemplate[] = _.map(statics, (template, name) => ({
|
||||
name,
|
||||
template: template as TemplateOrSummary,
|
||||
|
|
@ -51,14 +50,12 @@ type Props = OwnProps & StateProps
|
|||
|
||||
interface State {
|
||||
searchTerm: string
|
||||
sortKey: SortKey
|
||||
sortKey: TemplateSortKey
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
activeTab: string
|
||||
}
|
||||
|
||||
type SortKey = 'meta.name'
|
||||
|
||||
const FilterStaticTemplates = FilterList<StaticTemplate>()
|
||||
const FilterTemplateSummaries = FilterList<TemplateSummary>()
|
||||
|
||||
|
|
@ -78,43 +75,56 @@ class TemplatesPage extends PureComponent<Props, State> {
|
|||
|
||||
public render() {
|
||||
const {onImport} = this.props
|
||||
const {activeTab} = this.state
|
||||
const {activeTab, sortType, sortKey, sortDirection} = this.state
|
||||
|
||||
const leftHeaderItems = (
|
||||
<>
|
||||
{this.filterComponent}
|
||||
<SelectGroup>
|
||||
<SelectGroup.Option
|
||||
name="template-type"
|
||||
id="static-templates"
|
||||
active={activeTab === 'static-templates'}
|
||||
value="static-templates"
|
||||
onClick={this.handleClickTab}
|
||||
titleText="Static Templates"
|
||||
>
|
||||
Static Templates
|
||||
</SelectGroup.Option>
|
||||
<SelectGroup.Option
|
||||
name="template-type"
|
||||
id="user-templates"
|
||||
active={activeTab === 'user-templates'}
|
||||
value="user-templates"
|
||||
onClick={this.handleClickTab}
|
||||
titleText="User Templates"
|
||||
>
|
||||
User Templates
|
||||
</SelectGroup.Option>
|
||||
</SelectGroup>
|
||||
<ResourceSortDropdown
|
||||
resourceType={ResourceType.Templates}
|
||||
sortType={sortType}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
onSelect={this.handleSort}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsTabbedPageHeader>
|
||||
<FlexBox direction={FlexDirection.Row} margin={ComponentSize.Small}>
|
||||
{this.filterComponent}
|
||||
<SelectGroup>
|
||||
<SelectGroup.Option
|
||||
name="template-type"
|
||||
id="static-templates"
|
||||
active={activeTab === 'static-templates'}
|
||||
value="static-templates"
|
||||
onClick={this.handleClickTab}
|
||||
titleText="Static Templates"
|
||||
>
|
||||
Static Templates
|
||||
</SelectGroup.Option>
|
||||
<SelectGroup.Option
|
||||
name="template-type"
|
||||
id="user-templates"
|
||||
active={activeTab === 'user-templates'}
|
||||
value="user-templates"
|
||||
onClick={this.handleClickTab}
|
||||
titleText="User Templates"
|
||||
>
|
||||
User Templates
|
||||
</SelectGroup.Option>
|
||||
</SelectGroup>
|
||||
</FlexBox>
|
||||
<Button
|
||||
text="Import Template"
|
||||
icon={IconFont.Plus}
|
||||
color={ComponentColor.Primary}
|
||||
onClick={onImport}
|
||||
/>
|
||||
</SettingsTabbedPageHeader>
|
||||
<TabbedPageHeader
|
||||
childrenLeft={leftHeaderItems}
|
||||
childrenRight={
|
||||
<Button
|
||||
text="Import Template"
|
||||
icon={IconFont.Plus}
|
||||
color={ComponentColor.Primary}
|
||||
onClick={onImport}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{this.templatesList}
|
||||
</>
|
||||
)
|
||||
|
|
@ -124,9 +134,12 @@ class TemplatesPage extends PureComponent<Props, State> {
|
|||
this.setState({activeTab: val})
|
||||
}
|
||||
|
||||
private handleClickColumn = (nextSort: Sort, sortKey: SortKey) => {
|
||||
const sortType = SortTypes.String
|
||||
this.setState({sortKey, sortDirection: nextSort, sortType})
|
||||
private handleSort = (
|
||||
sortKey: TemplateSortKey,
|
||||
sortDirection: Sort,
|
||||
sortType: SortTypes
|
||||
): void => {
|
||||
this.setState({sortKey, sortDirection, sortType})
|
||||
}
|
||||
|
||||
private get templatesList(): JSX.Element {
|
||||
|
|
@ -150,7 +163,6 @@ class TemplatesPage extends PureComponent<Props, State> {
|
|||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onClickColumn={this.handleClickColumn}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
|
|
@ -176,7 +188,6 @@ class TemplatesPage extends PureComponent<Props, State> {
|
|||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onClickColumn={this.handleClickColumn}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ import {Sort} from '@influxdata/clockface'
|
|||
// Selectors
|
||||
import {getSortedResources} from 'src/shared/utils/sort'
|
||||
|
||||
type SortKey = keyof Variable | 'arguments.type'
|
||||
|
||||
interface Props {
|
||||
variables: Variable[]
|
||||
emptyState: JSX.Element
|
||||
|
|
@ -24,7 +22,6 @@ interface Props {
|
|||
sortKey: string
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
onClickColumn: (nextSort: Sort, sortKey: SortKey) => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
|
@ -49,25 +46,11 @@ export default class VariableList extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const {emptyState, sortKey, sortDirection, onClickColumn} = this.props
|
||||
const {emptyState} = this.props
|
||||
|
||||
return (
|
||||
<>
|
||||
<ResourceList>
|
||||
<ResourceList.Header>
|
||||
<ResourceList.Sorter
|
||||
name="Name"
|
||||
sortKey={this.headerKeys[0]}
|
||||
sort={sortKey === this.headerKeys[0] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
/>
|
||||
<ResourceList.Sorter
|
||||
name="Type"
|
||||
sortKey={this.headerKeys[1]}
|
||||
sort={sortKey === this.headerKeys[1] ? sortDirection : Sort.None}
|
||||
onClick={onClickColumn}
|
||||
/>
|
||||
</ResourceList.Header>
|
||||
<ResourceList.Body emptyState={emptyState}>
|
||||
{this.rows}
|
||||
</ResourceList.Body>
|
||||
|
|
@ -76,10 +59,6 @@ export default class VariableList extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private get headerKeys(): SortKey[] {
|
||||
return ['name', 'arguments.type']
|
||||
}
|
||||
|
||||
private get rows(): JSX.Element[] {
|
||||
const {
|
||||
variables,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader
|
|||
import VariableList from 'src/variables/components/VariableList'
|
||||
import Filter from 'src/shared/components/FilterList'
|
||||
import AddResourceDropdown from 'src/shared/components/AddResourceDropdown'
|
||||
import ResourceSortDropdown from 'src/shared/components/resource_sort_dropdown/ResourceSortDropdown'
|
||||
import GetResources from 'src/resources/components/GetResources'
|
||||
import {Sort} from '@influxdata/clockface'
|
||||
|
||||
|
|
@ -22,6 +23,7 @@ import {Sort} from '@influxdata/clockface'
|
|||
import {AppState, OverlayState, ResourceType, Variable} from 'src/types'
|
||||
import {ComponentSize} from '@influxdata/clockface'
|
||||
import {SortTypes} from 'src/shared/utils/sort'
|
||||
import {VariableSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems'
|
||||
|
||||
interface StateProps {
|
||||
variables: Variable[]
|
||||
|
|
@ -36,13 +38,11 @@ type Props = StateProps & DispatchProps & WithRouterProps
|
|||
interface State {
|
||||
searchTerm: string
|
||||
importOverlayState: OverlayState
|
||||
sortKey: SortKey
|
||||
sortKey: VariableSortKey
|
||||
sortDirection: Sort
|
||||
sortType: SortTypes
|
||||
}
|
||||
|
||||
type SortKey = keyof Variable
|
||||
|
||||
const FilterList = Filter<Variable>()
|
||||
|
||||
class VariablesTab extends PureComponent<Props, State> {
|
||||
|
|
@ -58,20 +58,37 @@ class VariablesTab extends PureComponent<Props, State> {
|
|||
const {variables} = this.props
|
||||
const {searchTerm, sortKey, sortDirection, sortType} = this.state
|
||||
|
||||
const leftHeaderItems = (
|
||||
<>
|
||||
<SearchWidget
|
||||
placeholderText="Filter variables..."
|
||||
searchTerm={searchTerm}
|
||||
onSearch={this.handleFilterChange}
|
||||
/>
|
||||
<ResourceSortDropdown
|
||||
onSelect={this.handleSort}
|
||||
resourceType={ResourceType.Variables}
|
||||
sortDirection={sortDirection}
|
||||
sortKey={sortKey}
|
||||
sortType={sortType}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
const rightHeaderItems = (
|
||||
<AddResourceDropdown
|
||||
resourceName="Variable"
|
||||
onSelectImport={this.handleOpenImportOverlay}
|
||||
onSelectNew={this.handleOpenCreateOverlay}
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabbedPageHeader>
|
||||
<SearchWidget
|
||||
placeholderText="Filter variables..."
|
||||
searchTerm={searchTerm}
|
||||
onSearch={this.handleFilterChange}
|
||||
/>
|
||||
<AddResourceDropdown
|
||||
resourceName="Variable"
|
||||
onSelectImport={this.handleOpenImportOverlay}
|
||||
onSelectNew={this.handleOpenCreateOverlay}
|
||||
/>
|
||||
</TabbedPageHeader>
|
||||
<TabbedPageHeader
|
||||
childrenLeft={leftHeaderItems}
|
||||
childrenRight={rightHeaderItems}
|
||||
/>
|
||||
<GetResources resources={[ResourceType.Labels]}>
|
||||
<FilterList
|
||||
searchTerm={searchTerm}
|
||||
|
|
@ -87,7 +104,6 @@ class VariablesTab extends PureComponent<Props, State> {
|
|||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
sortType={sortType}
|
||||
onClickColumn={this.handleClickColumn}
|
||||
/>
|
||||
)}
|
||||
</FilterList>
|
||||
|
|
@ -96,9 +112,12 @@ class VariablesTab extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private handleClickColumn = (nextSort: Sort, sortKey: SortKey) => {
|
||||
const sortType = SortTypes.String
|
||||
this.setState({sortKey, sortDirection: nextSort, sortType})
|
||||
private handleSort = (
|
||||
sortKey: VariableSortKey,
|
||||
sortDirection: Sort,
|
||||
sortType: SortTypes
|
||||
): void => {
|
||||
this.setState({sortKey, sortDirection, sortType})
|
||||
}
|
||||
|
||||
private get emptyState(): JSX.Element {
|
||||
|
|
|
|||
Loading…
Reference in New Issue