From d56966faceaaa360523afafb5fd0b35cda84f92b Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Mon, 27 Apr 2020 14:19:12 -0700 Subject: [PATCH] feat(ui): ability to create a bucket from time machine (#17860) * feat: WIP allow on the fly bucket creation * refactor: fully implement create bucket in both places * refactor: use separate popover based component for selector list bucket creation * chore: prettier * chore: cleanup buckets tab Doesn't need org, overlay state, or any overlay components * chore: prettier * refactor: convert CreateBucketOverlay to function component * chore: changelog * chore: cleanup * feat: add integration test for creating a bucket from the query builder * refactor: rebuild selector list bucket creator with useReducer * refactor: keeping it DRY - both bucket creation components use the same state management --- CHANGELOG.md | 10 + ui/cypress/e2e/queryBuilder.test.ts | 20 ++ .../buckets/components/BucketOverlayForm.tsx | 13 +- ui/src/buckets/components/BucketsTab.tsx | 81 +------ .../buckets/components/CreateBucketButton.tsx | 97 ++++++++ .../components/CreateBucketOverlay.tsx | 208 ++++++++---------- .../components/UpdateBucketOverlay.tsx | 2 +- ui/src/buckets/reducers/createBucket.ts | 62 ++++++ .../overlays/components/OverlayController.tsx | 4 + ui/src/overlays/reducers/overlays.ts | 1 + .../timeMachine/components/SelectorList.scss | 22 +- .../timeMachine/components/SelectorList.tsx | 18 +- .../components/SelectorListCreateBucket.tsx | 192 ++++++++++++++++ .../queryBuilder/BucketsSelector.tsx | 30 ++- 14 files changed, 545 insertions(+), 215 deletions(-) create mode 100644 ui/src/buckets/components/CreateBucketButton.tsx create mode 100644 ui/src/buckets/reducers/createBucket.ts create mode 100644 ui/src/timeMachine/components/SelectorListCreateBucket.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index cfae6b2fd0..974dd216fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## v2.0.0-beta.10 [Unreleased] + +### Features + +### Bug Fixes + +### UI Improvements + +1. [17860](https://github.com/influxdata/influxdb/pull/17860): Allow bucket creation from the Data Explorer and Cell Editor + ## v2.0.0-beta.9 [2020-04-23] ### Features diff --git a/ui/cypress/e2e/queryBuilder.test.ts b/ui/cypress/e2e/queryBuilder.test.ts index 267c3a20fe..8e32c42ebe 100644 --- a/ui/cypress/e2e/queryBuilder.test.ts +++ b/ui/cypress/e2e/queryBuilder.test.ts @@ -88,6 +88,26 @@ describe('The Query Builder', () => { cy.getByTestID('cancel-cell-edit--button').click() cy.contains('Basic Ole Dashboard').should('exist') }) + + it('can create a bucket from the buckets list', () => { + cy.get('@org').then((org: Organization) => { + cy.visit(`orgs/${org.id}/data-explorer`) + }) + + const newBucketName = '٩(。•́‿•̀。)۶' + + cy.getByTestID('selector-list add-bucket').click() + + cy.getByTestID('bucket-form').should('exist') + + cy.getByTestID('bucket-form-name').type(newBucketName) + + cy.getByTestID('bucket-form-submit').click() + + cy.getByTestID('buckets-list').within(() => { + cy.contains(newBucketName).should('exist') + }) + }) }) describe('the group() function', () => { diff --git a/ui/src/buckets/components/BucketOverlayForm.tsx b/ui/src/buckets/components/BucketOverlayForm.tsx index 8236eca625..524ad0851f 100644 --- a/ui/src/buckets/components/BucketOverlayForm.tsx +++ b/ui/src/buckets/components/BucketOverlayForm.tsx @@ -16,15 +16,16 @@ import { ComponentColor, ComponentStatus, } from '@influxdata/clockface' +import {RuleType} from 'src/buckets/reducers/createBucket' interface Props { name: string retentionSeconds: number ruleType: 'expire' onSubmit: (e: FormEvent) => void - onCloseModal: () => void + onClose: () => void onChangeRetentionRule: (seconds: number) => void - onChangeRuleType: (t: 'expire' | null) => void + onChangeRuleType: (t: RuleType) => void onChangeInput: (e: ChangeEvent) => void disableRenaming: boolean buttonText: string @@ -40,7 +41,7 @@ export default class BucketOverlayForm extends PureComponent { buttonText, retentionSeconds, disableRenaming, - onCloseModal, + onClose, onChangeInput, onChangeRuleType, onChangeRetentionRule, @@ -50,7 +51,7 @@ export default class BucketOverlayForm extends PureComponent { const nameInputStatus = disableRenaming && ComponentStatus.Disabled return ( -
+ @@ -69,6 +70,7 @@ export default class BucketOverlayForm extends PureComponent { autoFocus={true} value={name} onChange={onChangeInput} + testID="bucket-form-name" /> )} @@ -90,7 +92,7 @@ export default class BucketOverlayForm extends PureComponent { + ( + + )} + /> + + ) +} + +const mstp = (state: AppState): StateProps => { + const org = getOrg(state) + const isRetentionLimitEnforced = !!extractBucketMaxRetentionSeconds( + state.cloud.limits + ) + const limitStatus = extractBucketLimits(state.cloud.limits) + + return { + org, + isRetentionLimitEnforced, + limitStatus, + } +} + +const mdtp: DispatchProps = { + createBucket, + checkBucketLimits: checkBucketLimitsAction, +} + +export default connect( + mstp, + mdtp +)(SelectorListCreateBucket) diff --git a/ui/src/timeMachine/components/queryBuilder/BucketsSelector.tsx b/ui/src/timeMachine/components/queryBuilder/BucketsSelector.tsx index 0ff482f2f3..c146d1512a 100644 --- a/ui/src/timeMachine/components/queryBuilder/BucketsSelector.tsx +++ b/ui/src/timeMachine/components/queryBuilder/BucketsSelector.tsx @@ -6,21 +6,23 @@ import {connect} from 'react-redux' import BuilderCard from 'src/timeMachine/components/builderCard/BuilderCard' import WaitingText from 'src/shared/components/WaitingText' import SelectorList from 'src/timeMachine/components/SelectorList' +import SelectorListCreateBucket from 'src/timeMachine/components/SelectorListCreateBucket' import {Input} from '@influxdata/clockface' // Actions import {selectBucket} from 'src/timeMachine/actions/queryBuilder' // Utils -import {getActiveTimeMachine, getActiveQuery} from 'src/timeMachine/selectors' +import {getActiveQuery} from 'src/timeMachine/selectors' +import {getAll, getStatus} from 'src/resources/selectors' // Types -import {AppState} from 'src/types' +import {AppState, Bucket, ResourceType} from 'src/types' import {RemoteDataState} from 'src/types' interface StateProps { selectedBucket: string - buckets: string[] + bucketNames: string[] bucketsStatus: RemoteDataState } @@ -30,16 +32,17 @@ interface DispatchProps { type Props = StateProps & DispatchProps -const fb = term => b => b.toLocaleLowerCase().includes(term.toLocaleLowerCase()) +const fb = term => bucket => + bucket.toLocaleLowerCase().includes(term.toLocaleLowerCase()) const BucketSelector: FunctionComponent = ({ selectedBucket, - buckets, + bucketNames, bucketsStatus, onSelectBucket, }) => { const [searchTerm, setSearchTerm] = useState('') - const list = buckets.filter(fb(searchTerm)) + const list = bucketNames.filter(fb(searchTerm)) const onSelect = (bucket: string) => { onSelectBucket(bucket, true) @@ -57,7 +60,7 @@ const BucketSelector: FunctionComponent = ({ ) } - if (bucketsStatus === RemoteDataState.Done && !buckets.length) { + if (bucketsStatus === RemoteDataState.Done && !bucketNames.length) { return No buckets found } @@ -97,16 +100,21 @@ const Selector: FunctionComponent = ({ selectedItems={[selected]} onSelectItem={onSelect} multiSelect={false} - /> + testID="buckets-list" + > + + ) } const mstp = (state: AppState) => { - const {buckets, bucketsStatus} = getActiveTimeMachine(state).queryBuilder + const buckets = getAll(state, ResourceType.Buckets) + const bucketNames = buckets.map(bucket => bucket.name || '') + const bucketsStatus = getStatus(state, ResourceType.Buckets) const selectedBucket = - getActiveQuery(state).builderConfig.buckets[0] || buckets[0] + getActiveQuery(state).builderConfig.buckets[0] || bucketNames[0] - return {selectedBucket, buckets, bucketsStatus} + return {selectedBucket, bucketNames, bucketsStatus} } const mdtp = {