From 4c1f89156a68721640fdb01122eef05ed4eb394b Mon Sep 17 00:00:00 2001 From: Vadim Ogievetsky Date: Tue, 17 Aug 2021 15:40:27 -0700 Subject: [PATCH 1/8] polish lookup view UX --- .../src/components/auto-form/auto-form.tsx | 11 +- .../form-group-with-info.scss | 2 +- .../form-group-with-info.tsx | 2 +- .../formatted-input-group.spec.tsx.snap | 25 --- .../formatted-input-group.tsx | 69 ------ .../formatted-input/formatted-input.scss | 29 +++ .../formatted-input.spec.tsx} | 8 +- .../formatted-input/formatted-input.tsx | 107 +++++++++ web-console/src/components/index.ts | 2 +- .../components/rule-editor/rule-editor.tsx | 4 +- .../suggestible-input/suggestible-input.tsx | 9 +- .../lookup-edit-dialog.spec.tsx | 2 +- .../lookup-edit-dialog/lookup-edit-dialog.tsx | 17 +- .../src/druid-models/ingestion-spec.spec.ts | 14 +- .../src/druid-models/ingestion-spec.tsx | 18 +- web-console/src/druid-models/input-format.tsx | 3 +- web-console/src/druid-models/lookup-spec.tsx | 205 +++++++++++------- web-console/src/utils/index.tsx | 1 + web-console/src/utils/sanitizers.ts | 21 ++ .../src/views/lookups-view/lookups-view.tsx | 74 +++++-- 20 files changed, 398 insertions(+), 225 deletions(-) delete mode 100644 web-console/src/components/formatted-input-group/__snapshots__/formatted-input-group.spec.tsx.snap delete mode 100644 web-console/src/components/formatted-input-group/formatted-input-group.tsx create mode 100644 web-console/src/components/formatted-input/formatted-input.scss rename web-console/src/components/{formatted-input-group/formatted-input-group.spec.tsx => formatted-input/formatted-input.spec.tsx} (87%) create mode 100644 web-console/src/components/formatted-input/formatted-input.tsx create mode 100644 web-console/src/utils/sanitizers.ts diff --git a/web-console/src/components/auto-form/auto-form.tsx b/web-console/src/components/auto-form/auto-form.tsx index f2f13a2820f7..af4f82b902d9 100644 --- a/web-console/src/components/auto-form/auto-form.tsx +++ b/web-console/src/components/auto-form/auto-form.tsx @@ -20,7 +20,7 @@ import { Button, ButtonGroup, FormGroup, Intent, NumericInput } from '@blueprint import { IconNames } from '@blueprintjs/icons'; import React from 'react'; -import { deepDelete, deepGet, deepSet } from '../../utils'; +import { deepDelete, deepGet, deepSet, durationSanitizer } from '../../utils'; import { ArrayInput } from '../array-input/array-input'; import { FormGroupWithInfo } from '../form-group-with-info/form-group-with-info'; import { IntervalInput } from '../interval-input/interval-input'; @@ -281,15 +281,16 @@ export class AutoForm> extends React.PureComponent ); } - private renderStringInput(field: Field, sanitize?: (str: string) => string): JSX.Element { + private renderStringInput(field: Field, sanitizer?: (str: string) => string): JSX.Element { const { model, large, onFinalize } = this.props; const { required, defaultValue, modelValue } = AutoForm.computeFieldValues(model, field); return ( { - if (sanitize && typeof v === 'string') v = sanitize(v); this.fieldChange(field, v); }} onBlur={() => { @@ -397,9 +398,7 @@ export class AutoForm> extends React.PureComponent case 'string': return this.renderStringInput(field); case 'duration': - return this.renderStringInput(field, (str: string) => - str.toUpperCase().replace(/[^0-9PYMDTHS.,]/g, ''), - ); + return this.renderStringInput(field, durationSanitizer); case 'boolean': return this.renderBooleanInput(field); case 'string-array': diff --git a/web-console/src/components/form-group-with-info/form-group-with-info.scss b/web-console/src/components/form-group-with-info/form-group-with-info.scss index 4d61773bb0cf..40ac3cc1c30b 100644 --- a/web-console/src/components/form-group-with-info/form-group-with-info.scss +++ b/web-console/src/components/form-group-with-info/form-group-with-info.scss @@ -24,7 +24,7 @@ .bp3-form-content { position: relative; - & > .bp3-popover2-target { + & > .info-popover { position: absolute; right: 0; top: 5px; diff --git a/web-console/src/components/form-group-with-info/form-group-with-info.tsx b/web-console/src/components/form-group-with-info/form-group-with-info.tsx index c6113b21a8d7..22533a1dd0ea 100644 --- a/web-console/src/components/form-group-with-info/form-group-with-info.tsx +++ b/web-console/src/components/form-group-with-info/form-group-with-info.tsx @@ -36,7 +36,7 @@ export const FormGroupWithInfo = React.memo(function FormGroupWithInfo( const { label, info, inlineInfo, children } = props; const popover = ( - + ); diff --git a/web-console/src/components/formatted-input-group/__snapshots__/formatted-input-group.spec.tsx.snap b/web-console/src/components/formatted-input-group/__snapshots__/formatted-input-group.spec.tsx.snap deleted file mode 100644 index 672f57266496..000000000000 --- a/web-console/src/components/formatted-input-group/__snapshots__/formatted-input-group.spec.tsx.snap +++ /dev/null @@ -1,25 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`FormattedInputGroup matches snapshot on undefined value 1`] = ` -
- -
-`; - -exports[`FormattedInputGroup matches snapshot with escaped value 1`] = ` -
- -
-`; diff --git a/web-console/src/components/formatted-input-group/formatted-input-group.tsx b/web-console/src/components/formatted-input-group/formatted-input-group.tsx deleted file mode 100644 index 5622906bfb39..000000000000 --- a/web-console/src/components/formatted-input-group/formatted-input-group.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { InputGroup, InputGroupProps2 } from '@blueprintjs/core'; -import classNames from 'classnames'; -import React, { useState } from 'react'; - -import { Formatter } from '../../utils'; - -export interface FormattedInputGroupProps extends InputGroupProps2 { - formatter: Formatter; - onValueChange: (newValue: undefined | string) => void; -} - -export const FormattedInputGroup = React.memo(function FormattedInputGroup( - props: FormattedInputGroupProps, -) { - const { className, formatter, value, defaultValue, onValueChange, onBlur, ...rest } = props; - - const [intermediateValue, setIntermediateValue] = useState(); - - return ( - { - const rawValue = e.target.value; - setIntermediateValue(rawValue); - - let parsedValue: string | undefined; - try { - parsedValue = formatter.parse(rawValue); - } catch { - return; - } - onValueChange(parsedValue); - }} - onBlur={e => { - setIntermediateValue(undefined); - onBlur?.(e); - }} - {...rest} - /> - ); -}); diff --git a/web-console/src/components/formatted-input/formatted-input.scss b/web-console/src/components/formatted-input/formatted-input.scss new file mode 100644 index 000000000000..4369df8b5b16 --- /dev/null +++ b/web-console/src/components/formatted-input/formatted-input.scss @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.formatted-input { + position: relative; + + & > .bp3-popover2-target { + position: absolute; + width: 0; + right: 0; + top: 0; + bottom: 0; + } +} diff --git a/web-console/src/components/formatted-input-group/formatted-input-group.spec.tsx b/web-console/src/components/formatted-input/formatted-input.spec.tsx similarity index 87% rename from web-console/src/components/formatted-input-group/formatted-input-group.spec.tsx rename to web-console/src/components/formatted-input/formatted-input.spec.tsx index dfa6739ee8de..da74ec711157 100644 --- a/web-console/src/components/formatted-input-group/formatted-input-group.spec.tsx +++ b/web-console/src/components/formatted-input/formatted-input.spec.tsx @@ -21,12 +21,12 @@ import React from 'react'; import { JSON_STRING_FORMATTER } from '../../utils'; -import { FormattedInputGroup } from './formatted-input-group'; +import { FormattedInput } from './formatted-input'; -describe('FormattedInputGroup', () => { +describe('FormattedInput', () => { it('matches snapshot on undefined value', () => { const suggestibleInput = ( - {}} formatter={JSON_STRING_FORMATTER} /> + {}} formatter={JSON_STRING_FORMATTER} /> ); const { container } = render(suggestibleInput); @@ -35,7 +35,7 @@ describe('FormattedInputGroup', () => { it('matches snapshot with escaped value', () => { const suggestibleInput = ( - {}} formatter={JSON_STRING_FORMATTER} diff --git a/web-console/src/components/formatted-input/formatted-input.tsx b/web-console/src/components/formatted-input/formatted-input.tsx new file mode 100644 index 000000000000..fb3c0a7bd4ac --- /dev/null +++ b/web-console/src/components/formatted-input/formatted-input.tsx @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { InputGroup, InputGroupProps2, Intent } from '@blueprintjs/core'; +import { Tooltip2 } from '@blueprintjs/popover2'; +import classNames from 'classnames'; +import React, { useState } from 'react'; + +import { Formatter } from '../../utils'; + +import './formatted-input.scss'; + +export interface FormattedInputProps extends InputGroupProps2 { + formatter: Formatter; + onValueChange: (newValue: undefined | string) => void; + sanitizer?: (rawValue: string) => string; + issueWithValue?: (value: any) => string | undefined; +} + +export const FormattedInput = React.memo(function FormattedInput(props: FormattedInputProps) { + const { + className, + formatter, + sanitizer, + issueWithValue, + value, + defaultValue, + onValueChange, + onFocus, + onBlur, + intent, + ...rest + } = props; + + const [intermediateValue, setIntermediateValue] = useState(); + const [isFocused, setIsFocused] = useState(false); + + const issue: string | undefined = issueWithValue?.(value); + const showIssue = Boolean(!isFocused && issue); + + return ( +
+ { + let rawValue = e.target.value; + if (sanitizer) rawValue = sanitizer(rawValue); + setIntermediateValue(rawValue); + + let parsedValue: string | undefined; + try { + parsedValue = formatter.parse(rawValue); + } catch { + return; + } + onValueChange(parsedValue); + }} + onFocus={e => { + setIsFocused(true); + onFocus?.(e); + }} + onBlur={e => { + setIntermediateValue(undefined); + setIsFocused(false); + onBlur?.(e); + }} + intent={showIssue ? Intent.DANGER : intent} + {...rest} + /> + {showIssue && ( + +
+ + )} +
+ ); +}); diff --git a/web-console/src/components/index.ts b/web-console/src/components/index.ts index 7b412960c5b8..fdefa7cc34b6 100644 --- a/web-console/src/components/index.ts +++ b/web-console/src/components/index.ts @@ -25,7 +25,7 @@ export * from './center-message/center-message'; export * from './clearable-input/clearable-input'; export * from './external-link/external-link'; export * from './form-json-selector/form-json-selector'; -export * from './formatted-input-group/formatted-input-group'; +export * from './formatted-input/formatted-input'; export * from './header-bar/header-bar'; export * from './highlight-text/highlight-text'; export * from './json-collapse/json-collapse'; diff --git a/web-console/src/components/rule-editor/rule-editor.tsx b/web-console/src/components/rule-editor/rule-editor.tsx index c062e63e6825..c2546d62c628 100644 --- a/web-console/src/components/rule-editor/rule-editor.tsx +++ b/web-console/src/components/rule-editor/rule-editor.tsx @@ -30,6 +30,7 @@ import { import { IconNames } from '@blueprintjs/icons'; import React, { useState } from 'react'; +import { durationSanitizer } from '../../utils'; import { Rule, RuleUtil } from '../../utils/load-rule'; import { SuggestibleInput } from '../suggestible-input/suggestible-input'; @@ -175,10 +176,9 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps) {RuleUtil.hasPeriod(rule) && ( { if (typeof period === 'undefined') return; - // Ensure the period is upper case and does not contain anytihng but the allowed chars - period = period.toUpperCase().replace(/[^PYMDTHS0-9]/g, ''); onChange(RuleUtil.changePeriod(rule, period)); }} placeholder={PERIOD_SUGGESTIONS[0]} diff --git a/web-console/src/components/suggestible-input/suggestible-input.tsx b/web-console/src/components/suggestible-input/suggestible-input.tsx index 710bc134ec23..6723bea53fb0 100644 --- a/web-console/src/components/suggestible-input/suggestible-input.tsx +++ b/web-console/src/components/suggestible-input/suggestible-input.tsx @@ -23,10 +23,7 @@ import classNames from 'classnames'; import React, { useRef } from 'react'; import { JSON_STRING_FORMATTER } from '../../utils'; -import { - FormattedInputGroup, - FormattedInputGroupProps, -} from '../formatted-input-group/formatted-input-group'; +import { FormattedInput, FormattedInputProps } from '../formatted-input/formatted-input'; export interface SuggestionGroup { group: string; @@ -35,7 +32,7 @@ export interface SuggestionGroup { export type Suggestion = undefined | string | SuggestionGroup; -export interface SuggestibleInputProps extends Omit { +export interface SuggestibleInputProps extends Omit { onFinalize?: () => void; suggestions?: Suggestion[]; } @@ -60,7 +57,7 @@ export const SuggestibleInput = React.memo(function SuggestibleInput(props: Sugg } return ( - { onClose={() => {}} onSubmit={() => {}} onChange={() => {}} - lookupName="test" + lookupId="test" lookupTier="test" lookupVersion="test" lookupSpec={{ type: 'map', map: { a: 1 } }} diff --git a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx index ffd4e49a62ce..53aa0da24fd4 100644 --- a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx +++ b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx @@ -36,10 +36,10 @@ export interface LookupEditDialogProps { onClose: () => void; onSubmit: (updateLookupVersion: boolean) => void; onChange: ( - field: 'name' | 'tier' | 'version' | 'spec', + field: 'id' | 'tier' | 'version' | 'spec', value: string | Partial, ) => void; - lookupName: string; + lookupId: string; lookupTier: string; lookupVersion: string; lookupSpec: Partial; @@ -53,7 +53,7 @@ export const LookupEditDialog = React.memo(function LookupEditDialog(props: Look onSubmit, lookupSpec, lookupTier, - lookupName, + lookupId, lookupVersion, onChange, isEdit, @@ -64,7 +64,7 @@ export const LookupEditDialog = React.memo(function LookupEditDialog(props: Look const [jsonError, setJsonError] = useState(); const disableSubmit = Boolean( - jsonError || isLookupInvalid(lookupName, lookupVersion, lookupTier, lookupSpec), + jsonError || isLookupInvalid(lookupId, lookupVersion, lookupTier, lookupSpec), ); return ( @@ -73,13 +73,14 @@ export const LookupEditDialog = React.memo(function LookupEditDialog(props: Look isOpen onClose={onClose} title={isEdit ? 'Edit lookup' : 'Add lookup'} + canEscapeKeyClose={false} >
onChange('name', e.target.value)} - intent={lookupName ? Intent.NONE : Intent.PRIMARY} + value={lookupId} + onChange={(e: any) => onChange('id', e.target.value)} + intent={lookupId ? Intent.NONE : Intent.PRIMARY} disabled={isEdit} placeholder="Enter the lookup name" /> @@ -112,7 +113,7 @@ export const LookupEditDialog = React.memo(function LookupEditDialog(props: Look rightElement={ + + + caret-down + + + + + + - +
`; exports[`SuggestibleInput matches snapshot with escaped value 1`] = `
- - + - + + + caret-down + + + + + + - +
`; diff --git a/web-console/src/dialogs/lookup-edit-dialog/__snapshots__/lookup-edit-dialog.spec.tsx.snap b/web-console/src/dialogs/lookup-edit-dialog/__snapshots__/lookup-edit-dialog.spec.tsx.snap index 14d7d58fbb1f..385cc0a862ec 100644 --- a/web-console/src/dialogs/lookup-edit-dialog/__snapshots__/lookup-edit-dialog.spec.tsx.snap +++ b/web-console/src/dialogs/lookup-edit-dialog/__snapshots__/lookup-edit-dialog.spec.tsx.snap @@ -2,6 +2,7 @@ exports[`LookupEditDialog matches snapshot 1`] = ` } value="test" @@ -86,7 +87,7 @@ exports[`LookupEditDialog matches snapshot 1`] = ` }, Object { "defined": [Function], - "label": "Globally cached lookup type", + "label": "Extraction type", "name": "extractionNamespace.type", "placeholder": "uri", "required": true, @@ -98,7 +99,22 @@ exports[`LookupEditDialog matches snapshot 1`] = ` }, Object { "defined": [Function], - "info": "A URI which specifies a directory (or other searchable resource) in which to search for files", + "info":

+ A URI which specifies a directory (or other searchable resource) in which to search for files specified as a + + file + + , + + hdfs + + , or + + s3 + + path prefix. +

, + "issueWithValue": [Function], "label": "URI prefix", "name": "extractionNamespace.uriPrefix", "placeholder": "s3://bucket/some/key/prefix/", @@ -109,12 +125,26 @@ exports[`LookupEditDialog matches snapshot 1`] = ` "defined": [Function], "info":

- URI for the file of interest, specified as a file, hdfs, or s3 path + URI for the file of interest, specified as a + + file + + , + + hdfs + + , or + + + s3 + + path

The URI prefix option is strictly better than URI and should be used instead

, + "issueWithValue": [Function], "label": "URI (deprecated)", "name": "extractionNamespace.uri", "placeholder": "s3://bucket/some/key/prefix/lookups-01.gz", @@ -154,10 +184,22 @@ exports[`LookupEditDialog matches snapshot 1`] = ` ], "type": "string", }, + Object { + "defaultValue": " ", + "defined": [Function], + "name": "extractionNamespace.namespaceParseSpec.delimiter", + "suggestions": Array [ + " ", + ";", + "|", + "#", + ], + "type": "string", + }, Object { "defaultValue": 0, "defined": [Function], - "info": "Number of header rows to be skipped. The default number of header rows to be skipped is 0.", + "info": "Number of header rows to be skipped.", "name": "extractionNamespace.namespaceParseSpec.skipHeaderRows", "type": "number", }, @@ -172,8 +214,8 @@ exports[`LookupEditDialog matches snapshot 1`] = ` "defined": [Function], "info": "The list of columns in the csv file", "name": "extractionNamespace.namespaceParseSpec.columns", - "placeholder": "[\\"key\\", \\"value\\"]", - "required": [Function], + "placeholder": "key, value", + "required": true, "type": "string-array", }, Object { @@ -190,18 +232,6 @@ exports[`LookupEditDialog matches snapshot 1`] = ` "placeholder": "(optional - defaults to the second column)", "type": "string", }, - Object { - "defined": [Function], - "name": "extractionNamespace.namespaceParseSpec.delimiter", - "placeholder": "(optional)", - "type": "string", - }, - Object { - "defined": [Function], - "name": "extractionNamespace.namespaceParseSpec.listDelimiter", - "placeholder": "(optional)", - "type": "string", - }, Object { "defined": [Function], "name": "extractionNamespace.namespaceParseSpec.keyFieldName", @@ -216,16 +246,10 @@ exports[`LookupEditDialog matches snapshot 1`] = ` "required": true, "type": "string", }, - Object { - "defaultValue": "0", - "defined": [Function], - "info": "Period between polling for updates", - "name": "extractionNamespace.pollPeriod", - "type": "string", - }, Object { "defined": [Function], - "info": "Defines the connectURI value on the The connector config to used", + "info": "Defines the connectURI for connecting to the database", + "issueWithValue": [Function], "label": "Connect URI", "name": "extractionNamespace.connectorConfig.connectURI", "required": true, @@ -243,12 +267,6 @@ exports[`LookupEditDialog matches snapshot 1`] = ` "name": "extractionNamespace.connectorConfig.password", "type": "string", }, - Object { - "defined": [Function], - "info": "Should tables be created", - "name": "extractionNamespace.connectorConfig.createTables", - "type": "boolean", - }, Object { "defined": [Function], "info": @@ -264,7 +282,7 @@ exports[`LookupEditDialog matches snapshot 1`] = `

, "name": "extractionNamespace.table", - "placeholder": "some_lookup_table", + "placeholder": "lookup_table", "required": true, "type": "string", }, @@ -283,7 +301,7 @@ exports[`LookupEditDialog matches snapshot 1`] = `

, "name": "extractionNamespace.keyColumn", - "placeholder": "my_key_value", + "placeholder": "key_column", "required": true, "type": "string", }, @@ -302,7 +320,7 @@ exports[`LookupEditDialog matches snapshot 1`] = `

, "name": "extractionNamespace.valueColumn", - "placeholder": "my_column_value", + "placeholder": "value_column", "required": true, "type": "string", }, @@ -310,39 +328,54 @@ exports[`LookupEditDialog matches snapshot 1`] = ` "defined": [Function], "info":

- The filter to be used when selecting lookups, this is used to create a where clause on lookup population. This will become the expression filter in the SQL query: + The column in table which contains when the key was updated. This will become the Value in the SQL query:

- SELECT keyColumn, valueColumn, tsColumn? FROM namespace.table WHERE - + SELECT keyColumn, valueColumn, - filter + tsColumn + ? FROM namespace.table WHERE filter

, - "name": "extractionNamespace.filter", - "placeholder": "(optional)", + "label": "Timestamp column", + "name": "extractionNamespace.tsColumn", + "placeholder": "timestamp_column (optional)", "type": "string", }, Object { "defined": [Function], "info":

- The column in table which contains when the key was updated. This will become the Value in the SQL query: + The filter to be used when selecting lookups, this is used to create a where clause on lookup population. This will become the expression filter in the SQL query:

- SELECT keyColumn, valueColumn, + SELECT keyColumn, valueColumn, tsColumn? FROM namespace.table WHERE + - tsColumn + filter - ? FROM namespace.table WHERE filter

, - "label": "Timestamp column", - "name": "extractionNamespace.tsColumn", - "placeholder": "(optional)", + "name": "extractionNamespace.filter", + "placeholder": "for_lookup = 1 (optional)", "type": "string", }, + Object { + "defined": [Function], + "info": "Period between polling for updates", + "name": "extractionNamespace.pollPeriod", + "required": true, + "suggestions": Array [ + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT6H", + "P1D", + ], + "type": "duration", + }, Object { "defaultValue": 0, "defined": [Function], diff --git a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx index 53aa0da24fd4..fb70c6c72a42 100644 --- a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx +++ b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx @@ -137,6 +137,7 @@ export const LookupEditDialog = React.memo(function LookupEditDialog(props: Look setJsonError(undefined); }} onError={setJsonError} + issueWithValue={spec => AutoForm.issueWithModel(spec, LOOKUP_FIELDS)} /> )} diff --git a/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap b/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap index 98840963b3de..9de130d09f3a 100644 --- a/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap +++ b/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap @@ -232,47 +232,51 @@ exports[`retention dialog matches snapshot 1`] = `
- - + - + + + caret-down + + + + + + - +