From 828302c430eb5ec4e5c91ed63e75a91c5a02c98f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 14 May 2020 08:25:05 -0700 Subject: [PATCH] feat: tokens empty state (#18060) * feat(tokens): add TokensEmptyState component * feat: token empty state --- ui/cypress/e2e/tokens.test.ts | 41 ++++++++++++++++++- .../components/GenerateTokenDropdown.tsx | 35 +++++++++++----- .../authorizations/components/TokenList.tsx | 28 ++++--------- .../components/TokensEmptyState.tsx | 29 +++++++++++++ .../authorizations/components/TokensTab.tsx | 25 +---------- 5 files changed, 103 insertions(+), 55 deletions(-) create mode 100644 ui/src/authorizations/components/TokensEmptyState.tsx diff --git a/ui/cypress/e2e/tokens.test.ts b/ui/cypress/e2e/tokens.test.ts index 993644957c..6cc5334d18 100644 --- a/ui/cypress/e2e/tokens.test.ts +++ b/ui/cypress/e2e/tokens.test.ts @@ -219,13 +219,52 @@ describe('tokens', () => { cy.getByTestID('table-row') .contains('token test 03') .should('not.exist') + + // Delete remaining tokens + cy.getByTestID('table-row') + .first() + .within(() => { + cy.getByTestID('delete-token--button').click() + }) + .then(() => { + cy.getByTestID('delete-token--popover--contents').within(() => { + cy.getByTestID('delete-token--confirm-button').click() + }) + }) + + cy.getByTestID('table-row') + .first() + .within(() => { + cy.getByTestID('delete-token--button').click() + }) + .then(() => { + cy.getByTestID('delete-token--popover--contents').within(() => { + cy.getByTestID('delete-token--confirm-button').click() + }) + }) + + cy.getByTestID('table-row') + .first() + .within(() => { + cy.getByTestID('delete-token--button').click() + }) + .then(() => { + cy.getByTestID('delete-token--popover--contents').within(() => { + cy.getByTestID('delete-token--confirm-button').click() + }) + }) + + // Assert empty state + cy.getByTestID('empty-state').within(() => { + cy.getByTestID('dropdown--gen-token').should('exist') + }) }) it('can generate a read/write token', () => { cy.getByTestID('table-row').should('have.length', 4) // create some extra buckets for filters - cy.get('@org').then(({id, name}) => { + cy.get('@org').then(({id, name}: Organization) => { cy.createBucket(id, name, 'Magna Carta').then(() => { cy.createBucket(id, name, 'Sicilsky Bull').then(() => { cy.createBucket(id, name, 'A la Carta') diff --git a/ui/src/authorizations/components/GenerateTokenDropdown.tsx b/ui/src/authorizations/components/GenerateTokenDropdown.tsx index 076fb99cdf..6372f64aa9 100644 --- a/ui/src/authorizations/components/GenerateTokenDropdown.tsx +++ b/ui/src/authorizations/components/GenerateTokenDropdown.tsx @@ -1,6 +1,6 @@ // Libraries import React, {PureComponent} from 'react' -import _ from 'lodash' +import {withRouter, WithRouterProps} from 'react-router' // Components import {Dropdown} from '@influxdata/clockface' @@ -8,14 +8,9 @@ import {Dropdown} from '@influxdata/clockface' // Types import {IconFont, ComponentColor} from '@influxdata/clockface' -interface OwnProps { - onSelectAllAccess: () => void - onSelectReadWrite: () => void -} +type Props = WithRouterProps -type Props = OwnProps - -export default class GenerateTokenDropdown extends PureComponent { +class GenerateTokenDropdown extends PureComponent { public render() { return ( { private handleSelect = (selection: string): void => { if (selection === this.allAccessOption) { - this.props.onSelectAllAccess() + this.handleAllAccess() } else if (selection === this.bucketReadWriteOption) { - this.props.onSelectReadWrite() + this.handleReadWrite() } } + + private handleAllAccess = () => { + const { + router, + params: {orgID}, + } = this.props + + router.push(`/orgs/${orgID}/load-data/tokens/generate/all-access`) + } + + private handleReadWrite = () => { + const { + router, + params: {orgID}, + } = this.props + + router.push(`/orgs/${orgID}/load-data/tokens/generate/buckets`) + } } + +export default withRouter<{}>(GenerateTokenDropdown) diff --git a/ui/src/authorizations/components/TokenList.tsx b/ui/src/authorizations/components/TokenList.tsx index 817e7cf772..e053227de1 100644 --- a/ui/src/authorizations/components/TokenList.tsx +++ b/ui/src/authorizations/components/TokenList.tsx @@ -4,17 +4,18 @@ import _ from 'lodash' import memoizeOne from 'memoize-one' // Components -import {EmptyState, Overlay, IndexList} from '@influxdata/clockface' +import {Overlay, IndexList} from '@influxdata/clockface' import TokenRow from 'src/authorizations/components/TokenRow' import ViewTokenOverlay from 'src/authorizations/components/ViewTokenOverlay' // Types import {Authorization} from 'src/types' import {SortTypes} from 'src/shared/utils/sort' -import {ComponentSize, Sort} from '@influxdata/clockface' +import {Sort} from '@influxdata/clockface' // Utils import {getSortedResources} from 'src/shared/utils/sort' +import TokensEmptyState from './TokensEmptyState' type SortKey = keyof Authorization @@ -46,7 +47,7 @@ export default class TokenList extends PureComponent { } public render() { - const {sortKey, sortDirection, onClickColumn} = this.props + const {sortKey, sortDirection, onClickColumn, searchTerm} = this.props const {isTokenOverlayVisible, authInView} = this.state return ( @@ -68,7 +69,10 @@ export default class TokenList extends PureComponent { width="50%" /> - + } + columnCount={2} + > {this.rows} @@ -112,20 +116,4 @@ export default class TokenList extends PureComponent { const authInView = this.props.auths.find(a => a.id === authID) this.setState({isTokenOverlayVisible: true, authInView}) } - - private get emptyState(): JSX.Element { - const {searchTerm} = this.props - let emptyStateText = - 'There are not any Tokens associated with this account. Contact your administrator' - - if (searchTerm) { - emptyStateText = 'No Tokens match your search term' - } - - return ( - - {emptyStateText} - - ) - } } diff --git a/ui/src/authorizations/components/TokensEmptyState.tsx b/ui/src/authorizations/components/TokensEmptyState.tsx new file mode 100644 index 0000000000..2b269c9088 --- /dev/null +++ b/ui/src/authorizations/components/TokensEmptyState.tsx @@ -0,0 +1,29 @@ +// Libraries +import React, {FC} from 'react' +import {EmptyState, ComponentSize} from '@influxdata/clockface' +import GenerateTokenDropdown from './GenerateTokenDropdown' + +interface Props { + searchTerm: string +} + +const TokensEmptyState: FC = ({searchTerm}) => { + if (searchTerm) { + return ( + + No Tokens match your search + + ) + } + + return ( + + + Looks like you don't have any Tokens, why not add one? + + + + ) +} + +export default TokensEmptyState diff --git a/ui/src/authorizations/components/TokensTab.tsx b/ui/src/authorizations/components/TokensTab.tsx index 2a346d2bbe..bd33f9bc6d 100644 --- a/ui/src/authorizations/components/TokensTab.tsx +++ b/ui/src/authorizations/components/TokensTab.tsx @@ -64,12 +64,7 @@ class TokensTab extends PureComponent { /> ) - const rightHeaderItems = ( - - ) + const rightHeaderItems = return ( <> @@ -109,24 +104,6 @@ class TokensTab extends PureComponent { private get searchKeys(): AuthSearchKeys[] { return [AuthSearchKeys.Status, AuthSearchKeys.Description] } - - private handleGenerateAllAccess = () => { - const { - router, - params: {orgID}, - } = this.props - - router.push(`/orgs/${orgID}/load-data/tokens/generate/all-access`) - } - - private handleGenerateReadWrite = () => { - const { - router, - params: {orgID}, - } = this.props - - router.push(`/orgs/${orgID}/load-data/tokens/generate/buckets`) - } } const mstp = (state: AppState) => ({