Merge pull request #15068 from influxdata/fix/disable-check-save
fix(alerting): disable save button if query is invalidpull/15075/head
commit
cc22607bd7
|
@ -0,0 +1,34 @@
|
|||
const measurement = 'my_meas'
|
||||
const field = 'my_field'
|
||||
describe('Checks', () => {
|
||||
beforeEach(() => {
|
||||
cy.flush()
|
||||
|
||||
cy.signin().then(({body}) => {
|
||||
const {
|
||||
org: {id},
|
||||
} = body
|
||||
cy.writeData([`${measurement} ${field}=0`, `${measurement} ${field}=1`])
|
||||
cy.wrap(body.org).as('org')
|
||||
|
||||
// visit the alerting index
|
||||
cy.fixture('routes').then(({orgs, alerting}) => {
|
||||
cy.visit(`${orgs}/${id}${alerting}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('can validate a check', () => {
|
||||
cy.getByTestID('create-check').click()
|
||||
|
||||
cy.getByTestID(`selector-list ${measurement}`).click()
|
||||
|
||||
cy.getByTestID('save-cell--button').should('be.disabled')
|
||||
cy.getByTestID('checkeo--header alerting-tab').should('be.disabled')
|
||||
|
||||
cy.getByTestID(`selector-list ${field}`).click()
|
||||
|
||||
cy.getByTestID('save-cell--button').should('be.enabled')
|
||||
cy.getByTestID('checkeo--header alerting-tab').should('be.enabled')
|
||||
})
|
||||
})
|
|
@ -305,25 +305,14 @@ export const flush = () => {
|
|||
}
|
||||
|
||||
export const writeData = (
|
||||
lines: string[],
|
||||
chunkSize: number = 100
|
||||
lines: string[]
|
||||
): Cypress.Chainable<Cypress.Response> => {
|
||||
return cy.fixture('user').then(({org, bucket}) => {
|
||||
let chunk: string[]
|
||||
let chunkCt: number = 0
|
||||
while (chunkCt < lines.length) {
|
||||
chunk =
|
||||
chunkCt + chunkSize <= lines.length
|
||||
? lines.slice(chunkCt, chunkCt + chunkSize - 1)
|
||||
: lines.slice(chunkCt, chunkCt + (chunkSize % lines.length))
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: '/api/v2/write?org=' + org + '&bucket=' + bucket,
|
||||
body: chunk.join('\n'),
|
||||
})
|
||||
chunkCt += chunkSize
|
||||
chunk = []
|
||||
}
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: '/api/v2/write?org=' + org + '&bucket=' + bucket,
|
||||
body: lines.join('\n'),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Libraries
|
||||
import React, {FunctionComponent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
// Components
|
||||
import {
|
||||
|
@ -16,26 +15,20 @@ import {
|
|||
} from '@influxdata/clockface'
|
||||
|
||||
// Utils
|
||||
import {getActiveTimeMachine} from 'src/timeMachine/selectors'
|
||||
import {isDraftQueryAlertable} from 'src/timeMachine/utils/queryBuilder'
|
||||
|
||||
// Actions
|
||||
import {setActiveTab} from 'src/timeMachine/actions'
|
||||
|
||||
// Types
|
||||
import {AppState, TimeMachineTab, DashboardDraftQuery} from 'src/types'
|
||||
import {TimeMachineTab, DashboardDraftQuery} from 'src/types'
|
||||
|
||||
interface DispatchProps {
|
||||
interface Props {
|
||||
setActiveTab: typeof setActiveTab
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
activeTab: TimeMachineTab
|
||||
draftQueries: DashboardDraftQuery[]
|
||||
}
|
||||
|
||||
type Props = DispatchProps & StateProps
|
||||
|
||||
const CheckAlertingButton: FunctionComponent<Props> = ({
|
||||
setActiveTab,
|
||||
draftQueries,
|
||||
|
@ -101,6 +94,7 @@ const CheckAlertingButton: FunctionComponent<Props> = ({
|
|||
<Radio.Button
|
||||
key="alerting"
|
||||
id="alerting"
|
||||
testID="checkeo--header alerting-tab"
|
||||
titleText="alerting"
|
||||
value="alerting"
|
||||
active={activeTab === 'alerting'}
|
||||
|
@ -114,20 +108,7 @@ const CheckAlertingButton: FunctionComponent<Props> = ({
|
|||
)
|
||||
}
|
||||
|
||||
const mstp = (state: AppState): StateProps => {
|
||||
const {activeTab, draftQueries} = getActiveTimeMachine(state)
|
||||
|
||||
return {activeTab, draftQueries}
|
||||
}
|
||||
|
||||
const mdtp: DispatchProps = {
|
||||
setActiveTab: setActiveTab,
|
||||
}
|
||||
|
||||
export default connect<StateProps, DispatchProps, {}>(
|
||||
mstp,
|
||||
mdtp
|
||||
)(CheckAlertingButton)
|
||||
export default CheckAlertingButton
|
||||
|
||||
interface ChecklistItemProps {
|
||||
selected: boolean
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Libraries
|
||||
import React, {useState, FC, MouseEvent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
// Components
|
||||
import RenamablePageTitle from 'src/pageLayout/components/RenamablePageTitle'
|
||||
|
@ -13,22 +14,53 @@ import {
|
|||
} from '@influxdata/clockface'
|
||||
import CheckAlertingButton from 'src/alerting/components/CheckAlertingButton'
|
||||
|
||||
// Utils
|
||||
import {getActiveTimeMachine} from 'src/timeMachine/selectors'
|
||||
import {isCheckSaveable} from 'src/timeMachine/utils/queryBuilder'
|
||||
|
||||
// Actions
|
||||
import {setActiveTab} from 'src/timeMachine/actions'
|
||||
|
||||
// Constants
|
||||
import {DEFAULT_CHECK_NAME, CHECK_NAME_MAX_LENGTH} from 'src/alerting/constants'
|
||||
|
||||
// Types
|
||||
import {RemoteDataState} from 'src/types'
|
||||
import {
|
||||
TimeMachineTab,
|
||||
RemoteDataState,
|
||||
AppState,
|
||||
DashboardDraftQuery,
|
||||
} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
interface OwnProps {
|
||||
name: string
|
||||
onSetName: (name: string) => void
|
||||
onCancel: () => void
|
||||
onSave: () => Promise<void>
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
activeTab: TimeMachineTab
|
||||
draftQueries: DashboardDraftQuery[]
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
setActiveTab: typeof setActiveTab
|
||||
}
|
||||
|
||||
type Props = OwnProps & StateProps & DispatchProps
|
||||
|
||||
const saveButtonClass = 'veo-header--save-cell-button'
|
||||
|
||||
const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
|
||||
const CheckEOHeader: FC<Props> = ({
|
||||
name,
|
||||
onSetName,
|
||||
onCancel,
|
||||
onSave,
|
||||
setActiveTab,
|
||||
activeTab,
|
||||
draftQueries,
|
||||
}) => {
|
||||
const [saveStatus, setSaveStatus] = useState(RemoteDataState.NotStarted)
|
||||
|
||||
const handleSave = async () => {
|
||||
|
@ -47,10 +79,17 @@ const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
|
|||
}
|
||||
}
|
||||
|
||||
const saveButtonStatus =
|
||||
saveStatus === RemoteDataState.Loading
|
||||
? ComponentStatus.Loading
|
||||
: ComponentStatus.Default
|
||||
const saveButtonStatus = () => {
|
||||
if (!isCheckSaveable(draftQueries)) {
|
||||
return ComponentStatus.Disabled
|
||||
}
|
||||
|
||||
if (saveStatus == RemoteDataState.Loading) {
|
||||
return ComponentStatus.Loading
|
||||
}
|
||||
|
||||
return ComponentStatus.Default
|
||||
}
|
||||
|
||||
return (
|
||||
<Page.Header fullWidth={true}>
|
||||
|
@ -64,7 +103,11 @@ const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
|
|||
/>
|
||||
</Page.Header.Left>
|
||||
<Page.Header.Center widthPixels={300}>
|
||||
<CheckAlertingButton />
|
||||
<CheckAlertingButton
|
||||
activeTab={activeTab}
|
||||
draftQueries={draftQueries}
|
||||
setActiveTab={setActiveTab}
|
||||
/>
|
||||
</Page.Header.Center>
|
||||
<Page.Header.Right>
|
||||
<SquareButton
|
||||
|
@ -77,7 +120,7 @@ const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
|
|||
icon={IconFont.Checkmark}
|
||||
color={ComponentColor.Success}
|
||||
size={ComponentSize.Small}
|
||||
status={saveButtonStatus}
|
||||
status={saveButtonStatus()}
|
||||
onClick={handleSave}
|
||||
testID="save-cell--button"
|
||||
/>
|
||||
|
@ -86,4 +129,17 @@ const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
|
|||
)
|
||||
}
|
||||
|
||||
export default CheckEOHeader
|
||||
const mstp = (state: AppState): StateProps => {
|
||||
const {activeTab, draftQueries} = getActiveTimeMachine(state)
|
||||
|
||||
return {activeTab, draftQueries}
|
||||
}
|
||||
|
||||
const mdtp: DispatchProps = {
|
||||
setActiveTab: setActiveTab,
|
||||
}
|
||||
|
||||
export default connect<StateProps, DispatchProps>(
|
||||
mstp,
|
||||
mdtp
|
||||
)(CheckEOHeader)
|
||||
|
|
|
@ -46,6 +46,18 @@ export const isDraftQueryAlertable = (
|
|||
}
|
||||
}
|
||||
|
||||
export const isCheckSaveable = (
|
||||
draftQueries: DashboardDraftQuery[]
|
||||
): boolean => {
|
||||
const {
|
||||
oneQuery,
|
||||
builderMode,
|
||||
singleAggregateFunc,
|
||||
singleField,
|
||||
} = isDraftQueryAlertable(draftQueries)
|
||||
return oneQuery && builderMode && singleAggregateFunc && singleField
|
||||
}
|
||||
|
||||
export function buildQuery(builderConfig: BuilderConfig): string {
|
||||
const {functions} = builderConfig
|
||||
|
||||
|
|
Loading…
Reference in New Issue