diff --git a/CHANGELOG.md b/CHANGELOG.md index e678905bb91..748e96d3a29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ 1. [12302](https://github.com/influxdata/influxdb/pull/12302): Make code snippet copy functionality easier to use 1. [12304](https://github.com/influxdata/influxdb/pull/12304): Always show live preview in Note Cell editor +1. [12317](https://github.com/influxdata/influxdb/pull/12317): Redesign Create Scraper workflow +1. [12317](https://github.com/influxdata/influxdb/pull/12317): Show warning in Telegrafs and Scrapers lists when user has no buckets ## v2.0.0-alpha.4 [2019-02-21] diff --git a/ui/cypress/e2e/scrapers.test.ts b/ui/cypress/e2e/scrapers.test.ts index 86bf88940a0..2adb7f7ad3b 100644 --- a/ui/cypress/e2e/scrapers.test.ts +++ b/ui/cypress/e2e/scrapers.test.ts @@ -19,13 +19,13 @@ describe('Scrapers', () => { }) describe('from the org view', () => { - it('can create a scraper', () => { - const newScraper = 'Scraper' + it('can create a scraper from the create button in the page header', () => { + const newScraper = '🐍craper' const newURL = 'http://google.com' cy.getByTestID('table-row').should('have.length', 0) - cy.contains('Create').click() + cy.getByTestID('create-scraper-button-header').click() cy.getByTestID('overlay--container').within(() => { cy.getByInputName('name') .clear() @@ -33,14 +33,30 @@ describe('Scrapers', () => { cy.getByInputName('url') .clear() .type(newURL) - cy.get('.button') - .contains('Finish') - .click() + cy.getByTestID('create-scraper--submit').click() }) - cy.getByTestID('table-row') - .should('have.length', 1) - .and('contain', newScraper) + cy.getByTestID('table-row').should('have.length', 1) + }) + + it('can create a scraper from the create button in the empty state', () => { + const newScraper = '🐍craper' + const newURL = 'http://google.com' + + cy.getByTestID('table-row').should('have.length', 0) + + cy.getByTestID('create-scraper-button-empty').click() + cy.getByTestID('overlay--container').within(() => { + cy.getByInputName('name') + .clear() + .type(newScraper) + cy.getByInputName('url') + .clear() + .type(newURL) + cy.getByTestID('create-scraper--submit').click() + }) + + cy.getByTestID('table-row').should('have.length', 1) }) it('can update scrapers name', () => { @@ -63,7 +79,7 @@ describe('Scrapers', () => { }) }) - it.skip('can delete a scraper', () => { + it('can delete a scraper', () => { const scraperName = 'New Scraper' const url = 'http://google.com' const type = 'Prometheus' diff --git a/ui/src/clockface/components/alerts/Alert.scss b/ui/src/clockface/components/alerts/Alert.scss new file mode 100644 index 00000000000..b1b95dbbeaf --- /dev/null +++ b/ui/src/clockface/components/alerts/Alert.scss @@ -0,0 +1,108 @@ +@import 'src/style/modules'; + +/* + Alert + ------------------------------------------------------------------------------ +*/ + +.alert { + width: 100%; + border-radius: $radius; + padding: $ix-border; + font-size: $form-md-font; + user-select: none; + + p, + h1, + h2, + h3, + h4, + h5, + h6, + ol, + ul, + li { + font-weight: 600; + strong { + font-weight: 900; + } + + &:first-of-type { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + + p { + font-size: $form-md-font; + } +} + +.alert--contents { + width: 100%; + border-radius: $radius - 1px; + padding: $ix-marg-b; + background-color: rgba($g3-castle, 0.85); +} + +.alert--has-icon { + padding-left: $ix-marg-d + $ix-marg-b; + position: relative; +} + +.alert--icon { + position: absolute; + top: 50%; + left: $ix-marg-b + $ix-marg-a; + transform: translateY(-50%); + font-size: 1.5em; + color: rgba($g3-castle, 0.85); +} + +/* Color Modifiers */ +@mixin alertColorModifier($mainColor, $accentColor, $textColor, $boldColor) { + color: $textColor; + + strong { + color: $boldColor; + } + + @include gradient-h($mainColor, $accentColor); +} + +.alert--default { + @include alertColorModifier($g7-graphite, $g5-pepper, $g13-mist, $g20-white); +} + +.alert--primary { + @include alertColorModifier($c-ocean, $c-star, $c-hydrogen, $g20-white); +} + +.alert--secondary { + @include alertColorModifier($c-comet, $c-star, $c-moonstone, $g20-white); +} + +.alert--success { + @include alertColorModifier( + $c-rainforest, + $c-viridian, + $c-wasabi, + $g20-white + ); +} + +.alert--warning { + @include alertColorModifier($c-pineapple, $c-thunder, $c-sulfur, $g20-white); +} + +.alert--danger { + @include alertColorModifier( + $c-dreamsicle, + $c-curacao, + $c-marmelade, + $g20-white + ); +} diff --git a/ui/src/clockface/components/alerts/Alert.tsx b/ui/src/clockface/components/alerts/Alert.tsx new file mode 100644 index 00000000000..3b850673839 --- /dev/null +++ b/ui/src/clockface/components/alerts/Alert.tsx @@ -0,0 +1,31 @@ +// Libraries +import React, {SFC} from 'react' +import classnames from 'classnames' + +// Types +import {ComponentColor, IconFont} from '@influxdata/clockface' + +// Styles +import 'src/clockface/components/alerts/Alert.scss' + +interface Props { + children: JSX.Element | JSX.Element[] + color: ComponentColor + icon?: IconFont +} + +const Alert: SFC = ({children, color, icon}) => { + const className = classnames('alert', { + [`alert--${color}`]: color, + 'alert--has-icon': icon, + }) + + return ( +
+ {icon && } +
{children}
+
+ ) +} + +export default Alert diff --git a/ui/src/clockface/index.ts b/ui/src/clockface/index.ts index c077dfd3cd2..db70aac0244 100644 --- a/ui/src/clockface/index.ts +++ b/ui/src/clockface/index.ts @@ -1,4 +1,5 @@ // Import Components +import Alert from './components/alerts/Alert' import AutoInput from './components/auto_input/AutoInput' import ConfirmationButton from './components/confirmation_button/ConfirmationButton' import Dropdown, {DropdownMode} from './components/dropdowns/Dropdown' @@ -54,6 +55,7 @@ import { // Fire de lazer export { + Alert, Alignment, AutoComplete, AutoInput, diff --git a/ui/src/dataLoaders/components/BucketsDropdown.test.tsx b/ui/src/dataLoaders/components/BucketsDropdown.test.tsx index 80a856e5b57..7a8bf0fdd46 100644 --- a/ui/src/dataLoaders/components/BucketsDropdown.test.tsx +++ b/ui/src/dataLoaders/components/BucketsDropdown.test.tsx @@ -11,7 +11,7 @@ import {bucket} from 'mocks/dummyData' const setup = (override = {}) => { const props = { - selected: '', + selectedBucketID: '', buckets: [], onSelectBucket: jest.fn(), ...override, diff --git a/ui/src/dataLoaders/components/BucketsDropdown.tsx b/ui/src/dataLoaders/components/BucketsDropdown.tsx index a43d1e59951..48ee4b13810 100644 --- a/ui/src/dataLoaders/components/BucketsDropdown.tsx +++ b/ui/src/dataLoaders/components/BucketsDropdown.tsx @@ -8,19 +8,21 @@ import {Dropdown, ComponentStatus} from 'src/clockface' import {Bucket} from '@influxdata/influx' interface Props { - selected: string + selectedBucketID: string buckets: Bucket[] onSelectBucket: (bucket: Bucket) => void } class BucketsDropdown extends PureComponent { public render() { + const {selectedBucketID, onSelectBucket} = this.props + return ( {this.dropdownBuckets} @@ -43,10 +45,6 @@ class BucketsDropdown extends PureComponent { return ComponentStatus.Default } - private get selectedID(): string { - return this.props.selected || '' - } - private get isBucketsEmpty(): boolean { const {buckets} = this.props return !buckets || !buckets.length @@ -59,17 +57,11 @@ class BucketsDropdown extends PureComponent { } return buckets.map(b => ( - + {b.name} )) } - - private handleSelectBucket = (bucketName: string) => { - const bucket = this.props.buckets.find(b => b.name === bucketName) - - this.props.onSelectBucket(bucket) - } } export default BucketsDropdown diff --git a/ui/src/dataLoaders/components/DataLoaderSwitcher.test.tsx b/ui/src/dataLoaders/components/DataLoaderSwitcher.test.tsx index 22df7692550..30c47156efc 100644 --- a/ui/src/dataLoaders/components/DataLoaderSwitcher.test.tsx +++ b/ui/src/dataLoaders/components/DataLoaderSwitcher.test.tsx @@ -4,7 +4,7 @@ import {shallow} from 'enzyme' // Components import DataLoaderSwitcher from 'src/dataLoaders/components/DataLoaderSwitcher' -import DataLoadersWizard from 'src/dataLoaders/components/DataLoadersWizard' +import CreateScraperOverlay from 'src/organizations/components/CreateScraperOverlay' import CollectorsWizard from 'src/dataLoaders/components/collectorsWizard/CollectorsWizard' import LineProtocolWizard from 'src/dataLoaders/components/lineProtocolWizard/LineProtocolWizard' @@ -26,13 +26,26 @@ const setup = (override = {}) => { } describe('DataLoading.Components.DataLoaderSwitcher', () => { - it('renders data loaders wizard', () => { - const {wrapper} = setup() + describe('if type is empty', () => { + it('renders empty div', () => { + const {wrapper} = setup({type: DataLoaderType.Empty}) - const wizard = wrapper.find(DataLoadersWizard) + const emptyDiv = wrapper.find({'data-testid': 'data-loader-empty'}) - expect(wrapper.exists()).toBe(true) - expect(wizard.exists()).toBe(true) + expect(wrapper.exists()).toBe(true) + expect(emptyDiv.exists()).toBe(true) + }) + }) + + describe('if type is scraping', () => { + it('renders create scraper overlay', () => { + const {wrapper} = setup({type: DataLoaderType.Scraping}) + + const overlay = wrapper.find(CreateScraperOverlay) + + expect(wrapper.exists()).toBe(true) + expect(overlay.exists()).toBe(true) + }) }) describe('if type is streaming', () => { diff --git a/ui/src/dataLoaders/components/DataLoaderSwitcher.tsx b/ui/src/dataLoaders/components/DataLoaderSwitcher.tsx index 2b39c32780d..dd483bfc112 100644 --- a/ui/src/dataLoaders/components/DataLoaderSwitcher.tsx +++ b/ui/src/dataLoaders/components/DataLoaderSwitcher.tsx @@ -3,12 +3,12 @@ import React, {PureComponent} from 'react' import _ from 'lodash' // Components -import DataLoadersWizard from 'src/dataLoaders/components/DataLoadersWizard' +import CreateScraperOverlay from 'src/organizations/components/CreateScraperOverlay' import CollectorsWizard from 'src/dataLoaders/components/collectorsWizard/CollectorsWizard' import LineProtocolWizard from 'src/dataLoaders/components/lineProtocolWizard/LineProtocolWizard' // Types -import {Substep, DataLoaderType} from 'src/types/v2/dataLoaders' +import {DataLoaderType} from 'src/types/v2/dataLoaders' import {Bucket} from '@influxdata/influx' interface Props { @@ -16,9 +16,7 @@ interface Props { onCompleteSetup: () => void visible: boolean buckets: Bucket[] - startingType?: DataLoaderType - startingStep?: number - startingSubstep?: Substep + overrideBucketIDSelection?: string } class DataLoaderSwitcher extends PureComponent { @@ -28,22 +26,19 @@ class DataLoaderSwitcher extends PureComponent { type, visible, onCompleteSetup, - startingStep, - startingSubstep, - startingType, + overrideBucketIDSelection, } = this.props switch (type) { - case DataLoaderType.Scraping: case DataLoaderType.Empty: + return
+ case DataLoaderType.Scraping: return ( - ) case DataLoaderType.Streaming: diff --git a/ui/src/dataLoaders/components/DataLoadersWizard.tsx b/ui/src/dataLoaders/components/DataLoadersWizard.tsx deleted file mode 100644 index 44f33f35897..00000000000 --- a/ui/src/dataLoaders/components/DataLoadersWizard.tsx +++ /dev/null @@ -1,224 +0,0 @@ -// Libraries -import React, {PureComponent} from 'react' -import {connect} from 'react-redux' -import _ from 'lodash' - -// Components -import {ErrorHandling} from 'src/shared/decorators/errors' -import WizardOverlay from 'src/clockface/components/wizard/WizardOverlay' -import StepSwitcher from 'src/dataLoaders/components/StepSwitcher' - -// Actions -import {notify as notifyAction} from 'src/shared/actions/notifications' -import { - setBucketInfo, - incrementCurrentStepIndex, - decrementCurrentStepIndex, - setCurrentStepIndex, - setSubstepIndex, - clearSteps, -} from 'src/dataLoaders/actions/steps' - -import { - setDataLoadersType, - clearDataLoaders, -} from 'src/dataLoaders/actions/dataLoaders' - -// Types -import {Links} from 'src/types/v2/links' -import {DataLoaderType, Substep} from 'src/types/v2/dataLoaders' -import {Notification, NotificationFunc} from 'src/types' -import {AppState} from 'src/types/v2' -import {Bucket} from '@influxdata/influx' - -export interface DataLoaderStepProps { - links: Links - currentStepIndex: number - substep: Substep - onSetCurrentStepIndex: (stepNumber: number) => void - onIncrementCurrentStepIndex: () => void - onDecrementCurrentStepIndex: () => void - onSetSubstepIndex: (index: number, subStep: Substep) => void - notify: (message: Notification | NotificationFunc) => void - onCompleteSetup: () => void - onExit: () => void -} - -interface OwnProps { - onCompleteSetup: () => void - visible: boolean - buckets: Bucket[] - startingType?: DataLoaderType - startingStep?: number - startingSubstep?: Substep -} - -interface DispatchProps { - notify: (message: Notification | NotificationFunc) => void - onSetBucketInfo: typeof setBucketInfo - onSetDataLoadersType: typeof setDataLoadersType - onIncrementCurrentStepIndex: typeof incrementCurrentStepIndex - onDecrementCurrentStepIndex: typeof decrementCurrentStepIndex - onSetCurrentStepIndex: typeof setCurrentStepIndex - onSetSubstepIndex: typeof setSubstepIndex - onClearDataLoaders: typeof clearDataLoaders - onClearSteps: typeof clearSteps -} - -interface StateProps { - links: Links - currentStepIndex: number - substep: Substep - username: string - bucket: string - org: string - type: DataLoaderType -} - -type Props = OwnProps & StateProps & DispatchProps - -@ErrorHandling -class DataLoadersWizard extends PureComponent { - public componentDidMount() { - this.handleSetBucketInfo() - this.handleSetStartingValues() - } - - public componentDidUpdate(prevProps: Props) { - const hasBecomeVisible = !prevProps.visible && this.props.visible - - if (hasBecomeVisible) { - this.handleSetBucketInfo() - this.handleSetStartingValues() - } - } - - public render() { - const { - currentStepIndex, - visible, - bucket, - org, - onSetBucketInfo, - buckets, - type, - } = this.props - - return ( - - - - ) - } - - private handleSetBucketInfo = () => { - const {bucket, buckets} = this.props - - if (!bucket && (buckets && buckets.length)) { - const {organization, organizationID, name, id} = buckets[0] - - this.props.onSetBucketInfo(organization, organizationID, name, id) - } - } - - private handleSetStartingValues = () => { - const {startingStep, startingType, startingSubstep} = this.props - - const hasStartingStep = startingStep || startingStep === 0 - const hasStartingSubstep = startingSubstep || startingSubstep === 0 - const hasStartingType = - startingType || startingType === DataLoaderType.Empty - - if (hasStartingType) { - this.props.onSetDataLoadersType(startingType) - } - - if (hasStartingSubstep) { - this.props.onSetSubstepIndex( - hasStartingStep ? startingStep : 0, - startingSubstep - ) - } else if (hasStartingStep) { - this.props.onSetCurrentStepIndex(startingStep) - } - } - - private handleDismiss = () => { - this.props.onCompleteSetup() - this.props.onClearDataLoaders() - this.props.onClearSteps() - } - - private get stepProps(): DataLoaderStepProps { - const { - links, - notify, - substep, - onCompleteSetup, - currentStepIndex, - onSetCurrentStepIndex, - onSetSubstepIndex, - onDecrementCurrentStepIndex, - onIncrementCurrentStepIndex, - } = this.props - - return { - substep, - currentStepIndex, - onSetCurrentStepIndex, - onSetSubstepIndex, - onIncrementCurrentStepIndex, - onDecrementCurrentStepIndex, - links, - notify, - onCompleteSetup, - onExit: this.handleDismiss, - } - } -} - -const mstp = ({ - links, - dataLoading: { - dataLoaders: {type}, - steps: {currentStep, substep, bucket, org}, - }, - me: {name}, -}: AppState): StateProps => ({ - links, - type, - currentStepIndex: currentStep, - substep, - username: name, - bucket, - org, -}) - -const mdtp: DispatchProps = { - notify: notifyAction, - onSetBucketInfo: setBucketInfo, - onSetDataLoadersType: setDataLoadersType, - onIncrementCurrentStepIndex: incrementCurrentStepIndex, - onDecrementCurrentStepIndex: decrementCurrentStepIndex, - onSetCurrentStepIndex: setCurrentStepIndex, - onSetSubstepIndex: setSubstepIndex, - onClearDataLoaders: clearDataLoaders, - onClearSteps: clearSteps, -} - -export default connect( - mstp, - mdtp -)(DataLoadersWizard) diff --git a/ui/src/dataLoaders/components/StepSwitcher.tsx b/ui/src/dataLoaders/components/StepSwitcher.tsx deleted file mode 100644 index 011ca7e9072..00000000000 --- a/ui/src/dataLoaders/components/StepSwitcher.tsx +++ /dev/null @@ -1,56 +0,0 @@ -// Libraries -import React, {PureComponent} from 'react' -import _ from 'lodash' - -// Components -import ConfigureDataSourceStep from 'src/dataLoaders/components/configureStep/ConfigureDataSourceStep' -import {ErrorHandling} from 'src/shared/decorators/errors' - -// Actions -import {setBucketInfo} from 'src/dataLoaders/actions/steps' - -// Types -import {DataLoaderType, DataLoaderStep} from 'src/types/v2/dataLoaders' -import {DataLoaderStepProps} from 'src/dataLoaders/components/DataLoadersWizard' -import {Bucket} from '@influxdata/influx' - -interface Props { - onboardingStepProps: DataLoaderStepProps - bucketName: string - buckets: Bucket[] - type: DataLoaderType - currentStepIndex: number - onSetBucketInfo: typeof setBucketInfo - org: string -} - -@ErrorHandling -class StepSwitcher extends PureComponent { - public render() { - const { - currentStepIndex, - onboardingStepProps, - bucketName, - org, - buckets, - type, - } = this.props - - switch (currentStepIndex) { - case DataLoaderStep.Configure: - return ( - - ) - default: - return
- } - } -} - -export default StepSwitcher diff --git a/ui/src/dataLoaders/components/collectorsWizard/select/SelectCollectorsStep.tsx b/ui/src/dataLoaders/components/collectorsWizard/select/SelectCollectorsStep.tsx index db15c95ce35..f4a9f995933 100644 --- a/ui/src/dataLoaders/components/collectorsWizard/select/SelectCollectorsStep.tsx +++ b/ui/src/dataLoaders/components/collectorsWizard/select/SelectCollectorsStep.tsx @@ -65,7 +65,7 @@ export class SelectCollectorsStep extends PureComponent { telegrafPlugins={this.props.telegrafPlugins} onTogglePluginBundle={this.handleTogglePluginBundle} buckets={this.props.buckets} - bucket={this.props.bucket} + selectedBucketName={this.props.bucket} onSelectBucket={this.handleSelectBucket} /> diff --git a/ui/src/dataLoaders/components/collectorsWizard/select/StreamingSelector.test.tsx b/ui/src/dataLoaders/components/collectorsWizard/select/StreamingSelector.test.tsx index 369b0c087b9..a5daa1f8b5e 100644 --- a/ui/src/dataLoaders/components/collectorsWizard/select/StreamingSelector.test.tsx +++ b/ui/src/dataLoaders/components/collectorsWizard/select/StreamingSelector.test.tsx @@ -10,14 +10,18 @@ import {Input} from 'src/clockface' // Constants import {PLUGIN_BUNDLE_OPTIONS} from 'src/dataLoaders/constants/pluginConfigs' +// Mocks +import {buckets} from 'mocks/dummyData' + const setup = (override = {}) => { + const selectedBucketName = buckets[0].name + const props = { telegrafPlugins: [], pluginBundles: [], onTogglePluginBundle: jest.fn(), - buckets: [], - bucket: '', - selectedBucket: '', + buckets, + selectedBucketName, onSelectBucket: jest.fn(), ...override, } diff --git a/ui/src/dataLoaders/components/collectorsWizard/select/StreamingSelector.tsx b/ui/src/dataLoaders/components/collectorsWizard/select/StreamingSelector.tsx index a68924ec8ce..649d520bd3b 100644 --- a/ui/src/dataLoaders/components/collectorsWizard/select/StreamingSelector.tsx +++ b/ui/src/dataLoaders/components/collectorsWizard/select/StreamingSelector.tsx @@ -30,7 +30,7 @@ import {Bucket} from '@influxdata/influx' export interface Props { buckets: Bucket[] - bucket: string + selectedBucketName: string pluginBundles: BundleName[] telegrafPlugins: TelegrafPlugin[] onTogglePluginBundle: (telegrafPlugin: string, isSelected: boolean) => void @@ -68,7 +68,7 @@ class StreamingSelector extends PureComponent { } public render() { - const {bucket, buckets} = this.props + const {buckets} = this.props const {searchTerm} = this.state return ( @@ -77,7 +77,7 @@ class StreamingSelector extends PureComponent { @@ -117,6 +117,12 @@ class StreamingSelector extends PureComponent { ) } + private get selectedBucketID(): string { + const {buckets, selectedBucketName} = this.props + + return buckets.find(b => b.name === selectedBucketName).id + } + private handleSelectBucket = (bucket: Bucket) => { this.props.onSelectBucket(bucket) } diff --git a/ui/src/dataLoaders/components/configureStep/ConfigureDataSourceStep.test.tsx b/ui/src/dataLoaders/components/configureStep/ConfigureDataSourceStep.test.tsx deleted file mode 100644 index 5aab806db14..00000000000 --- a/ui/src/dataLoaders/components/configureStep/ConfigureDataSourceStep.test.tsx +++ /dev/null @@ -1,51 +0,0 @@ -// Libraries -import React from 'react' -import {shallow} from 'enzyme' - -// Components -import {ConfigureDataSourceStep} from 'src/dataLoaders/components/configureStep/ConfigureDataSourceStep' -import ConfigureDataSourceSwitcher from 'src/dataLoaders/components/configureStep/ConfigureDataSourceSwitcher' - -// Types -import {DataLoaderType} from 'src/types/v2/dataLoaders' - -// Dummy Data -import {defaultOnboardingStepProps} from 'mocks/dummyData' - -const setup = (override = {}) => { - const props = { - ...defaultOnboardingStepProps, - telegrafPlugins: [], - onSetActiveTelegrafPlugin: jest.fn(), - onUpdateTelegrafPluginConfig: jest.fn(), - onSetPluginConfiguration: jest.fn(), - type: DataLoaderType.Empty, - onAddConfigValue: jest.fn(), - onRemoveConfigValue: jest.fn(), - onSaveTelegrafConfig: jest.fn(), - authToken: '', - currentStepIndex: 3, - substep: 0, - location: null, - router: null, - routes: [], - onSetConfigArrayValue: jest.fn(), - bucket: '', - org: '', - username: '', - buckets: [], - ...override, - } - - return shallow() -} - -describe('Onboarding.Components.ConfigureStep.ConfigureDataSourceStep', () => { - it('renders switcher and buttons', async () => { - const wrapper = setup() - const switcher = wrapper.find(ConfigureDataSourceSwitcher) - - expect(wrapper.exists()).toBe(true) - expect(switcher.exists()).toBe(true) - }) -}) diff --git a/ui/src/dataLoaders/components/configureStep/ConfigureDataSourceStep.tsx b/ui/src/dataLoaders/components/configureStep/ConfigureDataSourceStep.tsx deleted file mode 100644 index 97c408b2b34..00000000000 --- a/ui/src/dataLoaders/components/configureStep/ConfigureDataSourceStep.tsx +++ /dev/null @@ -1,55 +0,0 @@ -// Libraries -import React, {PureComponent} from 'react' -import _ from 'lodash' - -// Components -import {ErrorHandling} from 'src/shared/decorators/errors' -import ConfigureDataSourceSwitcher from 'src/dataLoaders/components/configureStep/ConfigureDataSourceSwitcher' - -// Types -import {DataLoaderStepProps} from 'src/dataLoaders/components/DataLoadersWizard' -import {DataLoaderType} from 'src/types/v2/dataLoaders' -import {Bucket} from '@influxdata/influx' - -export interface OwnProps extends DataLoaderStepProps { - type: DataLoaderType - bucket: string - org: string - buckets: Bucket[] -} - -type Props = OwnProps - -@ErrorHandling -export class ConfigureDataSourceStep extends PureComponent { - constructor(props: Props) { - super(props) - } - - public render() { - const {type, bucket, org, buckets} = this.props - - return ( - - ) - } - - private handleNext = async () => { - const {onIncrementCurrentStepIndex, type, onExit} = this.props - - if (type === DataLoaderType.Scraping) { - onExit() - return - } - - onIncrementCurrentStepIndex() - } -} - -export default ConfigureDataSourceStep diff --git a/ui/src/dataLoaders/components/configureStep/ConfigureDataSourceSwitcher.tsx b/ui/src/dataLoaders/components/configureStep/ConfigureDataSourceSwitcher.tsx deleted file mode 100644 index a42efcaf0da..00000000000 --- a/ui/src/dataLoaders/components/configureStep/ConfigureDataSourceSwitcher.tsx +++ /dev/null @@ -1,36 +0,0 @@ -// Libraries -import React, {PureComponent} from 'react' -import _ from 'lodash' - -// Components -import {ErrorHandling} from 'src/shared/decorators/errors' -import EmptyDataSourceState from 'src/dataLoaders/components/configureStep/EmptyDataSourceState' -import Scraping from 'src/dataLoaders/components/configureStep/Scraping' - -// Types -import {DataLoaderType} from 'src/types/v2/dataLoaders' -import {Bucket} from '@influxdata/influx' - -export interface Props { - dataLoaderType: DataLoaderType - buckets: Bucket[] - bucket: string - org: string - onClickNext: () => void -} - -@ErrorHandling -class ConfigureDataSourceSwitcher extends PureComponent { - public render() { - const {dataLoaderType, onClickNext, buckets} = this.props - - switch (dataLoaderType) { - case DataLoaderType.Scraping: - return - default: - return - } - } -} - -export default ConfigureDataSourceSwitcher diff --git a/ui/src/dataLoaders/components/configureStep/ScraperTarget.test.tsx b/ui/src/dataLoaders/components/configureStep/ScraperTarget.test.tsx deleted file mode 100644 index 59bfc0960e8..00000000000 --- a/ui/src/dataLoaders/components/configureStep/ScraperTarget.test.tsx +++ /dev/null @@ -1,54 +0,0 @@ -// Libraries -import React from 'react' -import {shallow} from 'enzyme' - -// Components -import {ScraperTarget} from 'src/dataLoaders/components/configureStep/ScraperTarget' - -const setup = (override = {}) => { - const props = { - bucket: '', - buckets: [], - onSelectBucket: jest.fn(), - onChangeURL: jest.fn(), - onChangeName: jest.fn(), - url: '', - name: '', - ...override, - } - const wrapper = shallow() - - return {wrapper} -} - -describe('ScraperTarget', () => { - describe('rendering', () => { - it('renders correctly with no bucket, url, name', () => { - const {wrapper} = setup() - expect(wrapper.exists()).toBe(true) - - expect(wrapper).toMatchSnapshot() - }) - - it('renders correctly with bucket', () => { - const {wrapper} = setup({bucket: 'defbuck'}) - expect(wrapper.exists()).toBe(true) - - expect(wrapper).toMatchSnapshot() - }) - - it('renders correctly with url', () => { - const {wrapper} = setup({url: 'http://url.com'}) - expect(wrapper.exists()).toBe(true) - - expect(wrapper).toMatchSnapshot() - }) - - it('renders correctly with name', () => { - const {wrapper} = setup({name: 'MyScraper'}) - expect(wrapper.exists()).toBe(true) - - expect(wrapper).toMatchSnapshot() - }) - }) -}) diff --git a/ui/src/dataLoaders/components/configureStep/ScraperTarget.tsx b/ui/src/dataLoaders/components/configureStep/ScraperTarget.tsx deleted file mode 100644 index 271aea8a7a1..00000000000 --- a/ui/src/dataLoaders/components/configureStep/ScraperTarget.tsx +++ /dev/null @@ -1,114 +0,0 @@ -// Libraries -import React, {PureComponent, ChangeEvent} from 'react' - -// Components -import { - Form, - Input, - Columns, - Grid, - ComponentSize, - InputType, - ComponentStatus, -} from 'src/clockface' -import BucketDropdown from 'src/dataLoaders/components/BucketsDropdown' -import {Bucket} from '@influxdata/influx' - -interface Props { - bucket: string - buckets: Bucket[] - onSelectBucket: (bucket: Bucket) => void - onChangeURL: (value: string) => void - onChangeName: (value: string) => void - url: string - name: string -} - -export class ScraperTarget extends PureComponent { - constructor(props: Props) { - super(props) - } - - public render() { - const {onSelectBucket, url, name, bucket, buckets} = this.props - return ( - - - - - - - - - - - - - - - - - - ) - } - - private get urlStatus(): ComponentStatus { - if (this.urlEmpty) { - return ComponentStatus.Error - } - return ComponentStatus.Default - } - - private get urlEmpty(): boolean { - return !this.props.url - } - - private get nameStatus(): ComponentStatus { - if (this.nameEmpty) { - return ComponentStatus.Error - } - return ComponentStatus.Default - } - private get nameEmpty(): boolean { - return !this.props.name - } - - private handleChangeURL = (e: ChangeEvent) => { - const value = e.target.value - this.props.onChangeURL(value) - } - - private handleChangeName = (e: ChangeEvent) => { - const value = e.target.value - this.props.onChangeName(value) - } -} - -export default ScraperTarget diff --git a/ui/src/dataLoaders/components/configureStep/Scraping.tsx b/ui/src/dataLoaders/components/configureStep/Scraping.tsx deleted file mode 100644 index 48a0db972d0..00000000000 --- a/ui/src/dataLoaders/components/configureStep/Scraping.tsx +++ /dev/null @@ -1,169 +0,0 @@ -// Libraries -import React, {PureComponent} from 'react' -import {connect} from 'react-redux' -import _ from 'lodash' - -// Components -import {Form, ComponentStatus} from 'src/clockface' -import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar' -import OnboardingButtons from 'src/onboarding/components/OnboardingButtons' -import ScraperTarget from 'src/dataLoaders/components/configureStep/ScraperTarget' - -// Actions -import { - setScraperTargetBucket, - setScraperTargetURL, - setScraperTargetName, - saveScraperTarget, -} from 'src/dataLoaders/actions/dataLoaders' -import {setBucketInfo} from 'src/dataLoaders/actions/steps' -import {notify as notifyAction, notify} from 'src/shared/actions/notifications' - -// Types -import {Bucket} from '@influxdata/influx' -import {AppState} from 'src/types/v2/index' -import { - scraperCreateSuccess, - scraperCreateFailed, -} from 'src/shared/copy/v2/notifications' - -interface OwnProps { - onClickNext: () => void - buckets: Bucket[] -} - -interface DispatchProps { - onSetScraperTargetBucket: typeof setScraperTargetBucket - onSetScraperTargetURL: typeof setScraperTargetURL - onSetScraperTargetName: typeof setScraperTargetName - onSaveScraperTarget: typeof saveScraperTarget - onSetBucketInfo: typeof setBucketInfo - notify: typeof notifyAction -} - -interface StateProps { - scraperBucket: string - url: string - currentBucket: string - name: string -} - -type Props = OwnProps & DispatchProps & StateProps - -export class Scraping extends PureComponent { - public componentDidMount() { - const { - buckets, - scraperBucket, - currentBucket, - onSetScraperTargetBucket, - } = this.props - - if (!scraperBucket) { - onSetScraperTargetBucket(currentBucket || _.get(buckets, '0.name', '')) - } - } - - public render() { - const { - scraperBucket, - onSetScraperTargetURL, - onSetScraperTargetName, - url, - buckets, - name, - } = this.props - - return ( -
- -
-

Add Scraper Target

-
- Scrapers collect data from multiple targets at regular intervals - and to write to a bucket -
-
- -
- - - ) - } - - private handleSelectBucket = (bucket: Bucket) => { - const {onSetBucketInfo, onSetScraperTargetBucket} = this.props - const {organization, organizationID, id, name} = bucket - - onSetBucketInfo(organization, organizationID, name, id) - onSetScraperTargetBucket(bucket.name) - } - - private get nextButtonStatus(): ComponentStatus { - if ( - !this.props.url || - !this.props.name || - !this.props.buckets || - !this.props.buckets.length - ) { - return ComponentStatus.Disabled - } - return ComponentStatus.Default - } - - private handleSubmit = async () => { - try { - const {onSaveScraperTarget, onClickNext, notify} = this.props - await onSaveScraperTarget() - onClickNext() - notify(scraperCreateSuccess()) - } catch (e) { - console.error(e) - notify(scraperCreateFailed()) - } - } -} - -const mstp = ({ - dataLoading: { - dataLoaders: {scraperTarget}, - steps: {bucket}, - }, -}: AppState): StateProps => { - return { - currentBucket: bucket, - scraperBucket: scraperTarget.bucket, - url: scraperTarget.url, - name: scraperTarget.name, - } -} - -const mdtp: DispatchProps = { - onSetScraperTargetBucket: setScraperTargetBucket, - onSetScraperTargetURL: setScraperTargetURL, - onSaveScraperTarget: saveScraperTarget, - onSetBucketInfo: setBucketInfo, - onSetScraperTargetName: setScraperTargetName, - notify: notifyAction, -} - -export default connect( - mstp, - mdtp -)(Scraping) diff --git a/ui/src/dataLoaders/components/configureStep/__snapshots__/ScraperTarget.test.tsx.snap b/ui/src/dataLoaders/components/configureStep/__snapshots__/ScraperTarget.test.tsx.snap deleted file mode 100644 index 51528bb0fe6..00000000000 --- a/ui/src/dataLoaders/components/configureStep/__snapshots__/ScraperTarget.test.tsx.snap +++ /dev/null @@ -1,265 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ScraperTarget rendering renders correctly with bucket 1`] = ` - - - - - - - - - - - - - - - - - -`; - -exports[`ScraperTarget rendering renders correctly with name 1`] = ` - - - - - - - - - - - - - - - - - -`; - -exports[`ScraperTarget rendering renders correctly with no bucket, url, name 1`] = ` - - - - - - - - - - - - - - - - - -`; - -exports[`ScraperTarget rendering renders correctly with url 1`] = ` - - - - - - - - - - - - - - - - - -`; diff --git a/ui/src/organizations/actions/orgView.ts b/ui/src/organizations/actions/orgView.ts index c25fb1d510d..2df03be4aac 100644 --- a/ui/src/organizations/actions/orgView.ts +++ b/ui/src/organizations/actions/orgView.ts @@ -1,4 +1,5 @@ import {Task, Organization} from 'src/types/v2' +import {ScraperTargetRequest} from '@influxdata/influx' import {client} from 'src/utils/api' @@ -26,3 +27,7 @@ export const getTasks = (org: Organization) => async dispatch => { dispatch(populateTasks(tasksWithOrg)) } + +export const createScraper = (scraper: ScraperTargetRequest) => async () => { + await client.scrapers.create(scraper) +} diff --git a/ui/src/organizations/components/BucketList.tsx b/ui/src/organizations/components/BucketList.tsx index be58277a6d3..61f6ceea588 100644 --- a/ui/src/organizations/components/BucketList.tsx +++ b/ui/src/organizations/components/BucketList.tsx @@ -15,7 +15,7 @@ import {setBucketInfo} from 'src/dataLoaders/actions/steps' // Types import {OverlayState} from 'src/types' -import {Substep, DataLoaderStep, DataLoaderType} from 'src/types/v2/dataLoaders' +import {DataLoaderType} from 'src/types/v2/dataLoaders' import {setDataLoadersType} from 'src/dataLoaders/actions/dataLoaders' import {AppState} from 'src/types/v2' @@ -73,6 +73,7 @@ class BucketList extends PureComponent { onDeleteBucket, onFilterChange, } = this.props + const {bucketID} = this.state return ( <> @@ -108,7 +109,7 @@ class BucketList extends PureComponent { visible={this.isDataLoadersWizardVisible} onCompleteSetup={this.handleDismissDataLoaders} buckets={buckets} - {...this.startingValues} + overrideBucketIDSelection={bucketID} /> ) @@ -118,27 +119,6 @@ class BucketList extends PureComponent { return this.props.buckets.find(b => b.id === this.state.bucketID) } - private get startingValues(): { - startingType: DataLoaderType - startingStep: number - startingSubstep?: Substep - } { - const {dataLoaderType} = this.props - - switch (dataLoaderType) { - case DataLoaderType.Scraping: - return { - startingType: DataLoaderType.Scraping, - startingStep: DataLoaderStep.Configure, - } - case DataLoaderType.LineProtocol: - return { - startingType: DataLoaderType.LineProtocol, - startingStep: DataLoaderStep.Configure, - } - } - } - private handleCloseModal = () => { this.setState({bucketOverlayState: OverlayState.Closed}) } diff --git a/ui/src/organizations/components/Buckets.tsx b/ui/src/organizations/components/Buckets.tsx index 3f71a1702df..a765e575f25 100644 --- a/ui/src/organizations/components/Buckets.tsx +++ b/ui/src/organizations/components/Buckets.tsx @@ -196,7 +196,7 @@ export default class Buckets extends PureComponent { if (_.isEmpty(searchTerm)) { return ( - + { } return ( - + ) diff --git a/ui/src/organizations/components/Collectors.tsx b/ui/src/organizations/components/Collectors.tsx index ebffe4853f1..c3391a87501 100644 --- a/ui/src/organizations/components/Collectors.tsx +++ b/ui/src/organizations/components/Collectors.tsx @@ -14,11 +14,12 @@ import { IconFont, ComponentSize, Columns, + ComponentStatus, } from '@influxdata/clockface' import {EmptyState, Grid, Input, InputType, Tabs} from 'src/clockface' import CollectorsWizard from 'src/dataLoaders/components/collectorsWizard/CollectorsWizard' import FilterList from 'src/shared/components/Filter' - +import NoBucketsWarning from 'src/organizations/components/NoBucketsWarning' // APIS import {client} from 'src/utils/api' @@ -87,7 +88,7 @@ export class Collectors extends PureComponent { } public render() { - const {collectors, buckets} = this.props + const {collectors} = this.props const {searchTerm} = this.state return ( @@ -107,6 +108,10 @@ export class Collectors extends PureComponent { + searchTerm={searchTerm} searchKeys={['plugins.0.config.bucket', 'labels[].name']} @@ -135,12 +140,7 @@ export class Collectors extends PureComponent { - + {this.collectorsWizard} { ) } + private get hasNoBuckets(): boolean { + const {buckets} = this.props + + if (!buckets || !buckets.length) { + return true + } + + return false + } + + private get collectorsWizard(): JSX.Element { + const {buckets} = this.props + + if (this.hasNoBuckets) { + return + } + + return ( + + ) + } + private get selectedCollector() { return this.props.collectors.find(c => c.id === this.state.collectorID) } @@ -203,12 +230,23 @@ export class Collectors extends PureComponent { } private get createButton(): JSX.Element { + let status = ComponentStatus.Default + let titleText = 'Create a new Telegraf Configuration' + + if (this.hasNoBuckets) { + status = ComponentStatus.Disabled + titleText = + 'You need at least 1 bucket in order to create a Telegraf Configuration' + } + return (
-
-
-
+ data-testid="data-loader-empty" + />
, "container":
@@ -678,16 +670,8 @@ Object { />
-
-
-
+ data-testid="data-loader-empty" + />
, "debug": [Function], "getAllByAltText": [Function], diff --git a/ui/src/organizations/components/__snapshots__/Scrapers.test.tsx.snap b/ui/src/organizations/components/__snapshots__/Scrapers.test.tsx.snap index ef44c05fef0..1d6d654aa46 100644 --- a/ui/src/organizations/components/__snapshots__/Scrapers.test.tsx.snap +++ b/ui/src/organizations/components/__snapshots__/Scrapers.test.tsx.snap @@ -7,17 +7,22 @@ exports[`ScraperList rendering renders 1`] = ` +