diff --git a/public/app/core/components/Animations/SlideDown.tsx b/public/app/core/components/Animations/SlideDown.tsx index 4d515f98f16..70dacd73849 100644 --- a/public/app/core/components/Animations/SlideDown.tsx +++ b/public/app/core/components/Animations/SlideDown.tsx @@ -1,15 +1,22 @@ -import React from 'react'; +import React from 'react'; import Transition from 'react-transition-group/Transition'; -const defaultMaxHeight = '200px'; // When animating using max-height we need to use a static value. +interface Style { + transition?: string; + overflow?: string; +} + +// When animating using max-height we need to use a static value. // If this is not enough, pass in
{title}
- + {buttonTitle} diff --git a/public/app/core/components/EmptyListCTA/__snapshots__/EmptyListCTA.test.tsx.snap b/public/app/core/components/EmptyListCTA/__snapshots__/EmptyListCTA.test.tsx.snap index 6d47c984d5e..b85660bcc6f 100644 --- a/public/app/core/components/EmptyListCTA/__snapshots__/EmptyListCTA.test.tsx.snap +++ b/public/app/core/components/EmptyListCTA/__snapshots__/EmptyListCTA.test.tsx.snap @@ -12,6 +12,7 @@ exports[`EmptyListCTA renders correctly 1`] = ` { deleteApiKey: jest.fn(), setSearchQuery: jest.fn(), addApiKey: jest.fn(), + apiKeysCount: 0, }; Object.assign(props, propOverrides); @@ -28,14 +29,19 @@ const setup = (propOverrides?: object) => { }; describe('Render', () => { - it('should render component', () => { - const { wrapper } = setup(); + it('should render API keys table if there are any keys', () => { + const { wrapper } = setup({ + apiKeys: getMultipleMockKeys(5), + apiKeysCount: 5, + }); + expect(wrapper).toMatchSnapshot(); }); - it('should render API keys table', () => { + it('should render CTA if there are no API keys', () => { const { wrapper } = setup({ - apiKeys: getMultipleMockKeys(5), + apiKeys: getMultipleMockKeys(0), + apiKeysCount: 0, hasFetched: true, }); diff --git a/public/app/features/api-keys/ApiKeysPage.tsx b/public/app/features/api-keys/ApiKeysPage.tsx index 6052b0f4fc8..d2aa1f24c57 100644 --- a/public/app/features/api-keys/ApiKeysPage.tsx +++ b/public/app/features/api-keys/ApiKeysPage.tsx @@ -1,17 +1,19 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent } from 'react'; import ReactDOMServer from 'react-dom/server'; import { connect } from 'react-redux'; import { hot } from 'react-hot-loader'; import { NavModel, ApiKey, NewApiKey, OrgRole } from 'app/types'; import { getNavModel } from 'app/core/selectors/navModel'; -import { getApiKeys } from './state/selectors'; +import { getApiKeys, getApiKeysCount } from './state/selectors'; import { loadApiKeys, deleteApiKey, setSearchQuery, addApiKey } from './state/actions'; import PageHeader from 'app/core/components/PageHeader/PageHeader'; -import SlideDown from 'app/core/components/Animations/SlideDown'; import PageLoader from 'app/core/components/PageLoader/PageLoader'; +import SlideDown from 'app/core/components/Animations/SlideDown'; import ApiKeysAddedModal from './ApiKeysAddedModal'; import config from 'app/core/config'; import appEvents from 'app/core/app_events'; +import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import DeleteButton from 'app/core/components/DeleteButton/DeleteButton'; export interface Props { navModel: NavModel; @@ -22,6 +24,7 @@ export interface Props { deleteApiKey: typeof deleteApiKey; setSearchQuery: typeof setSearchQuery; addApiKey: typeof addApiKey; + apiKeysCount: number; } export interface State { @@ -82,6 +85,7 @@ export class ApiKeysPage extends PureComponent { return { ...prevState, newApiKey: initialApiKeyState, + isAdding: false, }; }); }; @@ -101,115 +105,152 @@ export class ApiKeysPage extends PureComponent { }); }; - renderTable() { - const { apiKeys } = this.props; - - return [ -

- Existing Keys -

, - - - - - - - - {apiKeys.length > 0 && ( - - {apiKeys.map(key => { - return ( - - - - - - ); - })} - + renderEmptyList() { + const { isAdding } = this.state; + return ( +
+ {!isAdding && ( + )} -
NameRole -
{key.name}{key.role} - this.onDeleteApiKey(key)} className="btn btn-danger btn-mini"> - - -
, - ]; + {this.renderAddApiKeyForm()} + + ); + } + + renderAddApiKeyForm() { + const { newApiKey, isAdding } = this.state; + + return ( + +
+ +
Add API Key
+
+
+
+ Key name + this.onApiKeyStateUpdate(evt, ApiKeyStateProps.Name)} + /> +
+
+ Role + + + +
+
+ +
+
+
+
+
+ ); + } + + renderApiKeyList() { + const { isAdding } = this.state; + const { apiKeys, searchQuery } = this.props; + + return ( +
+
+
+ +
+ +
+ +
+ + {this.renderAddApiKeyForm()} + +

Existing Keys

+ + + + + + + + {apiKeys.length > 0 ? ( + + {apiKeys.map(key => { + return ( + + + + + + ); + })} + + ) : null} +
NameRole +
{key.name}{key.role} + this.onDeleteApiKey(key)} /> +
+
+ ); } render() { - const { newApiKey, isAdding } = this.state; - const { hasFetched, navModel, searchQuery } = this.props; + const { hasFetched, navModel, apiKeysCount } = this.props; return (
-
-
-
- -
- -
- -
- - -
- -
Add API Key
-
-
-
- Key name - this.onApiKeyStateUpdate(evt, ApiKeyStateProps.Name)} - /> -
-
- Role - - - -
-
- -
-
-
-
-
- {hasFetched ? this.renderTable() : } -
+ {hasFetched ? ( + apiKeysCount > 0 ? ( + this.renderApiKeyList() + ) : ( + this.renderEmptyList() + ) + ) : ( + + )}
); } @@ -220,6 +261,7 @@ function mapStateToProps(state) { navModel: getNavModel(state.navIndex, 'apikeys'), apiKeys: getApiKeys(state.apiKeys), searchQuery: state.apiKeys.searchQuery, + apiKeysCount: getApiKeysCount(state.apiKeys), hasFetched: state.apiKeys.hasFetched, }; } diff --git a/public/app/features/api-keys/__snapshots__/ApiKeysPage.test.tsx.snap b/public/app/features/api-keys/__snapshots__/ApiKeysPage.test.tsx.snap index b1cac8469be..63b92a16ee0 100644 --- a/public/app/features/api-keys/__snapshots__/ApiKeysPage.test.tsx.snap +++ b/public/app/features/api-keys/__snapshots__/ApiKeysPage.test.tsx.snap @@ -1,276 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Render should render API keys table 1`] = ` +exports[`Render should render API keys table if there are any keys 1`] = `
-
-
-
- -
-
- -
- -
- -
- Add API Key -
-
-
-
- - Key name - - -
-
- - Role - - - - -
-
- -
-
-
-
-
-

- Existing Keys -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Name - - Role - -
- test-1 - - Viewer - - - - -
- test-2 - - Viewer - - - - -
- test-3 - - Viewer - - - - -
- test-4 - - Viewer - - - - -
- test-5 - - Viewer - - - - -
-
+
`; -exports[`Render should render component 1`] = ` +exports[`Render should render CTA if there are no API keys 1`] = `
-
-
- -
-
- -
+
-
`; diff --git a/public/app/features/api-keys/state/selectors.ts b/public/app/features/api-keys/state/selectors.ts index 8065c252e85..789237ae9a3 100644 --- a/public/app/features/api-keys/state/selectors.ts +++ b/public/app/features/api-keys/state/selectors.ts @@ -1,5 +1,7 @@ import { ApiKeysState } from 'app/types'; +export const getApiKeysCount = (state: ApiKeysState) => state.keys.length; + export const getApiKeys = (state: ApiKeysState) => { const regex = RegExp(state.searchQuery, 'i');