mirror of
https://github.com/supabase/supabase.git
synced 2026-10-14 05:39:11 +03:00
port SteppedFlow polish and BigQuery JSON-on-submit onto wizard tip
This commit is contained in:
1 parent
7fb29e8192
commit
7fa8093ee5
3 files changed
+106
-13
No files matched your search
+33
-5
@@ -28,7 +28,10 @@ import * as z from 'zod'
|
||||
import { AdvancedSettings } from '../DestinationPanel/DestinationForm/AdvancedSettings'
|
||||
import { getAnalyticsBucketValidationIssues } from '../DestinationPanel/DestinationForm/AnalyticsBucket/AnalyticsBucket.utils'
|
||||
import { AnalyticsBucketFields } from '../DestinationPanel/DestinationForm/AnalyticsBucket/Fields'
|
||||
import { getBigQueryValidationIssues } from '../DestinationPanel/DestinationForm/BigQuery/BigQuery.utils'
|
||||
import {
|
||||
BIGQUERY_SERVICE_ACCOUNT_JSON_MESSAGE,
|
||||
getBigQueryValidationIssues,
|
||||
} from '../DestinationPanel/DestinationForm/BigQuery/BigQuery.utils'
|
||||
import { BigQueryFields } from '../DestinationPanel/DestinationForm/BigQuery/Fields'
|
||||
import { getClickHouseValidationIssues } from '../DestinationPanel/DestinationForm/ClickHouse/ClickHouse.utils'
|
||||
import { ClickHouseFields } from '../DestinationPanel/DestinationForm/ClickHouse/Fields'
|
||||
@@ -250,9 +253,11 @@ export const CreatePipelineWizard = () => {
|
||||
}
|
||||
|
||||
if (selectedType === 'BigQuery') {
|
||||
getBigQueryValidationIssues(data).forEach(({ path, message }) => {
|
||||
addRequiredFieldError(path, message)
|
||||
})
|
||||
getBigQueryValidationIssues(data, { validateJson: false }).forEach(
|
||||
({ path, message }) => {
|
||||
addRequiredFieldError(path, message)
|
||||
}
|
||||
)
|
||||
} else if (selectedType === 'Analytics Bucket') {
|
||||
getAnalyticsBucketValidationIssues(data).forEach(({ path, message }) => {
|
||||
addRequiredFieldError(path, message)
|
||||
@@ -384,6 +389,17 @@ export const CreatePipelineWizard = () => {
|
||||
}),
|
||||
}
|
||||
|
||||
if (selectedType === 'BigQuery') {
|
||||
const jsonIssue = getBigQueryValidationIssues(data).find(
|
||||
(issue) => issue.message === BIGQUERY_SERVICE_ACCOUNT_JSON_MESSAGE
|
||||
)
|
||||
if (jsonIssue) {
|
||||
form.setError(jsonIssue.path, { message: jsonIssue.message })
|
||||
setStep('connection')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const previousValidationFailures = allValidationFailures
|
||||
const previousWarnings = previousValidationFailures.filter((f) => f.failure_type === 'warning')
|
||||
const previousFailuresAreOnlyWarnings =
|
||||
@@ -444,7 +460,19 @@ export const CreatePipelineWizard = () => {
|
||||
|
||||
if (step === 'connection' && selectedType) {
|
||||
const valid = await form.trigger(getPipelineCreateConnectionStepFieldNames(selectedType))
|
||||
if (valid) setStep('data')
|
||||
if (!valid) return
|
||||
|
||||
if (selectedType === 'BigQuery') {
|
||||
const jsonIssue = getBigQueryValidationIssues(form.getValues()).find(
|
||||
(issue) => issue.message === BIGQUERY_SERVICE_ACCOUNT_JSON_MESSAGE
|
||||
)
|
||||
if (jsonIssue) {
|
||||
form.setError(jsonIssue.path, { message: jsonIssue.message })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
setStep('data')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,19 @@ describe('SteppedFlow', () => {
|
||||
expect(onStepChange).toHaveBeenCalledWith('destination')
|
||||
})
|
||||
|
||||
test('advances to the next step when onNext is omitted', () => {
|
||||
const onStepChange = vi.fn()
|
||||
|
||||
customRender(
|
||||
<SteppedFlow steps={steps} currentStep="destination" onStepChange={onStepChange}>
|
||||
Step body
|
||||
</SteppedFlow>
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Next' }))
|
||||
expect(onStepChange).toHaveBeenCalledWith('connection')
|
||||
})
|
||||
|
||||
test('shows the final action on the last step instead of Next', () => {
|
||||
const onFinal = vi.fn()
|
||||
|
||||
@@ -126,4 +139,35 @@ describe('SteppedFlow', () => {
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Back' })).toBeDisabled()
|
||||
})
|
||||
|
||||
test('disables Next while navigation is locked', () => {
|
||||
customRender(
|
||||
<SteppedFlow
|
||||
steps={steps}
|
||||
currentStep="destination"
|
||||
onStepChange={vi.fn()}
|
||||
navigationDisabled
|
||||
>
|
||||
Step body
|
||||
</SteppedFlow>
|
||||
)
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Next' })).toBeDisabled()
|
||||
})
|
||||
|
||||
test('disables the final action while navigation is locked', () => {
|
||||
customRender(
|
||||
<SteppedFlow
|
||||
steps={steps}
|
||||
currentStep="review"
|
||||
onStepChange={vi.fn()}
|
||||
navigationDisabled
|
||||
finalAction={{ label: 'Create and start pipeline' }}
|
||||
>
|
||||
Step body
|
||||
</SteppedFlow>
|
||||
)
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Create and start pipeline' })).toBeDisabled()
|
||||
})
|
||||
})
|
||||
@@ -75,19 +75,39 @@ export const SteppedFlow = ({
|
||||
0,
|
||||
steps.findIndex((step) => step.id === currentStep)
|
||||
)
|
||||
const isLastStep = currentIndex === steps.length - 1
|
||||
const stepCount = steps.length
|
||||
const isLastStep = stepCount > 0 && currentIndex === stepCount - 1
|
||||
const isFirstStep = currentIndex === 0
|
||||
const currentStepLabel = steps[currentIndex]?.label
|
||||
const showCancel = isFirstStep && !!onCancel
|
||||
const nextStepId = steps[currentIndex + 1]?.id
|
||||
|
||||
const handleNext = () => {
|
||||
if (onNext) {
|
||||
onNext()
|
||||
return
|
||||
}
|
||||
|
||||
if (nextStepId) {
|
||||
onStepChange(nextStepId)
|
||||
}
|
||||
}
|
||||
|
||||
if (stepCount === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full flex-col">
|
||||
<div className="mx-auto w-full max-w-[760px] flex-1 px-6 pt-8 pb-10">
|
||||
<p className="mb-4 text-xs text-foreground-lighter">
|
||||
Step {currentIndex + 1} of {steps.length}
|
||||
<p className="mb-4 text-xs text-foreground-lighter" role="status">
|
||||
Step {currentIndex + 1} of {stepCount}
|
||||
{currentStepLabel ? ` · ${currentStepLabel}` : ''}
|
||||
</p>
|
||||
<Card key={currentStep} className="animate-in fade-in-0 duration-200">
|
||||
<Card
|
||||
key={currentStep}
|
||||
className="animate-in fade-in-0 duration-200 motion-reduce:animate-none"
|
||||
>
|
||||
{children}
|
||||
<CardFooter
|
||||
className={cn(currentIndex > 0 || showCancel ? 'justify-between' : 'justify-end')}
|
||||
@@ -101,7 +121,8 @@ export const SteppedFlow = ({
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
) : showCancel ? (
|
||||
) : null}
|
||||
{currentIndex === 0 && showCancel ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="default"
|
||||
@@ -118,7 +139,7 @@ export const SteppedFlow = ({
|
||||
form={finalAction.form}
|
||||
variant="primary"
|
||||
loading={finalAction.loading}
|
||||
disabled={finalAction.disabled}
|
||||
disabled={navigationDisabled || finalAction.disabled}
|
||||
onClick={finalAction.onClick}
|
||||
>
|
||||
{finalAction.label}
|
||||
@@ -127,9 +148,9 @@ export const SteppedFlow = ({
|
||||
<Button
|
||||
type="button"
|
||||
variant="primary"
|
||||
disabled={nextDisabled}
|
||||
disabled={navigationDisabled || nextDisabled || !nextStepId}
|
||||
loading={nextLoading}
|
||||
onClick={onNext}
|
||||
onClick={handleNext}
|
||||
>
|
||||
{nextLabel}
|
||||
</Button>
|
||||
|
||||
Reference in new issue
Block a user