Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ exports[`AutoForm matches snapshot 1`] = `
className="auto-form"
>
<Memo(FormGroupWithInfo)
key="testOne"
label="Test one"
key="testNumber"
label="Test number"
>
<Memo(NumericInputWithDefault)
disabled={false}
Expand All @@ -18,8 +18,8 @@ exports[`AutoForm matches snapshot 1`] = `
/>
</Memo(FormGroupWithInfo)>
<Memo(FormGroupWithInfo)
key="testTwo"
label="Test two"
key="testSizeBytes"
label="Test size bytes"
>
<Blueprint3.NumericInput
allowNumericCharactersOnly={true}
Expand All @@ -40,8 +40,8 @@ exports[`AutoForm matches snapshot 1`] = `
/>
</Memo(FormGroupWithInfo)>
<Memo(FormGroupWithInfo)
key="testThree"
label="Test three"
key="testString"
label="Test string"
>
<Memo(SuggestibleInput)
disabled={false}
Expand All @@ -52,8 +52,20 @@ exports[`AutoForm matches snapshot 1`] = `
/>
</Memo(FormGroupWithInfo)>
<Memo(FormGroupWithInfo)
key="testFour"
label="Test four"
key="testStringWithDefault"
label="Test string with default"
>
<Memo(SuggestibleInput)
disabled={false}
onBlur={[Function]}
onValueChange={[Function]}
placeholder=""
value="Hello World"
/>
</Memo(FormGroupWithInfo)>
<Memo(FormGroupWithInfo)
key="testBoolean"
label="Test boolean"
>
<Blueprint3.ButtonGroup>
<Blueprint3.Button
Expand All @@ -73,8 +85,8 @@ exports[`AutoForm matches snapshot 1`] = `
</Blueprint3.ButtonGroup>
</Memo(FormGroupWithInfo)>
<Memo(FormGroupWithInfo)
key="testFourWithDefault"
label="Test four with default"
key="testBooleanWithDefault"
label="Test boolean with default"
>
<Blueprint3.ButtonGroup>
<Blueprint3.Button
Expand All @@ -94,8 +106,8 @@ exports[`AutoForm matches snapshot 1`] = `
</Blueprint3.ButtonGroup>
</Memo(FormGroupWithInfo)>
<Memo(FormGroupWithInfo)
key="testFive"
label="Test five"
key="testStringArray"
label="Test string array"
>
<Memo(ArrayInput)
disabled={false}
Expand All @@ -105,23 +117,43 @@ exports[`AutoForm matches snapshot 1`] = `
/>
</Memo(FormGroupWithInfo)>
<Memo(FormGroupWithInfo)
key="testSix"
label="Test six"
key="testStringArrayWithDefault"
label="Test string array with default"
>
<Memo(JsonInput)
<Memo(ArrayInput)
disabled={false}
onChange={[Function]}
placeholder=""
values={
Array [
"Hello",
"World",
]
}
/>
</Memo(FormGroupWithInfo)>
<Memo(FormGroupWithInfo)
key="testSeven"
label="Test seven"
key="testJson"
label="Test json"
>
<Memo(JsonInput)
onChange={[Function]}
placeholder=""
/>
</Memo(FormGroupWithInfo)>
<Memo(FormGroupWithInfo)
key="testStringRequiredAndDefaultValue"
label="Test string required and default value"
>
<Memo(SuggestibleInput)
disabled={false}
intent="primary"
onBlur={[Function]}
onValueChange={[Function]}
placeholder=""
value=""
/>
</Memo(FormGroupWithInfo)>
<Blueprint3.FormGroup
key="more-or-less"
>
Expand Down
28 changes: 20 additions & 8 deletions web-console/src/components/auto-form/auto-form.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,26 @@ describe('AutoForm', () => {
const autoForm = shallow(
<AutoForm
fields={[
{ name: 'testOne', type: 'number' },
{ name: 'testTwo', type: 'size-bytes' },
{ name: 'testThree', type: 'string' },
{ name: 'testFour', type: 'boolean' },
{ name: 'testFourWithDefault', type: 'boolean', defaultValue: false },
{ name: 'testFive', type: 'string-array' },
{ name: 'testSix', type: 'json' },
{ name: 'testSeven', type: 'json' },
{ name: 'testNumber', type: 'number' },
{ name: 'testSizeBytes', type: 'size-bytes' },
{ name: 'testString', type: 'string' },
{ name: 'testStringWithDefault', type: 'string', defaultValue: 'Hello World' },
{ name: 'testBoolean', type: 'boolean' },
{ name: 'testBooleanWithDefault', type: 'boolean', defaultValue: false },
{ name: 'testStringArray', type: 'string-array' },
{
name: 'testStringArrayWithDefault',
type: 'string-array',
defaultValue: ['Hello', 'World'],
},
{ name: 'testJson', type: 'json' },

{
name: 'testStringRequiredAndDefaultValue',
type: 'string',
defaultValue: 'hello',
required: () => true,
},

{ name: 'testNotDefined', type: 'string', defined: false },
{ name: 'testAdvanced', type: 'string', hideInMore: true },
Expand Down
69 changes: 34 additions & 35 deletions web-console/src/components/auto-form/auto-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ export interface Field<M> {
issueWithValue?: (value: any) => string | undefined;
}

interface ComputedFieldValues {
required: boolean;
defaultValue?: any;
modelValue: any;
}

export interface AutoFormProps<M> {
fields: Field<M>[];
model: M | undefined;
Expand Down Expand Up @@ -93,6 +99,15 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent
return newLabel;
}

static computeFieldValues<M>(model: M | undefined, field: Field<M>): ComputedFieldValues {
const required = AutoForm.evaluateFunctor(field.required, model, false);
return {
required,
defaultValue: required ? undefined : field.defaultValue,
modelValue: deepGet(model as any, field.name),
};
}

static evaluateFunctor<M, R>(
functor: undefined | Functor<M, R>,
model: M | undefined,
Expand Down Expand Up @@ -204,12 +219,12 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent

private renderNumberInput(field: Field<T>): JSX.Element {
const { model, large, onFinalize } = this.props;
const { required, defaultValue, modelValue } = AutoForm.computeFieldValues(model, field);

const modelValue = deepGet(model as any, field.name);
return (
<NumericInputWithDefault
value={modelValue}
defaultValue={field.defaultValue}
defaultValue={defaultValue}
onValueChange={(valueAsNumber: number, valueAsString: string) => {
let newValue: number | undefined;
if (valueAsString !== '' && !isNaN(valueAsNumber)) {
Expand All @@ -228,21 +243,18 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent
large={large}
disabled={AutoForm.evaluateFunctor(field.disabled, model, false)}
placeholder={AutoForm.evaluateFunctor(field.placeholder, model, '')}
intent={
AutoForm.evaluateFunctor(field.required, model, false) && modelValue == null
? AutoForm.REQUIRED_INTENT
: undefined
}
intent={required && modelValue == null ? AutoForm.REQUIRED_INTENT : undefined}
/>
);
}

private renderSizeBytesInput(field: Field<T>): JSX.Element {
const { model, large, onFinalize } = this.props;
const { required, defaultValue, modelValue } = AutoForm.computeFieldValues(model, field);

return (
<NumericInput
value={deepGet(model as any, field.name) || field.defaultValue}
value={modelValue || defaultValue}
onValueChange={(v: number) => {
if (isNaN(v)) return;
this.fieldChange(field, v);
Expand All @@ -256,17 +268,18 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent
fill
large={large}
disabled={AutoForm.evaluateFunctor(field.disabled, model, false)}
intent={required && modelValue == null ? AutoForm.REQUIRED_INTENT : undefined}
/>
);
}

private renderStringInput(field: Field<T>, sanitize?: (str: string) => string): JSX.Element {
const { model, large, onFinalize } = this.props;
const { required, defaultValue, modelValue } = AutoForm.computeFieldValues(model, field);

const modelValue = deepGet(model as any, field.name);
return (
<SuggestibleInput
value={modelValue != null ? modelValue : field.defaultValue || ''}
value={modelValue != null ? modelValue : defaultValue || ''}
onValueChange={v => {
if (sanitize && typeof v === 'string') v = sanitize(v);
this.fieldChange(field, v);
Expand All @@ -279,24 +292,17 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent
suggestions={AutoForm.evaluateFunctor(field.suggestions, model, undefined)}
large={large}
disabled={AutoForm.evaluateFunctor(field.disabled, model, false)}
intent={
AutoForm.evaluateFunctor(field.required, model, false) && modelValue == null
? AutoForm.REQUIRED_INTENT
: undefined
}
intent={required && modelValue == null ? AutoForm.REQUIRED_INTENT : undefined}
/>
);
}

private renderBooleanInput(field: Field<T>): JSX.Element {
const { model, large, onFinalize } = this.props;
const modelValue = deepGet(model as any, field.name);
const shownValue = modelValue == null ? field.defaultValue : modelValue;
const { required, defaultValue, modelValue } = AutoForm.computeFieldValues(model, field);
const shownValue = modelValue == null ? defaultValue : modelValue;
const disabled = AutoForm.evaluateFunctor(field.disabled, model, false);
const intent =
AutoForm.evaluateFunctor(field.required, model, false) && modelValue == null
? AutoForm.REQUIRED_INTENT
: undefined;
const intent = required && modelValue == null ? AutoForm.REQUIRED_INTENT : undefined;

return (
<ButtonGroup large={large}>
Expand Down Expand Up @@ -342,41 +348,34 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent

private renderStringArrayInput(field: Field<T>): JSX.Element {
const { model, large } = this.props;
const modelValue = deepGet(model as any, field.name);
const { required, defaultValue, modelValue } = AutoForm.computeFieldValues(model, field);

return (
<ArrayInput
values={modelValue || []}
values={modelValue || defaultValue || []}
onChange={(v: any) => {
this.fieldChange(field, v);
}}
placeholder={AutoForm.evaluateFunctor(field.placeholder, model, '')}
large={large}
disabled={AutoForm.evaluateFunctor(field.disabled, model, false)}
intent={
AutoForm.evaluateFunctor(field.required, model, false) && modelValue == null
? AutoForm.REQUIRED_INTENT
: undefined
}
intent={required && modelValue == null ? AutoForm.REQUIRED_INTENT : undefined}
/>
);
}

private renderIntervalInput(field: Field<T>): JSX.Element {
const { model } = this.props;
const { required, defaultValue, modelValue } = AutoForm.computeFieldValues(model, field);

const modelValue = deepGet(model as any, field.name);
return (
<IntervalInput
interval={modelValue != null ? modelValue : field.defaultValue || ''}
interval={modelValue != null ? modelValue : defaultValue || ''}
onValueChange={(v: any) => {
this.fieldChange(field, v);
}}
placeholder={AutoForm.evaluateFunctor(field.placeholder, model, '')}
intent={
AutoForm.evaluateFunctor(field.required, model, false) && modelValue == null
? AutoForm.REQUIRED_INTENT
: undefined
}
intent={required && modelValue == null ? AutoForm.REQUIRED_INTENT : undefined}
/>
);
}
Expand Down
1 change: 0 additions & 1 deletion web-console/src/druid-models/ingestion-spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,6 @@ export function getIoConfigFormFields(ingestionComboType: IngestionComboType): F
'kinesis.us-gov-east-1.amazonaws.com',
'kinesis.us-gov-west-1.amazonaws.com',
],
required: true,
info: (
<>
The Amazon Kinesis stream endpoint for a region. You can find a list of endpoints{' '}
Expand Down
1 change: 0 additions & 1 deletion web-console/src/druid-models/timestamp-spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ export const TIMESTAMP_SPEC_FIELDS: Field<TimestampSpec>[] = [
name: 'column',
type: 'string',
defaultValue: 'timestamp',
required: true,
},
{
name: 'format',
Expand Down
15 changes: 10 additions & 5 deletions web-console/src/views/load-data-view/load-data-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
comboType: IngestionComboTypeWithExtra,
disabled?: boolean,
): JSX.Element | undefined {
const { overlordModules, selectedComboType } = this.state;
const { overlordModules, selectedComboType, spec } = this.state;
if (!overlordModules) return;
const requiredModule = getRequiredModule(comboType);
const goodToGo = !disabled && (!requiredModule || overlordModules.includes(requiredModule));
Expand All @@ -722,10 +722,15 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
className={classNames({ disabled: !goodToGo, active: selectedComboType === comboType })}
interactive
elevation={1}
onClick={() => {
this.setState({
selectedComboType: selectedComboType !== comboType ? comboType : undefined,
});
onClick={e => {
if (e.altKey && e.shiftKey) {
this.updateSpec(updateIngestionType(spec, comboType as any));
this.updateStep('connect');
} else {
this.setState({
selectedComboType: selectedComboType !== comboType ? comboType : undefined,
});
}
}}
>
<img
Expand Down